Clearly I accomplished substandard and incomplete suggestions
in my earlier efforts. Hard to explain how I could pick the
cherries so selectively. I was certainly blinded by the fear
of changing underlying mechanisms in the original code.

The good thing is that my renewed analysis has brought into
light more than one shortcoming of the code as it was maintained
up to this date.

The new patch is to be applied on top of an unpackaged Debian
debian source

   $ dpkg-source -x netkit-tftp_0.17-17.dsc
   $ patch -p0 < netkit_tftp_implement_ipv6_tftp_tftpd.diff

Nothing fancy like NMU! I am burned my fingers for good in that jar.
As always, the client is much harder to improve, than the server is.

Best regards,

Mats Erik Andersson, fil. dr
2459 41E9 C420 3F6D F68B  2E88 F768 4541 F25B 5D41
--- netkit-tftp-0.17.debian/tftp/tftpsubs.h
+++ netkit-tftp-0.17/tftp/tftpsubs.h
@@ -1,6 +1,6 @@
 #define PKTSIZE SEGSIZE+4       /* should be moved to tftp.h */
 
-void initsock(void);
+void initsock(int);
 void synchnet(int, int);
 struct tftphdr *r_init(void);
 struct tftphdr *w_init(void);
--- netkit-tftp-0.17.debian/tftp/tftpsubs.c
+++ netkit-tftp-0.17/tftp/tftpsubs.c
@@ -55,6 +55,7 @@ char subs_rcsid[] =
 #include <arpa/tftp.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <string.h>
 #include <signal.h>
 
 #ifndef FIONREAD
@@ -254,7 +255,7 @@ synchnet(int f /* socket to flush */, in
 {
 	int i, j = 0;
 	char rbuf[PKTSIZE];
-	struct sockaddr_in from;
+	struct sockaddr_storage from;
 	socklen_t fromlen;
 
 	while (1) {
--- netkit-tftp-0.17.debian/tftpd/tftpd.c
+++ netkit-tftp-0.17/tftpd/tftpd.c
@@ -89,7 +89,7 @@ static int		maxtimeout = 5*TIMEOUT;
 
 static char		buf[PKTSIZE];
 static char		ackbuf[PKTSIZE];
-static struct		sockaddr_in from;
+static struct		sockaddr_storage from;
 static socklen_t	fromlen;
 
 static const char	*default_dirs[] = { "/tftpboot", 0 };
@@ -101,7 +101,7 @@ static int		secure_tftp;
 int
 main(int ac, char **av)
 {
-	struct sockaddr_in sn;
+	struct sockaddr_storage sn;
 	socklen_t snsize;
 	int dobind=1;
 
@@ -173,7 +173,7 @@ main(int ac, char **av)
 				 */
 				k = sizeof(from);
 				i = recvfrom(0, buf, sizeof (buf), 0,
-				    (struct sockaddr *)&from, &k);
+					(struct sockaddr *)&from, &k);
 				if (i > 0) {
 					n = i;
 					fromlen = k;
@@ -202,7 +202,6 @@ main(int ac, char **av)
 			exit(1);
 		}
 	}
-	from.sin_family = AF_INET;
 	alarm(0);
 
 	/*
@@ -213,15 +212,33 @@ main(int ac, char **av)
 	 * interface.
 	 */
 	snsize = sizeof(sn);
-	if (getsockname(0, (struct sockaddr *)&sn, &snsize)<0 ||
-	    sn.sin_addr.s_addr == INADDR_ANY) {
+	if (getsockname(0, (struct sockaddr *)&sn, &snsize) < 0) {
 		dobind = 0;
 	}
-	sn.sin_port = 0;
+
+	if ( dobind ) {
+	    /* Was the wildcard address contained in the socket? */
+	    if ( ((sn.ss_family == AF_INET) &&
+		    ((struct sockaddr_in *)&sn)->sin_addr.s_addr
+				== INADDR_ANY )
+		|| ((sn.ss_family == AF_INET6) &&
+		    IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)&sn)->sin6_addr))
+		)
+	    {
+		/* Implicit binding suffices for wildcard addresses. */
+		dobind = 0;
+	    }
+	}
+
+	if ( sn.ss_family == AF_INET )
+	    ((struct sockaddr_in *) &sn)->sin_port = 0;
+	else if ( sn.ss_family == AF_INET6 )
+	    ((struct sockaddr_in6 *) &sn)->sin6_port = 0;
 
 	close(0);
 	close(1);
