Synopsis: traceroute can do packet floods.
NetBSD versions: 1.3.3 and before, NetBSD-current until 19990217
Thanks to: Curt Sampson
Reported in NetBSD Security Advisory: SA1999-004

This patch fixes the traceroute flooding problem described in the
NetBSD-SA1999-004 security advisory. For it to apply, make sure
you have NetBSD 1.3.3 sources unpacked in /usr/src, then do:

   % cd /usr/src/usr.sbin/traceroute
   % patch <19990217-traceroute
   % make
   % su root
   # make install



--- traceroute.c.orig-1.3.3     Wed Feb 17 02:29:50 1999
+++ traceroute.c-SA004  Wed Feb 17 02:49:28 1999
@@ -1,4 +1,4 @@
-/*     $NetBSD: traceroute.c,v 1.19.2.2 1997/11/04 22:34:23 mellon Exp $       */
+/*     $NetBSD: traceroute.c,v 1.27 1999/02/16 20:47:24 cjs Exp $      */

/*
 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
@@ -29,7 +29,7 @@
#else
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997\n\
The Regents of the University of California.  All rights reserved.\n");
-__RCSID("$NetBSD: traceroute.c,v 1.19.2.2 1997/11/04 22:34:23 mellon Exp $");
+__RCSID("$NetBSD: traceroute.c,v 1.27 1999/02/16 20:47:24 cjs Exp $");
#endif
#endif

@@ -225,6 +225,7 @@

#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
@@ -334,6 +335,7 @@
void   tvsub(struct timeval *, struct timeval *);
__dead void usage(void);
int    wait_for_reply(int, struct sockaddr_in *, struct timeval *);
+int    find_local_ip(struct sockaddr_in *, struct sockaddr_in *);

int
main(int argc, char **argv)
@@ -694,7 +696,7 @@
                * Otherwise, use the first interface found.
                * Warn if there are more than one.
                */
-               setsin(from, al->addr);
+               setsin(from, al->addr && !find_local_ip(from, to));
               if (n > 1 && device == NULL) {
                       Fprintf(stderr,
                   "%s: Warning: Multiple interfaces found; using %s @ %s\n",
@@ -871,6 +873,7 @@
       struct timezone tz;
       register int cc = 0;
       int fromlen = sizeof(*fromp);
+       int retval;

       FD_ZERO(&fds);
       FD_SET(sock, &fds);
@@ -880,9 +883,16 @@
       (void)gettimeofday(&now, &tz);
       tvsub(&wait, &now);

-       if (select(sock + 1, &fds, NULL, NULL, &wait) > 0)
-               cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
-                           (struct sockaddr *)fromp, &fromlen);
+       retval = select(sock + 1, &fds, NULL, NULL, &wait);
+       if (retval < 0)  {
+               /* If we continue, we probably just flood the remote host. */
+               Fprintf(stderr, "%s: select: %s\n", prog, strerror(errno));
+               exit(1);
+       }
+       if (retval > 0)  {
+              cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
+                            (struct sockaddr *)fromp, &fromlen);
+       }

       return(cc);
}
@@ -1363,4 +1373,39 @@
[-w waittime]\n\thost [packetlen]\n",
           prog);
       exit(1);
+}
+
+int
+find_local_ip(struct sockaddr_in *from, struct sockaddr_in *to)
+{
+       int sock;
+       struct sockaddr_in help;
+       int help_len;
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0) return (0);
+
+       help.sin_family = AF_INET;
+       /*
+        * At this point the port number doesn't matter
+        * since it only has to be greater than zero.
+        */
+       help.sin_port = 42;
+       help.sin_addr.s_addr = to->sin_addr.s_addr;
+       if (connect(sock, (struct sockaddr *)&help, sizeof(help)) < 0) {
+               (void)close(sock);
+               return (0);
+       }
+
+       help_len = sizeof(help);
+       if (getsockname(sock, (struct sockaddr *)&help, &help_len) < 0 ||
+           help_len != sizeof(help) ||
+           help.sin_addr.s_addr == INADDR_ANY) {
+               (void)close(sock);
+               return (0);
+       }
+
+       (void)close(sock);
+       setsin(from, help.sin_addr.s_addr);
+       return (1);
}