/* $Id$ * * redir - a utility for redirecting tcp connections * * Author: Nigel Metheringham * Nigel.Metheringham@ThePLAnet.net * * Based on, but much modified from, code originally written by * sammy@freenet.akron.oh.us - original header is below. * * redir is released under the GNU General Public license, * version 2, or at your discretion, any later version. * */ /* * modification of the version -W by wildcat for compilation works on * Compaq TRU64 UNIX. * * Changed use of getopt_long by getopt * * Also added SHADE_PS, which will make redir more descrete :) * */ /* * redir is currently maintained by Sam Creasey (sammy@oh.verio.com). * Please send patches, etc. there. * */ /* 980601: dl9sau * added some nice new features: * * --bind_addr=my.other.ip.address * forces to use my.other.ip.address for the outgoing connection * * you can also specify, that redir listens not on all IP addresses of * your system but only for the given one, i.e.: * if my host has the addresses * irc.thishost.my.domain and mail.thishost.my.domain * but you want that your users do connect for the irc redir service * only on irc.thishost.my.domain, then do it this way: * redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667 * my need was that: * addr1.first.domain 6667 redirects to irc.first.net port 6667 * and addr2.second.domain 6667 redirects to irc.second.net port 6667 * while addr1 and addr2 are the same maschine and the ports can be equal. * * enjoy it! * - thomas , * * btw: i tried without success implementing code for the following scenario: * redir --force_addr irc.fu-berlin.de 6667 6667 * if "--force_addr" is given and a user connects to my system, that address * of my system will be used on the outgoing connection that the user * connected to. * i was not successful to determine, to which of my addresses the user * has connected. */ /* 990320 added support for ftp connection done by the client, now this code * should work for all ftp clients. * * - harald */ /* 991221 added options to simulate a slow connection and to limit * bandwidth. * * - Emmanuel Chantréau */ #define VERSION "2.2.1-W" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCP_WRAPPERS #include #endif #define debug(x) if (dodebug) fprintf(stderr, x) #define debug1(x,y) if (dodebug) fprintf(stderr, x, y) /* let's set up some globals... */ int dodebug = 0; int dosyslog = 0; unsigned char reuse_addr = 1; unsigned char linger_opt = 0; char * bind_addr = NULL; struct sockaddr_in addr_out; int timeout = 0; #ifndef NO_FTP int ftp = 0; #endif int transproxy = 0; #ifndef NO_SHAPER int max_bandwidth = 0; int random_wait = 0; int wait_in_out=3; /* bit 0: wait for "in", bit 1: wait for "out" */ int wait_in=1; int wait_out=1; #endif unsigned int bufsize=4096; char *connect_str = NULL; /* CONNECT string passed to proxy */ char * ident = NULL; #ifndef NO_FTP /* what ftp to redirect */ #define FTP_PORT 1 #define FTP_PASV 2 #endif #ifdef USE_TCP_WRAPPERS struct request_info request; int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* USE_TCP_WRAPPERS */ #ifdef NEED_STRRCHR #define strrchr rindex #endif /* NEED_STRRCHR */ #define REDIR_IN 1 #define REDIR_OUT 0 /* prototype anything needing it */ void do_accept(int servsock, struct sockaddr_in *target); int bindsock(char *addr, int port, int fail); #ifndef NO_SHAPER /* Used in this program to write something in a socket, it has the same parameters and return value as "write", but with the flag "in": true if it's the "in" socket and false if it's the "out" socket */ static ssize_t redir_write (int fd, const void *buf, size_t size, int in) { ssize_t result; int wait; wait=in ? wait_in : wait_out; if( random_wait > 0 && wait) { fd_set empty; struct timeval waitbw; /* for bandwidth */ int rand_time; FD_ZERO(&empty); rand_time=rand()%(random_wait*2); debug1("random wait: %u\n", rand_time); waitbw.tv_sec=rand_time/1000; waitbw.tv_usec=rand_time%1000; select (1, &empty, NULL, NULL, &waitbw); } result=write(fd, buf, size); if( max_bandwidth > 0 && wait) { fd_set empty; unsigned long bits; struct timeval waitbw; /* for bandwidth */ FD_ZERO(&empty); /* wait to be sure tu be below the allowed bandwidth */ bits=size*8; debug1("bandwidth wait: %lu\n", 1000*bits/max_bandwidth); waitbw.tv_sec=bits/max_bandwidth; waitbw.tv_usec=(1000*(bits%max_bandwidth))/max_bandwidth; select (1, &empty, NULL, NULL, &waitbw); } return result; } #else /* macro if traffic shaper is disabled */ #define redir_write(fd, buf, size, in) write(fd, buf,size) #endif #ifdef NEED_STRDUP char * strdup(char * str) { char * result; if (result = (char *) malloc(strlen(str) + 1)) strcpy(result, str); return result; } #endif /* NEED_STRDUP */ void redir_usage(char *name) { fprintf(stderr,"usage:\n"); fprintf(stderr, "\t%s --lport= --cport= [options]\n", name); // fprintf(stderr, "\t%s --inetd --cport=\n", name); fprintf(stderr, "\n\tOptions are:-\n"); fprintf(stderr, "\t\t-l=\t\tport to listen on\n"); fprintf(stderr, "\t\t-a=IP\t\taddress of interface to listen on\n"); fprintf(stderr, "\t\t-r=\t\tport to connect to\n"); fprintf(stderr, "\t\t-c=\t\tremote host to connect to\n"); fprintf(stderr, "\t\t-i\t\trun from inetd\n"); fprintf(stderr, "\t\t-d\t\toutput debugging info\n"); fprintf(stderr, "\t\t-t=\tset timeout to n seconds\n"); fprintf(stderr, "\t\t-s\tlog messages to syslog\n"); fprintf(stderr, "\t\t-n=\ttag syslog messages with 'str'\n"); fprintf(stderr, "\t\t-x=\tCONNECT string passed to proxy server\n"); #ifdef USE_TCP_WRAPPERS fprintf(stderr, "\t\t \tAlso used as service name for TCP wrappers\n"); #endif /* USE_TCP_WRAPPERS */ fprintf(stderr, "\t\t-b=IP\tbind() outgoing IP to given addr\n"); #ifndef NO_FTP fprintf(stderr, "\t\t-f=\t\tredirect ftp connections\n"); fprintf(stderr, "\t\t\twhere type is either port, pasv, both\n"); #endif fprintf(stderr, "\t\t-p\trun in linux's transparent proxy mode\n"); #ifndef NO_SHAPER /* options for bandwidth */ fprintf(stderr, "\t\t-z=\tsize of the buffer\n"); fprintf(stderr, "\t\t-m=\tlimit the bandwidth\n"); fprintf(stderr, "\t\t-w=\twait before each packet\n"); fprintf(stderr, "\t\t-o=\t1 wait for in, 2 out, 3 in&out\n"); /* end options for bandwidth */ #endif fprintf(stderr, "\n\tVersion %s.\n", VERSION); #ifdef SHADE_PS fprintf(stderr, "\tShade opt enabled.\n"); #endif exit(2); } void parse_args(int argc, char * argv[], char ** target_addr, int * target_port, char ** local_addr, int * local_port, int * timeout, int * dodebug, int * inetd, int * dosyslog, char ** bind_addr, #ifndef NO_FTP int * ftp, #endif int *transproxy, #ifndef NO_SHAPER unsigned int * bufsize, int * max_bandwidth, int * random_wait, int * wait_in_out, #endif char **connect_str) { /* static struct option long_options[] = { {"lport", required_argument, 0, 'l'}, {"laddr", required_argument, 0, 'a'}, {"cport", required_argument, 0, 'r'}, {"caddr", required_argument, 0, 'c'}, {"bind_addr", required_argument, 0, 'b'}, {"debug", no_argument, 0, 'd'}, {"timeout", required_argument, 0, 't'}, {"inetd", no_argument, 0, 'i'}, {"ident", required_argument, 0, 'n'}, {"name", required_argument, 0, 'n'}, {"syslog", no_argument, 0, 's'}, {"ftp", required_argument, 0, 'f'}, {"transproxy", no_argument, 0, 'p'}, {"connect", required_argument, 0, 'x'}, {"bufsize", required_argument, 0, 'z'}, {"max_bandwidth", required_argument, 0, 'm'}, {"random_wait", required_argument, 0, 'w'}, {"wait_in_out", required_argument, 0, 'o'}, {0,0,0,0} }; */ int option_index = 0; extern int optind; int opt; struct servent *portdesc; char *lport = NULL; char *tport = NULL; #ifndef NO_FTP char *ftp_type = NULL; #endif *local_addr = NULL; *target_addr = NULL; *target_port = 0; *local_port = 0; while ((opt = getopt(argc, argv, "disfpn:t:b:a:l:r:c:x:z:m:w:o:" ))!= -1) { switch (opt) { case 'x': *connect_str = strdup(optarg); break; case 'a': *local_addr = strdup(optarg); break; case 'l': lport = optarg; break; case 'r': tport = optarg; break; case 'c': *target_addr = strdup(optarg); break; case 'b': *bind_addr = strdup(optarg); break; case 'd': (*dodebug)++; break; case 't': *timeout = atol(optarg); break; case 'i': (*inetd)++; break; case 'n': /* This is the ident which is added to syslog messages */ ident = strdup(optarg); break; case 's': (*dosyslog)++; break; #ifndef NO_FTP case 'f': ftp_type = optarg; if(!ftp_type) { redir_usage(argv[0]); exit(1); } break; #endif case 'p': (*transproxy)++; break; #ifndef NO_SHAPER case 'z': *bufsize = (unsigned int)atol(optarg); break; case 'm': *max_bandwidth = atol(optarg); break; case 'w': *random_wait = atol(optarg); break; case 'o': *wait_in_out = atol(optarg); wait_in=*wait_in_out & 1; wait_out=*wait_in_out & 2; break; #endif default: redir_usage(argv[0]); exit(1); break; } } if(tport == NULL) { redir_usage(argv[0]); exit(1); } if ((portdesc = getservbyname(tport, "tcp")) != NULL) { *target_port = ntohs(portdesc->s_port); } else { *target_port = atol(tport); } /* only check local port if not running from inetd */ if(!(*inetd)) { if(lport == NULL) { redir_usage(argv[0]); exit(1); } if ((portdesc = getservbyname(lport, "tcp")) != NULL) *local_port = ntohs(portdesc->s_port); else *local_port = atol(lport); } /* if *inetd */ if (!ident) { if ((ident = (char *) strrchr(argv[0], '/'))) { ident++; } else { ident = argv[0]; } } #ifndef NO_FTP /* some kind of ftp being forwarded? */ if(ftp_type) { if(!strncasecmp(ftp_type, "port", 4)) *ftp = FTP_PORT; else if(!strncasecmp(ftp_type, "pasv", 4)) *ftp = FTP_PASV; else if(!strncasecmp(ftp_type, "both", 4)) *ftp = FTP_PORT | FTP_PASV; else { redir_usage(argv[0]); exit(1); } } #endif openlog(ident, LOG_PID, LOG_DAEMON); return; } #ifndef NO_FTP /* with the --ftp option, this one changes passive mode replies from the ftp server to point to a new redirector which we spawn, now it also change the PORT commando when the client accept the dataconnection */ void ftp_clean(int send, char *buf, unsigned long *bytes, int ftpsrv) { char *port_start; int rporthi, lporthi; int lportlo, rportlo; int lport, rport; int remip[4]; int localsock; int socksize = sizeof(struct sockaddr_in); struct sockaddr_in newsession; struct sockaddr_in sockname; if (ftpsrv == 0) { /* is this a port commando ? */ if(strncmp(buf, "PORT", 4)) { redir_write(send, buf, (*bytes), REDIR_OUT); return; } /* parse the old address out of the buffer */ port_start = strchr(buf, ' '); sscanf(port_start, " %d,%d,%d,%d,%d,%d", &remip[0], &remip[1], &remip[2], &remip[3], &rporthi, &rportlo); } else { /* is this a passive mode return ? */ if(strncmp(buf, "227", 3)) { redir_write(send, buf, (*bytes), REDIR_OUT); return; } /* parse the old address out of the buffer */ port_start = strchr(buf, '('); sscanf(port_start, "(%d,%d,%d,%d,%d,%d", &remip[0], &remip[1], &remip[2], &remip[3], &rporthi, &rportlo); } /* get the outside interface so we can listen */ if(getsockname(send, (struct sockaddr *)&sockname, &socksize) != 0) { perror("getsockname"); exit(1); } rport = (rporthi << 8) | rportlo; /* we need to listen on a port for the incoming connection. we will use the port 0, so let the system pick one. */ localsock = bindsock(inet_ntoa(sockname.sin_addr), 0, 1); /* get the real info */ if(getsockname(localsock, (struct sockaddr *)&sockname, &socksize) < 0) { perror("getsockname"); if (dosyslog) syslog(LOG_ERR, "getsockname failed: %m"); exit(1); } lport = ntohs(sockname.sin_port); lporthi=(lport >> 8 ) & 0xff; lportlo=lport & 0xff; /* check to see if we bound */ if(localsock == -1) { fprintf(stderr, "ftp: unable to bind new listening address\n"); exit(1); } if (ftpsrv == 0) { /* send the new port and ipaddress to the server */ (*bytes) = sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\n", sockname.sin_addr.s_addr & 0xff, (sockname.sin_addr.s_addr >> 8) & 0xff, (sockname.sin_addr.s_addr >> 16) & 0xff, sockname.sin_addr.s_addr >> 24, lporthi, lportlo); } else { /* send the new port and ipaddress to the client */ (*bytes) = sprintf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n", sockname.sin_addr.s_addr & 0xff, (sockname.sin_addr.s_addr >> 8) & 0xff, (sockname.sin_addr.s_addr >> 16) & 0xff, sockname.sin_addr.s_addr >> 24, lporthi, lportlo); } newsession.sin_port = htons(rport); newsession.sin_family = AF_INET; newsession.sin_addr.s_addr = remip[0] | (remip[1] << 8) | (remip[2] << 16) | (remip[3] << 24); debug1("ftpdata server ip: %s\n", inet_ntoa(newsession.sin_addr)); debug1("ftpdata server port: %d\n", rport); debug1("listening for ftpdata on port %d\n", lport); debug1("listening for ftpdata on addr %s\n", inet_ntoa(sockname.sin_addr)); /* now that we're bound and listening, we can safely send the new string without fear of them getting a connection refused. */ redir_write(send, buf, (*bytes), REDIR_OUT); /* make a new process to handle the dataconnection correctly, for the PASV mode this isn't a problem because after sending the PASV command, the data connection, get active. For the PORT command the server must send a success, if starting here with the copyloop the success command never arrive the client.*/ switch(fork()) { case -1: /* Error */ syslog(LOG_ERR, "Couldn't fork: %m"); _exit(1); case 0: /* Child */ { /* turn off ftp checking while the data connection is active */ ftp = 0; do_accept(localsock, &newsession); close(localsock); _exit(0); } default: /* Parent */ { close(localsock); } } return; } #endif void copyloop(int insock, int outsock, int timeout_secs) { fd_set iofds; fd_set c_iofds; int max_fd; /* Maximum numbered fd used */ struct timeval timeout; unsigned long bytes; unsigned long bytes_in = 0; unsigned long bytes_out = 0; unsigned int start_time, end_time; char buf[bufsize]; /* Record start time */ start_time = (unsigned int) time(NULL); /* Set up timeout */ timeout.tv_sec = timeout_secs; timeout.tv_usec = 0; /* file descriptor bits */ FD_ZERO(&iofds); FD_SET(insock, &iofds); FD_SET(outsock, &iofds); if (insock > outsock) { max_fd = insock; } else { max_fd = outsock; } debug1("Entering copyloop() - timeout is %d\n", timeout_secs); while(1) { (void) memcpy(&c_iofds, &iofds, sizeof(iofds)); if (select(max_fd + 1, &c_iofds, (fd_set *)0, (fd_set *)0, (timeout_secs ? &timeout : NULL)) <= 0) { /* syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/ break; } if(FD_ISSET(insock, &c_iofds)) { if((bytes = read(insock, buf, sizeof(buf))) <= 0) break; #ifndef NO_FTP if (ftp & FTP_PORT) /* if we're correcting FTP, lookup for a PORT commando in the buffer, if yes change this and establish a new redirector for the data */ ftp_clean(outsock, buf, &bytes,0); else #endif if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes) break; bytes_out += bytes; } if(FD_ISSET(outsock, &c_iofds)) { if((bytes = read(outsock, buf, sizeof(buf))) <= 0) break; /* if we're correcting for PASV on ftp redirections, then fix buf and bytes to have the new address, among other things */ #ifndef NO_FTP if(ftp & FTP_PASV) ftp_clean(insock, buf, &bytes,1); else #endif if(redir_write(insock, buf, bytes, REDIR_IN) != bytes) break; bytes_in += bytes; } } debug("Leaving main copyloop\n"); /* setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); */ shutdown(insock,0); shutdown(outsock,0); close(insock); close(outsock); debug("copyloop - sockets shutdown and closed\n"); end_time = (unsigned int) time(NULL); debug1("copyloop - connect time: %8d seconds\n", end_time - start_time); debug1("copyloop - transfer in: %8ld bytes\n", bytes_in); debug1("copyloop - transfer out: %8ld bytes\n", bytes_out); if (dosyslog) { syslog(LOG_NOTICE, "disconnect %d secs, %ld in %ld out", (end_time - start_time), bytes_in, bytes_out); } return; } void doproxyconnect(int socket) { char buf[128]; int x; /* write CONNECT string to proxy */ sprintf((char *) &buf, "CONNECT %s HTTP/1.0\n\n", connect_str); x = write(socket, (char *) &buf, strlen(buf)); if (x < 1) { perror("doproxyconnect: failed"); exit(1); } /* now read result */ x = read(socket, (char *) &buf, sizeof(buf)); if (x < 1) { perror("doproxyconnect: failed reading fra proxy"); exit(1); } /* no more error checking for now -- something should be added later */ /* HTTP/1.0 200 Connection established */ } /* lwait for a connection and move into copyloop... again, ftp redir will call this, so we don't dupilcate it. */ void do_accept(int servsock, struct sockaddr_in *target) { int clisock; int targetsock; struct sockaddr_in client; int clientlen = sizeof(client); int accept_errno; debug("top of accept loop\n"); if ((clisock = accept(servsock, (struct sockaddr *) &client, &clientlen)) < 0) { accept_errno = errno; perror("server: accept"); if (dosyslog) syslog(LOG_ERR, "accept failed: %m"); /* determine if this error is fatal */ switch(accept_errno) { /* non-fatal errors */ case EHOSTUNREACH: case ECONNRESET: case ETIMEDOUT: return; /* all other errors assumed fatal */ default: exit(1); } } debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); /* * Double fork here so we don't have to wait later * This detaches us from our parent so that the parent * does not need to pick up dead kids later. * * This needs to be done before the hosts_access stuff, because * extended hosts_access options expect to be run from a child. */ switch(fork()) { case -1: /* Error */ perror("(server) fork"); if (dosyslog) syslog(LOG_ERR, "(server) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ { int status; /* Wait for child (who has forked off grandchild) */ (void) wait(&status); /* Close sockets to prevent confusion */ close(clisock); return; } } /* We are now the first child. Fork again and exit */ switch(fork()) { case -1: /* Error */ perror("(child) fork"); if (dosyslog) syslog(LOG_ERR, "(child) fork failed: %m"); _exit(1); case 0: /* Child */ break; default: /* Parent */ _exit(0); } /* We are now the grandchild */ #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) { refuse(&request); _exit(0); } if (dosyslog) syslog(LOG_INFO, "accepted connect from %s", eval_client(&request)); #endif /* USE_TCP_WRAPPERS */ if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("target: socket"); if (dosyslog) syslog(LOG_ERR, "socket failed: %m"); _exit(1); } if(transproxy) { memcpy(&addr_out, &client, sizeof(struct sockaddr_in)); addr_out.sin_port = 0; } if (bind_addr || transproxy) { /* this only makes sense if an outgoing IP addr has been forced; * at this point, we have a valid targetsock to bind() to.. */ /* also, if we're in transparent proxy mode, this option never makes sense */ if (bind(targetsock, (struct sockaddr *) &addr_out, sizeof(struct sockaddr_in)) < 0) { perror("bind_addr: cannot bind to forcerd outgoing addr"); /* the port parameter fetch the really port we are listening, it should only be different if the input value is 0 (let the system pick a port) */ if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); _exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr)); } if (connect(targetsock, (struct sockaddr *) target, sizeof(struct sockaddr_in)) < 0) { perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); _exit(1); } debug1("connected to %s\n", inet_ntoa(target->sin_addr)); /* thanks to Anders Vannman for the fix to make proper syslogging happen here... */ if (dosyslog) { char tmp1[20], tmp2[20]; strcpy(tmp1, inet_ntoa(client.sin_addr)); strcpy(tmp2, inet_ntoa(target->sin_addr)); syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", tmp1, client.sin_port, tmp2, target->sin_port); } /* do proxy stuff */ if (connect_str) doproxyconnect(targetsock); #ifndef NO_SHAPER /* initialise random number if necessary */ if( random_wait > 0 ) { srand(getpid()); } #endif copyloop(clisock, targetsock, timeout); exit(0); /* Exit after copy */ } /* bind to a new socket, we do this out here because passive-fixups are going to call it too, and there's no sense dupliciting the code. */ /* fail is true if we should just return a -1 on error, false if we should bail. */ int bindsock(char *addr, int port, int fail) { int servsock; struct sockaddr_in server; /* * Get a socket to work with. This socket will * be in the Internet domain, and will be a * stream socket. */ if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if(fail) { return -1; } else { perror("server: socket"); if (dosyslog) syslog(LOG_ERR, "socket failed: %m"); exit(1); } } memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(port); if (addr != NULL) { struct hostent *hp; debug1("listening on %s\n", addr); if ((hp = gethostbyname(addr)) == NULL) { fprintf(stderr, "%s: cannot resolve hostname.\n", addr); exit(1); } memcpy(&server.sin_addr, hp->h_addr, hp->h_length); } else { debug("local IP is default\n"); server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); setsockopt(servsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); /* * Try to bind the address to the socket. */ if (bind(servsock, (struct sockaddr *) &server, sizeof(server)) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: bind"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } } /* * Listen on the socket. */ if (listen(servsock, 10) < 0) { if(fail) { close(servsock); return -1; } else { perror("server: listen"); if (dosyslog) syslog(LOG_ERR, "listen failed: %m"); exit(1); } } return servsock; } int main(int argc, char *argv[]) { struct sockaddr_in target; char *target_addr; int target_port; char *local_addr; int local_port; int inetd = 0; char * target_ip; char * ip_to_target; int i,j; debug("parse args\n"); parse_args(argc, argv, &target_addr, &target_port, &local_addr, &local_port, &timeout, &dodebug, &inetd, &dosyslog, &bind_addr, #ifndef NO_FTP &ftp, #endif &transproxy, #ifndef NO_SHAPER &bufsize, &max_bandwidth, &random_wait, &wait_in_out, #endif &connect_str); #ifdef SHADE_PS for (i=0;i=0;j--) argv[i][j] = 0; } } #endif /* Set up target */ target.sin_family = AF_INET; target.sin_port = htons(target_port); if (target_addr != NULL) { struct hostent *hp; debug1("target is %s\n", target_addr); if ((hp = gethostbyname(target_addr)) == NULL) { fprintf(stderr, "%s: host unknown.\n", target_addr); exit(1); } memcpy(&target.sin_addr, hp->h_addr, hp->h_length); } else { debug("target is default\n"); target.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); } target_ip = strdup(inet_ntoa(target.sin_addr)); debug1("target IP address is %s\n", target_ip); debug1("target port is %d\n", target_port); /* Set up outgoing IP addr (optional); * we have to wait for bind until targetsock = socket() is done */ if (bind_addr && !transproxy) { struct hostent *hp; fprintf(stderr, "bind_addr is %s\n", bind_addr); addr_out.sin_family = AF_INET; addr_out.sin_port = 0; if ((hp = gethostbyname(bind_addr)) == NULL) { fprintf(stderr, "%s: cannot resolve forced outgoing IP address.\n", bind_addr); exit(1); } memcpy(&addr_out.sin_addr, hp->h_addr, hp->h_length); ip_to_target = strdup(inet_ntoa(addr_out.sin_addr)); debug1("IP address for target is %s\n", ip_to_target); } if (inetd) { int targetsock; struct sockaddr_in client; int client_size = sizeof(client); #ifdef USE_TCP_WRAPPERS request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0); sock_host(&request); sock_hostname(&request); sock_hostaddr(&request); if (!hosts_access(&request)) refuse(&request); #endif /* USE_TCP_WRAPPERS */ if (!getpeername(0, (struct sockaddr *) &client, &client_size)) { debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); } if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("target: socket"); if (dosyslog) syslog(LOG_ERR, "targetsock failed: %m"); exit(1); } if(transproxy) { memcpy(&addr_out, &client, sizeof(struct sockaddr_in)); addr_out.sin_port = 0; } if (bind_addr || transproxy) { /* this only makes sense if an outgoing IP addr has been forced; * at this point, we have a valid targetsock to bind() to.. */ if (bind(targetsock, (struct sockaddr *) &addr_out, sizeof(addr_out)) < 0) { perror("bind_addr: cannot bind to forcerd outgoing addr"); if (dosyslog) syslog(LOG_ERR, "bind failed: %m"); exit(1); } debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr)); } if (connect(targetsock, (struct sockaddr *) &target, sizeof(target)) < 0) { perror("target: connect"); if (dosyslog) syslog(LOG_ERR, "connect failed: %m"); exit(1); } if (dosyslog) { syslog(LOG_NOTICE, "connecting %s/%d to %s/%d", inet_ntoa(client.sin_addr), client.sin_port, target_ip, target.sin_port); } /* Just start copying - one side of the loop is stdin - 0 */ copyloop(0, targetsock, timeout); } else { int servsock; if(local_addr) servsock = bindsock(local_addr, local_port, 0); else servsock = bindsock(NULL, local_port, 0); /* * Accept connections. When we accept one, ns * will be connected to the client. client will * contain the address of the client. */ while (1) do_accept(servsock, &target); } /* this should really never be reached */ exit(0); }