/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: ttcp.c,v 1.17.2.1 92/06/24 14:12:52 marker Exp $"
/*
 *	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.
 */
#ifndef lint
static char RCSid[] = "@(#)$Header: ttcp.c,v 1.17.2.1 92/06/24 14:12:52 marker Exp $ (BRL)";
#endif

#ifndef SYSV
#define BSD43
#endif SYSV
/* #define BSD43 */
/* #define BSD42 */
/* #define BSD41a */

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#ifdef TLI
#include <fcntl.h>
#include <sys/xti.h>
#define _SVR4_SOCKETS 1
#ifdef _SYSTYPE_SVR4
#include <sys/netconfig.h>
#endif _SYSTYPE_SVR4
#include <sys/timod.h>
#endif TLI
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>		/* struct timeval */
#include <arpa/inet.h>

#ifdef SYSV
#include <sys/times.h>
#include <sys/param.h>
#define bcopy(a,b,c) memcpy(b,a,c)
#define bzero(a,b) memset(a,0,b)
#else
#include <sys/resource.h>
#endif

#include <netinet/in.h>
#include <netinet/tcp.h>

#ifdef _SYSTYPE_SVR3
#include <bsd/syslog.h>
#else _SYSTYPE_SVR3
#include <syslog.h>
#endif _SYSTYPE_SVR3

#include <varargs.h>

#ifdef TLI
extern char *malloc();
#endif TLI

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

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

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

int udp = 0;			/* 0 = tcp, !0 = udp */
int options = 0;		/* socket options */
int one = 1;                    /* for 4.3 BSD style setsockopt() */
short port = 2000;		/* TCP port number */
char *host;			/* ptr to name of host */
int trans;			/* 0=receive, !0=transmit mode */
int sinkmode;			/* 0=normal I/O, !0=sink/source mode */
int option_tcp_nodelay = 0;	/* 1 = set TCP_NODELAY */
int option_tcp_maxseg = 0;	/* non-zero= set TCP_MAXSEG to option value */
int option_buf = 0;		/* non-zero= set SO_SNDBUF or SO_RCVBUF */
int option_sequence = 0;	/* non-zero= sequence number buffers */
int option_daemon = 0;		/* non-zero= run as inetd daemon client */
int option_connection = 0;	/* non-zero= report addresses */

struct hostent *addr;
extern int errno;

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\
	-N	set TCP_NODELAY\n\
	-M##	set TCP_MAXSEG segment size\n\
	-S##	set SO_SNDBUF size\n\
	-O	sequence number buffers\n\
	-P	protocol to use (default is inet) for TLI only\n\
	-C	report connection addresses\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\
	-D	run as inetd daemon server\n\
	-N	set TCP_NODELAY if TCP\n\
	-M##	set TCP_MAXSEG segment size if TCP\n\
	-S##	set SO_RCVBUF size\n\
	-O	check buffer sequence numbers\n\
	-P	protocol to use (default is inet) for TLI only\n\
	-C	report connection addresses\n\
";	

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

void prep_timer();
double read_timer();
double cput, realt;		/* user, real time (seconds) */
#ifdef TLI
struct	t_info	info;
#endif TLI

