/*
 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may be used to endorse
 * or promote products derived from this software without specific prior
 * written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * This program comes from Van Jacobson <van@helios.ee.lbl.gov>
 *
 * Totally rewritten by Eric Wassenaar, Nikhef-H, <e07@nikhef.nl>
 *
 * The source of this particular version of the program is available
 * via anonymous ftp from machine 'ftp.nikhef.nl' [192.16.199.1]
 * in the directory '/pub/network' as 'traceroute.tar.Z'
 */

#ifndef lint
static char Version[] = "@(#)traceroute.c	e07@nikhef.nl (Eric Wassenaar) 991603";
#endif

#if defined(apollo) && defined(lint)
#define __attribute(x)
#endif
 
#if defined(__alpha) && defined(__osf__) && __GNUC__
#define __STDC__ 2		/* workaround for alpha <netinet/ip.h> bug */
#endif

#undef  obsolete		/* old code left as a reminder */
#undef  notyet			/* new code for possible future use */

/*
 * traceroute host  - trace the route ip packets follow going to "host".
 *
 * Attempt to trace the route an ip packet would follow to some
 * internet host.  We find out intermediate hops by launching probe
 * packets with a small ttl (time to live) then listening for an
 * icmp "time exceeded" reply from a gateway.  We start our probes
 * with a ttl of one and increase by one until we get an icmp "port
 * unreachable" (which means we got to "host") or hit a max (which
 * defaults to 30 hops & can be changed with the -m flag).  Three
 * probes (change with -q flag) are sent at each ttl setting and a
 * line is printed showing the ttl, address of the gateway and
 * round trip time of each probe.  If the probe answers come from
 * different gateways, the address of each responding system will
 * be printed.  If there is no response within a 3 sec. timeout
 * interval (changed with the -w flag), a "*" is printed for that
 * probe.
 * If the -l flag is used the ttl from the icmp reply will
 * be printed, otherwise it will be printed only if it has an
 * unexpected value.
 *
 * Probe packets are UDP format.  We don't want the destination
 * host to process them so the destination port is set to an
 * unlikely value (if some clod on the destination is using that
 * value, it can be changed with the -p flag).
 *
 * A sample use might be:
 *
 *     [yak 71]% traceroute nis.nsf.net.
 *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
 *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
 *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
 *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
 *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
 *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
 *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
 *
 * Note that lines 2 & 3 are the same.  This is due to a buggy
 * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
 * packets with a zero ttl.
 *
 * A more interesting example is:
 *
 *     [yak 72]% traceroute allspice.lcs.mit.edu.
 *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
 *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
 *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
 *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
 *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
 *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
 *     12  * * *
 *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
 *     14  * * *
 *     15  * * *
 *     16  * * *
 *     17  * * *
 *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
 *
 * (I start to see why I'm having so much trouble with mail to
 * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
 * either don't send ICMP "time exceeded" messages or send them
 * with a ttl too small to reach us.  14 - 17 are running the
 * MIT C Gateway code that doesn't send "time exceeded"s.  God
 * only knows what's going on with 12.
 *
 * The silent gateway 12 in the above may be the result of a bug in
 * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
 * sends an unreachable message using whatever ttl remains in the
 * original datagram.  Since, for gateways, the remaining ttl is
 * zero, the icmp "time exceeded" is guaranteed to not make it back
 * to us.  The behavior of this bug is slightly more interesting
 * when it appears on the destination system:
 *
 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
 *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
 *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
 *      7  * * *
 *      8  * * *
 *      9  * * *
 *     10  * * *
 *     11  * * *
 *     12  * * *
 *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
 *
 * Notice that there are 12 "gateways" (13 is the final
 * destination) and exactly the last half of them are "missing".
 * What's really happening is that rip (a Sun-3 running Sun OS3.5)
 * is using the ttl from our arriving datagram as the ttl in its
 * icmp reply.  So, the reply will time out on the return path
 * (with no notice sent to anyone since icmp's aren't sent for
 * icmp's) until we probe with a ttl that's at least twice the path
 * length.  I.e., rip is really only 7 hops away.  A reply that
 * returns with a ttl of 1 is a clue this problem exists.
 * Traceroute prints a "!" after the time if the ttl is <= 1.
 * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
 * non-standard (HPUX) software, expect to see this problem
 * frequently and/or take care picking the target host of your
 * probes.
 *
 * Other possible annotations after the time are !H, !N, !P (got a host,
 * network or protocol unreachable, respectively), !S or !F (source
 * route failed or fragmentation needed -- neither of these should
 * ever occur and the associated gateway is busted if you see one).  If
 * almost all the probes result in some kind of unreachable, traceroute
 * will give up and exit.
 *
 * Still other annotations are (ttl="n") and (ttl="n"!), where "n" is the
 * ttl from the icmp reply packet we received.  The first form will be
 * printed for every packet if you use the -l option.  The second form
 * will be printed (with or without -l) if this ttl is an unexpected value.
 * We expect that the return path from the n'th hop will contain n hops,
 * otherwise we know the reply packet is coming back via a different path than
 * it went out on.  Unfortunately, not everyone uses the same starting ttl
 * value for icmp messages.  The common ones used by routers are 29
 * (Proteon 8.1 and lower software), 59 (Proteon 8.2), 255 (cisco, BSD
 * since 4.3 tahoe).  30 and 60 are also often used by hosts, and probably
 * by some routers, because they were the BSD TCP ttl values.  This makes
 * some "off by one" return paths hard to detect, you might try removing
 * OLD_BSD_TCP and NEW_BSD_TCP from the case statement if this annoys you.
 * If you are using the lsrr (-g) code with the -l code you will see many
 * bogus "!".  
 *
 * With the -l option you will see the TTL value of the ICMP msg that
 * came back to you, as in:
 *
 * nettlerash> traceroute -l rip.berkeley.edu
 * traceroute to rip.berkeley.edu (128.32.131.22), 30 hops max, 40 byte packets
 *  1  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  0 ms (ttl=59)  10 ms (ttl=59)
 *  2  ccn-nerif35.Berkeley.EDU (128.32.168.35)  10 ms (ttl=58)  10 ms (ttl=58)
 *  3  csgw.Berkeley.EDU (128.32.133.254)  10 ms (ttl=253)  20 ms (ttl=253)
 *  4  rip.Berkeley.EDU (128.32.131.22)  30 ms (ttl=252)  30 ms (ttl=252)
 *
 * This shows that from nettlerash the route goes through two proteons (ttl=
 * 59, then 58), followed by a router using max_ttl to rip which also uses
 * max_ttl.  (-l and printing ttl stuff added by cliff@berkeley.edu.)
 *
 * Notes
 * -----
 * This program must be run by root or be setuid.  (I suggest that
 * you *don't* make it setuid -- casual use could result in a lot
 * of unnecessary traffic on our poor, congested nets.)
 *
 * This program requires a kernel mod that does not appear in any
 * system available from Berkeley:  A raw ip socket using proto
 * IPPROTO_RAW must interpret the data sent as an ip datagram (as
 * opposed to data to be wrapped in a ip datagram).  See the README
 * file that came with the source to this program for a description
 * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
 * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
 * MODIFIED TO RUN THIS PROGRAM.
 *
 * The udp port usage may appear bizarre (well, ok, it is bizarre).
 * The problem is that an icmp message only contains 8 bytes of
 * data from the original datagram.  8 bytes is the size of a udp
 * header so, if we want to associate replies with the original
 * datagram, the necessary information must be encoded into the
 * udp header (the ip id could be used but there's no way to
 * interlock with the kernel's assignment of ip id's and, anyway,
 * it would have taken a lot more kernel hacking to allow this
 * code to set the ip id).  So, to allow two or more users to
 * use traceroute simultaneously, we use this task's pid as the
 * source port (the high bit is set to move the port number out
 * of the "likely" range).  To keep track of which probe is being
 * replied to (so times and/or hop counts don't get confused by a
 * reply that was delayed in transit), we increment the destination
 * port number before each probe.
 *
 * Don't use this as a coding example.  I was trying to find a
 * routing problem and this code sort-of popped out after 48 hours
 * without sleep.  I was amazed it ever compiled, much less ran.
 *
 * I stole the idea for this program from Steve Deering.  Since
 * the first release, I've learned that had I attended the right
 * IETF working group meetings, I also could have stolen it from Guy
 * Almes or Matt Mathis.  I don't know (or care) who came up with
 * the idea first.  I envy the originators' perspicacity and I'm
 * glad they didn't keep the idea a secret.
 *
 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
 * enhancements to the original distribution.
 *
 * I've hacked up a round-trip-route version of this that works by
 * sending a loose-source-routed udp datagram through the destination
 * back to yourself.  Unfortunately, SO many gateways botch source
 * routing, the thing is almost worthless.  Maybe one day...
 *
 *  -- Van Jacobson (van@helios.ee.lbl.gov)
 *     Tue Dec 20 03:50:13 PST 1988
 */

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>

#include <sys/types.h>		/* not always automatically included */
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#if defined(_AIX)
#include <sys/select.h>		/* needed for fd_set */
#endif

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#if 0
#include <netinet/ip_var.h>	/* only needed for MAX_IPOPTLEN */
#endif
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

#include <netdb.h>
#undef NOERROR			/* in <sys/streams.h> on solaris 2.x */
#include <arpa/nameser.h>
#include <resolv.h>

#if defined(linux)
#include "linux.h"		/* special compatibility definitions */
#endif

#include "port.h"		/* various portability definitions */
#include "conf.h"		/* various configuration definitions */
#include "exit.h"		/* exit codes come from <sysexits.h> */
#include "icmp.h"		/* icmp types belong in <netinet/ip_icmp.h> */

#ifndef MAXDNAME
#define MAXDNAME	256	/* maximum length of domain name */
#endif

#ifdef lint
#define EXTERN
#else
#define EXTERN extern
#endif

EXTERN int errno;
EXTERN res_state_t _res;	/* defined in res_init.c */
extern char *version;		/* program version number */

char **optargv = NULL;		/* argument list including default options */
int optargc = 0;		/* number of arguments in new argument list */

char *separator = " ";		/* output field separator -- do not change */

/*
 * Probe packet structure.
 */

#define IPHDRSZ		20	/* actually sizeof(struct ip) */
#define UDPHDRSZ	8	/* actually sizeof(struct udphdr) */

struct ip ippkt;		/* temporary storage */
struct udphdr udppkt;		/* temporary storage */

struct probe			/* format of a (udp) probe packet */
{
	struct ip ip;
	struct udphdr udp;
	struct timeval tv;	/* time packet left */
	u_char seq;		/* sequence number of this packet */
	u_char ttl;		/* ttl packet left with */
};

#ifdef IP_MAXPACKET
#define MAXPACKET IP_MAXPACKET	/* max ip packet size */
#else
#define MAXPACKET 65535
#endif

#ifndef MAX_IPOPTLEN
#define MAX_IPOPTLEN	40	/* max ip options buffer size */
#endif
#define IPOPT_HDRLEN	3	/* actually IPOPT_MINOFF - 1 */

#define TIMLEN sizeof(struct timeval)
#define PROBELEN (IPHDRSZ + UDPHDRSZ + TIMLEN + 2)
#define MAXDATA (MAXPACKET - MAX_IPOPTLEN)
#define MAXFILL (MAXDATA - PROBELEN)

