Unix网络编程代码 第28章 原始套接字

第28章 原始套接字

28.5 ping程序

#define _GNU_SOURCE
#include	<sys/time.h>
#include	<sys/un.h>
#include	<string.h>
#include	<arpa/inet.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<syslog.h>
#include	<errno.h>
#include	<netinet/in_systm.h>
#include	<netinet/ip.h>
#include	<netinet/ip_icmp.h>
#include	<sys/types.h>
#include	<time.h>
#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<sys/socket.h>
#include	<netdb.h>
#include	<netinet/ip6.h>
#include	<netinet/icmp6.h>

#define	BUFSIZE		1500
//#define IPV6
			/* globals */
char sendbuf[BUFSIZE];

int datalen;			/* # bytes of data following ICMP header */
char *host;
int nsent;			/* add 1 for each sendto() */
pid_t pid;			/* our PID */
int sockfd;
int verbose;

struct proto {
	void (*fproc) (char *, ssize_t, struct msghdr *, struct timeval *);
	void (*fsend) (void);
	void (*finit) (void);
	struct sockaddr *sasend;	/* sockaddr{} for send, from getaddrinfo */
	struct sockaddr *sarecv;	/* sockaddr{} for receiving */
	socklen_t salen;	/* length of sockaddr{}s */
	int icmpproto;		/* IPPROTO_xxx value for ICMP */
} *pr;

#define	MAXLINE		4096	/* max text line length */
int daemon_proc;		/* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
	int errno_save, n;
	char buf[MAXLINE + 1];

	errno_save = errno;	/* value caller might want printed */
#ifdef	HAVE_VSNPRINTF
	vsnprintf(buf, MAXLINE, fmt, ap);	/* safe */
#else
	vsprintf(buf, fmt, ap);	/* not safe */
#endif
	n = strlen(buf);
	if (errnoflag)
		snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
	strcat(buf, "\n");

	if (daemon_proc) {
		syslog(level, "%s", buf);
	} else {
		fflush(stdout);	/* in case stdout and stderr are the same */
		fputs(buf, stderr);
		fflush(stderr);
	}
	return;
}

void err_quit(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(0, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

void err_sys(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(1, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
	static char str[128];	/* Unix domain is largest */

	switch (sa->sa_family) {
	case AF_INET:{
			struct sockaddr_in *sin = (struct sockaddr_in *)sa;

			if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
			    == NULL)
				return (NULL);
			return (str);
		}

#ifdef	IPV6
	case AF_INET6:{
			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;

			if (inet_ntop
			    (AF_INET6, &sin6->sin6_addr, str,
			     sizeof(str)) == NULL)
				return (NULL);
			return (str);
		}
#endif

#ifdef	AF_UNIX
	case AF_UNIX:{
			struct sockaddr_un *unp = (struct sockaddr_un *)sa;

			/* OK to have no pathname bound to the socket: happens on
			   every connect() unless client calls bind() first. */
			if (unp->sun_path[0] == 0)
				strcpy(str, "(no pathname bound)");
			else
				snprintf(str, sizeof(str), "%s", unp->sun_path);
			return (str);
		}
#endif

#ifdef	HAVE_SOCKADDR_DL_STRUCT
	case AF_LINK:{
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;

			if (sdl->sdl_nlen > 0)
				snprintf(str, sizeof(str), "%*s",
					 sdl->sdl_nlen, &sdl->sdl_data[0]);
			else
				snprintf(str, sizeof(str), "AF_LINK, index=%d",
					 sdl->sdl_index);
			return (str);
		}
#endif
	default:
		snprintf(str, sizeof(str),
			 "sock_ntop_host: unknown AF_xxx: %d, len %d",
			 sa->sa_family, salen);
		return (str);
	}
	return (NULL);
}

char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
	char *ptr;

	if ((ptr = sock_ntop_host(sa, salen)) == NULL)
		err_sys("sock_ntop_host error");	/* inet_ntop() sets errno */
	return (ptr);
}

void tv_sub(struct timeval *out, struct timeval *in)
{
	if ((out->tv_usec -= in->tv_usec) < 0) {	/* out -= in */
		--out->tv_sec;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}

void proc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
	int hlen1, icmplen;
	double rtt;
	struct ip *ip;
	struct icmp *icmp;
	struct timeval *tvsend;

	ip = (struct ip *)ptr;	/* start of IP header */
	hlen1 = ip->ip_hl << 2;	/* length of IP header */
	if (ip->ip_p != IPPROTO_ICMP)
		return;		/* not ICMP */

	icmp = (struct icmp *)(ptr + hlen1);	/* start of ICMP header */
	if ((icmplen = len - hlen1) < 8)
		return;		/* malformed packet */

	if (icmp->icmp_type == ICMP_ECHOREPLY) {
		if (icmp->icmp_id != pid)
			return;	/* not a response to our ECHO_REQUEST */
		if (icmplen < 16)
			return;	/* not enough data to use */

		tvsend = (struct timeval *)icmp->icmp_data;
		tv_sub(tvrecv, tvsend);
		rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;

		printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",
		       icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
		       icmp->icmp_seq, ip->ip_ttl, rtt);

	} else if (verbose) {
		printf("  %d bytes from %s: type = %d, code = %d\n",
		       icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
		       icmp->icmp_type, icmp->icmp_code);
	}
}

