/*
 *			P I N G . C
 *
 * This is yet another "pinger" program for Internet testing and Internet
 *    fault isolation using ICMP echoes.    It was (re-)written at the
 *    Information Sciences Institute of USC by Bob Braden, starting
 *    from a basic pinger program written by:
 * 
 *	            Mike Muuss
 *	            U. S. Army Ballistic Research Laboratory
 *	            December, 1983
 *
 * Status -
 *     	Public Domain.  Distribution Unlimited.
 *
 * Last update: 07Feb87  Bob Braden
 */

#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>

#include "histogram.h"

static HCBdefine(sumhcb, 100, 0, 300, "PINGER") ;
   /* Define a histogram area for 300 entries. Each bin is 100 ms, so
    *    RTTs beyond  30 seconds are lumped into highest bin.
    */
extern histogram(), histdisplay();

#define LOSS_LIM  5    /* Number of successive lost packets => black hole*/
#define MSPT 1        /* Clock resolution (milliseconds per tick) */
#define MAXPACKET	4096
u_char	packet[MAXPACKET];
u_char  outpack[MAXPACKET];
int	options;
extern	int errno;
char    time_buf[10] ; 
char    tod_buf[26] ; 

int s;			/* Socket file descriptor */
struct hostent *hp;	     /* Pointer to host info */
struct timezone tz;	     /* leftover */
struct timeval  startod, sendtod ; /* start time, last-send time */
struct timeval SIGINTime  ;

struct sockaddr whereto;    /* Who to ping */

char usage[] = 
  "Usage:  ping [-dData size] [-tInterval(ms)]  [-bBurst size]                 \
       [-rRTT threshold (ms)] [-fReport frequency] [-nNumber to send]          \
       [-hHistogram lines] [-e[raw]]  [-x]   Host \n";

char hostaddr[16];
char canhost[64] ;   /* canonical host name */
u_long resolve_name() ;
char *inet_ntoa();
extern u_short in_cksum() ;

int datalen = 64-ICMP_MINLEN ;
                        	/* Number data bytes after ICMP ECHO header */
int burst_size = 1 ;        /* Number to send in each burst (default = 1) */
int RTT_thresh = 0 ;        /* Only report if RTT exceeds this number */
int interval = 1000 ;       /* Pinging interval in ms. */
int echo_host = 0 ;         /* For echo host: send ECHO REPLY */
int RAW_echo = 0 ;          /* For echo host: send protocol 255 */
int send_limit = 99999999 ; /* Limit on number to send */
int hist_depth = 35 ;       /* Number of lines to use in histogram display */
int Report_freq = 0;        /* Reporting frequency (-f value) */
int Sample_freq = 0;        /* Sampling frequency  */

int ntransmitted = 1;		/* Sequence # for outbound packets = #sent */
int ident;
int count = 0 ;             /* 1-second count for sample display */
int nreceived = 0;		    /* Total # of packets we got back */
int nmangled = 0 ;          /* Count mangled packets */
int bits_differ = 0 ;       /* Count total bits mangled */

int is_fin = 0 ;            /* Termination-mode flag */
int fin_delay ;         	/* Time to delay before final statistics */

int last_Cum_loss = 0 ;
int last_secs = -1 ;        /* Elapsed seconds at last display */
int HWM_rcv_seq  = 65535;   /* High Water Mark of received sequence #s*/
int black_hole = 0 ;        /* Boolean: TRUE when no echoes are coming back*/
int loss_limit ;            /* Number of lost packets => black hole */
struct itimerval itim, otim ; /* time interval structures */

#define  MAXBURST   20
int btmin[MAXBURST] ;        /* Statistics for each position in burst */
int btmax[MAXBURST] ;
int btsum[MAXBURST] ;
int bnrecvd[MAXBURST] ;
	
int tmin ;                   /* Min RTT */
int tmax ;                   /* Max RTT */
int tsum ;			/* sum of all RTTs, for average */