u_char opacket[MAXPACKET];	/* last output (udp) packet */
u_char ipacket[MAXPACKET];	/* last inbound (icmp) packet */

u_char *optbuf;			/* ip options buffer */
int optlen = 0;			/* size of ip options buffer */

/*
 * Socket assignments.
 */

int sendsock;			/* send (udp) socket file descriptor */
int recvsock;			/* receive (icmp) socket file descriptor */
int sockopts = 0;		/* socket options */

#define SOCKOPT_DEBUG     0x0001
#define SOCKOPT_DONTROUTE 0x0010

struct sockaddr_in myaddr;	/* our own source address */
struct sockaddr_in toaddr;	/* address to try to reach */
struct sockaddr_in fromaddr;	/* address we got response from */

struct sockaddr_in *me   = (struct sockaddr_in *)&myaddr;
struct sockaddr_in *to   = (struct sockaddr_in *)&toaddr;
struct sockaddr_in *from = (struct sockaddr_in *)&fromaddr;

struct sockaddr *myaddr_sa   = (struct sockaddr *)&myaddr;
struct sockaddr *toaddr_sa   = (struct sockaddr *)&toaddr;
struct sockaddr *fromaddr_sa = (struct sockaddr *)&fromaddr;

/*
 * IP header parameters.
 */

u_short ident;			/* packet ident */
u_short port = 32768+666;	/* -p  starting udp destination port */
int tos = 0;			/* -t  type of service */
int datalen = PROBELEN;		/* size of raw ip packet minus options */
int dontfrag = 0;		/* -f  prevent fragmentation if set to IP_DF */
int needfrag = 0;		/* new mtu size if more fragmentation needed */
char *source = NULL;		/* -s  specific source of multi-homed sender */

/*
 * Special AS-number lookup parameters.
 */

char *ashost = NULL;		/* -h  explicit whois server for AS lookup */
bool keepopen = FALSE;		/* -k  keep connection to whois server open */
bool lookupas = FALSE;		/* -A  perform AS-number lookup */
bool lookupnet = FALSE;		/* -N  perform network name lookup */

/*
 * Internal control variables.
 */

int min_ttl = 1;		/* -i  initial ttl */
int max_ttl = DEF_MAXHOPS;	/* -m  max number of hops */
int nprobes = DEF_NPROBES;	/* -q  number of probe packets to send */
int maxquit = DEF_MAXQUIT;	/* -Q  quit after this consecutive timeouts */

int waitsecs = DEF_TIMEOUT;	/* -w  time to wait for response (seconds) */
int stopsecs = 0;		/* -c  time to wait between probes (seconds) */
bool verbose = FALSE;		/* -v  print additional information */
bool numeric = FALSE;		/* -n  print addresses numerically */
bool ttlflag = FALSE;		/* -l  print ttl values */
bool alladdr = FALSE;		/* -a  trace all addrs of destination host */
bool summary = FALSE;		/* -S  show per-hop summary statistics */
bool suppress = FALSE;		/* -S  suppress per-probe rtt and ttl report */

/*
 * Miscellaneous buffers.
 */

char hostnamebuf[MAXDNAME+1];
char *hostname;			/* remote host to query about */

#define MAXADDRS	35	/* max address count from gethostnamadr.c */

ipaddr_t hostaddr[MAXADDRS];	/* multiple destination addresses */
int naddrs = 0;			/* count of destination addresses */

#define MAXIPOPT	9	/* MAX_IPOPTLEN-IPOPT_MINOFF / INADDRSZ */
#define MAXLSRR (MAXIPOPT-1)	/* leave room for destination address */

ipaddr_t lsrraddr[MAXLSRR];	/* loose source route addresses */
int nlsrr = 0;			/* count of loose source route addresses */

#define MAXGATE (MAXLSRR*MAXADDRS)

ipaddr_t gateaddr[MAXGATE];	/* all known gateway addresses */
int ngate = 0;			/* count of all known gateway addresses */

/*
 * Miscellaneous definitions.
 */

#define NOT_DOTTED_QUAD	((ipaddr_t)-1)

#define MAXINT8		255
#define MAXINT16	65535

#define MAXSECS		2146	/* 2147,483,647 usec */

/*
 * Internal flags to indicate ICMP packet type.
 * We cannot use the original type and code any more because of
 * overlapping range due to the increased number of subcodes.
 */

#define BAD_ICMP_PACKET -1	/* invalid ICMP reply packet */
#define TIMXCEED_INTRANS 0	/* got to intermediate host */
#define DEST_UNREACHABLE 1	/* generic unreachable indicator */

#define UNREACH_NET		(DEST_UNREACHABLE + ICMP_UNREACH_NET)
#define UNREACH_HOST		(DEST_UNREACHABLE + ICMP_UNREACH_HOST)
#define UNREACH_PROTOCOL	(DEST_UNREACHABLE + ICMP_UNREACH_PROTOCOL)
#define UNREACH_PORT		(DEST_UNREACHABLE + ICMP_UNREACH_PORT)
#define UNREACH_SRCFAIL		(DEST_UNREACHABLE + ICMP_UNREACH_SRCFAIL)
#define UNREACH_NEEDFRAG	(DEST_UNREACHABLE + ICMP_UNREACH_NEEDFRAG)
#define UNREACH_NET_UNKNOWN	(DEST_UNREACHABLE + ICMP_UNREACH_NET_UNKNOWN)
#define UNREACH_HOST_UNKNOWN	(DEST_UNREACHABLE + ICMP_UNREACH_HOST_UNKNOWN)
#define UNREACH_ISOLATED	(DEST_UNREACHABLE + ICMP_UNREACH_ISOLATED)
#define UNREACH_NET_PROHIB	(DEST_UNREACHABLE + ICMP_UNREACH_NET_PROHIB)
#define UNREACH_HOST_PROHIB	(DEST_UNREACHABLE + ICMP_UNREACH_HOST_PROHIB)
#define UNREACH_TOSNET		(DEST_UNREACHABLE + ICMP_UNREACH_TOSNET)
#define UNREACH_TOSHOST		(DEST_UNREACHABLE + ICMP_UNREACH_TOSHOST)
#define UNREACH_ADM_PROHIB	(DEST_UNREACHABLE + ICMP_UNREACH_ADM_PROHIB)
#define UNREACH_PREC_VIOL	(DEST_UNREACHABLE + ICMP_UNREACH_PREC_VIOL)
#define UNREACH_PREC_CUT	(DEST_UNREACHABLE + ICMP_UNREACH_PREC_CUT)

/*
 * Useful macro definitions.
 */

#include "defs.h"		/* declaration of functions */

#define strlength(s)	(int)strlen(s)

#define is_space(c)	(isascii(c) && isspace(c))

#define newlist(a,n,t)	(t *)xalloc((ptr_t *)(a), (siz_t)((n)*sizeof(t)))
#define newstruct(t)	(t *)xalloc((ptr_t *)NULL, (siz_t)(sizeof(t)))
#define newstring(s)	(char *)xalloc((ptr_t *)NULL, (siz_t)(strlen(s)+1))
#define newstr(s)	strcpy(newstring(s), s)
#define xfree(a)	(void) free((ptr_t *)(a))

#ifdef DEBUG
#define assert(condition)\
{\
	if (!(condition))\
	{\
		(void) fprintf(stderr, "assertion botch: ");\
		(void) fprintf(stderr, "%s(%d): ", __FILE__, __LINE__);\
		(void) fprintf(stderr, "%s\n", "condition");\
		exit(EX_SOFTWARE);\
	}\
}
#else
#define assert(condition)
#endif

char Usage[] =
"\
Usage: %s [-g gateway] [-m maxhops] [-q nqueries] host [packetsize]\n\
Flags: [-a] [-f] [-i initial_ttl] [-l] [-n] [-Q maxquit] [-r] [-S] [-v]\n\
Other: [-c stoptime] [-p port] [-s source_addr] [-t tos] [-w waittime]\n\
ASnum: [-A] [-h server] [-k] [-N]\
";

/*
** MAIN -- Start of program traceroute
** -----------------------------------
**
**	Exits:
**		Various possibilities from <sysexits.h> among which
**		EX_SUCCESS	The destination host has been reached
**		EX_UNAVAILABLE	Could not reach the destination host
**		EX_USAGE	Improper parameter/option specified
*/