void Gettimeofday(struct timeval *tv, void *foo)
{
	if (gettimeofday(tv, foo) == -1)
		err_sys("gettimeofday error");
	return;
}

uint16_t in_cksum(uint16_t * addr, int len)
{
	int nleft = len;
	uint32_t sum = 0;
	uint16_t *w = addr;
	uint16_t 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;
	}

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

	/* 4add 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
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
       const struct sockaddr *sa, socklen_t salen)
{
	if (sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t) nbytes)
		err_sys("sendto error");
}

void send_v4(void)
{
	int len;
	struct icmp *icmp;

	icmp = (struct icmp *)sendbuf;
	icmp->icmp_type = ICMP_ECHO;
	icmp->icmp_code = 0;
	icmp->icmp_id = pid;
	icmp->icmp_seq = nsent++;
	memset(icmp->icmp_data, 0xa5, datalen);	/* fill with pattern */
	Gettimeofday((struct timeval *)icmp->icmp_data, NULL);

	len = 8 + datalen;	/* checksum ICMP header and data */
	icmp->icmp_cksum = 0;
	icmp->icmp_cksum = in_cksum((u_short *) icmp, len);

	Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
}

int datalen = 56;		/* data that goes with ICMP echo request */

typedef void Sigfunc(int);	/* for signal handlers */
Sigfunc *signal(int signo, Sigfunc * func)
{
	struct sigaction act, oact;

	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (signo == SIGALRM) {
#ifdef	SA_INTERRUPT
		act.sa_flags |= SA_INTERRUPT;	/* SunOS 4.x */
#endif
	} else {
#ifdef	SA_RESTART
		act.sa_flags |= SA_RESTART;	/* SVR4, 44BSD */
#endif
	}
	if (sigaction(signo, &act, &oact) < 0)
		return (SIG_ERR);
	return (oact.sa_handler);
}

Sigfunc *Signal(int signo, Sigfunc * func)
{				/* for our signal() function */
	Sigfunc *sigfunc;

	if ((sigfunc = signal(signo, func)) == SIG_ERR)
		err_sys("signal error");
	return (sigfunc);
}

struct addrinfo *Host_serv(const char *host, const char *serv, int family,
			   int socktype)
{
	int n;
	struct addrinfo hints, *res;

	bzero(&hints, sizeof(struct addrinfo));
	hints.ai_flags = AI_CANONNAME;	/* always return canonical name */
	hints.ai_family = family;	/* 0, AF_INET, AF_INET6, etc. */
	hints.ai_socktype = socktype;	/* 0, SOCK_STREAM, SOCK_DGRAM, etc. */

	if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
		err_quit("host_serv error for %s, %s: %s",
			 (host == NULL) ? "(no hostname)" : host,
			 (serv == NULL) ? "(no service name)" : serv,
			 gai_strerror(n));

	return (res);		/* return pointer to first on linked list */
}

void *Calloc(size_t n, size_t size)
{
	void *ptr;

	if ((ptr = calloc(n, size)) == NULL)
		err_sys("calloc error");
	return (ptr);
}

void sig_alrm(int signo)
{
	(*pr->fsend) ();

	alarm(1);
	return;
}

int Socket(int family, int type, int protocol)
{
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		err_sys("socket error");
	return (n);
}

void readloop(void)
{
	int size;
	char recvbuf[BUFSIZE];
	char controlbuf[BUFSIZE];
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;
	struct timeval tval;

	sockfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
	setuid(getuid());	/* don't need special permissions any more */
	if (pr->finit)
		(*pr->finit) ();

	size = 60 * 1024;	/* OK if setsockopt fails */
	setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));

	sig_alrm(SIGALRM);	/* send first packet */

	iov.iov_base = recvbuf;
	iov.iov_len = sizeof(recvbuf);
	msg.msg_name = pr->sarecv;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = controlbuf;
	for (;;) {
		msg.msg_namelen = pr->salen;
		msg.msg_controllen = sizeof(controlbuf);
		n = recvmsg(sockfd, &msg, 0);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			else
				err_sys("recvmsg error");
		}

		Gettimeofday(&tval, NULL);
		(*pr->fproc) (recvbuf, n, &msg, &tval);
	}
}

