Clearly I accomplished substandard and incomplete suggestions in my earlier efforts. Hard to explain how I could pick the cherries so selectively. I was certainly blinded by the fear of changing underlying mechanisms in the original code.
The good thing is that my renewed analysis has brought into light more than one shortcoming of the code as it was maintained up to this date. The new patch is to be applied on top of an unpackaged Debian debian source $ dpkg-source -x netkit-tftp_0.17-17.dsc $ patch -p0 < netkit_tftp_implement_ipv6_tftp_tftpd.diff Nothing fancy like NMU! I am burned my fingers for good in that jar. As always, the client is much harder to improve, than the server is. Best regards, Mats Erik Andersson, fil. dr 2459 41E9 C420 3F6D F68B 2E88 F768 4541 F25B 5D41
--- netkit-tftp-0.17.debian/tftp/tftpsubs.h
+++ netkit-tftp-0.17/tftp/tftpsubs.h
@@ -1,6 +1,6 @@
#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */
-void initsock(void);
+void initsock(int);
void synchnet(int, int);
struct tftphdr *r_init(void);
struct tftphdr *w_init(void);
--- netkit-tftp-0.17.debian/tftp/tftpsubs.c
+++ netkit-tftp-0.17/tftp/tftpsubs.c
@@ -55,6 +55,7 @@ char subs_rcsid[] =
#include <arpa/tftp.h>
#include <unistd.h>
#include <stdio.h>
+#include <string.h>
#include <signal.h>
#ifndef FIONREAD
@@ -254,7 +255,7 @@ synchnet(int f /* socket to flush */, in
{
int i, j = 0;
char rbuf[PKTSIZE];
- struct sockaddr_in from;
+ struct sockaddr_storage from;
socklen_t fromlen;
while (1) {
--- netkit-tftp-0.17.debian/tftpd/tftpd.c
+++ netkit-tftp-0.17/tftpd/tftpd.c
@@ -89,7 +89,7 @@ static int maxtimeout = 5*TIMEOUT;
static char buf[PKTSIZE];
static char ackbuf[PKTSIZE];
-static struct sockaddr_in from;
+static struct sockaddr_storage from;
static socklen_t fromlen;
static const char *default_dirs[] = { "/tftpboot", 0 };
@@ -101,7 +101,7 @@ static int secure_tftp;
int
main(int ac, char **av)
{
- struct sockaddr_in sn;
+ struct sockaddr_storage sn;
socklen_t snsize;
int dobind=1;
@@ -173,7 +173,7 @@ main(int ac, char **av)
*/
k = sizeof(from);
i = recvfrom(0, buf, sizeof (buf), 0,
- (struct sockaddr *)&from, &k);
+ (struct sockaddr *)&from, &k);
if (i > 0) {
n = i;
fromlen = k;
@@ -202,7 +202,6 @@ main(int ac, char **av)
exit(1);
}
}
- from.sin_family = AF_INET;
alarm(0);
/*
@@ -213,15 +212,33 @@ main(int ac, char **av)
* interface.
*/
snsize = sizeof(sn);
- if (getsockname(0, (struct sockaddr *)&sn, &snsize)<0 ||
- sn.sin_addr.s_addr == INADDR_ANY) {
+ if (getsockname(0, (struct sockaddr *)&sn, &snsize) < 0) {
dobind = 0;
}
- sn.sin_port = 0;
+
+ if ( dobind ) {
+ /* Was the wildcard address contained in the socket? */
+ if ( ((sn.ss_family == AF_INET) &&
+ ((struct sockaddr_in *)&sn)->sin_addr.s_addr
+ == INADDR_ANY )
+ || ((sn.ss_family == AF_INET6) &&
+ IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)&sn)->sin6_addr))
+ )
+ {
+ /* Implicit binding suffices for wildcard addresses. */
+ dobind = 0;
+ }
+ }
+
+ if ( sn.ss_family == AF_INET )
+ ((struct sockaddr_in *) &sn)->sin_port = 0;
+ else if ( sn.ss_family == AF_INET6 )
+ ((struct sockaddr_in6 *) &sn)->sin6_port = 0;
close(0);
close(1);
- peer = socket(AF_INET, SOCK_DGRAM, 0);
+
+ peer = socket(from.ss_family, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m\n");
exit(1);
--- netkit-tftp-0.17.debian/tftp/tftp.c
+++ netkit-tftp-0.17/tftp/tftp.c
@@ -61,7 +61,8 @@ char tftp_rcsid[] =
#include "../version.h"
-extern struct sockaddr_in s_inn; /* filled in by main */
+extern struct sockaddr_storage s_inn; /* filled in by main */
+extern socklen_t s_inn_len;
extern int f; /* the opened socket */
extern int trace;
extern int verbose;
@@ -71,6 +72,8 @@ extern sigjmp_buf toplevel;
void sendfile(int fd, char *name, char *modestr);
void recvfile(int fd, char *name, char *modestr);
+static struct sockaddr_storage from; /* most recent remote address */
+static socklen_t fromlen;
static char ackbuf[PKTSIZE];
static int timeout;
@@ -111,8 +114,6 @@ sendfile(int fd, char *name, char *mode)
volatile u_int16_t block = 0;
int n;
volatile unsigned long amount = 0;
- struct sockaddr_in from;
- socklen_t fromlen;
volatile int convert; /* true if doing nl->crlf conversion */
FILE *file;
volatile int firsttrip = 1;
@@ -123,6 +124,9 @@ sendfile(int fd, char *name, char *mode)
file = fdopen(fd, "r");
convert = !strcmp(mode, "netascii");
+ memcpy(&from, &s_inn, sizeof(from));
+ fromlen = s_inn_len;
+
mysignal(SIGALRM, timer);
do {
if (firsttrip) {
@@ -143,8 +147,8 @@ sendfile(int fd, char *name, char *mode)
send_data:
if (trace)
tpacket("sent", dp, size + 4);
- n = sendto(f, dp, size + 4, 0,
- (struct sockaddr *)&s_inn, sizeof(s_inn));
+
+ n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&from, fromlen);
if (n != size + 4) {
perror("tftp: sendto");
goto abort;
@@ -162,7 +166,7 @@ send_data:
perror("tftp: recvfrom");
goto abort;
}
- s_inn.sin_port = from.sin_port; /* added */
+
if (trace)
tpacket("received", ap, n);
/* should verify packet came from server */
@@ -198,11 +202,11 @@ send_data:
block++;
} while (1);
abort:
- initsock();
fclose(file);
stopclock();
if (amount > 0)
printstats("Sent", amount);
+ initsock(from.ss_family); /* Synchronize address family. */
}
/*
@@ -217,8 +221,6 @@ recvfile(int fd, char *name, char *mode)
volatile u_int16_t block = 1;
int n;
volatile unsigned long amount = 0;
- struct sockaddr_in from;
- socklen_t fromlen;
volatile int firsttrip = 1;
FILE *file;
volatile int convert; /* true if converting crlf -> lf */
@@ -229,6 +231,9 @@ recvfile(int fd, char *name, char *mode)
file = fdopen(fd, "w");
convert = !strcmp(mode, "netascii");
+ memcpy(&from, &s_inn, sizeof(from));
+ fromlen = s_inn_len;
+
mysignal(SIGALRM, timer);
do {
if (firsttrip) {
@@ -245,8 +250,9 @@ recvfile(int fd, char *name, char *mode)
send_ack:
if (trace)
tpacket("sent", ap, size);
- if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_inn,
- sizeof (s_inn)) != size) {
+
+ n = sendto(f, ackbuf, size, 0, (struct sockaddr *)&from, fromlen);
+ if ( n != size) {
alarm(0);
perror("tftp: sendto");
goto abort;
@@ -264,7 +270,7 @@ send_ack:
perror("tftp: recvfrom");
goto abort;
}
- s_inn.sin_port = from.sin_port; /* added */
+
if (trace)
tpacket("received", dp, n);
/* should verify client address */
@@ -299,16 +305,15 @@ send_ack:
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 *)&s_inn, sizeof(s_inn));
+ (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&from, fromlen);
abort:
- initsock();
-
write_behind(file, convert); /* flush last buffer */
fclose(file);
stopclock();
if (amount > 0)
printstats("Received", amount);
+ initsock(from.ss_family); /* Synchronize address family. */
}
int
@@ -369,8 +374,8 @@ nak(int error)
length = strlen(pe->e_msg) + 4;
if (trace)
tpacket("sent", tp, length);
- if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_inn,
- sizeof (s_inn)) != length)
+ if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&from, fromlen)
+ != length)
perror("nak");
}
--- netkit-tftp-0.17.debian/tftp/main.c
+++ netkit-tftp-0.17/tftp/main.c
@@ -68,7 +68,8 @@ char main_rcsid[] =
#define TIMEOUT 5 /* secs between rexmt's */
-struct sockaddr_in s_inn;
+struct sockaddr_storage s_inn;
+socklen_t s_inn_len;
int f = -1;
int trace;
int verbose;
@@ -79,8 +80,8 @@ void sendfile(int fd, char *name, char *
void recvfile(int fd, char *name, char *modestr);
-static int connected;
-static short port;
+static int connected = AF_UNSPEC; /* If non-zero, contains active address family! */
+static char service[NI_MAXSERV] = "tftp";
static char mode[32];
static char line[200];
static int margc;
@@ -151,18 +152,20 @@ struct cmd cmdtab[] = {
static struct cmd *getcmd(const char *name);
static char *tail(char *filename);
-void initsock() {
- struct sockaddr_in s_in;
+void initsock(int af) {
+ struct sockaddr_storage s_in;
if (f >= 0)
close(f);
- f = socket(AF_INET, SOCK_DGRAM, 0);
+
+ f = socket(af, SOCK_DGRAM, 0);
if (f < 0) {
perror("tftp: socket");
exit(3);
}
+
memset(&s_in, 0, sizeof(s_in));
- s_in.sin_family = AF_INET;
+ s_in.ss_family = af;
if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
perror("tftp: bind");
exit(1);
@@ -174,12 +177,13 @@ main(int argc, char *argv[])
{
int top;
+ /* Make a minimal sanity check. */
sp = getservbyname("tftp", "udp");
if (sp == 0) {
fprintf(stderr, "tftp: udp/tftp: unknown service\n");
exit(1);
}
- initsock();
+
strcpy(mode, "netascii");
mysignal(SIGINT, intr);
if (argc > 1) {
@@ -192,12 +196,12 @@ main(int argc, char *argv[])
command(top);
}
-static char hostname[100];
+static char hostname[NI_MAXHOST];
void
setpeer(int argc, char *argv[])
{
- struct hostent *host;
+ struct addrinfo hints, *aiptr, *ai;
size_t len;
if (argc < 2) {
@@ -216,36 +220,54 @@ setpeer(int argc, char *argv[])
printf("usage: %s host-name [port]\n", argv[0]);
return;
}
- host = gethostbyname(argv[1]);
- if (host) {
- s_inn.sin_family = host->h_addrtype;
- if (host->h_length > (int)sizeof(s_inn.sin_addr)) {
- host->h_length = sizeof(s_inn.sin_addr);
- }
- memcpy(&s_inn.sin_addr, host->h_addr, host->h_length);
- strncpy(hostname, host->h_name, sizeof(hostname));
- hostname[sizeof(hostname)-1] = 0;
- }
- else {
- s_inn.sin_family = AF_INET;
- if (!inet_aton(argv[1], &s_inn.sin_addr)) {
- connected = 0;
- printf("%s: unknown host\n", argv[1]);
- return;
- }
- strcpy(hostname, argv[1]);
- }
- port = sp->s_port;
+
+ /* First we record the service name. Default is "tftp".
+ */
if (argc == 3) {
- port = atoi(argv[2]);
- if (port < 0) {
- printf("%s: bad port number\n", argv[2]);
+ if (argv[2] == NULL || *argv[2] == '\0') {
+ printf("%s: bad port name\n", argv[2]);
connected = 0;
return;
}
- port = htons(port);
+ strncpy(service, argv[2], sizeof(service));
+ service[sizeof(service)-1] = '\0';
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if ( getaddrinfo(argv[1], service, &hints, &aiptr) ) {
+ connected = 0;
+ printf("%s: unknown host\n", argv[1]);
+ return;
+ }
+
+ /* Choose first applicable address. */
+ ai = aiptr;
+
+ while ( ai && (ai->ai_family != AF_INET6)
+ && (ai->ai_family != AF_INET) )
+ ai = ai->ai_next;
+
+
+ if ( ai == NULL ) {
+ connected = 0;
+ freeaddrinfo(aiptr);
+ printf("%s: unknown host\n", argv[1]);
+ return;
}
- connected = 1;
+
+ memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+ s_inn_len = ai->ai_addrlen;
+ connected = ai->ai_family;
+ strncpy(hostname, aiptr->ai_canonname, sizeof(hostname));
+ hostname[sizeof(hostname)-1] = '\0';
+ freeaddrinfo(aiptr);
+
+ /* Test and set socket for the relevant address family. */
+ initsock(connected);
}
struct modes {
@@ -346,7 +368,8 @@ put(int argc, char *argv[])
targ = argv[argc - 1];
if (strchr(argv[argc - 1], ':')) {
char *cp;
- struct hostent *hp;
+ struct addrinfo hints, *aiptr, *ai;
+ int status;
for (n = 1; n < argc - 1; n++)
if (strchr(argv[n], ':')) {
@@ -354,22 +377,39 @@ put(int argc, char *argv[])
return;
}
cp = argv[argc - 1];
- targ = strchr(cp, ':');
+ /* Last colon. Numerical IPv6 addresses! */
+ targ = strrchr(cp, ':');
*targ++ = 0;
- hp = gethostbyname(cp);
- if (hp == NULL) {
- fprintf(stderr, "tftp: %s: ", cp);
- herror((char *)NULL);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ status = getaddrinfo(cp, service, &hints, &aiptr);
+ if ( status != 0 ) {
+ fprintf(stderr, "tftp: %s: %s\n", cp, gai_strerror(status));
return;
}
- if (hp->h_length > (int)sizeof(s_inn.sin_addr)) {
- hp->h_length = sizeof(s_inn.sin_addr);
+
+ ai = aiptr;
+ while ( ai && (ai->ai_family != AF_INET)
+ && (ai->ai_family != AF_INET6) )
+ ai = ai->ai_next;
+
+ if ( ai == NULL ) {
+ freeaddrinfo(aiptr);
+ fprintf(stderr, "tftp: %s: %s\n", cp, "Address not found");
+ return;
}
- memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length);
- s_inn.sin_family = hp->h_addrtype;
- connected = 1;
- strncpy(hostname, hp->h_name, sizeof(hostname));
- hostname[sizeof(hostname)-1] = 0;
+
+ memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+ s_inn_len = ai->ai_addrlen;
+ connected = ai->ai_family;
+ strncpy(hostname, aiptr->ai_canonname, sizeof(hostname));
+ hostname[sizeof(hostname)-1] = '\0';
+ freeaddrinfo(aiptr);
+ initsock(connected);
}
if (!connected) {
printf("No target machine specified.\n");
@@ -386,7 +426,7 @@ put(int argc, char *argv[])
if (verbose)
printf("putting %s to %s:%s [%s]\n",
ccp, hostname, targ, mode);
- s_inn.sin_port = port;
+
sendfile(fd, targ, mode);
return;
}
@@ -404,7 +444,7 @@ put(int argc, char *argv[])
if (verbose)
printf("putting %s to %s:%s [%s]\n",
argv[n], hostname, targ, mode);
- s_inn.sin_port = port;
+
sendfile(fd, targ, mode);
}
}
@@ -449,29 +489,50 @@ get(int argc, char *argv[])
}
}
for (n = 1; n < argc ; n++) {
- src = strchr(argv[n], ':');
+ /* Last colon. Numerical IPv6 addresses! */
+ src = strrchr(argv[n], ':');
if (src == NULL)
src = argv[n];
else {
- struct hostent *hp;
+ struct addrinfo hints, *aiptr, *ai;
+ int status;
*src++ = 0;
- hp = gethostbyname(argv[n]);
- if (hp == NULL) {
- fprintf(stderr, "tftp: %s: ", argv[n]);
- herror(NULL);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ status = getaddrinfo(argv[n], service, &hints, &aiptr);
+ if ( status ) {
+ fprintf(stderr, "tftp: %s: %s\n", argv[n],
+ gai_strerror(status));
continue;
}
- if (hp->h_length > (int)sizeof(s_inn.sin_addr)) {
- hp->h_length = sizeof(s_inn.sin_addr);
+
+ ai = aiptr;
+ while ( ai && (ai->ai_family != AF_INET)
+ && (ai->ai_family != AF_INET6) )
+ ai = ai->ai_next;
+
+
+ if ( ai == NULL ) {
+ freeaddrinfo(aiptr);
+ fprintf(stderr, "tftp: %s: %s\n", argv[n],
+ "Address not found");
+ continue;
}
- memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length);
- s_inn.sin_family = hp->h_addrtype;
- connected = 1;
- strncpy(hostname, hp->h_name, sizeof(hostname));
+
+ memcpy(&s_inn, ai->ai_addr, ai->ai_addrlen);
+ s_inn_len = ai->ai_addrlen;
+ connected = ai->ai_family;
+ strncpy(hostname, aiptr->ai_canonname,
+ sizeof(hostname));
hostname[sizeof(hostname)-1] = 0;
+ freeaddrinfo(aiptr);
+ initsock(connected);
}
- if (argc < 4) {
+ if (argc == 2 || (argc == 3 && n == 1 && !strchr(argv[2], ':'))) {
cp = argc == 3 ? argv[2] : tail(src);
fd = creat(cp, 0644);
if (fd < 0) {
@@ -481,7 +542,7 @@ get(int argc, char *argv[])
if (verbose)
printf("getting from %s:%s to %s [%s]\n",
hostname, src, cp, mode);
- s_inn.sin_port = port;
+
recvfile(fd, src, mode);
break;
}
@@ -494,7 +555,7 @@ get(int argc, char *argv[])
if (verbose)
printf("getting from %s:%s to %s [%s]\n",
hostname, src, cp, mode);
- s_inn.sin_port = port;
+
recvfile(fd, src, mode);
}
}
@@ -503,7 +564,8 @@ void
getusage(const char *s)
{
printf("usage: %s host:file host:file ... file, or\n", s);
- printf(" %s file file ... file if connected\n", s);
+ printf(" %s file file ... file if connected, or\n", s);
+ printf(" %s host:rfile lfile\n", s);
}
signature.asc
Description: Digital signature

