diff -NurpP oftpd-0.3.7/src/daemon_assert.c oftpd-0.3.7-r3/src/daemon_assert.c --- oftpd-0.3.7/src/daemon_assert.c 2001-04-22 09:50:41.000000000 +0200 +++ oftpd-0.3.7-r3/src/daemon_assert.c 2006-12-03 13:43:47.459738389 +0100 @@ -3,6 +3,7 @@ #include #include #include +#include #ifndef NDEBUG void daemon_assert_fail(const char *assertion, diff -NurpP oftpd-0.3.7/src/ftp_command.c oftpd-0.3.7-r3/src/ftp_command.c --- oftpd-0.3.7/src/ftp_command.c 2004-03-25 21:46:57.000000000 +0100 +++ oftpd-0.3.7-r3/src/ftp_command.c 2006-12-03 13:43:47.459738389 +0100 @@ -137,7 +137,7 @@ int ftp_command_parse(const char *input, input++; /* parse the host & port information (if any) */ - input = parse_host_port(&tmp.arg[0].host_port, input); + input = parse_host_port((struct sockaddr_in *)&tmp.arg[0].host_port, input); if (input == NULL) { return 0; } @@ -325,6 +325,7 @@ static const char *parse_host_port(struc struct in_addr in_addr; daemon_assert(addr != NULL); + daemon_assert(SAFAM(addr) == AF_INET); daemon_assert(s != NULL); /* scan in 5 pairs of "#," */ @@ -448,8 +449,8 @@ static const char *parse_host_port_long( if (port_len != 2) { return NULL; } - memcpy(&SINADDR(sa), addr, addr_len); - SINPORT(sa) = htons((port[0] << 8) + port[1]); + memcpy(&SIN4ADDR(sa), addr, addr_len); + SIN4PORT(sa) = htons((port[0] << 8) + port[1]); } #ifdef INET6 else if (family == 6) { @@ -461,7 +462,7 @@ static const char *parse_host_port_long( return NULL; } memcpy(&SIN6ADDR(sa), addr, addr_len); - SINPORT(sa) = htons((port[0] << 8) + port[1]); + SIN6PORT(sa) = htons((port[0] << 8) + port[1]); } #endif else { @@ -550,7 +551,7 @@ static const char *parse_host_port_ext(s #else { struct addrinfo hints; - struct *res; + struct addrinfo*res; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; @@ -567,7 +568,12 @@ static const char *parse_host_port_ext(s #endif /* INET6 */ SAFAM(sa) = family; - SINPORT(sa) = htons(port); +#ifdef INET6 + if (family == AF_INET6) + SIN6PORT(sa) = htons(port); + else +#endif + SIN4PORT(sa) = htons(port); /* return new pointer */ return s; diff -NurpP oftpd-0.3.7/src/ftp_listener.c oftpd-0.3.7-r3/src/ftp_listener.c --- oftpd-0.3.7/src/ftp_listener.c 2001-05-11 01:29:46.000000000 +0200 +++ oftpd-0.3.7-r3/src/ftp_listener.c 2006-12-03 14:20:52.382787389 +0100 @@ -135,17 +135,23 @@ int ftp_listener_init(ftp_listener_t *f, * -- Matthew Danish [3/20/2001] */ + hints.ai_flags = AI_PASSIVE; #ifdef INET6 + /* For IPv6, try provided host/address as IPv6 address, but fall + * back to IPv4 if we fail + */ hints.ai_family = AF_INET6; + gai_err = getaddrinfo(address, NULL, &hints, &res); + if (gai_err == EAI_ADDRFAMILY) { + hints.ai_family = AF_INET; + gai_err = getaddrinfo(address, NULL, &hints, &res); + } #else hints.ai_family = AF_INET; -#endif - - hints.ai_flags = AI_PASSIVE; - gai_err = getaddrinfo(address, NULL, &hints, &res); +#endif if (gai_err != 0) { - error_init(err, gai_err, "error parsing server socket address; %s", + error_init(err, -gai_err, "error parsing server socket address; %s", gai_strerror(gai_err)); return 0; } @@ -154,17 +160,25 @@ int ftp_listener_init(ftp_listener_t *f, freeaddrinfo(res); } - if (port == 0) { - SINPORT(&sock_addr) = htons(DEFAULT_FTP_PORT); - } else { - SINPORT(&sock_addr) = htons(port); - } - - - inet_ntop_ret = inet_ntop(SAFAM(&sock_addr), - (void *)&SINADDR(&sock_addr), +#ifdef INET6 + if (SAFAM(&sock_addr) == AF_INET6) + SIN6PORT(&sock_addr) = htons(port == 0 ? DEFAULT_FTP_PORT : port); + else +#endif + SIN4PORT(&sock_addr) = htons(port == 0 ? DEFAULT_FTP_PORT : port); +#ifdef INET6 + if (SAFAM(&sock_addr) == AF_INET6) + inet_ntop_ret = inet_ntop(AF_INET6, + (void *)&SIN6ADDR(&sock_addr), buf, sizeof(buf)); + else +#endif + inet_ntop_ret = inet_ntop(SAFAM(&sock_addr), + (void *)&SIN4ADDR(&sock_addr), + buf, + sizeof(buf)); + if (inet_ntop_ret == NULL) { error_init(err, errno, "error converting server address to ASCII; %s", strerror(errno)); @@ -177,7 +191,7 @@ int ftp_listener_init(ftp_listener_t *f, /* okay, finally do some socket manipulation */ - fd = socket(AF_INET, SOCK_STREAM, 0); + fd = socket(SAFAM(&sock_addr), SOCK_STREAM, 0); if (fd == -1) { error_init(err, errno, "error creating socket; %s", strerror(errno)); return 0; @@ -194,7 +208,7 @@ int ftp_listener_init(ftp_listener_t *f, } if (bind(fd, (struct sockaddr *)&sock_addr, - sizeof(struct sockaddr_in)) != 0) + SAFAM(&sock_addr) == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0) { close(fd); error_init(err, errno, "error binding address; %s", strerror(errno)); @@ -461,7 +475,7 @@ static char *addr2string(const sockaddr_ 0, NI_NUMERICHOST); if (error != 0) { - syslog(LOG_WARN, "getnameinfo error; %s", gai_strerror(error)); + syslog(LOG_WARNING, "getnameinfo error; %s", gai_strerror(error)); ret_val = "Unknown IP"; } else { ret_val = addr; diff -NurpP oftpd-0.3.7/src/ftp_session.c oftpd-0.3.7-r3/src/ftp_session.c --- oftpd-0.3.7/src/ftp_session.c 2004-03-25 21:46:40.000000000 +0100 +++ oftpd-0.3.7-r3/src/ftp_session.c 2006-12-03 16:48:17.175552389 +0100 @@ -44,6 +44,7 @@ #define ADDRPORT_STRLEN 58 /* prototypes */ +static int alloc_ipv4(ftp_session_t *f); static int invariant(const ftp_session_t *f); static void reply(ftp_session_t *f, int code, const char *fmt, ...); static void change_dir(ftp_session_t *f, const char *new_dir); @@ -137,39 +138,7 @@ int ftp_session_init(ftp_session_t *f, daemon_assert(strlen(dir) <= PATH_MAX); daemon_assert(err != NULL); -#ifdef INET6 - /* if the control connection is on IPv6, we need to get an IPv4 address */ - /* to bind the socket to */ - if (SSFAM(server_addr) == AF_INET6) { - struct addrinfo hints; - struct addrinfo *res; - int errcode; - - /* getaddrinfo() does the job nicely */ - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_flags = AI_PASSIVE; - if (getaddrinfo(NULL, "ftp", &hints, &res) != 0) { - error_init(err, 0, "unable to determing IPv4 address; %s", - gai_strerror(errcode)); - return 0; - } - - /* let's sanity check */ - daemon_assert(res != NULL); - daemon_assert(sizeof(f->server_ipv4_addr) >= res->ai_addrlen); - daemon_assert(SSFAM(host_port) == AF_INET); - - /* copy the result and free memory as necessary */ - memcpy(&f->server_ipv4_addr, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - } else { - daemon_assert(SSFAM(host_port) == AF_INET); - f->server_ipv4_addr = *server_addr; - } -#else - f->server_ipv4_addr = *server_addr; -#endif + memset(&f->server_ipv4_addr, 0, sizeof(f->server_ipv4_addr)); f->session_active = 1; f->command_number = 0; @@ -291,6 +260,46 @@ void ftp_session_destroy(ftp_session_t * } } +#ifdef INET6 +static int alloc_ipv4(ftp_session_t *f) +{ + /* if the control connection is on IPv6, we need to get an IPv4 address */ + /* to bind the socket to */ + if (SSFAM(&f->server_addr) == AF_INET6) { + struct addrinfo hints; + struct addrinfo *res; + int errcode; + + /* getaddrinfo() does the job nicely */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_PASSIVE; + if ((errcode = getaddrinfo(NULL, "ftp", &hints, &res)) != 0) { + syslog(LOG_ERR, "Unable to determing IPv4 address: %s", gai_strerror(errcode)); + return 0; + } + + /* let's sanity check */ + daemon_assert(res != NULL); + daemon_assert(sizeof(f->server_ipv4_addr) >= res->ai_addrlen); + daemon_assert(SSFAM(&f->server_ipv4_addr) == AF_INET); + + /* copy the result and free memory as necessary */ + memcpy(&f->server_ipv4_addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } else { + daemon_assert(SSFAM(&f->server_addr) == AF_INET); + memcpy(&f->server_ipv4_addr, &f->server_addr, sizeof(f->server_ipv4_addr)); + } + return 1; +} +#else +inline static int alloc_ipv4(ftp_session_t *f) +{ + return 0; +} +#endif + #ifndef NDEBUG static int invariant(const ftp_session_t *f) { @@ -333,7 +342,8 @@ static int invariant(const ftp_session_t return 0; } break; - case DATA_PASSIVE: + case DATA_PASSIVE_4: + case DATA_PASSIVE_6: if (f->server_fd < 0) { return 0; } @@ -424,16 +434,16 @@ static void get_addr_str(const sockaddr_ /* buf must be able to contain (at least) a string representing an * ipv4 addr, followed by the string " port " (6 chars) and the port * number (which is 5 chars max), plus the '\0' character. */ - daemon_assert(bufsiz >= (INET_ADDRSTRLEN + 12)); + daemon_assert(bufsiz >= (INET6_ADDRSTRLEN + 12)); - error = getnameinfo(client_addr, sizeof(sockaddr_storage_t), buf, + error = getnameinfo((struct sockaddr *)s, sizeof(sockaddr_storage_t), buf, bufsiz, NULL, 0, NI_NUMERICHOST); /* getnameinfo() should never fail when called with NI_NUMERICHOST */ daemon_assert(error == 0); len = strlen(buf); daemon_assert(bufsiz >= len+12); - snprintf(buf+len, bufsiz-len, " port %d", ntohs(SINPORT(&f->client_addr))); + snprintf(buf+len, bufsiz-len, " port %d", ntohs(SINPORT(s))); } #else static void get_addr_str(const sockaddr_storage_t *s, char *buf, int bufsiz) @@ -639,7 +649,7 @@ static void set_port(ftp_session_t *f, c reply(f, 500, "Port may not be less than 1024, which is reserved."); } else { /* close any outstanding PASSIVE port */ - if (f->data_channel == DATA_PASSIVE) { + if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { close(f->server_fd); f->server_fd = -1; } @@ -733,12 +743,19 @@ static int set_pasv(ftp_session_t *f, so for (;;) { port = get_passive_port(); - SINPORT(bind_addr) = htons(port); +#ifdef INET6 + if (SAFAM(bind_addr) == AF_INET6) + SIN6PORT(bind_addr) = htons(port); + else + SIN4PORT(bind_addr) = htons(port); if (bind(socket_fd, (struct sockaddr *)bind_addr, - sizeof(struct sockaddr)) == 0) - { + SAFAM(bind_addr) == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == 0) break; - } +#else + SIN4PORT(bind_addr) = htons(port); + if (bind(socket_fd, (struct sockaddr *)bind_addr, sizeof(struct sockaddr_in)) == 0) + break; +#endif if (errno != EADDRINUSE) { reply(f, 500, "Error binding server port; %s.", strerror(errno)); close(socket_fd); @@ -771,14 +788,28 @@ static void do_pasv(ftp_session_t *f, co goto exit_pasv; } - socket_fd = set_pasv(f, &f->server_ipv4_addr); + if (SAFAM(&f->server_addr) == AF_INET) + socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_addr); + else if (SAFAM(&f->server_ipv4_addr) == AF_UNSPEC) { + if (!alloc_ipv4(f)) { + reply(f, 522, "PASV not possible, use EPSV"); + goto exit_pasv; + } + socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_ipv4_addr); + } else + socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_ipv4_addr); if (socket_fd == -1) { goto exit_pasv; } /* report port to client */ - addr = ntohl(f->server_ipv4_addr.sin_addr.s_addr); - port = ntohs(f->server_ipv4_addr.sin_port); + if (SAFAM(&f->server_addr) == AF_INET) { + addr = ntohl(SIN4ADDR(&f->server_addr).s_addr); + port = ntohs(SIN4PORT(&f->server_addr)); + } else { + addr = ntohl(SIN4ADDR(&f->server_ipv4_addr).s_addr); + port = ntohs(SIN4PORT(&f->server_ipv4_addr)); + } reply(f, 227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d).", addr >> 24, (addr >> 16) & 0xff, @@ -788,10 +819,10 @@ static void do_pasv(ftp_session_t *f, co port & 0xff); /* close any outstanding PASSIVE port */ - if (f->data_channel == DATA_PASSIVE) { + if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { close(f->server_fd); } - f->data_channel = DATA_PASSIVE; + f->data_channel = DATA_PASSIVE_4; f->server_fd = socket_fd; exit_pasv: @@ -841,10 +872,14 @@ static void do_lpsv(ftp_session_t *f, co reply(f, 228, "Entering Long Passive Mode %s", addr); /* close any outstanding PASSIVE port */ - if (f->data_channel == DATA_PASSIVE) { + if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { close(f->server_fd); } - f->data_channel = DATA_PASSIVE; +#ifdef INET6 + f->data_channel = SSFAM(&f->server_addr) == AF_INET6 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; +#else + f->data_channel = DATA_PASSIVE_4; +#endif f->server_fd = socket_fd; exit_lpsv: @@ -875,11 +910,24 @@ static void do_epsv(ftp_session_t *f, co reply(f, 200, "EPSV ALL command successful."); goto exit_epsv; case 1: - addr = (sockaddr_storage_t *)&f->server_ipv4_addr; + if (SAFAM(&f->server_addr) == AF_INET) + addr = &f->server_addr; + else if (SAFAM(&f->server_ipv4_addr) == AF_UNSPEC) { + if (!alloc_ipv4(f)) { + reply(f, 522, "Only IPv6 supported, use (2)"); + goto exit_epsv; + } + addr = &f->server_ipv4_addr; + } else + addr = &f->server_ipv4_addr; break; #ifdef INET6 case 2: - addr = &f->server_addr; + if (SAFAM(&f->server_addr) == AF_INET) { + reply(f, 522, "Only IPv4 supported, use (1)"); + goto exit_epsv; + } + addr = &f->server_addr; break; default: reply(f, 522, "Only IPv4 and IPv6 supported, use (1,2)"); @@ -903,10 +951,16 @@ static void do_epsv(ftp_session_t *f, co ntohs(SINPORT(&f->server_addr))); /* close any outstanding PASSIVE port */ - if (f->data_channel == DATA_PASSIVE) { + if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { close(f->server_fd); } - f->data_channel = DATA_PASSIVE; + f->data_port = *addr; +#ifdef INET6 + if (cmd->num_arg == 0) + f->data_channel = SAFAM(&f->server_addr) == AF_INET6 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; + else +#endif + f->data_channel = cmd->arg[0].num == 2 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; f->server_fd = socket_fd; exit_epsv: @@ -1278,11 +1332,12 @@ static void do_stor(ftp_session_t *f, co static int open_connection(ftp_session_t *f) { int socket_fd; - struct sockaddr_in addr; + sockaddr_storage_t addr; unsigned addr_len; daemon_assert((f->data_channel == DATA_PORT) || - (f->data_channel == DATA_PASSIVE)); + (f->data_channel == DATA_PASSIVE_4) || + (f->data_channel == DATA_PASSIVE_6)); if (f->data_channel == DATA_PORT) { socket_fd = socket(SSFAM(&f->data_port), SOCK_STREAM, 0); @@ -1298,9 +1353,9 @@ static int open_connection(ftp_session_t return -1; } } else { - daemon_assert(f->data_channel == DATA_PASSIVE); + daemon_assert(f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6); - addr_len = sizeof(struct sockaddr_in); + addr_len = sizeof(addr); socket_fd = accept(f->server_fd, (struct sockaddr *)&addr, &addr_len); if (socket_fd == -1) { reply(f, 425, "Error accepting connection; %s.", strerror(errno)); @@ -1310,24 +1365,26 @@ static int open_connection(ftp_session_t /* in IPv6, the client can connect to a channel using a different */ /* protocol - in that case, we'll just blindly let the connection */ /* through, otherwise verify addresses match */ - if (SAFAM(addr) == SSFAM(&f->client_addr)) { - if (memcmp(&SINADDR(&f->client_addr), &SINADDR(&addr), - sizeof(SINADDR(&addr)))) - { - reply(f, 425, - "Error accepting connection; connection from invalid IP."); - close(socket_fd); - return -1; - } - } -#else - if (memcmp(&f->client_addr.sin_addr, - &addr.sin_addr, sizeof(struct in_addr))) - { - reply(f, 425, - "Error accepting connection; connection from invalid IP."); - close(socket_fd); - return -1; + if (SAFAM(&addr) == SSFAM(&f->client_addr)) { + if (SAFAM(&addr) == AF_INET6) { + if (memcmp(&SIN6ADDR(&f->client_addr), &SIN6ADDR(&addr), + sizeof(struct in6_addr))) { + reply(f, 425, "Error accepting connection; " + "connection from invalid IP."); + close(socket_fd); + return -1; + } + } else { +#endif + if (memcmp(&SIN4ADDR(&f->client_addr), &SIN4ADDR(&addr), + sizeof(struct in_addr))) { + reply(f, 425, "Error accepting connection; " + "connection from invalid IP."); + close(socket_fd); + return -1; + } +#ifdef INET6 + } } #endif } @@ -1822,9 +1879,17 @@ static int ip_equal(const sockaddr_stora if (SSFAM(a) != SSFAM(b)) { return 0; } - if (memcmp(&SINADDR(a), &SINADDR(b), sizeof(SINADDR(a))) != 0) { - return 0; +#ifdef INET6 + if (SSFAM(a) == AF_INET6) { + if (memcmp(&SIN6ADDR(a), &SIN6ADDR(b), sizeof(struct in6_addr)) != 0) + return 0; + } else { +#endif + if (memcmp(&SIN4ADDR(a), &SIN4ADDR(b), sizeof(struct in_addr)) != 0) + return 0; +#ifdef INET6 } +#endif return 1; } diff -NurpP oftpd-0.3.7/src/ftp_session.h oftpd-0.3.7-r3/src/ftp_session.h --- oftpd-0.3.7/src/ftp_session.h 2001-05-11 01:29:12.000000000 +0200 +++ oftpd-0.3.7-r3/src/ftp_session.h 2006-12-03 13:43:47.459738389 +0100 @@ -25,8 +25,9 @@ #define STRU_RECORD 1 /* data path chosen */ -#define DATA_PORT 0 -#define DATA_PASSIVE 1 +#define DATA_PORT 0 +#define DATA_PASSIVE_4 1 +#define DATA_PASSIVE_6 2 /* space required for text representation of address and port, e.g. "192.168.0.1 port 1024" or @@ -59,7 +60,7 @@ typedef struct { /* address of server (including IPv4 version) */ sockaddr_storage_t server_addr; - struct sockaddr_in server_ipv4_addr; + sockaddr_storage_t server_ipv4_addr; /* telnet session to encapsulate control channel logic */ telnet_session_t *telnet_session;