From 1e5db504b478265ffcfb535388bfbe7365bad6b4 Mon Sep 17 00:00:00 2001 From: Mats Erik Andersson Date: Fri, 17 Sep 2010 18:20:49 +0200 Subject: [PATCH] src/tftp.c: Migration to IPv6 supporting code. --- ChangeLog | 15 +++++ src/tftp.c | 181 +++++++++++++++++++++++++++++++++++------------------------- 2 files changed, 121 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9cdbaaa..ca5bc5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2010-09-12 Mats Erik Andersson + * src/tftp.c: Implementation of IPv6 support. Consistent use + of "struct sockaddr_storage". + (from, fromlen): New static variables of file scope. + (port, service): String value "service" replaces numeric "port". + (resolve_name): New signature adding service string. Coding now + uses getaddrinfo(3). Host and service are resolved in one go. + (send_file, put_file): Splitting into "peeraddr" and "from" + keeps track of the remotely allocated TID port. + (send_file, get_file): Explicit port manipulation is removed. + (put, get): Likewise. + (put, get): Split on rightmost colon when hosts are involved. + (nak): Message is sent to "from", being the most recent address. + +2010-09-12 Mats Erik Andersson + * libinetutils/tftpsubs.c (synchnet): Upgrade the variable "from" to be "struct sockaddr_storage". * src/tftpd.c: Upgrade all "sockaddr_in" to "sockaddr_storage". diff --git a/src/tftp.c b/src/tftp.c index 6515976..09671df 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -97,9 +97,13 @@ static void tpacket (const char *, struct tftphdr *, int); static int rexmtval = TIMEOUT; static int maxtimeout = 5 * TIMEOUT; -static struct sockaddr_in peeraddr; /* filled in by main */ -static int f; /* the opened socket */ -static short port; +static struct sockaddr_storage peeraddr; /* filled in by main */ +static socklen_t peeraddrlen; +static struct sockaddr_storage from; /* remote address with TID */ +static socklen_t fromlen; + +static int f = -1; /* the opened socket */ +static char service[NI_MAXSERV] = "tftp"; /* string for port selection */ static int trace; static int verbose; static int connected; @@ -220,7 +224,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); @@ -232,19 +236,7 @@ main (int argc, char *argv[]) fprintf (stderr, "tftp: udp/tftp: unknown service\n"); exit (EXIT_FAILURE); } - f = socket (AF_INET, SOCK_DGRAM, 0); - if (f < 0) - { - perror ("tftp: socket"); - exit (EXIT_FAILURE); - } - memset (&sin, 0, sizeof (sin)); - sin.sin_family = AF_INET; - if (bind (f, (struct sockaddr *) &sin, sizeof (sin)) < 0) - { - perror ("tftp: bind"); - exit (EXIT_FAILURE); - } + strcpy (mode, "netascii"); signal (SIGINT, intr); if (hostport_argc > 1) @@ -270,28 +262,64 @@ char *hostname; RESOLVE_NOT_RESOLVED name is not resolved and ALLOW_NULL is true */ static int -resolve_name (char *name, int allow_null) +resolve_name (char *name, char *serv, 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, serv, &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, ai->ai_addrlen)) + { + close (f); + f = -1; + continue; + } + + /* Successfully resolved hostname. */ + memcpy (&peeraddr, ai->ai_addr, ai->ai_addrlen); + peeraddrlen = 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 @@ -325,38 +353,21 @@ setpeer (int argc, char *argv[]) return; } - switch (resolve_name (argv[1], 1)) + if (argc == 3) + { + strncpy (service, argv[2], sizeof (service)); + service[sizeof (service) - 1] = '\0'; + } + + switch (resolve_name (argv[1], service, 0)) { case RESOLVE_OK: break; case RESOLVE_FAIL: return; - - case RESOLVE_NOT_RESOLVED: - peeraddr.sin_family = AF_INET; - peeraddr.sin_addr.s_addr = inet_addr (argv[1]); - if (peeraddr.sin_addr.s_addr == -1) - { - connected = 0; - printf ("%s: unknown host\n", argv[1]); - return; - } - hostname = xstrdup (argv[1]); } - 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; } @@ -461,9 +472,12 @@ put (int argc, char *argv[]) return; } cp = argv[argc - 1]; - targ = strchr (cp, ':'); + /* The rightmost colon is used, since colon + * appears in numerical IPv6 addresses. + */ + targ = strrchr (cp, ':'); *targ++ = 0; - if (resolve_name (cp, 0) != RESOLVE_OK) + if (resolve_name (cp, service, 0) != RESOLVE_OK) return; } if (!connected) @@ -483,7 +497,7 @@ 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; + send_file (fd, targ, mode); return; } @@ -503,7 +517,7 @@ 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; + send_file (fd, targ, mode); } } @@ -545,13 +559,16 @@ get (int argc, char *argv[]) } for (n = 1; n < argc; n++) { - src = strchr (argv[n], ':'); + /* The rightmost colon is used, since colon + * appears in numerical IPv6 addresses. + */ + src = strrchr (argv[n], ':'); if (src == NULL) src = argv[n]; else { *src++ = 0; - if (resolve_name (argv[n], 0) != RESOLVE_OK) + if (resolve_name (argv[n], service, 0) != RESOLVE_OK) continue; } @@ -568,7 +585,7 @@ 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; + recvfile (fd, src, mode); break; } @@ -582,7 +599,7 @@ 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; + recvfile (fd, src, mode); } } @@ -832,8 +849,6 @@ send_file (int fd, char *name, char *mode) register int n; volatile int block, size, convert; volatile unsigned long amount; - struct sockaddr_in from; - socklen_t fromlen; FILE *file; startclock (); /* start stat's clock */ @@ -844,6 +859,13 @@ send_file (int fd, char *name, char *mode) block = 0; amount = 0; + /* The server is first contacted at the client selected port. + * Later packet exchange will use an updated address reflecting + * the port allocated at the server end. + */ + memcpy(&from, &peeraddr, peeraddrlen); + fromlen = peeraddrlen; + signal (SIGALRM, timer); do { @@ -868,7 +890,7 @@ 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 *) &from, fromlen); if (n != size + 4) { perror ("tftp: sendto"); @@ -882,6 +904,7 @@ send_file (int fd, char *name, char *mode) do { fromlen = sizeof (from); + /* Receive data and update remote port. */ n = recvfrom (f, ackbuf, sizeof (ackbuf), 0, (struct sockaddr *) &from, &fromlen); } @@ -892,7 +915,7 @@ send_file (int fd, char *name, char *mode) perror ("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + if (trace) tpacket ("received", ap, n); /* should verify packet came from server */ @@ -945,8 +968,6 @@ recvfile (int fd, char *name, char *mode) register int n; volatile int block, size, firsttrip; volatile unsigned long amount; - struct sockaddr_in from; - socklen_t fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ @@ -959,6 +980,13 @@ recvfile (int fd, char *name, char *mode) firsttrip = 1; amount = 0; + /* The server is first contacted at the client selected port. + * Later packet exchange will use an updated address reflecting + * the port allocated at the server end. + */ + memcpy(&from, &peeraddr, peeraddrlen); + fromlen = peeraddrlen; + signal (SIGALRM, timer); do { @@ -980,8 +1008,8 @@ recvfile (int fd, char *name, char *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 *) &from, fromlen) != size) { alarm (0); perror ("tftp: sendto"); @@ -995,6 +1023,7 @@ recvfile (int fd, char *name, char *mode) do { fromlen = sizeof (from); + /* Receive data and update remote port. */ n = recvfrom (f, (char *) dp, PKTSIZE, 0, (struct sockaddr *) &from, &fromlen); } @@ -1006,7 +1035,7 @@ recvfile (int fd, char *name, char *mode) perror ("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + if (trace) tpacket ("received", dp, n); /* should verify client address */ @@ -1049,7 +1078,7 @@ 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 *) &from, fromlen); write_behind (file, convert); /* flush last buffer */ fclose (file); stopclock (); @@ -1120,8 +1149,10 @@ nak (int 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) + + /* Must send NAK to remote host at port of value TID! */ + if (sendto (f, ackbuf, length, 0, + (struct sockaddr *) &from, fromlen) != length) perror ("nak"); } -- 1.7.0