/*
 * bbnet.c
 * BIG BROTHER NETWORK TESTING PROGRAM
 * Sean MacGuire
 * Version 1.9 
 * Mar 13th, 2002
 *
 * (c) Copyright Quest Software, Inc.  1997-2002  All rights reserved.
 *
 * Format: bbnet machine:port ARG 
 *
 * i.e.:	http://blah.com/bobo	HTTP TESTING USING HEAD COMMAND
 *		http://blah.com/http://bobo.com		PROXY TESTING
 * 		http://blah.com "GET /"	GET AN ENTIRE PAGE AS TEST
 *		blah.com		TELNET TESTING (OR ANY OTHER PORT)
 *
 * bbnet understands real machine names and port numbers
 * i.e. blah.com:21 will open a connection to the FTP server
 *
 * Displays whatever answers on that port/address to stdout
 *
 * Returns: 	0	OK
 *		1	Host not found
 *		2	Can't connect to server
 *		99	Timeout
 */

#include "bb.h"		/* THE BIG BROTHER INCLUDE FILE */
#ifdef TIMEH
#include <time.h>	/* FOR TIMING STUFF SMM */
#if GETTIMEOFDAY
#include <sys/time.h>
#endif
#else
#include <sys/time.h>	/* FOR TIMING STUFF SMM */
#endif
#include <netdb.h>	/* FOR HOST INFO */

char bbprog[] = "bbnet";

int http=0;		/* ARE WE HTTP TESTING? */

struct in_addr ip;	/* THE IP ADDRESS WE'RE TESTING */

int port;		/* THE PORT NUMBER WE'RE TESTING */
int timer=3;		/* HOW LONG TO WAIT */
int loopcnt=0;		/* LOOP COUNTER FOR ALARM TIMING */
int quiet=0;		/* QUIET MODE */
int noerrors=0;		/* Don't show errors, thanks to Mark.Deiss@acs-gsg.com  */
int rc=0;		/* ERROR CODES */
int silent=0;		/* ONLY CHECK IF PORT CAN BE OPENED */
char *dir;		/* DIRECTORY */
char *machine;		/* MACHINE NAME ONLY */
#define BBNETTIMER1	3
#define BBNETTIMER2	5
#define BBNETTIMER3	12	
int timer1 = BBNETTIMER1;
int timer2 = BBNETTIMER2;
int timer3 = BBNETTIMER3;

char errormsg[MAXLINE];

#ifdef SIGSETJMP
sigjmp_buf jmpenv;  
#else
jmp_buf jmpenv;  
#endif

#ifdef GETTIMEOFDAY
/*
 * SMM FOR FREEBSD... PROBABLY ISN'T PORTABLE...
 * STOPWATCH - YEAH, IT'S WHAT YOU THINK...
 */
void stopwatch()
{
	static long sec, usec;
	static int stop;
	struct timeval *ti;
	struct timezone tz;		/* CAN'T BE NULL */
	ti = (struct timeval *)malloc(sizeof (struct timeval));
	gettimeofday(ti, &tz);		/* START THE CLOCK */

#if DEBUG
	debug("IN STOPWATCH quiet=%d stop=%d\n", quiet, stop);
#endif

	if (stop) {			/* 2nd PRESS OF THE BUTTON */
		sec = ti->tv_sec - sec;
		usec = ti->tv_usec - usec;
		if (usec < 0) {
			sec--;			/* ONE LESS SEC */
			usec += 1000000;	/* IZZAT A USEC? */
		}
		usec /= 10000;			/* 1/100 sec? */
		if (quiet != 1)
			printf("\nSeconds: %ld.%02ld\n", sec, usec);
		stop=0;
	}
	else {					/* START THE WATCH */
		stop = 1;
		sec = ti->tv_sec;
		usec = ti->tv_usec;
	}
}
#endif


