[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Patch to give netkit-ftp IPv6-capability



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


Reply to: