Logo Search packages:      
Sourcecode: tcputils version File versions  Download package

relay.c

#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#if defined(__sun__)
#  include <sys/filio.h>    /* The official way to get FIONREAD in SunOS;
                         others seem to get it from <sys/ioctl.h> */
#endif
#include <sys/socket.h>

#include "relay.h"


#define Export


#define MIN(a,b)  ((a) < (b) ? (a) : (b))
#define MAX(a,b)  ((a) > (b) ? (a) : (b))


/*
 *  Estimate how many bytes are available for reading on a file descriptor.
 */
static      long
available_bytes(int fd)
{
    long      nbytes;
    struct stat     sb;
    off_t     pos;

    /*
     *  Some Unices don't support the FIONREAD ioctl on regular files.
     *  They don't even return an error, but says there are zero bytes
     *  to be read.  We then try to fstat() the file descriptor to see
     *  how big the file is, and try to determine at what offset the
     *  file pointer is at now.  That doesn't work always either...
     */

    if (ioctl(fd, FIONREAD, &nbytes) < 0  ||  nbytes == 0)
    {
      if (fstat(fd, &sb) < 0)
          return -1;
      errno = 0;
      pos = lseek(fd, 0, SEEK_CUR);
      if ((pos < 0  &&  errno != 0)  ||  !S_ISREG(sb.st_mode))
          pos = 0;
      nbytes = sb.st_size - pos;
    }
    return nbytes;
}



/*
 *  Wait for data to arrive on any of the source file descriptors in
 *  RELAYS, read all available data, and write to respective dest
 *  file descriptors.  TIMEOUT is the maximum time to wait, or a nil
 *  pointer to never time out.  For each read() done, the function
 *  CALLBACK is called.  Note that CALLBACK may be called more than
 *  once for each file descriptor if much data is available.
 *  NRELAYS is the number of elements in RELAYS.
 *
 *  If the 'readerror' field is non-zero for a relay, no read will be
 *  attempted from the source file descriptor of that relay.
 *  If the 'writerror' field is non-zero for a relay, no write will be
 *  attempted to the dest file descriptor of that relay, but the source
 *  descriptor will still be drained from any available data.
 *
 *  If end-of-file is reached on the source of a relay, the corresponding
 *  destination will be shutdown() for writing, thus causing the other
 *  end to see a end-of-file.
 *  If a write error occurs on the destination of a relay, the source
 *  of that relay will be shutdown() for reading, causing the other end
 *  to get errors when trying to write more data to us.
 *  Note that the SIGPIPE signal should be ignored, or possibly caught,
 *  by the caller, or the process will die when a receiver closes its
 *  end for receiving.
 *
 *  Returns the number of file descriptor read from, or negative on error.
 */
Export      int
relay_once(struct relay       * relays,
         int                    nrelays,
         struct timeval * timeout,
         int (*callback)(struct relay*, char*, size_t)
    )
{
    int           maxfd;
    fd_set  readset;
    int           i;
    int           nfds;
    int           nerrors           = 0;

    FD_ZERO(&readset);
    maxfd = 0;
    for (i = nrelays - 1 ;  i >= 0 ;  i--) 
    {
      if (!relays[i].readerror) {
          FD_SET(relays[i].source, &readset);
          if (relays[i].source > maxfd)
            maxfd = relays[i].source;
      }
    }

    nfds = select(maxfd+1, &readset, (fd_set*)NULL, (fd_set*)NULL, timeout);
    if (nfds <= 0) {
      if (errno != EINTR)
          return nfds;
      else
          return 0;
    }

    for (i = nrelays - 1 ;  i >= 0 ;  i--)
    {
      if (FD_ISSET(relays[i].source, &readset))
      {
          long  unread = available_bytes(relays[i].source);
          do 
          {
            char buffer[8192];
            int bytes_read = read(relays[i].source, buffer, sizeof buffer);

            if (bytes_read < 0) {
                relays[i].readerror = errno;
                nerrors++;
                break;
            }
            unread -= bytes_read;
            if (bytes_read == 0) {
                relays[i].readerror = -1;
                shutdown(relays[i].dest, 1);
                nerrors++;
            }
            if (callback)
                (*callback)(&relays[i], buffer, bytes_read);
            if (!relays[i].writeerror  &&  bytes_read > 0) {
                int written = write(relays[i].dest, buffer, bytes_read);
                if (written < 0) {
                  relays[i].writeerror = errno;
                  shutdown(relays[i].source, 0);
                  nerrors++;
                  break;
                }
            }
          } while (unread > 0);
      }
    }

    return nerrors ? -nerrors : nfds;
}



/*
 *  Call relay_once() until end-of-file has been reached on all sources.
 *  No time limit.
 */
Export      int
relay_all(struct relay  * relaylist,
        int         nrelays,
        int (*callback)(struct relay*, char*, size_t))
{
    int  nclosed = 0;
    do
    {
      int  status = relay_once(relaylist, nrelays, NULL, callback);
      if (status < 0)
          nclosed += -status;
    } while (nclosed < nrelays);

    return 0;
}

Generated by  Doxygen 1.6.0   Back to index