/* * $Id: tunnel.c,v 1.66 2001/07/30 15:10:19 coelho Exp $ * * * COPYRIGHT * * (c) Fabien Coelho 2000-2001 * sometimes on the web as http://www.coelho.net/ * * * LICENSE * * THIS PROGRAM IS DISTRIBUTED AS IS, WITHOUT ANY WARRANTY, UNDER * THE TERMS OF THE GNU GENERAL PUBLIC LICENSE (GNU-GPL). * * This means NO WARRANTY what so ever. * * Don't even expect the program to work for any particular purpose: * Maybe it is going to reboot your machine when run, or destroy your most * precious data, or make you hairs becoming gray. Maybe all your friends * are going to think you're a jerk because of the use of this program. * So use at your own risks. * * You may modify the source code and distribute it as you want, provided * that the initial copyright is preserved and acknowledged and that your * changes are clearly documented and signed in this section. Bug fixes, * comments or additionnal features may be sent to the author who will * do whatever pleases him with them, including but not limited to, nothing. * * See http://www.gnu.org/ for more information. * * * DESCRIPTION * * It does basic single process single thread TCP/IP tunnelling in C. * It is expected to be quite small and maybe efficient (?). * It is believed to be reasonnably coded with respect to MY standards;-) * * The process listens to a single ip/port. * Every incoming connexion is forwarded to a fixed destination. * SIGHUP (signal 1) may display the tunnel status. * No fork, no threads, the tunnel parallelism is managed thru select() calls. * There is a maximum number of simultaneous connexions. * There are many options that I needed for various purposes. * It does echo instead of tunnel if no destination is specified. * * * DOCUMENTATION * * You hold it! It is this very source code! * See 'tunnel -h' for a list of available options. * See source code for the list of available compile-time options. * The comments in the source are not necessarily pertinent. * * * SEE ALSO * * The 'bounce' program does the same (with forks and no options). * The 'Zedebee' program provides an compressed encrypted tcp/udp tunnel. * Many other programs or kernel/routeur settings to do a similar job. * * * COMPILATION * * This software is known to have run for me under * Linux/Intel, Solaris/Intel, Solaris/Sparc, SunOS/Sparc. * * Linux: gcc -O2 -Wall tunnel.c -o tunnel * Solaris: gcc -O2 -Wall -lsocket -lnsl -DSOLARIS tunnel.c -o tunnel * SunOS: gcc -O2 -Wall -DSUNOS tunnel.c -o tunnel * Anyway: strip tunnel * */ /*********************************************************** C/UNIX INCLUDES */ #include #include #include #include #include #include #include #include #include #include #include /*************************************************************** PORTABILITY */ #if defined(SOLARIS) #define herror perror #endif /* SOLARIS */ #if defined(SUNOS) /* getopt stuff */ extern char * optarg; extern int optind, opterr; /* quite partial compatibility... it won't work correctly. */ #define strtoul(a,b,c) atoi(a) #define herror perror typedef unsigned int ssize_t; #endif /* SUNOS */ /******************************************************* COMPILATION OPTIONS */ /* whether SIGHUP can display the tunnel status. comment out to disable. */ #define SIGNAL_TO_STATUS 1 /* whether SNOOP option is compiled in. comment out to disable. */ #define MAY_SNOOP_TRAFFIC 1 /* whether to drop all output or allow some. comment out to disable. * if not set, both previous defines are also disabled. */ #define ALLOW_SOME_OUTPUT 1 /* whether to check an fd for writability alone, * or to put it globally in the full loop. * I don't know which one should be more efficient. * depends on select internals I guess? * on linux, even with a large max_connexions, it is better without. */ #undef CHECK_WRITE_ALONE /* default connexion. */ #define LHOST "localhost" /* this really means 127.0.0.1, thus no network! */ #define LPORT "2023" /* DHOST: */ #define DPORT "23" /* telnet port */ /*************************************************************** USEFUL DEFS */ #if !defined(ALLOW_SOME_OUTPUT) /* maybe these function returns should be checked for errors. */ #define fputc(c,f) #define fputs(s,f) #define fprintf(f,a...) /* GCC only. */ #define fwrite(b,s,n,f) #define perror(s) #define herror(s) #define gettimeofday(x,y) /* definition coherency. */ #if defined(MAY_SNOOP_TRAFFIC) #undef MAY_SNOOP_TRAFFIC #endif /* MAY_SNOOP_TRAFFIC */ #if defined(SIGNAL_TO_STATUS) #undef SIGNAL_TO_STATUS #endif /* SIGNAL_TO_STATUS */ #endif /* ALLOW_SOME_OUTPUT */ /* very theoretical IP packet max size is 2^16==65536. * must be a multiple of sizeof(long) for the scramble loop. */ #define BUFFER_SIZE (1<<16) /* maximum number of simultaneous connexions. could be a moved as an option with a default? is there a maximum? well, it depends on the operating system. there can be 1024 (0..1023) sockets for a process under my linux. */ #define DEFAULT_MAX_CONNEXIONS (512) /* prefix when logging. */ #define LOG "[tunnel:%d] " /* Left Circular Shift on a long by b bits. */ #define LCS(l,b) ((l<>(8*sizeof(long)-b)))) /* some convenient typedef that I like. */ typedef enum { false, true } boolean; typedef char * string; /******************************************************************* GLOBALS */ static boolean verbose = false; /* -v/-q (verbose => log) */ #if (defined(ALLOW_SOME_OUTPUT)) static boolean silent = false; /* -s to be very silent... */ #else static boolean silent = true; #endif /* ALLOW_SOME_OUTPUT */ static boolean debug = false; /* -d (debug => verbose) */ static boolean log = false; /* -l */ /* optionnal scramble with a simple xor. easy because stateless. */ static unsigned long scramble = 0x0; /* set with -p pass or -x long */ /* process id for logging purposes. */ static int pid; /* echo instead of tunnel */ static boolean echo = false; #if defined(MAY_SNOOP_TRAFFIC) /* Whether to snoop the INCOMING stream. * This is intended as a way to log commands if the tunnel * is directed to a telnet port. This is not a sniffer, although * if used as described, it would collect passwords going thru. * Also useful for my HTTP practical exercise, to avoid the use * of tcpdump or snoop by the students. * Use OTP or SSH for something serious. * May insure that the snoop data are 'readable', by switching * unprintable characteres to '.' with the -r option. */ static boolean snoop = false; /* -L (snoop => log) */ static boolean printable = false; /* -r */ /* size of snoop buffer. default is no buffer. */ static int snoopsize = 0; /* -b size (=> snoop) */ #endif /* MAY_SNOOP_TRAFFIC */ /********************************************************* SOCKET CONNEXIONS */ static int serv_socket; /* server socket which is listenned to. */ static struct sockaddr_in serv_addr; /* fixed server address */ static struct sockaddr_in dest_addr; /* fixed destination address */ /* describe a current connexion handled by the process. */ struct socket_connexion { boolean open; /* whether it is open/available */ int index; /* index in array for log messages */ /* connexion descriptor: a pair of socket and addr */ struct sockaddr_in client_addr; /* to client side */ int client; /* client socket */ boolean client_r; /* ready to read */ boolean client_w; /* ready to write */ struct sockaddr_in dest_addr; /* to destination side */ int dest; /* dest socket */ boolean dest_r; /* ready to read */ boolean dest_w; /* ready to write */ /* statistics for the connexion */ int requests; /* amount of requests client->dest. */ int nreq; /* number of request paquets. */ int responses; /* amount of responses dest->client. */ int nres; /* number of response paquets. */ #if defined(MAY_SNOOP_TRAFFIC) /* snoop buffer for incoming traffic. */ int sindex; /* index in the buffer. */ char * snooped; /* buffer of snooped stuff. [snoopsize] */ #endif /* MAY_SNOOP_TRAFFIC */ }; /* array of current connexions. */ static struct socket_connexion * connexions; /* maximum number of allowed connexions. */ static int max_connexions = DEFAULT_MAX_CONNEXIONS; /* current number of connexions. */ static int number_of_connexions; /* maximum index of open connexion. */ static int max_index_of_connexions; /* various global statistics. */ static unsigned int total_number_of_connexions; static unsigned int total_number_of_events; static unsigned int total_number_of_bytes; /* all connexions are closed on startup. zeros stats. */ static void initialize_connexions(void) { int i; number_of_connexions = 0; total_number_of_connexions = 0; total_number_of_bytes = 0; total_number_of_events = 0; max_index_of_connexions = 0; connexions = (struct socket_connexion *) malloc(max_connexions*sizeof(struct socket_connexion)); if (!connexions) abort(); for (i=0; i=0 && !connexions[index].open) index--; max_index_of_connexions = index+1; } /* returns a free chunk if any, or NULL */ static struct socket_connexion * available_connexion(void) { int i; for (i=0; isnooped) { if (printable) /* switch non printable characters to '.' */ { int i; for (i=0; isindex; i++) { register char ci = scp->snooped[i]; if ((ci<32 && ci!=10) || (ci > 126)) /* keep 10 + 32..126 */ scp->snooped[i] = '.'; } } print_snooped_buffer(scp->snooped, scp->sindex, scp->client); scp->sindex = 0; } } /* keep or print snooped data */ static void append_snooped_data(struct socket_connexion * scp, char * buffer, int size) { if (scp->snooped) { /* pre-empty buffer is not large enough */ if (scp->sindex && scp->sindex+size>=snoopsize) flush_and_reset_snooped(scp); /* put in snoop buffer */ while (size) { while (scp->sindexsnooped[scp->sindex++] = *buffer++, size--; if (scp->sindex==snoopsize) flush_and_reset_snooped(scp); } } else /* no buffer */ { print_snooped_buffer(buffer, size, scp->client); } } #endif /* MAY_SNOOP_TRAFFIC */ /* close the connexion. */ static void shutdown_connexion(struct socket_connexion * scp, string why) { if (scp->open) { if (debug) fprintf(stderr, "shuting down %d and %d\n", scp->client, scp->dest); if (scp->client!=-1) shutdown_socket(scp->client); #if defined(MAY_SNOOP_TRAFFIC) flush_and_reset_snooped(scp); #endif /* MAY_SNOOP_TRAFFIC */ if (scp->dest!=-1 && scp->dest!=scp->client) shutdown_socket(scp->dest); if (log) { struct timeval tv; gettimeofday(&tv, NULL); /* close could show collected statistics? */ fprintf(stderr, LOG "#%d (%d,%d) %s 0x%08x:%d/0x%08x:%d at %ld.%06ld\n", pid, scp->index, scp->client, scp->dest, why, ntohl(scp->client_addr.sin_addr.s_addr), ntohs(scp->client_addr.sin_port), ntohl(scp->dest_addr.sin_addr.s_addr), ntohs(scp->dest_addr.sin_port), tv.tv_sec, tv.tv_usec); } scp->open = false; scp->client = -1; scp->dest = -1; number_of_connexions--; if (max_index_of_connexions == scp->index+1) set_max_index_of_connexions(scp->index); } } /* connect the connexion or close the client */ static void open_connexion_or_shutdown(struct socket_connexion * scp) { /* let's be optimistic... */ scp->open = true; scp->client_r = false; scp->client_w = false; scp->dest_r = false; scp->dest_w = false; scp->requests = 0; scp->nreq = 0; scp->responses = 0; scp->nres = 0; number_of_connexions++; total_number_of_connexions++; if (scp->index >= max_index_of_connexions) max_index_of_connexions = scp->index+1; /* something to do if tunnel, nothing on echo. */ if (scp->dest==-1) { if ((scp->dest = socket(AF_INET, SOCK_STREAM, 0))==-1) { if (verbose) perror("socket()"); shutdown_connexion(scp, "socket same error[socket]"); return; } if (connect(scp->dest, (struct sockaddr *) &scp->dest_addr, sizeof(struct sockaddr))) { if (verbose) perror("connect()"); shutdown_connexion(scp, "error2 [connect]"); return; } } if (log) { struct timeval tv; gettimeofday(&tv, NULL); fprintf(stderr, LOG "#%d (%d,%d) open 0x%08x:%d/0x%08x:%d at %ld.%06ld\n", pid, scp->index, scp->client, scp->dest, ntohl(scp->client_addr.sin_addr.s_addr), ntohs(scp->client_addr.sin_port), ntohl(scp->dest_addr.sin_addr.s_addr), ntohs(scp->dest_addr.sin_port), tv.tv_sec, tv.tv_usec); } } #if defined(CHECK_WRITE_ALONE) /* quick check whether fd is available for writing */ static boolean can_write_now(int fd) { struct timeval timeout; fd_set s; FD_ZERO(&s); FD_SET(fd, &s); timeout.tv_sec = 0; timeout.tv_usec = 0; if (select(fd+1, NULL, &s, NULL, &timeout)==-1) return false; return FD_ISSET(fd, &s)? true: false; } #endif /* CHECK_WRITE_ALONE */ /* transmit data if any src: source socket dst: destination socket to transmit data if any amount: pointer to byte statistics, or NULL n: pointer to event statistics, or NULL scp: pointer to socket_connexion if to be snooped. */ static boolean transmit( int src, int dst, int * amount, int * n, struct socket_connexion * scp) { static char buffer[BUFFER_SIZE]; /* yes, static, thus not on stack. */ ssize_t rsize, wsize; if ((rsize = read(src, buffer, BUFFER_SIZE))==-1) { if (verbose) perror("read()"); return false; } if (debug) fprintf(stderr, "transmit() %d -> %d, size=%d\n", src, dst, rsize); /* it may happen that an empty packet comes??? */ if (rsize==0) { if (verbose) fputs("no data to read...\n", stderr); return false; } #if defined(MAY_SNOOP_TRAFFIC) if (scp) append_snooped_data(scp, buffer, rsize); #endif /* MAY_SNOOP_TRAFFIC */ /* Optional scrambling (a simple 32 bits xor against a constant). * It is not intended as a cypher, but just to prevent basic dumps. */ if (scramble) { char * ptr = buffer; for (; ptr < buffer+rsize; ptr+=sizeof(long)) * ((unsigned long *) ptr) ^= scramble; } if ((wsize = write(dst, buffer, rsize))==-1) /* should loop? */ { if (verbose) perror("write()"); return false; } if (wsize!=rsize) { if (verbose) fprintf(stderr, "transmit sizes r=%d w=%d\n", rsize, wsize); return false; } /* update global and connexion statistics. */ total_number_of_bytes += rsize; total_number_of_events++; if (amount) (*amount) += rsize; if (n) (*n)++; if (debug) fprintf(stderr, "transmit done\n"); return true; } /* transmit data <-> for a connexion, and set fd_set as needed. */ static void transmit_connexion( struct socket_connexion * scp, fd_set * ptoread, fd_set * ptowrite, fd_set * ptoexcept, int * n) { int client = scp->client, dest = scp-> dest; /* saved as may be cleared */ if (FD_ISSET(client, ptoread)) scp->client_r = true; if (FD_ISSET(dest, ptowrite)) scp->dest_w = true; if (!echo) { if (FD_ISSET(dest, ptoread)) scp->dest_r = true; if (FD_ISSET(client, ptowrite)) scp->client_w = true; } if (debug) fprintf(stderr, "IN transmit_connexion() of #%d (%d/%d%d,%d/%d%d)\n", scp->index, scp->client, scp->client_r, scp->client_w, scp->dest, scp->dest_r, scp->dest_w); #if defined(CHECK_WRITE_ALONE) if (scp->client_r && can_write_now(dest)) scp->dest_w = true; #endif /* CHECK_WRITE_ALONE */ if (scp->open && scp->client_r && scp->dest_w) { if(!transmit(client, dest, &scp->requests, &scp->nreq, #if defined(MAY_SNOOP_TRAFFIC) snoop? scp: NULL #else NULL #endif /* MAY_SNOOP_TRAFFIC */ )) shutdown_connexion(scp, "close[client]"); scp->client_r = false; scp->dest_w = false; } if (!echo) { #if defined(CHECK_WRITE_ALONE) if (scp->dest_r && can_write_now(client)) scp->client_w = true; #endif /* CHECK_WRITE_ALONE */ if (scp->open && scp->dest_r && scp->client_w) { if (!transmit(dest, client, &scp->responses, &scp->nres, NULL)) shutdown_connexion(scp, "close[dest]"); scp->dest_r = false; scp->client_w = false; } } /* update fd_set */ if (scp->open) { /* still open */ if (scp->client_r) { FD_CLR(client, ptoread); FD_SET(dest, ptowrite); } else { FD_SET(client, ptoread); FD_CLR(dest, ptowrite); } if (!echo) { if (scp->dest_r) { FD_CLR(dest, ptoread); FD_SET(client, ptowrite); } else { FD_SET(dest, ptoread); FD_CLR(client, ptowrite); } } FD_SET(dest, ptoexcept); FD_SET(client, ptoexcept); if (*nindex, scp->client, scp->client_r, scp->client_w, scp->dest, scp->dest_r, scp->dest_w); } /******************************************************************** STATUS */ #if defined(SIGNAL_TO_STATUS) /* generate a report about current status and cumulated statistics to stderr */ static void info(int sig) { struct timeval tv; int i; if (silent) return; /* we don't care now and latter. */ gettimeofday(&tv, NULL); fprintf(stderr, LOG "status at %ld.%06ld: #con=%d/%d, #bytes=%d, #events=%d\n", pid, tv.tv_sec, tv.tv_usec, number_of_connexions, total_number_of_connexions, total_number_of_bytes, total_number_of_events); for (i=0; iopen) { fprintf(stderr, LOG "#%d (%d/%d%d,%d/%d%d):" " 0x%08x:%d(e=%d,b=%d)/0x%08x:%d(e=%d,b=%d)\n", pid, i, p->client, p->client_r, p->client_w, p->dest, p->dest_r, p->dest_w, ntohl(p->client_addr.sin_addr.s_addr), ntohs(p->client_addr.sin_port), p->nreq, p->requests, ntohl(p->dest_addr.sin_addr.s_addr), ntohs(p->dest_addr.sin_port), p->nres, p->responses); } } if (sig) signal(sig, info); /* rearm signal (linux, not as BSD) */ } #endif /* SIGNAL_TO_STATUS */ /* is it useful? let say yes if buffering with snoop. */ static void down(int sig) { int i; struct timeval tv; gettimeofday(&tv, NULL); if (log) fprintf(stderr, LOG "down (signal=%d) at %ld.%06ld\n", pid, sig, tv.tv_sec, tv.tv_usec); #if defined(SIGNAL_TO_STATUS) info(0); #endif /* SIGNAL_TO_STATUS */ for (i=0; i verbose)\n" "\t-h: print this help\n" "\t-l: log connexion activity to stderr\n" #if defined(MAY_SNOOP_TRAFFIC) /* snoop option help. */ "\t-L: log activity stream to stderr (=> log)\n" "\t-r: log with printable caracters only\n" "\t-b size: buffer size for -L (default no buffer)\n" #endif /* MAY_SNOOP_TRAFFIC */ "\t-q: quiet mode (not verbose, this is the default)\n" "\t-s: silent (no output at all, even on errors)\n" "\t-v: verbose mode (=> log)\n" "\t-V: version\n" "\t-M n: maximum number of simultaneous connexions (%d)\n" "\t-x number: optional basic XOR scrambling\n" "\t-p pass: idem, XOR constant based on pass\n" "\t-m msg: insert message as a header to accepted connexions\n" "\tlochost:port local host ip and tcp port to listen\n" "\tdsthost:port tunnel destination host and tcp port\n" "\tor from environment LHOST LPORT DHOST DPORT\n" "\tor defaults: " LHOST ":" LPORT " :" DPORT "\n" "\texample: %s -l localhost:2023 mir:23\n" "\t (send localhost:2023 packets to mir:23 and log)\n" #if defined(MAY_SNOOP_TRAFFIC) "\texample: %s -Lr localhost:1080 proxy:80\n" "\t (send localhost:1080 packets to proxy:80 and snoop)\n" #endif /* MAY_SNOOP_TRAFFIC */ , program, DEFAULT_MAX_CONNEXIONS, program #if defined(MAY_SNOOP_TRAFFIC) , program #endif /* MAY_SNOOP_TRAFFIC */ ); exit(exitcode); } /********************************************************** LET'S DO THE JOB */ int main(int argc, char * argv[]) { int client_socket, len, i, code, n, opt; unsigned short int lport = 0, dport = 0; /* in_port_t */ struct in_addr lhost, dhost; /* in_addr_t */ struct sockaddr_in client_addr; fd_set toread, towrite, toexcept; boolean okay = false; string lhosts = NULL, lports = NULL, dhosts = NULL, dports = NULL; string msg = NULL; /* option management. */ boolean help = false; pid = (int) getpid(); while ((opt=getopt(argc, argv, #if defined(MAY_SNOOP_TRAFFIC) "b:Lr" /* snoop options. */ #endif /* MAY_SNOOP_TRAFFIC */ "dhlm:M:p:qsvVx:"))!=EOF) { switch (opt) { case 'd': debug = true; /* no break, debug => verbose */ case 'v': verbose = true; /* no break, verbose => log */ case 'l': log = true; break; case 's': silent = true; opterr = 0; break; case 'q': verbose = false; break; #if defined(MAY_SNOOP_TRAFFIC) /* snoop-related option management. */ case 'L': snoop = true; break; case 'r': printable = true; break; case 'b': snoopsize = strtoul(optarg, NULL, 0); break; #endif /* MAY_SNOOP_TRAFFIC */ case 'm': msg = strdup(optarg); break; case 'M': max_connexions = atoi(optarg); break; case 'x': scramble = strtoul(optarg, NULL, 0); break; case 'p': /* 7 bits left circular shift and a XOR */ while (*optarg) scramble = LCS(scramble, 7)^(*optarg++); break; case 'h': case 'V': default: help = true; } } /* these streams are not needed! */ fclose(stdin); fclose(stdout); /* insure option coherency. */ if (silent) { if (help) exit(7); verbose = false; log = false; help = false; debug = false; fclose(stderr); /* one more socket to use! */ } #if defined(MAY_SNOOP_TRAFFIC) if (snoopsize) snoop = true; if (snoop) log = true; #endif /* MAY_SNOOP_TRAFFIC */ if (debug) verbose = true; if (verbose) log = true; /* arguments: lhost:lport dhost:dport or from environment * they should be checked? */ if (argc==optind) { okay = true; /* configure from environment, with defaults. */ lhosts = getenv("LHOST"); lports = getenv("LPORT"); dhosts = getenv("DHOST"); dports = getenv("DPORT"); } if (argc-optind>=1) { okay = true; echo = true; /* local */ lhosts = argv[optind]; lports = strchr(lhosts, ':'); if (lports) *lports++ = '\0'; } if (argc-optind==2) { okay = true; echo = false; /* destination */ dhosts = argv[optind+1]; dports = strchr(dhosts, ':'); if (dports) *dports++ = '\0'; } /* set default values (as string for homogeneity) if needed. */ if (!lhosts || !*lhosts) lhosts = LHOST; if (!lports || !*lports) lports = LPORT; if (!dhosts || !*dhosts) dhosts = lhosts; if (!dports || !*dports) dports = DPORT; if (okay) { struct hostent * he = gethostbyname(lhosts); if (!he) { if (!silent) herror("gethostbyname()"); exit(5); } lhost.s_addr = *((unsigned int *)he->h_addr_list[0]); lport = atoi(lports); /* check? */ he = gethostbyname(dhosts); if (!he) { if (!silent) herror("gethostbyname()"); exit(6); } dhost.s_addr = *((unsigned int *)he->h_addr_list[0]); dport = atoi(dports); /* check? */ } else /* no or bad configuration. */ { if (silent) exit(8); help = true; } if (verbose || help) fputs("TCP/IP tunnel $Revision: 1.66 $\n", stderr); if (help) usage(argv[0], 0); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(lport); serv_addr.sin_addr = lhost; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(dport); dest_addr.sin_addr = dhost; if (log) { struct timeval tv; gettimeofday(&tv, NULL); if (echo) fprintf(stderr, LOG "start echo 0x%08x:%d x=0x%08lx at %ld.%06ld\n", pid, ntohl(serv_addr.sin_addr.s_addr), ntohs(serv_addr.sin_port), scramble, tv.tv_sec, tv.tv_usec); else fprintf(stderr, LOG "start tunnel 0x%08x:%d/0x%08x:%d x=0x%08lx at %ld.%06ld\n", pid, ntohl(serv_addr.sin_addr.s_addr), ntohs(serv_addr.sin_port), ntohl(dest_addr.sin_addr.s_addr), ntohs(dest_addr.sin_port), scramble, tv.tv_sec, tv.tv_usec); } #if defined(SIGNAL_TO_STATUS) signal(SIGHUP, info); #endif /* SIGNAL_TO_STATUS */ signal(SIGINT, down); signal(SIGQUIT, down); signal(SIGABRT, down); signal(SIGTERM, down); initialize_connexions(); serv_socket = new_server((struct sockaddr *) &serv_addr); /* initial file descriptor set */ FD_ZERO(&toread); FD_ZERO(&towrite); FD_ZERO(&toexcept); FD_SET(serv_socket, &toread); FD_SET(serv_socket, &toexcept); n = serv_socket+1; while ((code=select(n, &toread, &towrite, &toexcept, NULL))!=-1 || (code==-1 && errno==EINTR) /* allow signals */ || true) /* on select errors, let us go on anyway? */ { if ((code==-1 && errno!=EINTR) && verbose) perror("select()"); if (code==-1 && errno==EINTR) { if (debug) fprintf(stderr, "zeroing sets after select() interrupt\n"); FD_ZERO(&toread); FD_ZERO(&towrite); FD_ZERO(&toexcept); } if (debug) { dump_fd_sets("after select", n, &toread, &towrite, &toexcept); fprintf(stderr, "select code=%d\n", code); } if (code!=-1 && FD_ISSET(serv_socket, &toread)) { /* it is a new connexion. */ total_number_of_events++; len = sizeof(struct sockaddr); client_socket = accept(serv_socket, (struct sockaddr*) &client_addr, &len); if (client_socket==-1 && verbose) perror("accept()"); /* let us ignore... ??? */ if (client_socket>0) { struct socket_connexion * scp = available_connexion(); if (scp) { /* should be checked? */ if (msg) write(client_socket, msg, strlen(msg)); scp->client = client_socket; scp->client_addr = client_addr; if (echo) { /* back to client */ scp->dest_addr = client_addr; scp->dest = client_socket; } else { /* fixed destination */ scp->dest_addr = dest_addr; scp->dest = -1; } open_connexion_or_shutdown(scp); } else { if (log) fprintf(stderr, LOG "%d client refused, max #connect reached\n", pid, client_socket); shutdown_socket(client_socket); } } } FD_ZERO(&toexcept); FD_SET(serv_socket, &toexcept); FD_SET(serv_socket, &toread); n = serv_socket; /* transmit if needed. maybe the list of open connexions could be kept? */ for (i=0; i