On Fri, Apr 27, 2012 at 12:34:19PM -0400, Brad Smith wrote:
> Tested against OpenBSD's tftpd and dnsmasq. Working fine for me.

here is an updated version of the diff.

- setpeer0() and setpeer() were renamed accordingly for clarity and 
consistency. (requested by henning@)
- fix memleak(call freeaddrinfo() to release *res0)
- checks against valid server port number
- man page tweaked based on points by sthen@ and jmc@

this diff has been OK by sthen@ jmc@ henning@
i will commit it tomorrow if no objections till there.
? 1
? tftp_ipv6_3.diff
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/tftp/main.c,v
retrieving revision 1.30
diff -u -p -r1.30 main.c
--- main.c      27 Oct 2009 23:59:44 -0000      1.30
+++ main.c      24 Apr 2012 15:39:19 -0000
@@ -68,7 +68,8 @@ void                   put(int, char **);
 void                    quit(int, char **);
 void                    setascii(int, char **);
 void                    setbinary(int, char **);
-void                    setpeer(int, char **);
+void                    setpeer(char *, char *);
+void                    parsearg(int, char **);
 void                    setrexmt(int, char **);
 void                    settimeout(int, char **);
 void                    settrace(int, char **);
@@ -86,9 +87,8 @@ static __dead void     command(void);
 struct cmd             *getcmd(char *);
 char                   *tail(char *);
 
-struct sockaddr_in      peeraddr;
+struct sockaddr_storage         peeraddr;
 int                     f;
-short                   port;
 int                     trace;
 int                     verbose;
 int                     connected;
@@ -98,7 +98,6 @@ int                    margc;
 char                   *margv[MAXARGV+1];
 char                   *prompt = "tftp";
 void                    intr(int);
-struct servent         *sp;
 int                     rexmtval = TIMEOUT;
 int                     maxtimeout = 5 * TIMEOUT;
 char                    hostname[MAXHOSTNAMELEN];
