/*
 * bbnet.c
 * BIG BROTHER NETWORK TESTING PROGRAM
 * Sean MacGuire - The MacLawran Group Inc.
 * Version 1.4
 * Mar 05th, 2000
 *
 * This program is Copyright (c) 1997-2000
 * The MacLawran Group Inc.
 * 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 */
#else
#include <sys/time.h>	/* FOR TIMING STUFF SMM */
#endif
#include <netdb.h>	/* FOR HOST INFO */

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 rc=0;		/* ERROR CODES */
char *dir;		/* DIRECTORY */
char *machine;		/* MACHINE NAME ONLY */

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

main(argc, argv)
int argc;
char *argv[];
{
	if (argc < 2 || argc > 3) {	/* ONLY 2 OR 3 ARGUMENTS ARE VALID */
		fprintf(stderr, "bbnet: incorrect number of arguments\n");
		fprintf(stderr, "Format: bbnet machine[:port]|http-request\n");
		exit(1);
	}
	if ((rc = setup(argv[1])) == 0) {	/* IF NO ERRORS DURING SETUP */
		if (argc==3) rc = bnetsend(argv[2]);	/* DO TEST */
		else rc = bnetsend((char *)NULL);
	}
	exit(rc);
}

/*
 * THIS FUNCTION SETS UP EVERYTHING WE NEED TO CALL THE PORT
 */
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;
		debug("We have an http request\n");
		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) {
		debug("SCANNING FOR A PORT %s\n", ptr);
		/*
		 * 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;
	}
	debug("GOT PORT: %d\n", port);
	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 */
			debug("PROXY REQUEST for %s\n", dir);
		}
		newdir = (char *)malloc(strlen(dir) + 1);
		strcpy(newdir, dir);
		debug("DIRECTORY = %s\n", newdir);
		*dir = '\0';
	}
	/*
         * From: Doug White <dwhite@gdi.uoregon.edu>
	 */
	else  /* no dir was specified, default to / */
	{
		debug("NO DIR\n");
		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') {
			debug("BOX IS LOCAL\n");
			ho = gethostbyname("localhost");
		}
		else
			ho = gethostbyname(machine);

		if (ho == (struct hostent *)NULL) {
			fprintf(stderr, "bbnet: Unknown host: %s\n", machine);
			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 */
}

bnetsend(input)
char *input;
{
 	int n, sockfd, noinput(), timeout();
        struct sockaddr_in serv_addr;

	char line[MAXLINE+1];


        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
	 */
	loopcnt = 0;	
	timer = 3;

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

        /* OPEN SOCKET */
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                fprintf(stderr, "bbnet: CAN'T OPEN SOCKET\n");
		return(2);
        }
        if (connect(sockfd, (struct sockaddr *) &serv_addr,
                        sizeof(serv_addr)) < 0) {
                fprintf(stderr, "bbnet: CAN'T CONNECT TO SERVER ");
                fprintf(stderr, "@ %s ON PORT %d\n", machine, port);
		/*
		 * THANKS TO: Douwe Dijkstra <D.Dijkstra@Twinfo.nl>
		 */ 
		return(2);
        }

#ifdef GETTIMEOFDAY
	stopwatch();			/* START THE CLOCK */
#endif

	/*
	 * 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> */
		if (dir) sprintf(line, "HEAD %s HTTP/1.0\r\nUser-Agent: BigBrother/%s\r\nHost: %s\r\n\r\n", dir,BBREL,machine);
		else sprintf(line, "HEAD / HTTP/1.0\r\nUser-Agent: BigBrother/%s\r\nHost: %s\r\n\r\n", BBREL,machine);
	}
	/*
 	 * Thanks to Jon Lewis <jlewis@inorganic5.fdt.net>
	 */
	else {
		sprintf(line, "quit\r\n");
		debug("We have default: %s\n", line);
	}
	if (input) {
		if (strlen(input)) {
			strncpy(line, input, MAXLINE -5);
			strcat(line, "\r\n");
			debug("We have input: %s\n", line);
		}
		else {	/* THIS IS TO ALLOW NULL INPUT */
			line[0] = '\0';
			debug("NULL INPUT\n");
		}
	}

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

	line[0]='\0';

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

#ifdef SIGSETJMP
 	if (sigsetjmp(env,1) != 0) {
#else
 	if (setjmp(env) != 0) {
#endif
                debug("SETTING TIMER TO %d\n", timer);
		if (timer == 99) return(99);
        }
        signal(SIGALRM, (void *)timeout);
	debug("PAUSE FOR RETURN\n");
        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
		 */
		loopcnt = 0;
		timer = 3;
#ifdef SIGSETJMP
	 	if (sigsetjmp(env,1) != 0) {
#else
 		if (setjmp(env) != 0) {	/* RETURN OK BUT INCLUDE MESSAGE */
#endif
			printf("*** bbnet: Stop waiting for server data\n");
			return(0);		/* PRETEND IT'S OK */
        	}
		debug("RESET PAUSE FOR RETURN\n");
        	alarm(timer);

		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
			return(0); 
		}
		else printf("%s",line);		/* DUMP OUT THE LINE */
	}
	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
 */
timeout()
{
	loopcnt++;
	if (loopcnt == 1) timer = 5;
	else if (loopcnt == 2) timer = 12;
	else {
		timer = 99;
		fprintf(stderr, "bbnet: TIMEOUT %s PORT %d...\n",machine, port);
	}
	debug("RESET TIMER TO %d\n", timer);

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

#ifdef GETTIMEOFDAY
/*
 * SMM FOR FREEBSD... PROBABLY ISN'T PORTABLE...
 * STOPWATCH - YEAH, IT'S WHAT YOU THINK...
 */
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 (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? */
		printf("\nSeconds: %ld.%02ld\n", sec, usec);
		stop=0;
	}
	else {					/* START THE WATCH */
		stop = 1;
		sec = ti->tv_sec;
		usec = ti->tv_usec;
	}
}
#endif
