/*
 *      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 by Hwa-Jin Bae, bae@mail.com, Piedmont, California
 */

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

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

#include <stdio.h>

#if !defined(VXWORKS) && !defined(__CYGWIN32__)
#include <malloc.h>
#include <io.h>
#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 <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

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

static struct sockaddr_in sinme;
static struct sockaddr_in sinhim;
static struct sockaddr_in sindum;
static struct sockaddr_in frominet;

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

static int	       buflen =	1024;  /* length of buffer */
static char	      *buf;	       /* ptr to dynamic buffer	*/
static int	       nbuf = 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 = 2000;    /* TCP port number */
static char	      *host;	       /* ptr to name of host */
static int	       trans;	       /* 0=receive, !0=transmit mode */
static int	       sinkmode;       /* 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) */


#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;

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

  sinme.sin_family = AF_INET;
  sinme.sin_addr.s_addr = 0;

#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");
  fprintf(stderr, "ttcp%s: nbuf=%d, buflen=%d, port=%d\n",
	  trans	? "-t" : "-r",
	  nbuf,	buflen, port);

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

  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");
    }
  }
  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 */
  }
  fprintf(stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", stats);
  if (realt <= 0.0)
    realt = 0.001;
  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);
#ifdef WINSOCK
  WSACleanup();
#endif
  exit(0);

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

void		err(s)
  char		 *s;
{
  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
  exit(1);
}

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

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

