/************************************************************************************************** $Header: /pub/cvsroot/yencode/src/ypost/sock.c,v 1.2 2002/03/21 04:58:31 bboy Exp $ Copyright (C) 2002 Don Moore This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at Your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA **************************************************************************************************/ #include "ypost.h" /* Current socket operation being performed (for timeout handler). */ static char *current_operation; /* Macro to start a timeout condition block. */ #define START_TIMEOUT(s) \ if (opt_timeout) { \ current_operation = s; \ signal(SIGALRM, sock_timeout_handler); \ alarm(opt_timeout); \ } /* Macro to stop a timeout condition block. */ #define STOP_TIMEOUT \ if (opt_timeout) { \ signal(SIGALRM, SIG_DFL); \ alarm(0); \ } /* Read buffers grow by this many bytes at a time. */ #define BUFFER_INCREMENT 128 /* Current FD if sock_setfd() is used. */ int _sock_current_fd = -1; #define Y_SOCKDEBUG_SEND 0 #define Y_SOCKDEBUG_RECV 1 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCKDEBUG If debug is enabled, this will output the data sent or received, hiding wierd control chars and converting CR and LF to '\r' and '\n'. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ static void sockdebug(int direction, unsigned char *buf, size_t buflen) { register int ct; if (!buf) return; if (direction == Y_SOCKDEBUG_RECV) fputs("[DEBUG] IN: `", stderr); else fputs("[DEBUG] OUT: `", stderr); for (ct = 0; ct < buflen; ct++) { if (buf[ct] == CR) { fputc('\\', stderr); fputc('r', stderr); } else if (buf[ct] == LF) { fputc('\\', stderr); fputc('n', stderr); } // else if (iscntrl(buf[ct])) // fputc('?', stderr); else fputc(buf[ct], stderr); } fputc('\'', stderr); fputc('\n', stderr); fflush(stderr); } /*--- sockdebug() -------------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_TIMEOUT_HANDLER Called if socket operations time out. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void sock_timeout_handler(int signo) { Err("%s: %s\n", current_operation ? current_operation : _("socket operation"), _("specified timeout exceeded")); exit(EXIT_FAILURE); } /*--- sock_timeout_handler() --------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Given a host and port, which should be specified in the format "host:port", constructs and returns a sockaddr_in record. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ struct sockaddr_in * sock_getsockaddr(char *address) { static struct sockaddr_in sa; // Socket structureA struct servent *ent; // Service entry struct hostent *he; // Host entry char *hostname, *portname; // Host and port part of `hostport' memset(&sa, 0, sizeof(struct sockaddr_in)); // Reset socket structure sa.sin_family = AF_INET; portname = address; hostname = strsep(&portname, ":"); if (!hostname) Err("%s: %s", address, _("invalid server name")); if (portname) sa.sin_port = htons(atoi(portname)); else { START_TIMEOUT(_("service entry lookup")); if (!(ent = getservbyname("nntp", "tcp"))) Err(_("unable to get default port for service `nntp/tcp'")); STOP_TIMEOUT; sa.sin_port = ent->s_port; } START_TIMEOUT(_("server name resolution")); if (!(he = gethostbyname(hostname))) Err("%s: %s", address, _("unable to resolve server address")); STOP_TIMEOUT; memcpy(&sa.sin_addr, he->h_addr_list[0], sizeof(struct in_addr)); return (&sa); } /*--- sock_getsockaddr() ------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Opens a connection to the specified address. Returns the new file descriptor. Errors fatal. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ int sock_open(struct sockaddr_in *sa) { int fd; // File descriptor of new socket int rv; // Return value from connect START_TIMEOUT(_("socket creation")); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) // Create socket ErrERR(_("error creating socket")); STOP_TIMEOUT; START_TIMEOUT(_("socket connection")); while ((rv = connect(fd, (struct sockaddr *)sa, sizeof(struct sockaddr_in))) != 0) { if (errno == EAGAIN) continue; else ErrERR(_("error connecting to server")); } STOP_TIMEOUT; return (fd); } /*--- sock_open() -------------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_READ_FD Reads from the socket, obeying timeout. Returns number of bytes read. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ssize_t sock_read_fd(int fd, unsigned char *buf, size_t count) { int rv; // Return value from read() memset(buf, 0, count); // XXX Is this necessary? START_TIMEOUT(_("socket read")); rv = read(fd, buf, count); STOP_TIMEOUT; if (rv < 0) // Will this fail falsely on EAGAIN? ErrERR(_("error reading from socket")); return (rv); } /*--- sock_read_fd() ----------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_GETS_FD Reads from the specified descriptor until '\n' is read. Returns a pointer to a dynamically allocated buffer, which should be free()'d when no longer needed. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ unsigned char * sock_gets_fd(int fd) { int rv; // Return value from select int offset = 0; // Number of characters read int buflen = 0; // Current size of input buffer unsigned char *buf; // Input buffer // Start our input buffer off buf = (unsigned char *)xmalloc(BUFFER_INCREMENT, 1); memset(buf, 0, BUFFER_INCREMENT); buflen = BUFFER_INCREMENT; for (;;) { START_TIMEOUT(_("socket read")); if (!(rv = sock_read_fd(fd, buf + offset, 1))) // Read one char at a time Err(_("connection closed by remote")); STOP_TIMEOUT; if (buf[offset] == LF) // Are we done? break; // Grow the buffer if necessary if (++offset == buflen - 1) { buf = xrealloc(buf, buflen + BUFFER_INCREMENT); memset(buf + buflen, 0, BUFFER_INCREMENT); buflen += BUFFER_INCREMENT; } }; if (opt_debug) sockdebug(Y_SOCKDEBUG_RECV, buf, strlen(buf)); while ((buf[offset] == CR) || (buf[offset] == LF)) buf[offset--] = (unsigned char)'\0'; return (buf); } /*--- sock_gets_fd() ----------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_WRITE_FD Writes to the socket, obeying timeout. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void sock_write_fd(int fd, const unsigned char *buf, int count) { int offset = 0; // Number of bytes written int rv; // Return value from write() do { START_TIMEOUT(_("socket write")); rv = write(fd, buf + offset, count - offset); STOP_TIMEOUT; if (rv == 0) Err(_("connection closed by remote")); if (rv < 0) { if (errno == EAGAIN) continue; Err(_("error writing to socket")); } if (opt_debug) sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(buf+offset), rv); offset += rv; } while (offset < count); } /*--- sock_write_fd() ---------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_PUTS_FD Writes a line to the descriptor. Appends CR/LF if necessary. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void sock_puts_fd(int fd, const unsigned char *data) { int datalen = strlen(data); if (!strstr(data, CRLF)) // Append CR/LF if necessary { unsigned char *buf; buf = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char)); memcpy(buf, data, datalen); buf[datalen] = CR; buf[datalen+1] = LF; buf[datalen+2] = (unsigned char)'\0'; sock_write_fd(fd, buf, datalen + 2); free(buf); } else sock_write_fd(fd, data, datalen); } /*--- sock_puts_fd() ----------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_PRINTF Only works if _sock_current_fd is set. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void sock_printf(const char *fmt, ...) { va_list ap; size_t len; unsigned char buf[BUFSIZ]; unsigned char *outbuf; register unsigned char *i, *o; if (_SOCK_CURRENT_FD == -1) Err(_("unable to write; not connected to remote server")); va_start(ap, fmt); len = vsnprintf(buf, sizeof(buf)-4, fmt, ap); va_end(ap); /* Remove CR, convert LF to CRLF */ outbuf = (unsigned char *)xmalloc(((len * 2) + 1) * sizeof(unsigned char)); for (i = buf, o = outbuf; *i; i++) { if (*i == CR) continue; else if (*i == LF) { *(o++) = CR; *(o++) = LF; } else *(o++) = *i; } *o = '\0'; sock_puts_fd(_SOCK_CURRENT_FD, outbuf); free(outbuf); } /*--- sock_printf() -----------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SOCK_PUTS_METER_FD Writes a line to the descriptor, displaying a meter. Appends CR/LF if necessary. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ long sock_puts_meter_fd(int fd, const unsigned char *buf, long current, long total, const char *desc) { int datalen = strlen(buf); // Length of data to write unsigned char *data = NULL; // The data to send register int offset = 0; // Number of bytes written int rv; // Return value from write() if (!strstr(buf, CRLF)) // Append CR/LF if necessary { data = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char)); memcpy(data, buf, datalen); data[datalen] = CR; data[datalen+1] = LF; data[datalen+2] = (unsigned char)'\0'; datalen += 2; } else data = (unsigned char *)xstrdup(buf); /* Write the data */ do { START_TIMEOUT(_("socket write")); rv = write(fd, data + offset, datalen - offset); STOP_TIMEOUT; if (rv == 0) Err(_("connection closed by remote")); if (rv < 0) { if (errno == EAGAIN) continue; Err(_("error writing to socket")); } if (opt_debug) sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(data+offset), rv); offset += rv; meter(current + offset, total, desc); } while (offset < datalen); free(data); return (offset); } /*--- sock_puts_fd() ----------------------------------------------------------------------------*/ /* vi:set ts=3: */