@@ -134,7 +133,7 @@ struct cmd {
 };
 
 struct cmd cmdtab[] = {
-       { "connect",    chelp,  setpeer },
+       { "connect",    chelp,  parsearg },
        { "mode",       mhelp,  modecmd },
        { "put",        shelp,  put },
        { "get",        rhelp,  get },
@@ -170,26 +169,14 @@ struct    modes {
 int
 main(int argc, char *argv[])
 {
-       struct sockaddr_in      s_in;
-
-       /* socket, bind */
-       sp = getservbyname("tftp", "udp");
-       if (sp == 0)
-               errx(1, "udp/tftp: unknown service");
-       f = socket(AF_INET, SOCK_DGRAM, 0);
-       if (f < 0)
-               err(3, "socket");
-       bzero((char *)&s_in, sizeof(s_in));
-       s_in.sin_family = AF_INET;
-       if (bind(f, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
-               err(1, "bind");
+       f = -1;
 
        /* set default transfer mode */
        strlcpy(mode, "netascii", sizeof(mode));
 
        /* set peer if given */
        if (argc > 1)
-               setpeer(argc, argv);
+               parsearg(argc, argv);
 
        /* catch SIGINT */
        signal(SIGINT, intr);
@@ -205,11 +192,73 @@ main(int argc, char *argv[])
 }
 
 void
-setpeer(int argc, char *argv[])
+setpeer(char *host, char *port)
 {
-       struct hostent  *host;
-       const char      *errstr;
+       struct addrinfo hints, *res0, *res;
+       int error;
+       struct sockaddr_storage ss;
+       char *cause = "unknown";
+
+       if (connected) {
+               close(f);
+               f = -1;
+       }
+       connected = 0;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_protocol = IPPROTO_UDP;
+       hints.ai_flags = AI_CANONNAME;
+       if (!port)
+               port = "tftp";
+       error = getaddrinfo(host, port, &hints, &res0);
+       if (error) {
+               warnx("%s", gai_strerror(error));
+               return;
+       }
+
+       for (res = res0; res; res = res->ai_next) {
+               if (res->ai_addrlen > sizeof(peeraddr))
+                       continue;
+               f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+               if (f < 0) {
+                       cause = "socket";
+                       continue;
+               }
 
+               memset(&ss, 0, sizeof(ss));
+               ss.ss_family = res->ai_family;
+               ss.ss_len = res->ai_addrlen;
+               if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
+                       cause = "bind";
+                       close(f);
+                       f = -1;
+                       continue;
+               }
+
+               break;
+       }
+
+       if (f < 0)
+               warn("%s", cause);
+       else {
+               /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
+               memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
+               if (res->ai_canonname) {
+                       (void) strncpy(hostname, res->ai_canonname,
+                               sizeof(hostname));
+               } else
+                       (void) strncpy(hostname, host, sizeof(hostname));
+                       hostname[sizeof(hostname)-1] = 0;
+                       connected = 1;
+       }
+       freeaddrinfo(res0);
+}
+
+void
+parsearg(int argc, char *argv[])
+{
        if (argc < 2) {
                strlcpy(line, "Connect ", sizeof(line));
                printf("(to) ");
@@ -223,32 +272,10 @@ setpeer(int argc, char *argv[])
                printf("usage: %s [host [port]]\n", argv[0]);
                return;
        }
-       if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) {
-               peeraddr.sin_family = AF_INET;
-               (void)strncpy(hostname, argv[1], sizeof(hostname));
-               hostname[sizeof(hostname) - 1] = '\0';
-       } else {
-               host = gethostbyname(argv[1]);
-               if (host == 0) {
-                       connected = 0;
-                       printf("%s: unknown host\n", argv[1]);
-                       return;
-               }
-               peeraddr.sin_family = host->h_addrtype;
-               bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
-               (void)strlcpy(hostname, host->h_name, sizeof(hostname));
-       }
-       port = sp->s_port;
-       if (argc == 3) {
-               port = strtonum(argv[2], 1, 65535, &errstr);
-               if (errstr) {
-                       printf("%s: port number is %s\n", argv[2], errstr);
-                       connected = 0;
-                       return;
-               }
-               port = htons(port);
-       }
-       connected = 1;
+       if (argc == 2)
+               setpeer(argv[1], NULL);
+       else
+               setpeer(argv[1], argv[2]);
 }
 
 void
@@ -331,8 +358,7 @@ put(int argc, char *argv[])
                return;
        }
        targ = argv[argc - 1];
-       if (strchr(argv[argc - 1], ':')) {
-               struct hostent  *hp;
+       if (strrchr(argv[argc - 1], ':')) {
 
                for (n = 1; n < argc - 1; n++)
                        if (strchr(argv[n], ':')) {
@@ -340,18 +366,13 @@ put(int argc, char *argv[])
                                return;
                        }
                cp = argv[argc - 1];
-               targ = strchr(cp, ':');
+               targ = strrchr(cp, ':');
                *targ++ = 0;
-               hp = gethostbyname(cp);
-               if (hp == NULL) {
-                       warnx("%s: %s", cp, hstrerror(h_errno));
-                       return;
+               if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
+                       cp[strlen(cp) - 1] = '\0';
+                       cp++;
                }
-               bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
-               peeraddr.sin_family = hp->h_addrtype;
-               connected = 1;
-               port = sp->s_port;
-               strlcpy(hostname, hp->h_name, sizeof(hostname));
+               setpeer(cp, NULL);
        }
        if (!connected) {
                printf("No target machine specified.\n");
@@ -367,7 +388,6 @@ put(int argc, char *argv[])
                if (verbose)
                        printf("putting %s to %s:%s [%s]\n",
                            cp, hostname, targ, mode);
-               peeraddr.sin_port = port;
                sendfile(fd, targ, mode);
                return;
        }
@@ -388,7 +408,6 @@ put(int argc, char *argv[])
                if (verbose)
                        printf("putting %s to %s:%s [%s]\n",
                            argv[n], hostname, cp, mode);
-               peeraddr.sin_port = port;
                sendfile(fd, cp, mode);
                free(cp);
        }
@@ -428,29 +447,27 @@ get(int argc, char *argv[])
        }
        if (!connected) {
                for (n = 1; n < argc; n++)
-                       if (strchr(argv[n], ':') == 0) {
+                       if (strrchr(argv[n], ':') == 0) {
                                getusage(argv[0]);
                                return;
                        }
        }
        for (n = 1; n < argc; n++) {
-               src = strchr(argv[n], ':');
+               src = strrchr(argv[n], ':');
                if (src == NULL)
                        src = argv[n];
                else {
-                       struct hostent  *hp;
+                       char *cp;
 
                        *src++ = 0;
-                       hp = gethostbyname(argv[n]);
-                       if (hp == NULL) {
-                               warnx("%s: %s", argv[n], hstrerror(h_errno));
-                               continue;
+                       cp = argv[n];
+                       if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
+                               cp[strlen(cp) - 1] = '\0';
+                               cp++;
                        }
-                       bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
-                           hp->h_length);
-                       peeraddr.sin_family = hp->h_addrtype;
-                       connected = 1;
-                       strlcpy(hostname, hp->h_name, sizeof(hostname));
+                       setpeer(cp, NULL);
+                       if (!connected)
+                               continue;
                }
                if (argc < 4) {
                        cp = argc == 3 ? argv[2] : tail(src);
@@ -462,7 +479,6 @@ get(int argc, char *argv[])
                        if (verbose)
                                printf("getting from %s:%s to %s [%s]\n",
                                    hostname, src, cp, mode);
-                       peeraddr.sin_port = port;
                        recvfile(fd, src, mode);
                        break;
                }
@@ -475,7 +491,6 @@ get(int argc, char *argv[])
                if (verbose)
                        printf("getting from %s:%s to %s [%s]\n",
                            hostname, src, cp, mode);
-               peeraddr.sin_port = port;
                recvfile(fd, src, mode);
        }
 }
