Description: Implement IPv6 support in client 'tftp'. The consistent use of 'struct sockaddr_storage' and the replacement of gethostbyname(3) with getaddrinfo(3) implements working address family independence. . All internal use of a port number is now handled in host byte order. . Two helper functions 'get_port' and 'set_port' are implemented in the library 'libinetutils.a'. These facilitate consistent and transparent manipulation of ports, independently of address family. The functions will be useful for IPv6 migration of other server and client software. Author: Mats Erik Andersson X-Signed-off-by: Mats Erik Andersson X-Depends-on-patch: gnu_inetutils_tftpd_ipv6.diff Last-Update: 2010-07-30 diff --git a/libinetutils/Makefile.am b/libinetutils/Makefile.am index 07e0566..2f02f0b 100644 --- a/libinetutils/Makefile.am +++ b/libinetutils/Makefile.am @@ -19,7 +19,8 @@ noinst_LIBRARIES = libinetutils.a -noinst_HEADERS = argcv.h libinetutils.h tftpsubs.h shishi_def.h +noinst_HEADERS = argcv.h libinetutils.h tftpsubs.h shishi_def.h \ + inet6_helper.h EXTRA_DIST = logwtmp.c @@ -29,6 +30,7 @@ libinetutils_a_SOURCES = \ daemon.c\ defauthors.c\ des_rw.c\ + inet6_helper.c \ kcmd.c\ krcmd.c\ localhost.c\ diff --git a/libinetutils/inet6_helper.c b/libinetutils/inet6_helper.c new file mode 100644 index 0000000..88cfb3a --- /dev/null +++ b/libinetutils/inet6_helper.c @@ -0,0 +1,73 @@ +/* + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GNU Inetutils. + + GNU Inetutils is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + GNU Inetutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see `http://www.gnu.org/licenses/'. +*/ + +/* A collection of helpers intended to handle IPv6 and IPv4 simultaneously + in a transparent manner. + + Mats Erik Andersson, July 2010 +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +/* + * get_port + * + * Return: port number in host byte order. + * Input: pointer to 'struct sockaddr'. + */ +in_port_t +get_port (struct sockaddr *sa) +{ + switch (sa->sa_family) + { + case AF_INET: + return ntohs (((struct sockaddr_in *) sa)->sin_port); + case AF_INET6: + return ntohs (((struct sockaddr_in6 *) sa)->sin6_port); + default: + return 0; + } +} + +/* + * set_port + * + * Input: pointer to 'struct sockaddr', and + * port number in host byte order. + */ +void +set_port (struct sockaddr *sa, in_port_t port) +{ + switch (sa->sa_family) + { + case AF_INET: + (((struct sockaddr_in *) sa)->sin_port) = htons (port); + break; + case AF_INET6: + (((struct sockaddr_in6 *) sa)->sin6_port) = htons (port); + default: + break; + } +} diff --git a/libinetutils/inet6_helper.h b/libinetutils/inet6_helper.h new file mode 100644 index 0000000..03f8d29 --- /dev/null +++ b/libinetutils/inet6_helper.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GNU Inetutils. + + GNU Inetutils is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + GNU Inetutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see `http://www.gnu.org/licenses/'. +*/ + +/* A collection of helpers intended to handle IPv6 and IPv4 simultaneously + in a transparent manner. + + Mats Erik Andersson, July 2010 +*/ + +/* + * Prototypes for address family independecy. + */ + +#include +#include + +in_port_t get_port (struct sockaddr *sa); + +void set_port (struct sockaddr *sa, in_port_t port); diff --git a/src/tftp.c b/src/tftp.c index f3b3760..8a8c587 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -80,8 +80,9 @@ #include "xalloc.h" #include "progname.h" #include "tftpsubs.h" +#include "inet6_helper.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,60 @@ 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; +# if defined(__linux__) || defined(__FreeBSD__) + hints.ai_flags += AI_ADDRCONFIG; +# endif + + if ( (rc = getaddrinfo (name, "tftp", &hints, &aiptr)) ) { 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) + { + if ( (f = socket (ai->ai_family, ai->ai_socktype, + ai->ai_protocol)) < 0 ) + continue; + + memset(&ss, '\0', sizeof (ss)); + ss.ss_family = ai->ai_family; + if ( bind (f, (struct sockaddr *) &ss, sizeof (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 +353,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 +361,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 +372,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 +385,6 @@ setpeer (int argc, char *argv[]) connected = 0; return; } - port = htons (port); } connected = 1; } @@ -488,7 +512,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 +533,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 +599,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 +614,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); } } @@ -901,7 +929,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 */ @@ -1015,7 +1044,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 */