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"); }