
/*********************************************************************
 *                                                                   *
 *     STiK : Ping Network Tool                                      *
 *                                                                   *
 *                                                                   *
 *      Version 0.1                        from 13. April 1999       *
 *      Version 0.2                        from 24. July  2000       *
 *		-recompiled for new .H files								 *
 *																	 *
 *   Initial Author:  Dan Ackerman - baldrick@netset.com			 *
 *                                                                   *
 *********************************************************************/

/* If the define below is enabled then ping will operate via 
 * the RAW interface entirely.
 *
 * When it is disabled, it will use ICMP send to send the ping
 * packets
 */


/*
#define RAW
*/

#include <aes.h>
#include <tos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <drivers.h>
#include <transprt.h>

#define CON 2

/* These definitions are necessary.  transprt.h has external
 * declarations for them.
 */
DRV_LIST *drivers = (DRV_LIST *)NULL;
TPL *tpl = (TPL *)NULL;
CONFIG *cfg = (CONFIG *)NULL;

/* Ping variables */

int16 socket;  /* Our connection handle */

unsigned long dest; /* Ping Destination IP   */

int		total; /* Ping Packets to send	*/
int 	sent;  /* Ping Packets sent 		*/
int		recvd; /* Ping Packets recieved */ 
	
long	max;	 /* max ping reply time in ms 		*/
long	min;	 /* min ping reply time in ms 		*/
long	ave;	 /* average ping reply time in ms */

int		seq;   /* Ping Packet sequence  */

int 	quiet = 0; /* if quiet true more info is printed to screen */

#define  ICMP_ECHO_REPLY      0
#define  ICMP_ECHO            8

int16 ping_work (char *, int);
int16	send_echo(void);
int16	send_recho(void);
long 	fetch_clock (void);
int16 process_ping(void);
void 	ping_stat (void);


extern uint16 calc_sum(char *, uint16, uint16);

uint32    our_ip;
char 	  pingstr[200];


#ifdef RAW
typedef 
struct icmp_pkt
{
        struct ip_header ip;
        struct icmp_header icmp;
        char data[32];
} pkt;
#endif

/* time def's from ansi */

struct timeval {
	long	tv_sec;		/* seconds */
	long	tv_usec;	/* microseconds */
};

struct timezone {
	int	tz_minuteswest;	/* minues west of GMT */
	int	tz_dsttime;	/* daylight savings time correction */
};


/* Standard C gettimeofday routine */

int
gettimeofday (tv, tz)
	struct timeval *tv;
	struct timezone *tz;
{
	time_t ticks = clock ();

	tv->tv_sec  = ticks / CLK_TCK;
	tv->tv_usec = ((ticks % CLK_TCK)*1000*1000) / CLK_TCK;
	
	return(1);
}

/* Function to wait for a keypress	 - really cheap but it works */

static void pause(void)
{
	Cconws("\r\nPress any key to continue...\r\n"); /* Just like DOS :-)	*/

	while (Bconstat(CON))
		;
	Bconin(CON);
}


/* This routine verifies the data, resolves the destination
 * and opens a RAW socket.  A RAW socket is used since then
 * we can have as many pings as we want listening.  It also 
 * simplifies the retrieval of data.
 *
 * The RAW interface is one of the more interesting aspects
 * of STiK2
 */

int16 
ping_work(char *host, int packets)
{
  char *realhostname;
  
#ifdef RAW
  CIB *connection;
#endif				

  if (host == (char *)NULL || packets == 0)
  		{
			Cconws("You must define a Host and a Number of Ping Packets! \r\n");
        	return(-1);
		}

	if (resolve(host, &realhostname, &dest, 1) < 0) 
		{
			Cconws("Can't Resolve \r\n");
			return (-1);
		}

	if ((socket = RAW_open(dest)) < 0)
		{
			Cconws("Can't Open Raw \r\n");
			return (-1);
		}

#ifdef RAW
	/* Set IP Header include socket option */
	CN_setopt(socket, IP_HDRINCL, NULL, 0);
	
	/* Get the Connection Information Block for our connection */
	connection = (CIB *)CNgetinfo(socket);
	
	/* Get our IP address from the CIB */
	our_ip = connection->lhost;
	
#endif

	Cconws("Ping host ");
	Cconws(realhostname);
	Cconws("...  \r\n");

	return(1);
}

