/* Ported to VxWorks by PSO Systems Inc, Portions Copyright 1999 */

/*
 *	T T C P . C
 *
 * Test TCP connection.  Makes a connection on port 2000
 * and transfers zero buffers or data copied from stdin.
 *
 * Usable on 4.2, 4.3, and 4.1a systems by defining one of
 * BSD42 BSD43 (BSD41a)
 *
 * Modified for operation under 4.2BSD, 18 Dec 84
 *      T.C. Slattery, USNA
 * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85.
 *
 * Ported to VxWorks and win32 by PSO Systems Inc.
 *
 * Notes on VxWorks port:
 * 
 * Due to the way global variables are used there are a number of 
 * bugs.  Running two copies on the local system (over loopback)
 * exposes a lot of these bugs.
 */

/* #define BSD43 */
/* #define BSD42 */
/* #define BSD41a */

/* #define VXWORKS */	/* XXX */

#ifdef VXWORKS
#include <vxWorks.h>
#endif

#include <stdio.h>

#if !defined(VXWORKS) && !defined(__CYGWIN32__)
#include <malloc.h>
#if 0
#include <io.h>
#endif
#else
#ifdef VXWORKS
#include "memLib.h"
#endif
#endif

#include <ctype.h>
#include <errno.h>
#include <sys/types.h>

#ifdef WINSOCK
#include <winsock.h>
#else
#include <sys/socket.h>
#endif

#ifndef WINSOCK
#include <netinet/in.h> 
#endif

#ifndef VXWORKS
#include <netdb.h> 
#include <sys/time.h>
#endif

#ifdef VXWORKS
#include "hostLib.h"
#include "sys/times.h"
#endif

/* new style socket address */

#ifdef BSD44
struct bsd44_sockaddr_in {
	unsigned char  sin_len;
	unsigned char  sin_family;
	unsigned short sin_port;
	struct	  in_addr sin_addr;
	unsigned char  sin_zero[8];
};
#endif

#define bzero(A,B)  memset(A,0,B)
#define bcopy(A,B,C) memcpy(B,A,C)

static void            err(char *s);
static void            mes(char *s);
static void            pattern(char *cp, int cnt);
static int             Nread(int fd, char *buf, int count);
static int             Nwrite(int fd, char *buf, int count);
static int             mread(int fd, char *bufp, unsigned n);
static int             delay(int us);

#ifdef BSD44
static struct bsd44_sockaddr_in sinme;
static struct bsd44_sockaddr_in sinhim;
static struct bsd44_sockaddr_in sindum;
static struct bsd44_sockaddr_in frominet;
#else
static struct sockaddr_in sinme;
static struct sockaddr_in sinhim;
static struct sockaddr_in sindum;
static struct sockaddr_in frominet;
#endif

static int             domain,  fromlen;
static int             fd;             /* fd of network socket */
static int             fd2;             /* fd of network socket */
static int             fd1;             /* fd of network socket */

static int             buflen = 8 * 1024;  /* length of buffer */
static char           *buf;            /* ptr to dynamic buffer */
static int             nbuf = 2 * 1024;    /* number of buffers to send in sinkmode */

static int             udp = 0;        /* 0 = tcp, !0 = udp */
static int             options = 0;    /* socket options */
static int             one = 1;        /* for 4.3 BSD style setsockopt() */
static short           port = 5001;    /* TCP port number */
static char           *host;           /* ptr to name of host */
static int             trans;          /* 0=receive, !0=transmit mode */
static int             sinkmode = 1;       /* 0=normal I/O, !0=sink/source mode */

static struct hostent *addr;

extern int      errno;

static char            Usage[] = "\
Usage: ttcp -t [-options] host <in\n\
	-l##	length of bufs written to network (default 1024)\n\
	-s	source a pattern to network\n\
	-n##	number of bufs written to network (-s only, default 1024)\n\
	-p##	port number to send to (default 2000)\n\
	-u	use UDP instead of TCP\n\
Usage: ttcp -r [-options] >out\n\
	-l##	length of network read buf (default 1024)\n\
	-s	sink (discard) all data from network\n\
	-p##	port number to listen at (default 2000)\n\
	-B	Only output full blocks, as specified in -l## (for TAR)\n\
	-u	use UDP instead of TCP\n\
";

