#ifndef lint
static char *RCSid = "$Header: /usr/src/ecn/getethers/RCS/ping.c,v 1.2 92/05/08 14:16:01 davy Exp $";
#endif

/*
 * ping.c - routines for pinging a host.
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * davy@ecn.purdue.edu
 * November, 1991
 *
 * $Log:	ping.c,v $
 * Revision 1.2  92/05/08  14:16:01  davy
 * Made portable to 4.3BSD, from Peter Shipley (shipley@tfs.com).
 * 
 * Revision 1.1  91/11/27  10:56:33  davy
 * Initial revision
 * 
 */
#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 <signal.h>
#include <setjmp.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include "defs.h"

static jmp_buf	env;
static int	ident;
static int	datalen = 64 - 8;
static u_char	inpacket[MAXPACKET], outpacket[MAXPACKET];

extern int	errno;

/*
 * ping - send ICMP ECHO REQUEST packets to the host at addr until it either
 *	  responds or we decide to bag it.  Most of this code was stolen and
 *	  simplified from Mike Muuss' ping program.
 */
ping(addr, id)
struct in_addr addr;
int id;
{
	int ringring();
	register int i, n, s;
	int cc, hlen, fromlen;
	register u_char *datap;
	register struct ip *ip;
	register struct icmp *icp;
	struct sockaddr_in from, to;
	static struct protoent *proto = NULL;

	bzero((char *) &to, sizeof(struct in_addr));

	/*
	 * Construct destination address.
	 */
	to.sin_family = AF_INET;
	bcopy((char *) &addr, (char *) &to.sin_addr, sizeof(struct in_addr));

	/*
	 * ICMP ID number.
	 */
	ident = (getpid() + id) & 0xFFFF;

	/*
	 * Look up protocol number.
	 */
	if (proto == NULL) {
		if ((proto = getprotobyname("icmp")) == NULL) {
			error("icmp: unknown protocol");
			return(0);
		}
	}

	/*
	 * Need a raw socket.
	 */
	if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
		error("socket");
		return(0);
	}

	icp = (struct icmp *) outpacket;

	/*
	 * Send up to MAXPING packets.
	 */
	for (i=0; i < MAXPING; i++) {
		/*
		 * Construct ICMP header.
		 */
		icp->icmp_type = ICMP_ECHO;
		icp->icmp_code = 0;
		icp->icmp_cksum = 0;
		icp->icmp_seq = i;
		icp->icmp_id = ident;

		/*
		 * Stick some junk in the packet.
		 */
		cc = datalen + 8;
		datap = &outpacket[8];

		for (n = 0; n < datalen; n++)
			*datap++ = n;

		/*
		 * Compute the IP checksum.
		 */
		icp->icmp_cksum = in_cksum(icp, cc);

		/*
		 * Send the packet...
		 */
		n = sendto(s, outpacket, cc, 0,
			   (struct sockaddr *) &to, sizeof(struct sockaddr));

		if ((n < 0) || (n != cc)) {
			if (n < 0)
				error("sendto");
			else
				error("sendto truncated");

			close(s);
			return(0);
		}

		/*
		 * We'll wait for PACKWAIT seconds for a response.
		 */
		signal(SIGALRM, ringring);
		fromlen = sizeof(struct sockaddr_in);

		alarm(PACKWAIT);

		/*
		 * Bag it... send the next packet.
		 */
		if (setjmp(env))
			continue;

		/*
		 * Get the packet.
		 */
		cc = recvfrom(s, inpacket, MAXPACKET, 0,
			      (struct sockaddr *) &from, &fromlen);
		alarm(0);

		if (cc < 0) {
			if (errno == EINTR)
				continue;

			error("recvfrom");
			continue;
		}

		/*
		 * Make sure it's a reply to ours.
		 */
		ip = (struct ip *) inpacket;
		hlen = ip->ip_hl << 2;

		if (cc < (hlen + ICMP_MINLEN))
			continue;

		cc -= hlen;
		icp = (struct icmp *) (&inpacket[hlen]);

		if (icp->icmp_type != ICMP_ECHOREPLY)
			continue;

		if (icp->icmp_id != ident)
			continue;

		/*
		 * Yay!  The host is up.
		 */
		close(s);
		return(1);
	}

	/*
	 * Boo!  The host is down.
	 */
	close(s);
	return(0);
}

/*
 * in_cksum - compute the IP checksum.
 */
in_cksum(addr, len)
u_short *addr;
int len;
{
	register u_short *w = addr;
	register int nleft = len;
	register u_short answer;
	register int sum = 0;

	/*
	 * Use a 32-bit accumulator (sum) and add sequential 16-bit
	 * words to it, then fold back all the carry bits from the
	 * top 16 bits into the lower 16 bits.
	 */
	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	/*
	 * Pick up odd byte if necessary.
	 */
	if (nleft == 1)
		sum += *(u_char *) w;

	/*
	 * Add back the carry bits.
	 */
	sum = (sum >> 16) + (sum & 0xFFFF);
	sum += (sum >> 16);

	/*
	 * Truncate to 16 bits.
	 */
	answer = ~sum;

	return(answer);
}

ringring()
{
	longjmp(env, 1);
}