void proc_v6(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
#ifdef	IPV6
	double rtt;
	struct icmp6_hdr *icmp6;
	struct timeval *tvsend;
	struct cmsghdr *cmsg;
	int hlim;

	icmp6 = (struct icmp6_hdr *)ptr;
	if (len < 8)
		return;		/* malformed packet */

	if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
		if (icmp6->icmp6_id != pid)
			return;	/* not a response to our ECHO_REQUEST */
		if (len < 16)
			return;	/* not enough data to use */

		tvsend = (struct timeval *)(icmp6 + 1);
		tv_sub(tvrecv, tvsend);
		rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;

		hlim = -1;
		for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
		     cmsg = CMSG_NXTHDR(msg, cmsg)) {
			if (cmsg->cmsg_level == IPPROTO_IPV6
			    && cmsg->cmsg_type == IPV6_HOPLIMIT) {
				hlim = *(u_int32_t *) CMSG_DATA(cmsg);
				break;
			}
		}
		printf("%d bytes from %s: seq=%u, hlim=",
		       len, Sock_ntop_host(pr->sarecv, pr->salen),
		       icmp6->icmp6_seq);
		if (hlim == -1)
			printf("???");	/* ancillary data missing */
		else
			printf("%d", hlim);
		printf(", rtt=%.3f ms\n", rtt);
	} else if (verbose) {
		printf("  %d bytes from %s: type = %d, code = %d\n",
		       len, Sock_ntop_host(pr->sarecv, pr->salen),
		       icmp6->icmp6_type, icmp6->icmp6_code);
	}
#endif				/* IPV6 */
}

void send_v6()
{
#ifdef	IPV6
	int len;
	struct icmp6_hdr *icmp6;

	icmp6 = (struct icmp6_hdr *)sendbuf;
	icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
	icmp6->icmp6_code = 0;
	icmp6->icmp6_id = pid;
	icmp6->icmp6_seq = nsent++;
	memset((icmp6 + 1), 0xa5, datalen);	/* fill with pattern */
	Gettimeofday((struct timeval *)(icmp6 + 1), NULL);

	len = 8 + datalen;	/* 8-byte ICMPv6 header */

	Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
	/* 4kernel calculates and stores checksum for us */
#endif				/* IPV6 */
}

void init_v6()
{
#ifdef IPV6
	int on = 1;

	if (verbose == 0) {
		/* install a filter that only passes ICMP6_ECHO_REPLY unless verbose */
		struct icmp6_filter myfilt;
		ICMP6_FILTER_SETBLOCKALL(&myfilt);
		ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &myfilt);
		setsockopt(sockfd, IPPROTO_IPV6, ICMP6_FILTER, &myfilt,
			   sizeof(myfilt));
		/* ignore error return; the filter is an optimization */
	}

	/* ignore error returned below; we just won't receive the hop limit */
#ifdef IPV6_RECVHOPLIMIT
	/* RFC 3542 */
	setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
#else
	/* RFC 2292 */
	setsockopt(sockfd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
#endif
#endif
}
struct proto proto_v4 = { proc_v4, send_v4, NULL, NULL, NULL, 0, IPPROTO_ICMP };

#ifdef	IPV6
struct proto proto_v6 =
    { proc_v6, send_v6, init_v6, NULL, NULL, 0, IPPROTO_ICMPV6 };
#endif
int main(int argc, char **argv)
{
	int c;
	struct addrinfo *ai;
	char *h;

	opterr = 0;		/* don't want getopt() writing to stderr */
	while ((c = getopt(argc, argv, "v")) != -1) {
		switch (c) {
		case 'v':
			verbose++;
			break;

		case '?':
			err_quit("unrecognized option: %c", c);
		}
	}

	if (optind != argc - 1)
		err_quit("usage: ping [ -v ] <hostname>");
	host = argv[optind];

	pid = getpid() & 0xffff;	/* ICMP ID field is 16 bits */
	Signal(SIGALRM, sig_alrm);

	ai = Host_serv(host, NULL, 0, 0);

	h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
	printf("PING %s (%s): %d data bytes\n",
	       ai->ai_canonname ? ai->ai_canonname : h, h, datalen);

	/* 4initialize according to protocol */
	if (ai->ai_family == AF_INET) {
		pr = &proto_v4;
#ifdef	IPV6
	} else if (ai->ai_family == AF_INET6) {
		pr = &proto_v6;
		if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)
					    ai->ai_addr)->sin6_addr)))
			err_quit("cannot ping IPv4-mapped IPv6 address");
