Package: netdiag
Version: 1.0-13
Severity: normal

I have implemented support for IPv6 in Tcpspray.
In doing so I had to observe that the code was
using an invalidated buffer when called with "-e".
This caused the occasional segmentation fault
in FreeBSD and a consistent EFAULT/"bad address"
with OpenBSD. Clearly GNU/Linux seems insensitive
to this defect, but both BSDs clearly locate the
problem.

The suggested patch is a direct replacement of the
present paching to "tcpspray/". The code produces
no warnings whatsoever with "-Wall" for GNU/Linux,
GNU/kFreeBSD, OpenBSD, and FreeBSD. The activation
of IPv6 support is done by passing "-DUSE_ADDRINFO=1"
as compiler flag.

Best regards,
  Mats Erik Andersson, DM
diff -Naur tcpspray.orig/Makefile tcpspray/Makefile
--- tcpspray.orig/Makefile	1993-06-08 01:30:55.000000000 +0200
+++ tcpspray/Makefile	2011-02-12 10:41:49.000000000 +0100
@@ -1,4 +1,4 @@
-CFLAGS= -O2 -s -Dsun
+CFLAGS= -O2 -Dsun
 BINDIR= /usr/local/etc
 INSTALL= install -c
 CC= gcc
diff -Naur tcpspray.orig/tcpspray.1 tcpspray/tcpspray.1
--- tcpspray.orig/tcpspray.1	1992-01-15 19:49:50.000000000 +0100
+++ tcpspray/tcpspray.1	2011-02-12 10:41:49.000000000 +0100
@@ -67,7 +67,7 @@
 .BI \-d " delay"
 Sets the time in microseconds to wait between successive buffer
 transmissions.  The default is no delay.
-.TP
+
 \".SH EXAMPLES
 \".SH FILES
 .SH SEE ALSO
diff -Naur tcpspray.orig/tcpspray.c tcpspray/tcpspray.c
--- tcpspray.orig/tcpspray.c	1992-01-17 01:42:27.000000000 +0100
+++ tcpspray/tcpspray.c	2011-02-12 15:13:30.000000000 +0100
@@ -14,18 +14,30 @@
 
 #include <netdb.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
-#include <malloc.h>
+#if USE_DECL_MALLOC
+#include <malloc.h>	/* is obsolete; <stdlib.h> suffices */ 
+#endif
 
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
+#include <sys/wait.h>
+#include <signal.h>
 
 #include <netinet/in.h>
+#include <arpa/inet.h>	/* inet_addr */
 
 #define DEFBLKSIZE 1024		/* default  blocksize is 1k */
 #define DEFNBLKS 100		/* default number of blocks is 100 */
 
+#ifndef USE_ADDRINFO
+# define USE_ADDRINFO 0
+#endif
+
 extern char *optarg;		/* external vars used by getopt(3) */
 extern int optind, opterr;
 
@@ -44,8 +56,9 @@
 static void usage  __ARGS((char *argv[]));		/* forward declaration */
 
 
-#if !defined(sun) && !defined(_AIX)	/* sunos and aix define pid_t, */
-					/* do others? */ 
+#if !defined(sun) && !defined(_AIX) && !defined(__GLIBC__) \
+    && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+	/* sunos and aix define pid_t, do others? */ 
 typedef int pid_t;
 #endif
 
@@ -72,7 +85,8 @@
 
   struct timeval start, end;	/* used to store start and end time of I/O */
 
-#ifndef sun
+#if !defined(sun) && !defined(__GLIBC__) \
+    && !defined(__OpenBSD__) && !defined(__FreeBSD__)
   struct timeval timeout;	/* used for timeout in select call */
 #endif /*sun*/
 
@@ -83,9 +97,15 @@
 
   int c;		/* used to return options from getopt(3) */
 
+#if !USE_ADDRINFO
   struct sockaddr_in sin;	/* sockaddr for socket */
   struct hostent *hp;		/* hostent for host name lookups */
   struct servent *serv;		/* service entry for port lookup */
+#else /* USE_ADDRINFO */
+  struct addrinfo hints, *ai, *res;	/* resolving with getaddrinfo(3) */
+
+  int err;			/* return value from getaddrinfo(3) */
+#endif /* USE_ADDRINFO */
 
   int sock;			/* socket descriptor */
   int fd[2];			/* descriptors for pipe */
@@ -98,7 +118,7 @@
   register char *buf;		/* input and output buffer (malloced) */
   register char *bufp;		/* placeholder pointer */
 