int finish(), terminate(), catcher() ;
char *pr_type() ;
          
        /* Compute  A -= B for timeval structs A, B
         */
#define tvsub(A, B)   (A)->tv_sec -= (B)->tv_sec ;\
         if (((A)->tv_usec -= (B)->tv_usec) < 0) {\
                  (A)->tv_sec-- ;\
                  (A)->tv_usec += 1000000 ; }
                  
		/* Compute (u_short) A - B if A, B are 16-bits or less. 
		 */
#define u_shortsub(A, B) ((long) (A) > (B)) ? (u_short) ( (A)-(B) ) : \
                (u_short)  ((long) (A) + 65536 - (B))
	
                     
/*
 * 			M A I N
 */
main(argc, argv)
char *argv[];
{
	struct sockaddr_in from;
	char **av = argv;
	struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
	register int i ;
	register u_char *datap ;

	
	if( argc < 2)  {
		printf(usage);
		exit(1);
	}

	while (--argc > 0 && **++av == '-') {
	
		switch (*(1+*av)) {
		
		case 'x':
			options |= SO_DEBUG;
			break ;
			
		case 'd':
		    datalen = atoi( 2+*av );
			if( datalen > MAXPACKET )  {
				printf("ping:  packet size too big\n");
				exit(1);
			}
		    break ;
		    
		case 't':
			interval = atoi(2+*av) ;
			break ;
			
		case 'b':
			burst_size = atoi(2+*av) ;
			break ;
			
		case 'n':
			send_limit = atoi(2+*av) ;
			break ;
			
		case 'f':
			Report_freq = atoi(2+*av) ;
			break ;
			
		case 'r':
			RTT_thresh = atoi(2+*av) ;
			break ;
			
		case 'e':
			echo_host = 1 ;
			if (0 == strcmp(2+*av, "raw")) RAW_echo = 1 ;
			break ;
			
		case 'h':
			hist_depth = atoi(2+*av) ;
			break ;

  	        case 'v':
			printf("ignoring -v argument.\n");
			break ;

	        case 'l': 
			printf("ignoring -l argument.\n");
			break ;

		default:
			printf(usage) ;
			exit(1) ;
		}
	}
		/*
		 *  Validate parameters...
		 */
	if (interval < MSPT)  {
		printf("Using min clock resolution for interval: %d ms\n") ;
		interval = MSPT ;
	}
	if (burst_size > MAXBURST) {
		printf("Burst size max is %d\n", MAXBURST) ;
		exit(1) ;
	}
	if (Report_freq && Report_freq*interval < 1000) {
		printf("Specified report frequency too short\n") ;
		Report_freq = 1000/interval ;
	}
	loss_limit = (interval < 2000)? (2000*LOSS_LIM)/interval : LOSS_LIM ;
	Sample_freq = burst_size *(1000/interval) ; 
	  /* how many per second  ?? */
	
	if (argc == 0) {
		printf("Missing Host Name\n") ;
		exit(1) ;
	}

	       /*
                * berkeley ping compatibility
	        */

	if(av[1]) {
	       datalen = atoi( av[1] );
	       if( datalen > MAXPACKET )  {
		     printf("ping:  packet size too big\n");
		     exit(1);
	       }
	       if(av[2]) {
		     send_limit = atoi( av[2] );
	       }
	}

		/*
		 *  Convert host name/number to IP address
		 */
	to->sin_family = AF_INET;
	if ((to->sin_addr.s_addr = resolve_name( *av, canhost, 64) ) == 0)  {
		printf("ping: unknown host %s\n", *av);
		return;
	}
#ifdef sun
	strcpy(hostaddr, inet_ntoa(to->sin_addr));
#else
	strcpy(hostaddr, inet_ntoa(to->sin_addr.s_addr));
#endif
	
	gettimeofday(&startod, &tz) ;
	strcpy(tod_buf, ctime(&startod.tv_sec) ) ;

	printf("---PING Host= [%s]: %s echo %d data bytes\n   %s %s", 
		 hostaddr, (RAW_echo)? "Raw": "ICMP", datalen,
	     canhost,  tod_buf);
	    
	if (datalen < sizeof(struct timeval)) {
		printf("NOTE:  Data length reset to min = 8 bytes\n") ;
		datalen = sizeof(struct timeval) ;
	}
	
    	/* 
         * Generate data in send packet buffer 
         */
	datap = &outpack[ICMP_MINLEN+sizeof(struct timeval)];
   	for( i= sizeof( struct timeval); i<datalen; i++)	/* skip time */
		*datap++ = i;

	setlinebuf( stdout );

	for (i = 0;i < MAXBURST; i++) {
		btmax[i] = btsum[i] = bnrecvd[i] =  0;
		btmin[i] = 99999999 ;
		}
	tmin = 99999999 ;
	tmax = tsum = 0 ;
		
		/* 
		 * Set up interval timer  structure
		 */
	itim.it_interval.tv_sec = itim.it_value.tv_sec = interval/1000 ;
	itim.it_interval.tv_usec = itim.it_value.tv_usec = 
		(interval%1000)*1000 ;

		/*
		 *  Use process ID as ICMP identifier
		 */
	ident = getpid() & 0xFFFF;

	if ((s = socket(AF_INET, SOCK_RAW, (RAW_echo)? 255 : IPPROTO_ICMP)) < 0)
	    {
		perror("ping: socket");
		exit(1) ;
	}
	
		/*
		 *  Read clock for start time and start pinging...
		 */
	gettimeofday(&startod, &tz) ;
	sendtod = startod ;    
	setitimer(ITIMER_REAL, &itim, &otim) ; /* Start interval timer */
	signal( SIGALRM, catcher );
	signal( SIGINT, terminate );

	for (;;) {
			/* 
			 * MAIN LOOP ... waiting to receive packets
			 */
		int len = sizeof (packet);
		int fromlen = sizeof (from);
		int cc;
		
		if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
			if( errno == EINTR )
				continue;
			perror("ping: recvfrom");
			continue;
		}
		if (RAW_echo)
			pr_pack( &packet[sizeof(struct ip)], cc-sizeof(struct ip), &from );
		else
		  /*	pr_pack( packet,  cc, &from ); */
			pr_pack( &packet[sizeof(struct ip)], cc-sizeof(struct ip), &from );
	}
	/*NOTREACHED*/
}

/*
 * 		    	C A T C H E R
 * 
 *     Catch interval timer expiration.
 *
 * This routine causes another PING (or burst of pings) to be transmitted,
 * and schedules another SIGALRM .  However, if we are in finish-up mode,
 * just call finish() to print statistics.
 * 
 */
catcher()
{
	int i ;
	
	if (is_fin) {
		if (!finish() )   {
			    /* It is possible (and in fact very likely, if interval is
			     * down near the minimum timer granularity, 20 ms) that the
			     * timer will go off and a SIGALRM will be queued while we 
			     * are processing SIGINT.  There is
			     * no way (?) to kill the SIGALRM that immediately follows
			     * exit from SIGINT, even though the SIGINT processing
			     * did a new setitimer() call.  Therefore, finish() checks
			     * for a long-enough delay, and if not returns immediately
			     * the value 0.  In that case, we activate the timer alarm
			     * again and wait again for the real timeout.
			     */
			signal( SIGALRM, catcher );
		}
	} 	
	else {
		signal( SIGALRM, catcher );
		for (i=0; i< burst_size;i++) {
			if (send_limit-- > 0) pinger(i);
			else {
				/* Reached limit on number to send. Pretend a ^C came */
				terminate() ;
				break ;
			}
		}
	}
}

/*
 * 			P I N G E R
 * 
 * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is our UNIX process ID,
 * and the sequence number is an ascending integer.  The first 8 bytes
 * of the data portion are used to hold a UNIX "timeval" struct in VAX
 * byte-order, to compute the round-trip time.
 *
 */