#endif
	} else
		err_quit("unknown address family %d", ai->ai_family);

	pr->sasend = ai->ai_addr;
	pr->sarecv = Calloc(1, ai->ai_addrlen);
	pr->salen = ai->ai_addrlen;

	readloop();

	exit(0);
}

28.6 traceroute程序

#define _GNU_SOURCE
#include	<sys/time.h>
#include	<sys/un.h>
#include	<string.h>
#include	<arpa/inet.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<syslog.h>
#include	<errno.h>
#include	<netinet/in_systm.h>
#include	<netinet/ip.h>
#include	<netinet/ip_icmp.h>
#include	<sys/types.h>
#include	<time.h>
#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<sys/socket.h>
#include	<netdb.h>
#include	<netinet/ip6.h>
#include	<netinet/icmp6.h>
#include	<netinet/udp.h>

#define	BUFSIZE		1500

#define	BUFSIZE		1500

struct rec {			/* format of outgoing UDP data */
	u_short rec_seq;	/* sequence number */
	u_short rec_ttl;	/* TTL packet left with */
	struct timeval rec_tv;	/* time packet left */
};

			/* globals */
char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];

int datalen;			/* # bytes of data following ICMP header */
char *host;
u_short sport, dport;
int nsent;			/* add 1 for each sendto() */
pid_t pid;			/* our PID */
int probe, nprobes;
int sendfd, recvfd;		/* send on UDP sock, read on raw ICMP sock */
int ttl, max_ttl;
int verbose;

			/* function prototypes */
struct proto {
	const char *(*icmpcode) (int);
	int (*recv) (int, struct timeval *);
	struct sockaddr *sasend;	/* sockaddr{} for send, from getaddrinfo */
	struct sockaddr *sarecv;	/* sockaddr{} for receiving */
	struct sockaddr *salast;	/* last sockaddr{} for receiving */
	struct sockaddr *sabind;	/* sockaddr{} for binding source port */
	socklen_t salen;	/* length of sockaddr{}s */
	int icmpproto;		/* IPPROTO_xxx value for ICMP */
	int ttllevel;		/* setsockopt() level to set TTL */
	int ttloptname;		/* setsockopt() name to set TTL */
} *pr;

#include	<netinet/ip6.h>
#include	<netinet/icmp6.h>

//#define IPV6
			/* globals */
char sendbuf[BUFSIZE];

int datalen;			/* # bytes of data following ICMP header */
char *host;
int nsent;			/* add 1 for each sendto() */
pid_t pid;			/* our PID */
int sockfd;
int verbose;

#define	MAXLINE		4096	/* max text line length */
int daemon_proc;		/* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
	int errno_save, n;
	char buf[MAXLINE + 1];

	errno_save = errno;	/* value caller might want printed */
#ifdef	HAVE_VSNPRINTF
	vsnprintf(buf, MAXLINE, fmt, ap);	/* safe */
#else
	vsprintf(buf, fmt, ap);	/* not safe */
#endif
	n = strlen(buf);
	if (errnoflag)
		snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
	strcat(buf, "\n");

	if (daemon_proc) {
		syslog(level, "%s", buf);
	} else {
		fflush(stdout);	/* in case stdout and stderr are the same */
		fputs(buf, stderr);
		fflush(stderr);
	}
	return;
}

