/****************************************************************
 *    NetQuery.c      Network Management Interface              *
 *                                                              *
 *      This is where you can add services to exercize          *
 * Modification History						*
 * 08/08/89 - Added AppleTalk testing	@wbn1			*
 ****************************************************************/
/* 
  * Copyright 1989
  * The Regents of the University of Michigan
  * All Rights Reserved
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted, provided
  * that the above copyright notice and this permission notice appear in
  * all copies of the software and derivative works or modified versions
  * thereof, and that both that copyright notice and this permission
  * notice appear in supporting documentation.
  *
  * The software is provided "as is" and the University of Michigan
  * disclaims all warranties with regard to this software, including
  * all implied warranties of merchantability and fitness.  In no event
  * shall the University of Michigan be liable for any special, direct,
  * indirect, or consequential damages or any damages whatsoever
  * resulting from loss of use, data or profits, whether in an action of
  * contract, negligence or other tortious action, arising out of or in
  * connection with the use or performance of this software.
  */

#include "netquery.h"
#include "Hostfileio.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/file.h>

/*#define DEBUG 1 /* */
/* Add services here  and update netquery.h */
static char *NetProto[]={"NONE","PING","SGMP","SNMP","TELNET",
		 "FTP","SMTP","NAMED","TROUBLE","Empty","ATPING","ATLOOK",
		"FINGER","GENERIC"};
#define MAX_NUMBER_OF_SERVICES (sizeof(NetProto)/sizeof(char *))

static char *NetStatus[]={"ITBROKE","ITWORKS","ITDEAD"};
#define MAX_NUMBER_OF_STATUS (sizeof(NetStatus)/sizeof(char *))
/*****************************************************************
 *    Forward Declarations of Network Management layer modules   *
 *****************************************************************/
static WORD TestPING();          /* Is a node Reachable - PING   */
static WORD TestSGMP();          /*          SGMP Query          */
static WORD TestSNMP();          /*          SNMP Query          */
extern WORD TestNAMED();         /* test Name Domain Services    */
extern WORD TestPORT();          /* connect to various ports     */
static WORD TestTROUBLE();	 /* See if mail is in TROUBLE-REPORT */
static WORD TestATPING();        /* Test Apple Talk Pingability  */ 
static WORD TestATLOOK();        /* Apple Talk GW Look for Node  */ 
static WORD TestGENERIC();	 /* Check if file exists */
				 /* Add modules Here to test services */
/*****************************************************************/

WORD NetService( Service )
char *Service;
{
char service[100];
int i=0;

   if (Service==NULL) return(NONE);
   for(i=0; isalpha(Service[i]); i++)
        service[i]= ( isupper(Service[i]) ? Service[i] : toupper(Service[i]) );
   service[i]='\0';

   for(i=0; i<MAX_NUMBER_OF_SERVICES; i++)
      if (strcmp(service,NetProto[i]) == 0 ) return(i);
   return(GENERIC);
}

/****************************************************************************
 *      NetQuery:     Network Entity Query using/testing services           *
 *		All modules should return ITWORK, ITBROKE, or ITERROR(fatal)*
 ****************************************************************************/
WORD NetQuery(addr,Test)
char *addr;                              /* Return 0 if it Doesn't Function */
struct TestType *Test;		 /* Network Management protocol */
{

    if ( Test == NULL ) panic(" NetQuery: NULL Test\n");
#ifdef DEBUG
   printf("NetQuery(addr=%s,service=%s) entering\n",
		addr,service);
   printf("netquery(): argv[0]=%s argv[1]=%s argv[2]=%s argv[3]=%s\n",
         Test->argv[0],Test->argv[1],Test->argv[2],Test->argv[3]);
#endif

   switch( NetService(  Test->argv[0] ) ) {
      case PING:   return(      TestPING( addr 			       ) );
      case SGMP:   return(      TestSGMP( 			       ) );
      case SNMP:   return(      TestSNMP(                              ) );
      case TELNET: return(      TestPORT( "telnet", addr               ) );
      case FTP:    return(      TestPORT( "ftp",    addr               ) );
      case SMTP:   return(      TestPORT( "smtp",   addr               ) );
      case FINGER: return(      TestPORT( "finger", addr               ) );
      case NAMED:  return(     TestNAMED( addr 			       ) );
      case TROUBLE:  return( TestTROUBLE(                              ) );
      case ATPING:    return(  TestATPING(        Test			) );
      case ATLOOK:    return(  TestATLOOK(        Test			) );
      case NONE:   return( ITWORKS );
      case ERROR:   
      case GENERIC: 
      default:
 		return(       TestGENERIC( Test ) );
   }
}

char *NetServiceString(index)
WORD index;
{
   if ( index > MAX_NUMBER_OF_SERVICES ) {
	/*fprintf(stderr,"Trying to print service #%d\n",index);*/
	return("ERROR");
   }
   return(NetProto[index]);
}