-	peer = socket(AF_INET, SOCK_DGRAM, 0);
+
+	peer = socket(from.ss_family, SOCK_DGRAM, 0);
 	if (peer < 0) {
 		syslog(LOG_ERR, "socket: %m\n");
 		exit(1);
--- netkit-tftp-0.17.debian/tftp/tftp.c
+++ netkit-tftp-0.17/tftp/tftp.c
@@ -61,7 +61,8 @@ char tftp_rcsid[] =
 
 #include "../version.h"
 
-extern  struct sockaddr_in s_inn;         /* filled in by main */
+extern  struct sockaddr_storage s_inn;  /* filled in by main */
+extern  socklen_t s_inn_len;
 extern  int     f;                      /* the opened socket */
 extern  int     trace;
 extern  int     verbose;
@@ -71,6 +72,8 @@ extern sigjmp_buf toplevel;
 void sendfile(int fd, char *name, char *modestr);
 void recvfile(int fd, char *name, char *modestr);
 
+static struct sockaddr_storage from;	/* most recent remote address */
+static socklen_t fromlen;
 
 static char ackbuf[PKTSIZE];
 static int timeout;
@@ -111,8 +114,6 @@ sendfile(int fd, char *name, char *mode)
 	volatile u_int16_t block = 0;
 	int n;
 	volatile unsigned long amount = 0;
-	struct sockaddr_in from;
-	socklen_t fromlen;
 	volatile int convert;            /* true if doing nl->crlf conversion */
 	FILE *file;
 	volatile int firsttrip = 1;
@@ -123,6 +124,9 @@ sendfile(int fd, char *name, char *mode)
 	file = fdopen(fd, "r");
 	convert = !strcmp(mode, "netascii");
 
+	memcpy(&from, &s_inn, sizeof(from));
+	fromlen = s_inn_len;
+
 	mysignal(SIGALRM, timer);
 	do {
 		if (firsttrip) {
@@ -143,8 +147,8 @@ sendfile(int fd, char *name, char *mode)
 send_data:
 		if (trace)
 			tpacket("sent", dp, size + 4);
-		n = sendto(f, dp, size + 4, 0,
-		    (struct sockaddr *)&s_inn, sizeof(s_inn));
+
+		n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&from, fromlen);
 		if (n != size + 4) {
 			perror("tftp: sendto");
 			goto abort;
@@ -162,7 +166,7 @@ send_data:
 				perror("tftp: recvfrom");
 				goto abort;
 			}
-			s_inn.sin_port = from.sin_port;   /* added */
+
 			if (trace)
 				tpacket("received", ap, n);
 			/* should verify packet came from server */
@@ -198,11 +202,11 @@ send_data:
 		block++;
 	} while (1);
 abort:
-	initsock();
 	fclose(file);
 	stopclock();
 	if (amount > 0)
 		printstats("Sent", amount);
+	initsock(from.ss_family);	/* Synchronize address family. */
 }
 
 /*
@@ -217,8 +221,6 @@ recvfile(int fd, char *name, char *mode)
 	volatile u_int16_t block = 1;
 	int n; 
 	volatile unsigned long amount = 0;
-	struct sockaddr_in from;
-	socklen_t fromlen;
 	volatile int firsttrip = 1;
 	FILE *file;
 	volatile int convert;            /* true if converting crlf -> lf */
@@ -229,6 +231,9 @@ recvfile(int fd, char *name, char *mode)
 	file = fdopen(fd, "w");
 	convert = !strcmp(mode, "netascii");
 
