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);
 }