/* $Id: redir.cc,v 1.15 2005/12/14 21:11:57 jacques Exp $ * * 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. * * Then modified a lot by Jacques Gelinas (jack@solucorp.qc.ca) * to support ftp proxying and session recording and few other bug fixes * * redir is released under the GNU General Public license, * version 2, or at your discretion, any later version. * */ /* Redir, the code to which is below, is actually a horrible hack of my * other cool network utility, daemon, which is actually a horrible hack * of ora's using C sample code, 12.2.c. But, hey, they do something. * (and that's the key.) * -- Sammy (sammy@freenet.akron.oh.us) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define debug(x) if (dodebug) fprintf(stderr, x) #define debug1(x,y) if (dodebug) fprintf(stderr, x, y) int dodebug = 0; int dosyslog = 0; char *record_prefix = NULL; #ifdef NEED_STRRCHR #define strrchr rindex #endif /* NEED_STRRCHR */ #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 [options] [remote-host,...] listen_port connect_port\n", name); fprintf(stderr, "\t%s --inetd [options] [remote-host,...] connect_port\n", name); fprintf(stderr, "\n\tOptions are:-\n"); fprintf(stderr, "\t\t--bind\t\tListen on a specific address instead of 0.0.0.0\n"); fprintf(stderr, "\t\t--inetd\t\trun from inetd\n"); fprintf(stderr, "\t\t--debug\t\toutput debugging info\n"); fprintf(stderr, "\t\t--ftp\t\tAct as an ftp proxy\n"); fprintf(stderr, "\t\t--record path_prefix\tRecord the sessions\n"); fprintf(stderr, "\t\t--retry count\tRetry the connection N time\n"); fprintf(stderr, "\t\t--timeout=\tset timeout to n seconds\n"); fprintf(stderr, "\t\t--con_timeout=\tset connection timeout to n seconds\n"); fprintf(stderr, "\t\t--proxy\t\tConfiguration file for dynamic ftp proxy\n"); fprintf(stderr, "\t\t--syslog=\tlog messages to syslog\n"); fprintf(stderr, "\t\t--name=\ttag syslog messages with 'str'\n"); fprintf(stderr, "\t\t--nodelay\tSet TCP_NODELAY option on sockets\n"); fprintf(stderr, "\t\t--byte\t\tSend received buffer as single bytes\n"); fprintf(stderr, "\t\t--sync\t\tSync the record files after each read/write\n"); fprintf(stderr, "\n\tVersion %s - $Id: redir.cc,v 1.15 2005/12/14 21:11:57 jacques Exp $\n", PACKAGE_REV); exit(2); } static char *redir_conf = NULL; struct REDIR_ARGS{ int timeout; // Session timeout. Disconnect if the client is idle // too long int con_timeout; // Connection timeout int retry; int target_port; int local_port; int dodebug; int inetd; int doftp; int bytemode; int sync; int nodelay; char pidfile[PATH_MAX]; char *target_addr; char *bind_addr; REDIR_ARGS(){ con_timeout = 0; timeout = 0; retry = 1; target_port = 0; local_port = 0; dodebug = 0; inetd = 0; doftp = 0; bytemode = 0; sync = 0; nodelay = 0; pidfile[0] = '\0'; target_addr = NULL; bind_addr = "0.0.0.0"; } }; void parse_args( int argc, char * argv[], REDIR_ARGS &opts) { static struct option long_options[] = { {"bind",required_argument,0,'B'}, {"con_timeout", required_argument, 0, 'T'}, {"debug", no_argument, 0, 'd'}, {"ftp", no_argument, 0, 'f'}, {"record", required_argument, 0, 'r'}, {"retry", required_argument, 0, 'R'}, {"timeout", required_argument, 0, 't'}, {"inetd", no_argument, 0, 'i'}, {"ident", required_argument, 0, 'n'}, {"name", required_argument, 0, 'n'}, {"nodelay", no_argument, 0, 'o'}, {"proxy", required_argument, 0, 'p'}, {"syslog", no_argument, 0, 's'}, {"pidfile", required_argument, 0, 'P'}, {"byte", no_argument, 0, 'b'}, {"sync", no_argument, 0, 'S'}, {0,0,0,0} /* End marker */ }; int option_index = 0; extern int optind; int opt; struct servent *portdesc; char * ident = NULL; while ((opt = getopt_long(argc, argv, "disn:ot:", long_options, &option_index)) != -1) { switch (opt) { case 'B': opts.bind_addr = optarg; break; case 'd': opts.dodebug++; break; case 'f': opts.doftp++; break; case 'b': opts.bytemode++; break; case 'S': opts.sync++; break; case 'o': opts.nodelay++; break; case 'p': redir_conf = optarg; break; case 'r': record_prefix = optarg; break; case 'R': opts.retry = atoi(optarg); break; case 'P': strcpy (opts.pidfile,optarg); break; case 't': opts.timeout = atol(optarg); break; case 'T': opts.con_timeout = atol(optarg); break; case 'i': opts.inetd++; break; case 'n': /* This is the ident which is added to syslog messages */ ident = optarg; break; case 's': dosyslog++; break; default: redir_usage(argv[0]); exit(1); break; } } /* Check number of args */ if (opts.inetd) { if (((argc - optind) == 1) || ((argc - optind) == 2)) { if ((argc - optind) == 2) { opts.target_addr = argv[optind++]; } opts.local_port = 0; if ((portdesc = getservbyname(argv[optind], "tcp")) != NULL) { opts.target_port = ntohs(portdesc->s_port); } else { opts.target_port = atol(argv[optind]); } optind++; } else { redir_usage(argv[0]); exit(1); } } else { if (((argc - optind) == 2) || ((argc - optind) == 3)) { if ((argc - optind) == 3) { opts.target_addr = argv[optind++]; } if ((portdesc = getservbyname(argv[optind], "tcp")) != NULL) { opts.local_port = ntohs(portdesc->s_port); } else { opts.local_port = atol(argv[optind]); } optind++; if ((portdesc = getservbyname(argv[optind], "tcp")) != NULL) { opts.target_port = ntohs(portdesc->s_port); } else { opts.target_port = atol(argv[optind]); } optind++; } else { redir_usage(argv[0]); exit(1); } } if (dosyslog) { if (!ident) { if ((ident = strrchr(argv[0], '/'))!=NULL) { ident++; } else { ident = argv[0]; } } openlog(ident, LOG_PID, LOG_DAEMON); } return; } static struct sockaddr_in target,client; struct FTP_INFO{ char line[4096]; // Buffer to reassemble lines // received from the client int len; // Number of bytes in line[] int insock; // File handles to receive int outsock; // and transmit the data to the client int waitsock; // Listening socket for the data connection unsigned long addr; // Address of the ftp client int port; // Port to connect to // Or address of the ftp server in passive // mode char is_passive; // Is it a passive connection ? }; /* Prepare de listening socket, which will be used to either accept a connection from the server (active mode) or from the client (passive mode) */ static void wait_ftpdata ( int outsock, struct FTP_INFO *info) { struct sockaddr_in server; /* * Get a socket to listen from the server. We will use the * port obtained for this socket to format a different PORT * command. */ if ((info->waitsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("server-port: socket"); exit(1); } server.sin_family = AF_INET; server.sin_port = 0; server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); /* * Try to bind the address to the socket. */ if (bind(info->waitsock, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("server-port: bind"); exit(1); } setsockopt(info->waitsock, SOL_SOCKET, SO_LINGER, 0, sizeof(SO_LINGER)); /* * Now we can send a PORT command to the ftp server so it connect * to us */ { char portline[100]; struct sockaddr_in tmp; socklen_t tmp_len = sizeof(tmp); int len; unsigned long myaddr; int myport; if (getsockname (outsock,(struct sockaddr *)&tmp,&tmp_len) <0){ perror ("server-port: getsockname"); exit (1); } myaddr = ntohl (tmp.sin_addr.s_addr); if (getsockname (info->waitsock,(struct sockaddr *)&tmp,&tmp_len) <0){ perror ("server-myport: getsockname"); exit (1); } myport = ntohs (tmp.sin_port); if (info->is_passive){ len = sprintf (portline,"227 ENtering Passive Mode (%ld,%ld,%ld,%ld,%d,%d)\r\n" ,(myaddr>>24)&0xff,(myaddr>>16)&0xff,(myaddr>>8)&0xff ,(myaddr&0xff) ,myport>>8,myport&0xff); }else{ len = sprintf (portline,"PORT %ld,%ld,%ld,%ld,%d,%d\r\n" ,(myaddr>>24)&0xff,(myaddr>>16)&0xff,(myaddr>>8)&0xff ,(myaddr&0xff) ,myport>>8,myport&0xff); } syslog (LOG_ERR,portline); write (outsock,portline,len); } /* * Listen on the socket. */ if (listen(info->waitsock, 1) < 0) { perror("server-port: listen"); exit(1); } } /* Parse de IP + port sequence (6 number separated by a comma) The first 4 numbers represent the IP number. The last 2 represent the port. */ static void parse_ftp_port( const char *portdata, struct FTP_INFO *info, int tb[6]) { const char *start = portdata; int i; for (i=0; i<6; i++){ tb[i] = atoi(portdata); while (isdigit(*portdata)) portdata++; if (i<5 && *portdata != ','){ /* Some protocol error */ syslog (LOG_ERR,"Invalid FTP PORT command '%s'",start); exit (-1); } portdata++; } info->addr = (tb[0]<<24) + (tb[1] << 16) + (tb[2]<<8) + tb[3]; info->port = tb[4] *256 + tb[5]; } /* Analyse the PORT command, build a listening socket and wait for a connection from the server */ static void process_ftp_port ( int insock, int outsock, const char *portdata, struct FTP_INFO *info) { /* The data of the PORT command is the IP number followed by the port. All this is expressed as 6 decimal numbers separated by commas. The last two numbers are combined to create the port. */ int tb[6]; while (isspace(*portdata)) portdata++; syslog (LOG_NOTICE,"port command :%s:",portdata); parse_ftp_port(portdata,info,tb); if (info->addr != ntohl(client.sin_addr.s_addr)){ const char *s = "rejected PORT command: Does not point to the ftp client\n"; syslog (LOG_ERR,"%s: %d.%d.%d.%d != %s",s ,tb[0],tb[1],tb[2],tb[3] ,inet_ntoa(client.sin_addr)); write (insock,"500 ",4); write (insock,s,strlen(s)); exit (1); } info->is_passive = 0; wait_ftpdata (outsock, info); } /* Send the pasv command to the server, wait for the answer and establish a connection to it and pass a modified version of the answer to the client. It also create a listening socket so the client can connect to it. */ static void process_ftp_pasv ( int insock, int outsock, struct FTP_INFO *info) { char buf[1000]; int nb; write (outsock,"PASV\r\n",6); nb = read (outsock,buf,1000); if (nb > 0){ if (strncmp(buf,"227",3)==0){ char *s = strchr (buf,'('); if (s != NULL){ int tb[6]; s++; parse_ftp_port(s,info,tb); info->is_passive = 1; wait_ftpdata (insock, info); } } } } static int redir_connect (const char *server, const char *port) { int ret = -1; struct servent *s = getservbyname (port,"tcp"); if (s == NULL){ syslog (LOG_ERR,"unknown port: %s\n",port); }else{ /* Set up target */ struct sockaddr_in; target.sin_family = AF_INET; target.sin_port = s->s_port; struct hostent *hp = gethostbyname(server); if (hp == NULL) { fprintf(stderr, "%s: host unknown.\n",server); syslog (LOG_ERR,"%s: host unknown",server); _exit(1); } memcpy(&target.sin_addr, hp->h_addr, hp->h_length); ret = socket(AF_INET, SOCK_STREAM, 0); if (ret < 0) { perror("target: socket"); _exit(1); } if (connect(ret, (struct sockaddr *) &target, sizeof(target)) < 0) { perror("target: connect"); _exit(1); } } return ret; } static void process_ftp_user ( int insock, int &outsock, const char *user, struct FTP_INFO *info) { /* The user may contain a @domain and the domain is used to lookup a target server. */ if (redir_conf != NULL){ while (isspace (*user)) user++; // syslog (LOG_ERR,"user :%s:",user); // fprintf (stderr,"user :%s:\n",user); const char *pt = strchr(user,'@'); if (pt == NULL){ const char *err = "530 no domain specified: user@domain required\n"; write (insock,err,strlen(err)); _exit (1); }else{ pt++; FILE *fin = fopen (redir_conf,"r"); if (fin == NULL){ close (outsock); syslog (LOG_ERR,"Can't open file %s (%m)",redir_conf); _exit (1); }else{ char line[200]; while (fgets(line,sizeof(line)-1,fin)!=NULL){ char domain[200],server[200]; if (sscanf (line,"%s %s\n",domain,server)==2){ if (strcasecmp(domain,pt)==0){ syslog (LOG_ERR,"redir_connect %s",server); int sock = redir_connect (server,"ftp"); if (sock != -1){ dup2 (sock,outsock); close (sock); { char buf[1000]; int len = read (outsock,buf,sizeof(buf)-1); if (len > 0){ buf[len] = '\0'; // fprintf (stderr,"Recoit1: %s\n",buf); } } char tmp[strlen(user)+1+4]; int len = (int)(pt-user)-1; len = sprintf (tmp,"USER %*.*s\n",len,len,user); // fprintf (stderr,"write :%s:\n",tmp); write (outsock,tmp,len); }else{ close (outsock); _exit (1); } break; } } } fclose (fin); } } } } /* Parse ftp commands and react on the port command to create another copyloop process */ static void parse_ftp_command ( const char *buf, int buflen, struct FTP_INFO *finfo, int insock, int &outsock) { const char *src = buf; char *dst = finfo->line + finfo->len; int i; for (i=0; iline; while (isspace(*dst)) dst++; if (strncasecmp(dst,"PORT",4)==0 && isspace(dst[4])){ process_ftp_port (insock,outsock,dst+4,finfo); }else if (strncasecmp(dst,"PASV",4)==0 && dst[4] < ' '){ process_ftp_pasv (insock,outsock,finfo); }else if (strncasecmp(dst,"USER",4)==0 && isspace(dst[4])){ if (redir_conf != NULL){ process_ftp_user (insock,outsock,dst+4,finfo); }else{ finfo->line[finfo->len++] = '\n'; write (outsock,finfo->line,finfo->len); } }else{ // fprintf (stderr,"SEND :%s:\n",dst); finfo->line[finfo->len++] = '\n'; write (outsock,finfo->line,finfo->len); } finfo->len= 0; dst = finfo->line; finfo->line[0] = '\0'; }else if (finfo->len == sizeof(finfo->line)-2){ syslog (LOG_NOTICE,"ftp command line overflow, exiting"); exit (-1); }else{ *dst++ = *src++; finfo->len++; } } } static void close_ftp_data ( struct FTP_INFO *finfo, fd_set *set) { FD_CLR (finfo->insock,set); FD_CLR (finfo->outsock,set); FD_CLR (finfo->waitsock,set); close (finfo->outsock); close (finfo->insock); close (finfo->waitsock); finfo->outsock = finfo->insock = finfo->waitsock = -1; } /* Output a record in a log file with proper time stamps */ static void redir_record (FILE *fout, char buf[], int bytes, bool sync) { // The sequence is easily parseable and viewable. The // *** is there so we can tell the data apart from the // timestamp. Just a helper. Not perfect. The record file // is still a sequential file. struct timeval tv; gettimeofday (&tv,NULL); fprintf (fout,"***%08lx.%08lx:%04d\n",tv.tv_sec,tv.tv_usec,bytes); fwrite (buf,1,bytes,fout); fflush (fout); if (sync){ fsync(fileno(fout)) ; } } static int redir_write ( int fd, const char *buf, int size, bool bytemode) // Write one byte at a time { int ret = -1; if (bytemode){ ret = size; for (int i=0; i outsock) { max_fd = insock; } else { max_fd = outsock; } debug1("Entering copyloop() - timeout is %d\n", timeout_secs); while(1) { fd_set c_iofds; (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; if (record_in != NULL) redir_record (record_in,buf,bytes,sync); if (ftpproxy){ parse_ftp_command (buf,bytes,&finfo,insock,outsock); if (finfo.waitsock != -1){ FD_SET (finfo.waitsock,&iofds); if (finfo.waitsock > max_fd) max_fd = finfo.waitsock; } }else{ if(redir_write(outsock, buf, bytes,bytemode) != bytes) break; bytes_out += bytes; } } if(FD_ISSET(outsock, &c_iofds)) { if((bytes = read(outsock, buf, sizeof(buf))) <= 0) break; if (record_out != NULL) redir_record (record_out,buf,bytes,sync); if (redir_write(insock, buf, bytes,bytemode) != bytes) break; bytes_in += bytes; } if(finfo.outsock != -1 && FD_ISSET(finfo.outsock, &c_iofds)) { if((bytes = read(finfo.outsock, buf, sizeof(buf))) <= 0 || write(finfo.insock, buf, bytes) != bytes){ close_ftp_data (&finfo,&iofds); } } if(finfo.insock != -1 && FD_ISSET(finfo.insock, &c_iofds)) { if((bytes = read(finfo.insock, buf, sizeof(buf))) <= 0 || write(finfo.outsock, buf, bytes) != bytes){ close_ftp_data (&finfo,&iofds); } } if (finfo.waitsock != -1 && FD_ISSET(finfo.waitsock, &c_iofds)) { struct sockaddr_in ftpserv; socklen_t ftpserv_len = sizeof(ftpserv); if ((finfo.insock = accept(finfo.waitsock , (struct sockaddr *) &ftpserv, &ftpserv_len)) < 0) { perror("server-port: accept"); exit(1); } if (finfo.is_passive){ syslog (LOG_NOTICE,"ftp-data: Connection from client %s" ,inet_ntoa (ftpserv.sin_addr)); }else{ syslog (LOG_NOTICE,"ftp-data: Connection from server %s" ,inet_ntoa (ftpserv.sin_addr)); } if (!finfo.is_passive && ftpserv.sin_addr.s_addr != target.sin_addr.s_addr){ syslog (LOG_CRIT,"server-port: Connection from invalid server %s" ,inet_ntoa (ftpserv.sin_addr)); close (finfo.insock); finfo.insock = -1; }else{ struct sockaddr_in ftpclient; /* * Now we must connect to the ftp client using the * port it supplied */ if ((finfo.outsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("ftpclient: socket"); exit(1); } if (finfo.is_passive){ syslog (LOG_NOTICE,"ftp-data: Connecting to server %ld.%ld.%ld.%ld/%d" ,finfo.addr>>24,(finfo.addr>>16)&0xff ,(finfo.addr>>8)&0xff,finfo.addr&0xff ,finfo.port); }else{ syslog (LOG_NOTICE,"ftp-data: Connecting to client %ld.%ld.%ld.%ld/%d" ,finfo.addr>>24,(finfo.addr>>16)&0xff ,(finfo.addr>>8)&0xff,finfo.addr&0xff ,finfo.port); } ftpclient.sin_family = AF_INET; ftpclient.sin_port = htons(finfo.port); ftpclient.sin_addr.s_addr = htonl(finfo.addr); if (!finfo.is_passive){ /* We bind the socket to the original ip used by the client to reach us and we also use the port ftp-data. This confusion happen when the client reach us using an IP alias */ struct sockaddr_in me; socklen_t tmp_len=sizeof(me); int opt = 1; if (setsockopt(finfo.outsock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))==-1){ perror ("redir-myport: setsockopt"); exit (1); } if (getsockname (insock,(struct sockaddr *)&me,&tmp_len) <0){ perror ("redir-myport: getsockname"); exit (1); } me.sin_port = htons (20); if (bind(finfo.outsock , (struct sockaddr *) &me ,sizeof(me)) < 0) { perror("redir-bind-port: bind"); exit(1); } } if (connect (finfo.outsock , (struct sockaddr *) &ftpclient ,sizeof(ftpclient)) < 0) { syslog (LOG_ERR,"ftpclient: connect"); perror("ftpclient: connect"); exit(1); } FD_SET (finfo.insock,&iofds); if (finfo.insock > max_fd) max_fd = finfo.insock; FD_SET (finfo.outsock,&iofds); if (finfo.outsock > max_fd) max_fd = finfo.outsock; } } } debug("Leaving main copyloop\n"); { int opton=1,optoff=0; setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &opton, sizeof(opton)); setsockopt(insock, SOL_SOCKET, SO_LINGER, &optoff, sizeof(optoff)); setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &opton, sizeof(opton)); setsockopt(outsock, SOL_SOCKET, SO_LINGER, &optoff, sizeof(optoff)); } 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); } if (record_in != NULL) fclose (record_in); if (record_out != NULL) fclose (record_out); return; } static set pidset; static void fctchild (int ) { pid_t pid ; while ((pid = waitpid(-1, NULL, WNOHANG)) > 0){ pidset.erase(pid) ; } } static void fctterm(int ) { // printf ("fctterm\n"); for (set::iterator it=pidset.begin(); it != pidset.end() ; it++){ printf ("\tKilling process %d\n",*it); kill (*it,SIGTERM); } pidset.clear(); exit(-1); } static void fsig (int) { // fprintf (stderr,"signal\n"); } /* Install the signal handler and set the alarm */ static void redir_setsig(int time_out) { static struct sigaction siga; siga.sa_handler = fsig; sigaction (SIGALRM,&siga,NULL); alarm (time_out); } /* Remove the signal handler and reset the alarm */ static void redir_resetsig () { static struct sigaction siga; siga.sa_handler = SIG_DFL; sigaction (SIGALRM,&siga,NULL); alarm (0); } /* Establish the target connection. Return -1 if any error. */ static int connect_target ( vector &addrs, int retry, int con_timeout) // Connection timeout (seconds) or 0 { int ret = -1; string caddr (inet_ntoa(client.sin_addr)); for (int j=0; ret == -1 && j 0) sleep(1); for (int i=0; ih_addr, hp->h_length); string taddr (inet_ntoa(target.sin_addr)); int targetsock; // fprintf (stderr,"con_timeout = %d\n",con_timeout); redir_setsig (con_timeout); if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog (LOG_ERR,"target: socket"); }else if (connect(targetsock, (struct sockaddr *) &target, sizeof(target)) < 0) { syslog (LOG_ERR,"target: connect to %s/%d %m" ,taddr.c_str(),ntohs(target.sin_port)); close (targetsock); }else{ ret = targetsock; redir_resetsig (); if (dosyslog){ syslog(LOG_NOTICE, "connecting %s/%d to %s/%d" ,caddr.c_str(), ntohs(client.sin_port) ,taddr.c_str(), ntohs(target.sin_port)); } break; } } } } redir_resetsig (); return ret; } int main(int argc, char *argv[]) { signal (SIGCHLD,fctchild); signal (SIGTERM,fctterm); signal (SIGINT,fctterm); debug("parse args\n"); REDIR_ARGS opts; parse_args(argc, argv, opts); if (opts.pidfile[0] != '\0'){ FILE *fout = fopen (opts.pidfile,"w"); if (fout == NULL){ fprintf (stderr,"Can't open pidfile %s (%s)\n",opts.pidfile ,strerror(errno)); }else{ fprintf (fout,"%d\n",getpid()); fclose (fout); } } /* Set up target */ target.sin_family = AF_INET; target.sin_port = htons(opts.target_port); vector addrs; if (opts.target_addr != NULL) { const char *start = opts.target_addr; const char *pt = start; while (*pt != '\0'){ if (*pt == ','){ if (pt > start){ addrs.push_back(string(start,pt)); } pt++; start=pt; }else{ pt++; } } if (pt > start){ addrs.push_back(string(start,pt)); } } else { debug("target is default\n"); addrs.push_back ("0.0.0.0"); } debug1("target port is %d\n", opts.target_port); if (opts.inetd) { int targetsock; socklen_t client_size = sizeof(client); 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 = connect_target(addrs,opts.retry,opts.con_timeout)!=-1){ /* Just start copying - one side of the loop is stdin - 0 */ copyloop(0, targetsock, opts.timeout,opts.doftp,1,opts.bytemode ,opts.sync,opts.nodelay); } } else { int session = 0; // Compute the first unused record file if (record_prefix != NULL){ while (1){ char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s.out.%05d",record_prefix,session+1); struct stat st; if (stat(path,&st)==-1) break; session++; } } 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) { syslog (LOG_ERR,"server: socket"); exit(1); } { int opton = 1, optoff=0; setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &opton, sizeof(opton)); setsockopt(servsock, SOL_SOCKET, SO_LINGER, &optoff, sizeof(optoff)); } server.sin_family = AF_INET; server.sin_port = htons(opts.local_port); server.sin_addr.s_addr = inet_addr(opts.bind_addr); /* * Try to bind the address to the socket. */ if (bind(servsock, (struct sockaddr *) &server ,sizeof(server)) < 0) { perror("server: bind"); exit(1); } /* * Listen on the socket. */ if (listen(servsock, 1) < 0) { perror("server: listen"); exit(1); } /* * Accept connections. When we accept one, ns * will be connected to the client. client will * contain the address of the client. */ while (1) { int clisock; socklen_t clientlen = sizeof(client); debug("top of accept loop\n"); if ((clisock = accept(servsock, (struct sockaddr *) &client, &clientlen)) < 0 && errno != EINTR){ perror("server: accept"); exit(1); } session++; int forkpid = fork(); if (forkpid == 0){ close(servsock) ; signal (SIGCHLD,SIG_DFL); signal (SIGTERM,SIG_DFL); signal (SIGINT,SIG_DFL); debug1("peer IP is %s\n", inet_ntoa(client.sin_addr)); debug1("peer socket is %d\n", client.sin_port); int ret = 1; int targetsock = connect_target(addrs,opts.retry,opts.con_timeout); //fprintf (stderr,"connect %d\n",targetsock); if (targetsock != -1){ copyloop(clisock, targetsock, opts.timeout,opts.doftp ,session,opts.bytemode,opts.sync,opts.nodelay); ret = 0; } _exit(ret); /* Exit after copy */ } else if (forkpid == -1){ syslog (LOG_ERR,"Can't fork (%m)"); }else{ pidset.insert(forkpid); close(clisock); } } } return 0; }