char *NetStatusString(index)
WORD index;
{
   if ( index > MAX_NUMBER_OF_STATUS ) return("ITERROR");
   return(NetStatus[index]);
}
/****************************************************************
 *	Init_netquery:	Opens error log, and saves destriptor	*
 *			in "nq_errlog", and int;		*
 ****************************************************************/
static int nq_errlog;
#define NQ_ERRLOG "/dev/null"
void init_netquery()
{
   nq_errlog = open( NQ_ERRLOG, O_RDWR|O_CREAT, 0666 );
   if ( nq_errlog < 0 )
	syserr( "Opening error log: " );
}

/****************************************************************
 *       TestPING:    Test to see if a node is up               *
 *                    Return 0 if node is DOWN                  *
 *                    Return Non-Zero if node is UP             *
 ****************************************************************/
static WORD TestPING( address )
char *address;          /* character string representing address      */
{
char	*newav[5];
extern char **environ;
int	pid;
union wait status;

#ifdef DEBUG
printf("netquery: TestPING: Address=%s\n",address);
#endif

#ifndef BUILTIN
   switch ( pid = vfork() ) {
	case -1 :	/* Fork failed */
		syserr( "vfork: ");
		/*NOTREACHED*/
	case 0 :	/* Child process */
		newav[ 0 ] = "ping (pingky)";
		newav[ 1 ] = address;
		newav[ 2 ] = "5";
		newav[ 3 ] = "5";
		newav[ 4 ] = NULL;
		dup2( nq_errlog, 0 );
		dup2( nq_errlog, 1 );
		dup2( nq_errlog, 2 );
		(void) close( nq_errlog );
#define PINGCMD "./ping"
		execve( PINGCMD, newav, environ );
		syserr( "execve: " );
		/*NOTREACHED*/
	default :	/* Parent process */
		if ( pid != wait( &status ) )
			fprintf( stderr, "Unknown child (pid=%d) returned to me!\n",pid );
		if ( status.w_retcode != 0 ) {
			return( ITBROKE );
		} else {
			return( ITWORKS );
		}
	}
#else
int newac;

	newav[ 0 ] = "ping (pingky)";
	newav[ 1 ] = address;
	newav[ 2 ] = "5";
	newav[ 3 ] = "5";
	newav[ 4 ] = NULL;
	newac = 5;
	return(ping( newac, newav ));

#endif
}

static WORD TestTROUBLE() /* Test MERIT TROUBLE MAIL */
{
char cmd[100];

   sprintf(cmd,"( rsh merit.edu -n 'ls /usr/spool/mail/down' | grep down 2>&1 ) >/dev/null");

#ifdef DEBUG
     printf("TROUBLE COMMAND: %s\n",cmd);
#endif
   if ( system(cmd) ) {
      return( ITWORKS );
   }
   else {
      return( ITBROKE ); 
   }
}

static WORD TestATPING( Test ) /*  AppleTalk Pinger @wbn1*/
struct TestType *Test;
{
char cmd[100];

   sprintf(cmd,"atpinger %s@%s | grep 'Okay'  >/dev/null",
		Test->argv[2],Test->argv[1]);
#ifdef DEBUG
     printf("ATPING COMMAND: %s\n",cmd);
#endif
   return( (system(cmd)) ? 0 : 1 );
}

static WORD TestATLOOK( Test ) /*  AppleTalk Look @wbn1*/
struct TestType *Test;
{
char cmd[100];

   sprintf(cmd,"atlook %s | grep '%s'  >/dev/null",
		Test->argv[1],Test->argv[2]);
#ifdef DEBUG
     printf("ATLOOK COMMAND: %s\n",cmd);
#endif
   return( (system(cmd)) ? 0 : 1 );
}

static WORD TestSGMP( ) 
{
   return( ITWORKS );
}

static WORD TestSNMP( ) 
{
   return( ITWORKS );
}

static WORD TestGENERIC( Test )
struct TestType *Test;
{
struct stat buf;


   if ( Test->argv[1] == NULL ) return(ITWORKS);
   if ( stat( Test->argv[1], &buf ) == 0 ) {
	/*printf("GENERIC TEST:%s: %s Exists! TEST FAILED\n",Test->argv[0],Test->argv[1]);*/
	return ITBROKE;
   }
   else {
	/*printf("GENERIC TEST:%s: %s not there SUCCESS\n",Test->argv[0],Test->argv[1]);*/
	return ITWORKS;	
   }
}


static WORD TestNAMED( addr )
char *addr;
{
char cmd[100];

	sprintf(cmd,"./ns %s",addr);
	if ( system(cmd) ) {
		return( ITBROKE );
	}
	else {
		return( ITWORKS );
	}
}