void err_quit(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(0, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

void err_sys(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	err_doit(1, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
	static char str[128];	/* Unix domain is largest */

	switch (sa->sa_family) {
	case AF_INET:{
			struct sockaddr_in *sin = (struct sockaddr_in *)sa;

			if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
			    == NULL)
				return (NULL);
			return (str);
		}

#ifdef	IPV6
	case AF_INET6:{
			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;

			if (inet_ntop
			    (AF_INET6, &sin6->sin6_addr, str,
			     sizeof(str)) == NULL)
				return (NULL);
			return (str);
		}
#endif

#ifdef	AF_UNIX
	case AF_UNIX:{
			struct sockaddr_un *unp = (struct sockaddr_un *)sa;

			/* OK to have no pathname bound to the socket: happens on
			   every connect() unless client calls bind() first. */
			if (unp->sun_path[0] == 0)
				strcpy(str, "(no pathname bound)");
			else
				snprintf(str, sizeof(str), "%s", unp->sun_path);
			return (str);
		}
#endif

#ifdef	HAVE_SOCKADDR_DL_STRUCT
	case AF_LINK:{
			struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;

			if (sdl->sdl_nlen > 0)
				snprintf(str, sizeof(str), "%*s",
					 sdl->sdl_nlen, &sdl->sdl_data[0]);
			else
				snprintf(str, sizeof(str), "AF_LINK, index=%d",
					 sdl->sdl_index);
			return (str);
		}
#endif
	default:
		snprintf(str, sizeof(str),
			 "sock_ntop_host: unknown AF_xxx: %d, len %d",
			 sa->sa_family, salen);
		return (str);
	}
	return (NULL);
}

char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
	char *ptr;

	if ((ptr = sock_ntop_host(sa, salen)) == NULL)
		err_sys("sock_ntop_host error");	/* inet_ntop() sets errno */
	return (ptr);
}

void Gettimeofday(struct timeval *tv, void *foo)
{
	if (gettimeofday(tv, foo) == -1)
		err_sys("gettimeofday error");
	return;
}

uint16_t in_cksum(uint16_t * addr, int len)
{
	int nleft = len;
	uint32_t sum = 0;
	uint16_t *w = addr;
	uint16_t 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;
	}

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

	/* 4add 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
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
       const struct sockaddr *sa, socklen_t salen)
{
	if (sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t) nbytes)
		err_sys("sendto error");
}

int datalen = 56;		/* data that goes with ICMP echo request */

typedef void Sigfunc(int);	/* for signal handlers */
Sigfunc *signal(int signo, Sigfunc * func)
{
	struct sigaction act, oact;

	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (signo == SIGALRM) {
#ifdef	SA_INTERRUPT
		act.sa_flags |= SA_INTERRUPT;	/* SunOS 4.x */
#endif
	} else {
#ifdef	SA_RESTART
		act.sa_flags |= SA_RESTART;	/* SVR4, 44BSD */
#endif
	}
	if (sigaction(signo, &act, &oact) < 0)
		return (SIG_ERR);
	return (oact.sa_handler);
}

Sigfunc *Signal(int signo, Sigfunc * func)
{				/* for our signal() function */
	Sigfunc *sigfunc;

	if ((sigfunc = signal(signo, func)) == SIG_ERR)
		err_sys("signal error");
	return (sigfunc);
}

struct addrinfo *Host_serv(const char *host, const char *serv, int family,
			   int socktype)
{
	int n;
	struct addrinfo hints, *res;

	bzero(&hints, sizeof(struct addrinfo));
	hints.ai_flags = AI_CANONNAME;	/* always return canonical name */
	hints.ai_family = family;	/* 0, AF_INET, AF_INET6, etc. */
	hints.ai_socktype = socktype;	/* 0, SOCK_STREAM, SOCK_DGRAM, etc. */

	if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
		err_quit("host_serv error for %s, %s: %s",
			 (host == NULL) ? "(no hostname)" : host,
			 (serv == NULL) ? "(no service name)" : serv,
			 gai_strerror(n));

	return (res);		/* return pointer to first on linked list */
}

void *Calloc(size_t n, size_t size)
{
	void *ptr;

	if ((ptr = calloc(n, size)) == NULL)
		err_sys("calloc error");
	return (ptr);
}

int Socket(int family, int type, int protocol)
{
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		err_sys("socket error");
	return (n);
}

const char *icmpcode_v4(int code)
{
	static char errbuf[100];
	switch (code) {
	case 0:
		return ("network unreachable");
	case 1:
		return ("host unreachable");
	case 2:
		return ("protocol unreachable");
	case 3:
		return ("port unreachable");
	case 4:
		return ("fragmentation required but DF bit set");
	case 5:
		return ("source route failed");
	case 6:
		return ("destination network unknown");
	case 7:
		return ("destination host unknown");
	case 8:
		return ("source host isolated (obsolete)");
	case 9:
		return ("destination network administratively prohibited");
	case 10:
		return ("destination host administratively prohibited");
	case 11:
		return ("network unreachable for TOS");
	case 12:
		return ("host unreachable for TOS");
	case 13:
		return
		    ("communication administratively prohibited by filtering");
	case 14:
		return ("host recedence violation");
	case 15:
		return ("precedence cutoff in effect");
	default:
		sprintf(errbuf, "[unknown code %d]", code);
		return errbuf;
	}
}

int gotalarm;

void sig_alrm(int signo)
{
	gotalarm = 1;		/* set flag to note that alarm occurred */
	return;			/* and interrupt the recvfrom() */
}

int recv_v4(int seq, struct timeval *tv)
{
	int hlen1, ret;
	unsigned hlen2, icmplen;
	socklen_t len;
	ssize_t n;
	struct ip *ip, *hip;
	struct icmp *icmp;
	struct udphdr *udp;

	gotalarm = 0;
	alarm(3);
	for (;;) {
		if (gotalarm)
			return (-3);	/* alarm expired */
		len = pr->salen;
		n = recvfrom(recvfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv,
			     &len);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			else
				err_sys("recvfrom error");
		}

		ip = (struct ip *)recvbuf;	/* start of IP header */
		hlen1 = ip->ip_hl << 2;	/* length of IP header */

		icmp = (struct icmp *)(recvbuf + hlen1);	/* start of ICMP header */
		if ((icmplen = n - hlen1) < 8)
			continue;	/* not enough to look at ICMP header */

		if (icmp->icmp_type == ICMP_TIMXCEED &&
		    icmp->icmp_code == ICMP_TIMXCEED_INTRANS) {
			if (icmplen < 8 + sizeof(struct ip))
				continue;	/* not enough data to look at inner IP */

			hip = (struct ip *)(recvbuf + hlen1 + 8);
			hlen2 = hip->ip_hl << 2;
			if (icmplen < 8 + hlen2 + 4)
				continue;	/* not enough data to look at UDP ports */

			udp = (struct udphdr *)(recvbuf + hlen1 + 8 + hlen2);
			if (hip->ip_p == IPPROTO_UDP &&
			    udp->source == htons(sport) &&
			    udp->dest == htons(dport + seq)) {
				ret = -2;	/* we hit an intermediate router */
				break;
			}

		} else if (icmp->icmp_type == ICMP_UNREACH) {
			if (icmplen < 8 + sizeof(struct ip))
				continue;	/* not enough data to look at inner IP */

			hip = (struct ip *)(recvbuf + hlen1 + 8);
			hlen2 = hip->ip_hl << 2;
			if (icmplen < 8 + hlen2 + 4)
				continue;	/* not enough data to look at UDP ports */

			udp = (struct udphdr *)(recvbuf + hlen1 + 8 + hlen2);
			if (hip->ip_p == IPPROTO_UDP &&
			    udp->source == htons(sport) &&
			    udp->dest == htons(dport + seq)) {
				if (icmp->icmp_code == ICMP_UNREACH_PORT)
					ret = -1;	/* have reached destination */
				else
					ret = icmp->icmp_code;	/* 0, 1, 2, ... */
				break;
			}
		}
		if (verbose) {
			printf(" (from %s: type = %d, code = %d)\n",
			       Sock_ntop_host(pr->sarecv, pr->salen),
			       icmp->icmp_type, icmp->icmp_code);
		}
		/* Some other ICMP error, recvfrom() again */
	}
	alarm(0);		/* don't leave alarm running */
	Gettimeofday(tv, NULL);	/* get time of packet arrival */
	return (ret);
}