int
main(argc, argv)
int argc;
char *argv[];
{
	bool got_somewhere = FALSE;	/* set if some address was reached */
	char *program;			/* name of this program */
	ipaddr_t addr;
	struct in_addr inaddr;
	struct hostent *hp;
	register int i;
	register char *option;

	assert(sizeof(u_int) >= 4);	/* probably paranoid */
#ifdef obsolete
	assert(sizeof(u_short) == 2);	/* perhaps less paranoid */
	assert(sizeof(ipaddr_t) == 4);	/* but this is critical */
#endif /*obsolete*/

/*
 * Synchronize stdout and stderr in case output is redirected.
 */
	linebufmode(stdout);

/*
 * Initialize resolver. Shorter timeout values are set later.
 */
	(void) res_init();

/*
 * Check command line options.
 * Interpolate default options and parameters.
 */
	if (argc < 1 || argv[0] == NULL)
		exit(EX_USAGE);

	option = getenv("TRACEROUTE_DEFAULTS");
	if (option != NULL)
	{
		set_defaults(option, argc, argv);
		argc = optargc; argv = optargv;
	}

	program = rindex(argv[0], '/');
	if (program++ == NULL)
		program = argv[0];

	while (argc > 1 && argv[1] != NULL && argv[1][0] == '-')
	{
	    for (option = &argv[1][1]; *option != '\0'; option++)
	    {
		switch (*option)
		{
		    case 'A':
			/* lookup AS-number */
			lookupas = TRUE;
			break;

		    case 'a':
			/* probe all of multiple addresses */
			alladdr = TRUE;
			break;

		    case 'c':
			/* time to delay between probes to cisco */
			stopsecs = getval(argv[2], "stoptime value", 0, MAXSECS);
			argc--, argv++;
			break;

		    case 'd':
			/* socket level debugging */
			sockopts |= SOCKOPT_DEBUG;
			break;

		    case 'f':
			/* prevent fragmentation */
			dontfrag = IP_DF;
			break;

		    case 'g':
			/* loose source route gateway address */
			if (argv[2] == NULL || argv[2][0] == '-')
				fatal("Missing gateway name");
			if (nlsrr >= MAXLSRR)
				fatal("Maximum %s gateways", itoa(MAXLSRR));
			lsrraddr[nlsrr++] = getgate(argv[2]);
			argc--, argv++;
			break;

		    case 'h':
			/* explicit whois server host */
			if (argv[2] == NULL || argv[2][0] == '-')
				fatal("Missing whois server");
			ashost = argv[2];
			argc--, argv++;
			break;

		    case 'i':
			/* initial ttl value */
			min_ttl = getval(argv[2], "initial ttl", 1, max_ttl);
			argc--, argv++;
			break;

		    case 'k':
			/* keep whois connection open */
			keepopen = TRUE;
			break;

		    case 'l':
			/* list ttl values */
			ttlflag = TRUE;
			break;

		    case 'm':
			/* maximum hop (ttl) count */
			max_ttl = getval(argv[2], "maxhops value", min_ttl, MAXINT8);
			argc--, argv++;
			break;

		    case 'N':
			/* lookup network name */
			lookupnet = TRUE;
			break;

		    case 'n':
			/* print numeric addresses */
			numeric = TRUE;
			break;

		    case 'p':
			/* specific udp destination port number */
			port = getval(argv[2], "port number", 1, MAXINT16);
			argc--, argv++;
			break;

		    case 'Q':
			/* maximum number of consecutive timeouts */
			maxquit = getval(argv[2], "maxquit value", 1, MAXINT16);
			argc--, argv++;
			break;

		    case 'q':
			/* number of probe packets per hop */
			nprobes = getval(argv[2], "nqueries value", 1, MAXINT16);
			argc--, argv++;
			break;

		    case 'r':
			/* bypass normal routing */
			sockopts |= SOCKOPT_DONTROUTE;
			break;

		    case 'S':
			/* show per-hop summary statistics */
			summary = TRUE;
			suppress = !suppress;
			break;

		    case 's':
			/* explicit own source address */
			if (argv[2] == NULL || argv[2][0] == '-')
				fatal("Missing source address");
			source = argv[2];
			argc--, argv++;
			break;

		    case 't':
			/* type-of-service */
			tos = getval(argv[2], "tos value", 0, MAXINT8);
			argc--, argv++;
			break;

		    case 'v':
			/* verbose printout */
			verbose = TRUE;
			break;

		    case 'w':
			/* timeout to wait for replies */
			waitsecs = getval(argv[2], "waittime value", 1, MAXSECS);
			argc--, argv++;
			break;

		    case 'V':
			printf("%s\n", version);
			exit(EX_SUCCESS);

		    default:
			fatal(Usage, program);
		}
	    }

	    argc--, argv++;
	}

/*
 * Fetch (mandatory) remote host address(es) to probe.
 */
	if (argc < 2 || argv[1] == NULL)
		fatal(Usage, program);

	hostname = argv[1];
	addr = inet_addr(hostname);
	inaddr.s_addr = addr;

	if (addr == NOT_DOTTED_QUAD)
	{
		hp = gethostbyname(hostname);
		if (hp == NULL)
		{
			error("Unknown host %s", hostname);
			exit(EX_NOHOST);
		}

		hostname = strncpy(hostnamebuf, hp->h_name, MAXDNAME);
		hostname[MAXDNAME] = '\0';

		for (i = 0; i < MAXADDRS && hp->h_addr_list[i]; i++)
		{
			bcopy(hp->h_addr_list[i], (char *)&inaddr, INADDRSZ);
			hostaddr[i] = inaddr.s_addr;
		}
		naddrs = i;

		/* prep the address cache */
		for (i = 0; i < naddrs; i++)
		{
			inaddr.s_addr = hostaddr[i];
			(void) maphostbyaddr(inaddr);
		}
	}
	else
	{
		hostname = strcpy(hostnamebuf, inetname(inaddr));

		hostaddr[0] = addr;
		naddrs = 1;
	}

/*
 * Check for optional ip size (minus options) of output packet.
 */
	if (argc > 2 && argv[2] != NULL)
		datalen = getval(argv[2], "packet size", PROBELEN, MAXDATA);

	/* rest is undefined */
	if (argc > 3)
		fatal(Usage, program);

/*
 * Miscellaneous initialization.
 */
	/* becomes the udp source port */
	ident = (getpid() & 0xFFFF) | 0x8000;

	/* check the udp destination port */
	if (((int)port + ((max_ttl - min_ttl + 1)*nprobes)) > MAXINT16)
		fatal("Destination port value overflow");

	/* set shorter nameserver timeout */
	_res.retry = DEF_RETRIES;	/* number of datagram retries */
	_res.retrans = DEF_RETRANS;	/* timeout between retries */

	/* allocate IP output socket and ICMP input socket */
	check_proto();
	get_socket();

/*
 * Define explicit source address in output packet, if specified.
 */
	bzero((char *)&myaddr, sizeof(myaddr));
	me->sin_family = AF_INET;
	me->sin_addr.s_addr = INADDR_ANY;
	me->sin_port = 0;

	if (source)
	{
		addr = inet_addr(source);
		if (addr == NOT_DOTTED_QUAD)
			fatal("Illegal source address %s", source);

		me->sin_addr.s_addr = addr;
#ifndef IP_HDRINCL
		if (bind(sendsock, myaddr_sa, sizeof(myaddr)) < 0)
		{
			perror("bind");
			exit(EX_OSERR);
		}
#endif
	}

/*
 * Start of main loop.
 */
	/* don't need special privileges any more */
	(void) setuid(getuid());

	/* probe all addresses successively */
	for (i = 0; i < naddrs; i++)
	{
		if (ping(hostaddr[i]))
			got_somewhere = TRUE;

		if (!alladdr)
			break;
	}

	/* indicate success or failure */
	return(got_somewhere ? EX_SUCCESS : EX_UNAVAILABLE);
	/*NOTREACHED*/
}

/*
** SET_DEFAULTS -- Interpolate default options and parameters in argv
** ------------------------------------------------------------------
**
**	The TRACEROUTE_DEFAULTS env variable gives customized options.
**
**	Returns:
**		None.
**
**	Outputs:
**		Creates ``optargv'' vector with ``optargc'' arguments.
*/

void
set_defaults(option, argc, argv)
char *option;				/* option string */
int argc;				/* original command line arg count */
char *argv[];				/* original command line arguments */
{
	register char *p, *q;
	register int i;

/*
 * Allocate new argument vector.
 */
	optargv = newlist(NULL, 2, char *);
	optargv[0] = argv[0];
	optargc = 1;

/*
 * Construct argument list from option string.
 */
	for (q = newstr(option), p = q; *p != '\0'; p = q)
	{
		while (is_space(*p))
			p++;

		if (*p == '\0')
			break;

		for (q = p; *q != '\0' && !is_space(*q); q++)
			continue;

		if (*q != '\0')
			*q++ = '\0';

		optargv = newlist(optargv, optargc+2, char *);
		optargv[optargc] = p;
		optargc++;
	}

/*
 * Append command line arguments.
 */
	for (i = 1; i < argc && argv[i] != NULL; i++)
	{
		optargv = newlist(optargv, optargc+2, char *);
		optargv[optargc] = argv[i];
		optargc++;
	}

	/* and terminate */
	optargv[optargc] = NULL;
}

/*
** GETVAL -- Decode parameter value and perform range check
** --------------------------------------------------------
**
**	Returns:
**		Parameter value if successfully decoded.
**		Aborts in case of syntax or range errors.
*/

int
getval(optstring, optname, minvalue, maxvalue)
char *optstring;			/* parameter from command line */
char *optname;				/* descriptive name of option */
int minvalue;				/* minimum value for option */
int maxvalue;				/* maximum value for option */
{
	register int optvalue;

	if (optstring == NULL || optstring[0] == '-')
		fatal("Missing %s", optname);

	optvalue = atoi(optstring);

	if (optvalue == 0 && optstring[0] != '0')
		fatal("Invalid %s %s", optname, optstring);

	if (optvalue < minvalue)
		fatal("Minimum %s %s", optname, itoa(minvalue));

	if (maxvalue > 0 && optvalue > maxvalue)
		fatal("Maximum %s %s", optname, itoa(maxvalue));

	return(optvalue);
}

/*
** FATAL -- Abort program when illegal option encountered
** ------------------------------------------------------
**
**	Returns:
**		Aborts after issuing error message.
*/

void /*VARARGS1*/
fatal(fmt, a, b, c, d)
char *fmt;				/* format of message */
char *a, *b, *c, *d;			/* optional arguments */
{
	(void) fprintf(stderr, fmt, a, b, c, d);
	(void) fprintf(stderr, "\n");
	exit(EX_USAGE);
}


/*
** ERROR -- Issue error message to error output
** --------------------------------------------
**
**	Returns:
**		None.
*/

void /*VARARGS1*/
error(fmt, a, b, c, d)
char *fmt;				/* format of message */
char *a, *b, *c, *d;			/* optional arguments */
{
	(void) fprintf(stderr, fmt, a, b, c, d);
	(void) fprintf(stderr, "\n");
}

/*
** CHECK_PROTO -- Check protocol numbers
** -------------------------------------
**
**	traceroute uses protocol numbers as defined in <netinet/in.h>.
**	Verify whether they correspond to the values in /etc/protocols.
**	This is probably rather paranoid.
*/

void
check_proto()
{
	struct protoent *proto;

	proto = getprotobyname("ip");
	if (proto == NULL)
	{
		(void) fprintf(stderr, "ip: unknown protocol\n");
		exit(EX_OSFILE);
	}
	if (proto->p_proto != IPPROTO_IP)
	{
		(void) fprintf(stderr, "ip protocol %d should be %d\n",
			proto->p_proto, IPPROTO_IP);
		exit(EX_CONFIG);
	}

	proto = getprotobyname("icmp");
	if (proto == NULL)
	{
		(void) fprintf(stderr, "icmp: unknown protocol\n");
		exit(EX_OSFILE);
	}
	if (proto->p_proto != IPPROTO_ICMP)
	{
		(void) fprintf(stderr, "icmp protocol %d should be %d\n",
			proto->p_proto, IPPROTO_ICMP);
		exit(EX_CONFIG);
	}

	proto = getprotobyname("tcp");
	if (proto == NULL)
	{
		(void) fprintf(stderr, "tcp: unknown protocol\n");
		exit(EX_OSFILE);
	}
	if (proto->p_proto != IPPROTO_TCP)
	{
		(void) fprintf(stderr, "tcp protocol %d should be %d\n",
			proto->p_proto, IPPROTO_TCP);
		exit(EX_CONFIG);
	}

	proto = getprotobyname("udp");
	if (proto == NULL)
	{
		(void) fprintf(stderr, "udp: unknown protocol\n");
		exit(EX_OSFILE);
	}
	if (proto->p_proto != IPPROTO_UDP)
	{
		(void) fprintf(stderr, "udp protocol %d should be %d\n",
			proto->p_proto, IPPROTO_UDP);
		exit(EX_CONFIG);
	}
}

/*
** GET_SOCKET -- Allocate and configure output and input socket
** ------------------------------------------------------------
**
**	A raw ip socket is allocated for sending the probe packets.
**	A raw udp socket is allocated instead on those platforms
**	which do not support raw ip socket manipulation.
**	A raw icmp socket is allocated for receiving icmp messages.
**	These sockets can be allocated only by root.
**	Extra socket options are set as requested on the command line.
**
**	Inputs:
**		Global ``datalen'' contains the ip packet size.
**		This is used to set the size of the socket buffer.
**
**	Outputs:
**		Global ``recvsock'' is the input  socket descriptor.
**		Global ``sendsock'' is the output socket descriptor.
*/

