Dear friends,

I have reworked my patching needed to have 'src/tftp.c' and
'src/tftpd.c' enjoy correct IPv6-capabilities.

The code has been cleanly tested against git-HEAD for
Debian testing/squeeze.

After some workarounds, the intended IPv6-functionality is also
present for OpenBSD. The quirks are due to 'src/logger.c'
and 'libinetutils/utmp_init.c and 'libinetutils/utmp_logout.c',
but do not touch tftp, nor tftpd.


The patch set consists of three parts:

   tftp_related_patch_1.diff: Adds functionality to 'libinetutils'.
                              Both later patches depend on this first one.

   tftp_related_patch_2.diff: Implements changes to 'src/tftpd.c'.

   tftp_related_patch_3.diff: Likewise for 'src/tftp.c'.

The latter two are independent of each other (apart from touching ChangeLog).


Regards,

Mats E A
diff --git a/ChangeLog b/ChangeLog
index d52508b..af7429b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2010-08-19  Mats Erik Andersson <[email protected]>
+
+	* libinetutils/sockaddr_aux.h: New file.
+	* libinetutils/sockaddr_aux.c: New file.
+	* libinetutils/Makefile.am: Add sockaddr_aux.h and sockaddr_aux.c.
+	* libinetutils/tftpsubs.c (synchnet): Replace 'struct sockaddr_in' by
+	'struct sockaddr_storage'.
+
 2010-08-18  Giuseppe Scrivano  <[email protected]>
 
 	Under GNU/Linux print interfaces without an address with ifconfig -a.
diff --git a/libinetutils/Makefile.am b/libinetutils/Makefile.am
index 07e0566..a15b4e4 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 \
+		 sockaddr_aux.h
 
 EXTRA_DIST = logwtmp.c
 
@@ -29,6 +30,7 @@ libinetutils_a_SOURCES = \
  daemon.c\
  defauthors.c\
  des_rw.c\
+ sockaddr_aux.c \
  kcmd.c\
  krcmd.c\
  localhost.c\
diff --git a/libinetutils/sockaddr_aux.c b/libinetutils/sockaddr_aux.c
new file mode 100644
index 0000000..f882f6e
--- /dev/null
+++ b/libinetutils/sockaddr_aux.c
@@ -0,0 +1,98 @@
+/*
+  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. An underlying use of 'struct sockaddr_storage'
+   is conceiled as 'struct sockaddr' in the call from an application.
+
+   The helper function resolves and manipulates this as 'struct sockaddr_in'
+   or as 'struct sockaddr_in6', depending on contents.
+
+   Mats Erik Andersson, August 2010
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "sockaddr_aux.h"
+
+/*
+ * 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;
+    }
+}
+
+/*
+ * get_socklen
+ *
+ * Input:  pointer to 'struct sockaddr'.
+ * Return: exact address structure size for AF_INET and AF_INET6.
+ */
+socklen_t
+get_socklen (struct sockaddr *sa)
+{
+  switch (sa->sa_family)
+    {
+      case AF_INET:
+	return sizeof (struct sockaddr_in);
+	break;
+      case AF_INET6:
+	return sizeof (struct sockaddr_in6);
+	break;
+      default:
+	return sizeof (*sa);
+	break;
+    }
+}
diff --git a/libinetutils/sockaddr_aux.h b/libinetutils/sockaddr_aux.h
new file mode 100644
index 0000000..7e2545d
--- /dev/null
+++ b/libinetutils/sockaddr_aux.h
@@ -0,0 +1,43 @@
+/*
+  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. An underlying use of 'struct sockaddr_storage'
+   is conceiled as 'struct sockaddr' in the call from an application.
+
+   The helper function resolves and manipulates this as 'struct sockaddr_in'
+   or as 'struct sockaddr_in6', depending on contents.
+
+   Mats Erik Andersson, August 2010
+*/
+
+/*
+ * Prototypes for address family independecy.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+in_port_t get_port (struct sockaddr *sa);
+
+void set_port (struct sockaddr *sa, in_port_t port);
+
+socklen_t get_socklen (struct sockaddr *sa);
diff --git a/libinetutils/tftpsubs.c b/libinetutils/tftpsubs.c
index 6eb0a09..371764b 100644
--- a/libinetutils/tftpsubs.c
+++ b/libinetutils/tftpsubs.c
@@ -287,7 +287,7 @@ synchnet (int f)
 {
   int i, j = 0;
   char rbuf[PKTSIZE];
-  struct sockaddr_in from;
+  struct sockaddr_storage from;
   socklen_t fromlen;
 
   while (1)
diff --git a/ChangeLog b/ChangeLog
index af7429b..cbd3db2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2010-08-19  Mats Erik Andersson <[email protected]>
 
+	* src/tftpd.c: Implement full support for IPv6.
+
+2010-08-19  Mats Erik Andersson <[email protected]>
+
 	* libinetutils/sockaddr_aux.h: New file.
 	* libinetutils/sockaddr_aux.c: New file.
 	* libinetutils/Makefile.am: Add sockaddr_aux.h and sockaddr_aux.c.
diff --git a/src/tftpd.c b/src/tftpd.c
index f343f8a..892c709 100644
--- a/src/tftpd.c
+++ b/src/tftpd.c
@@ -83,6 +83,7 @@
 #include <unistd.h>
 
 #include "tftpsubs.h"
+#include "sockaddr_aux.h"
 
 #include <argp.h>
 #include <progname.h>
@@ -103,8 +104,9 @@ static int maxtimeout = 5 * TIMEOUT;
 #define PKTSIZE	SEGSIZE+4
 static char buf[PKTSIZE];
 static char ackbuf[PKTSIZE];
-static struct sockaddr_in from;
+static struct sockaddr_storage from;
 static socklen_t fromlen;
+static char host[INET6_ADDRSTRLEN];
 
 void tftp (struct tftphdr *, int);
 
@@ -126,7 +128,7 @@ static int logging;
 
 static const char *errtomsg (int);
 static void nak (int);
-static const char *verifyhost (struct sockaddr_in *);
+static const char *verifyhost (struct sockaddr_storage *);
 
 
 
@@ -174,7 +176,7 @@ main (int argc, char *argv[])
   int index;
   register struct tftphdr *tp;
   int on, n;
-  struct sockaddr_in sin;
+  struct sockaddr_storage sin;
 
   set_program_name (argv[0]);
   iu_argp_init ("tftpd", default_program_authors);
@@ -270,24 +272,28 @@ main (int argc, char *argv[])
 	exit (0);
       }
   }
-  from.sin_family = AF_INET;
+  /* The peer's address 'from' is valid at this point,
+   * and 'from.ss_family' contains the correct address
+   * family for any callback connection. */
   alarm (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);
     }
   memset (&sin, 0, sizeof (sin));