struct proto proto_v4 = { icmpcode_v4, recv_v4, NULL, NULL, NULL, NULL, 0,
	IPPROTO_ICMP, IPPROTO_IP, IP_TTL
};

const char *icmpcode_v6(int code)
{
#ifdef	IPV6
	static char errbuf[100];
	switch (code) {
	case ICMP6_DST_UNREACH_NOROUTE:
		return ("no route to host");
	case ICMP6_DST_UNREACH_ADMIN:
		return ("administratively prohibited");
	case ICMP6_DST_UNREACH_ADDR:
		return ("address unreachable");
	case ICMP6_DST_UNREACH_NOPORT:
		return ("port unreachable");
	default:
		sprintf(errbuf, "[unknown code %d]", code);
		return errbuf;
	}
#endif
}

int recv_v6(int seq, struct timeval *tv)
{
#ifdef	IPV6
	int ret;
	unsigned hlen2, icmp6len;
	ssize_t n;
	socklen_t len;
	struct ip6_hdr *hip6;
	struct icmp6_hdr *icmp6;
	struct udphdr *udp;

	gotalarm = 0;
	alarm(3);
	for (;;) {
		if (gotalarm)
			return (-3);	/* alarm expired */
		len = pr->salen;
		n = recvfrom(recvfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv,
			     &len);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			else
				err_sys("recvfrom error");
		}

		icmp6 = (struct icmp6_hdr *)recvbuf;	/* ICMP header */
		if ((icmp6len = n) < 8)
			continue;	/* not enough to look at ICMP header */

		if (icmp6->icmp6_type == ICMP6_TIME_EXCEEDED &&
		    icmp6->icmp6_code == ICMP6_TIME_EXCEED_TRANSIT) {
			if (icmp6len < 8 + sizeof(struct ip6_hdr) + 4)
				continue;	/* not enough data to look at inner header */

			hip6 = (struct ip6_hdr *)(recvbuf + 8);
			hlen2 = sizeof(struct ip6_hdr);
			udp = (struct udphdr *)(recvbuf + 8 + hlen2);
			if (hip6->ip6_nxt == IPPROTO_UDP &&
			    udp->source == htons(sport) &&
			    udp->dest == htons(dport + seq))
				ret = -2;	/* we hit an intermediate router */
			break;

		} else if (icmp6->icmp6_type == ICMP6_DST_UNREACH) {
			if (icmp6len < 8 + sizeof(struct ip6_hdr) + 4)
				continue;	/* not enough data to look at inner header */

			hip6 = (struct ip6_hdr *)(recvbuf + 8);
			hlen2 = sizeof(struct ip6_hdr);
			udp = (struct udphdr *)(recvbuf + 8 + hlen2);
			if (hip6->ip6_nxt == IPPROTO_UDP &&
			    udp->source == htons(sport) &&
			    udp->dest == htons(dport + seq)) {
				if (icmp6->icmp6_code ==
				    ICMP6_DST_UNREACH_NOPORT)
					ret = -1;	/* have reached destination */
				else
					ret = icmp6->icmp6_code;	/* 0, 1, 2, ... */
				break;
			}
		} else if (verbose) {
			printf(" (from %s: type = %d, code = %d)\n",
			       Sock_ntop_host(pr->sarecv, pr->salen),
			       icmp6->icmp6_type, icmp6->icmp6_code);
		}
		/* Some other ICMP error, recvfrom() again */
	}
	alarm(0);		/* don't leave alarm running */
	Gettimeofday(tv, NULL);	/* get time of packet arrival */
	return (ret);