void
get_socket()
{
	int on = 1;

/*
 * Allocate input socket to receive replies.
 */
	recvsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (recvsock < 0)
	{
		perror("icmp socket");
		exit(EX_OSERR);
	}

	if (sockopts & SOCKOPT_DEBUG)
		(void) setsockopt(recvsock, SOL_SOCKET, SO_DEBUG,
			(char *)&on, sizeof(on));

	if (sockopts & SOCKOPT_DONTROUTE)
		(void) setsockopt(recvsock, SOL_SOCKET, SO_DONTROUTE,
			(char *)&on, sizeof(on));

/*
 * Allocate output socket to send probe packets.
 */
#ifdef NO_RAW_IP
	sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
	if (sendsock < 0)
	{
		perror("udp socket");
		exit(EX_OSERR);
	}
#else
	sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sendsock < 0)
	{
		perror("raw socket");
		exit(EX_OSERR);
	}
#endif /*NO_RAW_IP*/

#ifdef SO_SNDBUF
	if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF,
		(char *)&datalen, sizeof(datalen)) < 0)
	{
		perror("setsockopt: SO_SNDBUF");
		exit(EX_OSERR);
	}
#endif

#ifdef IP_HDRINCL
	if (setsockopt(sendsock, IPPROTO_IP, IP_HDRINCL,
		(char *)&on, sizeof(on)) < 0)
	{
		perror("setsockopt: IP_HDRINCL");
		exit(EX_OSERR);
	}
#endif

	if (sockopts & SOCKOPT_DEBUG)
		(void) setsockopt(sendsock, SOL_SOCKET, SO_DEBUG,
			(char *)&on, sizeof(on));

	if (sockopts & SOCKOPT_DONTROUTE)
		(void) setsockopt(sendsock, SOL_SOCKET, SO_DONTROUTE,
			(char *)&on, sizeof(on));
}

/*
** SET_OPTIONS -- Initialize IP options
** ------------------------------------
**
**	Special IP options are set up as requested on the command line.
**	This will extend the IP header with the IP options buffer.
**	Not all platforms may support this, even if IP_OPTIONS exists.
**
**	Inputs:
**		``toaddr''  contains final destination address.
**		``datalen'' contains the basic IP packet size.
**
**	Outputs:
**		Stores address of IP options buffer in ``optbuf''.
**		Stores size of IP options buffer in ``optlen''.
**
**	Side effects:
**		Extends the size of the socket send buffer.
*/

#ifdef IP_OPTIONS

void
set_options(addrlist, naddr)
ipaddr_t addrlist[];			/* list of gateway addresses */
int naddr;				/* count of gateway addresses */
{
	static u_char ipoptbuf[MAX_IPOPTLEN];
	u_char *ipopt = ipoptbuf;
	register int i;

	bzero((char *)ipoptbuf, sizeof(ipoptbuf));

	/* make sure the remainder is 32-bit aligned */
	ipopt[IPOPT_OPTVAL] = IPOPT_NOP;
	ipopt++;

/*
 * Store the loose source route option addresses.
 */
	ipopt[IPOPT_OPTVAL] = IPOPT_LSRR;
	ipopt[IPOPT_OLEN]   = IPOPT_HDRLEN + (naddr + 1)*INADDRSZ;
	ipopt[IPOPT_OFFSET] = IPOPT_MINOFF;
	ipopt += IPOPT_HDRLEN;

	/* store intermediate gateway addresses */
	for (i = 0; i < naddr; i++)
	{
		struct in_addr inaddr;

		inaddr.s_addr = addrlist[i];
		bcopy((char *)&inaddr, (char *)ipopt, INADDRSZ);
		ipopt += INADDRSZ;
	}

	/* and the final destination */
	bcopy((char *)&to->sin_addr, (char *)ipopt, INADDRSZ);
	ipopt += INADDRSZ;

	/* export start and size of options */
	optbuf = ipoptbuf;
	optlen = ipopt - ipoptbuf;

	/* to keep certain platforms happy */
	if (optlen < sizeof(ipoptbuf))
		optlen = sizeof(ipoptbuf);

/*
 * Announce the use of the ip options. They will be automatically
 * inserted when the packet is sent, but not on 4.4BSD platforms.
 */
#ifndef BSD44
	if (setsockopt(sendsock, IPPROTO_IP, IP_OPTIONS,
		(char *)optbuf, optlen) < 0)
	{
		perror("setsockopt: IP_OPTIONS");
		exit(EX_OSERR);
	}
#endif /*BSD44*/

/*
 * Extend the size of the socket buffer to the full packet size.
 */
#ifdef SO_SNDBUF
	{
		int packetlen = datalen + optlen;

		if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF,
			(char *)&packetlen, sizeof(packetlen)) < 0)
		{
			perror("setsockopt: SO_SNDBUF");
			exit(EX_OSERR);
		}
	}
#endif
}

#endif /*IP_OPTIONS*/

/*
** PING -- Probe given address of destination host
** -----------------------------------------------
**
**	Wrapper for trace_route() to hide interrupt handling.
**
**	Returns:
**		TRUE if the destination was reached.
**		FALSE otherwise.
*/

bool interrupted = FALSE;	/* set upon interrupt if synchronous */
bool synchronous = FALSE;	/* perform synchronous interrupt handling */

static jmp_buf interrupt_buf;

static sigtype_t /*ARGSUSED*/
interrupt(sig)
int sig;
{
	if (!synchronous)
	{
		longjmp(interrupt_buf, 1);
		/*NOTREACHED*/
	}
	interrupted = TRUE;
	(void) signal(SIGINT, interrupt);
	sig_return(0);
}

bool
ping(addr)
ipaddr_t addr;				/* address of destination */
{
	bool got_there;			/* set if destination reached */

	if (setjmp(interrupt_buf) != 0)
	{
		printf("\n");
		(void) fprintf(stderr, "(interrupt)\n");
		(void) signal(SIGINT, SIG_DFL);
		return(FALSE);
	}

	interrupted = FALSE;
	(void) signal(SIGINT, interrupt);
	got_there = trace_route(addr);
	if (interrupted)
		longjmp(interrupt_buf, 1);
	(void) signal(SIGINT, SIG_DFL);
	return(got_there);
}

/*
** TRACE_ROUTE -- Probe given address of destination host
** ------------------------------------------------------
**
**	Returns:
**		TRUE if the destination was reached.
**		FALSE otherwise.
*/

bool
trace_route(addr)
ipaddr_t addr;				/* address of destination */
{
	int ttl;			/* current hop count */
	int probe;			/* current probe number */
	int seq = 0;			/* packet sequence number */
	int packetlen;			/* total ip packet size */
	bool got_there = FALSE;		/* set if destination reached */

	/* setup destination address */
	bzero((char *)&toaddr, sizeof(toaddr));
	to->sin_family = AF_INET;
	to->sin_addr.s_addr = addr;
	to->sin_port = 0;

	/* set ip options and compute options buffer size */
	if (nlsrr > 0)
	{
#ifdef IP_OPTIONS
#ifdef BSD44
		/* perform first hop routing */
		addr = lsrraddr[0];
		set_options(&lsrraddr[1], nlsrr - 1);
#else
		/* store all gateway addresses */
		set_options(lsrraddr, nlsrr);
#endif /*BSD44*/
#else /*IP_OPTIONS*/
		error("gateway source routing not supported");
		nlsrr = 0;
#endif /*IP_OPTIONS*/
	}

	/* total ip packet size includes options */
	packetlen = datalen + optlen;

	printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));
	if (source)
		printf(" from %s", source);
	printf(": %d-%d hops, %d byte packets\n", min_ttl, max_ttl, packetlen);

	/* actual first hop may have been reset */
	to->sin_addr.s_addr = addr;

/*
 * All set. Start emitting probe packets.
 */
	for (ttl = min_ttl; ttl <= max_ttl && !interrupted; ttl++)
	{
		ipaddr_t lastaddr = 0;	/* address we got icmp response from */
		int unreachable = 0;	/* count of unreachable responses */
		int timeouts = 0;	/* count of receive timeouts */
		int star = 0;		/* count of consecutive timeouts */
		int mtu = 0;		/* set if forced to use new mtu size */

		clear_stats();

		printf("%2d", ttl);

		(void) fflush(stdout);

		for (probe = 0; probe < nprobes && star < maxquit; probe++)
		{
			register int cc;
			int icmpcode;
			time_t rtt;
			struct ip *ip;
			struct timeval sendtime, recvtime;
			struct timeval waittime, nexttime;

			/* initialize maximum timeout */
			waittime.tv_sec = waitsecs;
			waittime.tv_usec = 0;

		/*
		 * Send probe packet. Decrease packet size if it is too long.
		 */
			(void) gettimeofday(&sendtime, (struct timezone *)NULL);

			cc = send_probe(sendsock, ++seq, ttl, packetlen-optlen);
			if (cc == 0)
				mtu = select_mtu(packetlen);
retry:
		/*
		 * Wait for a reply packet or timeout, if successfully sent.
		 * Delay printing the timeout marker until we have an address.
		 */
			(void) gettimeofday(&nexttime, (struct timezone *)NULL);

			if (cc > 0)
				cc = recv_reply(recvsock, &waittime);
			if (cc <= 0)
			{
				star++;
				timeouts++;
				continue;	/* next probe */
			}

		/*
		 * Decrement waittime in case we are catching packets that
		 * do not belong to us, e.g. because somebody is ping-ing.
		 */
			(void) gettimeofday(&recvtime, (struct timezone *)NULL);

			icmpcode = check_reply(ipacket, cc, seq);
			if (icmpcode == BAD_ICMP_PACKET)
			{
				(void) tvsub(&recvtime, &nexttime);
				(void) tvsub(&waittime, &recvtime);
				goto retry;	/* not our reply */
			}

		/*
		 * Valid reply packet received. Compute rtt value.
		 * Print remote address (if different from previous case).
		 */
			if (from->sin_addr.s_addr != lastaddr)
			{
				print_from(ipacket, cc, from->sin_addr);
				lastaddr = from->sin_addr.s_addr;
			}

			for (; star > 0; star--)
				printf("%s *", separator);

			rtt = tvsub(&recvtime, &sendtime);

			record_stats(rtt);

			if (!suppress)
				printf("%s %s ms", separator, tvprint(rtt));

		/*
		 * Check ttl for unexpected values. Print if necessary.
		 */
			ip = (struct ip *)ipacket;
			if (regular_ttl((int)ip->ip_ttl + (ttl - 1)))
			{
				if (!suppress && ttlflag)
					printf(" (ttl=%d)", (int)ip->ip_ttl);
			}
			else
			{
				if (!suppress && (ttlflag || (nlsrr == 0)))
					printf(" (ttl=%d!)", (int)ip->ip_ttl);
			}

		/*
		 * Certain routers will erroneously report ICMP_UNREACH_PORT
		 * if they are specified as loose source route gateway hosts.
 		 * We cheat and assume they are intermediate hosts.
		 */
			if (nlsrr > 0 && icmpcode == UNREACH_PORT)
			{
				if (gatewayaddr(from->sin_addr))
				{
					printf(" !G");
					icmpcode = TIMXCEED_INTRANS;
				}
			}

		/*
		 * Certain target routers fail to report ICMP_UNREACH_PORT.
		 */
			if (from->sin_addr.s_addr == addr && nlsrr == 0)
			{
				if (icmpcode == TIMXCEED_INTRANS)
				{
					printf(" !A");
			    		icmpcode = UNREACH_PORT;
				}
			}

		/*
		 * Check icmp reply code for special cases.
		 */
			switch (icmpcode)
			{
			    case TIMXCEED_INTRANS:
				/* intermediate host -- nothing to do */
				break;

			    case UNREACH_PORT:
				/* destination host -- normal case */
				if (ip->ip_ttl <= 1)
					printf(" !");
				got_there = TRUE;
				break;

			    case UNREACH_PROTOCOL:
				printf(" !P");
				got_there = TRUE;
				break;

			    case UNREACH_SRCFAIL:
				printf(" !S");
				unreachable++;
				break;

			    case UNREACH_HOST:
			    case UNREACH_HOST_UNKNOWN:
			    case UNREACH_HOST_PROHIB:
				printf(" !H");
				unreachable++;
				break;

			    case UNREACH_NET:
			    case UNREACH_NET_UNKNOWN:
			    case UNREACH_NET_PROHIB:
				printf(" !N");
				unreachable++;
				break;

			    case UNREACH_ISOLATED:
			    case UNREACH_ADM_PROHIB:
			    case UNREACH_PREC_VIOL:
			    case UNREACH_PREC_CUT:
				printf(" !U");
				unreachable++;
				break;

			    case UNREACH_TOSNET:
			    case UNREACH_TOSHOST:
				printf(" !T");
				unreachable++;
				break;

			    case UNREACH_NEEDFRAG:
				mtu = needfrag;
				if (mtu > 0)
					printf(" !F=%d", mtu);
				else
					printf(" !F");
				if (mtu == 0)
					mtu = select_mtu(packetlen);
				unreachable++;
				break;

			    default:
				printf(" ?");
				unreachable++;
				break;
			}

			(void) fflush(stdout);

			/* optionally pause between probes */
			if (stopsecs != 0)
				(void) sleep((unsigned)stopsecs);

		} /* for probe < nprobes */

		for (; star > 0; star--)
			printf("%s *", separator);

		if (summary)
			print_stats(probe);

		printf("\n");

	/*
	 * Check for special conditions after this series of probes.
	 */
		/* terminate if destination reached */
		if (got_there)
			break;

		/* retry same ttl with shorter mtu size */
		if ((mtu >= (PROBELEN + optlen)) && (mtu < packetlen))
		{
			printf("(switching to new packet size %d)\n", mtu);
			packetlen = mtu;
			ttl--;
			continue;
		}

		/* abort if all probes yielded unreachable */
		if (unreachable && (unreachable + timeouts >= probe))
			break;

	} /* for ttl <= max_ttl */

	/* indicate success or failure */
	return(got_there);
}