pinger()
{
	register struct icmp *icp  ;
	register int i ;
	int cc, outstand;
	struct timeval *tp = (struct timeval *) &outpack[ICMP_MINLEN];

	icp = (struct icmp *) outpack ;
	icp->icmp_type = (echo_host)? ICMP_ECHOREPLY : ICMP_ECHO;
	icp->icmp_code = 0;
	icp->icmp_cksum = 0;
	icp->icmp_seq = ntransmitted++;
	icp->icmp_id = ident;		/* ID */

	cc = datalen + ICMP_MINLEN ;		/* total ICMP packet length */
	gettimeofday( &sendtod, &tz );
	bcopy( &sendtod, tp, sizeof(struct timeval)) ;

	/* Compute ICMP checksum here */
	icp->icmp_cksum = in_cksum( icp, cc );

	/* cc = sendto(s, msg, len, flags, to, tolen) */
	i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );

	if( i < 0 || i != cc )  {
		if( i<0 )  perror("sendto");
		printf("ping: wrote %d chars, ret=%d\n",
			 cc, i );
		fflush(stdout);
	}
	   /* Test for a black hole...  This test is delicate.  We must 
	    *   consider the number of packets which may be in the pipeline,
	    *   which depends upon the RTT and the interval.  To bound the
	    *   RTT, we could use the largest RTT we have seen so far...
	    *   but a single spuriously large RTT could upset that completely.
	    *   So we use min(2*avg-RTT, max-RTT).
	    */ 
	if (nreceived-nmangled > 0) { 
		i = 2*tsum/(nreceived - nmangled) ;
		outstand = ((i > tmax) ? tmax : i)/interval ;
	}
	else 
		outstand = tmax/interval ;
	i = u_shortsub( ntransmitted & 0xFFFF, HWM_rcv_seq) - 2 ;
	if (!black_hole &&  (i > (loss_limit + outstand)) )
		{
		black_hole = 1 ;
		tvsub(&sendtod, &startod ) ;
		format_time( (sendtod.tv_usec > 500000)? sendtod.tv_sec+1:
			                               sendtod.tv_sec ) ;
                     
		printf("[+%s]  BLACK HOLE for %d packets...\n", time_buf, i) ;
		}
}


/*
 *			P R _ P A C K
 *
 * Process incoming ICMP packet...
 *
 */
pr_pack( icp, cc, from )
register struct icmp *icp;  /* Ptr to ICMP header */
int cc;                     /* Length of ICMP packet */
struct sockaddr_in *from;
{
	register int  p;
	
	int mangled = 0 ;
	struct timeval tv, elapse_tv;
	struct timeval *tp ;
	int now_secs, triptime, Cum_loss ;

	
	if( icp->icmp_type != ICMP_ECHOREPLY )  {
		
		if ( icp->icmp_type == ICMP_REDIRECT )  {
			printf("!Redirect to gateway: %s\n", 
			         inet_ntoa(icp->icmp_hun.ih_gwaddr )) ;
			return ;
		}
			/* Unknown ICMP type.  Dump packet. */
#ifdef sun

		printf("%d bytes from %s: ", cc, inet_ntoa( from->sin_addr) );
#else  /* sun */

		printf("%d bytes from %s: ", cc, inet_ntoa( from->sin_addr.s_addr) );
#endif /* sun */
		printf("icmp type=%d (%s) code=%d\n",
			icp->icmp_type, pr_type(icp->icmp_type), icp->icmp_code );

		hex_dump(icp, (cc < 48)? cc : 48) ;
	}
		
	if( icp->icmp_id != ident )
		return;			/* 'Twas not our ECHO */
		
	gettimeofday( &tv, &tz );
	elapse_tv = tv ;
	tvsub( &elapse_tv, &startod) ;   /* Elapsed since start of run */
	now_secs = elapse_tv.tv_sec ;
	if (elapse_tv.tv_usec > 500000 ) now_secs++ ;	
	
	tp = (struct timeval *) &icp->icmp_dun ;  /* pt to xmit-time in packet*/
	tvsub( &tv, tp );          /* RTT for this packet */
	
	nreceived++;	
	Cum_loss = icp->icmp_seq  - nreceived ;
	if (black_hole) {
		/* No more black hole.... */
		black_hole = 0 ;
		format_time(now_secs) ;
		printf("[+%s]  ECHOING AGAIN...\n", time_buf) ;
	}
	
	triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
	histogram(&sumhcb, triptime) ;
	
    if (RAW_echo &&  in_cksum(icp, cc) != 0)  { 
      		/* BAD!  Packet is mangled somehow. */ 
      		
      	bits_differ += bitcmp(
      		(char *) icp + ICMP_MINLEN+sizeof(struct timeval),
      	     &outpack[ICMP_MINLEN+sizeof(struct timeval)],
      	     cc - ICMP_MINLEN - sizeof(struct timeval) ) ;
      	     
      	    /*** hex_dump(icp, cc) ; ***/
	        /*** dump_ascii(icp, cc) ; ***/
	    nmangled++ ;
	    mangled++ ;
	}
	else {
	        /* Packet is OK.  Take statistics on it */
		
		tsum += triptime;
		if( triptime < tmin )  tmin = triptime;
		if( triptime > tmax )  tmax = triptime;
	
		if (burst_size != 1) {
			p = (icp->icmp_seq - 1) %  burst_size ;
			btsum[p] += triptime ;
			if (triptime < btmin[p]) btmin[p] = triptime ;
			if (triptime > btmax[p]) btmax[p] = triptime ;
			++bnrecvd[p] ;
		}
		if ( u_shortsub( icp->icmp_seq, HWM_rcv_seq)  < 32767 )  
			HWM_rcv_seq = icp->icmp_seq ;
	}
				
	if (Report_freq > 0)   {
		if  ((icp->icmp_seq %Report_freq) == 0) 
			{  /* Time for a report (summary) */
			last_secs = now_secs ;
			format_time(now_secs) ;
			printf("[+%s]", time_buf) ;
			print_summary() ;
		}
	}		    
	else if (
	        /* Normal case... 
	     	 * print no more than approx one sample per sec...
	     	 */	
	    ( RTT_thresh == 0 &&
	      ( Sample_freq == 0 || ((icp->icmp_seq-1) %Sample_freq) == 0 ) )
	               
		||   /* Threshold (-r) parameter given.  Suppress printout unless
		      *     something bad happens...
		      */
	    ( RTT_thresh > 0 &&
	    ( triptime >= RTT_thresh || Cum_loss != last_Cum_loss || mangled)) )
	     
	    { 	     			 
		last_Cum_loss = Cum_loss ;	      
		format_time(last_secs = now_secs ) ;
		printf("[+%s] Echo Reply: Seq=%-3d", time_buf, icp->icmp_seq );
		if (RAW_echo) {
			printf(" CumLoss=%d+%d mangled  RTT=%d ms\n", Cum_loss, 			            		nmangled, triptime) ;
		}
		else
			printf(" CumLoss=%d  RTT=%d ms\n", Cum_loss, triptime) ;
	} 	
			
	fflush(stdout);
}


format_time(t)
int t ;  /* time in seconds */
	{

	if (t < 1000)
		sprintf(time_buf, "%d Secs", t) ;
	else
		sprintf(time_buf, "%02d:%02d:%02d", t/3600, (t/60)%60, t%60) ;
}


	/* Compare two strings and return count of bits that differ.
	 *   Also, print offset and values of differing bytes.
	 */