/* PING packet msg structure */

typedef struct msg_data{
    uint8   ttl;            /*  Time to live                                */
    uint16  id;             /*  Packet identifier (For ping etc.)           */
    uint16  seq;            /*  Packet sequence (For ping etc.)             */
	uint16	extra1;			/*  extra uint16 number 1 - for later use		*/
	uint16  extra2;
	uint16  extra3;
  	char 	*msg;			/*  message for ICMP send */
}MSG_DATA;

/* returns the current value of the clock as a long */

long  fetch_clock(void)
{
   return (* (long *) 0x4baL);
}


/* This routine sends the ICMP packet, using the ICMP_send call
 * 
 * Note the ICMP_send call is not compatible with STiNG's ICMP_send
 *
 * It would have involved alot of internal work on STiK, and since
 * there probably will never be a big rush of writing ICMP clients
 * I decided my time was better spent elsewhere - Dan
 */


int16 
send_echo(void)
{
  MSG_DATA ping_msg;
  int16 x;
  long ptime;
  char junk[10];

  if (!quiet)
	  Cconws("Sending Ping \r\n");

  ping_msg.ttl = 255;            /*  Time to live                                */
  ping_msg.id  = (uint16)socket;        /*  Packet identifier (For ping etc.)           */
  ping_msg.seq = (uint16)seq++;       /*  Packet sequence (For ping etc.)             */
  ping_msg.extra1 = 0;			/*  extra uint16 number 1 - for later use		*/
  ping_msg.extra2 = 0;
  ping_msg.extra3 = 0;

 ptime = (long)clock();

 sprintf(ping_msg.msg,"%ld",ptime);

  if((x = ICMP_send(dest, 8, 0, &ping_msg, (uint16)32))<0)
  {
 	Cconws("We have an error\r\n");
	Cconws (itoa(x,junk,10));
  }	

  return(x);
}

#ifdef RAW

/*  calc_checksum()
 *
 *      This is the checksum routine (Taken from RFC1071) 
 *  It is needed if you recompile to run ping via the RAW interface
 *
 */

uint16 
calc_sum(char *p, uint16 initial, uint16 count)
{
    register int32 sum = initial;
    register uint16 *addr = (uint16 *)p;

    if ((long)p % 2) {
        sum = *p;
        addr = (uint16 *)&p[1];
        count -= 1;
    }

    while (count > 1) {
        sum += *addr++;
        count -= 2;
    }

    if (count > 0)
        sum += (((uint16)(*(uint8 *)addr))<<8); 

    while (sum >> 16)
        sum = (sum & 0xffff) + (sum >> 16);


    return ((uint16) ~sum & 0xffff);
}

/* This routine sends the ping packet via the RAW_out commands
 *
 * Here we have total control over the packet, the RAW commands
 * are usefull for prototyping new protocols and exploits.
 */

int16 
send_recho(void)
{
  int16 x;
  char junk[10];
  struct icmp_pkt ping_pkt;

  if (!quiet)
    Cconws("Sending Ping\r\n");

  memset(&ping_pkt, 0, sizeof(pkt));

  /* Fill in ip header */
  ping_pkt.ip.ver = 4;
  ping_pkt.ip.ihl = 5;
  ping_pkt.ip.len = (int16)sizeof(pkt);
  ping_pkt.ip.id = 0x455;
  ping_pkt.ip.ttl = 255;
  ping_pkt.ip.ptcl = IPPROTO_ICMP;
  ping_pkt.ip.s_ip = our_ip;
  ping_pkt.ip.d_ip = dest;
  ping_pkt.ip.ofst = 0x0000;        /* Don't fragment */

  /* fill in icmp header */
  ping_pkt.icmp.type = 8; 				/* Echo Request 			*/
  ping_pkt.icmp.code = 0;
  ping_pkt.icmp.sum = 0; 				/* at least atm 			*/
  ping_pkt.icmp.id	= socket; 			/* Cheap but only we own it */
  ping_pkt.icmp.seq = seq++;

  sprintf(ping_pkt.data,"%ld",(long)clock());

  ping_pkt.icmp.sum = calc_sum((char *)&ping_pkt.icmp, 0, (uint16)(32 + sizeof(ICMP_HDR)));
  
  ping_pkt.ip.sum = 0;
  ping_pkt.ip.sum = calc_sum((char *)&ping_pkt.ip, 0,(uint16)sizeof(IP_HDR));

  if((x = RAW_out(socket, &ping_pkt, (int16)sizeof(pkt), dest))<0)
  {
  	Cconws("We have an error\r\n");
	Cconws (itoa(x,junk,10));
	pause();
  }	

  return(x);
}