#endif
}

#ifdef	IPV6
struct proto proto_v6 = { icmpcode_v6, recv_v6, NULL, NULL, NULL, NULL, 0,
	IPPROTO_ICMPV6, IPPROTO_IPV6, IPV6_UNICAST_HOPS
};
#endif

int max_ttl = 30;
int nprobes = 3;
u_short dport = 32768 + 666;

void sock_set_port(struct sockaddr *sa, socklen_t salen, int port)
{
	switch (sa->sa_family) {
	case AF_INET:{
			struct sockaddr_in *sin = (struct sockaddr_in *)sa;

			sin->sin_port = port;
			return;
		}

#ifdef	IPV6
	case AF_INET6:{
			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;

			sin6->sin6_port = port;
			return;
		}
#endif
	}

	return;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
	if (bind(fd, sa, salen) < 0)
		err_sys("bind error");
}

void Setsockopt(int fd, int level, int optname, const void *optval,
		socklen_t optlen)
{
	if (setsockopt(fd, level, optname, optval, optlen) < 0)
		err_sys("setsockopt error");
}

int sock_cmp_addr(const struct sockaddr *sa1, const struct sockaddr *sa2,
		  socklen_t salen)
{
	if (sa1->sa_family != sa2->sa_family)
		return (-1);

	switch (sa1->sa_family) {
	case AF_INET:{
			return (memcmp(&((struct sockaddr_in *)sa1)->sin_addr,
				       &((struct sockaddr_in *)sa2)->sin_addr,
				       sizeof(struct in_addr)));
		}

#ifdef	IPV6
	case AF_INET6:{
			return (memcmp(&((struct sockaddr_in6 *)sa1)->sin6_addr,
				       &((struct sockaddr_in6 *)sa2)->sin6_addr,
				       sizeof(struct in6_addr)));
		}
#endif

#ifdef	AF_UNIX
	case AF_UNIX:{
			return (strcmp(((struct sockaddr_un *)sa1)->sun_path,
				       ((struct sockaddr_un *)sa2)->sun_path));
		}
#endif

#ifdef	HAVE_SOCKADDR_DL_STRUCT
	case AF_LINK:{
			return (-1);	/* no idea what to compare here ? */
		}
#endif
	}
	return (-1);
}

