Let me suggest a first straightforward change to `traceroute'
that will be useful also for an upcoming IPv6 migration, but
is already now important to protect against "RES_OPTIONS=inet6".

To understand this matter, you could try this invokation

    sudo    RES_OPTIONS=inet6  ./src/traceroute www.google.com

on the present executable.

The failure is due eto the use of gethostbyname(3), which gets
severely confused by AF_INET6 answers.

The submitted patch resolves this calamity by a standard migration
to getaddrinfo(3) and getnameinfo(3). There is no step undertaken
towards IPv6 yet, only a guard against unusual but legitimate system
setups.

Tested on GNU/Linux, OpenBSD, and FreeBSD. The service `traceroute'
is now immune against the above call.


Best regards,
Mats
From 25b295cdb88d15cd8c067a354162d361cfde3942 Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <[email protected]>
Date: Wed, 3 Nov 2010 14:55:46 +0100
Subject: [PATCH] src/traceroute: Protect against resolver option `inet6'.

---
 ChangeLog        |    8 ++++++++
 src/traceroute.c |   28 +++++++++++++++++++++-------
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 56ae073..51685ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2010-11-03  Mats Erik Andersson <[email protected]>
 
+	* src/traceroute.c: Delete HOST. New strings HOSTNAME, ADDRSTR.
+	(parse_opt) [ARGP_KEY_ARG]: Abolish gethostbyname. Only use
+	xstrdup to copy argument.
+	(main): Use getaddrinfo and getnameinfo for name resolving.
+	New variables HINTS and *RES of type `struct addrinfo'.
+
+2010-11-03  Mats Erik Andersson <[email protected]>
+
 	* tests/tftp.sh: Portability to FreeBSD and GNU/kFreeBSD.
 
 2010-11-03  Mats Erik Andersson <[email protected]>
diff --git a/src/traceroute.c b/src/traceroute.c
index f79e747..8226c71 100644
--- a/src/traceroute.c
+++ b/src/traceroute.c
@@ -47,6 +47,7 @@
 #include <argp.h>
 #include <icmp.h>
 
+#include "xalloc.h"
 #include "libinetutils.h"
 
 #define TRACE_UDP_PORT 33434
@@ -87,7 +88,8 @@ char *get_hostname (struct in_addr *addr);
 
 int stop = 0;
 int pid = 0;
-struct hostent *host;
+static char *hostname = NULL;
+char addrstr[INET6_ADDRSTRLEN];
 struct sockaddr_in dest;
 
 static enum trace_type opt_type = TRACE_ICMP;
@@ -158,9 +160,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case ARGP_KEY_ARG:
       host_is_given = true;
-      host = gethostbyname (arg);
-      if (host == NULL)
-        error (EXIT_FAILURE, 0, "unknown host");
+      hostname = xstrdup(arg);
       break;
 
     case ARGP_KEY_SUCCESS:
@@ -180,6 +180,7 @@ static struct argp argp = {argp_options, parse_opt, args_doc, doc};
 int
 main (int argc, char **argv)
 {
+  struct addrinfo hints, *res;
   trace_t trace;
 
   set_program_name (argv[0]);
@@ -188,15 +189,28 @@ main (int argc, char **argv)
   iu_argp_init ("traceroute", program_authors);
   argp_parse (&argp, argc, argv, 0, NULL, NULL);
 
+  /* Hostname lookup first for better information */
+  memset (&hints, 0, sizeof (hints));
+  hints.ai_family = AF_INET;
+  hints.ai_flags = AI_CANONNAME;
+
+  if ((hostname == NULL) || (*hostname == '\0')
+      || getaddrinfo (hostname, NULL, &hints, &res))
+    error (EXIT_FAILURE, 0, "unknown host");
+
   if (geteuid () != 0)
     error (EXIT_FAILURE, EPERM, "insufficient permissions");
 
-  dest.sin_addr = *(struct in_addr *) host->h_addr;
-  dest.sin_family = AF_INET;
+  memcpy (&dest, res->ai_addr, res->ai_addrlen);
   dest.sin_port = htons (opt_port);
 
+  getnameinfo (res->ai_addr, res->ai_addrlen, addrstr, sizeof (addrstr),
+                  NULL, 0, NI_NUMERICHOST);
+
   printf ("traceroute to %s (%s), %d hops max\n",
-	  host->h_name, inet_ntoa (dest.sin_addr), opt_max_hops);
+	  res->ai_canonname, addrstr, opt_max_hops);
+
+  freeaddrinfo (res);
 
   trace_init (&trace, dest, opt_type);
 
-- 
1.7.1

Attachment: signature.asc
Description: Digital signature

Reply via email to