-  FILE *infile;			/* used to preload buffer */
+  FILE *infile = NULL;		/* used to preload buffer */
 
   while ((c = getopt(argc, argv, "vehb:n:f:d:")) != -1) {
     switch (c) {
@@ -132,7 +152,8 @@
 
     case 'd':
       delay = atoi(optarg);
-#ifndef sun
+#if !defined(sun) && !defined(__GLIBC__) \
+    && !defined(__OpenBSD__) && !defined(__FreeBSD__)
 /* we need to fake usleep() with select() on non-sun machines */
 
       timeout.tv_usec = atoi(optarg) ;
@@ -150,6 +171,7 @@
   if ((argc - optind) != 1)	/* we better have a host name */
     usage(argv);
     
+#if !USE_ADDRINFO
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)  {
     perror("socket");
     exit(1);
@@ -157,8 +179,11 @@
 
   bzero((char *) &sin, sizeof(sin));
   sin.sin_family = AF_INET;
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
+  sin.sin_len = sizeof(sin);
+#endif
 
-  if (bind(sock, &sin, sizeof (sin)) == -1) {
+  if (bind(sock, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
     perror("bind");
     exit(1);
   }
@@ -184,10 +209,48 @@
   serv = getservbyname(eflag ? "echo" : "discard", "tcp"); /* get port */
   sin.sin_port = serv->s_port;
 
-  if (connect(sock, &sin, sizeof(sin)) == -1) {
+  if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
     perror("connect");
     exit(1);
   }
+#else /* USE_ADDRINFO */
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;	/* FreeBSD offers also "echo/ddp" */
+# ifdef AI_ADDRCONFIG
+  hints.ai_flags = AI_ADDRCONFIG;
+# endif
+
+  err = getaddrinfo(argv[optind], eflag ? "echo" : "discard", &hints, &res);
+  if (err) {
+    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
+    exit(1);
+  }
+
+  for (ai = res; ai; ai = ai->ai_next) {
+    sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (sock < 0)
+      continue;
+
+    if (connect(sock, ai->ai_addr, ai->ai_addrlen)) {
+      close(sock);
+      sock = -1;
+      continue;
+    }
+
+    /* Success */
+    break;
+  }
+
+  if (res)
+    freeaddrinfo(res);
+
+  if (ai == NULL) {
+    fprintf(stderr, "host not responding: %s\n", argv[optind]);
+    exit(1);
+  }
+#endif /* USE_ADDRINFO */
 
   nbytes = nblks * blksize;	/* number of bytes to send/receive */ 
 
@@ -220,10 +283,18 @@
       exit(1);
     }
   }
-  if (eflag && pid != 0) {			/* we are the receiver */
+  if (eflag && (pid != 0)) {			/* we are the receiver */
     close (fd[1]);
     bytes_left = nblks * blksize;
 
+    /* The buffer was invalidated at forking time.
+     * We must get a new buffer for receiving 
+     * transmitted data. */
+    if ((buf =  malloc((blksize < 4096) ? 4096 : blksize)) == NULL) {
+      perror("malloc buf");
+      exit(1);
+    }
+
     if (gettimeofday(&start, NULL) == -1) {
       perror("gettimeofday");
       exit(1);
@@ -234,6 +305,8 @@
       while (bytes_left) {
 	if ((cnt = read(sock, buf, bytes_left)) == -1)  {
 	  perror("receive:");
+	  kill(pid, SIGTERM);
+	  wait(NULL);
 	  exit(2);
 	}
 
@@ -261,6 +334,8 @@
 	
 	if ((cnt = read(sock, buf, bytes_left)) == -1)  {
 	  perror("receive:");
+	  kill(pid, SIGTERM);
+	  wait(NULL);
 	  exit(2);
 	}
 	bytes_left -= cnt;
@@ -268,12 +343,14 @@
       }
       if (gettimeofday(&end, NULL) == -1) {
 	perror("gettimeofday");
-	_exit(1);
+	exit(1);
       }
     }
     
     delta = (double) (end.tv_sec - start.tv_sec) + (double)
       ((double) (end.tv_usec - start.tv_usec) / 1000000.0);
+    if (vflag)
+      putchar('\n');
     printf("Received %d bytes in %f seconds (%0.3f kbytes/s)\n",
 	   nbytes, delta, (double) (nbytes / delta) /1024);
   
@@ -315,7 +392,8 @@
 	  bufp += cnt;
 	  bytes_left -= cnt;
 
-#ifdef sun
+#if defined(sun) || defined(__GLIBC__) \
+    || defined(__OpenBSD__) || defined(__FreeBSD__)
 	  if (delay)
 	    usleep(delay);
 #else /*sun*/
@@ -351,7 +429,8 @@
 	  bufp += cnt;
 	  bytes_left -= cnt;
 
-#ifdef sun
+#if defined(sun) || defined(__GLIBC__) \
+    || defined(__OpenBSD__) || defined(__FreeBSD__)
 	  if (delay)
 	    usleep(delay);
 #else /*sun*/
@@ -379,10 +458,12 @@
       }
       exit(0);
     }
-    else
-      
-      printf("\nTransmitted %d bytes in %f seconds (%0.3f kbytes/s)\n",
+    else {
+      if (vflag)
+	putchar('\n');
+      printf("Transmitted %d bytes in %f seconds (%0.3f kbytes/s)\n",
 	     nbytes, delta, (double) (nbytes / delta) / 1024); 
+    }
  
   }
   

Attachment: signature.asc
Description: Digital signature

Reply via email to