main(argc,argv)
int argc;
char **argv;
{
	unsigned long addr_tmp;
#ifdef TLI
	struct	t_bind bind_req;
	struct	t_bind bind_ret;
	struct	sockaddr_in bind_ret_addr;
	struct	t_optmgmt optmgmt_req;
	struct	t_optmgmt optmgmt_ret;
	struct	optmgmt_int {
		struct	opthdr	hdr;
		int	optval;
	};
	struct	optmgmt_int optmgmt_req_buf;
	struct	optmgmt_int optmgmt_ret_buf;
	struct	t_call connect_req;
	struct	t_call connect_ret;
	struct	sockaddr_in connect_ret_addr;
	char	*listen_optbuf;
	int	listen_optlen;
	char	*listen_udatabuf;
	int	listen_udatalen;
	int	accept_fd;
	char save_device[1024];
#ifdef _SYSTYPE_SVR4
	struct	netconfig *nconf;
	NCONF_HANDLE *nc_handle;	/* Net config handle */
	char *nc_type = NC_INET;
#else
	char *nc_type = "inet";
#endif _SYSTYPE_SVR4
	struct	netbuf getmyname_buf;
	struct	netbuf getpeername_buf;
#else TLI
	int	getsockname_len;
	int	getpeername_len;
#endif TLI
	int	buffer_sequence;

	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;
		case 'D':
			option_daemon = 1;
			break;
		case 'N':
			option_tcp_nodelay = 1;
			break;
		case 'M':
			option_tcp_maxseg = atoi(&argv[0][2]);
			break;
		case 'S':
			option_buf = atoi(&argv[0][2]);
			break;
		case 'O':
			option_sequence = 1;
			break;
		case 'C':
			option_connection = 1;
			break;
#ifdef TLI
		case 'P':
			nc_type = &argv[0][2];
			break;
#endif TLI
		default:
			goto usage;
		}
		argv++; argc--;
	}
	if (trans && option_daemon)
		goto usage;

	if(trans)  {
		/* xmitr */
		if (argc > 1) goto usage;
		bzero((char *)&sinhim, sizeof(sinhim));
		if (argc == 1) {
			host = argv[0];
			if (atoi(host) > 0 )  {
				/* Numeric */
				sinhim.sin_family = AF_INET;
#ifdef cray
				addr_tmp = inet_addr(host);
				sinhim.sin_addr = addr_tmp;
#else
				sinhim.sin_addr.s_addr = inet_addr(host);
#endif
			} else {
				if ((addr=gethostbyname(host)) == NULL)
					err("bad hostname");
				sinhim.sin_family = addr->h_addrtype;
				bcopy(addr->h_addr,(char*)&addr_tmp,
					addr->h_length);
#ifdef cray
				sinhim.sin_addr = addr_tmp;
#else	
				sinhim.sin_addr.s_addr = addr_tmp;
#endif cray
			}
		}
		sinhim.sin_port = htons(port);
		sinme.sin_port = 0;		/* free choice */
	} else if (! option_daemon) {
		/* rcvr */
		sinme.sin_port =  htons(port);
	}

	if (option_daemon)
		openlog("ttcpd", LOG_PID, LOG_DAEMON);

	if( (buf = (char *)malloc(buflen)) == (char *)NULL)
		err("malloc");

	if (option_daemon) {
		fd = 0;
#ifdef TLI
		if (t_sync(fd) == -1)
			terr("t_sync");
		getmyname_buf.maxlen = sizeof(sinme);
		getmyname_buf.len = 0;
		getmyname_buf.buf = (char *) &sinme;
		if (ioctl(fd,TI_GETMYNAME,(char *) &getmyname_buf) == -1)
			terr("ioctl TI_GETMYNAME");
		getpeername_buf.maxlen = sizeof(sinhim);
		getpeername_buf.len = 0;
		getpeername_buf.buf = (char *) &sinhim;
		if (ioctl(fd,TI_GETPEERNAME,(char *) &getpeername_buf) == -1)
			terr("ioctl TI_GETPEERNAME");
#else TLI
		getsockname_len = sizeof(sinme);
		if (getsockname(fd,(struct sockaddr *) &sinme,
				&getsockname_len) == -1)
			err("getsockname");
		getpeername_len = sizeof(sinhim);
		if (getpeername(fd,(struct sockaddr *) &sinhim,
				&getpeername_len) == -1)
			err("getpeername");
#endif TLI
		port = ntohs(sinme.sin_port);
	}

	err_printf(LOG_INFO,"ttcp%s: nbuf=%d, buflen=%d, port=%d\n",
		trans?"-t":"-r",
		nbuf, buflen, port);

	if (option_daemon)
		goto receive_input;

#ifdef TLI
#ifdef _SYSTYPE_SVR4
	nc_handle = setnetconfig();
	if (nc_handle == NULL) {
		mes("setnetconfig failed");
		exit (1);
	}

	while (nconf = getnetconfig(nc_handle)) {
		if (strcmp(nconf->nc_protofmly, nc_type) == 0) {
			if ((udp && (nconf->nc_semantics ==  NC_TPI_CLTS)) ||
			    (!udp && (nconf->nc_semantics == NC_TPI_COTS_ORD)))
				break;
		}
	}

	if (nconf == NULL) {
		endnetconfig(nc_handle);
		mes("netconfig: Unable to find device.\n");
		exit (1);
		}
	strcpy (save_device, nconf->nc_device);
	endnetconfig(nc_handle);
#else
	if (!strcmp (nc_type, "inet")) {
		strcpy (save_device, udp ? "/dev/udp" : "/dev/tcp");
	} else if (!strcmp (nc_type, "loopback")) {
		strcpy (save_device, udp ? "/dev/ticlts" : "/dev/ticotsord");
	}		
#endif _SYSTYPE_SVR4
	if ((fd = t_open(save_device, O_RDWR, &info)) < 0)
		terr("t_open");
	mes("t_open");

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

