From 6406851b3fb6cf4d396c49c7f2143369d7bd4a0e Mon Sep 17 00:00:00 2001 From: Mats Erik Andersson Date: Sun, 17 Apr 2011 23:27:31 +0200 Subject: [PATCH] ftp: Activate support for IPv6. --- ChangeLog | 12 +++++++++ ftp/ftp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- ftp/ftp_var.h | 1 + ftp/main.c | 11 ++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 67e6427..31a9a84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2011-04-17 Mats Erik Andersson + + * ftp/ftp_var.h (usefamily): New variable. + * ftp/main.c (argp_options): New options `ipv4` and `ipv6`. + (parse_opt): Parse cases '4' and '6', set USEFAMILY accordingly. + (main): Initialize USEFAMILY to `AF_UNSPEC`. + * ftp/ftp.c (hookup): Resolve addresses according to USEFAMILY. + Use macro AI_ADDRCONFIG only with family `AF_UNSPEC`. + (initconn): New IPv6 protocol methods `EPSV` and `LPSV`. + New variable DATA_ADDR_SA6. + (initconn) [noport]: New IPv6 methods `EPRT` and `LPRT`. + 2011-04-08 Mats Erik Andersson * ftp/cmds.c (setepsv4): New function. diff --git a/ftp/ftp.c b/ftp/ftp.c index f144570..e8d21e1 100644 --- a/ftp/ftp.c +++ b/ftp/ftp.c @@ -132,9 +132,13 @@ hookup (char *host, int port) memset (&hisctladdr, 0, sizeof (hisctladdr)); memset (&hints, 0, sizeof (hints)); - hints.ai_family = AF_INET; + hints.ai_family = usefamily; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; +#ifdef AI_ADDRCONFIG + if (hints.ai_family == AF_UNSPEC) + hints.ai_flags |= AI_ADDRCONFIG; +#endif /* AI_ADDRCONFIG */ status = getaddrinfo (host, portstr, &hints, &res); if (status) @@ -1128,8 +1132,9 @@ initconn (void) socklen_t len; int on = 1; uint32_t a0, a1, a2, a3, p0, p1, port; - uint32_t af, hal, h[4], pal; /* RFC 1639: LPSV resonse. */ + uint32_t af, hal, h[16], pal; /* RFC 1639: LPSV resonse. */ struct sockaddr_in *data_addr_sa4 = (struct sockaddr_in *) &data_addr; + struct sockaddr_in6 *data_addr_sa6 = (struct sockaddr_in6 *) &data_addr; if (passivemode) { @@ -1171,11 +1176,25 @@ initconn (void) printf ("Passive mode refused.\n"); goto bad; break; + case AF_INET6: + if (command ("EPSV") == COMPLETE) + { + good_epsv = 1; + break; + } + if (command ("LPSV") == COMPLETE) + { + good_lpsv = 1; + break; + } + printf ("Passive mode refused.\n"); + goto bad; + break; } if (good_epsv) { - /* EPSV: IPv4 + /* EPSV: IPv4 or IPv6 * * Expected response (perl): pasv =~ '%u|' * This communicates a port number. @@ -1193,11 +1212,14 @@ initconn (void) case AF_INET: data_addr_sa4->sin_port = htons (port); break; + case AF_INET6: + data_addr_sa6->sin6_port = htons (port); + break; } } /* EPSV */ else if (good_lpsv) { - /* LPSV: IPv4 + /* LPSV: IPv4 or IPv6 * * At this point we have got a string of comma * separated, one-byte unsigned integer values. @@ -1229,7 +1251,37 @@ initconn (void) uint32_t *pu32 = (uint32_t *) &data_addr_sa4->sin_addr.s_addr; pu32[0] = htonl ( (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]); } - } + } /* LPSV IPv4 */ + else /* IPv6 */ + { + if ((sscanf (pasv, "%u," /* af */ + "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u," /* hal, h[16] */ + "%u,%u,%u", /* pal, p0, p1 */ + &af, &hal, &h[0], &h[1], &h[2], &h[3], &h[4], &h[5], &h[6], &h[7], + &h[8], &h[9], &h[10], &h[11], &h[12], &h[13], &h[14], &h[15], + &pal, &p0, &p1) != 21) + || (/* Strong checking */ af != 6 || hal != 16 || pal != 2) ) + { + printf ("Passive mode address scan failure. " + "Shouldn't happen!\n"); + (void) command ("ABOR"); /* Cancel any open connection. */ + goto bad; + } + for (j = 0; j < 16; ++j) + h[j] &= 0xff; /* Mask only the significant bits. */ + + data_addr.ss_family = AF_INET6; + data_addr_sa6->sin6_port = + htons (((p0 & 0xff) << 8) | (p1 & 0xff)); + + { + uint32_t *pu32 = (uint32_t *) &data_addr_sa6->sin6_addr.s6_addr; + pu32[0] = htonl ( (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]); + pu32[1] = htonl ( (h[4] << 24) | (h[5] << 16) | (h[6] << 8) | h[7]); + pu32[2] = htonl ( (h[8] << 24) | (h[9] << 16) | (h[10] << 8) | h[11]); + pu32[3] = htonl ( (h[12] << 24) | (h[13] << 16) | (h[14] << 8) | h[15]); + } + } /* LPSV IPv6 */ } else /* !EPSV && !LPSV */ { /* PASV */ @@ -1282,6 +1334,9 @@ noport: case AF_INET: data_addr_sa4->sin_port = 0; break; + case AF_INET6: + data_addr_sa6->sin6_port = 0; + break; } if (data != -1) @@ -1322,6 +1377,7 @@ noport: #define UC(b) (((int)b)&0xff) /* Preferences: * IPv4: EPRT, PORT, LPRT + * IPv6: EPRT, LPRT */ result = ERROR; /* For success detection. */ if (data_addr.ss_family != AF_INET || doepsv4) @@ -1362,6 +1418,16 @@ noport: result = command ("LPRT 4,4,%u,%u,%u,%u,2,%u,%u", h[0], h[1], h[2], h[3], p[0], p[1]); break; + case AF_INET6: + h = (uint8_t *) &data_addr_sa6->sin6_addr; + p = (uint8_t *) &data_addr_sa6->sin6_port; + result = command ("LPRT 6,16," /* af, hal */ + "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u," /* h[16] */ + "2,%u,%u", /* pal, p[2] */ + h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], + h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15], + p[0], p[1]); + break; } } diff --git a/ftp/ftp_var.h b/ftp/ftp_var.h index 52212ee..601b0bf 100644 --- a/ftp/ftp_var.h +++ b/ftp/ftp_var.h @@ -88,6 +88,7 @@ FTP_EXTERN int crflag; /* if 1, strip car. rets. on ascii gets */ FTP_EXTERN char pasv[64]; /* passive port for proxy data connection */ FTP_EXTERN int passivemode; /* passive mode enabled */ FTP_EXTERN int doepsv4; /* EPSV/EPRT for IPv4 enabled */ +FTP_EXTERN int usefamily; /* Precondition on an adress family */ FTP_EXTERN char *altarg; /* argv[1] with no shell-like preprocessing */ FTP_EXTERN char ntin[17]; /* input translation table */ FTP_EXTERN char ntout[17]; /* output translation table */ diff --git a/ftp/main.c b/ftp/main.c index 147f66f..38e36f0 100644 --- a/ftp/main.c +++ b/ftp/main.c @@ -108,6 +108,8 @@ static struct argp_option argp_options[] = { {"prompt", OPT_PROMPT, "PROMPT", OPTION_ARG_OPTIONAL, "print a command line PROMPT " "(optionally), even if not on a tty", GRP+1}, {"verbose", 'v', NULL, 0, "verbose output", GRP+1}, + {"ipv4", '4', NULL, 0, "contact IPv4 hosts", GRP+1}, + {"ipv6", '6', NULL, 0, "contact IPv6 hosts", GRP+1}, #undef GRP {NULL} }; @@ -154,6 +156,14 @@ parse_opt (int key, char *arg, struct argp_state *state) passivemode = 0; break; + case '4': + usefamily = AF_INET; + break; + + case '6': + usefamily = AF_INET6; + break; + default: return ARGP_ERR_UNKNOWN; } @@ -179,6 +189,7 @@ main (int argc, char *argv[]) autologin = 1; passivemode = 0; /* passive mode not active */ doepsv4 = 0; /* use EPRT/EPSV for IPv4 */ + usefamily = AF_UNSPEC; /* allow any address family */ /* Parse command line */ iu_argp_init ("ftp", default_program_authors); -- 1.7.2.3