⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 icmptime.c

📁 TCP/IP详解
💻 C
字号:
/*
 * Fetch the current time from another host using the ICMP timestamp
 * request/reply.
 *
 * You must be superuser to run this program (or it must be suid to root)
 * since it requires a raw socket.
 */

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

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>

#define	DEFDATALEN	(12)	/* default data length */
#define	MAXIPLEN	60
#define	MAXICMPLEN	76
#define	MAXPACKET	(65536 - 60 - 8)/* max packet size */

struct sockaddr	whereto;	/* who to ping */
int		datalen = DEFDATALEN;
int		s;
u_char		outpack[MAXPACKET];
char		*hostname;

struct timeval	tvorig, tvrecv;	/* originate & receive timeval structs */
long		tsorig, tsrecv;	/* originate & receive timestamps */
			/* timestamp = #millisec since midnight UTC */
			/* both host byte ordered */
long		tsdiff;		/* adjustment must also be signed */

u_long		inet_addr();
char		*inet_ntoa();
void		sig_alrm(int);

main(argc, argv)
int	argc;
char	**argv;
{
	int			i, ch, fdmask, hold, packlen, preload;
	extern int		errno;
	struct hostent		*hp;
	struct sockaddr_in	*to;
	struct protoent		*proto;
	u_char			*packet;
	char			*target, hnamebuf[MAXHOSTNAMELEN], *malloc();

	if (argc != 2)
		exit(1);

	target = argv[1];

	bzero((char *)&whereto, sizeof(struct sockaddr));
	to = (struct sockaddr_in *)&whereto;
	to->sin_family = AF_INET;

		/* try to convert as dotted decimal address,
		   else if that fails assume it's a hostname */
	to->sin_addr.s_addr = inet_addr(target);
	if (to->sin_addr.s_addr != (u_int)-1)
		hostname = target;
	else {
		hp = gethostbyname(target);
		if (!hp) {
			fprintf(stderr, "unknown host %s\n", target);
			exit(1);
		}
		to->sin_family = hp->h_addrtype;
		bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
		strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
		hostname = hnamebuf;
	}

	packlen = datalen + MAXIPLEN + MAXICMPLEN;
	if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL) {
		fprintf(stderr, "malloc error\n");
		exit(1);
	}

	if ( (proto = getprotobyname("icmp")) == NULL) {
		fprintf(stderr, "unknown protocol icmp\n");
		exit(1);
	}

	if ( (s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
		perror("socket");
		exit(1);
	}

	/*
	 * We send one request and wait up to 30 seconds for a reply.
	 */

	signal(SIGALRM, sig_alrm);
	alarm(30);	/* 30 second time limit */

	sender();	/* send the request */

	for (;;) {
		struct sockaddr_in from;
		register int cc;
		int fromlen;

		fromlen = sizeof(from);
		if ( (cc = recvfrom(s, (char *)packet, packlen, 0,
		    (struct sockaddr *)&from, &fromlen)) < 0) {
			if (errno == EINTR)
				continue;
			perror("recvfrom error");
			continue;
		}

			/* process received ICMP message */
		if (procpack((char *)packet, cc, &from) == 0)
			exit(0);	/* terminate if reply received */
	}
}

/*
 * Send the request.
 */

sender()
{
	int		i, cc;
	struct icmp	*icp;

	icp = (struct icmp *)outpack;
	icp->icmp_type = ICMP_TSTAMP;
	icp->icmp_code = 0;
	icp->icmp_cksum = 0;	/* compute checksum below */
	icp->icmp_seq = 12345;	/* seq and id must be reflected */
	icp->icmp_id = getpid();

		/* fill in originate timestamp: have to convert tv_sec
		   from seconds since the Epoch to milliseconds since
		   midnight, then add in microseconds */

	gettimeofday(&tvorig, (struct timezone *)NULL);
	tsorig = (tvorig.tv_sec % (24*60*60)) * 1000 + tvorig.tv_usec / 1000;
	icp->icmp_otime = htonl(tsorig);
	icp->icmp_rtime = 0;		/* filled in by receiver */
	icp->icmp_ttime = 0;		/* filled in by receiver */

	cc = datalen + 8;	/* 8 bytes of header, 12 bytes of data */

					/* compute ICMP checksum */
	icp->icmp_cksum = in_cksum((u_short *)icp, cc);

	i = sendto(s, (char *)outpack, cc, 0, &whereto,
	    				sizeof(struct sockaddr));
	if (i < 0 || i != cc)  {
		if (i < 0)
			perror("sendto error");
		printf("wrote %s %d chars, ret=%d\n", hostname, cc, i);
	}
}