#ifdef TLI
	bzero(&bind_req,sizeof(struct t_bind));
	bind_req.addr.len = sizeof(struct sockaddr_in);
	bind_req.addr.buf = (caddr_t) &sinme;
	bind_req.qlen = (trans || udp ? 0 : 1);
	bind_ret.addr.maxlen = sizeof(struct sockaddr_in);
	bind_ret.addr.buf = (caddr_t) &bind_ret_addr;
	if (t_bind(fd,&bind_req,&bind_ret) < 0)
		terr("t_bind");
#else TLI
	if (bind(fd, &sinme, sizeof(sinme)) < 0)
		err("bind");
#endif TLI

	if (!udp)  {
	    if (trans) {
		/* We are the client if transmitting */
		if(options)  {
#ifdef TLI
			int	i;

			for (i = 1; i <= 0x400; i <<= 1) {
			    if (! (options & i))
				continue;
			    optmgmt_req.opt.len = sizeof(optmgmt_req_buf);
			    optmgmt_req.opt.buf = (caddr_t) &optmgmt_req_buf;
			    optmgmt_req.flags = T_NEGOTIATE;
			    optmgmt_req_buf.hdr.level = SOL_SOCKET;
			    optmgmt_req_buf.hdr.name = options;
			    optmgmt_req_buf.hdr.len = sizeof(int);
			    optmgmt_req_buf.optval = 1;
			    optmgmt_ret.opt.maxlen = sizeof(optmgmt_ret_buf);
			    optmgmt_ret.opt.buf = (caddr_t) &optmgmt_ret_buf;
			    if (t_optmgmt(fd,&optmgmt_req,&optmgmt_ret) < 0)
				terr("t_optmgmt");
			}
#else TLI
#ifdef BSD42
			if( setsockopt(fd, SOL_SOCKET, options, 0, 0) < 0)
#else BSD43
			if( setsockopt(fd, SOL_SOCKET, options, &one, sizeof(one)) < 0)
#endif
				err("setsockopt");
#endif TLI
		}
#ifdef TLI
		connect_req.addr.len = sizeof(struct sockaddr_in);
		connect_req.addr.buf = (caddr_t) &sinhim;
		connect_req.opt.len = 0;
		connect_req.opt.buf = NULL;
		connect_req.udata.len = 0;
		connect_req.udata.buf = NULL;
		connect_ret.addr.maxlen = sizeof(struct sockaddr_in);
		connect_ret.addr.buf = (caddr_t) &connect_ret_addr;
		connect_ret.opt.maxlen = info.options;
		if (connect_ret.opt.maxlen > 0) {
			connect_ret.opt.buf = malloc(connect_ret.opt.maxlen);
			if (connect_ret.opt.buf == NULL)
				err("malloc for t_connect");
		} else
			connect_ret.opt.buf = NULL;
		connect_ret.udata.maxlen = 
			(info.connect < 0 ? 0 : info.connect);
		connect_ret.udata.buf = NULL;
		if (connect_ret.udata.maxlen > 0) {
			connect_ret.udata.buf = malloc(connect_ret.udata.maxlen);
			if (connect_ret.udata.buf == NULL)
				err("malloc for t_connect");
		} else
			connect_ret.udata.buf = NULL;
		if (t_connect(fd,&connect_req,&connect_ret) < 0)
			terr("t_connect");
		mes("t_connect");
#else TLI
		if(connect(fd, &sinhim, sizeof(sinhim) ) < 0)
			err("connect");
		mes("connect");
#endif TLI
	    } else {
		/* otherwise, we are the server and 
	         * should listen for the connections
	         */
#ifndef TLI
		listen(fd,0);   /* allow a queue of 0 */
#endif TLI
		if(options)  {
#ifdef TLI
			int	i;

			for (i = 1; i <= 0x400; i <<= 1) {
			    if (! (options & i))
				continue;
			    optmgmt_req.opt.len = sizeof(optmgmt_req_buf);
			    optmgmt_req.opt.buf = (caddr_t) &optmgmt_req_buf;
			    optmgmt_req.flags = T_NEGOTIATE;
			    optmgmt_req_buf.hdr.level = SOL_SOCKET;
			    optmgmt_req_buf.hdr.name = options;
			    optmgmt_req_buf.hdr.len = sizeof(int);
			    optmgmt_req_buf.optval = 1;
			    optmgmt_ret.opt.maxlen = sizeof(optmgmt_ret_buf);
			    optmgmt_ret.opt.buf = (caddr_t) &optmgmt_ret_buf;
			    if (t_optmgmt(fd,&optmgmt_req,&optmgmt_ret) < 0)
				terr("t_optmgmt");
			}
#else TLI
#ifdef BSD42
			if( setsockopt(fd, SOL_SOCKET, options, 0, 0) < 0)
#else BSD43
			if( setsockopt(fd, SOL_SOCKET, options, &one, sizeof(one)) < 0)
#endif
				err("setsockopt");
#endif TLI
		}
#ifdef TLI
		connect_ret.addr.maxlen = sizeof(frominet);
		connect_ret.addr.buf = (caddr_t) &frominet;
		connect_ret.opt.maxlen = info.options;
		if (info.options > 0) {
			listen_optbuf = malloc(info.options);
			if (listen_optbuf == NULL)
				err("malloc listen_optbuf");
		} else
			listen_optbuf = NULL;
		connect_ret.opt.buf = listen_optbuf;
		connect_ret.udata.maxlen = info.connect;
		if (info.connect > 0) {
			listen_udatabuf = malloc(info.connect);
			if (listen_udatabuf == NULL)
				err("malloc listen_udatabuf");
		} else
			listen_udatabuf = NULL;
		connect_ret.udata.buf = listen_udatabuf;
		if (t_listen(fd, &connect_ret) < 0)
			terr("t_listen");
		mes("t_listen");
		if ((accept_fd = t_open(save_device,
			 O_RDWR,
			 &info)) < 0)
			terr("t_open for accept");
		mes("t_open for accept");
		if (t_bind (accept_fd, NULL, NULL) < 0)
			terr ("t_bind for responding fd failed");
		mes("t_bind");
		if (t_accept(fd,accept_fd,&connect_ret) < 0)
			terr("t_accept");
		mes("t_accept");
		fd = accept_fd;
#else TLI
		fromlen = sizeof(frominet);
		domain = AF_INET;
		if((fd=accept(fd, &frominet, &fromlen) ) < 0)
			err("accept");
		mes("accept");
#endif TLI
	    }
	}

	if (option_connection) {
#ifdef TLI
		getmyname_buf.maxlen = sizeof(sinme);
		getmyname_buf.len = 0;
		getmyname_buf.buf = (char *) &sinme;
		if (ioctl(fd,TI_GETMYNAME,(char *) &getmyname_buf) == -1)
			terr("ioctl TI_GETMYNAME");
		getpeername_buf.maxlen = sizeof(sinhim);
		getpeername_buf.len = 0;
		getpeername_buf.buf = (char *) &sinhim;
		if (ioctl(fd,TI_GETPEERNAME,(char *) &getpeername_buf) == -1)
			terr("ioctl TI_GETPEERNAME");
#else TLI
		getsockname_len = sizeof(sinme);
		if (getsockname(fd,(struct sockaddr *) &sinme,
				&getsockname_len) == -1)
			err("getsockname");
		getpeername_len = sizeof(sinhim);
		if (getpeername(fd,(struct sockaddr *) &sinhim,
				&getpeername_len) == -1)
			err("getpeername");
#endif TLI
	}