int bitcmp(a, b, n)
register char *a,  *b ;
int n ;
	{
	register char c ;
	register int i, j ;
	register int k = 0 ;
	
	for (i=0;i<n;i++)
		if (c = (*a++ ^ *b++ ) ) {
			printf("+%d: %x ", i, c&0xff) ;
			for (j = 256; (j >>= 1) ;)
				if (j&c) k++ ;
		} ;
	printf("\n") ;
	return(k) ;
}


/*
 *      T E R M I N A T E
 *
 *  User has entered ^C.  Set termination flag to inhibit further
 *  pings, and delay to await stragglers.
 *
 */
 terminate()
 {
 	int delay  ;
 	struct timezone tz ;
	
 	gettimeofday(&SIGINTime, &tz) ;   /* time now... */
 	
 		/* Wait twice the max delay, or 20 seconds, whichever is
 		 * less.
 		 */
 	delay = (2*tmax + 500)/1000 ;
 	itim.it_value.tv_sec =  (delay > 20) ?  20 : (delay < 2) ? 1 : delay ;
	itim.it_value.tv_usec =  0 ;
	itim.it_interval.tv_sec = itim.it_interval.tv_usec = 0 ; /* then disable*/
	setitimer(ITIMER_REAL, &itim, &otim) ; /* Start interval timer */
 	is_fin = 1 ;  /* Enter finish-up mode. */
 	
 	printf("... Delay %d seconds for stragglers...\n", itim.it_value.tv_sec) ;
 	fin_delay = itim.it_value.tv_sec * 1000 ;
 	fflush(stdout) ;
 	
 		/* Another control C and we will quit anyway... */
	signal( SIGINT, finish );
 	
 }
 
 
/*
 *			F I N I S H
 *
 * Print out final statistics.
 *
 */
int finish()
{
	int elapse, i , actual_delay ;
 	struct timezone tz ;
 	struct timeval fintime, fintime1 ;
 	
	signal( SIGINT, SIG_DFL );  /* Restore default action */
 	
	 	/* Determine the actual delay (to let stragglers show up), and if
	 	 *   it is too short (due to spurious clock interrupt -- see the
	 	 *   comment in catcher()), just return to await the real timeout.
	 	 */
	gettimeofday(&fintime, &tz) ;
	fintime1 = fintime ;
	tvsub(&fintime, &SIGINTime) ;
	actual_delay = fintime.tv_sec*1000 + fintime.tv_usec/1000  ;
	   /* If this was spurious timeout, return 0 for retry. */
	if (actual_delay < fin_delay - 20) return(0) ;
	
	/**	printf("delayed: %d secs  (%d times)\n", actual_delay, fin_count) ;
	 **/
	
	tvsub(&sendtod, &startod) ;
	elapse = sendtod.tv_sec * 1000 + (sendtod.tv_usec / 1000) ;

	strcpy(tod_buf, ctime(&fintime1.tv_sec) ) ;
	format_time( (elapse+999)/1000 ) ;
	printf(
    "\n   %s  %s---PING Stats--- Host= [%s]   %d byte packets  Elapsed= %s\n",
        canhost, tod_buf,
        hostaddr, datalen+ICMP_MINLEN, time_buf) ;
	      
 	print_summary() ;	
	
	if (nreceived && burst_size > 1) {
		printf("     By position in burst: \n") ;
		for (i=0;i < burst_size; i++)  {
			printf("[+%d]: %d packets received,", i, bnrecvd[i]);
			if (bnrecvd[i])
				printf("RTT= %dMin %dAvg %dMax ms\n",
			   			btmin[i], btsum[i]/bnrecvd[i], btmax[i] ) ;
			else
			   	printf("\n") ;
		}
	}
	
    printf("\n") ;
	if (hist_depth > 0 && nreceived)
		histdisplay( &sumhcb, "(ms)| Count - HISTOGRAM : \n",
				 60, hist_depth, stdout);

	fflush(stdout);
	exit(0);
}