static char            stats[128];
static double          t;              /* transmission time */
static long            nbytes;         /* bytes on net */
static int             b_flag = 0;     /* use mread() */

static void            prep_timer();
static void            read_timer();
static double          realt;          /* user, real time (seconds) */

int
my_exit(int num)
{
	if (fd1 != -1) {
		shutdown(fd1,2);
		close(fd1);
	}
	if (fd2 != -1) {
		shutdown(fd2,2);
		close(fd2);
	}
	exit(num);
}

#ifndef VXWORKS
void            main(argc, argv)
  int             argc;
  char          **argv;
#else
void ttcp(int b_flagX, int transX, int debug, int nbufX, int buflenX,
			int sinkmodeX, int portX, int udpX, char *host)
#endif
{
  unsigned long   addr_tmp;
#ifdef WINSOCK
  WSADATA         wsaData;
  WORD            wVersionRequested;
#endif
  int             ret;

   fd = -1;
   fd1 = -1;
   fd2 = -1;

#ifndef VXWORKS
  if (argc < 2)
    goto usage;

  argv++;
  argc--;
  while (argc > 0 && argv[0][0] == '-')
  {
    switch (argv[0][1])
      {

      case 'B':
          b_flag = 1;
          break;
      case 't':
          trans = 1;
          break;
      case 'r':
          trans = 0;
          break;
      case 'd':
          options |= SO_DEBUG;
          break;
      case 'n':
          nbuf = atoi(&argv[0][2]);
          break;
      case 'l':
          buflen = atoi(&argv[0][2]);
          break;
      case 's':
          sinkmode = 1;         /* source or sink, really */
          break;
      case 'p':
          port = atoi(&argv[0][2]);
          break;
      case 'u':
          udp = 1;
          break;
      default:
          goto usage;
      }
    argv++;
    argc--;
  }

#else
  
  b_flag = b_flagX;
  trans = transX;
  if (debug)
    options  	|= SO_DEBUG;
  nbuf = nbufX;
  buflen = buflenX;
  sinkmode = sinkmodeX;
  port = portX;
  udp = udpX;
#endif

  if (trans)
  {
    /* xmitr */

    bzero((char *) &sinhim, sizeof(sinhim));

#ifndef VXWORKS    
    if (argc != 1)
      goto usage;
    host = argv[0];
#else
	if (!host)
   	goto usage;
#endif

    if (atoi(host) > 0)
    {
      /* Numeric */
      sinhim.sin_family = AF_INET;
      sinhim.sin_addr.s_addr = inet_addr(host);
    }
    else
    {
#ifndef VXWORKS
      if ((addr = gethostbyname(host)) == NULL)
        err("bad hostname");
      sinhim.sin_family = addr->h_addrtype;
      bcopy(addr->h_addr, (char *) &addr_tmp, addr->h_length);
#else
	if ((addr = hostGetByName(host)) == ERROR)
      	err("bad hostname");
      sinhim.sin_family = AF_INET;
      addr_tmp = addr;
#endif

      sinhim.sin_addr.s_addr = addr_tmp;
    }
    sinhim.sin_port = htons(port);
    sinme.sin_port = 0;         /* free choice */
  }
  else
  {
    /* rcvr */
    sinme.sin_port = htons(port);
  }

#if 0 /* GPU */
  sinme.sin_family = AF_INET;
  sinme.sin_addr.s_addr = 0;
#endif

#ifdef WINSOCK
  wVersionRequested = MAKEWORD(1, 1);

  ret = WSAStartup(wVersionRequested, &wsaData);
  if (ret != 0)
    err("WSAStartup");
#endif


  if ((buf = (char *) malloc(buflen)) == (char *) NULL)
    err("malloc");
#ifndef VXWORKS
  fprintf(stderr, "ttcp%s: nbuf=%d, buflen=%d, port=%d\n",
          trans ? "-t" : "-r",
          nbuf, buflen, port);
#else
  logMsg("ttcp: nbuf=%d, buflen=%d, port=%d\n",
  	nbuf, buflen, port);
#endif

  if ((fd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0)
    err("socket");
  mes("socket");

#if 0
  fd1 = fd;
  { 
	int on = 1;
	int sz = 30000;	/* XXX */
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz));
	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sz, sizeof(sz));
  }
