Hello there,

I would like to ask for permission to push a change to

    srv/logger.c
    doc/inetutils.texi

that implements support for IPv6 in the logging client.

The changes have been tested on GNU/Linux, GNU/kFreeBSD,
OpenBSD, and FreeBSD.

Best regards,
  Mats E A
From 704d0d62b4ab901e349e01a3638a3d845c1192d0 Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <[email protected]>
Date: Tue, 18 Jan 2011 12:05:41 +0100
Subject: [PATCH] src/logger: Implement support for IPv6.

---
 ChangeLog          |   13 +++++
 doc/inetutils.texi |   49 ++++++++++++-----
 src/logger.c       |  151 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 194 insertions(+), 19 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c474101..b2df64d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2011-01-18  Mats Erik Andersson <[email protected]>
+
+	* src/logger.c (host_family): New variable.
+	(struct logger_sockaddr): New component SINET6.
+	(open_socket): Implement new code based on getaddrinfo.
+	These are conditioned on HAVE_DECL_GETADDRINFO and HAVE_IPV6.
+	Reorganize old handler of port specification.
+	(argp_options): New options `--ipv4' and `--ipv6` conditioned
+	on HAVE_IPV6.
+	(parse_opt): Implement parsing of `--ipv4' and `--ipv6'.
+	* doc/inetutils.texi (logger): Describe implications of
+	IPv6-support.  Small corrections to example usage.
+
 2010-12-21  Mats Erik Andersson <[email protected]>
 
 	* src/inetd.c (pidfile_option): New variable.
diff --git a/doc/inetutils.texi b/doc/inetutils.texi
index 7249bbf..740dd8b 100644
--- a/doc/inetutils.texi
+++ b/doc/inetutils.texi
@@ -287,6 +287,24 @@ logger [@var{option}@dots{}] [@var{message}]
 @end example
 
 @table @option
+@item -4
+@itemx --ipv4
+@opindex -4
+@opindex --ipv4
+Use IPv4 as transport when logging to a host.  The default behaviour
+is to use whatever IP version that matches the host.
+
+@item -6
+@itemx --ipv6
+@opindex -6
+@opindex --ipv6
+Use IPv6 as transport when loggin to a host.
+
+The above two options are present on systems supporting IPv6.
+Both options are influencial only when the target host is
+named using a symbolic name, but numerical addresses must also
+match if either of @option{--ipv4} or @option{--ipv6} is stated.
+
 @item -i[@var{pid}]
 @itemx --id=[@var{pid}]
 @opindex -i
@@ -303,26 +321,29 @@ must be separated from it by exactly one equals sign.
 @opindex -h
 @opindex --host
 Send messages to the given host or socket.  The @var{host} argument