print_summary() {
	register nt = ntransmitted-1 ;
	
	printf(" Sent=%4d  CumLoss= %d", 
	     nt, nt-nreceived );
	if (nt != nreceived)  
	    printf(" (%.1f%%)",
				100*  (1.0 - (float) nreceived/nt ) );
	if (RAW_echo && nmangled)
		printf("\n***** %d (%.1f%%) mangled, %d bits differ (BER=%.1e)",
		      nmangled,
			  100* (float) nmangled / nt,
			  bits_differ,
			  ((float) bits_differ/nreceived)/(datalen*8)  );
					
	if( nreceived ) {
	    printf("  RTT: %dMin %dAvg %dMax ms",
		tmin,
		tsum / (nreceived-nmangled),
		tmax );
	}
	printf("\n") ;
}


/*
 * 			P R _ T Y P E
 *
 * Convert an ICMP "type" field to a printable string.
 */
char *
pr_type( t )
register int t;
{
	static char *ttab[] = {
		"Echo Reply",
		"ICMP 1",
		"ICMP 2",
		"Dest Unreachable",
		"Source Quence",
		"Redirect",
		"ICMP 6",
		"ICMP 7",
		"Echo",
		"ICMP 9",
		"ICMP 10",
		"Time Exceeded",
		"Parameter Problem",
		"Timestamp",
		"Timestamp Reply",
		"Info Request",
		"Info Reply"
	};

	if( t < 0 || t > 16 )
		return("OUT-OF-RANGE");

	return(ttab[t]);
}

  
  /* Host name lookup...
   *
   *   Resolve host name using domain resolver or whatever, and copy 
   *   canonical host name into canonp[canonl].  But if
   *   argument is dotted-decimal string (first char is digit), just
   *   convert it without lookup (we don't want to fail just because
   *   the INADDR database is not complete).  
   *
   */
u_long resolve_name( namep, canonp, canonl )
 char *namep, canonp[] ;
 int  canonl ;
    {
    struct hostent *hp ;
    long inetaddr = 0;
    int n ;
    
    if (isdigit(*namep)) {
            /* Assume dotted-decimal */
        inetaddr = inet_addr(namep) ;
        *canonp = '\0' ;   /* No canonical name */
        if(inetaddr > 0)
             return(inetaddr) ;
      }
    if(inetaddr <= 0)  {
        if (NULL == (hp =  gethostbyname( namep )))
        	return(inetaddr) ;
        n = ((n = strlen(hp->h_name)) >= canonl) ?  canonl-1 : n ;
        bcopy(hp->h_name, canonp, n) ;
        hp->h_name[n] = '\0' ;
         
    	return( *( u_long *) hp->h_addr) ;
    }
}
	    	
hex_dump(p, len)
register char *p ;
register int len ;
	{
	register int i = 0, j ;
	
	while (len > 0) {
		printf("x%2.2x: ", i );
		for (j = 0; j<4; j++)  {
			if (len > 4) {
				printf(" x%8.8x", * (long *) p) ;
				p += sizeof(long) ;
				i += sizeof(long) ;
				len -= sizeof(long) ;
			}
			else {
				printf(" x%*.*x",  2*len, 2*len, * (long *) p) ;
				len = 0 ;
				break ;
			}
		}
		printf("\n") ;
	}
}


#ifdef notdef
 
dump_ascii( p, len)
char *p ;
int len ;
	{
	register int  j, n ;
	register char c ;
	char *first_p ;
	
	while (len > 0) {
		printf("x%2.2x: ", p - first_p );
		n = (len < 64)? len : 64 ;
		for (j = 0; j < 64; j++)  {
			c = (*p++) & 0x7f ;
			putchar( (c < 0x20  || c == 0x7F ) ? '.' : c) ; 
	    }
	    len -= n ;
	    putchar('\n') ;
	}
}

	
#endif 
