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