Index: tftp.1
===================================================================
RCS file: /cvs/src/usr.bin/tftp/tftp.1,v
retrieving revision 1.19
diff -u -p -r1.19 tftp.1
--- tftp.1      1 Mar 2012 03:47:19 -0000       1.19
+++ tftp.1      24 Apr 2012 15:39:19 -0000
@@ -119,6 +119,9 @@ When using the
 argument, the
 .Ar host
 will be used as the default host for future transfers.
+IPv6 addresses can be specified by enclosing
+.Ar host
+in square brackets.
 If
 .Ar localname
 is specified,
@@ -156,6 +159,9 @@ When using the
 argument, the
 .Ar host
 will be used as the default host for future transfers.
+IPv6 addresses can be specified by enclosing
+.Ar host
+in square brackets.
 If
 .Ar remotename
 is specified, the file is stored remotely as
Index: tftp.c
===================================================================
RCS file: /cvs/src/usr.bin/tftp/tftp.c,v
retrieving revision 1.22
diff -u -p -r1.22 tftp.c
--- tftp.c      27 Oct 2009 23:59:44 -0000      1.22
+++ tftp.c      24 Apr 2012 15:39:21 -0000
@@ -53,12 +53,14 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <netdb.h>
 
 #include "extern.h"
 #include "tftpsubs.h"
 
+static int     cmpport(struct sockaddr *, struct sockaddr *);
 static int     makerequest(int, const char *, struct tftphdr *, const char *);
-static void    nak(int);
+static void    nak(int, struct sockaddr *);
 static void    tpacket(const char *, struct tftphdr *, int);
 static void    startclock(void);
 static void    stopclock(void);