+	memcpy(&from, &s_inn, sizeof(from));
+	fromlen = s_inn_len;
+
 	mysignal(SIGALRM, timer);
 	do {
 		if (firsttrip) {
@@ -245,8 +250,9 @@ recvfile(int fd, char *name, char *mode)
 send_ack:
 		if (trace)
 			tpacket("sent", ap, size);
-		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_inn,
-		    sizeof (s_inn)) != size) {
+
+		n = sendto(f, ackbuf, size, 0, (struct sockaddr *)&from, fromlen);
+		if ( n != size) {
 			alarm(0);
 			perror("tftp: sendto");
 			goto abort;
@@ -264,7 +270,7 @@ send_ack:
 				perror("tftp: recvfrom");
 				goto abort;
 			}
-			s_inn.sin_port = from.sin_port;   /* added */
+
 			if (trace)
 				tpacket("received", dp, n);
 			/* should verify client address */
@@ -299,16 +305,15 @@ send_ack:
 
 	ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
 	ap->th_block = htons((u_short)block);
-	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_inn, sizeof(s_inn));
+	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);
 
 abort:
-	initsock();
-
 	write_behind(file, convert);            /* flush last buffer */
 	fclose(file);
 	stopclock();
 	if (amount > 0)
 		printstats("Received", amount);
+	initsock(from.ss_family);	/* Synchronize address family. */
 }
 
 int
@@ -369,8 +374,8 @@ nak(int error)
 	length = strlen(pe->e_msg) + 4;
 	if (trace)
 		tpacket("sent", tp, length);
-	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_inn,
-	    sizeof (s_inn)) != length)
+	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&from, fromlen)
+	    != length)
 		perror("nak");
 }
 
--- netkit-tftp-0.17.debian/tftp/main.c
+++ netkit-tftp-0.17/tftp/main.c
@@ -68,7 +68,8 @@ char main_rcsid[] =
 
 #define	TIMEOUT		5		/* secs between rexmt's */
 
-struct sockaddr_in s_inn;
+struct sockaddr_storage s_inn;
+socklen_t s_inn_len;
 int f = -1;
 int trace;
 int verbose;
