home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 13 / AACD13.ISO / AACD / Online / tcpxd / tcpxd.c < prev   
C/C++ Source or Header  |  2000-02-18  |  23KB  |  765 lines

  1. /*     $Id: tcpxd.c,v 1.4 2000/02/18 06:16:08 root Exp root $     */
  2.  
  3. #ifndef lint
  4. static char vcid[] = "$Id: tcpxd.c,v 1.4 2000/02/18 06:16:08 root Exp root $";
  5. #endif /* lint */
  6.  
  7. #define VERSION "0.4"
  8.  
  9. /*
  10.     tcpxd-0.4, a generic TCP/IP relay proxy
  11.     Copyright (C) 2000  James Cameron <quozl@us.netrek.org>
  12.  
  13.     This program is free software; you can redistribute it and/or modify
  14.     it under the terms of the GNU General Public License as published by
  15.     the Free Software Foundation; either version 2 of the License, or
  16.     (at your option) any later version.
  17.  
  18.     This program is distributed in the hope that it will be useful,
  19.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.     GNU General Public License for more details.
  22.  
  23.     You should have received a copy of the GNU General Public License
  24.     along with this program; if not, write to the Free Software
  25.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  26.  
  27. */
  28.  
  29. /*
  30.  
  31. wishlist
  32.  
  33. default to background
  34. use --foreground to override
  35. handle sighup by repeating hostname lookups
  36.  
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <unistd.h>
  42. #include <string.h>
  43. #include <errno.h>
  44. #include <time.h>
  45. #include <fcntl.h>
  46. #include <sys/time.h>
  47. #include <sys/types.h>
  48. #include <sys/wait.h>
  49. #include <sys/socket.h>
  50. #include <netinet/in.h>
  51. #include <arpa/inet.h>
  52. #include <sys/uio.h>
  53. #ifndef TCP_NODELAY
  54. #include <netinet/tcp.h>
  55. #endif
  56. #include <netdb.h>
  57.  
  58. #define MAXCHUNK 65536    /* maximum transfer in any net I/O    */
  59.  
  60. /*
  61.  * One service structure exists for each port that the relay is to listen for
  62.  * connections on.  The services are connected with a doubly linked list.
  63.  */
  64. struct service {
  65.   struct service *prior;
  66.   struct service *next;
  67.   short int bound;    /* listening port number        */
  68.   short int local;    /* local source port number        */
  69.   short int port;    /* destination port number        */
  70.   char *host;        /* remote host name            */
  71.   struct sockaddr_in remote;
  72.   int bandwidth;    /* bytes per second            */
  73.   int listener;        /* file descriptor for listening on    */
  74.   int accepts;        /* count of accept() calls on socket    */
  75. } *services;
  76.  
  77. /*
  78.  * For each connection that arrives on a service listening port, a new per-
  79.  * connection structure is created to maintain context.  Connections are also
  80.  * arranged as a doubly linked list, with a pointer back to the owning service.
  81.  */
  82. struct connection {
  83.   struct connection *prior;    /* link to prior connection    */
  84.   struct connection *next;    /* link to next connection    */
  85.   struct service *service;    /* link to owning service    */
  86.   int number;            /* which connection this is    */
  87.   int connected;        /* whether connection is done    */
  88.   int incoming;            /* socket file descriptor    */
  89.   int outgoing;            /* socket file descriptor    */
  90.   int fast;            /* incoming socket has shut    */
  91.   int constipate;        /* outgoing socket has shut    */
  92.   int total;            /* total byte count so far    */
  93.   int allow;            /* currently allowed byte count */
  94.   time_t start, now;        /* start time of connection    */
  95. } *connections;
  96.  
  97. fd_set sr, sw, se;        /* set bits for select()    */
  98. fd_set or, ow, oe;        /* our bits after select()    */
  99.  
  100. int verbose = 0;        /* how verbose to be in logging */
  101.  
  102. static void block (int fd)    /* set socket blocking        */
  103. {
  104.     int flags;
  105.     flags = (~O_NONBLOCK) & fcntl(fd, F_GETFL);
  106.     fcntl(fd, F_SETFL, flags);
  107. }
  108.  
  109. static void unblock (int fd)    /* set socket non-blocking    */
  110. {
  111.     int flags;
  112.     flags = O_NONBLOCK | fcntl(fd, F_GETFL);
  113.     fcntl(fd, F_SETFL, flags);
  114. }
  115.  
  116. /* 
  117.  * Transfer data from one side to the other for a connection.  The code
  118.  * presumes that select() has already indicated that the incoming socket
  119.  * has data ready, and the outgoing socket is writeable.
  120.  */
  121. static int transfer ( int incoming, int outgoing, int allow )
  122. {
  123.   char buffer[MAXCHUNK];
  124.   int bytes;
  125.  
  126.   bytes = recv(outgoing, buffer, allow, 0);
  127.   if (bytes == 0) return 0;
  128.   if (bytes < 0) { perror("recv(): outgoing"); return 0; }
  129.   bytes = send(incoming, buffer, bytes, 0);
  130.   if (bytes < 0) { perror("send(): incoming"); return 0; }
  131.   return bytes;
  132. }
  133.  
  134. /*
  135.  * Lookup the IP address of a service target host and save it in the service
  136.  * structure.  This is done during initialisation only, to reduce delays when
  137.  * a connection arrives.
  138.  */
  139. static void resolve ( struct service *service )
  140. {
  141.   struct hostent *entry;
  142.   static struct hostent saved;
  143.   
  144.   if (verbose > 0)
  145.     printf("%d: gethostbyname(): resolving %s\n", 
  146.        service->bound, service->host);
  147.  
  148.   entry = gethostbyname(service->host);
  149.   if (entry == NULL) {
  150.     herror(service->host);
  151.     exit(1);
  152.   }
  153.   
  154.   saved = *entry;
  155.   service->remote.sin_family = saved.h_addrtype;
  156.   service->remote.sin_port = htons(service->port);
  157.   service->remote.sin_addr = * ((struct in_addr *) saved.h_addr);
  158. }
  159.  
  160. static void usage()
  161. {
  162.   fprintf(stderr, 
  163.       "tcpxd version " VERSION ", Copyright (C) 2000 James Cameron\n"
  164.       "tcpxd comes with ABSOLUTELY NO WARRANTY; for details see source.\n"
  165.       "This is free software, and you are welcome to redistribute it\n"
  166.       "under certain conditions; see source for details.\n\n" );
  167.   
  168.   fprintf(stderr, 
  169.       "Usage: tcpxd [options] [listen-port call-host call-port]\n"
  170.       "\n"
  171.       "Options can be\n"
  172.       "    --once         allow only one connection to happen then exit,\n"
  173.       "    --local port   specify the port to call out on,\n"
  174.       "    --input file   list of 'port host port' triplets to use,\n"
  175.       "    --verbose      increment verbosity, repeat as required.\n"
  176.       "\n"
  177.       "Parameters are\n"
  178.       "    listen-port    which local port to accept connection on\n"
  179.       "    call-host      host to connect to when relaying connection\n"
  180.       "    call-port      port to connect to when relaying connection\n"
  181.       "\n"
  182.       "Example: tcpxd 110 pop.example.com 110\n"
  183.       "         relays POP3 connections to another host\n"
  184.       "\n"
  185.       "Example: tcpxd 8023 localhost 23\n"
  186.       "         redirects connections on port 8023 to port 23\n" );
  187. }
  188.  
  189. int main ( int argc, char *argv[] )
  190. {
  191.                 /* command line arguments    */
  192.   int arg_bound    = 0;        /* port number to listen on    */
  193.   char *arg_host   = NULL;    /* remote host to connect to    */
  194.   int arg_port     = 0;        /* remote port to connect to    */
  195.   int arg_local    = 0;        /* local source port to use    */
  196.   int arg_once     = 0;        /* do only one session?        */
  197.   int arg_band     = 0;        /* bandwidth limit bytes/second    */
  198.   char *arg_input  = NULL;    /* file name of services    */
  199.   
  200.   int i;
  201.   int status;
  202.   
  203.   struct service *service;
  204.  
  205.   FD_ZERO(&sr);
  206.   FD_ZERO(&sw);
  207.   FD_ZERO(&se);
  208.  
  209.   services = NULL;
  210.   connections = NULL;
  211.  
  212.   if (argc == 1) {
  213.     usage();
  214.     exit(1);
  215.   }
  216.  
  217.   /* process command line arguments */
  218.   for(i=1;i<argc;i++) {
  219.     
  220.     /* --input takes a parameter which is the file containing services */
  221.     if (!strcmp(argv[i], "-i")||!strcmp(argv[i], "--input")) {
  222.       if ( i++ == argc ) break;
  223.       arg_input = argv[i];
  224.       continue;
  225.     }
  226.  
  227.     /* --bandwidth takes a parameter in bytes per second */
  228.     if (!strcmp(argv[i], "-b")||!strcmp(argv[i], "--bandwidth")) {
  229.       if ( i++ == argc ) break;
  230.       arg_band = atoi(argv[i]);
  231.       continue;
  232.     }
  233.     
  234.     /* --verbose increments the verbosity value, more messages */
  235.     if (!strcmp(argv[i], "-v")||!strcmp(argv[i], "--verbose")) {
  236.       verbose++;
  237.       continue;
  238.     }
  239.     
  240.     /* --version reports the version */
  241.     if (!strcmp(argv[i], "-V")||!strcmp(argv[i], "--version")) {
  242.       printf("%s\n", vcid);
  243.       continue;
  244.     }
  245.     
  246.     /* --once allows just one incoming connection and stops listening */
  247.     if (!strcmp(argv[i], "--once")) {
  248.       arg_once = 1;
  249.       continue;
  250.     }
  251.     
  252.     /* --local takes a parameter to use as the local port number */
  253.     if (!strcmp(argv[i], "--local")) {
  254.       if ( i++ == argc ) break;
  255.       arg_local = atoi(argv[i]);
  256.       continue;
  257.     }
  258.     
  259.     if (!strcmp(argv[i], "-h")||!strcmp(argv[i], "--help")) {
  260.       usage();
  261.       exit(1);
  262.     }
  263.     
  264.     if (arg_bound == 0)    { arg_bound = atoi(argv[i]); continue; }
  265.     if (arg_host  == NULL) { arg_host  = argv[i];       continue; }
  266.     if (arg_port  == 0)    { arg_port  = atoi(argv[i]); continue; }
  267.   }
  268.   
  269.   /* did user give us a file name to read for services */
  270.   if (arg_input == NULL) {
  271.  
  272.     /* no, so add the command line service only */
  273.  
  274.     /* die if not given command line service description */
  275.     if (arg_port == 0 || arg_host == NULL || arg_bound == 0) {
  276.       usage();
  277.       exit(1);
  278.     }
  279.  
  280.     service = malloc ( sizeof ( struct service ) );
  281.     service->bound = arg_bound;
  282.     service->local = arg_local;
  283.     service->port = arg_port;
  284.     service->host = arg_host;
  285.     resolve(service);
  286.     service->bandwidth = arg_band;
  287.     service->listener = -1;
  288.     service->next = services;
  289.     service->prior = NULL;
  290.     service->accepts = 0;
  291.     services = service;
  292.   } else {
  293.  
  294.     /* yes, so read the file */
  295.     FILE *input;
  296.  
  297.     input = fopen(arg_input, "r");
  298.     if (input == NULL) {
  299.       perror(arg_input);
  300.       exit(1);
  301.     }
  302.  
  303.     while(1) {
  304.       char buffer[1024], *line, *token;
  305.       
  306.       line = fgets(buffer, 1024, input);
  307.       if (line == NULL) break;
  308.  
  309.       /* ignore lines starting with comment characters */
  310.       if (line[0] == '#') continue;
  311.       if (line[0] == '!') continue;
  312.  
  313.       token = strtok(line, " ");
  314.       if (token == NULL) continue;
  315.       arg_bound = atoi(token);
  316.  
  317.       token = strtok(NULL, " ");
  318.       if (token == NULL) continue;
  319.       arg_host = strdup(token);
  320.  
  321.       token = strtok(NULL, " ");
  322.       if (token == NULL) continue;
  323.       arg_port = atoi(token);
  324.  
  325.       service = malloc ( sizeof ( struct service ) );
  326.       service->bound = arg_bound;
  327.       service->local = 0;
  328.       service->port = arg_port;
  329.       service->host = arg_host;
  330.       resolve(service);
  331.       service->bandwidth = 0;
  332.       service->listener = -1;
  333.       service->next = services;
  334.       service->prior = NULL;
  335.       service->accepts = 0;
  336.       services = service;
  337.     }
  338.  
  339.     fclose(input);
  340.   }
  341.   
  342.   /* for each service defined */
  343.   for(service = services;service != NULL;service = service->next) {
  344.     
  345.     /* create the listening socket */
  346.     service->listener = socket(AF_INET, SOCK_STREAM, 0);
  347.     if (service->listener == -1) { perror("socket"); exit(1); }
  348.     
  349.     /* set the socket to allow address re-use */
  350.     {
  351.       int option_value = 1;
  352.       status = setsockopt(service->listener, SOL_SOCKET, SO_REUSEADDR, 
  353.               (char *) &option_value, sizeof(option_value));
  354.       if (status < 0) { perror("setsockopt"); exit(1); }
  355.     }
  356.     
  357.     /* bind the socket to the specified port */
  358.     {
  359.       struct sockaddr_in bound;
  360.       
  361.       memset((char *) &bound, 0, sizeof(bound));
  362.       bound.sin_family = AF_INET;
  363.       bound.sin_port = htons(service->bound);
  364.       bound.sin_addr.s_addr = htonl(INADDR_ANY);
  365.       
  366.       status = bind(service->listener, &bound, sizeof bound);
  367.       if (status) { perror("bind"); exit(1); }
  368.       if (verbose > 1) 
  369.     printf("%d: bind(): listener socket %d bound to %s:%d\n", 
  370.            service->bound, service->listener, inet_ntoa(bound.sin_addr), 
  371.            ntohs(bound.sin_port));
  372.     }
  373.     
  374.     /* start listening for connections */
  375.     status = listen(service->listener, 10);
  376.     if (status) { perror("listen"); exit(1); }
  377.     if (verbose > 0)
  378.       printf("%d: listen(): listener socket %d waiting for connection\n",
  379.          service->bound, service->listener);
  380.  
  381.     /* make a note that we need to watch this file descriptor */
  382.     FD_SET(service->listener,&sr);
  383.   }
  384.   
  385.   /* main loop, exits only on error or if --once is used */
  386.   while(1) {
  387.     struct connection *connection;
  388.   
  389.     /* copy our file descriptor masks to use in select() call */
  390.     memcpy(&or,&sr,sizeof(fd_set));
  391.     memcpy(&ow,&sw,sizeof(fd_set));
  392.     memcpy(&oe,&se,sizeof(fd_set));
  393.     
  394.     if (verbose > 2) {
  395.       int i;
  396.  
  397.       printf("select: ");
  398.       for(i=0;i<FD_SETSIZE;i++) {
  399.     if (FD_ISSET(i,&or)||FD_ISSET(i,&ow)) {
  400.       printf("%d",i);
  401.       if (FD_ISSET(i,&or)) printf("r"); else printf(" ");
  402.       if (FD_ISSET(i,&ow)) printf("w"); else printf(" ");
  403.       printf(" ");
  404.     }
  405.       }
  406.       printf("\r");
  407.       fflush(stdout);
  408.     }
  409.  
  410.     /* wait for activity on the current file descriptors */
  411.     status = select(FD_SETSIZE,&or,&ow,NULL,NULL);
  412.     if (status < 0) { perror("select"); exit(1); }
  413.  
  414.     if (verbose > 2) {
  415.       int i;
  416.  
  417.       printf("select: ");
  418.       for(i=0;i<FD_SETSIZE;i++) {
  419.     if (FD_ISSET(i,&sr)||FD_ISSET(i,&sw)) {
  420.       printf("%d",i);
  421.       if (FD_ISSET(i,&sr)) 
  422.         if (FD_ISSET(i,&or)) 
  423.           printf("R"); 
  424.         else 
  425.           printf("r"); 
  426.       else 
  427.         printf(" ");
  428.       if (FD_ISSET(i,&sw)) 
  429.         if (FD_ISSET(i,&ow)) 
  430.           printf("R"); 
  431.         else 
  432.           printf("r"); 
  433.       else 
  434.         printf(" ");
  435.       printf(" ");
  436.     }
  437.       }
  438.       printf("\r");
  439.       fflush(stdout);
  440.     }
  441.     
  442.     /* for each defined service ... */
  443.     for(service = services;service != NULL;service = service->next) {
  444.       
  445.       /* is the listening socket ready for reading? */
  446.       if (FD_ISSET(service->listener,&or)) {
  447.  
  448.     /* yes, it is, so a connection has arrived */
  449.     struct connection *connection;
  450.     struct sockaddr_in peer;
  451.     int length = sizeof(peer);
  452.     
  453.     /* allocate heap for a connection structure and accept it */
  454.     connection = malloc(sizeof(struct connection));
  455.     connection->incoming = accept(service->listener, &peer, &length);
  456.     if (connection->incoming == -1) { 
  457.       perror("accept"); 
  458.       free(connection);
  459.       continue;
  460.     }
  461.     
  462.     service->accepts++;
  463.     connection->number = service->accepts;
  464.  
  465.     if (verbose > 0) 
  466.       printf("%d.%d: accept(): incoming socket %d accepted from %s:%d\n", 
  467.          service->bound, connection->number, connection->incoming, 
  468.          inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
  469.     
  470.     /* 
  471.      * Set the socket to disable the Nagle algorithm, trust the other ends
  472.      * to use Nagle if required ... thus we don't almalgamate packets but
  473.      * rather pass them right through at the same rate.  Useful for X.
  474.      */
  475.     {
  476.       int option_value = 1;
  477.       status = setsockopt(connection->incoming, IPPROTO_TCP, TCP_NODELAY, 
  478.                   (char *) &option_value, sizeof(option_value));
  479.       if (status < 0) { perror("setsockopt"); }
  480.     }
  481.       
  482.       retry:
  483.     
  484.     connection->outgoing = socket(AF_INET, SOCK_STREAM, 0);
  485.     if (connection->outgoing == -1) { 
  486.       perror("socket"); 
  487.       close(connection->incoming);
  488.       free(connection);
  489.       continue;
  490.     }
  491.     
  492.     {
  493.       int option_value = 1;
  494.       status = setsockopt(connection->outgoing, SOL_SOCKET, SO_REUSEADDR, 
  495.                   (char *) &option_value, sizeof(option_value));
  496.       if (status < 0) { perror("setsockopt"); }
  497.     }
  498.     
  499.     if (service->bandwidth != 0) {
  500.       int option_value = service->bandwidth*10;
  501.       
  502.       status = setsockopt
  503.         (
  504.          connection->outgoing,    /* socket        */
  505.          SOL_SOCKET,              /* level         */
  506.          SO_RCVBUF,               /* option name   */
  507.          (char *) &option_value,  /* option value  */
  508.          sizeof(option_value)     /* option length */
  509.          );
  510.       
  511.       if (status < 0) { perror("setsockopt (SO_RCVBUF)"); }
  512.     } else {
  513.       int option_value = 1;
  514.       
  515.       status = setsockopt(connection->outgoing, IPPROTO_TCP, TCP_NODELAY, 
  516.                   (char *) &option_value, sizeof(option_value));
  517.       if (status < 0) { perror("setsockopt"); }
  518.     }
  519.     
  520.     if (service->local != 0) {
  521.       struct sockaddr_in local;
  522.  
  523.       local.sin_family = AF_INET;
  524.       memset((char *) &local.sin_addr, 0, sizeof(local.sin_addr));
  525.       local.sin_port = htons(service->local);
  526.       status = bind(connection->outgoing, (struct sockaddr *)&local, 
  527.             sizeof(local));
  528.       if (status < 0) { perror("bind"); }
  529.       
  530.       if (verbose > 1) 
  531.         printf("%d.%d: bind(): outgoing socket %d bound to %s:%d\n",
  532.            service->bound, connection->number, connection->outgoing, 
  533.            inet_ntoa(local.sin_addr), ntohs(local.sin_port));
  534.     }
  535.  
  536.     unblock(connection->outgoing);
  537.  
  538.     status = connect(connection->outgoing, &service->remote, 
  539.              sizeof(service->remote));
  540.     if (status) {
  541.       if (errno != EINPROGRESS) {
  542.         if (errno != EADDRINUSE) { 
  543.           perror("connect");
  544.           close(connection->incoming);
  545.           close(connection->outgoing);
  546.           free(connection);
  547.           continue;
  548.         }
  549.         service->remote.sin_port = 
  550.           htons(ntohs(service->remote.sin_port)+1);
  551.         close(connection->outgoing);
  552.         printf("tcpxd: retry\n");
  553.         goto retry;
  554.       }
  555.     }
  556.     
  557.     if (verbose > 1) 
  558.       printf("%d.%d: connect(): outgoing socket %d connecting to %s:%d\n",
  559.          service->bound, connection->number, connection->outgoing, 
  560.          inet_ntoa(service->remote.sin_addr), 
  561.          ntohs(service->remote.sin_port));
  562.     
  563.     connection->connected = 0;
  564.     connection->fast = 0;
  565.     connection->constipate = 0;
  566.     connection->service = service;
  567.     connection->total = 0;
  568.     connection->allow = service->bandwidth;
  569.     if (connection->allow == 0) connection->allow = MAXCHUNK;
  570.     connection->start = time(NULL);
  571.     connection->now = connection->start;
  572.  
  573.     /* add to start of connection chain */
  574.     connection->next = connections;
  575.     if (connections != NULL) connection->next->prior = connection;
  576.     connection->prior = NULL;
  577.     connections = connection;
  578.     
  579.     /* begin by waiting for the sockets to be writeable */
  580.     FD_SET(connection->incoming,&sw);
  581.     FD_SET(connection->outgoing,&sw);
  582.       }
  583.     }
  584.     
  585.     /* from this point the file descriptor bits dance around in an amusingly 
  586.        complex manner; first we set the bit to indicate we want to be told
  587.        when the socket is writeable, and only when it is do we dare ask to be
  588.        told that the peer socket is readable, and once both of those have 
  589.        happened we can transfer data without stalling. */  
  590.     
  591.     /* process each active connection */
  592.     for(connection = connections;connection != NULL;) {
  593.       int bytes;
  594.       
  595.       /* if the outgoing connection is now writeable */
  596.       if (FD_ISSET(connection->outgoing,&ow)) {
  597.  
  598.     /* check result of connection? */
  599.     if (connection->connected == 0) {
  600.       int deferred_errno, length;
  601.       struct service *service = connection->service;
  602.  
  603.       length = sizeof(deferred_errno);
  604.       if (getsockopt(connection->outgoing, SOL_SOCKET, SO_ERROR, 
  605.              &deferred_errno, &length) < 0) deferred_errno = errno;
  606.       block(connection->outgoing);
  607.  
  608.       if (deferred_errno == 0) {
  609.  
  610.         if (verbose > 0) 
  611.           printf("%d.%d: connect(): outgoing socket %d "
  612.              "connected to %s:%d\n",
  613.              service->bound, connection->number, connection->outgoing, 
  614.              service->host, service->port);
  615.         
  616.         connection->connected++;
  617.  
  618.       } else {
  619.         /* connection failed! */
  620.         printf("%d.%d: connect(): failed, to %s:%d, %s\n", 
  621.            service->bound, connection->number,
  622.            service->host, service->port,
  623.            strerror(deferred_errno));
  624.         /* simulate close of both ends */
  625.         connection->fast++;
  626.         connection->constipate++;
  627.         /* ignore this connection for writeability test now */
  628.         FD_CLR(connection->outgoing,&sw);
  629.         FD_CLR(connection->incoming,&sw);
  630.         FD_CLR(connection->outgoing,&sr);
  631.         FD_CLR(connection->incoming,&sr);
  632.  
  633.         FD_CLR(connection->outgoing,&ow);
  634.         FD_CLR(connection->incoming,&ow);
  635.         FD_CLR(connection->outgoing,&or);
  636.         FD_CLR(connection->incoming,&or);
  637.       }
  638.     } else {
  639.       /* start looking for incoming readable data */
  640.       FD_SET(connection->incoming,&sr);
  641.       /* and stop looking for outgoing writeability */
  642.       FD_CLR(connection->outgoing,&sw);
  643.     }
  644.       }
  645.       
  646.       /* if the incoming connection is now writeable */
  647.       if (FD_ISSET(connection->incoming,&ow)) {
  648.     /* start looking for outgoing readable data */
  649.     FD_SET(connection->outgoing,&sr);
  650.     /* and stop looking for incoming writeability */
  651.     FD_CLR(connection->incoming,&sw);
  652.       }
  653.       
  654.       /* if there is outgoing readable data */
  655.       if (FD_ISSET(connection->outgoing,&or)) {
  656.     /* transfer it */
  657.     bytes = transfer(connection->incoming, connection->outgoing, 
  658.              connection->allow);
  659.     if (bytes == 0) {
  660.       if (verbose > 1) 
  661.         printf("%d.%d: recv(): outgoing socket %d connection closed\n", 
  662.            connection->service->bound, connection->number, 
  663.            connection->outgoing);
  664.       connection->constipate++;
  665.       FD_CLR(connection->outgoing,&sr);
  666.       shutdown(connection->incoming,1);
  667.       shutdown(connection->outgoing,0);
  668.     } else {
  669.       /* stop looking for readable data */
  670.       FD_CLR(connection->outgoing,&sr);
  671.       /* wait for ability to write again */
  672.       FD_SET(connection->incoming,&sw);
  673.     }
  674.     connection->total += bytes;
  675.       }
  676.       
  677.       if (FD_ISSET(connection->incoming,&or)) {
  678.     bytes = transfer(connection->outgoing, connection->incoming, 
  679.              connection->allow);
  680.     if (bytes == 0) {
  681.       if (verbose > 1) 
  682.         printf("%d.%d: recv(): incoming socket %d connection closed\n",
  683.            connection->service->bound, connection->number, 
  684.            connection->incoming);
  685.       connection->fast++;
  686.       FD_CLR(connection->incoming,&sr);
  687.       shutdown(connection->outgoing,1);
  688.       shutdown(connection->incoming,0);
  689.     } else {
  690.       /* stop looking for readable data */
  691.       FD_CLR(connection->incoming,&sr);
  692.       /* wait for ability to write again */
  693.       FD_SET(connection->outgoing,&sw);
  694.     }
  695.     connection->total += bytes;
  696.       }
  697.       
  698.       /* bandwidth support temporarily disabled until i rework it */
  699.  
  700.       /*
  701.     if (connection->service->bandwidth != 0) {
  702.     int actual;
  703.     
  704.     connection->now = time(NULL);
  705.     actual = (total/(now-start+1));
  706.     connection->allow = (connection->service->bandwidth-actual)*5+connection->service->bandwidth;
  707.     printf("tcpxd: interim bandwidth %d bytes per second\n", actual);
  708.     if (connection->allow < 1) connection->allow = 1;
  709.     if (connection->allow > MAXCHUNK) connection->allow = MAXCHUNK;
  710.     if (actual > connection->service->bandwidth/2) sleep(1);
  711.     }
  712.       */
  713.       
  714.       if (connection->fast && connection->constipate) {
  715.     struct connection *waste;
  716.     
  717.     if (verbose > 0) 
  718.       printf("%d.%d: connection is closed\n", connection->service->bound, 
  719.          connection->number );
  720.     
  721.     if (verbose > 2) 
  722.       if (connection->service->bandwidth != 0)
  723.         printf("%d.%d: full run bandwidth %d bytes per second\n",
  724.            connection->service->bound, connection->number, 
  725.            (int)(connection->total/(connection->now-connection->start+1)));
  726.     
  727.     status = shutdown(connection->incoming, 2);
  728.     if (status == -1) { 
  729.       if (errno != ENOTCONN) perror("shutdown(): incoming");
  730.     }
  731.     status = shutdown(connection->outgoing, 2);
  732.     if (status == -1) { 
  733.       if (errno != ENOTCONN) perror("shutdown(): outgoing");
  734.     }
  735.  
  736.     status = close(connection->incoming);
  737.     if (status) { perror("close(): incoming"); }
  738.     status = close(connection->outgoing);
  739.     if (status) { perror("close(): outgoing"); }
  740.     
  741.     /* delete connection from list */
  742.     if (connection->prior != NULL) {
  743.       connection->prior->next = connection->next;
  744.     } else {
  745.       connections = connection->next;
  746.     }
  747.     
  748.     if (connection->next != NULL) {
  749.       connection->next->prior = connection->prior;
  750.     }
  751.     
  752.     /* gymnastics for freeing */
  753.     waste = connection;
  754.     connection = connection->next;
  755.     free(waste);
  756.  
  757.     if (verbose > 1) printf("\n");
  758.       } else {
  759.     connection = connection->next;
  760.       }
  761.     }
  762.     
  763.   } /* while(1) */
  764. }
  765.