@@ -67,7 +69,7 @@ static void   printtimeout(void);
 static void    oack(struct tftphdr *, int, int);
 static int     oack_set(const char *, const char *);
 
-extern struct sockaddr_in       peeraddr;      /* filled in by main */
+extern struct sockaddr_storage  peeraddr;      /* filled in by main */
 extern int                      f;             /* the opened socket */
 extern int                      trace;
 extern int                      verbose;
@@ -124,7 +126,8 @@ void
 sendfile(int fd, char *name, char *mode)
 {
        struct tftphdr          *dp, *ap; /* data and ack packets */
-       struct sockaddr_in       from;
+       struct sockaddr_storage  from, peer;
+       struct sockaddr_storage  serv; /* valid server port number */
        struct pollfd            pfd[1];
        unsigned long            amount;
        socklen_t                fromlen;
@@ -138,6 +141,8 @@ sendfile(int fd, char *name, char *mode)
        convert = !strcmp(mode, "netascii");
        block = 0;
        amount = 0;
+       memcpy(&peer, &peeraddr, peeraddr.ss_len);
+       memset(&serv, 0, sizeof(serv));
 
        do {
                /* read data from file */
@@ -146,7 +151,7 @@ sendfile(int fd, char *name, char *mode)
                else {
                        size = readit(file, &dp, convert, segment_size);
                        if (size < 0) {
-                               nak(errno + 100);
+                               nak(errno + 100, (struct sockaddr *)&peer);
                                break;
                        }
                        dp->th_opcode = htons((u_short)DATA);
@@ -164,8 +169,8 @@ sendfile(int fd, char *name, char *mode)
                                if (trace)
                                        tpacket("sent", dp, size + 4);
                                if (sendto(f, dp, size + 4, 0,
-                                   (struct sockaddr *)&peeraddr,
-                                   sizeof(peeraddr)) != size + 4) {
+                                   (struct sockaddr *)&peer,
+                                   peer.ss_len) != size + 4) {
                                        warn("sendto");
                                        goto abort;
                                }
@@ -202,7 +207,14 @@ sendfile(int fd, char *name, char *mode)
                                warn("recvfrom");
                                goto abort;
                        }
-                       peeraddr.sin_port = from.sin_port;      /* added */
+                       if (!serv.ss_family)
+                               serv = from;
+                       else if (!cmpport((struct sockaddr *)&serv,
+                                   (struct sockaddr *)&from)) {
+                               warn("server port mismatch");
+                               goto abort;
+                       }
+                       peer = from;
                        if (trace)
                                tpacket("received", ap, n);
 
@@ -256,7 +268,8 @@ void
 recvfile(int fd, char *name, char *mode)
 {
        struct tftphdr          *dp, *ap; /* data and ack packets */
-       struct sockaddr_in       from;
+       struct sockaddr_storage  from, peer;
+       struct sockaddr_storage  serv; /* valid server port number */
        struct pollfd            pfd[1];
        unsigned long            amount;
        socklen_t                fromlen;
@@ -273,6 +286,8 @@ recvfile(int fd, char *name, char *mode)
        block = 1;
        amount = 0;
        firsttrip = 1;
+       memcpy(&peer, &peeraddr, peeraddr.ss_len);
+       memset(&serv, 0, sizeof(serv));
 
 options:
        do {
@@ -298,8 +313,8 @@ options:
                                if (trace)
                                        tpacket("sent", ap, size);
                                if (sendto(f, ackbuf, size, 0,
-                                   (struct sockaddr *)&peeraddr,
-                                   sizeof(peeraddr)) != size) {
+                                   (struct sockaddr *)&peer,
+                                   peer.ss_len) != size) {
                                        warn("sendto");
                                        goto abort;
                                }
@@ -335,7 +350,14 @@ options:
                                warn("recvfrom");
                                goto abort;
                        }
-                       peeraddr.sin_port = from.sin_port;      /* added */
+                       if (!serv.ss_family)
+                               serv = from;
+                       else if (!cmpport((struct sockaddr *)&serv,
+                                   (struct sockaddr *)&from)) {
+                               warn("server port mismatch");
+                               goto abort;
+                       }
+                       peer = from;
                        if (trace)
                                tpacket("received", dp, n);
 
@@ -371,7 +393,7 @@ options:
                /* write data to file */
                size = writeit(file, &dp, n - 4, convert);
                if (size < 0) {
-                       nak(errno + 100);
+                       nak(errno + 100, (struct sockaddr *)&peer);
                        break;
                }
                amount += size;
@@ -381,8 +403,8 @@ abort:
        /* ok to ack, since user has seen err msg */
        ap->th_opcode = htons((u_short)ACK);
        ap->th_block = htons((u_short)block);
-       (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
-           sizeof(peeraddr));
+       (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
+           peer.ss_len);
        write_behind(file, convert);    /* flush last buffer */
 
        fclose(file);
@@ -395,6 +417,20 @@ abort:
 }
 
 static int