/*
** SEND_PROBE -- Send out probe packet
** -----------------------------------
**
**	Inputs:
**		Global ``ident''    contains our identification.
**		Global ``port''     contains the base port offset.
**		Global ``tos''      contains the type of service.
**		Global ``dontfrag'' contains the don't fragment flag.
**		Global ``toaddr''   contains destination address.
**		Global ``myaddr''   contains own source address.
**		Global ``optlen''   contains the size of the IP options.
**
**	Returns:
**		Number of bytes sent.
**		0 if packet size is too large.
**		-1 in case of other errors.
*/

int
send_probe(sock, seq, ttl, iplen)
int sock;				/* output socket descriptor */
int seq;				/* sequence no. determines port */
int ttl;				/* current ttl value */
int iplen;				/* size of ip packet minus options */
{
	u_char *pkt = opacket;
	struct ip *ip = &ippkt;		/* temporary ip header */
	struct udphdr *udp = &udppkt;	/* temporary udp header */
	struct timeval tv;
	int iphdrlen = IPHDRSZ;		/* size of ip header minus options */
	int udplen = iplen - iphdrlen;	/* size of udp packet */
	register int n;

/*
 * On traditional platforms, the kernel inserts the IP options.
 * On 4.4BSD-based platforms, we must construct them ourselves.
 */
#ifdef BSD44
	iphdrlen += optlen;		/* size of ip header plus options */
	iplen += optlen;		/* size of ip packet plus options */
#endif /*BSD44*/

/*
 * Fill in ip header. NOTE: certain fields are in machine byte order.
 * On traditional platforms, the kernel stores ip_v and ip_hl itself,
 * and properly computes ip_hl depending on the IP options.
 * The kernel always generates ip_id and ip_sum.
 */
	ip->ip_v   = (u_char)  IPVERSION;	/* version */
	ip->ip_hl  = (u_char)  (iphdrlen >> 2);	/* header length */
	ip->ip_tos = (u_char)  tos;		/* type of service */
	ip->ip_len = (short)   iplen;		/* total size */
	ip->ip_id  = (u_short) 0;		/* identification */
	ip->ip_off = (short)   dontfrag;	/* fragment offset */
	ip->ip_ttl = (u_char)  ttl;		/* time-to-live */
	ip->ip_p   = (u_char)  IPPROTO_UDP;	/* protocol */
	ip->ip_sum = (u_short) 0;		/* checksum */
	ip->ip_src = me->sin_addr;		/* source address */
	ip->ip_dst = to->sin_addr;		/* destination address */

/*
 * Some platforms require the entire header to be in network byte order.
 */
#ifdef RAW_IP_NET_ORDER
	ip->ip_len = htons((u_short)ip->ip_len);
	ip->ip_off = htons((u_short)ip->ip_off);
#endif /*RAW_IP_NET_ORDER*/

/*
 * Fill in udp header. There is no udp checksum.
 */
	udp->uh_sport = htons(ident);		/* source port */
	udp->uh_dport = htons(port+seq);	/* destination port */
	udp->uh_ulen  = htons((u_short)udplen);	/* udp size */
	udp->uh_sum   = 0;			/* checksum */

/*
 * Construct the output packet.
 */
	bcopy((char *)ip, (char *)pkt, IPHDRSZ);
	pkt += IPHDRSZ;

#ifdef BSD44
	bcopy((char *)optbuf, (char *)pkt, optlen);
	pkt += optlen;
#endif /*BSD44*/

	bcopy((char *)udp, (char *)pkt, UDPHDRSZ);
	pkt += UDPHDRSZ;

/*
 * Fill in remainder of output packet (actually unused).
 */
	(void) gettimeofday(&tv, (struct timezone *)NULL);
	bcopy((char *)&tv, (char *)pkt, TIMLEN);
	pkt += TIMLEN;

	pkt[0] = (u_char)seq;
	pkt[1] = (u_char)ttl;

/*
 * If we are sending only the udp packet instead of the ip packet,
 * we request an explicit ttl value for the resulting ip packet.
 * On new platforms we can also request an explicit tos value.
 */
#ifdef NO_RAW_IP

	if (setsockopt(sock, IPPROTO_IP, IP_TTL,
		(char *)&ttl, sizeof(ttl)) < 0)
	{
		perror("setsockopt: IP_TTL");
		exit(EX_OSERR);
	}

#ifdef IP_TOS
	if ((tos > 0) && setsockopt(sock, IPPROTO_IP, IP_TOS,
		(char *)&tos, sizeof(tos)) < 0)
	{
		perror("setsockopt: IP_TOS");
		exit(EX_OSERR);
	}
#endif

#endif /*NO_RAW_IP*/

/*
 * Send out the probe packet. Either full raw ip, or only udp.
 */
#ifdef NO_RAW_IP

	/* skip the ip header */
	udp = (struct udphdr *)&opacket[iphdrlen];
	n = sendto(sock, (char *)udp, udplen, 0, toaddr_sa, sizeof(toaddr));
	if (n > 0)
		n += iphdrlen;

#else /*NO_RAW_IP*/

	/* send raw ip packet */
	ip = (struct ip *)opacket;
	n = sendto(sock, (char *)ip, iplen, 0, toaddr_sa, sizeof(toaddr));

#endif /*NO_RAW_IP*/

/*
 * Check for errors. Report packet too large for fragmentation.
 */
	if (n < 0 || n != iplen)
	{
#ifdef EMSGSIZE
		/* message too long */
		if (n < 0 && errno == EMSGSIZE)
			return(0);
#endif /*EMSGSIZE*/

#if defined(apollo)
		/* message too long */
		if ((n < 0 && errno == EIO) && dontfrag)
			return(0);
#endif

		printf("\n");
		if (n < 0)
			perror("sendto");
		else
			error("sendto: truncated packet to %s", hostname);

		/* failure */
		return(-1);
	}

	/* successfully sent */
	return(n);
}

/*
** RECV_REPLY -- Wait for arrival of input packet
** ----------------------------------------------
**
**	Outputs:
**		Input packet  is stored in global ``ipacket''.
**		Reply address is stored in global ``fromaddr''.
**
**	Returns:
**		Number of bytes received.
**		-1 in case of error or timeout.
*/

int
recv_reply(sock, wait)
int sock;				/* input socket descriptor */
struct timeval *wait;			/* timeout value */
{
	struct timeval timer;
	fd_set fds;
	int fromlen, len;
	register int n;

	/* avoid clobbering original timer */
	timer.tv_sec  = wait->tv_sec;
	timer.tv_usec = wait->tv_usec;
rewait:
	/* FD_ZERO(&fds); */
	bzero((char *)&fds, sizeof(fds));
	FD_SET(sock, &fds);

	/* wait for arrival of input packet, or timeout */
	n = select(FD_SETSIZE, &fds, (fd_set *)0, (fd_set *)0, &timer);
	if (n <= 0)
	{
		if (n < 0 && errno == EINTR)
			goto rewait;
		if (n == 0)
			errno = ETIMEDOUT;
		return(-1);
	}
reread:
	/* fake an error if nothing was actually read */
	len = sizeof(ipacket);
	fromlen = sizeof(fromaddr);
	n = recvfrom(sock, (char *)ipacket, len, 0, fromaddr_sa, &fromlen);
	if (n < 0 && errno == EINTR)
		goto reread;
	if (n == 0)
		errno = ECONNRESET;
	return(n);
}

/*
** CHECK_REPLY -- Check validity of received input packet
** ------------------------------------------------------
**
**	Inputs:
**		Global ``ident'' contains our identification.
**		Global ``port''  contains the base port offset.
**
**	Returns:
**		TIMXCEED_INTRANS if ttl dropped to zero (intermediate host).
**		DEST_UNREACHABLE + subcode of ICMP_UNREACH if unreachable
**		(got to the final destination if ICMP_UNREACH_PORT).
**		BAD_ICMP_PACKET if not one of the above.
**
**	Side effects:
**		Sets ``needfrag'' if further fragmentation is needed
**		and the new mtu size to use has been told.
*/