#endif /* ifdef RAW */


/* This routine retrieves the next packet from the queue
 * and processes it.  It also sends the next packet out.
 */

int16 
process_ping(void)
{
	IP_HDR *iph;
	ICMP_HDR *icmph;
	char *data;
	long pkt_time, delay, t;
	int16 hlen;
	NDB *packet;

	packet = CNget_NDB(socket); /* Get the packet from the queue */

	iph = (IP_HDR *)packet->ndata; /* Get the ip header */

    hlen = iph->ihl * 4; /* Number of octets in ip header */

	icmph = (ICMP_HDR *)&packet->ndata[hlen]; /* Get the icmp header */

    data = (char *)&icmph[1]; /* The pointer to our data */

	pkt_time = atol(data); /* convert it to a long */
 
 	/* check if it's ours */
 	if(icmph->id == socket)
 	{
		if (icmph->type == 0) /* ICMP ECHO REPLY */
		{
			/* get round trip time */

			t = (long)clock();

		    delay = (long)((long)t - (long)pkt_time);

			/*  check if it's the fastest */
	
		  	if ((min > delay) || (sent == 1))
  				min = delay;
			  				
		  	/*  check if it's the longest */
		  				
	  	 	if (max < delay)   
 				max = delay;
		  	 				
	  	 	/* compute the average time */
		  	 				
	  	 	ave += delay;

			sprintf(pingstr," Reply received seq: %d  ttl: %d  trip time: %ld ms\r\n",
									icmph->seq,iph->ttl,delay);
				
			if (!quiet)
			  Cconws(pingstr);

			sprintf(pingstr,"     min: %ld ms  max: %ld ms ave: %ld ms\r\n\r\n",
								min,max,(long)(ave / recvd));
				
			if (!quiet)
			  Cconws(pingstr);

		  	recvd++;
 
			if ((sent) < ((total) - 1))
			{
#ifdef RAW
				if(send_recho() == 0)
#else
				if(send_echo() == 0)
#endif
				{
					sent++;
				}
			}
		}
		else
		{
			if (!quiet)
				Cconws("Not an ICMP reply packet\r\n");
		}
	}
	else
	{
		if (!quiet)
			Cconws("It's not showing up as ours\r\n");
	}
		
	/* delete the NDB, we don't need it anymore */

	if (packet->ptr)           /* packet->ptr will never be NULL, though */
		KRfree(packet->ptr);
			
	KRfree((char *)packet);

	return (TRUE);
}

/* End of program statistics */

void 
ping_stat(void)
{
	Cconws("\r\n--- ping statisics ---\r\n");

	sprintf(pingstr," %d packets transmitted\r\n",sent+1);
	Cconws(pingstr);

	sprintf(pingstr," %d packets received\r\n",recvd);
	Cconws(pingstr);

	if (recvd > (sent+1))
		Cconws("--- somebody's printing up packets!\r\n");
	else
	{
		sprintf(pingstr," %d%% packet loss\r\n",(int)((((sent+1) - recvd) * 100) / sent));
		Cconws(pingstr);
	}

	sprintf(pingstr,"       min: %ld ms  max: %ld ms ave: %ld ms\r\n",
							min,max,(ave / recvd));
				
	Cconws(pingstr);
}



/* Put 'STIK' cookie value into drivers 
 * 
 * This is fetching the addresses of the STiK routines
 */

typedef struct {
    long cktag;
    long ckvalue;
} ck_entry;

static long init_drivers(void)
{
	long i = 0;
	ck_entry *jar = *((ck_entry **) 0x5a0);

	while (jar[i].cktag) {
		if (!strncmp((char *)&jar[i].cktag, CJTAG, 4)) {
			drivers = (DRV_LIST *)jar[i].ckvalue;
			return (0);
		}
		++i;
	}
	return (0);	/* Pointless return value...	*/
}

/* Init the drivers and print opening message */