receive_input:
	if (option_connection || option_daemon) {
		err_printf(LOG_INFO,"ttcp%s: local address %s, port %d\n",
			trans?"-t":"-r",
			inet_ntoa(sinme.sin_addr),
			ntohs(sinme.sin_port));
		err_printf(LOG_INFO,"ttcp%s: remote address %s, port %d\n",
			trans?"-t":"-r",
			inet_ntoa(sinhim.sin_addr),
			ntohs(sinhim.sin_port));
	}

	if (! udp) {
#ifdef TLI
		if (option_tcp_nodelay) {
		    optmgmt_req.opt.len = sizeof(optmgmt_req_buf);
		    optmgmt_req.opt.buf = (caddr_t) &optmgmt_req_buf;
		    optmgmt_req.flags = T_NEGOTIATE;
		    optmgmt_req_buf.hdr.level = IPPROTO_TCP;
		    optmgmt_req_buf.hdr.name = TCP_NODELAY;
		    optmgmt_req_buf.hdr.len = sizeof(int);
		    optmgmt_req_buf.optval = 1;
		    optmgmt_ret.opt.maxlen = sizeof(optmgmt_ret_buf);
		    optmgmt_ret.opt.buf = (caddr_t) &optmgmt_ret_buf;
		    if (t_optmgmt(fd,&optmgmt_req,&optmgmt_ret) < 0)
			terr("t_optmgmt TCP_NODELAY");
		}
		if (option_tcp_maxseg) {
		    optmgmt_req.opt.len = sizeof(optmgmt_req_buf);
		    optmgmt_req.opt.buf = (caddr_t) &optmgmt_req_buf;
		    optmgmt_req.flags = T_NEGOTIATE;
		    optmgmt_req_buf.hdr.level = IPPROTO_TCP;
		    optmgmt_req_buf.hdr.name = TCP_MAXSEG;
		    optmgmt_req_buf.hdr.len = sizeof(int);
		    optmgmt_req_buf.optval = option_tcp_maxseg;
		    optmgmt_ret.opt.maxlen = sizeof(optmgmt_ret_buf);
		    optmgmt_ret.opt.buf = (caddr_t) &optmgmt_ret_buf;
		    if (t_optmgmt(fd,&optmgmt_req,&optmgmt_ret) < 0)
			terr("t_optmgmt TCP_MAXSEG");
		}
#else TLI
		if (option_tcp_nodelay &&
		    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
			err("setsockopt TCP_NODELAY");
		if (option_tcp_maxseg &&
		    setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &option_tcp_maxseg, sizeof(option_tcp_maxseg)) < 0)
			err("setsockopt TCP_MAXSEG");
#endif TLI
	};