+cmpport(struct sockaddr *sa, struct sockaddr *sb)
+{
+       char a[NI_MAXSERV], b[NI_MAXSERV];
+       if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
+               return (0);
+       if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
+               return (0);
+       if (strcmp(a, b) != 0)
+               return (0);
+
+       return (1);
+}
+
+static int
 makerequest(int request, const char *name, struct tftphdr *tp,
     const char *mode)
 {
@@ -436,7 +472,7 @@ makerequest(int request, const char *nam
  * offset by 100.
  */
 static void
-nak(int error)
+nak(int error, struct sockaddr *peer)
 {
        struct errmsg   *pe;
        struct tftphdr  *tp;
@@ -457,8 +493,8 @@ nak(int error)
                length = packet_size;
        if (trace)
                tpacket("sent", tp, length);
-       if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
-           sizeof(peeraddr)) != length)
+       if (sendto(f, ackbuf, length, 0, peer,
+           peer->sa_len) != length)
                warn("nak");
 }
 
@@ -588,6 +624,8 @@ oack_set(const char *option, const char 
 {
        int              i, n;
        const char      *errstr;
+       struct sockaddr_storage peer;
+       memcpy(&peer, &peeraddr, peeraddr.ss_len);
 
        for (i = 0; options[i].o_type != NULL; i++) {
                if (!strcasecmp(options[i].o_type, option)) {
@@ -600,7 +638,7 @@ oack_set(const char *option, const char 
                                    &errstr);
                                if (errstr || rexmtval != n ||
                                    opt_tout == 0) {
-                                       nak(EOPTNEG);
+                                       nak(EOPTNEG, (struct sockaddr *)&peer);
                                        intrflag = 1;
                                        return (-1);
                                }
@@ -612,7 +650,7 @@ oack_set(const char *option, const char 
                                    &errstr);
                                if (errstr || opt_blksize != n ||
                                    opt_blksize == 0) {
-                                       nak(EOPTNEG);   
+                                       nak(EOPTNEG, (struct sockaddr *)&peer);
                                        intrflag = 1;
                                        return (-1);
                                }
Index: tftpsubs.c
===================================================================
RCS file: /cvs/src/usr.bin/tftp/tftpsubs.c,v
retrieving revision 1.14
diff -u -p -r1.14 tftpsubs.c
--- tftpsubs.c  27 Oct 2009 23:59:44 -0000      1.14
+++ tftpsubs.c  24 Apr 2012 15:39:21 -0000
@@ -258,7 +258,7 @@ synchnet(int f)
 {
        int                     i, j = 0;
        char                    rbuf[SEGSIZE_MIN];
-       struct sockaddr_in      from;
+       struct sockaddr_storage from;
        socklen_t               fromlen;
 
        for (;;) {

Reply via email to