#endif

  if (bind(fd, (struct sockaddr *) & sinme, sizeof(sinme)) < 0)
    err("bind");
  mes("bind");

  if (!udp)
  {
    if (trans)
    {
      /* We are the client if transmitting */
      if (options)
      {
        if (setsockopt(fd, SOL_SOCKET, options, (const char *) &one, sizeof(one)) < 0)
          err("setsockopt");
      }
      if (connect(fd, (struct sockaddr *) & sinhim, sizeof(sinhim)) < 0)
        err("connect");
      mes("connect");
    }
    else
    {
      /* otherwise, we are the server and should listen for the connections */
      listen(fd, 0);            /* allow a queue of 0 */
      if (options)
      {
        if (setsockopt(fd, SOL_SOCKET, options, (const char *) &one, sizeof(one)) < 0)
          err("setsockopt");
      }
      fromlen = sizeof(frominet);
      domain = AF_INET;
      if ((fd = accept(fd, (struct sockaddr *) & frominet, &fromlen)) < 0)
        err("accept");
      mes("accept");
      fd2 =fd;
    }
  }

#if 0
  { 
	int on = 1;
	int sz = 30000;	/* XXX */
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz));
	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sz, sizeof(sz));
  }
#endif

  prep_timer();
  errno = 0;
  if (sinkmode)
  {
    register int    cnt;
    if (trans)
    {
      pattern(buf, buflen);
      if (udp)
        (void) Nwrite(fd, buf, 4);  /* rcvr start */
      while (nbuf-- && Nwrite(fd, buf, buflen) == buflen)
        nbytes += buflen;
      if (udp)
        (void) Nwrite(fd, buf, 4);  /* rcvr end */
    }
    else
    {
      while ((cnt = Nread(fd, buf, buflen)) > 0)
      {
        static int      going = 0;
        if (cnt <= 4)
        {
          if (going)
            break;              /* "EOF" */
          going = 1;
          prep_timer();
        }
        else
          nbytes += cnt;
      }
    }
  }
  else
  {
    register int    cnt;
    if (trans)
    {
      while ((cnt = read(0, buf, buflen)) > 0 &&
             Nwrite(fd, buf, cnt) == cnt)
        nbytes += cnt;
    }
    else
    {
      while ((cnt = Nread(fd, buf, buflen)) > 0 &&
             write(1, buf, cnt) == cnt)
        nbytes += cnt;
    }
  }
#ifndef VXWORKS
  if (errno)
    err("IO");
#endif
  read_timer(stats, sizeof(stats));
  if (udp && trans)
  {
    (void) Nwrite(fd, buf, 4);  /* rcvr end */
    (void) Nwrite(fd, buf, 4);  /* rcvr end */
    (void) Nwrite(fd, buf, 4);  /* rcvr end */
    (void) Nwrite(fd, buf, 4);  /* rcvr end */
  }
#ifndef VXWORKS
  fprintf(stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", stats);
#else
  logMsg("ttcp: %s\n", stats);
#endif
  if (realt <= 0.0)
    realt = 0.001;
#ifndef VXWORKS
  fprintf(stderr, "ttcp%s: %ld bytes processed\n",
          trans ? "-t" : "-r", nbytes);
  fprintf(stderr, "ttcp%s: %9g real sec = %9g KB/real sec, %9g Kbits/sec\n",
          trans ? "-t" : "-r",
          realt,
          ((double) nbytes) / realt / 1024,
          ((double) nbytes) * 8 / realt / 1024);
#else
  logMsg("ttcp: %ld bytes processed\n", nbytes);
  logMsg("ttcp: %9g real sec = %9g KB/real sec, %9g Kbits/sec\n",
          realt,
          ((double) nbytes) / realt / 1024,
          ((double) nbytes) * 8 / realt / 1024);
#endif

#ifdef WINSOCK
  WSACleanup();
#endif
  my_exit(0);

usage:
  fprintf(stderr, Usage);
#ifdef WINSOCK
  WSACleanup();
#endif
  my_exit(1);
}