-can be either a local UNIX socket name (always starting with a
-@samp{/} or:
+can be either a local UNIX socket name (starting with a dash @samp{/}),
+or:
 
 @smallexample
 @var{host}[:@var{port}]
 @end smallexample
 
 @noindent
-where @var{host} is the remote host name or IP address (only IPv4 is
-supported so far), and optional @var{port} is a decimal port number or
-symbolic service name from @file{/etc/services}.  If @var{port} is not
-specified, the port number corresponding to the @samp{syslog} service
-is used.
+where @var{host} is the remote host name or IP address, and the
+optional @var{port} is a decimal port number or symbolic service
+name from @file{/etc/services}.  If @var{port} is not specified,
+the port number corresponding to the @samp{syslog} service is used.
+If a numerical IPv6 address is given without a port specification,
+then the address must be enclosed within brackets (like [::1]).
 
 @item -S @var{addr}
 @itemx --source=@var{addr}
 @opindex -S
 @opindex --source
 Supply the source IP address for INET connections.  This option is
-useful in conjunction with @option{--host} (see above).
+useful in conjunction with @option{--host} (see above).  The kind of
+address specified here (IPv4 or IPv6) will propagate to influence
+the resolution of the host address, if it is a symbolic name.
 
 @item -s
 @itemx --stderr
@@ -366,23 +387,23 @@ The following examples illustrate the usage of the @command{logger}
 command:
 
 @enumerate 1
-@item Log the @samp{System rebooted} message to the local syslog. Use
-the default facility and priority:
+@item Log the message @samp{System rebooted} to the local syslog.
+Use default facility and priority:
 
 @example
 logger System rebooted
 @end example
 
 @item Run command and send its error output to the channel
-@samp{local0.err} channel.  Mark each message with tag @samp{cmd}:
+@samp{local0.err}.  Mark each message with tag @samp{cmd}:
 
 @example
 command 2>&1 | logger -p local0.notice -t cmd
 @end example
 
-@item Log each line from file @file{warnings} to @samp{daemon.warn}
-channel on host @samp{logger.runasimi.org}, using the source IP
-@samp{10.10.10.1}:
+@item Log each line from file @file{warnings} to channel
+@samp{daemon.warn} on host @samp{logger.runasimi.org},
+using the source IP @samp{10.10.10.1}:
 
 @example
 logger -p daemon.warn -h logger.runasimi.org -S 10.10.10.1 --file
diff --git a/src/logger.c b/src/logger.c
index f112334..505af04 100644
--- a/src/logger.c
+++ b/src/logger.c
@@ -56,6 +56,10 @@ static char *host = PATH_LOG;
 static char *source;
 static char *pidstr;
 
+#if HAVE_DECL_GETADDRINFO
+static int host_family = AF_UNSPEC;
+#endif
+
 
 
 int
@@ -104,6 +108,9 @@ union logger_sockaddr
   {
     struct sockaddr sa;
     struct sockaddr_in sinet;
+#if HAVE_IPV6
+    struct sockaddr_in6 sinet6;
+#endif
     struct sockaddr_un sunix;
 };
 
@@ -115,6 +122,9 @@ open_socket (void)
   union logger_sockaddr sockaddr;
   socklen_t socklen;
   int family;
+#if HAVE_DECL_GETADDRINFO
+  int ret;
+#endif
 
   if (host[0] == '/')
     {
@@ -128,14 +138,131 @@ open_socket (void)
     }
   else
     {
+#if HAVE_DECL_GETADDRINFO
+      struct addrinfo hints, *ai, *res;
+#else
       struct hostent *hp;
       struct servent *sp;
       unsigned short port;
+#endif /* !HAVE_DECL_GETADDRINFO */
       char *p;
 
+#if HAVE_IPV6
+      /* Bare, numeric IPv6 addresses must be contained
+       * in brackets in order that an appended port not
+       * be read by mistake.  */
+      if (*host == '[')
+	{
+	  ++host;
+	  p = strchr (host, ']');
+	  if (p)
+	    {
+	      *p++ = '\0';
+	      if (*p == ':')
+		++p;
+	      else
+		p = NULL;
+	    }
+	}
+      else
+        {
+	  /* When no bracket was detected, then seek the
+	   * right-most colon character in order to correctly
+	   * parse IPv6 addresses.  */
+	  p = strrchr (host, ':');
+	  if (p)
+	    *p++ = 0;
+	}
+#else /* !HAVE_IPV6 */
       p = strchr (host, ':');
       if (p)
 	*p++ = 0;
+#endif /* !HAVE_IPV6 */
+
+      if (!p)
+	p = "syslog";
+
+#if HAVE_DECL_GETADDRINFO
+      memset (&hints, 0, sizeof (hints));
+      hints.ai_socktype = SOCK_DGRAM;
+
+# if HAVE_IPV6
+      hints.ai_family = host_family;
+# else /* !HAVE_IPV6 */
+      hints.ai_family = AF_INET;
+# endif /* !HAVE_IPV6 */
+
+# ifdef AI_ADDRCONFIG
+      hints.ai_flags = AI_ADDRCONFIG;
+# endif
+
+      /* The complete handshake is attempted within
+       * a single while-loop, since the answers from
+       * getaddrinfo() need to be checked in detail.  */
+      ret = getaddrinfo (host, p, &hints, &res);
+      if (ret < 0)
+	error (EXIT_FAILURE, 0, "cannot resolve host");
+
+
+      for (ai = res; ai; ai = ai->ai_next)
+        {
+	  fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+	  if (fd < 0)
+	    continue;
+
+	  if (source)
+	    {
+	      /* Make the assumption that the source address
+	       * encodes a desired domain family and that it
+	       * be numeric.  */
+	      int ret;
+	      struct addrinfo tips, *a;
+
+	      memset (&tips, 0, sizeof (tips));
+	      tips.ai_family = ai->ai_family;
+	      tips.ai_flags = AI_NUMERICHOST;
+
+	      ret = getaddrinfo(source, NULL, &tips, &a);
+	      if (ret)
+		{
+		  close (fd);
+		  continue;
+		}
+
+	      if (bind (fd, a->ai_addr, a->ai_addrlen))
+		{
+		  freeaddrinfo (a);
+		  close (fd);
+		  continue;
+		}
+
+	      freeaddrinfo (a);
+	    }
+
+	  if (connect (fd, ai->ai_addr, ai->ai_addrlen))
+	    {
+	      close (fd);
+	      continue;
+	    }
+
+	  /* Socket standing, bound and connected.  */
+	  break;
+	}
+
+      freeaddrinfo (res);
+
+      if (ai == NULL)
+	error (EXIT_FAILURE, 0, "cannot connect to host");
+
+      /* Existing socket can be returned now.
+       * This handles AF_INET and AF_INET6 in case
+       * HAVE_DECL_GETADDRINFO is true.  */
+      return;
+
+#else /* !HAVE_DECL_GETADDRINFO */
+
+      sockaddr.sinet.sin_family = AF_INET;
+      family = PF_INET;
 
       hp = gethostbyname (host);
       if (hp)
@@ -144,11 +271,6 @@ open_socket (void)
 	       != 1)
 	error (EXIT_FAILURE, 0, "unknown host name");
 
-      sockaddr.sinet.sin_family = AF_INET;
-      family = PF_INET;
-      if (!p)
-	p = "syslog";
-
       if (isdigit (*p))
 	{
 	  char *end;
@@ -163,9 +285,14 @@ open_socket (void)
 	error (EXIT_FAILURE, 0, "%s: unknown service name", p);
 
       sockaddr.sinet.sin_port = port;
+#endif /* !HAVE_DECL_GETADDRINFO */
+
       socklen = sizeof (sockaddr.sinet);
     }
 
+  /* Execution arrives here for AF_UNIX and for
+   * situations with !HAVE_DECL_GETADDRINFO.  */
+
   fd = socket (family, SOCK_DGRAM, 0);
   if (fd < 0)
     error (EXIT_FAILURE, errno, "cannot create socket");
@@ -244,6 +371,10 @@ const char args_doc[] = "[MESSAGE]";
 const char doc[] = "Send messages to syslog";
 
 static struct argp_option argp_options[] = {
+#if HAVE_IPV6
+  {"ipv4", '4', NULL, 0, "use IPv4 for logging to host" },
+  {"ipv6", '6', NULL, 0, "use IPv6 with a host target" },
+#endif
   { "host", 'h', "HOST", 0,
     "log to host instead of the default " PATH_LOG },
   { "source", 'S', "IP", 0,
@@ -264,6 +395,16 @@ parse_opt (int key, char *arg, struct argp_state *state)
 {
   switch (key)
     {
+#if HAVE_IPV6
+    case '4':
+      host_family = AF_INET;
+      break;
+
+    case '6':
+      host_family = AF_INET6;
+      break;
+#endif /* HAVE_IPV6 */
+
     case 'h':
       host = arg;
       break;
-- 
1.7.2.3

Attachment: signature.asc
Description: Digital signature

Reply via email to