int
check_reply(buf, cc, seq)
u_char *buf;				/* address of input packet */
int cc;					/* total size of input packet */
int seq;				/* sequence no. determines port */
{
	struct ip *ip;
	struct icmp *icp;
	struct udphdr *udp;
	int iphdrlen;
	int type;			/* type of icmp input packet */
	int code;			/* subcode of type */

/*
 * The input packet contains the ip header in front of the icmp packet.
 * Make sure the packet contains the icmp header after the ip header.
 */
	ip = (struct ip *)buf;
	iphdrlen = ip->ip_hl << 2;

	if (ip->ip_p != IPPROTO_ICMP)
	{
		if (verbose)
		{
			printf("\n%d bytes from %s: (not icmp)\n",
				cc, pr_addr(from->sin_addr));

			/* dump the ip packet */
			print_ippkt(ip, cc);
		}
		return(BAD_ICMP_PACKET);
	}

	if (cc < iphdrlen + ICMP_MINLEN)
	{
		if (verbose)
		{
			printf("\n%d bytes from %s: (too short)\n",
				cc, pr_addr(from->sin_addr));

			/* dump the ip packet */
			print_ippkt(ip, cc);
		}
		return(BAD_ICMP_PACKET);
	}

	/* move to the icmp packet */
	icp = (struct icmp *)(buf + iphdrlen);
	cc -= iphdrlen;

	/* pickup type and its subcode */
	type = icp->icmp_type;
	code = icp->icmp_code;

/*
 * Make sure this is really our response packet.
 * The original ip packet is stored in the icmp message after the
 * icmp header, with the original udp header after the ip header.
 */
	ip = &icp->icmp_ip;	/* original ip packet */
	iphdrlen = ip->ip_hl << 2;
	udp = (struct udphdr *)((u_char *)ip + iphdrlen);

	if (cc < (ICMP_MINLEN + iphdrlen + 2*INT16SZ))
		goto bad;
	if (ip->ip_p != IPPROTO_UDP)
		goto bad;
	if (udp->uh_sport != htons(ident))
		goto bad;
	if (udp->uh_dport != htons(port+seq))
		goto bad;

/*
 * Check for fragmentation problems.
 * This should happen only if the dontfrag option is set, and
 * we hit a machine which cannot handle the current mtu size.
 * Some of the more recent routers return the new mtu size.
 */
	needfrag = 0;
	if (type == ICMP_UNREACH && code == ICMP_UNREACH_NEEDFRAG)
		needfrag = ntohl(icp->icmp_void) & 0xFFFF;

/*
 * Check the type of the icmp packet.
 * We should get ICMP_TIMXCEED if we got to an intermediate host.
 * We get ICMP_UNREACH if the desired host is currently unreachable,
 * or if we got to the final destination itself.
 */

	if (type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS)
		return(TIMXCEED_INTRANS);

	if (type == ICMP_UNREACH)
		return(DEST_UNREACHABLE + code);

/*
 * Useless packet. Dump its contents if so requested.
 */
bad:
	if (verbose)
	{
		printf("\n%d bytes from %s: (bad packet) ",
			cc, pr_addr(from->sin_addr));

		/* dump the icmp packet */
		print_icmph(icp, cc);

#ifdef obsolete
		printf("\n%d bytes from %s: icmp",
			cc, pr_addr(from->sin_addr));
		printf(" type %d (%s)", type, pr_type(type));
		if (type == ICMP_UNREACH)
			printf(" code %d (%s)", code, pr_code(code));
		else
			printf(" code %d", code);
		printf("\n");
#endif /*obsolete*/
	}

	return(BAD_ICMP_PACKET);
}

/*
** PRINT_ICMPH -- Print a descriptive string about an ICMP header
** --------------------------------------------------------------
*/

void
print_icmph(icp, cc)
struct icmp *icp;			/* icmp packet buffer */
int cc;					/* size of icmp packet */
{
	/* check basic type and subcode */
	switch (icp->icmp_type)
	{
	    case ICMP_ECHOREPLY:
		printf("Echo reply\n");
		/* XXX ID + Seq + Data */
		break;

	    case ICMP_UNREACH:
		switch (icp->icmp_code)
		{
		    case ICMP_UNREACH_NET:
			printf("Network unreachable\n");
			break;
		    case ICMP_UNREACH_HOST:
			printf("Host unreachable\n");
			break;
		    case ICMP_UNREACH_PROTOCOL:
			printf("Protocol unreachable\n");
			break;
		    case ICMP_UNREACH_PORT:
			printf("Port unreachable\n");
			break;
		    case ICMP_UNREACH_NEEDFRAG:
			printf("Frag needed and DF set\n");
			break;
		    case ICMP_UNREACH_SRCFAIL:
			printf("Source route failed\n");
			break;
		    case ICMP_UNREACH_NET_UNKNOWN:
			printf("Network unknown\n");
			break;
		    case ICMP_UNREACH_HOST_UNKNOWN:
			printf("Host unknown\n");
			break;
		    case ICMP_UNREACH_ISOLATED:
			printf("Source host isolated\n");
			break;
		    case ICMP_UNREACH_NET_PROHIB:
			printf("Network access prohibited\n");
			break;
		    case ICMP_UNREACH_HOST_PROHIB:
			printf("Host access prohibited\n");
			break;
		    case ICMP_UNREACH_TOSNET:
			printf("Network unreachable for TOS\n");
			break;
		    case ICMP_UNREACH_TOSHOST:
			printf("Host unreachable for TOS\n");
			break;
		    case ICMP_UNREACH_ADM_PROHIB:
			printf("Access prohibited\n");
			break;
		    case ICMP_UNREACH_PREC_VIOL:
			printf("Precedence violation\n");
			break;
		    case ICMP_UNREACH_PREC_CUT:
			printf("Precedence cutoff\n");
			break;
		    default:
			printf("Destination unreachable, unknown code %d\n", icp->icmp_code);
			break;
		}
		print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN);
		break;

	    case ICMP_SOURCEQUENCH:
		printf("Source quench\n");
		print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN);
		break;

	    case ICMP_REDIRECT:
		switch (icp->icmp_code)
		{
		    case ICMP_REDIRECT_NET:
			printf("Redirect network");
			break;
		    case ICMP_REDIRECT_HOST:
			printf("Redirect host");
			break;
		    case ICMP_REDIRECT_TOSNET:
			printf("Redirect TOS and network");
			break;
		    case ICMP_REDIRECT_TOSHOST:
			printf("Redirect TOS and host");
			break;
		    default:
			printf("Redirect, unknown code %d", icp->icmp_code);
			break;
		}
		printf(" (new: %s)\n", inet_ntoa(icp->icmp_gwaddr));
		print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN);
		break;

	    case ICMP_ECHO:
		printf("Echo request\n");
		/* XXX ID + Seq + Data */
		break;

	    case ICMP_ROUTERADVERT:
		printf("Router advertisement\n");
		break;

	    case ICMP_ROUTERSOLICIT:
		printf("Router solicitation\n");
		break;

	    case ICMP_TIMXCEED:
		switch (icp->icmp_code)
		{
		    case ICMP_TIMXCEED_INTRANS:
			printf("Time to live exceeded\n");
			break;
		    case ICMP_TIMXCEED_REASS:
			printf("Frag reassembly time exceeded\n");
			break;
		    default:
			printf("Time exceeded, unknown code %d\n", icp->icmp_code);
			break;
		}
		print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN);
		break;

	    case ICMP_PARAMPROB:
		printf("Parameter problem");
		printf(" (pointer: 0x%02x)\n", icp->icmp_pptr);
		print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN);
		break;

	    case ICMP_TSTAMP:
		printf("Timestamp request\n");
		/* XXX ID + Seq + 3 timestamps */
		break;

	    case ICMP_TSTAMPREPLY:
		printf("Timestamp reply\n");
		/* XXX ID + Seq + 3 timestamps */
		break;

	    case ICMP_IREQ:
		printf("Information request\n");
		/* XXX ID + Seq */
		break;

	    case ICMP_IREQREPLY:
		printf("Information reply\n");
		/* XXX ID + Seq */
		break;

	    case ICMP_MASKREQ:
		printf("Address mask request\n");
		break;

	    case ICMP_MASKREPLY:
		printf("Address mask reply\n");
		break;

	    default:
		printf("Unknown ICMP type %d\n", icp->icmp_type);
		break;
	}
}

/*
** PRINT_IPPKT -- Dump some info on a returned (via ICMP) IP packet
** ----------------------------------------------------------------
*/

void
print_ippkt(ip, cc)
struct ip *ip;				/* returned ip packet buffer */
int cc;					/* size of ip packet */
{
	int iphdrlen;			/* total size of ip header */
	struct tcphdr *tcp;		/* start of tcp packet */
	struct udphdr *udp;		/* start of udp packet */
	struct icmp *icp;		/* start of icmp packet */

	/* silently discard too short packets */
	if (cc < IPHDRSZ)
		return;
/*
 * Print ip header itself.
 */
	print_iphdr(ip, cc);

/*
 * Plus extra info for certain protocols.
 */
	iphdrlen = ip->ip_hl << 2;
	cc -= iphdrlen;

	if (ip->ip_p == IPPROTO_TCP && cc >= 2*INT16SZ)
	{
		tcp = (struct tcphdr *)((u_char *)ip + iphdrlen);
		printf("TCP: ");
		printf("from port %s", pr_port("tcp", tcp->th_sport));
		printf(", to port %s", pr_port("tcp", tcp->th_dport));
		printf("\n");
	}
	else if (ip->ip_p == IPPROTO_UDP && cc >= 2*INT16SZ)
	{
		udp = (struct udphdr *)((u_char *)ip + iphdrlen);
		printf("UDP: ");
		printf("from port %s", pr_port("udp", udp->uh_sport));
		printf(", to port %s", pr_port("udp", udp->uh_dport));
		printf("\n");
	}
	else if (ip->ip_p == IPPROTO_ICMP && cc >= ICMP_MINLEN)
	{
		icp = (struct icmp *)((u_char *)ip + iphdrlen);
		printf("ICMP: ");
		print_icmph(icp, cc);
	}
}

/*
** PRINT_IPHDR -- Print an IP header with options
** ----------------------------------------------
*/

void
print_iphdr(ip, cc)
struct ip *ip;				/* returned ip packet buffer */
int cc;					/* size of ip packet */
{
	int iphdrlen;			/* total size of ip header */

/*
 * Some platforms return the entire header in network byte order.
 */
#ifdef RAW_IP_NET_ORDER
	ip->ip_id  = ntohs(ip->ip_id);
	ip->ip_sum = ntohs(ip->ip_sum);

	ip->ip_len = ntohs((u_short)ip->ip_len);
	ip->ip_off = ntohs((u_short)ip->ip_off);
#endif /*RAW_IP_NET_ORDER*/

/*
 * Dump the ip header.
 */
	printf("VR HL TOS  LEN   ID FLG  OFF TTL PRO  CKS SRC             DST\n");
	printf("%2d %2d", ip->ip_v, ip->ip_hl);
	printf("  %02x",  ip->ip_tos);
	printf(" %4d",    (int)ip->ip_len);
	printf(" %04x",   ip->ip_id);
	printf("   %01x", ((ip->ip_off) & 0xE000) >> 13);
	printf(" %04x",   ((ip->ip_off) & 0x1FFF));
	printf(" %3d",    (int)ip->ip_ttl);
	printf(" %3d",    (int)ip->ip_p);
	printf(" %04x",   ip->ip_sum);
	printf(" %-15s",  inet_ntoa(ip->ip_src));
	printf(" %-15s",  inet_ntoa(ip->ip_dst));
	printf("\n");

/*
 * Dump option bytes.
 */
	iphdrlen = ip->ip_hl << 2;
	if (iphdrlen > IPHDRSZ && cc >= iphdrlen)
	{
		register int i;
		u_char *ipopt;		/* address of options buffer */
		int ipoptlen;		/* total size of options buffer */

		ipopt = (u_char *)ip + IPHDRSZ;
		ipoptlen = iphdrlen - IPHDRSZ;

		printf("IPOPT:");
		for (i = 0; i < ipoptlen; i++)
			printf(" %02x", ipopt[i]);
		printf("\n");
#ifdef IP_OPTIONS
		print_options(ipopt, ipoptlen);
#endif /*IP_OPTIONS*/
	}
}