#ifdef TLI
	if (option_buf) {
	    optmgmt_req.opt.len = sizeof(optmgmt_req_buf);
	    optmgmt_req.opt.buf = (caddr_t) &optmgmt_req_buf;
	    optmgmt_req.flags = T_NEGOTIATE;
	    optmgmt_req_buf.hdr.level = SOL_SOCKET;
	    optmgmt_req_buf.hdr.name = (trans ? SO_SNDBUF : SO_RCVBUF);
	    optmgmt_req_buf.hdr.len = sizeof(int);
	    optmgmt_req_buf.optval = option_buf;
	    optmgmt_ret.opt.maxlen = sizeof(optmgmt_ret_buf);
	    optmgmt_ret.opt.buf = (caddr_t) &optmgmt_ret_buf;
	    if (t_optmgmt(fd,&optmgmt_req,&optmgmt_ret) < 0)
		terr("t_optmgmt set buffer size");
	}
#else TLI
	if (option_buf &&
	    setsockopt(fd, SOL_SOCKET, (trans ? SO_SNDBUF : SO_RCVBUF), &option_buf, sizeof(option_buf)) < 0)
		err("setsockopt set buffer size");
#endif TLI
		
	prep_timer();
	errno = 0;
	if (sinkmode) {      
		register int cnt;
		if (trans)  {
			pattern( buf, buflen );
			if(udp)  (void)Nwrite( fd, buf, 4 ); /* rcvr start */
			if (option_sequence) {
				buffer_sequence = 0;
				*((int *) buf) = 0;
			}
			while (nbuf-- && Nwrite(fd,buf,buflen) == buflen) {
				if (option_sequence) {
					buffer_sequence++;
					*((int *) buf) = buffer_sequence;
				}
				nbytes += buflen;
			}
			if(udp)  (void)Nwrite( fd, buf, 4 ); /* rcvr end */
		} else {
			if (option_sequence)
				buffer_sequence = 0;
			while ((cnt=Nread(fd,buf,buflen)) > 0)  {
				static int going = 0;
				if( cnt <= 4 )  {
					if( going )
						break;	/* "EOF" */
					going = 1;
					prep_timer();
				} else {
					if (option_sequence) {
					    if (*((int *) buf) !=
							buffer_sequence) {
						char msg[100];

						sprintf(msg,"Buffer out of sequence: expected %d, got %d",
							buffer_sequence,
							*((int *)buf));
						mes(msg);
					    }
					    buffer_sequence = *((int *) buf) + 1;
					}
					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;
		}
	}
	if(errno) err("IO");
	(void)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 */
	}
	err_printf(LOG_INFO,"ttcp%s: %s\n", trans?"-t":"-r", stats);
	if( cput <= 0.0 )  cput = 0.001;
	if( realt <= 0.0 )  realt = 0.001;
	err_printf(LOG_INFO,"ttcp%s: %ld bytes processed\n",
		trans?"-t":"-r", nbytes );
	err_printf(LOG_INFO,"ttcp%s: %9g CPU sec  = %9g KB/cpu sec,  %9g Kbits/cpu sec\n",
		trans?"-t":"-r",
		cput,
		((double)nbytes)/cput/1024,
		((double)nbytes)*8/cput/1024 );
	err_printf(LOG_INFO,"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 );
	exit(0);

usage:
	err_printf(LOG_ERR,Usage);
	exit(1);
}

#ifdef TLI
terr(s)
char *s;
{
	char	*m;
	char	msg[2048];

	sprintf(msg,"ttcp%s: ", trans?"-t":"-r");
	perror_sprintf(msg + strlen(msg),s);
	err_printf(LOG_ERR,msg);
	err_printf(LOG_ERR,"errno=%d\n",errno);
	switch (t_errno) {
	case TBADADDR:
		m = "incorrect addr format";
		break;

	case TBADOPT:
		m = "incorrect option format";
		break;

	case TACCES:
		m = "incorrect permissions";
		break;

	case TBADF:
		m = "illegal transport fd";
		break;

	case TNOADDR:
		m = "couldn't allocate addr";
		break;

	case TOUTSTATE:
		m = "out of state";
		break;

	case TBADSEQ:
		m = "bad call sequnce number";
		break;

	case TSYSERR:
		m = "system error";
		break;

	case TLOOK:
		m = "event requires attention";
		break;

	case TBADDATA:
		m = "illegal amount of data";
		break;

	case TBUFOVFLW:
		m = "buffer not large enough";
		break;

	case TFLOW:
		m = "flow control";
		break;

	case TNODATA:
		m = "no data";
		break;

	case TNODIS:
		m = "discon_ind not found on q";
		break;

	case TNOUDERR:
		m = "unitdata error not found";
		break;

	case TBADFLAG:
		m = "bad flags";
		break;

	case TNOREL:
		m = "no ord rel found on q";
		break;

	case TNOTSUPPORT:
		m = "primitive not supported";
		break;

	case TSTATECHNG:
		m = "state is in process of changing";
		break;

	default:
		m = NULL;
	}
	err_printf(LOG_ERR,"t_errno = %d %s\n",t_errno,m);
	exit(1);
}
#endif TLI

err(s)
char *s;
{
	char msg[2048];

	sprintf(msg,"ttcp%s: ", trans?"-t":"-r");
	perror_sprintf(msg + strlen(msg),s);
	err_printf(LOG_ERR,msg);
	err_printf(LOG_ERR,"errno=%d\n",errno);
	exit(1);
}

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

extern char *sys_errlist[];
extern int sys_nerr;

perror_sprintf(m,s)
	char	*m;
	char	*s;
{
	if (errno < 0 || errno >= sys_nerr)
		sprintf(m,"%s: error %d\n",s,errno);
	else
		sprintf(m,"%s: %s\n",s,sys_errlist[errno]);
}

	
err_printf(level,s,va_alist)
	int	level;
	char *s;
	va_dcl
{
	va_list ap;
	char	msg[2048];

	va_start(ap);
	vsprintf(msg,s,ap);
	va_end(ap);
	if (option_daemon) {
		syslog(level,"%s",msg);
	} else
		fprintf(stderr,"%s",msg);
}


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 *********/

#ifdef SYSV
extern long time();
static long time0;
static struct tms tms0;
#else
static struct	timeval time0;	/* Time at which timeing started */
static struct	rusage ru0;	/* Resource utilization at the start */

static void prusage();
static void tvadd();
static void tvsub();
static void psecs();
#endif

/*
 *			P R E P _ T I M E R
 */
void
prep_timer()
{
#ifdef SYSV
	(void)time(&time0);
	(void)times(&tms0);
#else
	gettimeofday(&time0, (struct timezone *)0);
	getrusage(RUSAGE_SELF, &ru0);
#endif
}

/*
 *			R E A D _ T I M E R
 * 
 */
double
read_timer(str,len)
char *str;
{
#ifdef SYSV
	long now;
	struct tms tmsnow;
	char line[132];

	(void)time(&now);
	realt = now-time0;
	(void)times(&tmsnow);
	cput = tmsnow.tms_utime - tms0.tms_utime;
	cput /= HZ;
	if( cput < 0.00001 )  cput = 0.01;
	if( realt < 0.00001 )  realt = cput;
	sprintf(line,"%g CPU secs in %g elapsed secs (%g%%)",
		cput, realt,
		cput/realt*100 );
	(void)strncpy( str, line, len );
	return( cput );
#else
	/* BSD */
	struct timeval timedol;
	struct rusage ru1;
	struct timeval td;
	struct timeval tend, tstart;
	char line[132];

	getrusage(RUSAGE_SELF, &ru1);
	gettimeofday(&timedol, (struct timezone *)0);
	prusage(&ru0, &ru1, &timedol, &time0, line);
	(void)strncpy( str, line, len );

	/* Get real time */
	tvsub( &td, &timedol, &time0 );
	realt = td.tv_sec + ((double)td.tv_usec) / 1000000;

	/* Get CPU time (user+sys) */
	tvadd( &tend, &ru1.ru_utime, &ru1.ru_stime );
	tvadd( &tstart, &ru0.ru_utime, &ru0.ru_stime );
	tvsub( &td, &tend, &tstart );
	cput = td.tv_sec + ((double)td.tv_usec) / 1000000;
	if( cput < 0.00001 )  cput = 0.00001;
	return( cput );
#endif
}

#ifndef SYSV
static void
prusage(r0, r1, e, b, outp)
	register struct rusage *r0, *r1;
	struct timeval *e, *b;
	char *outp;
{
	struct timeval tdiff;
	register time_t t;
	register char *cp;
	register int i;
	int ms;

	t = (r1->ru_utime.tv_sec-r0->ru_utime.tv_sec)*100+
	    (r1->ru_utime.tv_usec-r0->ru_utime.tv_usec)/10000+
	    (r1->ru_stime.tv_sec-r0->ru_stime.tv_sec)*100+
	    (r1->ru_stime.tv_usec-r0->ru_stime.tv_usec)/10000;
	ms =  (e->tv_sec-b->tv_sec)*100 + (e->tv_usec-b->tv_usec)/10000;

#define END(x)	{while(*x) x++;}
	cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw";
	for (; *cp; cp++)  {
		if (*cp != '%')
			*outp++ = *cp;
		else if (cp[1]) switch(*++cp) {

		case 'U':
			tvsub(&tdiff, &r1->ru_utime, &r0->ru_utime);
			sprintf(outp,"%d.%01d", tdiff.tv_sec, tdiff.tv_usec/100000);
			END(outp);
			break;

		case 'S':
			tvsub(&tdiff, &r1->ru_stime, &r0->ru_stime);
			sprintf(outp,"%d.%01d", tdiff.tv_sec, tdiff.tv_usec/100000);
			END(outp);
			break;

		case 'E':
			psecs(ms / 100, outp);
			END(outp);
			break;

		case 'P':
			sprintf(outp,"%d%%", (int) (t*100 / ((ms ? ms : 1))));
			END(outp);
			break;

		case 'W':
			i = r1->ru_nswap - r0->ru_nswap;
			sprintf(outp,"%d", i);
			END(outp);
			break;

		case 'X':
			sprintf(outp,"%d", t == 0 ? 0 : (r1->ru_ixrss-r0->ru_ixrss)/t);
			END(outp);
			break;

		case 'D':
			sprintf(outp,"%d", t == 0 ? 0 :
			    (r1->ru_idrss+r1->ru_isrss-(r0->ru_idrss+r0->ru_isrss))/t);
			END(outp);
			break;

		case 'K':
			sprintf(outp,"%d", t == 0 ? 0 :
			    ((r1->ru_ixrss+r1->ru_isrss+r1->ru_idrss) -
			    (r0->ru_ixrss+r0->ru_idrss+r0->ru_isrss))/t);
			END(outp);
			break;

		case 'M':
			sprintf(outp,"%d", r1->ru_maxrss/2);
			END(outp);
			break;

		case 'F':
			sprintf(outp,"%d", r1->ru_majflt-r0->ru_majflt);
			END(outp);
			break;

		case 'R':
			sprintf(outp,"%d", r1->ru_minflt-r0->ru_minflt);
			END(outp);
			break;

		case 'I':
			sprintf(outp,"%d", r1->ru_inblock-r0->ru_inblock);
			END(outp);
			break;

		case 'O':
			sprintf(outp,"%d", r1->ru_oublock-r0->ru_oublock);
			END(outp);
			break;
		case 'C':
			sprintf(outp,"%d+%d", r1->ru_nvcsw-r0->ru_nvcsw,
				r1->ru_nivcsw-r0->ru_nivcsw );
			END(outp);
			break;
		}
	}
	*outp = '\0';
}

static void
tvadd(tsum, t0, t1)
	struct timeval *tsum, *t0, *t1;
{

	tsum->tv_sec = t0->tv_sec + t1->tv_sec;
	tsum->tv_usec = t0->tv_usec + t1->tv_usec;
	if (tsum->tv_usec > 1000000)
		tsum->tv_sec++, tsum->tv_usec -= 1000000;
}

static void
tvsub(tdiff, t1, t0)
	struct timeval *tdiff, *t1, *t0;
{

	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
	if (tdiff->tv_usec < 0)
		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
}

static void
psecs(l,cp)
long l;
register char *cp;
{
	register int i;

	i = l / 3600;
	if (i) {
		sprintf(cp,"%d:", i);
		END(cp);
		i = l % 3600;
		sprintf(cp,"%d%d", (i/60) / 10, (i/60) % 10);
		END(cp);
	} else {
		i = l;
		sprintf(cp,"%d", i / 60);
		END(cp);
	}
	i %= 60;
	*cp++ = ':';
	sprintf(cp,"%d%d", i / 10, i % 10);
}
#endif

/*
 *			N R E A D
 */
Nread( fd, buf, count )
{
	struct sockaddr_in from;
	int len = sizeof(from);
	register int cnt;
	if( udp )  {
#ifdef TLI
		struct	t_unitdata unitdata;
		int	flags;

		unitdata.addr.maxlen = sizeof(from);
		unitdata.addr.buf = (caddr_t) &from;
		unitdata.opt.maxlen = 0;
		unitdata.opt.buf = NULL;
		unitdata.udata.maxlen = count;
		unitdata.udata.buf = (caddr_t) buf;
		cnt = t_rcvudata(fd, &unitdata, &flags);
		if (cnt >= 0)
			cnt = unitdata.udata.len;
#else TLI
		cnt = recvfrom( fd, buf, count, 0, &from, &len );
#endif TLI
	} else {
		if( b_flag )
			cnt = mread( fd, buf, count );	/* fill buf */
		else
#ifdef TLI
			cnt = tli_read( fd, buf, count);
#else TLI
			cnt = read( fd, buf, count );
#endif TLI
	}
	return(cnt);
}

/*
 *			N W R I T E
 */
Nwrite( fd, buf, count )
{
	register int cnt;
	if( udp )  {
#ifdef TLI
		struct t_unitdata unitdata;

		unitdata.addr.len = sizeof(sinhim);
		unitdata.addr.buf = (caddr_t) &sinhim;
		unitdata.opt.len = 0;
		unitdata.opt.buf = NULL;
		unitdata.udata.len = count;
		unitdata.udata.buf = (caddr_t) buf;
		cnt = t_sndudata(fd, &unitdata);
		if (cnt >= 0)
			cnt = count;		
#else TLI
again:
		cnt = sendto( fd, buf, count, 0, &sinhim, sizeof(sinhim) );
#ifndef SYSV
		if( cnt<0 && errno == ENOBUFS )  {
			delay(18000);
			errno = 0;
			goto again;
		}
#endif SYSV
#endif TLI
	} else {
#ifdef TLI
		cnt = t_snd(fd, buf, count, 0);
#else TLI
		cnt = write( fd, buf, count );
#endif TLI
	}
	return(cnt);
}

#ifndef SYSV
delay(us)
{
	struct timeval tv;

	tv.tv_sec = 0;
	tv.tv_usec = us;
	(void)select( 1, (char *)0, (char *)0, (char *)0, &tv );
	return(1);
}
#endif SYSV

/*
 *			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 {
#ifdef TLI
		nread = tli_read(fd, bufp, n - count);
#else TLI
		nread = read(fd, bufp, n-count);
#endif TLI
		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);
}



#ifdef TLI
int
tli_read(fd, buf, count)
	int	fd;
	char	*buf;
	int	count;
{
	int	flags;
	int	cnt;
	
retry_read:
	cnt = t_rcv(fd,	buf, count, &flags);
	if (cnt == -1) {
		if (t_errno == TLOOK) {
			int	event;

			event = t_look(fd);
			switch (event) {
			case T_ORDREL:
				if (t_rcvrel(fd) < 0)
					terr("t_rcvrel");
				return(0);

			case T_DISCONNECT:
			{
				struct t_discon discon;
				char	msg[100];

				discon.udata.maxlen = info.discon;
				discon.udata.buf = malloc(info.discon);
				if (t_rcvdis(fd,&discon) < 0)
					terr("t_rcvdis");
				sprintf(msg,"t_rcvdis: reason = %d",
						discon.reason);
				mes(msg);
				if (discon.udata.buf != NULL)
					free(discon.udata.buf);
				return(0);
			}

			case 0:
			case T_DATA:
			case T_EXDATA:
			case T_GODATA:
			case T_GOEXDATA:
				goto retry_read;

			default:
				return(-1);
			}
		} else if (t_errno = TOUTSTATE)
			return(0);
	}
	return(cnt);
}
#endif TLI
