Logo Search packages:      
Sourcecode: yencode version File versions

sock.c

/**************************************************************************************************
      $Header: /pub/cvsroot/yencode/src/ypost/sock.c,v 1.2 2002/03/21 04:58:31 bboy Exp $

      Copyright (C) 2002  Don Moore <bboy@bboy.net>

      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: */

Generated by  Doxygen 1.6.0   Back to index