int main(argc, argv)
int argc;
char *argv[];
{

	int argmin=2, argmax=3, curarg=1;
	char *arg1, *arg2;
	char *bbnettimer1, *bbnettimer2, *bbnettimer3;

	if (argc > 1) {
		while ( (curarg++ < argc) && *argv[1] == '-') {		/* QUIET MODE */
			if (strcmp(argv[1], "-q") == 0) quiet=1; /* NO SECONDS */
			else if (strcmp(argv[1], "-Q") == 0) noerrors=1; /* NO ERRORS */
			else if (strcmp(argv[1], "-s") == 0) silent=1; /* Open port only */
			else {
				fprintf(stderr, "bbnet: Invalid argument\n");
				fprintf(stderr, "Format: bbnet [-s] [-q] [-Q] machine[:port]|http-request\n");
				exit(1);
			}
			argmin++;
			argmax++;
			argv++;
		}
		arg1=argv[1];
		arg2=argv[2];
	}

	if (argc < argmin || argc > argmax) {	
		fprintf(stderr, "bbnet: incorrect number of arguments\n");
		fprintf(stderr, "Format: bbnet [-s] [-q] [-Q] machine[:port]|http-request\n");
		exit(1);
	}
	bbnettimer1 = (char *) getenv("BBNETTIMER1");
	if( !bbnettimer1 ) {
		timer1 = BBNETTIMER1;
	}
	else {
		timer1 = atoi(bbnettimer1);
		if( timer1 <= 0 ) {
			timer1 = BBNETTIMER1;
		}
	}
	bbnettimer2 = (char *) getenv("BBNETTIMER2");
	if( !bbnettimer2 ) {
		timer2 = BBNETTIMER2;
	}
	else {
		timer2 = atoi(bbnettimer2);
		if( timer2 <= 0 ) {
			timer2 = BBNETTIMER2;
		}
	}
	bbnettimer3 = (char *) getenv("BBNETTIMER3");
	if( !bbnettimer3 ) {
		timer3 = BBNETTIMER3;
	}
	else {
		timer3 = atoi(bbnettimer3);
		if( timer3 <= 0 ) {
			timer3 = BBNETTIMER3;
		}
	}
	if ((rc = setup(arg1)) == 0) {	/* IF NO ERRORS DURING SETUP */
		if (argc==argmax) rc = bnetsend(arg2);	/* DO TEST */
		else rc = bnetsend((char *)NULL);
	}
	exit(rc);
}

/*
 * THIS FUNCTION SETS UP EVERYTHING WE NEED TO CALL THE PORT
 */
int setup(arg)
char *arg;
{
	char *eob = 0, *ptr,*newdir;
	struct hostent *ho = 0;

	/*
	 * PARSE THE ARGUMENT.  THIS SHOULD BE A DROP-IN REPLACEMENT
	 * FOR lynx, UGH.  THAT MEANS THE FIRST ARGUMENT MAY BE A REALLY
	 * NASTY THING.
	 */
	machine = (char *)malloc(MAXLINE);
	strncpy(machine, arg, MAXLINE - 1);

	if (strncmp(machine, "http://", 7) == 0) {
		http = 1;
#if DEBUG
		debug("We have an http request\n");
#endif
		machine += 7;
		port = 80;	/* STANDARD HTTP PORT */
	}
	else {
		http=0;		/* NOT TESTING HTTP */
		port = 23;	/* TELNET DEFAULT */
	}

	ptr = (char *)strchr(machine, ':');
	if (ptr != (char *)NULL) {
#if DEBUG
		debug("SCANNING FOR A PORT %s\n", ptr);
#endif
		/*
		 * SMM v1.06b
		 * FOR PROXY TESTING... THERE WILL BE ANOTHER
		 * COLON ':' BUT IT'LL BE FOR THE http: REQUEST
		 * SO CHECK THIS BY MAKING SURE THERE IS NO PORT #
		 */
		if (sscanf(ptr, ":%d", &port) != 0) eob = ptr;
	}
#if DEBUG
	debug("GOT PORT: %d\n", port);
#endif
	dir = (char *)strchr(machine, '/');

	if (dir) {
		/*
		 * CHECK FOR A PROXY REQUEST
		 */
		if (strncmp(dir, "/http://", 8) == 0) {
			if (eob == 0) eob = dir;	
			dir++;		/* SKIP LEADING SLASH */
#if DEBUG
			debug("PROXY REQUEST for %s\n", dir);
#endif
		}
		newdir = (char *)malloc(strlen(dir) + 1);
		strcpy(newdir, dir);
#if DEBUG
		debug("DIRECTORY = %s\n", newdir);
#endif
		*dir = '\0';
	}
	/*
         * From: Doug White <dwhite@gdi.uoregon.edu>
	 */
	else  /* no dir was specified, default to / */
	{
#if DEBUG
		debug("NO DIR\n");
#endif
		newdir = (char *)malloc(2);
 		strcpy(newdir, "/");
	}

	if (eob) *eob = '\0';		/* NOW GET MACHINE */

	if((ip.s_addr = inet_addr(machine)) == -1) { /* IS IT IN DOT FMT */
		if (*machine == '\0') {
#if DEBUG
			debug("BOX IS LOCAL\n");
#endif
			ho = gethostbyname("localhost");
		}
		else
			ho = gethostbyname(machine);

		if (ho == (struct hostent *)NULL) {
			if( noerrors != 1 ) {
				sprintf(errormsg,"Unknown host: %s",machine);
				bb_errmsg(NULL,errormsg);
			}
			return(1);	/* NO HOST */
		}
		/*
		 * IF WE MAKE IT HERE, WE HAVE TO COPY THE HOST IP
		 * INTO THE ADDRESS STRUCTURE. IT'S SAVED IN A CHAR* ...
		 */
		memcpy(&ip.s_addr, ho->h_addr, ho->h_length);
	}
	dir=newdir;			/* SET DIRECTORY */
	return(0);			/* ALL IS WELL */
}