@@ -79,8 +80,8 @@ void sendfile(int fd, char *name, char *
 void recvfile(int fd, char *name, char *modestr);
 
 
-static int connected;
-static short port;
+static int connected = AF_UNSPEC;	/* If non-zero, contains active address family! */
+static char service[NI_MAXSERV] = "tftp";
 static char mode[32];
 static char line[200];
 static int margc;
@@ -151,18 +152,20 @@ struct cmd cmdtab[] = {
 static struct cmd *getcmd(const char *name);
 static char *tail(char *filename);
 
-void initsock() {
-	struct sockaddr_in s_in;
+void initsock(int af) {
+	struct sockaddr_storage s_in;
 
 	if (f >= 0)
 		close(f);
-	f = socket(AF_INET, SOCK_DGRAM, 0);
+
+	f = socket(af, SOCK_DGRAM, 0);
 	if (f < 0) {
 		perror("tftp: socket");
 		exit(3);
 	}
+
 	memset(&s_in, 0, sizeof(s_in));
-	s_in.sin_family = AF_INET;
+	s_in.ss_family = af;
 	if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
 		perror("tftp: bind");
 		exit(1);
@@ -174,12 +177,13 @@ main(int argc, char *argv[])
 {
 	int top;
 
+	/* Make a minimal sanity check. */
 	sp = getservbyname("tftp", "udp");
 	if (sp == 0) {
 		fprintf(stderr, "tftp: udp/tftp: unknown service\n");
 		exit(1);
 	}
-	initsock();
+
 	strcpy(mode, "netascii");
 	mysignal(SIGINT, intr);
 	if (argc > 1) {
@@ -192,12 +196,12 @@ main(int argc, char *argv[])
 		command(top);
 }
 
-static char hostname[100];
+static char hostname[NI_MAXHOST];
 
 void
 setpeer(int argc, char *argv[])
 {
-	struct hostent *host;
+	struct addrinfo hints, *aiptr, *ai;
 	size_t len;
 
 	if (argc < 2) {
@@ -216,36 +220,54 @@ setpeer(int argc, char *argv[])
 		printf("usage: %s host-name [port]\n", argv[0]);
 		return;
 	}
-	host = gethostbyname(argv[1]);
-	if (host) {
-		s_inn.sin_family = host->h_addrtype;
-		if (host->h_length > (int)sizeof(s_inn.sin_addr)) {
-			host->h_length = sizeof(s_inn.sin_addr);
-		}
-		memcpy(&s_inn.sin_addr, host->h_addr, host->h_length);
-		strncpy(hostname, host->h_name, sizeof(hostname));
-		hostname[sizeof(hostname)-1] = 0;
-	} 
-	else {
-		s_inn.sin_family = AF_INET;
-		if (!inet_aton(argv[1], &s_inn.sin_addr)) {
-			connected = 0;
-			printf("%s: unknown host\n", argv[1]);
-			return;
-		}
-		strcpy(hostname, argv[1]);
-	}
-	port = sp->s_port;
+
+	/* First we record the service name. Default is "tftp".
+	 */
 	if (argc == 3) {
-		port = atoi(argv[2]);
-		if (port < 0) {
-			printf("%s: bad port number\n", argv[2]);
+		if (argv[2] == NULL || *argv[2] == '\0') {
+			printf("%s: bad port name\n", argv[2]);
 			connected = 0;
 			return;
 		}
-		port = htons(port);
+		strncpy(service, argv[2], sizeof(service));
+		service[sizeof(service)-1] = '\0';
+	}
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_DGRAM;
+
+	if ( getaddrinfo(argv[1], service, &hints, &aiptr) ) {
+		connected = 0;
+		printf("%s: unknown host\n", argv[1]);
+		return;
+	}
+
+	/* Choose first applicable address. */
+	ai = aiptr;
+
+	while ( ai && (ai->ai_family != AF_INET6)
+			&& (ai->ai_family != AF_INET) )
+		ai = ai->ai_next;
+
+
+	if ( ai == NULL ) {
+		connected = 0;
+		freeaddrinfo(aiptr);
+		printf("%s: unknown host\n", argv[1]);
+		return;
 	}
-	connected = 1;
+
+	memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+	s_inn_len = ai->ai_addrlen;
+	connected = ai->ai_family;
+	strncpy(hostname, aiptr->ai_canonname, sizeof(hostname));
+	hostname[sizeof(hostname)-1] = '\0';
+	freeaddrinfo(aiptr);
+
+	/* Test and set socket for the relevant address family. */
+	initsock(connected);
 }
 
 struct	modes {
@@ -346,7 +368,8 @@ put(int argc, char *argv[])
 	targ = argv[argc - 1];
 	if (strchr(argv[argc - 1], ':')) {
 		char *cp;
-		struct hostent *hp;
+		struct addrinfo hints, *aiptr, *ai;
+		int status;
 
 		for (n = 1; n < argc - 1; n++)
 			if (strchr(argv[n], ':')) {
@@ -354,22 +377,39 @@ put(int argc, char *argv[])
 				return;
 			}
 		cp = argv[argc - 1];
-		targ = strchr(cp, ':');
+		/* Last colon. Numerical IPv6 addresses! */
+		targ = strrchr(cp, ':');
 		*targ++ = 0;
-		hp = gethostbyname(cp);
-		if (hp == NULL) {
-			fprintf(stderr, "tftp: %s: ", cp);
-			herror((char *)NULL);
+
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME; 
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = SOCK_DGRAM;
+
+		status = getaddrinfo(cp, service, &hints, &aiptr);
+		if ( status != 0 ) {
+			fprintf(stderr, "tftp: %s: %s\n", cp, gai_strerror(status));
 			return;
 		}
-		if (hp->h_length > (int)sizeof(s_inn.sin_addr)) {
-			hp->h_length = sizeof(s_inn.sin_addr);
+
+		ai = aiptr;
+		while ( ai && (ai->ai_family != AF_INET)
+				&& (ai->ai_family != AF_INET6) )
+			ai = ai->ai_next;
+
+		if ( ai == NULL ) {
+			freeaddrinfo(aiptr);
+			fprintf(stderr, "tftp: %s: %s\n", cp, "Address not found");
+			return;
 		}
-		memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length);
-		s_inn.sin_family = hp->h_addrtype;
-		connected = 1;
-		strncpy(hostname, hp->h_name, sizeof(hostname));
-		hostname[sizeof(hostname)-1] = 0;
+
+		memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+		s_inn_len = ai->ai_addrlen;
+		connected = ai->ai_family;
+		strncpy(hostname, aiptr->ai_canonname, sizeof(hostname));
+		hostname[sizeof(hostname)-1] = '\0';
+		freeaddrinfo(aiptr);
+		initsock(connected);
 	}
 	if (!connected) {
 		printf("No target machine specified.\n");
@@ -386,7 +426,7 @@ put(int argc, char *argv[])
 		if (verbose)
 			printf("putting %s to %s:%s [%s]\n",
 				ccp, hostname, targ, mode);
-		s_inn.sin_port = port;
+
 		sendfile(fd, targ, mode);
 		return;
 	}
@@ -404,7 +444,7 @@ put(int argc, char *argv[])
 		if (verbose)
 			printf("putting %s to %s:%s [%s]\n",
 				argv[n], hostname, targ, mode);
-		s_inn.sin_port = port;
+
 		sendfile(fd, targ, mode);
 	}
 }
@@ -449,29 +489,50 @@ get(int argc, char *argv[])
 			}
 	}
 	for (n = 1; n < argc ; n++) {
-		src = strchr(argv[n], ':');
+		/* Last colon. Numerical IPv6 addresses! */
+		src = strrchr(argv[n], ':');
 		if (src == NULL)
 			src = argv[n];
 		else {
-			struct hostent *hp;
+			struct addrinfo hints, *aiptr, *ai;
+			int status;
 
 			*src++ = 0;
-			hp = gethostbyname(argv[n]);
-			if (hp == NULL) {
-				fprintf(stderr, "tftp: %s: ", argv[n]);
-				herror(NULL);
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME; 
+			hints.ai_family = AF_UNSPEC;
+			hints.ai_socktype = SOCK_DGRAM;
+
+			status = getaddrinfo(argv[n], service, &hints, &aiptr);
+			if ( status ) {
+				fprintf(stderr, "tftp: %s: %s\n", argv[n],
+						gai_strerror(status));
 				continue;
 			}
-			if (hp->h_length > (int)sizeof(s_inn.sin_addr)) {
-				hp->h_length = sizeof(s_inn.sin_addr);
+
+			ai = aiptr;
+			while ( ai && (ai->ai_family != AF_INET)
+					&& (ai->ai_family != AF_INET6) )
+				ai = ai->ai_next;
+
+
+			if ( ai == NULL ) {
+				freeaddrinfo(aiptr);
+				fprintf(stderr, "tftp: %s: %s\n", argv[n],
+						"Address not found");
+				continue;
 			}
-			memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length);
-			s_inn.sin_family = hp->h_addrtype;
-			connected = 1;
-			strncpy(hostname, hp->h_name, sizeof(hostname));
+
+			memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+			s_inn_len = ai->ai_addrlen;
+			connected = ai->ai_family;
+			strncpy(hostname, aiptr->ai_canonname,
+					sizeof(hostname));
 			hostname[sizeof(hostname)-1] = 0;
+			freeaddrinfo(aiptr);
+			initsock(connected);
 		}
-		if (argc < 4) {
+		if (argc == 2 || (argc == 3 && n == 1 && !strchr(argv[2], ':'))) {
 			cp = argc == 3 ? argv[2] : tail(src);
 			fd = creat(cp, 0644);
 			if (fd < 0) {
@@ -481,7 +542,7 @@ get(int argc, char *argv[])
 			if (verbose)
 				printf("getting from %s:%s to %s [%s]\n",
 					hostname, src, cp, mode);
-			s_inn.sin_port = port;
+
 			recvfile(fd, src, mode);
 			break;
 		}
@@ -494,7 +555,7 @@ get(int argc, char *argv[])
 		if (verbose)
 			printf("getting from %s:%s to %s [%s]\n",
 				hostname, src, cp, mode);
-		s_inn.sin_port = port;
+
 		recvfile(fd, src, mode);
 	}
 }
@@ -503,7 +564,8 @@ void
 getusage(const char *s)
 {
 	printf("usage: %s host:file host:file ... file, or\n", s);
-	printf("       %s file file ... file if connected\n", s);
+	printf("       %s file file ... file   if connected, or\n", s);
+	printf("       %s host:rfile lfile\n", s);
 }
 
 

Attachment: signature.asc
Description: Digital signature

Reply via email to