diff --git a/ChangeLog b/ChangeLog index cbd3db2..b27d5e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2010-08-19 Mats Erik Andersson + * src/tftp.c: Implement full support for IPv6. + +2010-08-19 Mats Erik Andersson + * src/tftpd.c: Implement full support for IPv6. 2010-08-19 Mats Erik Andersson diff --git a/src/tftp.c b/src/tftp.c index f3b3760..e2cfe4a 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -80,8 +80,9 @@ #include "xalloc.h" #include "progname.h" #include "tftpsubs.h" +#include "sockaddr_aux.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; @@ -103,8 +104,8 @@ static void tpacket (const char *, struct tftphdr *, int); #define TIMEOUT 5 /* secs between rexmt's */ -struct sockaddr_in peeraddr; -int f; +struct sockaddr_storage peeraddr; +int f = -1; short port; int trace; int verbose; @@ -225,7 +226,7 @@ static struct argp argp = {argp_options, parse_opt, args_doc, doc}; int main (int argc, char *argv[]) { - struct sockaddr_in sin; + struct sockaddr_storage sin; set_program_name (argv[0]); iu_argp_init ("tftp", default_program_authors); @@ -237,19 +238,7 @@ main (int argc, char *argv[]) fprintf (stderr, "tftp: udp/tftp: unknown service\n"); exit (1); } - f = socket (AF_INET, SOCK_DGRAM, 0); - if (f < 0) - { - perror ("tftp: socket"); - exit (3); - } - memset (&sin, 0, sizeof (sin)); - sin.sin_family = AF_INET; - if (bind (f, (struct sockaddr *) &sin, sizeof (sin)) < 0) - { - perror ("tftp: bind"); - exit (1); - } + strcpy (mode, "netascii"); signal (SIGINT, intr); if (hostport_argc > 1) @@ -277,26 +266,62 @@ char *hostname; static int resolve_name (char *name, int allow_null) { - struct hostent *hp = gethostbyname (name); - if (hp == NULL) + int rc; + struct sockaddr_storage ss; + struct addrinfo hints, *ai, *aiptr; + + memset (&hints, '\0', sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_CANONNAME; +#ifdef AI_ADDRCONFIG + hints.ai_flags += AI_ADDRCONFIG; +#endif + + rc = getaddrinfo (name, "tftp", &hints, &aiptr); + if (rc) { if (allow_null) return RESOLVE_NOT_RESOLVED; - fprintf (stderr, "tftp: %s: ", name); - herror ((char *) NULL); + fprintf (stderr, "tftp: %s: %s\n", name, gai_strerror (rc)); return RESOLVE_FAIL; } - else if (hp->h_length != sizeof peeraddr.sin_addr) + + if (f >= 0) { - fprintf (stderr, "tftp: resolving %s returns unexpected length", name); - return RESOLVE_FAIL; + close (f); + f = -1; } - memcpy (&peeraddr.sin_addr, hp->h_addr, hp->h_length); - peeraddr.sin_family = hp->h_addrtype; - connected = 1; - free (hostname); - hostname = xstrdup (hp->h_name); - return RESOLVE_OK; + + for (ai = aiptr; ai; ai = ai->ai_next) + { + f = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (f < 0) + continue; + + memset(&ss, '\0', sizeof (ss)); + ss.ss_family = ai->ai_family; + if (bind (f, (struct sockaddr *) &ss, + get_socklen ((struct sockaddr *) &ss))) + { + close (f); + f = -1; + continue; + } + + /* Successfully resolved hostname. */ + memcpy (&peeraddr, ai->ai_addr, ai->ai_addrlen); + connected = 1; + free (hostname); + hostname = xstrdup (ai->ai_canonname); + break; + } + + freeaddrinfo(aiptr); + if (ai == NULL) + return RESOLVE_FAIL; + else + return RESOLVE_OK; } /* Prompt for more arguments from the user with PROMPT, putting the results @@ -330,7 +355,7 @@ setpeer (int argc, char *argv[]) return; } - switch (resolve_name (argv[1], 1)) + switch (resolve_name (argv[1], 0)) { case RESOLVE_OK: break; @@ -338,6 +363,7 @@ setpeer (int argc, char *argv[]) case RESOLVE_FAIL: return; +#if 0 case RESOLVE_NOT_RESOLVED: peeraddr.sin_family = AF_INET; peeraddr.sin_addr.s_addr = inet_addr (argv[1]); @@ -348,9 +374,10 @@ setpeer (int argc, char *argv[]) return; } hostname = xstrdup (argv[1]); +#endif } - port = sp->s_port; + port = ntohs (sp->s_port); if (argc == 3) { port = atoi (argv[2]); @@ -360,7 +387,6 @@ setpeer (int argc, char *argv[]) connected = 0; return; } - port = htons (port); } connected = 1; } @@ -488,7 +514,8 @@ put (int argc, char *argv[]) } if (verbose) printf ("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode); - peeraddr.sin_port = port ? port : sp->s_port; + set_port ((struct sockaddr *) &peeraddr, + port ? port : ntohs (sp->s_port)); send_file (fd, targ, mode); return; } @@ -508,7 +535,8 @@ put (int argc, char *argv[]) } if (verbose) printf ("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode); - peeraddr.sin_port = port ? port : sp->s_port; + set_port ((struct sockaddr *) &peeraddr, + port ? port : ntohs (sp->s_port)); send_file (fd, targ, mode); } } @@ -573,7 +601,8 @@ get (int argc, char *argv[]) if (verbose) printf ("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); - peeraddr.sin_port = port ? port : sp->s_port; + set_port ((struct sockaddr *) &peeraddr, + port ? port : ntohs (sp->s_port)); recvfile (fd, src, mode); break; } @@ -587,7 +616,8 @@ get (int argc, char *argv[]) } if (verbose) printf ("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); - peeraddr.sin_port = port ? port : sp->s_port; + set_port ((struct sockaddr *) &peeraddr, + port ? port : ntohs (sp->s_port)); recvfile (fd, src, mode); } } @@ -877,7 +907,8 @@ send_file (int fd, char *name, char *mode) if (trace) tpacket ("sent", dp, size + 4); n = sendto (f, (const char *) dp, size + 4, 0, - (struct sockaddr *) &peeraddr, sizeof (peeraddr)); + (struct sockaddr *) &peeraddr, + get_socklen ((struct sockaddr *) &peeraddr)); if (n != size + 4) { perror ("tftp: sendto"); @@ -901,7 +932,8 @@ send_file (int fd, char *name, char *mode) perror ("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + set_port ((struct sockaddr *) &peeraddr, + get_port ((struct sockaddr *) &from)); if (trace) tpacket ("received", ap, n); /* should verify packet came from server */ @@ -990,7 +1022,7 @@ recvfile (int fd, char *name, char *mode) if (trace) tpacket ("sent", ap, size); if (sendto (f, ackbuf, size, 0, (struct sockaddr *) &peeraddr, - sizeof (peeraddr)) != size) + get_socklen ((struct sockaddr *) &peeraddr)) != size) { alarm (0); perror ("tftp: sendto"); @@ -1015,7 +1047,8 @@ recvfile (int fd, char *name, char *mode) perror ("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + set_port ((struct sockaddr *) &peeraddr, + get_port ((struct sockaddr *) &from)); if (trace) tpacket ("received", dp, n); /* should verify client address */ @@ -1058,7 +1091,8 @@ recvfile (int fd, char *name, char *mode) abort: /* ok to ack, since user */ ap->th_opcode = htons ((u_short) ACK); /* has seen err msg */ ap->th_block = htons ((u_short) block); - sendto (f, ackbuf, 4, 0, (struct sockaddr *) &peeraddr, sizeof (peeraddr)); + sendto (f, ackbuf, 4, 0, (struct sockaddr *) &peeraddr, + get_socklen ((struct sockaddr *) &peeraddr)); write_behind (file, convert); /* flush last buffer */ fclose (file); stopclock (); @@ -1130,7 +1164,7 @@ nak (int error) if (trace) tpacket ("sent", tp, length); if (sendto (f, ackbuf, length, 0, (struct sockaddr *) &peeraddr, - sizeof (peeraddr)) != length) + get_socklen ((struct sockaddr *) &peeraddr)) != length) perror ("nak"); }