Package: tftpd
Version: 0.17-18
Severity: important
Tags: patch
Usertags: kfreebsd

Hello there!

In a GNU project I have discovered that the legacy code
of netkit-tftpd is not suitable for coping with IPv4
addresses mapped as IPv6. Here GNU/Linux is forgiving
enough, but GNU/kFreeBSD is hit by a defect.

Given that the superserver is running a dual stacked
listener for TFTP, then

    tftp> get [::1]:file

works correctly, but

    tftp> get 127.0.0.1:file

suffers a time out, since the TFTP server cannot properly
calculate the remote end address. The only solution I have
uncovered so far is to remove connect() and to use sendto()
throughout the server code.

The patch below resolves the clamity on a kfreebsd-amd64
system. The corresponding changes were recently applied
by me to the GNU Inetutils trunk (tested on five architectures),
but at the moment the text below has only been tried for
the Debian package on kfreebsd-amd64.

Best regards,
  Mats Erik Andersson, DM
Description: Defects in IPv4-mapped-as-IPv6.
 On GNU/kFreeBSD IPv4 addresses mapped as IPv6
 are not resolvable in the present code.
 .
 Removal of connect() and full usage of sendto()
 instead of send() does resolve this issue.
Author: Mats Erik Andersson <deb...@gisladisker.se>
Last-Update: 2011-10-31

--- netkit-tftp-0.17/tftpd/tftpd.c.orig
+++ netkit-tftp-0.17/tftpd/tftpd.c
@@ -249,10 +249,6 @@
 		exit(1);
 	}
 
-	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
-		syslog(LOG_ERR, "connect: %m\n");
-		exit(1);
-	}
 	tp = (struct tftphdr *)buf;
 	tp->th_opcode = ntohs(tp->th_opcode);
 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
@@ -464,7 +460,8 @@
 		(void) sigsetjmp(timeoutbuf, 1);
 
 send_data:
-		if (send(peer, dp, size + 4, 0) != size + 4) {
+		if (sendto(peer, dp, size + 4, 0, (struct sockaddr *)&from, fromlen)
+			!= size + 4) {
 			syslog(LOG_ERR, "tftpd: write: %m\n");
 			goto abort;
 		}
@@ -531,7 +528,7 @@
 		block++;
 		(void) sigsetjmp(timeoutbuf, 1);
 send_ack:
-		if (send(peer, ackbuf, 4, 0) != 4) {
+		if (sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen) != 4) {
 			syslog(LOG_ERR, "tftpd: write: %m\n");
 			goto abort;
 		}
@@ -571,7 +568,7 @@
 
 	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
 	ap->th_block = htons((u_short)(block));
-	(void) send(peer, ackbuf, 4, 0);
+	(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);
 
 	mysignal(SIGALRM, justquit);      /* just quit on timeout */
 	alarm(rexmtval);
@@ -580,7 +577,7 @@
 	if (n >= 4 &&                   /* if read some data */
 	    dp->th_opcode == DATA &&    /* and got a data block */
 	    block == dp->th_block) {	/* then my last ack was lost */
-		(void) send(peer, ackbuf, 4, 0);     /* resend final ack */
+		(void) sendto(peer, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);     /* resend final ack */
 	}
 abort:
 	return;
@@ -628,6 +625,6 @@
 	length = strlen(pe->e_msg);
 	tp->th_msg[length] = '\0';
 	length += 5;
-	if (send(peer, buf, length, 0) != length)
+	if (sendto(peer, buf, length, 0, (struct sockaddr *)&from, fromlen) != length)
 		syslog(LOG_ERR, "nak: %m\n");
 }

Reply via email to