int bnetsend(input)
char *input;
{
 	int n, sockfd, noinput(), timeout();
        struct sockaddr_in serv_addr;
	int stopwriting=0;
	char line[MAXLINE+1];
	char *lineptr;


        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = ip.s_addr;
        serv_addr.sin_port = htons(port);

	/*
	 * THIS IS A LITTLE NASTY
	 * IF THE SERVER'S NOT UP, THIS WILL WAIT A LONG TIME BEFORE
	 * IT TIMES OUT.  SO HANDLE THAT HERE... IT GETS 3 TRIES TO 
	 * ANSWER, 3, 5, AND 12 SECONDS RESPECTIVELY
	 *
	 * SMM 5 OCT 2001 - SET THE STOPWATCH BEFORE THE setenv ELSE
	 * IT GETS CALLED AGAIN WHEN jmpbuf HITS.  THE SECOND CALL TO
	 * stopwatch() STOPPED THE WATCH AND PRINTED THE ELAPSED TIME
	 * Seconds: 3
	 */

#ifdef GETTIMEOFDAY
	stopwatch();			/* START THE CLOCK */
#endif
	loopcnt = 0;	
	timer = timer1;

#ifdef SIGSETJMP
 	if (sigsetjmp(jmpenv,1) != 0) {
#else
 	if (setjmp(jmpenv) != 0) {
#endif
#if DEBUG
                debug("SETTING TIMER TO %d\n", timer);
#endif
		if (timer == 99) return(99);
        }
        signal(SIGALRM, (void *)timeout);
        alarm(timer);

        /* OPEN SOCKET */
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		if( noerrors != 1 ) {
			bb_errmsg(NULL,"Can't open socket");
			fflush(stderr);
		}
		return(2);
        }


        if (connect(sockfd, (struct sockaddr *) &serv_addr,
                        sizeof(serv_addr)) < 0) {
		if (noerrors != 1) {
			sprintf(errormsg,"Can't connect to server %s on port %d",machine,port);
			bb_errmsg(NULL,errormsg);
		}
		/*
		 * THANKS TO: Douwe Dijkstra <D.Dijkstra@Twinfo.nl>
		 */ 
		close(sockfd);
		return(2);
        }

	if( !silent ) {
		/*
		 * HTTP REQUESTS ARE SPECIAL
		 * PROPER HEAD PROTOCOL NOW INCLUDED...
		 * Thanks: Don Sullivan (dsullivan@gaia.arc.nasa.gov)
		 * EXTRA \r FOR Brian Lenihan <brianl@real.com> AND ROXEN
		 * VIRTUAL WEB SERVER SUPPORT FROM: Gabriel Wachob <gwachob@findlaw.com>
		 */
		if (http) {
			/* Added User-Agent */
			/* Thanks to Carl Privitt <carlp@pobox.com> */
			/* Added port in Host: HTTP message header: Christian Perrier <perrier@onera.fr> */
			if (dir)
				sprintf(line, "HEAD %s HTTP/1.0\r\nUser-Agent: BigBrother/%s\r\nHost: %s:%d\r\n\r\n", dir,BBREL,machine,port);
			else
				sprintf(line, "HEAD / HTTP/1.0\r\nUser-Agent: BigBrother/%s\r\nHost: %s:%d\r\n\r\n", BBREL,machine,port);
		}
		/*
 		 * Thanks to Jon Lewis <jlewis@inorganic5.fdt.net>
		 */
		else {
			sprintf(line, "quit\r\n");
#if DEBUG
			debug("We have default: %s\n", line);
#endif
		}
		if (input) {
			if (strlen(input)) {
				strncpy(line, input, MAXLINE -5);
				strcat(line, "\r\n");
#if DEBUG
				debug("We have input: %s\n", line);
#endif
			}
			else {	/* THIS IS TO ALLOW NULL INPUT */
				line[0] = '\0';
#if DEBUG
				debug("NULL INPUT\n");
#endif
			}
		}

        	n = send(sockfd, line, strlen(line),0);

		line[0]='\0';

		/* FIRST TIME AROUND - 3 SECOND DELAY */
		loopcnt = 0;	
		timer = timer1;

#ifdef SIGSETJMP
 		if (sigsetjmp(jmpenv,1) != 0) {
#else
 		if (setjmp(jmpenv) != 0) {
#endif
#if DEBUG
                	debug("SETTING TIMER TO %d\n", timer);
#endif
			if (timer == 99) {
				close(sockfd);
				return(99);
			}
        	}
        	signal(SIGALRM, (void *)timeout);
#if DEBUG
		debug("PAUSE FOR RETURN\n");
#endif
		/* Give a little more time for HTTP requests */
		if(http) {
			if(timer < 10) {
				timer = 10;
			}
		}
        	alarm(timer);

		while ( (n = recv(sockfd, line, MAXLINE, 0)) > 0 ) {
			/*
			 * SMM - 1.09
			 * SOME DAEMONS DON'T RETURN DATA PROMPTLY
			 * DON'T SCREAM, BUT DO NOTE IT AS A MESSAGE
			 */
			alarm(0);
			loopcnt = 0;
			timer = timer1;
			/* Give a little more time for HTTP requests */
			if(http) {
				if(timer < 10) {
					timer = 10;
				}
			}
#ifdef SIGSETJMP
	 		if (sigsetjmp(jmpenv,1) != 0) {
#else
 			if (setjmp(jmpenv) != 0) {	/* RETURN OK BUT INCLUDE MESSAGE */
#endif
				printf("*** bbnet: Stop waiting for server data\n");
				close(sockfd);
				return(0);		/* PRETEND IT'S OK */
        		}

			if (n > 0)
				 line[n]='\0';/* Shun-Jee Liu <liu@hermes.com.tw> */
			/*
 			 * SMM - 26 DEC 97
 			 * SOME TESTING SHOULDN'T BE DISPLAYED 'CAUSE IT'S GROSS
 			 * THANKS TO: Per.E.Berger@telia.se
 			 */
			if (port == 23) {
				puts("Telnet test OK");
#ifdef GETTIMEOFDAY
				stopwatch();		/* STOP THE CLOCK */
#endif
				/* Missing close(sockfd) here and elsewhere */
				/* Thanks to John Horne <J.Horne@plymouth.ac.uk> */
				close(sockfd);
				return(0); 
			}
			else {
				/*
				 * SMM - 4 OCT 2001
				 * DOESN'T WORK ON http://www.yahoo.com
				 * IT WAS ALL SORT OF BROKEN... FIXED
				 */
				if(http) {
					if(!stopwriting) {
						/* Don't use more than the header from the HEAD request */
						/* Thanks to Patrick.Debois@sos.be */
/* SMM
						lineptr=(char*)strstr(line,"\n\n");
						if (lineptr != (char *)NULL) {
							lineptr += 2;
							*lineptr = '\0';
						}
*/
						lineptr=(char*)strstr(line,"\r\n\r\n");
						if (lineptr != (char *)NULL) {
							lineptr += 4;
							*lineptr = '\0';
						}
						n = strlen(line);
						if (lineptr) stopwriting = 1;
					}
				}
				/* 
				 * SMM - 5 OCT 2001
				 * ONLY STOP WRITING OUTPUT IF A SERVER
				 * IGNORES THE HEAD REQUEST AND SENDS THE
				 * WHOLE PAGE.  THANK www.yahoo.com FOR THIS
				 * STOPWRITING=0 NOT HTTP, 1 DO IT ONCE AND STOP
				 */
				if( stopwriting <= 1) {
					write(1, line, n); 	/* WRITE TO STDOUT */
					if (stopwriting == 1) stopwriting = 2;
#ifdef OLDWAY
					printf("%s",line);		/* DUMP OUT THE LINE */
#endif
				}
			}
#if DEBUG
			debug("RESET PAUSE FOR RETURN\n");
#endif
        		alarm(timer);
		}
	} /* !silent */

	close(sockfd);

#ifdef GETTIMEOFDAY
	stopwatch();				/* STOP THE CLOCK */
#endif
	return(0);				/* RETURN OK */
}

/*
 * timeout
 * HANDLE TIMEOUTS WHEN TALKING TO OUR FRIENDS...
 * START AT 3 - THEN GO TO 5 SEC, 12 SEC, then DIE
 */
int timeout()
{
	loopcnt++;
	if (loopcnt == 1) timer = timer2;
	else if (loopcnt == 2) timer = timer3;
	else {
		timer = 99;
		if( noerrors != 1 ) {
			sprintf(errormsg,"Timeout with server %s on port %d",machine,port);
			bb_errmsg(NULL,errormsg);
		}
	}
#if DEBUG
	debug("RESET TIMER TO %d\n", timer);
#endif

#ifdef SIGSETJMP
	siglongjmp(jmpenv,1);
#else
	longjmp(jmpenv,1);
#endif
}

