vBulletin Search Engine Optimization
| |||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| thanks, got that. just learned the convention here is diff -urN <orig> <new>. here's using diff -urN. Dries Schellekens wrote: > Ronnel P. Maglasang wrote: > >> apologies, the patch is incorrect, i diff'ed in the wrong direction. >> please disregard the previous one. >> >> this is the correct patch file. > > > OpenBSD developers prefer if you use diff -u > > > Cheers, > > Dries diff -urN ftp-proxy/Makefile ftp-proxy-new/Makefile --- ftp-proxy/Makefile Fri Nov 21 07:23:09 2003 +++ ftp-proxy-new/Makefile Fri Mar 18 16:34:47 2005 @@ -14,4 +14,7 @@ DPADD+= ${LIBWRAP} .endif +# For ipv6 support +CFLAGS+= -DIPV6_SUP + .include <bsd.prog.mk> diff -urN ftp-proxy/ftp-proxy.c ftp-proxy-new/ftp-proxy.c --- ftp-proxy/ftp-proxy.c Sun Jul 11 09:54:36 2004 +++ ftp-proxy-new/ftp-proxy.c Fri Mar 18 18:04:33 2005 @@ -55,6 +55,13 @@ * the real destination address - the tcp wrapper stuff is done after * the real destination address is retrieved from pf * + * Added support for IPv6. + * Changed all address structures to accomodate IPv4 and IPv6 addresses. + * Modified processing of EPRT. Added support for processing the response + * of LPRT to support IPv6 ftp session. + * + * - Ronnel Maglasang(rmaglasang@infoweapons.com) + * www.infoweapons.com */ /* @@ -125,11 +132,24 @@ double xfer_start_time; +#ifdef IPV6_SUP +struct sockaddr_storage real_server_sa; +struct sockaddr_storage client_listen_sa; +struct sockaddr_storage server_listen_sa; +struct sockaddr_storage proxy_sa; +int address_family; +#else struct sockaddr_in real_server_sa; struct sockaddr_in client_listen_sa; struct sockaddr_in server_listen_sa; struct sockaddr_in proxy_sa; +#endif + +#ifdef IPV6_SUP +struct sockaddr_storage src_addr; +#else struct in_addr src_addr; +#endif int client_listen_socket = -1; /* Only used in PASV mode */ int client_data_socket = -1; /* Connected socket to real client */ @@ -146,18 +166,33 @@ char RealServerName[NI_MAXHOST]; char OurName[NI_MAXHOST]; +#ifdef IPV6_SUP +char ClientPortName[NI_MAXHOST]; +char RealServerPortName[NI_MAXHOST]; +char OurPortName[NI_MAXHOST]; +#endif + char *User = "proxy"; char *Group; extern int Debug_Level; extern int Use_Rdns; +#ifdef IPV6_SUP +extern int Bind_Addr_Flag; +extern struct sockaddr_storage Bind_Addr; +#else extern in_addr_t Bind_Addr; +#endif extern char *__progname; typedef enum { UNKNOWN_MODE, PORT_MODE, PASV_MODE, +#ifdef IPV6_SUP + LPRT_MODE, + LPSV_MODE, +#endif EPRT_MODE, EPSV_MODE } connection_mode_t; @@ -246,16 +281,50 @@ * if we are set to do reverse DNS, otherwise no. */ static int +#ifdef IPV6_SUP +check_host(struct sockaddr_storage *client_sin, struct sockaddr_storage *server_sin) +#else check_host(struct sockaddr_in *client_sin, struct sockaddr_in *server_sin) +#endif { char cname[NI_MAXHOST]; char sname[NI_MAXHOST]; +#ifdef IPV6_SUP + char caddr[NI_MAXHOST]; + char saddr[NI_MAXHOST]; +#endif struct request_info request; int i; +#ifdef IPV6_SUP + /* retrieve client address */ + i = getnameinfo((struct sockaddr *)client_sin, + client_sin->ss_len, caddr, sizeof(caddr), + NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { + syslog(LOG_NOTICE, "1 getnameinfo() failed!\n"); + return(0); + } + + /* retrieve server address */ + i = getnameinfo((struct sockaddr *)server_sin, + server_sin->ss_len, saddr, sizeof(saddr), + NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { + syslog(LOG_NOTICE, "2 getnameinfo() failed!\n"); + return(0); + } + + request_init(&request, RQ_DAEMON, __progname, RQ_CLIENT_SIN, + client_sin, RQ_SERVER_SIN, server_sin, RQ_CLIENT_ADDR, + caddr, 0); +#else request_init(&request, RQ_DAEMON, __progname, RQ_CLIENT_SIN, client_sin, RQ_SERVER_SIN, server_sin, RQ_CLIENT_ADDR, inet_ntoa(client_sin->sin_addr), 0); +#endif if (Use_Rdns) { /* @@ -264,6 +333,21 @@ * the tcp wrapper cares about these things, and we don't * want to pass in a printed address as a name. */ +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)client_sin, + client_sin->ss_len, cname, sizeof(cname), + NULL, 0, NI_NAMEREQD); + + if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) + strlcpy(cname, STRING_UNKNOWN, sizeof(cname)); + + i = getnameinfo((struct sockaddr *)server_sin, + server_sin->ss_len, sname, sizeof(sname), + NULL, 0, NI_NAMEREQD); + + if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) + strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); +#else i = getnameinfo((struct sockaddr *) &client_sin->sin_addr, sizeof(&client_sin->sin_addr), cname, sizeof(cname), NULL, 0, NI_NAMEREQD); @@ -277,6 +361,7 @@ if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); +#endif } else { /* * ensure the TCP wrapper doesn't start doing @@ -286,8 +371,12 @@ strlcpy(sname, STRING_UNKNOWN, sizeof(sname)); } +#ifdef IPV6_SUP + request_set(&request, RQ_SERVER_ADDR, saddr, 0); +#else request_set(&request, RQ_SERVER_ADDR, inet_ntoa(server_sin->sin_addr), 0); +#endif request_set(&request, RQ_CLIENT_NAME, cname, RQ_SERVER_NAME, sname, 0); if (!hosts_access(&request)) { @@ -422,6 +511,12 @@ * Close existing data conn. */ +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "new_dataconn() server: [%d]\n", server); +#endif +#endif + if (client_listen_socket != -1) { close(client_listen_socket); client_listen_socket = -1; @@ -436,8 +531,13 @@ if (server) { bzero(&server_listen_sa, sizeof(server_listen_sa)); +#ifdef IPV6_SUP + server_listen_socket = get_backchannel_socket(address_family, + SOCK_STREAM, min_port, max_port, -1, 1, &server_listen_sa); +#else server_listen_socket = get_backchannel_socket(SOCK_STREAM, min_port, max_port, -1, 1, &server_listen_sa); +#endif if (server_listen_socket == -1) { syslog(LOG_INFO, "server socket bind() failed (%m)"); @@ -449,8 +549,13 @@ } } else { bzero(&client_listen_sa, sizeof(client_listen_sa)); +#ifdef IPV6_SUP + client_listen_socket = get_backchannel_socket(address_family, + SOCK_STREAM, min_port, max_port, -1, 1, &client_listen_sa); +#else client_listen_socket = get_backchannel_socket(SOCK_STREAM, min_port, max_port, -1, 1, &client_listen_sa); +#endif if (client_listen_socket == -1) { syslog(LOG_NOTICE, @@ -469,14 +574,32 @@ static void connect_pasv_backchannel(void) { +#ifdef IPV6_SUP + struct sockaddr_storage listen_sa; +#else struct sockaddr_in listen_sa; +#endif socklen_t salen; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + char host[NI_MAXHOST]; + char port[NI_MAXHOST]; +#endif +#endif /* * We are about to accept a connection from the client. * This is a PASV data connection. */ +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "client listen socket ready"); +#endif + debuglog(2, "client listen socket ready"); +#else + debuglog(2, "client listen socket ready"); +#endif close_server_data(); close_client_data(); @@ -493,14 +616,41 @@ client_listen_socket = -1; memset(&listen_sa, 0, sizeof(listen_sa)); +#ifdef IPV6_SUP + server_data_socket = get_backchannel_socket(address_family, SOCK_STREAM, min_port, + max_port, -1, 1, (struct sockaddr_storage *)&listen_sa); +#else server_data_socket = get_backchannel_socket(SOCK_STREAM, min_port, - max_port, -1, 1, &listen_sa); + max_port, -1, 1, &listen_sa); +#endif if (server_data_socket < 0) { syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); exit(EX_OSERR); } +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&server_listen_sa, + server_listen_sa.ss_len, + host, sizeof(host), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "1 server_data_socket: [%d], host: [%s], port: [%s]\n", + server_data_socket, host, port); + + getnameinfo((struct sockaddr *)&listen_sa, + listen_sa.ss_len, + host, sizeof(host), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "2 host: [%s], port: [%s]\n", host, port); +#endif + + if (connect(server_data_socket, (struct sockaddr *) &server_listen_sa, + server_listen_sa.ss_len) != 0) { +#else if (connect(server_data_socket, (struct sockaddr *) &server_listen_sa, sizeof(server_listen_sa)) != 0) { +#endif syslog(LOG_NOTICE, "connect() failed (%m)"); exit(EX_NOHOST); } @@ -512,14 +662,34 @@ static void connect_port_backchannel(void) { +#ifdef IPV6_SUP + struct sockaddr_storage listen_sa; + struct sockaddr_in *sockaddr4; + struct sockaddr_in6 *sockaddr6; +#else struct sockaddr_in listen_sa; +#endif socklen_t salen; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + char host[NI_MAXHOST]; + char port[NI_MAXHOST]; +#endif +#endif /* * We are about to accept a connection from the server. * This is a PORT or EPRT data connection. */ +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "server listen socket ready"); +#endif + debuglog(2, "server listen socket ready"); +#else + debuglog(2, "server listen socket ready"); +#endif close_server_data(); close_client_data(); @@ -531,6 +701,17 @@ syslog(LOG_NOTICE, "accept() failed (%m)"); exit(EX_OSERR); } +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&listen_sa, + listen_sa.ss_len, + host, sizeof(host), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "server_listen_socket: [%d], host: [%s], port: [%s]\n", + server_listen_socket, host, port); +#endif +#endif close(server_listen_socket); server_listen_socket = -1; @@ -541,9 +722,34 @@ * getting one bound to port 20 - This is deliberately * not RFC compliant. */ +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "running as non-root\n"); + syslog(LOG_INFO, "address_family: [%d]\n", address_family); +#endif +#endif + +#ifdef IPV6_SUP + /* clear address based on address family */ + if (address_family == AF_INET) { + sockaddr4 = (struct sockaddr_in *)&listen_sa; + bzero(&sockaddr4->sin_addr, sizeof(struct in_addr)); + } + else { + sockaddr6 = (struct sockaddr_in6 *)&listen_sa; + bzero(&sockaddr6->sin6_addr, sizeof(struct in6_addr)); + } +#else bzero(&listen_sa.sin_addr, sizeof(struct in_addr)); +#endif + +#ifdef IPV6_SUP + client_data_socket = get_backchannel_socket(address_family, SOCK_STREAM, + min_port, max_port, -1, 1, (struct sockaddr_storage *)&listen_sa); +#else client_data_socket = get_backchannel_socket(SOCK_STREAM, - min_port, max_port, -1, 1, &listen_sa); + min_port, max_port, -1, 1, &listen_sa); +#endif if (client_data_socket < 0) { syslog(LOG_NOTICE, "get_backchannel_socket() failed (%m)"); exit(EX_OSERR); @@ -555,12 +761,33 @@ * We're root, get our backchannel socket bound to port * 20 here, so we're fully RFC compliant. */ +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "running as root\n"); +#endif +#endif + +#ifdef IPV6_SUP + client_data_socket = socket(address_family, SOCK_STREAM, 0); + + salen = 1; + if (address_family == AF_INET) { + sockaddr4 = (struct sockaddr_in *)&listen_sa; + sockaddr4->sin_family = AF_INET; + bcopy(&((struct sockaddr_in *)&src_addr)->sin_addr, + &sockaddr4->sin_addr, sizeof(struct in_addr)); + sockaddr4->sin_port = htons(20); + } + else { + } +#else client_data_socket = socket(AF_INET, SOCK_STREAM, 0); salen = 1; listen_sa.sin_family = AF_INET; bcopy(&src_addr, &listen_sa.sin_addr, sizeof(struct in_addr)); listen_sa.sin_port = htons(20); +#endif if (setsockopt(client_data_socket, SOL_SOCKET, SO_REUSEADDR, &salen, sizeof(salen)) == -1) { @@ -575,8 +802,23 @@ } } +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&client_listen_sa, + client_listen_sa.ss_len, + host, sizeof(host), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "client_data_socket: [%d], host: [%s], port: [%s]\n", + client_data_socket, host, port); +#endif + + if (connect(client_data_socket, (struct sockaddr *) &client_listen_sa, + client_listen_sa.ss_len) != 0) { +#else if (connect(client_data_socket, (struct sockaddr *) &client_listen_sa, sizeof(client_listen_sa)) != 0) { +#endif syslog(LOG_INFO, "cannot connect data channel (%m)"); exit(EX_NOHOST); } @@ -592,9 +834,32 @@ int i, j, rv; char tbuf[100]; char *sendbuf = NULL; +#ifdef IPV6_SUP + char c_addr_str[NI_MAXHOST]; + char c_port_str[NI_MAXHOST]; + char s_addr_str[NI_MAXHOST]; + char s_port_str[NI_MAXHOST]; + int ret; +#endif log_control_command((char *)client->line_buffer, 1); +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "client->line_buffer: [%s]\n", (char *)client->line_buffer); +#endif +#endif + +#ifdef IPV6_SUP + ret = getnameinfo((struct sockaddr *)&server->sa, server->sa.ss_len, s_addr_str, + sizeof(s_addr_str), NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) { + strcpy(s_addr_str, "unknown server address"); + } +#endif + /* client->line_buffer is an ftp control command. * There is no reason for these to be very long. * In the interest of limiting buffer overrun attempts, @@ -667,11 +932,20 @@ goto protounsupp; memset(&hints, 0, sizeof(hints)); +#ifdef IPV6_SUP + /* Support for both IPV4 and IPV6 */ + if ((proto != 1) && (proto != 2)) + goto protounsupp; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; /*no DNS*/ +#else if (proto != 1) /* 1 == AF_INET - all we support for now */ goto protounsupp; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; /*no DNS*/ +#endif if (getaddrinfo(result[1], result[2], &hints, &res)) goto parsefail; if (res->ai_next) @@ -680,9 +954,28 @@ goto parsefail; memcpy(&client_listen_sa, res->ai_addr, res->ai_addrlen); +#ifdef IPV6_SUP + ret = getnameinfo(res->ai_addr, res->ai_addrlen, c_addr_str, + sizeof(c_addr_str), c_port_str, sizeof(c_port_str), + NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) { + strcpy(c_addr_str, "unknown client address"); + strcpy(c_port_str, "unknown client port"); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "client wants us to use %s:%s", + c_addr_str, c_port_str); +#endif + + debuglog(1, "client wants us to use %s:%s", + c_addr_str, c_port_str); +#else debuglog(1, "client wants us to use %s:%u", inet_ntoa(client_listen_sa.sin_addr), htons(client_listen_sa.sin_port)); +#endif /* * Configure our own listen socket and tell the server about it @@ -690,13 +983,42 @@ new_dataconn(1); connection_mode = EPRT_MODE; +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)&server_listen_sa, server_listen_sa.ss_len, NULL, + 0, s_port_str, sizeof(s_port_str), + NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) { + strcpy(s_port_str, "unknown server port"); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "we want server to use %s:%s", + s_addr_str, s_port_str); +#endif + + debuglog(1, "we want server to use %s:%s", + s_addr_str, s_port_str); +#else debuglog(1, "we want server to use %s:%u", inet_ntoa(server->sa.sin_addr), ntohs(server_listen_sa.sin_port)); +#endif +#ifdef IPV6_SUP + snprintf(tbuf, sizeof(tbuf), "EPRT |%u|%s|%s|\r\n", (unsigned int)proto, + s_addr_str, s_port_str); +#else snprintf(tbuf, sizeof(tbuf), "EPRT |%d|%s|%u|\r\n", 1, inet_ntoa(server->sa.sin_addr), ntohs(server_listen_sa.sin_port)); +#endif + +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "to server (modified): %s", tbuf); +#endif +#endif debuglog(1, "to server (modified): %s", tbuf); sendbuf = tbuf; goto out; @@ -706,6 +1028,11 @@ sendbuf = NULL; goto out; protounsupp: +#ifdef IPV6_SUP + /* we support AF_INET and AF_INET6 now */ + snprintf(tbuf, sizeof(tbuf), + "501 Protocol not supported\r\n"); +#else /* we only support AF_INET for now */ if (proto == 2) snprintf(tbuf, sizeof(tbuf), @@ -713,6 +1040,7 @@ else snprintf(tbuf, sizeof(tbuf), "501 Protocol not supported\r\n"); +#endif sendbuf = NULL; out: if (line) @@ -765,6 +1093,11 @@ strlen("port ")) == 0) { unsigned int values[6]; char *tailptr; +#ifdef IPV6_SUP + struct sockaddr_in *client_sockaddr_in; + struct sockaddr_in *server_sockaddr_in; + struct sockaddr_in *server_listen_sockaddr_in; +#endif debuglog(1, "Got a PORT command"); @@ -789,6 +1122,19 @@ } } +#ifdef IPV6_SUP + client_sockaddr_in = (struct sockaddr_in *)&client_listen_sa; + client_sockaddr_in->sin_family = AF_INET; + client_sockaddr_in->sin_addr.s_addr = htonl((values[0] << 24) | + (values[1] << 16) | (values[2] << 8) | + (values[3] << 0)); + + client_sockaddr_in->sin_port = htons((values[4] << 8) | + values[5]); + debuglog(1, "client wants us to use %u.%u.%u.%u:%u", + values[0], values[1], values[2], values[3], + (values[4] << 8) | values[5]); +#else client_listen_sa.sin_family = AF_INET; client_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | (values[1] << 16) | (values[2] << 8) | @@ -799,6 +1145,7 @@ debuglog(1, "client wants us to use %u.%u.%u.%u:%u", values[0], values[1], values[2], values[3], (values[4] << 8) | values[5]); +#endif /* * Configure our own listen socket and tell the server about it @@ -806,10 +1153,39 @@ new_dataconn(1); connection_mode = PORT_MODE; +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)&server_listen_sa, server_listen_sa.ss_len, NULL, + 0, s_port_str, sizeof(s_port_str), + NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) { + strcpy(s_port_str, "unknown server port"); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "we want server to use %s:%s", + s_addr_str, s_port_str); +#endif + + debuglog(1, "we want server to use %s:%s", + s_addr_str, s_port_str); +#else debuglog(1, "we want server to use %s:%u", inet_ntoa(server->sa.sin_addr), ntohs(server_listen_sa.sin_port)); +#endif +#ifdef IPV6_SUP + server_sockaddr_in = (struct sockaddr_in *)&server->sa; + server_listen_sockaddr_in = (struct sockaddr_in *)&server_listen_sa; + snprintf(tbuf, sizeof(tbuf), "PORT %u,%u,%u,%u,%u,%u\r\n", + ((u_char *)&server_sockaddr_in->sin_addr.s_addr)[0], + ((u_char *)&server_sockaddr_in->sin_addr.s_addr)[1], + ((u_char *)&server_sockaddr_in->sin_addr.s_addr)[2], + ((u_char *)&server_sockaddr_in->sin_addr.s_addr)[3], + ((u_char *)&server_listen_sockaddr_in->sin_port)[0], + ((u_char *)&server_listen_sockaddr_in->sin_port)[1]); +#else snprintf(tbuf, sizeof(tbuf), "PORT %u,%u,%u,%u,%u,%u\r\n", ((u_char *)&server->sa.sin_addr.s_addr)[0], ((u_char *)&server->sa.sin_addr.s_addr)[1], @@ -817,7 +1193,13 @@ ((u_char *)&server->sa.sin_addr.s_addr)[3], ((u_char *)&server_listen_sa.sin_port)[0], ((u_char *)&server_listen_sa.sin_port)[1]); +#endif +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "to server (modified): %s", tbuf); +#endif +#endif debuglog(1, "to server (modified): %s", tbuf); sendbuf = tbuf; @@ -848,9 +1230,25 @@ struct in_addr *iap; static int continuing = 0; char tbuf[100], *sendbuf, *p; +#ifdef IPV6_SUP + struct sockaddr_in *client_sockaddr_in; + struct sockaddr_in *server_sockaddr_in; + struct sockaddr_in *proxy_sockaddr_in; + struct sockaddr_in6 *client_sockaddr_in6; + struct sockaddr_in6 *server_sockaddr_in6; + struct sockaddr_in6 *use_sockaddr_in6; + char host[NI_MAXHOST]; + char port[NI_MAXHOST]; +#endif log_control_command((char *)server->line_buffer, 0); +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "server->line_buffer: [%s]\n", (char *)server->line_buffer); +#endif +#endif + if (strlen((char *)server->line_buffer) > 512) { /* * someone's playing games. Have a cow in the syslogs and @@ -887,6 +1285,13 @@ unsigned int values[6]; char *tailptr; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "PASV reply\n"); + syslog(LOG_INFO, "address_family: [%d]\n", address_family); +#endif +#endif + debuglog(1, "Got a PASV reply"); debuglog(1, "{%s}", (char *)server->line_buffer); @@ -917,6 +1322,32 @@ exit(EX_DATAERR); } +#ifdef IPV6_SUP + if (address_family == AF_INET) { + server_sockaddr_in = (struct sockaddr_in *)&server_listen_sa; + server_sockaddr_in->sin_family = AF_INET; + server_sockaddr_in->sin_len = sizeof(struct sockaddr_in); + server_sockaddr_in->sin_addr.s_addr = htonl((values[0] << 24) | + (values[1] << 16) | (values[2] << 8) | (values[3] << 0)); + server_sockaddr_in->sin_port = htons((values[4] << 8) | + values[5]); + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "server wants us to use %s:%u", + inet_ntoa(server_sockaddr_in->sin_addr), (values[4] << 8) | + values[5]); +#endif + + debuglog(1, "server wants us to use %s:%u", + inet_ntoa(server_sockaddr_in->sin_addr), (values[4] << 8) | + values[5]); + } + else { + syslog(LOG_NOTICE, "failed in do_server_reply(), unknown af: [%d]\n", + address_family); + exit(EX_DATAERR); + } +#else server_listen_sa.sin_family = AF_INET; server_listen_sa.sin_addr.s_addr = htonl((values[0] << 24) | (values[1] << 16) | (values[2] << 8) | (values[3] << 0)); @@ -926,9 +1357,42 @@ debuglog(1, "server wants us to use %s:%u", inet_ntoa(server_listen_sa.sin_addr), (values[4] << 8) | values[5]); +#endif new_dataconn(0); connection_mode = PASV_MODE; + +#ifdef IPV6_SUP + if (ReverseMode) { + if (address_family == AF_INET) { + proxy_sockaddr_in = (struct sockaddr_in *)&proxy_sa; + iap = &(proxy_sockaddr_in->sin_addr); + } + } + else { + if (address_family == AF_INET) { + server_sockaddr_in = (struct sockaddr_in *)&server->sa; + iap = &(server_sockaddr_in->sin_addr); + } + } + + if (address_family == AF_INET) { + client_sockaddr_in = (struct sockaddr_in *)&client_listen_sa; +#ifdef DEBUG_LOG + syslog(LOG_INFO, "we want client to use %s:%u", inet_ntoa(*iap), + htons(client_sockaddr_in->sin_port)); +#endif + debuglog(1, "we want client to use %s:%u", inet_ntoa(*iap), + htons(client_sockaddr_in->sin_port)); + + snprintf(tbuf, sizeof(tbuf), + "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n", + ((u_char *)iap)[0], ((u_char *)iap)[1], + ((u_char *)iap)[2], ((u_char *)iap)[3], + ((u_char *)&client_sockaddr_in->sin_port)[0], + ((u_char *)&client_sockaddr_in->sin_port)[1]); + } +#else if (ReverseMode) iap = &(proxy_sa.sin_addr); else @@ -943,9 +1407,264 @@ ((u_char *)iap)[2], ((u_char *)iap)[3], ((u_char *)&client_listen_sa.sin_port)[0], ((u_char *)&client_listen_sa.sin_port)[1]); +#endif + debuglog(1, "to client (modified): %s", tbuf); sendbuf = tbuf; - } else { + } +#ifdef IPV6_SUP + /* + * Hack to support IPV6. + * Process LPSV response which includes IPV6 packets. + * Basically same processing with PASV. + */ + else if (code == 228 && !NatMode) { + + unsigned int af = 0; + unsigned int hal = 0; + unsigned int pal = 0; + unsigned int h_values[16]; + unsigned int p_values[2]; + char *tailptr; + int x; + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "LPSV reply\n"); + syslog(LOG_INFO, "address_family: [%d]\n", address_family); +#endif + + /* support only IPV6 */ + if (address_family != AF_INET6) { + syslog(LOG_NOTICE, "for LPSV only IPv6 is supported"); + exit(EX_DATAERR); + } + + debuglog(1, "Got a LPSV reply"); + debuglog(1, "{%s}", (char *)server->line_buffer); + + tailptr = (char *)strchr((char *)server->line_buffer, '('); + if (tailptr == NULL) { + tailptr = strrchr((char *)server->line_buffer, ' '); + if (tailptr == NULL) { + syslog(LOG_NOTICE, "malformed 228 reply"); + exit(EX_DATAERR); + } + } + + tailptr++; /* skip past space or ( */ + + /* read af */ + i = sscanf(tailptr, "%u", &af); + if (i != 1) { + syslog(LOG_INFO, "1 malformed LPSV reply (%s)", + client->line_buffer); + exit(EX_DATAERR); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "af: [%d]\n", af); +#endif + + /* move past af */ + tailptr = (char *)strchr(tailptr, ','); + if (tailptr == NULL) { + syslog(LOG_NOTICE, "malformed 228 reply, af not found"); + exit(EX_DATAERR); + } + + tailptr++; + + /* read address length */ + i = sscanf(tailptr, "%u", &hal); + if (i != 1) { + syslog(LOG_INFO, "2 malformed LPSV reply (%s)", + client->line_buffer); + exit(EX_DATAERR); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "hal: [%d]\n", hal); +#endif + /* we only support a hack for IPV6 for now */ + if (hal != 16) { + syslog(LOG_NOTICE, "malformed 228 reply, address length not supported"); + exit(EX_DATAERR); + } + + /* move past hal */ + tailptr = (char *)strchr(tailptr, ','); + if (tailptr == NULL) { + syslog(LOG_NOTICE, "malformed 228 reply, hal not found"); + exit(EX_DATAERR); + } + + tailptr++; + + /* read address */ + bzero(&h_values, sizeof(h_values)); + i = sscanf(tailptr, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u, %u", + &h_values[0], &h_values[1], &h_values[2], &h_values[3], + &h_values[4], &h_values[5], &h_values[6], &h_values[7], + &h_values[8], &h_values[9], &h_values[10], &h_values[11], + &h_values[12], &h_values[13], &h_values[14], &h_values[15]); + if (i != 16) { + syslog(LOG_INFO, "3 malformed LPSV reply (%s)", + client->line_buffer); + exit(EX_DATAERR); + } + + /* move past address */ +#ifdef DEBUG_LOG + syslog(LOG_INFO, "h_values: "); +#endif + for (x = 0; x < i; x++) { +#ifdef DEBUG_LOG + syslog(LOG_INFO, "h_values[%d]: %d\n", x, h_values[x]); +#endif + if (tailptr != NULL) { + tailptr = (char *)strchr(tailptr, ','); + if (tailptr != NULL) + tailptr++; + } + } + + if (tailptr == NULL) { + syslog(LOG_NOTICE, "malformed 228 reply, adderess not found"); + exit(EX_DATAERR); + } + + /* read port length */ + i = sscanf(tailptr, "%u", &pal); + if (i != 1) { + syslog(LOG_INFO, "4 malformed LPSV reply (%s)", + client->line_buffer); + exit(EX_DATAERR); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "pal: [%d]\n", pal); +#endif + /* we only support a hack for IPV6 for now */ + if (pal != 2) { + syslog(LOG_NOTICE, "malformed 228 reply, port length not supported"); + exit(EX_DATAERR); + } + + /* move past pal */ + tailptr = (char *)strchr(tailptr, ','); + if (tailptr == NULL) { + syslog(LOG_NOTICE, "malformed 228 reply, pal not found"); + exit(EX_DATAERR); + } + tailptr++; + + /* read port */ + bzero(&p_values, sizeof(p_values)); + i = sscanf(tailptr, "%u,%u", &p_values[0], &p_values[1]); + if (i != 2) { + syslog(LOG_INFO, "5 malformed LPSV reply (%s)", + client->line_buffer); + exit(EX_DATAERR); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "p_values: "); +#endif + for (x = 0; x < i; x++) { +#ifdef DEBUG_LOG + syslog(LOG_INFO, "p_values[%d]: %d\n", x, p_values[x]); +#endif + + if (tailptr != NULL) + tailptr = (char *)strchr(tailptr, ','); + } + + if (address_family == AF_INET6) { + bzero(&server_listen_sa, sizeof(server_listen_sa)); + server_sockaddr_in6 = (struct sockaddr_in6 *)&server_listen_sa; + server_sockaddr_in6->sin6_family = AF_INET6; + server_sockaddr_in6->sin6_len = sizeof(struct sockaddr_in6); + server_sockaddr_in6->sin6_port = htons((p_values[0] << 8) | p_values[1]); + server_sockaddr_in6->sin6_addr.s6_addr[0] = h_values[0]; + server_sockaddr_in6->sin6_addr.s6_addr[1] = h_values[1]; + server_sockaddr_in6->sin6_addr.s6_addr[2] = h_values[2]; + server_sockaddr_in6->sin6_addr.s6_addr[3] = h_values[3]; + server_sockaddr_in6->sin6_addr.s6_addr[4] = h_values[4]; + server_sockaddr_in6->sin6_addr.s6_addr[5] = h_values[5]; + server_sockaddr_in6->sin6_addr.s6_addr[6] = h_values[6]; + server_sockaddr_in6->sin6_addr.s6_addr[7] = h_values[7]; + server_sockaddr_in6->sin6_addr.s6_addr[8] = h_values[8]; + server_sockaddr_in6->sin6_addr.s6_addr[9] = h_values[9]; + server_sockaddr_in6->sin6_addr.s6_addr[10] = h_values[10]; + server_sockaddr_in6->sin6_addr.s6_addr[11] = h_values[11]; + server_sockaddr_in6->sin6_addr.s6_addr[12] = h_values[12]; + server_sockaddr_in6->sin6_addr.s6_addr[13] = h_values[13]; + server_sockaddr_in6->sin6_addr.s6_addr[14] = h_values[14]; + server_sockaddr_in6->sin6_addr.s6_addr[15] = h_values[15]; + + getnameinfo((struct sockaddr *)&server_listen_sa, server_listen_sa.ss_len, + host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV); + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "server wants us to use %s:%s\n", host, port); +#endif + } + + new_dataconn(0); + + connection_mode = LPSV_MODE; + + if (ReverseMode) { + if (address_family == AF_INET6) { + use_sockaddr_in6 = (struct sockaddr_in6 *)&proxy_sa; + } + } + else { + if (address_family == AF_INET6) { + use_sockaddr_in6 = (struct sockaddr_in6 *)&server->sa; + } + } + + if (address_family == AF_INET6) { + client_sockaddr_in6 = (struct sockaddr_in6 *)&client_listen_sa; +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&use_sockaddr_in6, use_sockaddr_in6->sin6_len, + host, sizeof(host), NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV); + syslog(LOG_INFO, "we want client to use %s:%u", host, + htons(client_sockaddr_in6->sin6_port)); +#endif + snprintf(tbuf, sizeof(tbuf), + "228 Entering Long Passive Mode (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,% u,%u,%u,%u,%u)\r\n", + 6, 16, use_sockaddr_in6->sin6_addr.s6_addr[0], + use_sockaddr_in6->sin6_addr.s6_addr[1], + use_sockaddr_in6->sin6_addr.s6_addr[2], + use_sockaddr_in6->sin6_addr.s6_addr[3], + use_sockaddr_in6->sin6_addr.s6_addr[4], + use_sockaddr_in6->sin6_addr.s6_addr[5], + use_sockaddr_in6->sin6_addr.s6_addr[6], + use_sockaddr_in6->sin6_addr.s6_addr[7], + use_sockaddr_in6->sin6_addr.s6_addr[8], + use_sockaddr_in6->sin6_addr.s6_addr[9], + use_sockaddr_in6->sin6_addr.s6_addr[10], + use_sockaddr_in6->sin6_addr.s6_addr[11], + use_sockaddr_in6->sin6_addr.s6_addr[12], + use_sockaddr_in6->sin6_addr.s6_addr[13], + use_sockaddr_in6->sin6_addr.s6_addr[14], + use_sockaddr_in6->sin6_addr.s6_addr[15], + 2, ((u_char *)&client_sockaddr_in6->sin6_port)[0], + ((u_char *)&client_sockaddr_in6->sin6_port)[1]); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "to client (modified): %s", tbuf); +#endif + debuglog(1, "to client (modified): %s", tbuf); + sendbuf = tbuf; + } +#endif + else { sendit: sendbuf = (char *)server->line_buffer; } @@ -963,7 +1682,6 @@ else if (rv != -1) j += rv; } while (j >= 0 && j < i); - } int @@ -979,6 +1697,26 @@ #ifdef LIBWRAP int use_tcpwrapper = 0; #endif /* LIBWRAP */ +#ifdef IPV6_SUP + struct addrinfo hints; + struct addrinfo *res; +#ifdef DEBUG_LOG + char addr_str[NI_MAXHOST]; + char port_str[NI_MAXHOST]; +#endif +#endif + +#ifdef IPV6_SUP + openlog(__progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); +#ifdef DEBUG_LOG + syslog(LOG_INFO, "Starting ftp-proxy... argc: [%d]\n", argc); + + for (i=0; i< argc; i++) + { + syslog(LOG_INFO, " argv[%d]: [%s]\n", i, argv[i]); + } +#endif +#endif while ((ch = getopt(argc, argv, "a char *p; @@ -986,11 +1724,36 @@ case 'a': if (!*optarg) usage(); +#ifdef IPV6_SUP + Bind_Addr_Flag = 1; + + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(optarg, NULL, &hints, &res) != 0) { + syslog(LOG_NOTICE, + "%s: invalid address", optarg); + usage(); + } + + memcpy(&Bind_Addr, res->ai_addr, res->ai_addrlen); + +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&Bind_Addr, + Bind_Addr.ss_len, addr_str, sizeof(addr_str), + NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "Bind Address: [%s]\n", addr_str); +#endif + freeaddrinfo(res); +#else if ((Bind_Addr = inet_addr(optarg)) == INADDR_NONE) { syslog(LOG_NOTICE, "%s: invalid address", optarg); usage(); } +#endif break; case 'A': AnonFtpOnly = 1; /* restrict to anon usernames only */ @@ -1024,6 +1787,82 @@ Use_Rdns = 1; /* look up hostnames */ break; case 'R': { +#ifdef IPV6_SUP + char *s, *t, *b, *a, *p; + + if (!*optarg) + usage(); + + if ((s = strdup(optarg)) == NULL) { + syslog (LOG_NOTICE, + "Insufficient memory (malloc failed)"); + exit(EX_UNAVAILABLE); + } + + a = strchr(s, '['); + if (a != NULL) { + /* IPV6 address */ + a++; + + if (a == NULL) { + syslog(LOG_NOTICE, + "%s: invalid address or port", optarg); + usage(); + } + + p = strchr(a, ']'); + if (p == NULL) { + syslog(LOG_NOTICE, + "%s: invalid address or port", optarg); + usage(); + } + + b = p; + + p = strchr(p, ':'); + if (p == NULL) { + p = "21"; + } + else { + p++; + } + + *b = 0; + } + else { + /* IPV6 address */ + a = s; + t = strchr(s, ':'); + if (t == NULL) + p = "21"; + else { + p = t+1; + *t = 0; + } + } + + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(a, p, &hints, &res) != 0) { + syslog(LOG_NOTICE, + "%s: invalid address or port", optarg); + usage(); + } + + memcpy(&real_server_sa, res->ai_addr, res->ai_addrlen); + +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&real_server_sa, + real_server_sa.ss_len, addr_str, sizeof(addr_str), + port_str, sizeof(port_str), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "Reverse Address: [%s]/[%s]\n", addr_str, port_str); +#endif + freeaddrinfo(res); + free(s); +#else char *s, *t; if (!*optarg) @@ -1051,12 +1890,34 @@ if (real_server_sa.sin_addr.s_addr == INADDR_NONE) usage(); free(s); +#endif ReverseMode = 1; break; } case 'S': +#ifdef IPV6_SUP + bzero(&hints, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(optarg, NULL, &hints, &res) != 0) { + usage(); + } + + memcpy(&src_addr, res->ai_addr, res->ai_addrlen); + +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&src_addr, + src_addr.ss_len, addr_str, sizeof(addr_str), + NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "Source Address: [%s]\n", addr_str); +#endif + freeaddrinfo(res); +#else if (!inet_aton(optarg, &src_addr)) usage(); +#endif break; case 't': timeout_seconds = strtol(optarg, &p, 10); @@ -1085,7 +1946,9 @@ if (max_port < min_port) usage(); +#ifndef IPV6_SUP openlog(__progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); +#endif setlinebuf(stdout); setlinebuf(stderr); @@ -1118,21 +1981,36 @@ else flags = NI_NUMERICHOST | NI_NUMERICSERV; +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)&client_iob.sa, + client_iob.sa.ss_len, ClientName, sizeof(ClientName), + ClientPortName, sizeof(ClientPortName), + flags); +#else i = getnameinfo((struct sockaddr *)&client_iob.sa, sizeof(client_iob.sa), ClientName, sizeof(ClientName), NULL, 0, flags); +#endif if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { debuglog(2, "name resolution failure (client)"); + syslog(LOG_INFO, "1 name resolution failure: [%d]\n", i); exit(EX_OSERR); } +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)&real_server_sa, + real_server_sa.ss_len, RealServerName, sizeof(RealServerName), + RealServerPortName, sizeof(RealServerPortName), flags); +#else i = getnameinfo((struct sockaddr *)&real_server_sa, sizeof(real_server_sa), RealServerName, sizeof(RealServerName), NULL, 0, flags); +#endif if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { debuglog(2, "name resolution failure (server)"); + syslog(LOG_INFO, "2 name resolution failure: [%d]\n", i); exit(EX_OSERR); } @@ -1143,17 +2021,35 @@ client_iob.fd = 0; +#ifdef IPV6_SUP + syslog(LOG_INFO, "accepted connection from %s:%s to %s:%s", ClientName, + ClientPortName, RealServerName, RealServerPortName); +#else syslog(LOG_INFO, "accepted connection from %s:%u to %s:%u", ClientName, ntohs(client_iob.sa.sin_port), RealServerName, ntohs(real_server_sa.sin_port)); +#endif +#ifdef IPV6_SUP + server_iob.fd = get_backchannel_socket(real_server_sa.ss_family, + SOCK_STREAM, min_port, max_port, + -1, 1, &server_iob.sa); +#else server_iob.fd = get_backchannel_socket(SOCK_STREAM, min_port, max_port, -1, 1, &server_iob.sa); +#endif +#ifdef IPV6_SUP + if (connect(server_iob.fd, (struct sockaddr *)&real_server_sa, + real_server_sa.ss_len) != 0) { + syslog(LOG_INFO, "cannot connect to %s:%s (%m)", RealServerName, + RealServerPortName); +#else if (connect(server_iob.fd, (struct sockaddr *)&real_server_sa, sizeof(real_server_sa)) != 0) { syslog(LOG_INFO, "cannot connect to %s:%u (%m)", RealServerName, ntohs(real_server_sa.sin_port)); +#endif exit(EX_NOHOST); } @@ -1165,16 +2061,32 @@ salen = sizeof(server_iob.sa); getsockname(server_iob.fd, (struct sockaddr *)&server_iob.sa, &salen); +#ifdef IPV6_SUP + i = getnameinfo((struct sockaddr *)&server_iob.sa, + server_iob.sa.ss_len, OurName, sizeof(OurName), + OurPortName, sizeof(OurPortName), flags); +#else i = getnameinfo((struct sockaddr *)&server_iob.sa, sizeof(server_iob.sa), OurName, sizeof(OurName), NULL, 0, flags); +#endif if (i != 0 && i != EAI_NONAME && i != EAI_AGAIN) { debuglog(2, "name resolution failure (local)"); exit(EX_OSERR); } +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "local socket is %s:%s", OurName, + OurPortName); +#endif + + debuglog(1, "local socket is %s:%s", OurName, + OurPortName); +#else debuglog(1, "local socket is %s:%u", OurName, ntohs(server_iob.sa.sin_port)); +#endif /* ignore SIGPIPE */ bzero(&new_sa, sizeof(new_sa)); @@ -1224,6 +2136,12 @@ int maxfd = 0; fd_set *fdsp; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "main loop\n"); +#endif +#endif + if (client_iob.fd > maxfd) maxfd = client_iob.fd; if (client_listen_socket > maxfd) @@ -1237,6 +2155,13 @@ if (server_data_socket > maxfd) maxfd = server_data_socket; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "client is %s; server is %s", + client_iob.alive ? "alive" : "dead", + server_iob.alive ? "alive" : "dead"); +#endif +#endif debuglog(3, "client is %s; server is %s", client_iob.alive ? "alive" : "dead", server_iob.alive ? "alive" : "dead"); @@ -1303,12 +2228,25 @@ FD_ISSET(client_data_socket, fdsp)) { int rval; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "transfer: client to server"); +#endif +#endif debuglog(3, "transfer: client to server"); +#ifdef IPV6_SUP + rval = xfer_data("client to server", + client_data_socket, + server_data_socket, + &client_iob.sa, + &real_server_sa); +#else rval = xfer_data("client to server", client_data_socket, server_data_socket, client_iob.sa.sin_addr, real_server_sa.sin_addr); +#endif if (rval <= 0) { close_client_data(); close_server_data(); @@ -1320,12 +2258,25 @@ FD_ISSET(server_data_socket, fdsp)) { int rval; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + syslog(LOG_INFO, "transfer: server to client"); +#endif +#endif debuglog(3, "transfer: server to client"); +#ifdef IPV6_SUP + rval = xfer_data("server to client", + server_data_socket, + client_data_socket, + &real_server_sa, + &client_iob.sa); +#else rval = xfer_data("server to client", server_data_socket, client_data_socket, real_server_sa.sin_addr, client_iob.sa.sin_addr); +#endif if (rval <= 0) { close_client_data(); close_server_data(); diff -urN ftp-proxy/ftp-proxy.cat8 ftp-proxy-new/ftp-proxy.cat8 --- ftp-proxy/ftp-proxy.cat8 Thu Jan 1 08:00:00 1970 +++ ftp-proxy-new/ftp-proxy.cat8 Fri Mar 18 16:37:40 2005 @@ -0,0 +1,170 @@ +FTP-PROXY(8) OpenBSD System Manager's Manual FTP-PROXY(8) + +NNAAMMEE + ffttpp--pprrooxxyy - Internet File Transfer Protocol proxy server + +SSYYNNOOPPSSIISS + ffttpp--pprrooxxyy [--AAnnrrVVww] [--aa _a_d_d_r_e_s_s] [--DD _d_e_b_u_g_l_e_v_e_l] [--gg _g_r_o_u_p] [--MM _m_a_x_p_o_r_t] + [--mm _m_i_n_p_o_r_t] [--RR _a_d_d_r_e_s_s_[_:_p_o_r_t_]] [--SS _a_d_d_r_e_s_s] [--tt _t_i_m_e_o_u_t] [--uu + _u_s_e_r] + +DDEESSCCRRIIPPTTIIOONN + ffttpp--pprrooxxyy is a proxy for the Internet File Transfer Protocol. The proxy + uses pf(4) and expects to have the FTP control connection as described in + services(5) redirected to it via a pf(4) _r_d_r command. An example of how + to do that is further down in this document. + + The options are as follows: + + --AA Permit only anonymous FTP connections. The proxy will allow con- + nections to log in to other sites as the user "ftp" or + "anonymous" only. Any attempt to log in as another user will be + blocked by the proxy. + + --aa _a_d_d_r_e_s_s + Specify the local IP address to use in bind(2) as the source for + connections made by ffttpp--pprrooxxyy when connecting to destination FTP + servers. This may be necessary if the interface address of your + default route is not reachable from the destinations ffttpp--pprrooxxyy is + attempting connections to, or this address is different from the + one connections are being NATed to. In the usual case this means + that _a_d_d_r_e_s_s should be a publicly visible IP address assigned to + one of the interfaces on the machine running ffttpp--pprrooxxyy and should + be the same address to which you are translating traffic if you + are using the --nn option. + + --DD _d_e_b_u_g_l_e_v_e_l + Specify a debug level, where the proxy emits verbose debug output + into syslogd(8) at level LOG_DEBUG. Meaningful values of debu- + glevel are 0-3, where 0 is no debug output and 3 is lots of debug + output, the default being 0. + + --gg _g_r_o_u_p + Specify the named group to drop group privileges to, after doing + pf(4) lookups which require root. By default, ffttpp--pprrooxxyy uses the + default group of the user it drops privilege to. + + --MM _m_a_x_p_o_r_t + Specify the upper end of the port range the proxy will use for + the data connections it establishes. The default is + IPPORT_HILASTAUTO defined in <_n_e_t_i_n_e_t_/_i_n_._h> as 65535. + + --mm _m_i_n_p_o_r_t + Specify the lower end of the port range the proxy will use for + all data connections it establishes. The default is + IPPORT_HIFIRSTAUTO defined in <_n_e_t_i_n_e_t_/_i_n_._h> as 49152. + + --nn Activate network address translation (NAT) mode. In this mode, + the proxy will not attempt to proxy passive mode (PASV or EPSV) + data connections. In order for this to work, the machine running + the proxy will need to be forwarding packets and doing network + address translation to allow the outbound passive connections + from the client to reach the server. See pf.conf(5) for more de- + tails on NAT. The proxy only ignores passive mode data connec- + tions when using this flag; it will still proxy PORT and EPRT + mode data connections. Without this flag, ffttpp--pprrooxxyy does not re- + quire any IP forwarding or NAT beyond the _r_d_r necessary to cap- + ture the FTP control connection. + + --rr Use reverse host (reverse DNS) lookups for logging and libwrap + use. By default, the proxy does not look up hostnames for lib- + wrap or logging purposes. + + --RR _a_d_d_r_e_s_s_:_[_p_o_r_t_] + Reverse proxy mode for FTP servers running behind a NAT gateway. + In this mode, no redirection is needed. The proxy is run from + inetd(8) on the port that external clients connect to (usually + 21). Control connections and passive data connections are for- + warded to the server. + + --SS _a_d_d_r_e_s_s + Source address to use for data connections made by the proxy. + Useful when there are multiple addresses (aliases) available to + the proxy. Clients may expect data connections to have the same + source address as the control connections, and reject or drop + other connections. + + --tt _t_i_m_e_o_u_t + Specifies a timeout, in seconds. The proxy will exit and close + open connections if it sees no data for the duration of the time- + out. The default is 0, which means the proxy will not time out. + + --uu _u_s_e_r + Specify the named user to drop privilege to, after doing pf(4) + lookups which require root privilege. By default, ffttpp--pprrooxxyy + drops privilege to the user _p_r_o_x_y. + + Running as root means that the source of data connections the + proxy makes for PORT and EPRT will be the RFC mandated port 20. + When running as a non-root user, the source of the data connec- + tions from ffttpp--pprrooxxyy will be chosen randomly from the range + _m_i_n_p_o_r_t to _m_a_x_p_o_r_t as described above. + + --VV Be verbose. With this option the proxy logs the control commands + sent by clients and the replies sent by the servers to + syslogd(8). + + --ww Use the tcp wrapper access control library hosts_access(3), al- + lowing connections to be allowed or denied based on the tcp wrap- + per's hosts.allow(5) and hosts.deny(5) files. The proxy does + libwrap operations after determining the destination of the cap- + tured control connection, so that tcp wrapper rules may be writ- + ten based on the destination as well as the source of FTP connec- + tions. + + ffttpp--pprrooxxyy is run from inetd(8) and requires that FTP connections are + redirected to it using a _r_d_r rule. A typical way to do this would be to + use a pf.conf(5) rule such as + + int_if = "xl0" + rdr pass on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021 + + inetd(8) must then be configured to run ffttpp--pprrooxxyy on the port from above + using + + 127.0.0.1:8021 stream tcp nowait root /usr/libexec/ftp-proxy ftp-proxy + + in inetd.conf(5). + + ffttpp--pprrooxxyy accepts the redirected control connections and forwards them to + the server. The proxy replaces the address and port number that the + client sends through the control connection to the server with its own + address and proxy port, where it listens for the data connection. When + the server opens the data connection back to this port, the proxy for- + wards it to the client. The pf.conf(5) rules need to let pass connec- + tions to these proxy ports (see options --uu, --mm, and --MM above) in on the + external interface. The following example allows only ports 49152 to + 65535 to pass in statefully: + + block in on $ext_if proto tcp all + pass in on $ext_if inet proto tcp from any to $ext_if \ + port > 49151 keep state + + Alternatively, rules can make use of the fact that by default, ffttpp--pprrooxxyy + runs as user "proxy" to allow the backchannel connections, as in the fol- + lowing example: + + block in on $ext_if proto tcp all + pass in on $ext_if inet proto tcp from any to $ext_if \ + user proxy keep state + + These examples do not cover the connections from the proxy to the foreign + FTP server. If one does not pass outgoing connections by default addi- + tional rules are needed. + +SSEEEE AALLSSOO + ftp(1), pf(4), hosts.allow(5), hosts.deny(5), inetd.conf(5), pf.conf(5), + inetd(8), pfctl(8), syslogd(8) + +BBUUGGSS + Extended Passive mode (EPSV) is not supported by the proxy and will not + work unless the proxy is run in network address translation mode. When + not in network address translation mode, the proxy returns an error to + the client, hopefully forcing the client to revert to passive mode (PASV) + which is supported. EPSV will work in network address translation mode, + assuming a pf.conf(5) setup which allows the EPSV connections through to + their destinations. + + IPv6 already supported. + +OpenBSD 3.6 August 17, 2001 3 diff -urN ftp-proxy/util.c ftp-proxy-new/util.c --- ftp-proxy/util.c Wed Jul 7 03:49:11 2004 +++ ftp-proxy-new/util.c Thu Mar 17 22:37:28 2005 @@ -58,9 +58,19 @@ extern int ReverseMode; +#ifdef IPV6_SUP +/* address family type (AF_INET|AF_INET6) */ +extern int address_family; +#endif + int Debug_Level; int Use_Rdns; +#ifdef IPV6_SUP +int Bind_Addr_Flag = 0; +struct sockaddr_storage Bind_Addr; +#else in_addr_t Bind_Addr = INADDR_NONE; +#endif void debuglog(int debug_level, const char *fmt, ...); @@ -76,12 +86,27 @@ } int +#ifdef IPV6_SUP +get_proxy_env(int connected_fd, struct sockaddr_storage *real_server_sa_ptr, + struct sockaddr_storage *client_sa_ptr, struct sockaddr_storage *proxy_sa_ptr) +#else get_proxy_env(int connected_fd, struct sockaddr_in *real_server_sa_ptr, struct sockaddr_in *client_sa_ptr, struct sockaddr_in *proxy_sa_ptr) +#endif { struct pfioc_natlook natlook; socklen_t slen; int fd; +#ifdef IPV6_SUP + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; + char client_host[NI_MAXHOST]; + char client_port[NI_MAXHOST]; + char proxy_host[NI_MAXHOST]; + char proxy_port[NI_MAXHOST]; + int ret; + char af; +#endif slen = sizeof(*proxy_sa_ptr); if (getsockname(connected_fd, (struct sockaddr *)proxy_sa_ptr, @@ -96,9 +121,96 @@ return(-1); } +#ifdef IPV6_SUP + ret = getnameinfo((struct sockaddr *)proxy_sa_ptr, + ((struct sockaddr *)proxy_sa_ptr)->sa_len, + proxy_host, sizeof(proxy_host), + proxy_port, sizeof(proxy_port), NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) + { + syslog(LOG_INFO, "getnameinfo() failed: [%d]\n", ret); + return(-1); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "proxy_host: [%s], proxy_port: [%s]\n", + proxy_host, proxy_port); +#endif + + /* updated global address family variable */ + af = ((struct sockaddr *)proxy_sa_ptr)->sa_family; + switch (af) + { + case AF_INET: + /* set adderss family to AF_INET */ + address_family = AF_INET; + break; + + case AF_INET6: + /* set adderss family to AF_INET */ + address_family = AF_INET6; + break; + + default: + syslog(LOG_ERR, "unsupported address family: [%d]\n", af); + return(-1); + break; + } + + ret = getnameinfo((struct sockaddr *)client_sa_ptr, + ((struct sockaddr *)client_sa_ptr)->sa_len, + client_host, sizeof(client_host), + client_port, sizeof(client_port), NI_NUMERICHOST | NI_NUMERICSERV); + + if (ret != 0) + { + syslog(LOG_INFO, "getnameinfo() failed: [%d]\n", ret); + return(-1); + } + +#ifdef DEBUG_LOG + syslog(LOG_INFO, "client_host: [%s], client_port: [%s]\n", + client_host, client_port); +#endif +#endif + if (ReverseMode) return(0); +#ifdef IPV6_SUP + /* + * Build up the pf natlook structure. + * Support only IPV4 and IPV6. + */ + memset((void *)&natlook, 0, sizeof(natlook)); + switch (af) + { + case AF_INET: + natlook.af = AF_INET; + ipv4 = (struct sockaddr_in *)client_sa_ptr; + natlook.saddr.addr32[0] = ipv4->sin_addr.s_addr; + natlook.sport = ipv4->sin_port; + ipv4 = (struct sockaddr_in *)proxy_sa_ptr; + natlook.daddr.addr32[0] = ipv4->sin_addr.s_addr; + natlook.dport = ipv4->sin_port; + natlook.proto = IPPROTO_TCP; + natlook.direction = PF_OUT; + break; + + case AF_INET6: + natlook.af = AF_INET6; + ipv6 = (struct sockaddr_in6 *)client_sa_ptr; + natlook.saddr.v6 = ipv6->sin6_addr; + natlook.sport = ipv6->sin6_port; + ipv6 = (struct sockaddr_in6 *)proxy_sa_ptr; + natlook.daddr.v6 = ipv6->sin6_addr; + natlook.dport = ipv6->sin6_port; + natlook.proto = IPPROTO_TCP; + natlook.direction = PF_OUT; + break; + } +#else /* * Build up the pf natlook structure. * Just for IPv4 right now @@ -111,6 +223,7 @@ natlook.sport = client_sa_ptr->sin_port; natlook.dport = proxy_sa_ptr->sin_port; natlook.direction = PF_OUT; +#endif /* * Open the pf device and lookup the mapping pair to find @@ -123,10 +236,16 @@ } if (ioctl(fd, DIOCNATLOOK, &natlook) == -1) { +#ifdef IPV6_SUP + syslog(LOG_INFO, + "pf nat lookup failed %s:%s (%m)", + client_host, client_port); +#else syslog(LOG_INFO, "pf nat lookup failed %s:%hu (%m)", inet_ntoa(client_sa_ptr->sin_addr), ntohs(client_sa_ptr->sin_port)); +#endif close(fd); return(-1); } @@ -136,11 +255,38 @@ * Now jam the original address and port back into the into * destination sockaddr_in for the proxy to deal with. */ +#ifdef IPV6_SUP + memset((void *)real_server_sa_ptr, 0, sizeof(struct sockaddr_storage)); + switch (af) + { + case AF_INET: + ipv4 = (struct sockaddr_in *)real_server_sa_ptr; + ipv4->sin_port = natlook.rdport; + ipv4->sin_addr.s_addr = natlook.rdaddr.addr32[0]; + ipv4->sin_len = sizeof(struct sockaddr_in); + ipv4->sin_family = AF_INET; + break; + + case AF_INET6: + ipv6 = (struct sockaddr_in6 *)real_server_sa_ptr; + ipv6->sin6_port = natlook.rdport; + ipv6->sin6_addr = natlook.rdaddr.v6; + ipv6->sin6_len = sizeof(struct sockaddr_in6); + ipv6->sin6_family = AF_INET6; + break; + + default: + syslog(LOG_ERR, "unsupported address family returned by pf: [%d]\n", af); + return(-1); + } +#else memset((void *)real_server_sa_ptr, 0, sizeof(struct sockaddr_in)); real_server_sa_ptr->sin_port = natlook.rdport; real_server_sa_ptr->sin_addr.s_addr = natlook.rdaddr.addr32[0]; real_server_sa_ptr->sin_len = sizeof(struct sockaddr_in); real_server_sa_ptr->sin_family = AF_INET; +#endif + return(0); } @@ -151,8 +297,13 @@ * A unit of data is as much as we get with a single read(2) call. */ int +#ifdef IPV6_SUP +xfer_data(const char *what_read,int from_fd, int to_fd, struct sockaddr_storage *from, + struct sockaddr_storage *to) +#else xfer_data(const char *what_read,int from_fd, int to_fd, struct in_addr from, struct in_addr to) +#endif { int rlen, offset, xerrno, mark, flags = 0; char tbuf[4096]; @@ -216,15 +367,84 @@ } } + +#ifdef IPV6_SUP +/* + * create sockaddr_storage structure for the INADDR_ANY for both + * IPV4 and IPV6 address family. + */ +int +create_sockaddr_any(int af, int type, int port, struct sockaddr_storage *sa) +{ + struct addrinfo hints; + struct addrinfo *res; + char port_str[NI_MAXHOST]; + int ret; + +#ifdef DEBUG_LOG + char addr_buf[NI_MAXHOST]; + char port_buf[NI_MAXHOST]; +#endif + + bzero(&hints, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = type; + hints.ai_flags = AI_NUMERICHOST; + + /* convert port into string */ + snprintf(port_str, NI_MAXHOST, "%d", port); + + /* create based on address family type */ + if (af == AF_INET) + ret = getaddrinfo("0.0.0.0", port_str, &hints, &res); + else if (af == AF_INET6) + ret = getaddrinfo("::", port_str, &hints, &res); + else { + syslog(LOG_INFO, "unknown af: [%d]\n", af); + return(-1); + } + + if (ret != 0) { + syslog(LOG_INFO, "getaddrinfo() failed: [%d]\n", ret); + } + + /* copy resulting address to our ouput argument */ + memcpy(sa, res->ai_addr, res->ai_addrlen); + +#ifdef DEBUG_LOG + getnameinfo(res->ai_addr, res->ai_addrlen, addr_buf, + sizeof(addr_buf), port_buf, sizeof(port_buf), + NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "addr_buf: [%s], port_buf: [%s]\n", addr_buf, port_buf); +#endif + freeaddrinfo(res); + + return(0); +} +#endif + + /* * get_backchannel_socket gets us a socket bound somewhere in a * particular range of ports */ int +#ifdef IPV6_SUP +get_backchannel_socket(int af, int type, int min_port, int max_port, int start_port, + int direction, struct sockaddr_storage *sap) +#else get_backchannel_socket(int type, int min_port, int max_port, int start_port, int direction, struct sockaddr_in *sap) +#endif { int count; +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + char host[NI_MAXHOST]; + char port[NI_MAXHOST]; +#endif +#endif /* * Make sure that direction is 'defined' and that min_port is not @@ -257,9 +477,39 @@ } while (count-- > 0) { +#ifdef IPV6_SUP + struct sockaddr_storage sa; + struct sockaddr_in *sockaddr4; + struct sockaddr_in6 *sockaddr6; +#else struct sockaddr_in sa; +#endif int one, fd; +#ifdef IPV6_SUP + fd = socket(af, type, 0); + + bzero(&sa, sizeof(sa)); + if (Bind_Addr_Flag == 0) { + if (sap == NULL) { + if (create_sockaddr_any(af, type, start_port, &sa) != 0) + return(-1); + } + else { + if ((sap->ss_family == AF_INET) || + (sap->ss_family == AF_INET6)) { + memcpy(&sa, sap, sap->ss_len); + } + else { + if (create_sockaddr_any(af, type, start_port, &sa) != 0) + return(-1); + } + } + } + else { + memcpy(&sa, &Bind_Addr, sizeof(sa)); + } +#else fd = socket(AF_INET, type, 0); bzero(&sa, sizeof sa); @@ -271,6 +521,7 @@ sa.sin_addr.s_addr = sap->sin_addr.s_addr; else sa.sin_addr.s_addr = Bind_Addr; +#endif /* * Indicate that we want to reuse a port if it happens that the @@ -281,13 +532,42 @@ sizeof(one)) == -1) return(-1); +#ifdef IPV6_SUP + /* update port */ + if (af == AF_INET) { + sockaddr4 = (struct sockaddr_in *)&sa; + sockaddr4->sin_port = htons(start_port); + } + else { + sockaddr6 = (struct sockaddr_in6 *)&sa; + sockaddr6->sin6_port = htons(start_port); + } +#else sa.sin_port = htons(start_port); +#endif +#ifdef IPV6_SUP +#ifdef DEBUG_LOG + getnameinfo((struct sockaddr *)&sa, + sa.ss_len, + host, sizeof(host), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + + syslog(LOG_INFO, "binding to host: [%s], port: [%s]\n", + host, port); +#endif + if (bind(fd, (struct sockaddr *)&sa, sa.ss_len) == 0) { +#else if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0) { +#endif if (sap != NULL) *sap = sa; return(fd); } + +#ifdef IPV6_SUP + syslog(LOG_INFO, "bind() failed\n"); +#endif if (errno != EADDRINUSE) return(-1); diff -urN ftp-proxy/util.h ftp-proxy-new/util.h --- ftp-proxy/util.h Wed Jul 7 03:49:11 2004 +++ ftp-proxy-new/util.h Fri Mar 11 23:30:48 2005 @@ -45,7 +45,11 @@ int fd; int line_buffer_size, io_buffer_size, io_buffer_len, next_byte; unsigned char *io_buffer, *line_buffer; +#ifdef IPV6_SUP + struct sockaddr_storage sa, real_sa; +#else struct sockaddr_in sa, real_sa; +#endif char *who; char alive, got_eof, data_available; int send_oob_flags; @@ -54,14 +58,29 @@ extern int telnet_getline(struct csiob *iobp, struct csiob *telnet_passthrough); +#ifdef IPV6_SUP +extern int get_proxy_env(int fd, struct sockaddr_storage *server_sa_ptr, + struct sockaddr_storage *client_sa_ptr, struct sockaddr_storage *proxy_sa_ptr); +#else extern int get_proxy_env(int fd, struct sockaddr_in *server_sa_ptr, struct sockaddr_in *client_sa_ptr, struct sockaddr_in *proxy_sa_ptr); +#endif +#ifdef IPV6_SUP +extern int get_backchannel_socket(int af, int type, int min_port, int max_port, + int start_port, int direction, struct sockaddr_storage *sap); +#else extern int get_backchannel_socket(int type, int min_port, int max_port, int start_port, int direction, struct sockaddr_in *sap); +#endif +#ifdef IPV6_SUP +extern int xfer_data(const char *what_read, int from_fd, int to_fd, + struct sockaddr_storage *from, struct sockaddr_storage *to); +#else extern int xfer_data(const char *what_read, int from_fd, int to_fd, struct in_addr from, struct in_addr to); +#endif extern char *ProgName; |