package ftp tags 228752 + patch thanks Hello, I would like to submit a newly developed patch that makes netkit-ftp fully IPv6-capable. The difference file is computed against netkit-ftp_0.17-19. Best regards, -- Mats Erik Andersson, fil. dr
Description: Impose IPv6-capacities on ftp.c. Super structure 'struct sockaddr_storage' is replacing 'struct sockaddr_in' . For peer address strings, a first try aims at IPv4. That failing, next try is for IPv6, and ultimatively a host lookup follows as last resort. . Use a command EPSV with explicit request for IPv6 address family as soon as parsing concluded that the control socket is using IPv6. Author: Mats Erik Andersson <debian@gisladisker.se> Forwarded: no Last-Update: 2010-02-27 --- netkit-ftp-0.17/ftp/ftp.c.debian +++ netkit-ftp-0.17/ftp/ftp.c @@ -71,9 +71,11 @@ int data = -1; off_t restart_point = 0; -static struct sockaddr_in hisctladdr; -static struct sockaddr_in data_addr; -static struct sockaddr_in myctladdr; +static char ipstring[INET6_ADDRSTRLEN]; /* Scribble area for resolver. */ + +static struct sockaddr_storage hisctladdr; +static struct sockaddr_storage data_addr; +static struct sockaddr_storage myctladdr; static int ptflag = 0; static int ptabflg = 0; @@ -98,7 +100,8 @@ char * hookup(char *host, int port) { - register struct hostent *hp = 0; + struct addrinfo hints, *ai = NULL, *aiptr = NULL; + int status; volatile int s = -1; int tos; socklen_t len; @@ -106,29 +109,28 @@ sigjmp_buf jmploc; sigjmp_buf *volatile oldtoplevel; int dupfd; + struct sockaddr_in *hisctl_sa4 = (struct sockaddr_in *) &hisctladdr; + struct sockaddr_in6 *hisctl_sa6 = (struct sockaddr_in6 *) &hisctladdr; memset(&hisctladdr, 0, sizeof(hisctladdr)); - if (inet_aton(host, &hisctladdr.sin_addr)) { - hisctladdr.sin_family = AF_INET; - strncpy(hostnamebuf, host, sizeof(hostnamebuf)); - hostnamebuf[sizeof(hostnamebuf)-1]=0; - } - else { - hp = gethostbyname(host); - if (hp == NULL) { - fprintf(stderr, "ftp: %s: ", host); - herror((char *)NULL); - code = -1; - return((char *) 0); - } - hisctladdr.sin_family = hp->h_addrtype; - if (hp->h_length > (int)sizeof(hisctladdr.sin_addr)) { - hp->h_length = sizeof(hisctladdr.sin_addr); - } - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], hp->h_length); - (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf)); - hostnamebuf[sizeof(hostnamebuf)-1] = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ( (status = getaddrinfo(host, NULL, &hints, &ai)) ) { + fprintf(stderr, "ftp: %s: %s", host, + gai_strerror(status)); + code = -1; + return((char *) 0); } + + aiptr = ai; + memcpy(&hisctladdr, aiptr->ai_addr, aiptr->ai_addrlen); + (void) strncpy(hostnamebuf, aiptr->ai_canonname, + sizeof(hostnamebuf)); + hostnamebuf[sizeof(hostnamebuf)-1] = 0; hostname = hostnamebuf; oldtoplevel = toplevel; @@ -141,32 +143,74 @@ toplevel = &jmploc; INTOFF; - s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + s = socket(hisctladdr.ss_family, SOCK_STREAM, 0); INTON; if (s < 0) { perror("ftp: socket"); code = -1; goto out; } - hisctladdr.sin_port = port; - while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) { - if (hp && hp->h_addr_list[1]) { + switch (hisctladdr.ss_family) { + case AF_INET: + hisctl_sa4->sin_port = port; + break; + case AF_INET6: + hisctl_sa6->sin6_port = port; + } + while (connect(s, (struct sockaddr *)&hisctladdr, + (hisctladdr.ss_family == AF_INET) + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)) + < 0) + { + if (aiptr && aiptr->ai_next) + { int oerrno = errno; + struct in_addr *ctladdr4 = &hisctl_sa4->sin_addr; + struct in6_addr *ctladdr6 = &hisctl_sa6->sin6_addr; - fprintf(stderr, "ftp: connect to address %s: ", - inet_ntoa(hisctladdr.sin_addr)); + switch (aiptr->ai_family) { + case AF_INET: + fprintf(stderr, "ftp: connect to address %s: ", + inet_ntop(aiptr->ai_family, + ctladdr4, + ipstring, + sizeof(ipstring))); + break; + case AF_INET6: + fprintf(stderr, "ftp: connect to address %s: ", + inet_ntop(aiptr->ai_family, + ctladdr6, + ipstring, + sizeof(ipstring))); + } errno = oerrno; perror((char *) 0); - hp->h_addr_list++; - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], - hp->h_length); - fprintf(stdout, "Trying %s...\n", - inet_ntoa(hisctladdr.sin_addr)); + + aiptr = aiptr->ai_next; + memcpy(&hisctladdr, aiptr->ai_addr, + aiptr->ai_addrlen); + switch (aiptr->ai_family) { + case AF_INET: + fprintf(stdout, "Trying %s...\n", + inet_ntop(aiptr->ai_family, + ctladdr4, + ipstring, + sizeof(ipstring))); + break; + case AF_INET6: + fprintf(stdout, "Trying %s...\n", + inet_ntop(aiptr->ai_family, + ctladdr6, + ipstring, + sizeof(ipstring))); + } INTOFF; (void) close(s); - s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + s = socket(aiptr->ai_family, SOCK_STREAM, 0); INTON; if (s < 0) { + freeaddrinfo(ai); perror("ftp: socket"); code = -1; goto out; @@ -183,6 +227,8 @@ code = -1; goto bad; } + if (ai) + freeaddrinfo(ai); #ifdef IP_TOS tos = IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) @@ -250,6 +296,8 @@ s = -1; INTON; out: + if (ai) + freeaddrinfo(ai); toplevel = oldtoplevel; return ((char *)0); } @@ -458,7 +506,7 @@ } if (dig < 4 && isdigit(c)) code = code * 10 + (c - '0'); - if (!pflag && code == 227) + if (!pflag && (code == 227 || code == 229)) pflag = 1; if (dig > 4 && pflag == 1 && isdigit(c)) pflag = 2; @@ -1198,18 +1246,21 @@ static int initconn(void) { - register char *p, *a; + register char *p = NULL, *a = NULL; int result, tmpno = 0; socklen_t len; int on = 1; int tos; u_long a1,a2,a3,a4,p1,p2; + unsigned short int port; + struct sockaddr_in *data_addr_sa4 = (struct sockaddr_in *) &data_addr; + struct sockaddr_in6 *data_addr_sa6 = (struct sockaddr_in6 *) &data_addr; if (passivemode) { INTOFF; if (data >= 0) close(data); - data = socket(AF_INET, SOCK_STREAM, 0); + data = socket(hisctladdr.ss_family, SOCK_STREAM, 0); INTON; if (data < 0) { perror("ftp: socket"); @@ -1219,32 +1270,67 @@ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) perror("ftp: setsockopt (ignored)"); - if (command("PASV") != COMPLETE) { - printf("Passive mode refused.\n"); - return(1); - } - - /* - * What we've got at this point is a string of comma separated - * one-byte unsigned integer values, separated by commas. - * The first four are the an IP address. The fifth is the MSB - * of the port number, the sixth is the LSB. From that we'll - * prepare a sockaddr_in. - */ - - if (sscanf(pasv,"%ld,%ld,%ld,%ld,%ld,%ld", - &a1,&a2,&a3,&a4,&p1,&p2) - != 6) - { - printf("Passive mode address scan failure. Shouldn't happen!\n"); - return(1); + switch (hisctladdr.ss_family) { + case AF_INET: + if (command("PASV") != COMPLETE) { + printf("Passive mode refused.\n"); + return(1); + } + break; + case AF_INET6: + if (command("EPSV 2") != COMPLETE) { + printf("Passive mode refused.\n"); + return(1); + } } - data_addr.sin_family = AF_INET; - data_addr.sin_addr.s_addr = htonl((a1 << 24) | (a2 << 16) | - (a3 << 8) | a4); - data_addr.sin_port = htons((p1 << 8) | p2); - + if (hisctladdr.ss_family == AF_INET) { + /* + * IPv4 + * + * What we've got at this point is a string of + * comma separated one-byte unsigned integer + * values, separated by commas. The first four + * are the an IP address. The fifth is the MSB + * of the port number, the sixth is the LSB. + * From that we will prepare a sockaddr_in. + */ + + if (sscanf(pasv,"%ld,%ld,%ld,%ld,%ld,%ld", + &a1,&a2,&a3,&a4,&p1,&p2) + != 6) + { + printf("Passive mode address scan failure." + "Shouldn't happen!\n"); + return(1); + } + + data_addr.ss_family = AF_INET; + data_addr_sa4->sin_addr.s_addr = + htonl((a1 << 24) | (a2 << 16) | + (a3 << 8) | a4); + data_addr_sa4->sin_port = htons((p1 << 8) | p2); + } /* Old IPv4 command PASV */ + else { + /* EPSV for IPv6 + * + * Expected: pasv =~ "%u|" + * + * This is a shortcut based on the old code + * for getreply(), only altered to accept + * return code "229" for ESPV, in addition + * to "227" which goes with PASV. + */ + if (sscanf(pasv, "%hu", &port) != 1) { + printf("Extended passive mode address " + "scan failure. Unfortunate!\n"); + return(1); + } + data_addr = hisctladdr; + data_addr.ss_family = AF_INET6; + data_addr_sa6->sin6_port = htons(port); + } /* EPSV for IPv6 */ + if (connect(data, (struct sockaddr *) &data_addr, sizeof(data_addr))<0) { perror("ftp: connect"); @@ -1261,11 +1347,18 @@ noport: data_addr = myctladdr; if (sendport) - data_addr.sin_port = 0; /* let system pick one */ + /* let the system pick a port */ + switch (data_addr.ss_family) { + case AF_INET: + data_addr_sa4->sin_port = 0; + break; + case AF_INET6: + data_addr_sa6->sin6_port = 0; + } INTOFF; if (data != -1) (void) close(data); - data = socket(AF_INET, SOCK_STREAM, 0); + data = socket(data_addr.ss_family, SOCK_STREAM, 0); INTON; if (data < 0) { perror("ftp: socket"); @@ -1293,13 +1386,23 @@ if (listen(data, 1) < 0) perror("ftp: listen"); if (sendport) { - a = (char *)&data_addr.sin_addr; - p = (char *)&data_addr.sin_port; #define UC(b) (((int)b)&0xff) - result = - command("PORT %d,%d,%d,%d,%d,%d", - UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(p[0]), UC(p[1])); + switch (data_addr.ss_family) { + case AF_INET: + a = (char *)&data_addr_sa4->sin_addr; + p = (char *)&data_addr_sa4->sin_port; + result = command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]), UC(a[1]), UC(a[2]), + UC(a[3]), UC(p[0]), UC(p[1])); + break; + case AF_INET6: + result = command("EPRT |2|%s|%d|", + inet_ntop(data_addr.ss_family, + &data_addr_sa6->sin6_addr, + ipstring, + sizeof(ipstring)), + ntohs(data_addr_sa6->sin6_port)); + } if (result == ERROR && sendport == -1) { sendport = 0; tmpno = 1; @@ -1327,7 +1430,7 @@ static FILE * dataconn(const char *lmode) { - struct sockaddr_in from; + struct sockaddr_storage from; int s, tos; socklen_t fromlen = sizeof(from); @@ -1398,8 +1501,8 @@ static struct comvars { int connect; char name[MAXHOSTNAMELEN]; - struct sockaddr_in mctl; - struct sockaddr_in hctl; + struct sockaddr_storage mctl; + struct sockaddr_storage hctl; FILE *in; FILE *out; int tpe;
Attachment:
signature.asc
Description: Digital signature