static int initialise(void)
{
	static long init_drivers(void);

	Supexec(init_drivers);

	/* See if we got a value	*/

	if (drivers == (DRV_LIST *)NULL) {
		Cconws("STiK is not loaded\r\n");
		return (FALSE);
	}

	/* Check Magic number	*/

	if (strcmp(MAGIC, drivers->magic)) {
		Cconws("Magic string doesn't match\r\n");
		return (FALSE);
	}

	/* OK, now we can get the address of the "TRANSPORT" layer
	 * driver.  If this seems unnecessarily complicated, it's
	 * because I tried to create today, what I would like to
	 * use later on.  In future, there will be multiple
	 * drivers accessible via this method.  With luck, your
	 * code will still work with future versions of my software.
	 */

	tpl = (TPL *)get_dftab(TRANSPORT_DRIVER);

	if (tpl == (TPL *)NULL) 
	{
		Cconws("Transport layer *not* loaded\r\n");
		return (FALSE);
	}

	cfg = (CONFIG *)drivers->cfg;
		
	Cconws("ping 0.2 - a STiK2 example client \r\n");
	Cconws("\r\n");
	Cconws("   Transport layer loaded\r\n");
	Cconws("   Author ");
	Cconws(tpl->author);
	Cconws(", version ");
	Cconws(tpl->version);
	Cconws("\r\n");

#ifdef RAW
  Cconws("\r\nThis version compiled for full RAW sockets interface\r\n\r\n");
#else
  Cconws("\r\nThis version compiled to use ICMP_SEND\r\n\r\n");
#endif	

	return (TRUE);
}

/* main routine
 *  parses arguments, and handles the ping process
 */

void 
main(int argc, char *argv[])
{
	int a = 2;
	int done = 0;
	int16 ret = 0;
	time_t next;
	char junk[10];
	
	Cconws("\033E");

	if (initialise())
	{		
		if (argc < 2)
		{ 
	   		Cconws("ping(0.1) use: ping site [ -q] [-c Number of packets] \r\n");
	   		Cconws("example: ping irc.stealth.net -c 10\r\n\r\n");
	   		Cconws(" -q run in quiet mode \r\n");
#ifdef RAW
			Cconws("\r\nThis version compiled for full RAW sockets interface\r\n");
#else
			Cconws("\r\nThis version compiled to use ICMP_SEND\r\n");
#endif					
	   	}
		else
		{
			/* We say 2 intead of 0 since we will always have the host */

			total = 10; /* default number of packets to send */
					
			while (argc > 2) 
			{
				if (strncmp("-c",argv[a],strlen("-c"))==0)
				{
					if((a++) >= (argc - 1))
					{
						Cconws(" Not enough parameters -c\r\n");
						exit(-1);
					}
					total = atoi(argv[a]);
				}			
				else if (strncmp("-q",argv[a],strlen("-q"))==0)
				{
					quiet = 1;
				}	
				else
				{
					fprintf(stdout,	"ping: %s: unknown option\n", argv[a]);
				}

				if((a++) >= (argc - 1))
					break;	
			}

			if (ping_work(argv[1], total) == -1) /* Open handle for use & resolve host */
				goto done;

#ifdef RAW
			send_recho(); /* send our first ping packet */
#else
			send_echo(); /* send our first ping packet */
#endif

			next = time( NULL ); /* init our timeout value in case a packet gets lost */
			next += 5;			 /* retransmit if no reply after 5 seconds (decent value?)*/

			do {
			    if ((ret = CNbyte_count(socket))>0)
			   	{
			         process_ping();
       		         next = time( NULL ) + 5;
       		     }
				else if (ret < 0)
				{
				  	Cconws("We have an error\r\n");
					Cconws (itoa(ret,junk,10));
					
					goto close_it;
				}

				if ( time( NULL ) > next )
				{
					next = time( NULL ) + 5;
	
					#ifdef RAW
						send_recho(); /* send a new ping packet - might have lost one */
					#else
						send_echo(); /* send a new ping packet - might have lost one */
					#endif
				}

				if (recvd == total) /* We've gotten our total of packets back */
					done = 1;	
						
			} while(!done);						


close_it:
			RAW_close(socket); /* close our handle it's not needed anymore */
					
			ping_stat(); /* display our statistics */			
		
		}
	}

done:
	pause();
}