-  sin.sin_family = AF_INET;
-  if (bind (peer, (struct sockaddr *) &sin, sizeof (sin)) < 0)
+  sin.ss_family = from.ss_family;
+  if (bind (peer, (struct sockaddr *) &sin,
+	get_socklen ((struct sockaddr *) &sin)) < 0)
     {
       syslog (LOG_ERR, "bind: %m\n");
       exit (1);
     }
-  if (connect (peer, (struct sockaddr *) &from, sizeof (from)) < 0)
+  if (connect (peer, (struct sockaddr *) &from,
+	get_socklen ((struct sockaddr *) &from)) < 0)
     {
       syslog (LOG_ERR, "connect: %m\n");
       exit (1);
@@ -739,16 +745,20 @@ nak (int error)
 }
 
 static const char *
-verifyhost (struct sockaddr_in *fromp)
+verifyhost (struct sockaddr_storage *fromp)
 {
-  struct hostent *hp;
+  int rc;
 
-  hp = gethostbyaddr ((char *) &fromp->sin_addr, sizeof (fromp->sin_addr),
-		      fromp->sin_family);
-  if (hp)
-    return hp->h_name;
+  rc = getnameinfo ((struct sockaddr *) fromp,
+		    get_socklen ((struct sockaddr *) fromp),
+		    host, sizeof (host), NULL, 0, NI_NUMERICHOST);
+  if (rc == 0)
+    return host;
   else
-    return inet_ntoa (fromp->sin_addr);
+    {
+      syslog (LOG_ERR, "getnameinfo: %s\n", gai_strerror(rc));
+      return "0.0.0.0";
+    }
 }
 
 static const char usage_str[] =
diff --git a/ChangeLog b/ChangeLog
index cbd3db2..b27d5e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2010-08-19  Mats Erik Andersson <[email protected]>
 
+	* src/tftp.c: Implement full support for IPv6.
+
+2010-08-19  Mats Erik Andersson <[email protected]>
+
 	* src/tftpd.c: Implement full support for IPv6.
 
 2010-08-19  Mats Erik Andersson <[email protected]>
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");
 }
 

Attachment: signature.asc
Description: Digital signature

Reply via email to