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 /* Redir, the code to which is below, is actually a horrible hack of my
17 * other cool network utility, daemon, which is actually a horrible hack
18 * of ora's using C sample code, 12.2.c. But, hey, they do something.
19 * (and that's the key.)
20 * -- Sammy (sammy@freenet.akron.oh.us)
23 /* oh, incidentally, Sammy is now sammy@oh.verio.com */
26 * added some nice new features:
28 * --bind_addr=my.other.ip.address
29 * forces to use my.other.ip.address for the outgoing connection
31 * you can also specify, that redir listens not on all IP addresses of
32 * your system but only for the given one, i.e.:
33 * if my host has the addresses
34 * irc.thishost.my.domain and mail.thishost.my.domain
35 * but you want that your users do connect for the irc redir service
36 * only on irc.thishost.my.domain, then do it this way:
37 * redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667
39 * addr1.first.domain 6667 redirects to irc.first.net port 6667
40 * and addr2.second.domain 6667 redirects to irc.second.net port 6667
41 * while addr1 and addr2 are the same maschine and the ports can be equal.
44 * - thomas <thomas@x-berg.in-berlin.de>, <dl9sau@db0tud.ampr.org>
46 * btw: i tried without success implementing code for the following scenario:
47 * redir --force_addr irc.fu-berlin.de 6667 6667
48 * if "--force_addr" is given and a user connects to my system, that address
49 * of my system will be used on the outgoing connection that the user
51 * i was not successful to determine, to which of my addresses the user
64 #include <sys/types.h>
65 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
73 #ifdef USE_TCP_WRAPPERS
77 #define debug(x) if (dodebug) fprintf(stderr, x)
78 #define debug1(x,y) if (dodebug) fprintf(stderr, x, y)
80 /* let's set up some globals... */
83 unsigned char reuse_addr
= 1;
84 unsigned char linger_opt
= 0;
85 char * bind_addr
= NULL
;
86 struct sockaddr_in addr_out
;
92 #ifdef USE_TCP_WRAPPERS
93 struct request_info request
;
94 int allow_severity
= LOG_INFO
;
95 int deny_severity
= LOG_WARNING
;
96 #endif /* USE_TCP_WRAPPERS */
99 #define strrchr rindex
100 #endif /* NEED_STRRCHR */
102 /* prototype anything needing it */
103 void do_accept(int servsock
, struct sockaddr_in
*target
);
104 int bindsock(char *addr
, int port
, int fail
);
113 if (result
= (char *) malloc(strlen(str
) + 1))
118 #endif /* NEED_STRDUP */
121 redir_usage(char *name
)
123 fprintf(stderr
,"usage:\n");
125 "\t%s --lport=<n> --cport=<n> [options]\n",
127 fprintf(stderr
, "\t%s --inetd --cport=<n>\n", name
);
128 fprintf(stderr
, "\n\tOptions are:-\n");
129 fprintf(stderr
, "\t\t--lport=<n>\t\tport to listen on\n");
130 fprintf(stderr
, "\t\t--laddr=IP\t\taddress of interface to listen on\n");
131 fprintf(stderr
, "\t\t--cport=<n>\t\tport to connect to\n");
132 fprintf(stderr
, "\t\t--caddr=<host>\t\tremote host to connect to\n");
133 fprintf(stderr
, "\t\t--inetd\t\trun from inetd\n");
134 fprintf(stderr
, "\t\t--debug\t\toutput debugging info\n");
135 fprintf(stderr
, "\t\t--timeout=<n>\tset timeout to n seconds\n");
136 fprintf(stderr
, "\t\t--syslog\tlog messages to syslog\n");
137 fprintf(stderr
, "\t\t--name=<str>\ttag syslog messages with 'str'\n");
138 #ifdef USE_TCP_WRAPPERS
139 fprintf(stderr
, "\t\t \tAlso used as service name for TCP wrappers\n");
140 #endif /* USE_TCP_WRAPPERS */
141 fprintf(stderr
, "\t\t--bind_addr=IP\tbind() outgoing IP to given addr\n");
142 fprintf(stderr
, "\t\t--ftp\t\tredirect passive ftp connections\n");
143 fprintf(stderr
, "\t\t--transproxy\trun in linux's transparent proxy mode\n");
144 fprintf(stderr
, "\n\tVersion %s - $Id$\n", VERSION
);
163 static struct option long_options
[] = {
164 {"lport", required_argument
, 0, 'l'},
165 {"laddr", required_argument
, 0, 'a'},
166 {"cport", required_argument
, 0, 'r'},
167 {"caddr", required_argument
, 0, 'c'},
168 {"bind_addr", required_argument
, 0, 'b'},
169 {"debug", no_argument
, 0, 'd'},
170 {"timeout", required_argument
, 0, 't'},
171 {"inetd", no_argument
, 0, 'i'},
172 {"ident", required_argument
, 0, 'n'},
173 {"name", required_argument
, 0, 'n'},
174 {"syslog", no_argument
, 0, 's'},
175 {"ftp", no_argument
, 0, 'f'},
176 {"transproxy", no_argument
, 0, 'p'},
177 {0,0,0,0} /* End marker */
180 int option_index
= 0;
183 struct servent
*portdesc
;
192 while ((opt
= getopt_long(argc
, argv
, "disfpn:t:b:a:l:r:c:",
193 long_options
, &option_index
)) != -1) {
196 *local_addr
= optarg
;
208 *target_addr
= optarg
;
220 *timeout
= atol(optarg
);
228 /* This is the ident which is added to syslog messages */
245 redir_usage(argv
[0]);
253 redir_usage(argv
[0]);
257 if ((portdesc
= getservbyname(tport
, "tcp")) != NULL
) {
258 *target_port
= ntohs(portdesc
->s_port
);
260 *target_port
= atol(tport
);
263 /* only check local port if not running from inetd */
267 redir_usage(argv
[0]);
271 if ((portdesc
= getservbyname(lport
, "tcp")) != NULL
)
272 *local_port
= ntohs(portdesc
->s_port
);
274 *local_port
= atol(lport
);
278 if ((ident
= (char *) strrchr(argv
[0], '/'))) {
285 openlog(ident
, LOG_PID
, LOG_DAEMON
);
290 /* with the --ftp option, this one changes passive mode replies from
291 the ftp server to point to a new redirector which we spawn */
292 void ftp_clean(int send
, char *buf
, unsigned long *bytes
)
296 int rporthi
, lporthi
;
297 int lportlo
, rportlo
;
301 int socksize
= sizeof(struct sockaddr_in
);
304 struct sockaddr_in newsession
;
305 struct sockaddr_in sockname
;
307 /* is this a passive mode return ? */
308 if(strncmp(buf
, "227", 3)) {
309 write(send
, buf
, (*bytes
));
313 /* get the outside interface so we can listen */
314 if(getsockname(send
, (struct sockaddr
*)&sockname
, &socksize
) != 0) {
316 perror("getsockname");
319 syslog(LOG_ERR
, "getsockname failed: %m");
324 /* parse the old address out of the buffer */
325 port_start
= strchr(buf
, '(');
327 sscanf(port_start
, "(%d,%d,%d,%d,%d,%d", &remip
[0], &remip
[1],
328 &remip
[2], &remip
[3], &rporthi
, &rportlo
);
330 /* we need to listen on a port for the incoming connection.
331 we'll use this strategy. start at 32768 for hi byte, then
332 sweep using the low byte of our pid and try to bind 5 times.
333 if we can't bind to any of those ports, fail */
336 lportlo
= getpid() & 0xf0;
337 rport
= (rporthi
<< 8) | rportlo
;
339 for(i
= 0, localsock
= -1; ((localsock
== -1) && (i
< 5));
342 lport
= ((lporthi
<< 8) | lportlo
) +1; /* weird off by 1 bug */
343 localsock
= bindsock(inet_ntoa(sockname
.sin_addr
), lport
, 1);
346 /* check to see if we bound */
347 if(localsock
== -1) {
348 fprintf(stderr
, "ftp: unable to bind new listening address\n");
352 (*bytes
) = sprintf(buf
, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n",
353 sockname
.sin_addr
.s_addr
& 0xff,
354 (sockname
.sin_addr
.s_addr
>> 8) & 0xff,
355 (sockname
.sin_addr
.s_addr
>> 16) & 0xff,
356 sockname
.sin_addr
.s_addr
>> 24, lporthi
, lportlo
);
358 newsession
.sin_port
= htons(rport
);
359 newsession
.sin_family
= AF_INET
;
360 newsession
.sin_addr
.s_addr
= remip
[0] | (remip
[1] << 8)
361 | (remip
[2] << 16) | (remip
[3] << 24);
363 debug1("ftp server ip: %s\n", inet_ntoa(newsession
.sin_addr
));
364 debug1("ftp server port: %d\n", rport
);
365 debug1("listening on port %d\n", lport
);
366 debug1("listening on addr %s\n",
367 inet_ntoa(sockname
.sin_addr
));
370 /* now that we're bound and listening, we can safely send the new
371 passive string without fear of them getting a connection
373 write(send
, buf
, (*bytes
));
375 /* turn off ftp checking while the data connection is active */
377 do_accept(localsock
, &newsession
);
393 int max_fd
; /* Maximum numbered fd used */
394 struct timeval timeout
;
396 unsigned long bytes_in
= 0;
397 unsigned long bytes_out
= 0;
398 unsigned int start_time
, end_time
;
401 /* Record start time */
402 start_time
= (unsigned int) time(NULL
);
405 timeout
.tv_sec
= timeout_secs
;
408 /* file descriptor bits */
410 FD_SET(insock
, &iofds
);
411 FD_SET(outsock
, &iofds
);
414 if (insock
> outsock
) {
420 debug1("Entering copyloop() - timeout is %d\n", timeout_secs
);
422 (void) memcpy(&c_iofds
, &iofds
, sizeof(iofds
));
425 if (select(max_fd
+ 1,
429 (timeout_secs
? &timeout
: NULL
)) <= 0) {
430 /* syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/
434 if(FD_ISSET(insock
, &c_iofds
)) {
435 if((bytes
= read(insock
, buf
, sizeof(buf
))) <= 0)
437 if(write(outsock
, buf
, bytes
) != bytes
)
441 if(FD_ISSET(outsock
, &c_iofds
)) {
442 if((bytes
= read(outsock
, buf
, sizeof(buf
))) <= 0)
444 /* if we're correcting for PASV on ftp redirections, then
445 fix buf and bytes to have the new address, among other
448 ftp_clean(insock
, buf
, &bytes
);
450 if(write(insock
, buf
, bytes
) != bytes
)
455 debug("Leaving main copyloop\n");
458 setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
459 setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER));
460 setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
461 setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER));
468 debug("copyloop - sockets shutdown and closed\n");
469 end_time
= (unsigned int) time(NULL
);
470 debug1("copyloop - connect time: %8d seconds\n", end_time
- start_time
);
471 debug1("copyloop - transfer in: %8ld bytes\n", bytes_in
);
472 debug1("copyloop - transfer out: %8ld bytes\n", bytes_out
);
474 syslog(LOG_NOTICE
, "disconnect %d secs, %ld in %ld out",
475 (end_time
- start_time
), bytes_in
, bytes_out
);
480 /* lwait for a connection and move into copyloop... again,
481 passive ftp's will call this, so we don't dupilcate it. */
484 do_accept(int servsock
, struct sockaddr_in
*target
)
489 struct sockaddr_in client
;
490 int clientlen
= sizeof(client
);
492 debug("top of accept loop\n");
493 if ((clisock
= accept(servsock
, (struct sockaddr
*) &client
,
496 perror("server: accept");
499 syslog(LOG_ERR
, "accept failed: %m");
504 debug1("peer IP is %s\n", inet_ntoa(client
.sin_addr
));
505 debug1("peer socket is %d\n", ntohs(client
.sin_port
));
508 * Double fork here so we don't have to wait later
509 * This detaches us from our parent so that the parent
510 * does not need to pick up dead kids later.
512 * This needs to be done before the hosts_access stuff, because
513 * extended hosts_access options expect to be run from a child.
518 perror("(server) fork");
521 syslog(LOG_ERR
, "(server) fork failed: %m");
526 default: /* Parent */
530 /* Wait for child (who has forked off grandchild) */
531 (void) wait(&status
);
533 /* Close sockets to prevent confusion */
540 /* We are now the first child. Fork again and exit */
545 perror("(child) fork");
548 syslog(LOG_ERR
, "(child) fork failed: %m");
553 default: /* Parent */
557 /* We are now the grandchild */
559 #ifdef USE_TCP_WRAPPERS
560 request_init(&request
, RQ_DAEMON
, ident
, RQ_FILE
, clisock
, 0);
562 sock_hostname(&request
);
563 sock_hostaddr(&request
);
565 if (!hosts_access(&request
)) {
571 syslog(LOG_INFO
, "accepted connect from %s", eval_client(&request
));
572 #endif /* USE_TCP_WRAPPERS */
574 if ((targetsock
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0) {
576 perror("target: socket");
579 syslog(LOG_ERR
, "socket failed: %m");
585 memcpy(&addr_out
, &client
, sizeof(struct sockaddr_in
));
586 addr_out
.sin_port
= 0;
589 if (bind_addr
|| transproxy
) {
590 /* this only makes sense if an outgoing IP addr has been forced;
591 * at this point, we have a valid targetsock to bind() to.. */
592 /* also, if we're in transparent proxy mode, this option
595 if (bind(targetsock
, (struct sockaddr
*) &addr_out
,
596 sizeof(struct sockaddr_in
)) < 0) {
597 perror("bind_addr: cannot bind to forcerd outgoing addr");
600 syslog(LOG_ERR
, "bind failed: %m");
604 debug1("outgoing IP is %s\n", inet_ntoa(addr_out
.sin_addr
));
607 if (connect(targetsock
, (struct sockaddr
*) target
,
608 sizeof(struct sockaddr_in
)) < 0) {
609 perror("target: connect");
612 syslog(LOG_ERR
, "bind failed: %m");
617 debug1("connected to %s\n", inet_ntoa(target
->sin_addr
));
619 /* thanks to Anders Vannman for the fix to make proper syslogging
623 char tmp1
[20], tmp2
[20];
624 strcpy(tmp1
, inet_ntoa(client
.sin_addr
));
625 strcpy(tmp2
, inet_ntoa(target
->sin_addr
));
627 syslog(LOG_NOTICE
, "connecting %s/%d to %s/%d",
628 tmp1
, ntohs(client
.sin_port
),
629 tmp2
, ntohs(target
->sin_port
));
631 copyloop(clisock
, targetsock
, timeout
);
632 exit(0); /* Exit after copy */
635 /* bind to a new socket, we do this out here because passive-fixups
636 are going to call it too, and there's no sense dupliciting the
638 /* fail is true if we should just return a -1 on error, false if we
641 int bindsock(char *addr
, int port
, int fail
)
645 struct sockaddr_in server
;
648 * Get a socket to work with. This socket will
649 * be in the Internet domain, and will be a
653 if ((servsock
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0) {
658 perror("server: socket");
661 syslog(LOG_ERR
, "socket failed: %m");
667 server
.sin_family
= AF_INET
;
668 server
.sin_port
= htons(port
);
672 debug1("listening on %s\n", addr
);
673 if ((hp
= gethostbyname(addr
)) == NULL
) {
674 fprintf(stderr
, "%s: cannot resolve hostname.\n", addr
);
677 memcpy(&server
.sin_addr
, hp
->h_addr
, hp
->h_length
);
679 debug("local IP is default\n");
680 server
.sin_addr
.s_addr
= htonl(inet_addr("0.0.0.0"));
683 setsockopt(servsock
, SOL_SOCKET
, SO_REUSEADDR
, &reuse_addr
, sizeof(reuse_addr
));
684 setsockopt(servsock
, SOL_SOCKET
, SO_LINGER
, &linger_opt
, sizeof(SO_LINGER
));
687 * Try to bind the address to the socket.
690 if (bind(servsock
, (struct sockaddr
*) &server
,
691 sizeof(server
)) < 0) {
696 perror("server: bind");
699 syslog(LOG_ERR
, "bind failed: %m");
706 * Listen on the socket.
709 if (listen(servsock
, 10) < 0) {
714 perror("server: listen");
717 syslog(LOG_ERR
, "listen failed: %m");
727 main(int argc
, char *argv
[])
730 struct sockaddr_in target
;
739 debug("parse args\n");
740 parse_args(argc
, argv
, &target_addr
, &target_port
, &local_addr
,
741 &local_port
, &timeout
, &dodebug
, &inetd
, &dosyslog
, &bind_addr
,
745 target
.sin_family
= AF_INET
;
746 target
.sin_port
= htons(target_port
);
747 if (target_addr
!= NULL
) {
750 debug1("target is %s\n", target_addr
);
751 if ((hp
= gethostbyname(target_addr
)) == NULL
) {
752 fprintf(stderr
, "%s: host unknown.\n", target_addr
);
755 memcpy(&target
.sin_addr
, hp
->h_addr
, hp
->h_length
);
757 debug("target is default\n");
758 target
.sin_addr
.s_addr
= htonl(inet_addr("0.0.0.0"));
761 target_ip
= strdup(inet_ntoa(target
.sin_addr
));
762 debug1("target IP address is %s\n", target_ip
);
763 debug1("target port is %d\n", target_port
);
765 /* Set up outgoing IP addr (optional);
766 * we have to wait for bind until targetsock = socket() is done
768 if (bind_addr
&& !transproxy
) {
771 fprintf(stderr
, "bind_addr is %s\n", bind_addr
);
772 addr_out
.sin_family
= AF_INET
;
773 addr_out
.sin_port
= 0;
774 if ((hp
= gethostbyname(bind_addr
)) == NULL
) {
775 fprintf(stderr
, "%s: cannot resolve forced outgoing IP address.\n", bind_addr
);
778 memcpy(&addr_out
.sin_addr
, hp
->h_addr
, hp
->h_length
);
780 ip_to_target
= strdup(inet_ntoa(addr_out
.sin_addr
));
781 debug1("IP address for target is %s\n", ip_to_target
);
787 struct sockaddr_in client
;
788 int client_size
= sizeof(client
);
790 #ifdef USE_TCP_WRAPPERS
791 request_init(&request
, RQ_DAEMON
, ident
, RQ_FILE
, 0, 0);
793 sock_hostname(&request
);
794 sock_hostaddr(&request
);
796 if (!hosts_access(&request
))
798 #endif /* USE_TCP_WRAPPERS */
800 if (!getpeername(0, (struct sockaddr
*) &client
, &client_size
)) {
801 debug1("peer IP is %s\n", inet_ntoa(client
.sin_addr
));
802 debug1("peer socket is %d\n", ntohs(client
.sin_port
));
804 if ((targetsock
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0) {
805 perror("target: socket");
808 syslog(LOG_ERR
, "targetsock failed: %m");
814 memcpy(&addr_out
, &client
, sizeof(struct sockaddr_in
));
815 addr_out
.sin_port
= 0;
818 if (bind_addr
|| transproxy
) {
819 /* this only makes sense if an outgoing IP addr has been forced;
820 * at this point, we have a valid targetsock to bind() to.. */
821 if (bind(targetsock
, (struct sockaddr
*) &addr_out
,
822 sizeof(addr_out
)) < 0) {
823 perror("bind_addr: cannot bind to forcerd outgoing addr");
826 syslog(LOG_ERR
, "bind failed: %m");
830 debug1("outgoing IP is %s\n", inet_ntoa(addr_out
.sin_addr
));
833 if (connect(targetsock
, (struct sockaddr
*) &target
,
834 sizeof(target
)) < 0) {
835 perror("target: connect");
838 syslog(LOG_ERR
, "connect failed: %m");
844 syslog(LOG_NOTICE
, "connecting %s/%d to %s/%d",
845 inet_ntoa(client
.sin_addr
), ntohs(client
.sin_port
),
846 target_ip
, ntohs(target
.sin_port
));
849 /* Just start copying - one side of the loop is stdin - 0 */
850 copyloop(0, targetsock
, timeout
);
855 servsock
= bindsock(local_addr
, local_port
, 0);
857 servsock
= bindsock(NULL
, local_port
, 0);
860 * Accept connections. When we accept one, ns
861 * will be connected to the client. client will
862 * contain the address of the client.
866 do_accept(servsock
, &target
);
869 /* this should really never be reached */