extended gitignore
[rofl0r-portredir.git] / redir.c
blob85ab20afa74c0dd02320b3e44f2e01406da3877b
1 /* $Id$
3 * redir - a utility for redirecting tcp connections
5 * Author: Nigel Metheringham
6 * Nigel.Metheringham@ThePLAnet.net
8 * Based on, but much modified from, code originally written by
9 * sammy@freenet.akron.oh.us - original header is below.
11 * redir is released under the GNU General Public license,
12 * version 2, or at your discretion, any later version.
16 /*
17 * redir is currently maintained by Sam Creasey (sammy@oh.verio.com).
18 * Please send patches, etc. there.
22 /* 980601: dl9sau
23 * added some nice new features:
25 * --bind_addr=my.other.ip.address
26 * forces to use my.other.ip.address for the outgoing connection
28 * you can also specify, that redir listens not on all IP addresses of
29 * your system but only for the given one, i.e.:
30 * if my host has the addresses
31 * irc.thishost.my.domain and mail.thishost.my.domain
32 * but you want that your users do connect for the irc redir service
33 * only on irc.thishost.my.domain, then do it this way:
34 * redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667
35 * my need was that:
36 * addr1.first.domain 6667 redirects to irc.first.net port 6667
37 * and addr2.second.domain 6667 redirects to irc.second.net port 6667
38 * while addr1 and addr2 are the same maschine and the ports can be equal.
40 * enjoy it!
41 * - thomas <thomas@x-berg.in-berlin.de>, <dl9sau@db0tud.ampr.org>
43 * btw: i tried without success implementing code for the following scenario:
44 * redir --force_addr irc.fu-berlin.de 6667 6667
45 * if "--force_addr" is given and a user connects to my system, that address
46 * of my system will be used on the outgoing connection that the user
47 * connected to.
48 * i was not successful to determine, to which of my addresses the user
49 * has connected.
52 /* 990320 added support for ftp connection done by the client, now this code
53 * should work for all ftp clients.
55 * - harald <harald.holzer@eunet.at>
58 /* 991221 added options to simulate a slow connection and to limit
59 * bandwidth.
61 * - Emmanuel Chantr�au <echant@maretmanu.org>
64 #define VERSION "2.2.1"
66 #include <stdio.h>
67 #include <unistd.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <signal.h>
71 #include <getopt.h>
72 #include <syslog.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <sys/time.h>
76 #include <sys/wait.h>
77 #include <netinet/in.h>
78 #include <arpa/inet.h>
79 #include <netdb.h>
80 #include <errno.h>
82 #ifdef USE_TCP_WRAPPERS
83 #include <tcpd.h>
84 #endif
86 #define debug(x) if (dodebug) fprintf(stderr, x)
87 #define debug1(x,y) if (dodebug) fprintf(stderr, x, y)
89 /* let's set up some globals... */
90 int dodebug = 0;
91 int dosyslog = 0;
92 unsigned char reuse_addr = 1;
93 unsigned char linger_opt = 0;
94 char * bind_addr = NULL;
95 struct sockaddr_in addr_out;
96 int timeout = 0;
98 #ifndef NO_FTP
99 int ftp = 0;
100 #endif
102 int transproxy = 0;
104 #ifndef NO_SHAPER
105 int max_bandwidth = 0;
106 int random_wait = 0;
107 int wait_in_out=3; /* bit 0: wait for "in", bit 1: wait for "out" */
108 int wait_in=1;
109 int wait_out=1;
110 #endif
112 unsigned int bufsize=4096;
113 char *connect_str = NULL; /* CONNECT string passed to proxy */
114 char * ident = NULL;
116 #ifndef NO_FTP
117 /* what ftp to redirect */
118 #define FTP_PORT 1
119 #define FTP_PASV 2
120 #endif
122 #ifdef USE_TCP_WRAPPERS
123 struct request_info request;
124 int allow_severity = LOG_INFO;
125 int deny_severity = LOG_WARNING;
126 #endif /* USE_TCP_WRAPPERS */
128 #ifdef NEED_STRRCHR
129 #define strrchr rindex
130 #endif /* NEED_STRRCHR */
132 #define REDIR_IN 1
133 #define REDIR_OUT 0
135 /* prototype anything needing it */
136 void do_accept(int servsock, struct sockaddr_in *target);
137 int bindsock(char *addr, int port, int fail);
139 #ifndef NO_SHAPER
140 /* Used in this program to write something in a socket, it has the same
141 parameters and return value as "write", but with the flag "in": true if
142 it's the "in" socket and false if it's the "out" socket */
143 static inline ssize_t redir_write (int fd, const void *buf, size_t size, int in)
145 ssize_t result;
146 int wait;
148 wait=in ? wait_in : wait_out;
149 if( random_wait > 0 && wait) {
150 fd_set empty;
151 struct timeval waitbw; /* for bandwidth */
152 int rand_time;
154 FD_ZERO(&empty);
156 rand_time=rand()%(random_wait*2);
157 debug1("random wait: %u\n", rand_time);
158 waitbw.tv_sec=rand_time/1000;
159 waitbw.tv_usec=rand_time%1000;
161 select (1, &empty, NULL, NULL, &waitbw);
164 result=write(fd, buf, size);
166 if( max_bandwidth > 0 && wait) {
167 fd_set empty;
168 unsigned long bits;
169 struct timeval waitbw; /* for bandwidth */
171 FD_ZERO(&empty);
173 /* wait to be sure tu be below the allowed bandwidth */
174 bits=size*8;
175 debug1("bandwidth wait: %lu\n", 1000*bits/max_bandwidth);
176 waitbw.tv_sec=bits/max_bandwidth;
177 waitbw.tv_usec=(1000*(bits%max_bandwidth))/max_bandwidth;
179 select (1, &empty, NULL, NULL, &waitbw);
182 return result;
184 #else
185 /* macro if traffic shaper is disabled */
186 #define redir_write(fd, buf, size, in) write(fd, buf,size)
187 #endif
190 #ifdef NEED_STRDUP
191 char *
192 strdup(char * str)
194 char * result;
196 if (result = (char *) malloc(strlen(str) + 1))
197 strcpy(result, str);
199 return result;
201 #endif /* NEED_STRDUP */
203 void
204 redir_usage(char *name)
206 fprintf(stderr,"usage:\n");
207 fprintf(stderr,
208 "\t%s --lport=<n> --cport=<n> [options]\n",
209 name);
210 fprintf(stderr, "\t%s --inetd --cport=<n>\n", name);
211 fprintf(stderr, "\n\tOptions are:-\n");
212 fprintf(stderr, "\t\t--lport=<n>\t\tport to listen on\n");
213 fprintf(stderr, "\t\t--laddr=IP\t\taddress of interface to listen on\n");
214 fprintf(stderr, "\t\t--cport=<n>\t\tport to connect to\n");
215 fprintf(stderr, "\t\t--caddr=<host>\t\tremote host to connect to\n");
216 fprintf(stderr, "\t\t--inetd\t\trun from inetd\n");
217 fprintf(stderr, "\t\t--debug\t\toutput debugging info\n");
218 fprintf(stderr, "\t\t--timeout=<n>\tset timeout to n seconds\n");
219 fprintf(stderr, "\t\t--syslog\tlog messages to syslog\n");
220 fprintf(stderr, "\t\t--name=<str>\ttag syslog messages with 'str'\n");
221 fprintf(stderr, "\t\t--connect=<str>\tCONNECT string passed to proxy server\n");
222 #ifdef USE_TCP_WRAPPERS
223 fprintf(stderr, "\t\t \tAlso used as service name for TCP wrappers\n");
224 #endif /* USE_TCP_WRAPPERS */
225 fprintf(stderr, "\t\t--bind_addr=IP\tbind() outgoing IP to given addr\n");
227 #ifndef NO_FTP
228 fprintf(stderr, "\t\t--ftp=<type>\t\tredirect ftp connections\n");
229 fprintf(stderr, "\t\t\twhere type is either port, pasv, both\n");
230 #endif
232 fprintf(stderr, "\t\t--transproxy\trun in linux's transparent proxy mode\n");
233 #ifndef NO_SHAPER
234 /* options for bandwidth */
235 fprintf(stderr, "\t\t--bufsize=<octets>\tsize of the buffer\n");
236 fprintf(stderr, "\t\t--maxbandwidth=<bit-per-sec>\tlimit the bandwidth\n");
237 fprintf(stderr, "\t\t--random_wait=<millisec>\twait before each packet\n");
238 fprintf(stderr, "\t\t--wait_in_out=<flag>\t1 wait for in, 2 out, 3 in&out\n");
239 /* end options for bandwidth */
240 #endif
241 fprintf(stderr, "\n\tVersion %s.\n", VERSION);
242 exit(2);
245 void
246 parse_args(int argc,
247 char * argv[],
248 char ** target_addr,
249 int * target_port,
250 char ** local_addr,
251 int * local_port,
252 int * timeout,
253 int * dodebug,
254 int * inetd,
255 int * dosyslog,
256 char ** bind_addr,
257 #ifndef NO_FTP
258 int * ftp,
259 #endif
260 int *transproxy,
261 #ifndef NO_SHAPER
262 unsigned int * bufsize,
263 int * max_bandwidth,
264 int * random_wait,
265 int * wait_in_out,
266 #endif
267 char **connect_str)
269 static struct option long_options[] = {
270 {"lport", required_argument, 0, 'l'},
271 {"laddr", required_argument, 0, 'a'},
272 {"cport", required_argument, 0, 'r'},
273 {"caddr", required_argument, 0, 'c'},
274 {"bind_addr", required_argument, 0, 'b'},
275 {"debug", no_argument, 0, 'd'},
276 {"timeout", required_argument, 0, 't'},
277 {"inetd", no_argument, 0, 'i'},
278 {"ident", required_argument, 0, 'n'},
279 {"name", required_argument, 0, 'n'},
280 {"syslog", no_argument, 0, 's'},
281 {"ftp", required_argument, 0, 'f'},
282 {"transproxy", no_argument, 0, 'p'},
283 {"connect", required_argument, 0, 'x'},
284 {"bufsize", required_argument, 0, 'z'},
285 {"max_bandwidth", required_argument, 0, 'm'},
286 {"random_wait", required_argument, 0, 'w'},
287 {"wait_in_out", required_argument, 0, 'o'},
288 {0,0,0,0} /* End marker */
291 int option_index = 0;
292 extern int optind;
293 int opt;
294 struct servent *portdesc;
295 char *lport = NULL;
296 char *tport = NULL;
297 #ifndef NO_FTP
298 char *ftp_type = NULL;
299 #endif
301 *local_addr = NULL;
302 *target_addr = NULL;
303 *target_port = 0;
304 *local_port = 0;
306 while ((opt = getopt_long(argc, argv, "disfpn:t:b:a:l:r:c:x:z:m:w:o:",
307 long_options, &option_index)) != -1) {
308 switch (opt) {
309 case 'x':
310 *connect_str = optarg;
311 break;
312 case 'a':
313 *local_addr = optarg;
314 break;
316 case 'l':
317 lport = optarg;
318 break;
320 case 'r':
321 tport = optarg;
322 break;
324 case 'c':
325 *target_addr = optarg;
326 break;
328 case 'b':
329 *bind_addr = optarg;
330 break;
332 case 'd':
333 (*dodebug)++;
334 break;
336 case 't':
337 *timeout = atol(optarg);
338 break;
340 case 'i':
341 (*inetd)++;
342 break;
344 case 'n':
345 /* This is the ident which is added to syslog messages */
346 ident = optarg;
347 break;
349 case 's':
350 (*dosyslog)++;
351 break;
353 #ifndef NO_FTP
354 case 'f':
355 ftp_type = optarg;
356 if(!ftp_type) {
357 redir_usage(argv[0]);
358 exit(1);
360 break;
361 #endif
363 case 'p':
364 (*transproxy)++;
365 break;
367 #ifndef NO_SHAPER
368 case 'z':
369 *bufsize = (unsigned int)atol(optarg);
370 break;
372 case 'm':
373 *max_bandwidth = atol(optarg);
374 break;
376 case 'w':
377 *random_wait = atol(optarg);
378 break;
380 case 'o':
381 *wait_in_out = atol(optarg);
382 wait_in=*wait_in_out & 1;
383 wait_out=*wait_in_out & 2;
384 break;
385 #endif
386 default:
387 redir_usage(argv[0]);
388 exit(1);
389 break;
393 if(tport == NULL)
395 redir_usage(argv[0]);
396 exit(1);
399 if ((portdesc = getservbyname(tport, "tcp")) != NULL) {
400 *target_port = ntohs(portdesc->s_port);
401 } else {
402 *target_port = atol(tport);
405 /* only check local port if not running from inetd */
406 if(!(*inetd)) {
407 if(lport == NULL)
409 redir_usage(argv[0]);
410 exit(1);
413 if ((portdesc = getservbyname(lport, "tcp")) != NULL)
414 *local_port = ntohs(portdesc->s_port);
415 else
416 *local_port = atol(lport);
417 } /* if *inetd */
419 if (!ident) {
420 if ((ident = (char *) strrchr(argv[0], '/'))) {
421 ident++;
422 } else {
423 ident = argv[0];
427 #ifndef NO_FTP
428 /* some kind of ftp being forwarded? */
429 if(ftp_type) {
430 if(!strncasecmp(ftp_type, "port", 4))
431 *ftp = FTP_PORT;
432 else if(!strncasecmp(ftp_type, "pasv", 4))
433 *ftp = FTP_PASV;
434 else if(!strncasecmp(ftp_type, "both", 4))
435 *ftp = FTP_PORT | FTP_PASV;
436 else {
437 redir_usage(argv[0]);
438 exit(1);
441 #endif
443 openlog(ident, LOG_PID, LOG_DAEMON);
445 return;
448 #ifndef NO_FTP
449 /* with the --ftp option, this one changes passive mode replies from
450 the ftp server to point to a new redirector which we spawn,
451 now it also change the PORT commando when the client accept the
452 dataconnection */
454 void ftp_clean(int send, char *buf, unsigned long *bytes, int ftpsrv)
457 char *port_start;
458 int rporthi, lporthi;
459 int lportlo, rportlo;
460 int lport, rport;
461 int remip[4];
462 int localsock;
463 int socksize = sizeof(struct sockaddr_in);
465 struct sockaddr_in newsession;
466 struct sockaddr_in sockname;
468 if (ftpsrv == 0)
470 /* is this a port commando ? */
471 if(strncmp(buf, "PORT", 4)) {
472 redir_write(send, buf, (*bytes), REDIR_OUT);
473 return;
475 /* parse the old address out of the buffer */
476 port_start = strchr(buf, ' ');
478 sscanf(port_start, " %d,%d,%d,%d,%d,%d", &remip[0], &remip[1],
479 &remip[2], &remip[3], &rporthi, &rportlo);
480 } else {
481 /* is this a passive mode return ? */
482 if(strncmp(buf, "227", 3)) {
483 redir_write(send, buf, (*bytes), REDIR_OUT);
484 return;
487 /* parse the old address out of the buffer */
488 port_start = strchr(buf, '(');
490 sscanf(port_start, "(%d,%d,%d,%d,%d,%d", &remip[0], &remip[1],
491 &remip[2], &remip[3], &rporthi, &rportlo);
494 /* get the outside interface so we can listen */
495 if(getsockname(send, (struct sockaddr *)&sockname, &socksize) != 0) {
496 perror("getsockname");
497 exit(1);
500 rport = (rporthi << 8) | rportlo;
502 /* we need to listen on a port for the incoming connection.
503 we will use the port 0, so let the system pick one. */
505 localsock = bindsock(inet_ntoa(sockname.sin_addr), 0, 1);
508 /* get the real info */
509 if(getsockname(localsock, (struct sockaddr *)&sockname, &socksize) < 0) {
510 perror("getsockname");
511 if (dosyslog)
512 syslog(LOG_ERR, "getsockname failed: %m");
513 exit(1);
516 lport = ntohs(sockname.sin_port);
518 lporthi=(lport >> 8 ) & 0xff;
519 lportlo=lport & 0xff;
521 /* check to see if we bound */
522 if(localsock == -1) {
523 fprintf(stderr, "ftp: unable to bind new listening address\n");
524 exit(1);
526 if (ftpsrv == 0) {
527 /* send the new port and ipaddress to the server */
528 (*bytes) = sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\n",
529 sockname.sin_addr.s_addr & 0xff,
530 (sockname.sin_addr.s_addr >> 8) & 0xff,
531 (sockname.sin_addr.s_addr >> 16) & 0xff,
532 sockname.sin_addr.s_addr >> 24, lporthi, lportlo);
533 } else {
534 /* send the new port and ipaddress to the client */
535 (*bytes) = sprintf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n",
536 sockname.sin_addr.s_addr & 0xff,
537 (sockname.sin_addr.s_addr >> 8) & 0xff,
538 (sockname.sin_addr.s_addr >> 16) & 0xff,
539 sockname.sin_addr.s_addr >> 24, lporthi, lportlo);
541 newsession.sin_port = htons(rport);
542 newsession.sin_family = AF_INET;
543 newsession.sin_addr.s_addr = remip[0] | (remip[1] << 8)
544 | (remip[2] << 16) | (remip[3] << 24);
546 debug1("ftpdata server ip: %s\n", inet_ntoa(newsession.sin_addr));
547 debug1("ftpdata server port: %d\n", rport);
548 debug1("listening for ftpdata on port %d\n", lport);
549 debug1("listening for ftpdata on addr %s\n", inet_ntoa(sockname.sin_addr));
552 /* now that we're bound and listening, we can safely send the new
553 string without fear of them getting a connection refused. */
554 redir_write(send, buf, (*bytes), REDIR_OUT);
556 /* make a new process to handle the dataconnection correctly,
557 for the PASV mode this isn't a problem because after sending the
558 PASV command, the data connection, get active. For the PORT command
559 the server must send a success, if starting here with the copyloop
560 the success command never arrive the client.*/
562 switch(fork())
564 case -1: /* Error */
565 syslog(LOG_ERR, "Couldn't fork: %m");
566 _exit(1);
567 case 0: /* Child */
569 /* turn off ftp checking while the data connection is active */
570 ftp = 0;
571 do_accept(localsock, &newsession);
572 close(localsock);
573 _exit(0);
575 default: /* Parent */
576 { close(localsock); }
578 return;
580 #endif
583 void
584 copyloop(int insock,
585 int outsock,
586 int timeout_secs)
588 fd_set iofds;
589 fd_set c_iofds;
590 int max_fd; /* Maximum numbered fd used */
591 struct timeval timeout;
592 unsigned long bytes;
593 unsigned long bytes_in = 0;
594 unsigned long bytes_out = 0;
595 unsigned int start_time, end_time;
596 char buf[bufsize];
598 /* Record start time */
599 start_time = (unsigned int) time(NULL);
601 /* Set up timeout */
602 timeout.tv_sec = timeout_secs;
603 timeout.tv_usec = 0;
605 /* file descriptor bits */
606 FD_ZERO(&iofds);
607 FD_SET(insock, &iofds);
608 FD_SET(outsock, &iofds);
611 if (insock > outsock) {
612 max_fd = insock;
613 } else {
614 max_fd = outsock;
617 debug1("Entering copyloop() - timeout is %d\n", timeout_secs);
618 while(1) {
619 (void) memcpy(&c_iofds, &iofds, sizeof(iofds));
622 if (select(max_fd + 1,
623 &c_iofds,
624 (fd_set *)0,
625 (fd_set *)0,
626 (timeout_secs ? &timeout : NULL)) <= 0) {
627 /* syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/
628 break;
631 if(FD_ISSET(insock, &c_iofds)) {
632 if((bytes = read(insock, buf, sizeof(buf))) <= 0)
633 break;
634 #ifndef NO_FTP
635 if (ftp & FTP_PORT)
636 /* if we're correcting FTP, lookup for a PORT commando
637 in the buffer, if yes change this and establish
638 a new redirector for the data */
639 ftp_clean(outsock, buf, &bytes,0);
640 else
641 #endif
642 if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes)
643 break;
644 bytes_out += bytes;
646 if(FD_ISSET(outsock, &c_iofds)) {
647 if((bytes = read(outsock, buf, sizeof(buf))) <= 0)
648 break;
649 /* if we're correcting for PASV on ftp redirections, then
650 fix buf and bytes to have the new address, among other
651 things */
652 #ifndef NO_FTP
653 if(ftp & FTP_PASV)
654 ftp_clean(insock, buf, &bytes,1);
655 else
656 #endif
657 if(redir_write(insock, buf, bytes, REDIR_IN) != bytes)
658 break;
659 bytes_in += bytes;
662 debug("Leaving main copyloop\n");
665 setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
666 setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER));
667 setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
668 setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER));
671 shutdown(insock,0);
672 shutdown(outsock,0);
673 close(insock);
674 close(outsock);
675 debug("copyloop - sockets shutdown and closed\n");
676 end_time = (unsigned int) time(NULL);
677 debug1("copyloop - connect time: %8d seconds\n", end_time - start_time);
678 debug1("copyloop - transfer in: %8ld bytes\n", bytes_in);
679 debug1("copyloop - transfer out: %8ld bytes\n", bytes_out);
680 if (dosyslog) {
681 syslog(LOG_NOTICE, "disconnect %d secs, %ld in %ld out",
682 (end_time - start_time), bytes_in, bytes_out);
684 return;
687 void doproxyconnect(int socket)
689 char buf[128];
690 int x;
691 /* write CONNECT string to proxy */
692 sprintf((char *) &buf, "CONNECT %s HTTP/1.0\n\n", connect_str);
693 x = write(socket, (char *) &buf, strlen(buf));
694 if (x < 1) {
695 perror("doproxyconnect: failed");
696 exit(1);
698 /* now read result */
699 x = read(socket, (char *) &buf, sizeof(buf));
700 if (x < 1) {
701 perror("doproxyconnect: failed reading fra proxy");
702 exit(1);
704 /* no more error checking for now -- something should be added later */
705 /* HTTP/1.0 200 Connection established */
709 /* lwait for a connection and move into copyloop... again,
710 ftp redir will call this, so we don't dupilcate it. */
712 void
713 do_accept(int servsock, struct sockaddr_in *target)
716 int clisock;
717 int targetsock;
718 struct sockaddr_in client;
719 int clientlen = sizeof(client);
720 int accept_errno;
722 debug("top of accept loop\n");
723 if ((clisock = accept(servsock, (struct sockaddr *) &client,
724 &clientlen)) < 0) {
726 accept_errno = errno;
727 perror("server: accept");
729 if (dosyslog)
730 syslog(LOG_ERR, "accept failed: %m");
732 /* determine if this error is fatal */
733 switch(accept_errno) {
734 /* non-fatal errors */
735 case EHOSTUNREACH:
736 case ECONNRESET:
737 case ETIMEDOUT:
738 return;
740 /* all other errors assumed fatal */
741 default:
742 exit(1);
747 debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
748 debug1("peer socket is %d\n", client.sin_port);
751 * Double fork here so we don't have to wait later
752 * This detaches us from our parent so that the parent
753 * does not need to pick up dead kids later.
755 * This needs to be done before the hosts_access stuff, because
756 * extended hosts_access options expect to be run from a child.
758 switch(fork())
760 case -1: /* Error */
761 perror("(server) fork");
763 if (dosyslog)
764 syslog(LOG_ERR, "(server) fork failed: %m");
766 _exit(1);
767 case 0: /* Child */
768 break;
769 default: /* Parent */
771 int status;
773 /* Wait for child (who has forked off grandchild) */
774 (void) wait(&status);
776 /* Close sockets to prevent confusion */
777 close(clisock);
779 return;
783 /* We are now the first child. Fork again and exit */
785 switch(fork())
787 case -1: /* Error */
788 perror("(child) fork");
790 if (dosyslog)
791 syslog(LOG_ERR, "(child) fork failed: %m");
793 _exit(1);
794 case 0: /* Child */
795 break;
796 default: /* Parent */
797 _exit(0);
800 /* We are now the grandchild */
802 #ifdef USE_TCP_WRAPPERS
803 request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0);
804 sock_host(&request);
805 sock_hostname(&request);
806 sock_hostaddr(&request);
808 if (!hosts_access(&request)) {
809 refuse(&request);
810 _exit(0);
813 if (dosyslog)
814 syslog(LOG_INFO, "accepted connect from %s", eval_client(&request));
815 #endif /* USE_TCP_WRAPPERS */
817 if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
819 perror("target: socket");
821 if (dosyslog)
822 syslog(LOG_ERR, "socket failed: %m");
824 _exit(1);
827 if(transproxy) {
828 memcpy(&addr_out, &client, sizeof(struct sockaddr_in));
829 addr_out.sin_port = 0;
832 if (bind_addr || transproxy) {
833 /* this only makes sense if an outgoing IP addr has been forced;
834 * at this point, we have a valid targetsock to bind() to.. */
835 /* also, if we're in transparent proxy mode, this option
836 never makes sense */
838 if (bind(targetsock, (struct sockaddr *) &addr_out,
839 sizeof(struct sockaddr_in)) < 0) {
840 perror("bind_addr: cannot bind to forcerd outgoing addr");
842 /* the port parameter fetch the really port we are listening, it should
843 only be different if the input value is 0 (let the system pick a
844 port) */
845 if (dosyslog)
846 syslog(LOG_ERR, "bind failed: %m");
848 _exit(1);
850 debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr));
853 if (connect(targetsock, (struct sockaddr *) target,
854 sizeof(struct sockaddr_in)) < 0) {
855 perror("target: connect");
857 if (dosyslog)
858 syslog(LOG_ERR, "bind failed: %m");
860 _exit(1);
863 debug1("connected to %s\n", inet_ntoa(target->sin_addr));
865 /* thanks to Anders Vannman for the fix to make proper syslogging
866 happen here... */
868 if (dosyslog) {
869 char tmp1[20], tmp2[20];
870 strcpy(tmp1, inet_ntoa(client.sin_addr));
871 strcpy(tmp2, inet_ntoa(target->sin_addr));
873 syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
874 tmp1, client.sin_port,
875 tmp2, target->sin_port);
878 /* do proxy stuff */
879 if (connect_str)
880 doproxyconnect(targetsock);
882 #ifndef NO_SHAPER
883 /* initialise random number if necessary */
884 if( random_wait > 0 ) {
885 srand(getpid());
887 #endif
889 copyloop(clisock, targetsock, timeout);
890 exit(0); /* Exit after copy */
893 /* bind to a new socket, we do this out here because passive-fixups
894 are going to call it too, and there's no sense dupliciting the
895 code. */
896 /* fail is true if we should just return a -1 on error, false if we
897 should bail. */
899 int bindsock(char *addr, int port, int fail)
902 int servsock;
903 struct sockaddr_in server;
906 * Get a socket to work with. This socket will
907 * be in the Internet domain, and will be a
908 * stream socket.
911 if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
912 if(fail) {
913 return -1;
915 else {
916 perror("server: socket");
918 if (dosyslog)
919 syslog(LOG_ERR, "socket failed: %m");
921 exit(1);
925 memset(&server, 0, sizeof(server));
926 server.sin_family = AF_INET;
927 server.sin_port = htons(port);
928 if (addr != NULL) {
929 struct hostent *hp;
931 debug1("listening on %s\n", addr);
932 if ((hp = gethostbyname(addr)) == NULL) {
933 fprintf(stderr, "%s: cannot resolve hostname.\n", addr);
934 exit(1);
936 memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
937 } else {
938 debug("local IP is default\n");
939 server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0"));
942 setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
943 setsockopt(servsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER));
946 * Try to bind the address to the socket.
949 if (bind(servsock, (struct sockaddr *) &server,
950 sizeof(server)) < 0) {
951 if(fail) {
952 close(servsock);
953 return -1;
954 } else {
955 perror("server: bind");
957 if (dosyslog)
958 syslog(LOG_ERR, "bind failed: %m");
960 exit(1);
965 * Listen on the socket.
968 if (listen(servsock, 10) < 0) {
969 if(fail) {
970 close(servsock);
971 return -1;
972 } else {
973 perror("server: listen");
975 if (dosyslog)
976 syslog(LOG_ERR, "listen failed: %m");
978 exit(1);
982 return servsock;
986 main(int argc, char *argv[])
989 struct sockaddr_in target;
990 char *target_addr;
991 int target_port;
992 char *local_addr;
993 int local_port;
994 int inetd = 0;
995 char * target_ip;
996 char * ip_to_target;
998 debug("parse args\n");
999 parse_args(argc, argv, &target_addr, &target_port, &local_addr,
1000 &local_port, &timeout, &dodebug, &inetd, &dosyslog, &bind_addr,
1001 #ifndef NO_FTP
1002 &ftp,
1003 #endif
1004 &transproxy,
1005 #ifndef NO_SHAPER
1006 &bufsize, &max_bandwidth, &random_wait,
1007 &wait_in_out,
1008 #endif
1009 &connect_str);
1011 /* Set up target */
1012 target.sin_family = AF_INET;
1013 target.sin_port = htons(target_port);
1014 if (target_addr != NULL) {
1015 struct hostent *hp;
1017 debug1("target is %s\n", target_addr);
1018 if ((hp = gethostbyname(target_addr)) == NULL) {
1019 fprintf(stderr, "%s: host unknown.\n", target_addr);
1020 exit(1);
1022 memcpy(&target.sin_addr, hp->h_addr, hp->h_length);
1023 } else {
1024 debug("target is default\n");
1025 target.sin_addr.s_addr = htonl(inet_addr("0.0.0.0"));
1028 target_ip = strdup(inet_ntoa(target.sin_addr));
1029 debug1("target IP address is %s\n", target_ip);
1030 debug1("target port is %d\n", target_port);
1032 /* Set up outgoing IP addr (optional);
1033 * we have to wait for bind until targetsock = socket() is done
1035 if (bind_addr && !transproxy) {
1036 struct hostent *hp;
1038 fprintf(stderr, "bind_addr is %s\n", bind_addr);
1039 addr_out.sin_family = AF_INET;
1040 addr_out.sin_port = 0;
1041 if ((hp = gethostbyname(bind_addr)) == NULL) {
1042 fprintf(stderr, "%s: cannot resolve forced outgoing IP address.\n", bind_addr);
1043 exit(1);
1045 memcpy(&addr_out.sin_addr, hp->h_addr, hp->h_length);
1047 ip_to_target = strdup(inet_ntoa(addr_out.sin_addr));
1048 debug1("IP address for target is %s\n", ip_to_target);
1052 if (inetd) {
1053 int targetsock;
1054 struct sockaddr_in client;
1055 int client_size = sizeof(client);
1057 #ifdef USE_TCP_WRAPPERS
1058 request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0);
1059 sock_host(&request);
1060 sock_hostname(&request);
1061 sock_hostaddr(&request);
1063 if (!hosts_access(&request))
1064 refuse(&request);
1065 #endif /* USE_TCP_WRAPPERS */
1067 if (!getpeername(0, (struct sockaddr *) &client, &client_size)) {
1068 debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
1069 debug1("peer socket is %d\n", client.sin_port);
1071 if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1072 perror("target: socket");
1074 if (dosyslog)
1075 syslog(LOG_ERR, "targetsock failed: %m");
1077 exit(1);
1080 if(transproxy) {
1081 memcpy(&addr_out, &client, sizeof(struct sockaddr_in));
1082 addr_out.sin_port = 0;
1085 if (bind_addr || transproxy) {
1086 /* this only makes sense if an outgoing IP addr has been forced;
1087 * at this point, we have a valid targetsock to bind() to.. */
1088 if (bind(targetsock, (struct sockaddr *) &addr_out,
1089 sizeof(addr_out)) < 0) {
1090 perror("bind_addr: cannot bind to forcerd outgoing addr");
1092 if (dosyslog)
1093 syslog(LOG_ERR, "bind failed: %m");
1095 exit(1);
1097 debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr));
1100 if (connect(targetsock, (struct sockaddr *) &target,
1101 sizeof(target)) < 0) {
1102 perror("target: connect");
1104 if (dosyslog)
1105 syslog(LOG_ERR, "connect failed: %m");
1107 exit(1);
1110 if (dosyslog) {
1111 syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
1112 inet_ntoa(client.sin_addr), client.sin_port,
1113 target_ip, target.sin_port);
1116 /* Just start copying - one side of the loop is stdin - 0 */
1117 copyloop(0, targetsock, timeout);
1118 } else {
1119 int servsock;
1121 if(local_addr)
1122 servsock = bindsock(local_addr, local_port, 0);
1123 else
1124 servsock = bindsock(NULL, local_port, 0);
1127 * Accept connections. When we accept one, ns
1128 * will be connected to the client. client will
1129 * contain the address of the client.
1132 while (1)
1133 do_accept(servsock, &target);
1136 /* this should really never be reached */
1138 exit(0);