/*
** PRINT_OPTIONS -- Print ip options data
** --------------------------------------
*/

#ifdef IP_OPTIONS

void
print_options(ipopt, ipoptlen)
u_char *ipopt;				/* address of options buffer */
int ipoptlen;				/* total size of options buffer */
{
	int optval;			/* option value */
	int optlen;			/* size of this option */

	while (ipoptlen > 0)
	{
		optval = ipopt[IPOPT_OPTVAL];
		optlen = ipopt[IPOPT_OLEN];

		switch (optval)
		{
		    case IPOPT_EOL:
			/* force end of options */
			optlen = 0;
			break;

		    case IPOPT_NOP:
			/* has no parameters */
			optlen = 1;
			break;

		    case IPOPT_RR:
			printf("<%s>\n", "recorded route");
			print_route(ipopt);
			break;

		    case IPOPT_LSRR:
			printf("<%s>\n", "loose source route");
			print_route(ipopt);
			break;

		    case IPOPT_SSRR:
			printf("<%s>\n", "strict source route");
			print_route(ipopt);
			break;

		    case IPOPT_TS:
			printf("<%s>\n", "time stamp");
			break;

		    case IPOPT_SECURITY:
			printf("<%s>\n", "security");
			break;

		    case IPOPT_SATID:
			printf("<%s>\n", "stream id");
			break;

		    default:
			printf("<option %d, length %d>\n", optval, optlen);
			break;
		}

		/* end of options encountered */
		if (optlen == 0)
			break;

		/* move to next option */
		ipopt += optlen;
		ipoptlen -= optlen;
	}
}

#endif /*IP_OPTIONS*/

/*
** PRINT_ROUTE -- Print ip route data
** ----------------------------------
*/

#ifdef IP_OPTIONS

void
print_route(ipopt)
u_char *ipopt;				/* start of current option */
{
	int optval;			/* option value */
	int optlen;			/* size of this option */
	u_char *optptr;			/* pointer to option data */
	int offset;			/* length of option data */

	optval = ipopt[IPOPT_OPTVAL];
	optlen = ipopt[IPOPT_OLEN];
	offset = ipopt[IPOPT_OFFSET];

	optptr = &ipopt[IPOPT_HDRLEN];
	optlen -= IPOPT_HDRLEN;

	while (offset > IPOPT_MINOFF)
	{
		struct in_addr inaddr;

		bcopy((char *)optptr, (char *)&inaddr, INADDRSZ);
		printf("\t%s\n", pr_addr(inaddr));

		optptr += INADDRSZ;
		offset -= INADDRSZ;
		optlen -= INADDRSZ;
	}

	/* buffer full -- perhaps route not complete */
	if (optval == IPOPT_RR && optlen == 0)
		printf("\t%s\n", "...");
}

#endif /*IP_OPTIONS*/

/*
** PR_PORT -- Return a service port name
** -------------------------------------
**
**	Returns:
**		Pointer to static storage containing port name/number.
*/

char *
pr_port(protocol, port)
char *protocol;				/* the protocol used */
u_short port;				/* port number in network order */
{
	struct servent *service;
	static char buf[BUFSIZ];

	service = getservbyport((int)port, protocol);
	if (service != NULL)
		(void) sprintf(buf, "%s", service->s_name);
	else
		(void) sprintf(buf, "%d", (int)ntohs(port));

	return(buf);
}

/*
** PR_TYPE -- Return type of icmp input packet
** -------------------------------------------
**
**	Returns:
**		Pointer to name of packet type.
*/

#ifdef obsolete

char *
pr_type(type)
int type;				/* type value of icmp packet */
{
	static char *ttab[] =
	{
	/*  0 */	"echo reply",
	/*  1 */	"ICMP 1",
	/*  2 */	"ICMP 2",
	/*  3 */	"destination unreachable",
	/*  4 */	"source quench",
	/*  5 */	"redirect route",
	/*  6 */	"ICMP 6",
	/*  7 */	"ICMP 7",
	/*  8 */	"echo request",
	/*  9 */	"router advertisement",
	/* 10 */	"router solicitation",
	/* 11 */	"time exceeded",
	/* 12 */	"parameter problem",
	/* 13 */	"timestamp request",
	/* 14 */	"timestamp reply",
	/* 15 */	"information request",
	/* 16 */	"information reply",
	/* 17 */	"address mask request",
	/* 18 */	"address mask reply"
	};

	if (type < 0 || type > 18)
		return("unknown icmp type");

	return(ttab[type]);
}

#endif /*obsolete*/

/*
** PR_CODE -- Return code of icmp dest unreachable packet
** ------------------------------------------------------
**
**	Returns:
**		Pointer to description of packet code.
*/

#ifdef obsolete

char *
pr_code(code)
int code;				/* code value of icmp packet */
{
	static char *utab[] =
	{
	/*  0 */	"network unreachable",
	/*  1 */	"host unreachable",
	/*  2 */	"protocol unreachable",
	/*  3 */	"port unreachable",
	/*  4 */	"frag needed and DF set",
	/*  5 */	"source route failed",
	/*  6 */	"network unknown",
	/*  7 */	"host unknown",
	/*  8 */	"source host isolated",
	/*  9 */	"network access prohibited",
	/* 10 */	"host access prohibited",
	/* 11 */	"network unreachable for TOS",
	/* 12 */	"host unreachable for TOS",
	/* 13 */	"access prohibited",
	/* 14 */	"precedence violation",
	/* 15 */	"precedence cutoff",
	};

	if (code < 0 || code > 15)
		return("unknown icmp code");

	return(utab[code]);
}

#endif /*obsolete*/

/*
** PRINT_FROM -- Print the address from which we got an input packet
** -----------------------------------------------------------------
**
**	Additional data is displayed depending on command options.
*/

void
print_from(buf, cc, inaddr)
u_char *buf;				/* address of input packet */
int cc;					/* number of bytes received */
struct in_addr inaddr;			/* internet address */
{
	char *asnumber = NULL;		/* set if AS-number available */
	struct netent *np = NULL;	/* set if network name available */
	struct ip *ip;
	int iphdrlen;

	ip = (struct ip *)buf;
	iphdrlen = ip->ip_hl << 2;
	cc -= iphdrlen;

	printf("%s", separator);

	if (numeric)
		printf(" %s", inet_ntoa(inaddr));
	else
		printf(" %s (%s)", inetname(inaddr), inet_ntoa(inaddr));

	if (lookupnet)
		np = getnetbyaddr(inet_netof(inaddr), AF_INET);

	if (np != NULL)
		printf(" (%s)", np->n_name);

	if (lookupas)
	{
		(void) fflush(stdout);
		synchronous = TRUE;
		asnumber = as_lookup(inaddr, ashost, 0, keepopen);
		synchronous = FALSE;
		if (interrupted)
			longjmp(interrupt_buf, 1);
	}

	if (asnumber != NULL)
		printf(" [%s]", asnumber);

	if (verbose)
		printf (" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
}

/*
** INETNAME -- Return a host name or dotted quad
** ---------------------------------------------
**
**	Returns:
**		Pointer to static storage containing host/address string.
*/

char *
inetname(inaddr)
struct in_addr inaddr;			/* IP address */
{
	static char buf[MAXDNAME+1];
	register char *host;

	if (numeric || (inaddr.s_addr == INADDR_ANY))
		host = NULL;
	else
		host = maphostbyaddr(inaddr);

	if (host != NULL)
		(void) strcpy(buf, host);
	else
		(void) strcpy(buf, inet_ntoa(inaddr));

	return(buf);
}

/*
** PR_ADDR -- Return a host name and/or dotted quad
** ------------------------------------------------
**
**	Returns:
**		Pointer to static storage containing host/address string.
*/

char *
pr_addr(inaddr)
struct in_addr inaddr;			/* IP address */
{
	static char buf[MAXDNAME+19+1];
	register char *host;

	if (numeric || (inaddr.s_addr == INADDR_ANY))
		host = NULL;
	else
		host = maphostbyaddr(inaddr);

	if (host != NULL)
		(void) sprintf(buf, "%s (%s)", host, inet_ntoa(inaddr));
	else
		(void) sprintf(buf, "%s", inet_ntoa(inaddr));

	return(buf);
}

/*
** MAPHOSTBYADDR -- Map IP address to host name
** --------------------------------------------
**
**	Returns:
**		Pointer to string containing host name.
**		NULL if address could not be resolved.
**
**	The results are cached for subsequent retrieval.
*/

#define AHASHSIZE	0x2000
#define AHASHMASK	0x1fff

typedef struct addr_tab {
	ipaddr_t *addrlist;		/* list of IP addresses */
	char **namelist;		/* list of corresponding host names */
	int addrcount;			/* count of addresses on the list */
} addr_tab_t;

addr_tab_t addrtab[AHASHSIZE];		/* hash list of addresses and names */

char *
maphostbyaddr(inaddr)
struct in_addr inaddr;			/* IP address to map */
{
	ipaddr_t addr = inaddr.s_addr;	/* address in network order */
	register addr_tab_t *s;
	register char *host;
 	register int i;
	struct hostent *hp;

	/* in case no mapping is desired */
	if (numeric || (inaddr.s_addr == INADDR_ANY))
		return(NULL);

/*
 * Lookup the address in the appropriate hash list.
 */
	s = &addrtab[ntohl(addr) & AHASHMASK];

	for (i = 0; i < s->addrcount; i++)
		if (s->addrlist[i] == addr)
			return(s->namelist[i]);

/*
 * Unknown address. Try to resolve it.
 */
	hp = gethostbyaddr((char *)&inaddr, INADDRSZ, AF_INET);

	if (hp != NULL)
		host = maxstr(newstr(hp->h_name), MAXDNAME, FALSE);
	else
		host = NULL;

/*
 * Put it on the hash list.
 */
	s->addrlist = newlist(s->addrlist, s->addrcount+1, ipaddr_t);
	s->namelist = newlist(s->namelist, s->addrcount+1, char *);
	s->addrlist[s->addrcount] = addr;
	s->namelist[s->addrcount] = host;
	s->addrcount++;

