Index: tircproxy.c --- tircproxy.c.orig +++ tircproxy.c @@ -57,6 +57,7 @@ char *Version = #include #include #include +#include #include @@ -119,6 +120,8 @@ char *Version = #endif #undef TRANS +#undef RANGEDPORTS + #if IPF # if HAVE_NETINET_IP_NAT_H # include @@ -142,6 +145,15 @@ char *Version = # endif #endif +#ifdef PF +#include +#include +#include +#include +#define TRANS 1 +#define RANGEDPORTS 1 +#endif + #ifndef LINUX # define LINUX 0 #endif @@ -187,7 +199,12 @@ int hosts_ctl(char *daemon, #endif +#ifdef RANGEDPORTS +int min_port = IPPORT_HIFIRSTAUTO; +int max_port = IPPORT_HILASTAUTO; +#endif + /* Typedefs. */ typedef unsigned long ipaddr_t; @@ -213,6 +230,10 @@ static uid_t get_user_id (char *user); static uid_t get_group_id (uid_t uid); static int bind_to_port (ipaddr_t bind_ip, short bind_port, int backlog, int dlev); +#ifdef RANGEDPORTS +static int bind_to_ranged_port (ipaddr_t bind_ip, short min_port, + short max_port, int backlog, int dlev); +#endif static int connect_to_server (struct sockaddr_in addr); static void lookup_hostname (struct sockaddr_in *addr, char *hostname, int hostlen, int needed); @@ -350,8 +371,14 @@ int main(int argc, char **argv) /* Parse the command line arguments. */ - while ((arg = getopt(argc, argv, "ab:d:h?i:o:pq:r:s:t:CDHIKLMNOQRSU")) != EOF) +#ifdef RANGEDPORTS + while ((arg = getopt(argc, argv, "ab:d:h?i:m:o:pq:r:s:t:x:CDHIKLMNOQRSU")) != EOF) { + char *p; +#else + while ((arg = getopt(argc, argv, "ab:d:h?i:o:pq:r:s:t:CDHIKLMNOQRSU")) != EOF) + { +#endif switch (arg) { @@ -375,6 +402,15 @@ int main(int argc, char **argv) case 'i': visible_ip_i = get_ip_addr(optarg); break; +#ifdef RANGEDPORTS + case 'm': + min_port = strtol(optarg, &p, 10); + if (!*optarg || *p) + usage(argv[0], "Missing min port."); + if (min_port < 0 || min_port > USHRT_MAX) + usage(argv[0], "Invalid min port."); + break; +#endif case 'o': visible_ip_o = get_ip_addr(optarg); break; @@ -408,6 +444,16 @@ int main(int argc, char **argv) case 't': throttle_seconds = atoi(optarg); break; + +#ifdef RANGEDPORTS + case 'x': + max_port = strtol(optarg, &p, 10); + if (!*optarg || *p) + usage(argv[0], "Missing max port."); + if (max_port < 0 || max_port > USHRT_MAX) + usage(argv[0], "Invalid max port."); + break; +#endif case 'C': allow_dcc_chat = 0; @@ -467,6 +513,11 @@ int main(int argc, char **argv) break; } } + +#ifdef RANGEDPORTS + if (max_port < min_port) + usage(argv[0], "max port can not be smaller than min port."); +#endif /* Set a few variables to 'default' values. */ @@ -544,7 +595,7 @@ int main(int argc, char **argv) */ if (use_anonymity) { - srand(time((time_t) NULL)); + srand(time(NULL)); anon_notval = rand(); } @@ -639,7 +690,7 @@ int main(int argc, char **argv) /* Randomize (well, not really..) the anonymouse userid. */ - if (use_anonymity) anon_notval = (time((time_t) NULL) >> 17); + if (use_anonymity) anon_notval = (time(NULL) >> 17); /* Make note of the client's IP address. */ @@ -684,7 +735,12 @@ Options:\n",Version,prog); -p Require a valid Unix password for access.\n\ -q file Read a list of 'quizzes' from the named file.\n"); #endif +#ifdef RANGEDPORTS fprintf(stderr,"\ + -m minport Specify the minimum port to bind incoming DCC connects to.\n\ + -x maxport Specify the maxmium port to bind incoming DCC connects to.\n"); +#endif + fprintf(stderr,"\ -a Anonymous mode, hide as much info about the user as possible.\n\ -r user Run as the specified user in server mode.\n\ -t n Force a sleep(1) between connections under n seconds apart.\n\ @@ -853,58 +909,146 @@ static uid_t get_group_id(uid_t uid) return (gid); } - /* Bind to the specified ip and port. */ static int bind_to_port(ipaddr_t bind_ip, short bind_port, int backlog, int dlev) { + struct sockaddr_in addr; + int sock; + + /* Allocate a socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, + getprotobyname("tcp")->p_proto)) < 0) + { + debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno)); + return(-1); + } + +#ifdef TIRC_DEBUG + /* Set the SO_REUSEADDR option for debugging. + */ + if (debug_level >= DEBUG_NOFORK) { + int on = 1; + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + } +#endif + + /* Set the address to listen to. + */ + memset(&addr, '\0', sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(bind_port); + addr.sin_addr.s_addr = bind_ip; + + /* Bind our socket to the above address. + */ + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno)); + return(-1); + } + + /* Establish a large listen backlog. + */ + if (listen(sock, backlog) < 0) + { + debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno)); + return(-1); + } + + return (sock); +} + +#ifdef RANGEDPORTS +/* Bind to the specified ip and choose a port in the range of min_port and max_port. +*/ +static int bind_to_ranged_port(ipaddr_t bind_ip, short min_port, short max_port, int backlog, int dlev) + +{ struct sockaddr_in addr; int sock; - /* Allocate a socket. - */ - if ((sock = socket(AF_INET, SOCK_STREAM, - getprotobyname("tcp")->p_proto)) < 0) + int direction = 1; + int count; + short start_port; + + if (min_port > max_port) { - debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno)); - return(-1); + debug_msg(dlev, LOG_WARNING, "min_port > max_port"); + return(-1); } + + count = 1 + max_port - min_port; + + start_port = (arc4random() % count) + min_port; + while (count-- > 0) { + + /* Allocate a socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, + getprotobyname("tcp")->p_proto)) < 0) + { + debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno)); + return(-1); + } + #ifdef TIRC_DEBUG - /* Set the SO_REUSEADDR option for debugging. - */ - if (debug_level >= DEBUG_NOFORK) { - int on = 1; + /* Set the SO_REUSEADDR option for debugging. + */ + if (debug_level >= DEBUG_NOFORK) { + int on = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - } + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + } #endif - /* Set the address to listen to. - */ - memset(&addr, '\0', sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(bind_port); - addr.sin_addr.s_addr = bind_ip; + /* Set the address to listen to. + */ + memset(&addr, '\0', sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(start_port); + addr.sin_addr.s_addr = bind_ip; - /* Bind our socket to the above address. - */ - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) - { - debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno)); - return(-1); - } + /* Bind our socket to the above address. + */ + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + if (errno != EADDRINUSE) + { + debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno)); + close(sock); + return(-1); + } + /* It's in use, try the next port */ + close(sock); - /* Establish a large listen backlog. - */ - if (listen(sock, backlog) < 0) - { - debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno)); - return(-1); - } + start_port += direction; - return (sock); + if (start_port < min_port) + start_port = max_port; + else if (start_port > max_port) + start_port = min_port; + continue; + } + + /* Establish a large listen backlog. + */ + if (listen(sock, backlog) < 0) + { + debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno)); + close(sock); + return(-1); + } + + return (sock); + } + errno = EAGAIN; + return(-1); } +#endif /* Connect to the a server. @@ -1018,7 +1162,7 @@ static void server_main_loop(int sock) /* At most send the server one connection every ** throttle_seconds seconds. */ - this_time = time((time_t) NULL); + this_time = time(NULL); if (this_time <= last_time+delay+throttle_seconds) delay++; else delay = 0; @@ -1070,6 +1214,11 @@ static void trans_proxy(int sock, struct sockaddr_in * natlookup_t natlook; int fd; #endif +#ifdef PF + struct sockaddr_in ext, gwy; + struct pfioc_natlook natlook; + int fd; +#endif /* Give this thing 10 minutes to get started (paranoia). */ @@ -1166,6 +1315,50 @@ static void trans_proxy(int sock, struct sockaddr_in * to_addr.sin_family = AF_INET; to_addr.sin_port = htons(ntohs(natlook.nl_realport)); to_addr.sin_addr.s_addr = get_ip_addr(inet_ntoa(natlook.nl_realip)); +# else +# ifdef PF + to_len = sizeof(ext); + if (getpeername(sock, (struct sockaddr *)&ext, &to_len) == -1) + { + perror("getpeername"); + exit(-1); + } + + to_len = sizeof(gwy); + if (getsockname(sock, (struct sockaddr *)&gwy, &to_len) == -1) + { + perror("getsockname"); + exit(-1); + } + + if ((fd = open("/dev/pf", O_RDWR)) == -1) { + perror("open(\"/dev/pf\")"); + exit(-1); + } + + memset(&natlook, 0, sizeof(struct pfioc_natlook)); + natlook.af = AF_INET; + natlook.proto = IPPROTO_TCP; + natlook.direction = PF_OUT; /* s = ext, d = gwy, r = svr */ + natlook.saddr.v4.s_addr = ext.sin_addr.s_addr; + natlook.sport = ext.sin_port; + natlook.daddr.v4.s_addr = gwy.sin_addr.s_addr; + natlook.dport = gwy.sin_port; + + if (ioctl(fd, DIOCNATLOOK, &natlook) == -1) + { + perror("ioctl"); + close(fd); + exit(-1); + } + + close(fd); + + memset(&to_addr, 0, sizeof(to_addr)); + to_addr.sin_family = AF_INET; + to_addr.sin_port = natlook.rdport; + to_addr.sin_addr.s_addr = natlook.rdaddr.v4.s_addr; +# endif /* PF */ # endif /* IFP */ #endif /* LINUX */ } @@ -2047,9 +2240,15 @@ static char *proxy_dcc(int destaddr, int destport, int ** (re: BUGTRAQ, 1998, December 22 & 23) */ len = 10; - while ((listen_sock = bind_to_port((incoming) ? visible_ip_o : visible_ip_i, - ((rand() + getpid()) % PPOOL) + 1025, +#ifdef RANGEDPORTS + while ((listen_sock = bind_to_ranged_port((incoming) ? visible_ip_o : visible_ip_i, + min_port, max_port, 1, DEBUG_TRIVIA)) < 0) +#else + while ((listen_sock = bind_to_port((incoming) ? visible_ip_o : visible_ip_i, + ((rand() + getpid()) % PPOOL) + 1025, + 1, DEBUG_TRIVIA)) < 0) +#endif { if (--len < 1) { debug_msg(0, LOG_ERR, "Failed to bind to port, dropping DCC!"); @@ -2643,7 +2842,7 @@ static void quiz_greet(void) /* Not using /etc/passwd, select a question from the quizfile */ - srand(time((time_t) NULL)); + srand(time(NULL)); if ((fd = fopen(quizfile,"r")) == NULL) {