On Tue, Mar 06, 2012 at 09:32:47AM +0000, Stuart Henderson wrote: > On 2011/01/22 19:40, Gleydson Soares wrote: > > can anyone test this diff? your feedback will be most welcome > > On Wed, Sep 08, 2010 at 11:51:11AM -0300, Gleydson Soares wrote: > > > hi, > > > > > > - ipv6 support for tftp client. > > > > > > based on an old itojun's diff. > > weerd@ pointed out this old diff - here's a slightly updated version; > only minor tweaks from gsoares@ version: whitespace nits in some lines > touched in the diff, and I rewrote the manpage diff. > > works for me against usr/sbin/tftpd; any comments?
anyone willing to give this diff a go? 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 19 Apr 2012 18:47:44 -0000 @@ -68,6 +68,7 @@ void put(int, char **); void quit(int, char **); void setascii(int, char **); void setbinary(int, char **); +void setpeer0(char *, char *); void setpeer(int, char **); void setrexmt(int, char **); void settimeout(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]; @@ -170,19 +169,7 @@ 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)); @@ -205,11 +192,69 @@ main(int argc, char *argv[]) } void -setpeer(int argc, char *argv[]) +setpeer0(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) { + 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 { + 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; + } +} +void +setpeer(int argc, char *argv[]) +{ if (argc < 2) { strlcpy(line, "Connect ", sizeof(line)); printf("(to) "); @@ -223,32 +268,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 == 3) + setpeer0(argv[1], NULL); + else + setpeer0(argv[1], argv[2]); } void @@ -331,8 +354,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 +362,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)); + setpeer0(cp, NULL); } if (!connected) { printf("No target machine specified.\n"); @@ -367,7 +384,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 +404,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 +443,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)); + setpeer0(cp, NULL); + if (!connected) + continue; } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); @@ -462,7 +475,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 +487,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 19 Apr 2012 18:47:44 -0000 @@ -156,6 +156,10 @@ When using the argument, the .Ar host will be used as the default host for future transfers. +A +.Ar host +specified as a numeric IPv6 address must be wrapped in square brackets: +.Ar [host]:filename . 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 19 Apr 2012 18:47:45 -0000 @@ -58,7 +58,7 @@ #include "tftpsubs.h" 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 +67,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 +124,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; + struct sockaddr_storage peer; struct pollfd pfd[1]; unsigned long amount; socklen_t fromlen; @@ -138,6 +139,7 @@ sendfile(int fd, char *name, char *mode) convert = !strcmp(mode, "netascii"); block = 0; amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); do { /* read data from file */ @@ -146,7 +148,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 +166,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 +204,19 @@ sendfile(int fd, char *name, char *mode) warn("recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + switch (peer.ss_family) { /* added */ + case AF_INET: + ((struct sockaddr_in *)&peer)->sin_port = + ((struct sockaddr_in *)&from)->sin_port; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&peer)->sin6_port = + ((struct sockaddr_in6 *)&from)->sin6_port; + break; + default: + /* unsupported */ + break; + } if (trace) tpacket("received", ap, n); @@ -256,7 +270,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; + struct sockaddr_storage peer; struct pollfd pfd[1]; unsigned long amount; socklen_t fromlen; @@ -273,6 +288,7 @@ recvfile(int fd, char *name, char *mode) block = 1; amount = 0; firsttrip = 1; + memcpy(&peer, &peeraddr, peeraddr.ss_len); options: do { @@ -298,8 +314,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 +351,19 @@ options: warn("recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + switch (peer.ss_family) { /* added */ + case AF_INET: + ((struct sockaddr_in *)&peer)->sin_port = + ((struct sockaddr_in *)&from)->sin_port; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&peer)->sin6_port = + ((struct sockaddr_in6 *)&from)->sin6_port; + break; + default: + /* unsupported */ + break; + } if (trace) tpacket("received", dp, n); @@ -371,7 +399,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 +409,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); @@ -436,7 +464,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 +485,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 +616,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 +630,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 +642,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 19 Apr 2012 18:47:45 -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 (;;) {