/* $Copyright: $
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ifndef	lint
static char rcsid[] = "$Header: spray.c 1.2 1991/05/01 17:24:41 $";
#endif

#ifndef lint
/* @(#)spray.c	2.1 86/04/16 NFSSRC */
static  char sccsid[] = "@(#)spray.c 1.1 86/02/05 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <ctype.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <rpcsvc/spray.h>

#define DEFBYTES 100000		/* default numbers of bytes to send */
#define MAXPACKETLEN 1514

char *adrtostr();
char *host;
int adr;

main(argc, argv)
	char **argv;
{
	int err, cnt, i, rcved, lnth;
	int psec, bsec;
	int buf[SPRAYMAX/4];
	struct hostent *hp;
	struct sprayarr arr;	
	struct spraycumul cumul;
	
	if (argc < 2)
		usage();
	
	cnt = -1;
	lnth = SPRAYOVERHEAD;
	while (argc > 1) {
		if (argv[1][0] == '-') {
			switch(argv[1][1]) {
				case 'c':
					cnt = atoi(argv[2]);
					argc--;
					argv++;
					break;
				case 'l':
					lnth = atoi(argv[2]);
					argc--;
					argv++;
					break;
				default:
					usage();
			}
		}
		else {
			if (host)
				usage();
			else
				host = argv[1];
		}
		argc--;
		argv++;
	}
	if (host == NULL)
		usage();
	if ((adr = inet_addr(host)) != -1) {
		host = adrtostr(adr);
	} else {
		if ((hp = gethostbyname(host)) == NULL) {
			fprintf(stderr, "%s is an unknown host name\n", host);
			exit(1);
		}
		adr = *((int *)hp->h_addr);
	}
	if (cnt == -1)
		cnt = DEFBYTES/lnth;
	if (lnth < SPRAYOVERHEAD)
		lnth = SPRAYOVERHEAD;
	else if (lnth >= SPRAYMAX)
		lnth = SPRAYMAX;
	if (lnth <= MAXPACKETLEN && lnth % 4 != 2)
		lnth = ((lnth+5)/4)*4 - 2;
	arr.lnth = lnth - SPRAYOVERHEAD;
	arr.data = buf;
	printf("sending %d packets of length %d to %s ...", cnt, lnth, host);
	fflush(stdout);
	
	if (err = mycallrpc(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_CLEAR,
	    xdr_void, NULL, xdr_void, NULL)) {
		fprintf(stderr, "SPRAYPROC_CLEAR ");
		clnt_perrno(err);
		fprintf(stderr, "\n");
		return;
	}
	for (i = 0; i < cnt; i++)
		callrpcnowait(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_SPRAY,
		    xdr_sprayarr, &arr, xdr_void, NULL);
	if (err = mycallrpc(adr, SPRAYPROG, SPRAYVERS, SPRAYPROC_GET,
	    xdr_void, NULL, xdr_spraycumul, &cumul)) {
		fprintf(stderr, "SPRAYPROC_GET ");
		fprintf(stderr, "%s ", host);
		clnt_perrno(err);
		fprintf(stderr, "\n");
		return;
	}
	if (cumul.counter < cnt)
		printf("\n\t%d packets (%.3f%%) dropped by %s\n",
		    cnt - cumul.counter,
		    100.0*(cnt - cumul.counter)/cnt, host);
	else
		printf("\n\tno packets dropped by %s\n", host);
	psec = (1000000.0 * cumul.counter)
	    / (1000000.0 * cumul.clock.tv_sec + cumul.clock.tv_usec);
	bsec = (lnth * 1000000.0 * cumul.counter)/
	    (1000000.0 * cumul.clock.tv_sec + cumul.clock.tv_usec);
	printf("\t%d packets/sec, %d bytes/sec\n", psec, bsec);
}

/* 
 * like callrpc, but with addr instead of host name
 */
mycallrpc(addr, prognum, versnum, procnum, inproc, in, outproc, out)
	xdrproc_t inproc, outproc;
	char *in, *out;
{
	struct sockaddr_in server_addr;
	enum clnt_stat clnt_stat;
	struct timeval timeout, tottimeout;

	static CLIENT *client;
	static int socket = RPC_ANYSOCK;
	static int oldprognum, oldversnum, valid;
	static int oldadr;

	if (valid && oldprognum == prognum && oldversnum == versnum
		&& adr == oldadr) {
		/* reuse old client */		
	}
	else {
		close(socket);
		socket = RPC_ANYSOCK;
		if (client) {
			clnt_destroy(client);
			client = NULL;
		}
		timeout.tv_usec = 0;
		timeout.tv_sec = 10;
		bcopy(&adr, &server_addr.sin_addr, sizeof(adr));
		server_addr.sin_family = AF_INET;
		server_addr.sin_port =  0;
		if ((client = clntudp_create(&server_addr, prognum,
		    versnum, timeout, &socket)) == NULL)
			return ((int) rpc_createerr.cf_stat);
		valid = 1;
		oldprognum = prognum;
		oldversnum = versnum;
		oldadr = adr;
	}
	tottimeout.tv_sec = 25;
	tottimeout.tv_usec = 0;
	clnt_stat = clnt_call(client, procnum, inproc, in,
	    outproc, out, tottimeout);
	/* 
	 * if call failed, empty cache
	 */
	if (clnt_stat != RPC_SUCCESS)
		valid = 0;
	return ((int) clnt_stat);
}

callrpcnowait(adr, prognum, versnum, procnum, inproc, in, outproc, out)
	xdrproc_t inproc, outproc;
	char *in, *out;
{
	struct sockaddr_in server_addr;
	enum clnt_stat clnt_stat;
	struct timeval timeout, tottimeout;

	static CLIENT *client;
	static int socket = RPC_ANYSOCK;
	static int oldprognum, oldversnum, valid;
	static int oldadr;

	if (valid && oldprognum == prognum && oldversnum == versnum
		&& oldadr == adr) {
		/* reuse old client */		
	}
	else {
		close(socket);
		socket = RPC_ANYSOCK;
		if (client) {
			clnt_destroy(client);
			client = NULL;
		}
		timeout.tv_usec = 0;
		timeout.tv_sec = 0;
		bcopy(&adr, &server_addr.sin_addr, sizeof(adr));
		server_addr.sin_family = AF_INET;
		server_addr.sin_port =  0;
		if ((client = clntudp_create(&server_addr, prognum,
		    versnum, timeout, &socket)) == NULL)
			return ((int) rpc_createerr.cf_stat);
		valid = 1;
		oldprognum = prognum;
		oldversnum = versnum;
		oldadr = adr;
	}
	tottimeout.tv_sec = 0;
	tottimeout.tv_usec = 0;
	clnt_stat = clnt_call(client, procnum, inproc, in,
	    outproc, out, tottimeout);
	/* 
	 * if call failed, empty cache
	 * since timeout is zero, normal return value is RPC_TIMEDOUT
	 */
	if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT)
		valid = 0;
	return ((int) clnt_stat);
}

usage()
{
	fprintf(stderr, "Usage: spray host [-c cnt] [-l lnth]\n");
	exit(1);
}

char *
adrtostr(adr)
int adr;
{
	struct hostent *hp;
	char buf[100];		/* hope this is long enough */
	
	hp = gethostbyaddr(&adr, sizeof(adr), AF_INET);
	if (hp == NULL) {
	    	sprintf(buf, "0x%x", adr);
		return buf;
	}
	else
		return hp->h_name;
}