void tv_sub(struct timeval *out, struct timeval *in)
{
	if ((out->tv_usec -= in->tv_usec) < 0) {	/* out -= in */
		--out->tv_sec;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}

void traceloop(void)
{
	int seq, code, done;
	double rtt;
	struct rec *rec;
	struct timeval tvrecv;

	recvfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
	setuid(getuid());	/* don't need special permissions anymore */

#ifdef	IPV6
	if (pr->sasend->sa_family == AF_INET6 && verbose == 0) {
		struct icmp6_filter myfilt;
		ICMP6_FILTER_SETBLOCKALL(&myfilt);
		ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &myfilt);
		ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &myfilt);
		setsockopt(recvfd, IPPROTO_IPV6, ICMP6_FILTER,
			   &myfilt, sizeof(myfilt));
	}
#endif

	sendfd = Socket(pr->sasend->sa_family, SOCK_DGRAM, 0);

	pr->sabind->sa_family = pr->sasend->sa_family;
	sport = (getpid() & 0xffff) | 0x8000;	/* our source UDP port # */
	sock_set_port(pr->sabind, pr->salen, htons(sport));
	Bind(sendfd, pr->sabind, pr->salen);

	sig_alrm(SIGALRM);

	seq = 0;
	done = 0;
	for (ttl = 1; ttl <= max_ttl && done == 0; ttl++) {
		Setsockopt(sendfd, pr->ttllevel, pr->ttloptname, &ttl,
			   sizeof(int));
		bzero(pr->salast, pr->salen);

		printf("%2d ", ttl);
		fflush(stdout);

		for (probe = 0; probe < nprobes; probe++) {
			rec = (struct rec *)sendbuf;
			rec->rec_seq = ++seq;
			rec->rec_ttl = ttl;
			Gettimeofday(&rec->rec_tv, NULL);

			sock_set_port(pr->sasend, pr->salen,
				      htons(dport + seq));
			Sendto(sendfd, sendbuf, datalen, 0, pr->sasend,
			       pr->salen);

			if ((code = (*pr->recv) (seq, &tvrecv)) == -3)
				printf(" *");	/* timeout, no reply */
			else {
				char str[NI_MAXHOST];

				if (sock_cmp_addr
				    (pr->sarecv, pr->salast, pr->salen) != 0) {
					if (getnameinfo
					    (pr->sarecv, pr->salen, str,
					     sizeof(str), NULL, 0, 0) == 0)
						printf(" %s (%s)", str,
						       Sock_ntop_host
						       (pr->sarecv, pr->salen));
					else
						printf(" %s",
						       Sock_ntop_host
						       (pr->sarecv, pr->salen));
					memcpy(pr->salast, pr->sarecv,
					       pr->salen);
				}
				tv_sub(&tvrecv, &rec->rec_tv);
				rtt =
				    tvrecv.tv_sec * 1000.0 +
				    tvrecv.tv_usec / 1000.0;
				printf("  %.3f ms", rtt);

				if (code == -1)	/* port unreachable; at destination */
					done++;
				else if (code >= 0)
					printf(" (ICMP %s)",
					       (*pr->icmpcode) (code));
			}
			fflush(stdout);
		}
		printf("\n");
	}
}

int main(int argc, char **argv)
{
	int c;
	struct addrinfo *ai;
	char *h;

	opterr = 0;		/* don't want getopt() writing to stderr */
	while ((c = getopt(argc, argv, "m:v")) != -1) {
		switch (c) {
		case 'm':
			if ((max_ttl = atoi(optarg)) <= 1)
				err_quit("invalid -m value");
			break;

		case 'v':
			verbose++;
			break;

		case '?':
			err_quit("unrecognized option: %c", c);
		}
	}

	if (optind != argc - 1)
		err_quit("usage: traceroute [ -m <maxttl> -v ] <hostname>");
	host = argv[optind];

	pid = getpid();
	Signal(SIGALRM, sig_alrm);

	ai = Host_serv(host, NULL, 0, 0);

	h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
	printf("traceroute to %s (%s): %d hops max, %d data bytes\n",
	       ai->ai_canonname ? ai->ai_canonname : h, h, max_ttl, datalen);

	/* initialize according to protocol */
	if (ai->ai_family == AF_INET) {
		pr = &proto_v4;
#ifdef	IPV6
	} else if (ai->ai_family == AF_INET6) {
		pr = &proto_v6;
		if (IN6_IS_ADDR_V4MAPPED
		    (&(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr)))
			err_quit("cannot traceroute IPv4-mapped IPv6 address");
#endif
	} else
		err_quit("unknown address family %d", ai->ai_family);

	pr->sasend = ai->ai_addr;	/* contains destination address */
	pr->sarecv = Calloc(1, ai->ai_addrlen);
	pr->salast = Calloc(1, ai->ai_addrlen);
	pr->sabind = Calloc(1, ai->ai_addrlen);
	pr->salen = ai->ai_addrlen;

	traceloop();

	exit(0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值