void            err(s)
  char           *s;
{
#ifndef VXWORKS
  fprintf(stderr, "ttcp%s: ", trans ? "-t" : "-r");
  perror(s);
#ifdef WINSOCK
  fprintf(stderr, "errno=%d  WSAGetLastError() = %d\n", errno, WSAGetLastError());
  WSACleanup();
#else
  fprintf(stderr, "errno=%d\n", errno);
#endif
#else /* VXWORKS */

	logMsg("ttcp: <%s> errno %d\n",s, errno);
#endif

  my_exit(1);
}

void            mes(s)
  char           *s;
{
#ifdef VXWORKS
  logMsg("ttcp: %s\n",  s);
#else
  fprintf(stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", s);
#endif
}

void            pattern(cp, cnt)
  register char  *cp;
  register int    cnt;
{
  register char   c;
  c = 0;
  while (cnt-- > 0)
  {
    while (!isprint((c & 0x7F)))
      c++;
    *cp++ = (c++ & 0x7F);
  }
}

/******* timing *********/

extern long     time();
static long     time0;

/*
 *			P R E P _ T I M E R
 */
void
                prep_timer()
{
  (void) time(&time0);
}

/*
 *			R E A D _ T I M E R
 *
 */
void
                read_timer(str, len)
  char           *str;
{
  long            now;
  char            line[132];

  (void) time(&now);
  realt = now - time0;
  if (realt < 0.00001)
    realt = 0;
  sprintf(line, "%g elapsed secs",
          realt);
  (void) strncpy(str, line, len);
}


/*
 *			N R E A D
 */
Nread(int fd, char *buf, int count)
{
  struct sockaddr_in from;
  int             len = sizeof(from);
  register int    cnt;
  if (udp)
  {
    cnt = recvfrom(fd, buf, count, 0, (struct sockaddr *) & from, &len);
  }
  else
  {
    if (b_flag)
      cnt = mread(fd, buf, count);  /* fill buf */
    else
      cnt = recv(fd, buf, count, 0);
  }
  return (cnt);
}


/*
 *			N W R I T E
 */
Nwrite(int fd, char *buf, int count)
{
  register int    cnt;
  if (udp)
  {
again:
    cnt = sendto(fd, buf, count, 0, (struct sockaddr *) & sinhim, sizeof(sinhim));
#ifdef WINSOCK
    if (cnt < 0 && (WSAGetLastError() == WSAENOBUFS))
    {
      delay(18000);
      errno = 0;
      goto again;
    }

#else
    if (cnt < 0 && errno == ENOBUFS)
    {
      delay(18000);
      errno = 0;
      goto again;
    }

#endif
  }
  else
  {
    cnt = send(fd, buf, count, 0);
  }
  return (cnt);
}

delay(us)
{
  struct timeval  tv;

  tv.tv_sec = 0;
  tv.tv_usec = us;
  (void) select(1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv);
  return (1);
}

/*
 *			M R E A D
 *
 * This function performs the function of a read(II) but will
 * call read(II) multiple times in order to get the requested
 * number of characters.  This can be necessary because
 * network connections don't deliver data with the same
 * grouping as it is written with.  Written by Robert S. Miles, BRL.
 */
int
                mread(fd, bufp, n)
  int             fd;
  register char  *bufp;
  unsigned        n;
{
  register unsigned count = 0;
  register int    nread;

  do
  {
    nread = recv(fd, bufp, n - count, 0);
    if (nread < 0)
    {
      perror("ttcp_mread");
      return (-1);
    }
    if (nread == 0)
      return ((int) count);
    count += (unsigned) nread;
    bufp += nread;
  } while (count < n);

  return ((int) count);
}
