diff options
author | ume <ume@FreeBSD.org> | 2002-04-11 17:14:22 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2002-04-11 17:14:22 +0000 |
commit | 4f51ffc3fede007b2d1c6ce56bdf217f700eb1c7 (patch) | |
tree | 17221edddfe751134988e362f0829a38aa1ab240 /usr.bin | |
parent | f8786d960f554dc901bb184a3bf33ca3ebf0eea2 (diff) | |
download | FreeBSD-src-4f51ffc3fede007b2d1c6ce56bdf217f700eb1c7.zip FreeBSD-src-4f51ffc3fede007b2d1c6ce56bdf217f700eb1c7.tar.gz |
IPv6 support for tftp/tftpd.
Obtained from: KAME
MFC after: 2 weeks
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/tftp/main.c | 167 | ||||
-rw-r--r-- | usr.bin/tftp/tftp.1 | 7 | ||||
-rw-r--r-- | usr.bin/tftp/tftp.c | 73 | ||||
-rw-r--r-- | usr.bin/tftp/tftpsubs.c | 2 |
4 files changed, 155 insertions, 94 deletions
diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c index 38b376c..251abf5 100644 --- a/usr.bin/tftp/main.c +++ b/usr.bin/tftp/main.c @@ -77,9 +77,8 @@ __FBSDID("$FreeBSD$"); #define MAXLINE 200 #define TIMEOUT 5 /* secs between rexmt's */ -struct sockaddr_in peeraddr; +struct sockaddr_storage peeraddr; int f; -short port; int trace; int verbose; int connected; @@ -88,7 +87,6 @@ char line[MAXLINE]; int margc; char *margv[20]; jmp_buf toplevel; -struct servent *sp; void get(int, char **); void help(int, char **); @@ -98,6 +96,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 **); @@ -160,18 +159,7 @@ main(argc, argv) int argc; char *argv[]; { - struct sockaddr_in lsin; - - 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 *)&lsin, sizeof(lsin)); - lsin.sin_family = AF_INET; - if (bind(f, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) - err(1, "bind"); + f = -1; strcpy(mode, "netascii"); signal(SIGINT, intr); if (argc > 1) { @@ -187,11 +175,78 @@ main(argc, argv) char hostname[MAXHOSTNAMELEN]; void +setpeer0(host, port) + char *host; + char *port; +{ + 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 setpeer(argc, argv) int argc; char *argv[]; { - struct hostent *host; if (argc < 2) { strcpy(line, "Connect "); @@ -205,34 +260,10 @@ setpeer(argc, argv) printf("usage: %s host-name [port]\n", argv[0]); return; } - host = gethostbyname(argv[1]); - if (host) { - peeraddr.sin_family = host->h_addrtype; - bcopy(host->h_addr, &peeraddr.sin_addr, - MIN(sizeof(peeraddr.sin_addr), (size_t)host->h_length)); - strncpy(hostname, host->h_name, sizeof(hostname)); - } else { - peeraddr.sin_family = AF_INET; - peeraddr.sin_addr.s_addr = inet_addr(argv[1]); - if (peeraddr.sin_addr.s_addr == INADDR_NONE) { - connected = 0; - printf("%s: unknown host\n", argv[1]); - return; - } - strncpy(hostname, argv[1], sizeof(hostname)); - } - hostname[sizeof(hostname) - 1] = '\0'; - port = sp->s_port; - if (argc == 3) { - port = atoi(argv[2]); - if (port < 0) { - printf("%s: bad port number\n", argv[2]); - connected = 0; - return; - } - port = htons(port); - } - connected = 1; + if (argc == 3) + setpeer0(argv[1], NULL); + else + setpeer0(argv[1], argv[2]); } struct modes { @@ -336,9 +367,8 @@ put(argc, argv) return; } targ = argv[argc - 1]; - if (index(argv[argc - 1], ':')) { + if (rindex(argv[argc - 1], ':')) { char *lcp; - struct hostent *hp; for (n = 1; n < argc - 1; n++) if (index(argv[n], ':')) { @@ -346,20 +376,13 @@ put(argc, argv) return; } lcp = argv[argc - 1]; - targ = index(lcp, ':'); + targ = rindex(lcp, ':'); *targ++ = 0; - hp = gethostbyname(lcp); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", lcp); - herror((char *)NULL); - return; + if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { + lcp[strlen(lcp) - 1] = '\0'; + lcp++; } - bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, - MIN(sizeof(peeraddr.sin_addr), (size_t)hp->h_length)); - peeraddr.sin_family = hp->h_addrtype; - connected = 1; - strncpy(hostname, hp->h_name, sizeof(hostname)); - hostname[sizeof(hostname) - 1] = '\0'; + setpeer0(lcp, NULL); } if (!connected) { printf("No target machine specified.\n"); @@ -375,7 +398,6 @@ put(argc, argv) if (verbose) printf("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode); - peeraddr.sin_port = port; xmitfile(fd, targ, mode); return; } @@ -393,7 +415,6 @@ put(argc, argv) if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode); - peeraddr.sin_port = port; xmitfile(fd, targ, mode); } } @@ -433,31 +454,27 @@ get(argc, argv) } if (!connected) { for (n = 1; n < argc ; n++) - if (index(argv[n], ':') == 0) { + if (rindex(argv[n], ':') == 0) { getusage(argv[0]); return; } } for (n = 1; n < argc ; n++) { - src = index(argv[n], ':'); + src = rindex(argv[n], ':'); if (src == NULL) src = argv[n]; else { - struct hostent *hp; + char *lcp; *src++ = 0; - hp = gethostbyname(argv[n]); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", argv[n]); - herror((char *)NULL); - continue; + lcp = argv[n]; + if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { + lcp[strlen(lcp) - 1] = '\0'; + lcp++; } - bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, - MIN(sizeof(peeraddr.sin_addr), (size_t)hp->h_length)); - peeraddr.sin_family = hp->h_addrtype; - connected = 1; - strncpy(hostname, hp->h_name, sizeof(hostname)); - hostname[sizeof(hostname) - 1] = '\0'; + setpeer0(lcp, NULL); + if (!connected) + continue; } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); @@ -469,7 +486,6 @@ get(argc, 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; } @@ -482,7 +498,6 @@ get(argc, argv) if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); - peeraddr.sin_port = port; recvfile(fd, src, mode); } } diff --git a/usr.bin/tftp/tftp.1 b/usr.bin/tftp/tftp.1 index a09cf37..7f21590 100644 --- a/usr.bin/tftp/tftp.1 +++ b/usr.bin/tftp/tftp.1 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1990, 1993, 1994 +.\" $NetBSD: tftp.1,v 1.11 1999/12/13 04:44:55 itojun Exp $ .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -137,6 +137,11 @@ If the remote-directory form is used, the remote host is assumed to be a .Tn UNIX machine. +If you need to specify IPv6 numeric address to +.Ar hosts , +wrap them using square bracket like +.Ar [hosts]:filename +to disambiguate the colon. .Pp .It Cm quit Exit diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c index 7b4eb95..b2cc868 100644 --- a/usr.bin/tftp/tftp.c +++ b/usr.bin/tftp/tftp.c @@ -61,11 +61,12 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <string.h> #include <unistd.h> +#include <netdb.h> #include "extern.h" #include "tftpsubs.h" -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; @@ -78,13 +79,14 @@ int timeout; jmp_buf toplevel; jmp_buf timeoutbuf; -static void nak(int); +static void nak(int, struct sockaddr *); static int makerequest(int, const char *, struct tftphdr *, const char *); static void printstats(const char *, unsigned long); static void startclock(void); static void stopclock(void); static void timer(int); static void tpacket(const char *, struct tftphdr *, int); +static int cmpport(struct sockaddr *, struct sockaddr *); /* * Send the requested file. @@ -101,9 +103,11 @@ xmitfile(fd, name, mode) volatile unsigned short block; volatile int size, convert; volatile unsigned long amount; - struct sockaddr_in from; + struct sockaddr_storage from; int fromlen; FILE *file; + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ startclock(); /* start stat's clock */ dp = r_init(); /* reset fillbuf/read-ahead code */ @@ -112,6 +116,8 @@ xmitfile(fd, name, mode) convert = !strcmp(mode, "netascii"); block = 0; amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); signal(SIGALRM, timer); do { @@ -121,7 +127,7 @@ xmitfile(fd, name, mode) /* size = read(fd, dp->th_data, SEGSIZE); */ size = readit(file, &dp, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, (struct sockaddr *)&peer); break; } dp->th_opcode = htons((u_short)DATA); @@ -133,7 +139,7 @@ send_data: if (trace) tpacket("sent", dp, size + 4); n = sendto(f, dp, size + 4, 0, - (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + (struct sockaddr *)&peer, peer.ss_len); if (n != size + 4) { warn("sendto"); goto abort; @@ -151,7 +157,14 @@ send_data: 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); /* should verify packet came from server */ @@ -207,10 +220,12 @@ recvfile(fd, name, mode) volatile unsigned short block; volatile int size, firsttrip; volatile unsigned long amount; - struct sockaddr_in from; + struct sockaddr_storage from; int fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ startclock(); dp = w_init(); @@ -220,6 +235,8 @@ recvfile(fd, name, mode) block = 1; firsttrip = 1; amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); signal(SIGALRM, timer); do { @@ -237,8 +254,8 @@ recvfile(fd, name, mode) send_ack: if (trace) tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != size) { + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer, + peer.ss_len) != size) { alarm(0); warn("sendto"); goto abort; @@ -256,7 +273,14 @@ send_ack: 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); /* should verify client address */ @@ -288,7 +312,7 @@ send_ack: /* size = write(fd, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, (struct sockaddr *)&peer); break; } amount += size; @@ -296,8 +320,8 @@ send_ack: abort: /* ok to ack, since user */ 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 *)&peeraddr, - sizeof(peeraddr)); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer, + peer.ss_len); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); @@ -347,8 +371,9 @@ struct errmsg { * offset by 100. */ static void -nak(error) +nak(error, peer) int error; + struct sockaddr *peer; { struct errmsg *pe; struct tftphdr *tp; @@ -368,8 +393,7 @@ nak(error) length = strlen(pe->e_msg) + 4; 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"); } @@ -457,3 +481,20 @@ timer(sig) } longjmp(timeoutbuf, 1); } + +static int +cmpport(sa, sb) + 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; +} diff --git a/usr.bin/tftp/tftpsubs.c b/usr.bin/tftp/tftpsubs.c index 50ff82d..a3673d8 100644 --- a/usr.bin/tftp/tftpsubs.c +++ b/usr.bin/tftp/tftpsubs.c @@ -260,7 +260,7 @@ synchnet(f) { int i, j = 0; char rbuf[PKTSIZE]; - struct sockaddr_in from; + struct sockaddr_storage from; int fromlen; while (1) { |