	return(host);
}

/*
** GETGATE -- Fetch internet address of host
** -----------------------------------------
**
**	Returns:
**		Internet address of given host.
**		Aborts if address could not be determined.
**
**	Only the first address as returned by the resolver is used.
**	This address is used as loose source route gateway address.
**
**	As a side effect, we will try to determine all its addresses
**	and add them to the global address list ``gateaddr[]'' which
**	has ``ngate'' entries.
*/

ipaddr_t
getgate(host)
char *host;				/* host name or dotted quad */
{
	register int i;
	ipaddr_t addr;
	struct in_addr inaddr;
	struct hostent *hp;

/*
 * Determine all addresses of the given host. Add original to the list.
 */
	addr = inet_addr(host);
	inaddr.s_addr = addr;

	if (addr == NOT_DOTTED_QUAD)
	{
		hp = gethostbyname(host);
		if (hp == NULL)
		{
			error("Unknown host %s", host);
			exit(EX_NOHOST);
		}

		bcopy(hp->h_addr, (char *)&inaddr, INADDRSZ);
		addr = inaddr.s_addr;
	}
	else
	{
		host = maphostbyaddr(inaddr);
		if (host != NULL)
			hp = gethostbyname(host);
		else
			hp = NULL;

		if ((ngate < MAXGATE) && !gatewayaddr(inaddr))
			gateaddr[ngate++] = inaddr.s_addr;
	}

/*
 * Append all found addresses to the global address list.
 */
	for (i = 0; hp != NULL && hp->h_addr_list[i]; i++)
	{
		bcopy(hp->h_addr_list[i], (char *)&inaddr, INADDRSZ);
		if ((ngate < MAXGATE) && !gatewayaddr(inaddr))
			gateaddr[ngate++] = inaddr.s_addr;
	}

	/* prep the address cache */
	for (i = 0; i < ngate; i++)
	{
		inaddr.s_addr = gateaddr[i];
		(void) maphostbyaddr(inaddr);
	}

	return(addr);
}

/*
** GATEWAYADDR -- Check whether address belongs to known gateways
** --------------------------------------------------------------
**
**	Returns:
**		TRUE if the address is listed in the global table.
**		FALSE otherwise.
*/

bool
gatewayaddr(inaddr)
struct in_addr inaddr;			/* internet address to check */
{
	register int i;

	for (i = 0; i < ngate; i++)
	{
		if (inaddr.s_addr == gateaddr[i])
			break;
	}

	return(i < ngate ? TRUE : FALSE);
}

/*
** IN_CHECKSUM -- Compute checksum for IP packets
** ----------------------------------------------
**
**	The complete packet must have been constructed.
**	The checksum field to be computed must be zero.
**
**	Returns:
**		Computed checksum.
*/

#ifdef obsolete

u_short
in_checksum(buf, len)
u_short *buf;				/* start of packet */
int len;				/* length of packet in bytes */
{
	register u_short *w = buf;	/* address of next 16-bit word */
	register int nleft = len;	/* remaining 16-bit words */
	register int sum = 0;		/* 32-bit accumulator */
	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;
	}

	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);
}

#endif /*obsolete*/

/*
** IN_CHECKSUM -- Compute checksum for IP packets
** ----------------------------------------------
**
**	The complete packet must have been constructed.
**	The checksum field to be computed must be zero.
**
**	Returns:
**		Computed checksum.
*/

#ifdef notyet

u_short
in_checksum(buf, len)
u_short *buf;				/* start of packet */
int len;				/* length of packet in bytes */
{
	register u_char *b = (u_char *)buf;
	register int n = len;		/* remaining 16-bit words */
	register int sum = 0;		/* 32-bit accumulator */
	u_short answer;

/*
 * 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 (n > 1)
	{
		answer = (b[0] << 8) | b[1];
		sum += answer;
		b += 2; n -= 2;
	}

	if (n == 1)
	{
		answer = (b[0] << 8);
		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 & 0xFFFF;			/* truncate to 16 bits */
	answer = htons(answer);			/* correct order */
	return(answer);
}

#endif /*notyet*/

/*
** TVSUB -- Subtract two timeval structs
** -------------------------------------
**
**	Returns:
**		Time difference in micro(!)seconds.
**
**	Side effects:
**		The difference of the two timeval structs
**		is stored back into the first.
**
**	This implementation assumes that time_t is a signed value.
**	On 32-bit machines this limits the range to ~35 minutes.
**	That seems sufficient for most practical purposes.
**	Note that tv_sec is an *un*signed entity on some platforms.
*/

time_t
tvsub(t2, t1)
struct timeval *t2;			/* latest timeval */
struct timeval *t1;			/* oldest timeval */
{
	register time_t usec;

	t2->tv_usec -= t1->tv_usec;
	while (t2->tv_usec < 0)
	{
		t2->tv_usec += 1000000;
		if (t2->tv_sec != 0)
			t2->tv_sec--;
		else
			t2->tv_usec = 0;
	}

	if (t2->tv_sec < t1->tv_sec)
	{
		t2->tv_sec = 0;
		t2->tv_usec = 0;
	}
	else
		t2->tv_sec -= t1->tv_sec;

	if (t2->tv_sec > MAXSECS)
	{
		t2->tv_sec = MAXSECS;
		t2->tv_usec = 0;
	}

	usec = t2->tv_sec*1000000 + t2->tv_usec;
	return(usec);
}

/*
** TVPRINT -- Convert time value to ascii string
** ---------------------------------------------
**
**	Returns:
**		Pointer to string in static storage.
**
**	Output is in variable format, depending on the value.
**	This avoids printing of non-significant digits.
*/

char *
tvprint(usec)
time_t usec;				/* value to convert */
{
	static char buf[30];		/* sufficient for 64-bit values */
	time_t uval;

	if (usec < 1000)
	{
		uval = usec;
		(void) sprintf(buf, "%ld.%3.3ld", uval/1000, uval%1000);
	}
	else if (usec < 10000)
	{
		uval = (usec + 5)/10;
		(void) sprintf(buf, "%ld.%2.2ld", uval/100, uval%100);
	}
	else if (usec < 100000)
	{
		uval = (usec + 50)/100;
		(void) sprintf(buf, "%ld.%1.1ld", uval/10, uval%10);
	}
	else
	{
		uval = (usec + 500)/1000;
		(void) sprintf(buf, "%ld", uval);
	}

	return(buf);
}

/*
** PRINT_STATS -- Print round-trip statistics
** ------------------------------------------
**
**	Returns:
**		None.
*/

time_t rttmin;			/* minimum rtt value */
time_t rttmax;			/* maximum rtt value */
double rttsum;			/* accumulated rtt values */
double rttssq;			/* sum of squared rtt values */
int nreceived = 0;		/* number of valid replies received */

void
print_stats(ntransmitted)
int ntransmitted;			/* number of probe packets sent */
{
	double rttavg;			/* average round-trip time */
	double rttstd;			/* rtt standard deviation */

	if (ntransmitted > 0 && ntransmitted >= nreceived)
	{
		int missed = ntransmitted - nreceived;
		double loss = 100 * (double)missed / (double)ntransmitted;

		printf("%s", separator);

		if (nreceived > 0)
		{
			rttavg = rttsum / nreceived;
			rttstd = rttssq - (rttavg * rttsum);
			rttstd = xsqrt(rttstd / nreceived);

			printf(" %s", tvprint(rttmin));
			printf("/%s", tvprint((time_t)rttavg));
			printf("/%s", tvprint(rttmax));

			printf(" (%s) ms ", tvprint((time_t)rttstd));
		}

		printf(" %d/%d pkts", nreceived, ntransmitted);

		if (nreceived == 0)
			printf(" (%d%% loss)", 100);
		else
			printf(" (%.2g%% loss)", loss);
	}
}

/*
** RECORD_STATS -- Update round-trip statistics
** --------------------------------------------
**
**	Returns:
**		None.
*/

void
record_stats(rtt)
time_t rtt;				/* round-trip value */
{
	if (nreceived == 0)
	{
		rttmin = rtt;
		rttmax = rtt;
		rttsum = 0.0;
		rttssq = 0.0;
	}

	nreceived++;

	if (rtt < rttmin)
		rttmin = rtt;

	if (rtt > rttmax)
		rttmax = rtt;

	rttsum += (double)rtt;
	rttssq += (double)rtt * (double)rtt;
}


/*
** CLEAR_STATS -- Clear out round-trip statistics
** ----------------------------------------------
**
**	Returns:
**		None.
*/

void
clear_stats()
{
	nreceived = 0;
}

/*
** SELECT_MTU -- Select next lower mtu size from table
** ---------------------------------------------------
**
**	Returns:
**		New mtu size, if available.
**		Old mtu size otherwise.
**
**	The table of known mtu sizes is defined in "conf.h"
*/

int
select_mtu(len)
int len;				/* current ip packet size */
{
	register int i;
	register int mtu;

	for (i = 0; (mtu = mtusize[i]) > 0; i++)
	{
		if (mtu < len)
			break;
	}

	if (mtu > 0)
		len = mtu;

	return(len);
}

/*
** REGULAR_TTL -- Check whether this is a known ttl value
** ------------------------------------------------------
**
**	Returns:
**		TRUE if this ttl belongs to the known values.
**		FALSE otherwise.
**
**	The known ttl values are defined in "conf.h"
*/

bool
regular_ttl(ttl)
int ttl;				/* ttl value */
{
	switch (ttl)
	{
	    case MAX_START_TTL:
	    case NETWARE_TTL:
	    case OLD_PROTEON_TTL:
	    case NEW_PROTEON_TTL:
	    case OLD_BSD_TCP:
	    case NEW_BSD_TCP:
		return(TRUE);
	}

	/* unknown value */
	return(FALSE);
}

/*
** MAXSTR -- Ensure string does not exceed maximum size
** ----------------------------------------------------
**
**	Returns:
**		Pointer to the (possibly truncated) string.
**
**	If necessary, a new string is allocated, and is then
**	truncated, and the original string is left intact.
**	Otherwise the original string is truncated in place.
**
*/

char *
maxstr(string, n, save)
char *string;				/* the string to check */
int n;					/* the maximum allowed size */
bool save;				/* allocate new string, if set */
{
	if (strlength(string) > n)
	{
		if (save)
			string = newstr(string);
		string[n] = '\0';
	}
	return(string);
}

/*
** XALLOC -- Allocate or reallocate additional memory
** --------------------------------------------------
**
**	Returns:
**		Pointer to (re)allocated buffer space.
**		Aborts if the requested memory could not be obtained.
*/

ptr_t *
xalloc(buf, size)
register ptr_t *buf;			/* current start of buffer space */
siz_t size;				/* number of bytes to allocate */
{
	if (buf == NULL)
		buf = malloc(size);
	else
		buf = realloc(buf, size);

	if (buf == NULL)
	{
		error("Out of memory");
		exit(EX_OSERR);
	}

	return(buf);
}

/*
** ITOA -- Convert integer value to ascii string
** ---------------------------------------------
**
**	Returns:
**		Pointer to string in static storage.
*/

char *
itoa(n)
int n;					/* value to convert */
{
	static char buf[30];		/* sufficient for 64-bit values */

	(void) sprintf(buf, "%d", n);
	return(buf);
}

/*
** XSQRT -- Compute arithmetic square root
** ---------------------------------------
**
**	Returns:
**		Computed square root value.
**
**	This is supplied to avoid linking with the -lm library.
**	Several Newton-Raphson iterations are performed until
**	the machine precision is hit.
*/

double
xsqrt(y)
double y;
{
	double t, x;

	if (y <= 0)
		return(0);

	x = (y < 1.0) ? 1.0 : y;
	do {
		t = x;
		x = (t + (y/t))/2.0;
	} while (0 < x && x < t);

	return(x);
}


syntax highlighted by Code2HTML, v. 0.9.1