/*
 * Process a received ICMP message.  Since we receive *all* ICMP messages
 * that the kernel receives, we may receive other than the timestamp
 * reply we're waiting for.
 */

int
procpack(buf, cc, from)
char			*buf;
int			cc;
struct sockaddr_in	*from;
{
	int		i, hlen;
	struct icmp	*icp;
	struct ip	*ip;
	struct timeval	tvdelta;

	/* We could call gettimeofday() again to measure the RTT and use
	 * that in computing the offset, but some (most?) systems only
	 * have 100 Hz resoultion for this function and the RTT on a
	 * LAN is usually less than this (i.e., it wouldn't buy us
	 * anything). */

#ifdef	notdef
	gettimeofday(&tvrecv, (struct timezone *)NULL);
#endif

		/* Check the IP header */
	ip = (struct ip *)buf;
	hlen = ip->ip_hl << 2;
	if (cc < hlen + ICMP_MINLEN) {
		fprintf(stderr, "packet too short (%d bytes) from %s\n", cc,
			  inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
		return;
	}

		/* Now the ICMP part */
	cc -= hlen;
	icp = (struct icmp *)(buf + hlen);

		/* With a raw ICMP socket we get all ICMP packets that
		   come into the kernel. */

	if (icp->icmp_type == ICMP_TSTAMPREPLY) {
		if (ntohl(icp->icmp_otime) != tsorig)
			printf("originate timestamp not echoed: sent $lu, received %lu\n",
						tsorig, ntohl(icp->icmp_otime));
		if (cc != 20)
			printf("cc = %d, expected cc = 20\n", cc);
		if (icp->icmp_seq != 12345)
			printf("received sequence # %d\n", icp->icmp_seq);
		if (icp->icmp_id != getpid())
			printf("received id %d\n", icp->icmp_id);

		/* tsorig and tsrecv are both signed longs.  The icmp_[ort]time
		 * members in the structure are unsigned, but the max value
		 * for the #millisec since midnight is (24*60*60*1000 - 1)
		 * or 85,399,999, which easily fits into a signed long.
		 * We want them signed to compute a signed difference. */

		tsrecv = ntohl(icp->icmp_rtime);
		tsdiff = tsrecv - tsorig;	/* difference in millisec */

		printf("orig = %ld, recv = %ld\n",
				ntohl(icp->icmp_otime), ntohl(icp->icmp_rtime));
		printf("adjustment = %ld ms\n", tsdiff);
		tvdelta.tv_sec  = tsdiff / 1000;	/* normally 0 */
		tvdelta.tv_usec = (tsdiff % 1000) * 1000;
		printf("correction = %ld sec, %ld usec\n",
				tvdelta.tv_sec, tvdelta.tv_usec);
		if (adjtime(&tvdelta, (struct timeval *) 0) < 0) {
			perror("adjtime error");
			exit(1);
		}
		return(0);	/* done */
	} else
		return(-1);	/* some other type of ICMP message */
}

/*
 * in_cksum --
 *	Checksum routine for Internet Protocol family headers (C Version)
 */
in_cksum(addr, len)
	u_short *addr;
	int len;
{
	register int nleft = len;
	register u_short *w = addr;
	register int sum = 0;
	u_short answer = 0;

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

	/* mop up an odd byte, if necessary */
	if (nleft == 1) {
		*(u_char *)(&answer) = *(u_char *)w ;
		sum += answer;
	}

	/* add back carry outs from top 16 bits to low 16 bits */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return(answer);
}

void
sig_alrm(int signo)
{
	printf("timeout\n");
	exit(1);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -