]> git.saurik.com Git - apple/network_cmds.git/blobdiff - traceroute.tproj/traceroute.c
network_cmds-606.100.3.tar.gz
[apple/network_cmds.git] / traceroute.tproj / traceroute.c
index 1f8a5fa8c89b1410cee068d8148556bc1063f2a8..a411d0a4a9edf90ff9ade89cd9977440ed897a50 100644 (file)
@@ -1,71 +1,59 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
- * 
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
-/*-
- * Copyright (c) 1990, 1993
+
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
  *     The Regents of the University of California.  All rights reserved.
  *
- * This code is derived from software contributed to Berkeley by
- * Van Jacobson.
- *
  * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * 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.
  */
 
-#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1990, 1993\n\
-       The Regents of the University of California.  All rights reserved.\n";
-#endif /* not lint */
+#include <sys/cdefs.h>
 
 #ifndef lint
-static char sccsid[] = "@(#)traceroute.c       8.1 (Berkeley) 6/6/93";
-#endif /* not lint */
+__unused static const char copyright[] =
+    "@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000\n\
+The Regents of the University of California.  All rights reserved.\n";
+#endif
 
 /*
  * traceroute host  - trace the route ip packets follow going to "host".
@@ -76,9 +64,9 @@ static char sccsid[] = "@(#)traceroute.c      8.1 (Berkeley) 6/6/93";
  * 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
+ * defaults to net.inet.ip.ttl hops & can be changed with the -m flag).
+ * Three probes (change with -q flag) are sent at each ttl setting and
+ * 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 5 sec. timeout
@@ -93,7 +81,7 @@ static char sccsid[] = "@(#)traceroute.c      8.1 (Berkeley) 6/6/93";
  * 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
+ *     traceroute to nis.nsf.net (35.1.1.48), 64 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
@@ -113,7 +101,7 @@ static char sccsid[] = "@(#)traceroute.c    8.1 (Berkeley) 6/6/93";
  * 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
+ *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 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
@@ -234,141 +222,423 @@ static char sccsid[] = "@(#)traceroute.c        8.1 (Berkeley) 6/6/93";
  * 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)
+ *  -- Van Jacobson (van@ee.lbl.gov)
  *     Tue Dec 20 03:50:13 PST 1988
  */
 
 #include <sys/param.h>
-#include <sys/time.h>
-#include <sys/socket.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#include <sys/time.h>
 
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <netinet/ip_var.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
 
 #include <arpa/inet.h>
 
+#ifdef IPSEC
+#include <net/route.h>
+#include <netinet6/ipsec.h>    /* XXX */
+#endif /* IPSEC */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <memory.h>
 #include <netdb.h>
 #include <stdio.h>
-#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#define        MAXPACKET       65535   /* max ip packet size */
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
+#include "gnuc.h"
+#ifdef HAVE_OS_PROTO_H
+#include "os-proto.h"
+#endif
+
+/* rfc1716 */
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB     13      /* admin prohibited filter */
 #endif
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE   14      /* host precedence violation */
+#endif
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15      /* precedence cutoff */
+#endif
+
+#include "findsaddr.h"
+#include "ifaddrlist.h"
+#include "as.h"
+#include "traceroute.h"
 
-#ifndef FD_SET
-#define NFDBITS         (8*sizeof(fd_set))
-#define FD_SETSIZE      NFDBITS
-#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
-#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
-#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
-#define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
+/* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
 #endif
 
 #define Fprintf (void)fprintf
-#define Sprintf (void)sprintf
 #define Printf (void)printf
 
-/*
- * format of a (udp) probe packet.
- */
-struct opacket {
-       struct ip ip;
-       struct udphdr udp;
+/* What a GRE packet header looks like */
+struct grehdr {
+       u_int16_t   flags;
+       u_int16_t   proto;
+       u_int16_t   length;     /* PPTP version of these fields */
+       u_int16_t   callId;
+};
+#ifndef IPPROTO_GRE
+#define IPPROTO_GRE    47
+#endif
+
+/* For GRE, we prepare what looks like a PPTP packet */
+#define GRE_PPTP_PROTO 0x880b
+
+/* Host name and address list */
+struct hostinfo {
+       char *name;
+       int n;
+       u_int32_t *addrs;
+};
+
+/* Data section of the probe packet */
+struct outdata {
        u_char seq;             /* sequence number of this packet */
        u_char ttl;             /* ttl packet left with */
        struct timeval tv;      /* time packet left */
 };
 
+#ifndef HAVE_ICMP_NEXTMTU
+/* Path MTU Discovery (RFC1191) */
+struct my_pmtu {
+       u_short ipm_void;
+       u_short ipm_nextmtu;
+};
+#endif
+
 u_char packet[512];            /* last inbound (icmp) packet */
-struct opacket *outpacket;     /* last output (udp) packet */
 
-int wait_for_reply __P((int, struct sockaddr_in *));
-void send_probe __P((int, int));
-double deltaT __P((struct timeval *, struct timeval *));
-int packet_ok __P((u_char *, int, struct sockaddr_in *, int));
-void print __P((u_char *, int, struct sockaddr_in *));
-void tvsub __P((struct timeval *, struct timeval *));
-char *inetname __P((struct in_addr));
-void usage __P(());
+struct ip *outip;              /* last output ip packet */
+u_char *outp;          /* last output inner protocol packet */
+
+struct ip *hip = NULL;         /* Quoted IP header */
+int hiplen = 0;
+
+/* loose source route gateway list (including room for final destination) */
+u_int32_t gwlist[NGATEWAYS + 1];
 
 int s;                         /* receive (icmp) socket file descriptor */
 int sndsock;                   /* send (udp) socket file descriptor */
-struct timezone tz;            /* leftover */
 
 struct sockaddr whereto;       /* Who to try to reach */
-int datalen;                   /* How much data */
-
-char *source = 0;
+struct sockaddr wherefrom;     /* Who we are */
+int packlen;                   /* total length of packet */
+int protlen;                   /* length of protocol part of packet */
+int minpacket;                 /* min ip packet size */
+int maxpacket = 32 * 1024;     /* max ip packet size */
+int pmtu;                      /* Path MTU Discovery (RFC1191) */
+u_int pausemsecs;
+
+char *prog;
+char *source;
 char *hostname;
+char *device;
+static const char devnull[] = "/dev/null";
 
-int nprobes = 3;
-int max_ttl = 30;
+int nprobes = -1;
+int max_ttl;
+int first_ttl = 1;
 u_short ident;
-u_short port = 32768+666;      /* start udp dest port # for probe packets */
+u_short port;                  /* protocol specific base "port" */
+
 int options;                   /* socket options */
 int verbose;
 int waittime = 5;              /* time to wait for response (in seconds) */
 int nflag;                     /* print addresses numerically */
+int as_path;                   /* print as numbers for each hop */
+char *as_server = NULL;
+void *asn;
+#ifdef CANT_HACK_IPCKSUM
+int doipcksum = 0;             /* don't calculate ip checksums by default */
+#else
+int doipcksum = 1;             /* calculate ip checksums by default */
+#endif
+int optlen;                    /* length of ip options */
+int fixedPort = 0;             /* Use fixed destination port for TCP and UDP */
+int printdiff = 0;             /* Print the difference between sent and quoted */
+
+extern int optind;
+extern int opterr;
+extern char *optarg;
+
+/* Forwards */
+double deltaT(struct timeval *, struct timeval *);
+void   freehostinfo(struct hostinfo *);
+void   getaddr(u_int32_t *, char *);
+struct hostinfo *gethostinfo(char *);
+u_short        in_cksum(u_short *, int);
+char   *inetname(struct in_addr);
+int    main(int, char **);
+u_short p_cksum(struct ip *, u_short *, int);
+int    packet_ok(u_char *, int, struct sockaddr_in *, int);
+char   *pr_type(u_char);
+void   print(u_char *, int, struct sockaddr_in *);
+#ifdef IPSEC
+int    setpolicy __P((int so, char *policy));
+#endif
+void   send_probe(int, int);
+struct outproto *setproto(char *);
+int    str2val(const char *, const char *, int, int);
+void   tvsub(struct timeval *, struct timeval *);
+void usage(void);
+int    wait_for_reply(int, struct sockaddr_in *, const struct timeval *);
+void pkt_compare(const u_char *, int, const u_char *, int);
+#ifndef HAVE_USLEEP
+int    usleep(u_int);
+#endif
+
+void   udp_prep(struct outdata *);
+int    udp_check(const u_char *, int);
+void   tcp_prep(struct outdata *);
+int    tcp_check(const u_char *, int);
+void   gre_prep(struct outdata *);
+int    gre_check(const u_char *, int);
+void   gen_prep(struct outdata *);
+int    gen_check(const u_char *, int);
+void   icmp_prep(struct outdata *);
+int    icmp_check(const u_char *, int);
+
+/* Descriptor structure for each outgoing protocol we support */
+struct outproto {
+       char    *name;          /* name of protocol */
+       const char *key;        /* An ascii key for the bytes of the header */
+       u_char  num;            /* IP protocol number */
+       u_short hdrlen;         /* max size of protocol header */
+       u_short port;           /* default base protocol-specific "port" */
+       void    (*prepare)(struct outdata *);
+                               /* finish preparing an outgoing packet */
+       int     (*check)(const u_char *, int);
+                               /* check an incoming packet */
+};
+
+/* List of supported protocols. The first one is the default. The last
+   one is the handler for generic protocols not explicitly listed. */
+struct outproto protos[] = {
+       {
+               "udp",
+               "spt dpt len sum",
+               IPPROTO_UDP,
+               sizeof(struct udphdr),
+               32768 + 666,
+               udp_prep,
+               udp_check
+       },
+       {
+               "tcp",
+               "spt dpt seq     ack     xxflwin sum urp",
+               IPPROTO_TCP,
+               sizeof(struct tcphdr),
+               32768 + 666,
+               tcp_prep,
+               tcp_check
+       },
+       {
+               "gre",
+               "flg pro len clid",
+               IPPROTO_GRE,
+               sizeof(struct grehdr),
+               GRE_PPTP_PROTO,
+               gre_prep,
+               gre_check
+       },
+       {
+               "icmp",
+               "typ cod sum ",
+               IPPROTO_ICMP,
+               sizeof(struct icmp),
+               0,
+               icmp_prep,
+               icmp_check
+       },
+       {
+               NULL,
+               NULL,
+               0,
+               2 * sizeof(u_short),
+               0,
+               gen_prep,
+               gen_check
+       },
+};
+struct outproto *proto = &protos[0];
+
+const char *ip_hdr_key = "vhtslen id  off tlprsum srcip   dstip   opts";
 
 int
-main(argc, argv)
-       int argc;
-       char *argv[];
+main(int argc, char **argv)
 {
-       extern char *optarg;
-       extern int optind;
-       struct hostent *hp;
-       struct protoent *pe;
-       struct sockaddr_in from, *to;
-       int ch, i, on, probe, seq, tos, ttl;
-
-       on = 1;
-       seq = tos = 0;
-       to = (struct sockaddr_in *)&whereto;
-       while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
-               switch(ch) {
+       register int op, code, n;
+       register char *cp;
+       register const char *err;
+       register u_int32_t *ap;
+       register struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom;
+       register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
+       register struct hostinfo *hi;
+       int on = 1;
+       register struct protoent *pe;
+       register int ttl, probe, i;
+       register int seq = 0;
+       int tos = 0, settos = 0;
+       register int lsrr = 0;
+       register u_short off = 0;
+       struct ifaddrlist *al;
+       char errbuf[132];
+       int requestPort = -1;
+       int sump = 0;
+       int sockerrno = 0;
+
+       if (argv[0] == NULL)
+               prog = "traceroute";
+       else if ((cp = strrchr(argv[0], '/')) != NULL)
+               prog = cp + 1;
+       else
+               prog = argv[0];
+       
+       /* Insure the socket fds won't be 0, 1 or 2 */
+       if (open(devnull, O_RDONLY) < 0 ||
+           open(devnull, O_RDONLY) < 0 ||
+           open(devnull, O_RDONLY) < 0) {
+               Fprintf(stderr, "%s: open \"%s\": %s\n",
+                   prog, devnull, strerror(errno));
+               exit(1);
+       }
+       /*
+        * Do the setuid-required stuff first, then lose priveleges ASAP.
+        * Do error checking for these two calls where they appeared in
+        * the original code.
+        */
+       cp = "icmp";
+       pe = getprotobyname(cp);
+       if (pe) {
+               if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0)
+                       sockerrno = errno;
+               else if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+                       sockerrno = errno;
+       }
+
+       setuid(getuid());
+
+#ifdef IPCTL_DEFTTL
+       {
+               int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
+               size_t sz = sizeof(max_ttl);
+
+               if (sysctl(mib, 4, &max_ttl, &sz, NULL, 0) == -1) {
+                       perror("sysctl(net.inet.ip.ttl)");
+                       exit(1);
+               }
+       }
+#else
+       max_ttl = 30;
+#endif
+
+       opterr = 0;
+       while ((op = getopt(argc, argv, "aA:edDFInrSvxf:g:i:M:m:P:p:q:s:t:w:z:")) != EOF)
+               switch (op) {
+               case 'a':
+                       as_path = 1;
+                       break;
+                       
+               case 'A':
+                       as_path = 1;
+                       as_server = optarg;
+                       break;
+                           
                case 'd':
                        options |= SO_DEBUG;
                        break;
-               case 'm':
-                       max_ttl = atoi(optarg);
-                       if (max_ttl <= 1) {
+
+               case 'D':
+                       printdiff = 1;
+                       break;
+
+               case 'e':
+                       fixedPort = 1;
+                       break;
+
+               case 'f':
+               case 'M':       /* FreeBSD compat. */
+                       first_ttl = str2val(optarg, "first ttl", 1, 255);
+                       break;
+
+               case 'F':
+                       off = IP_DF;
+                       break;
+
+               case 'g':
+                       if (lsrr >= NGATEWAYS) {
                                Fprintf(stderr,
-                                   "traceroute: max ttl must be >1.\n");
+                                   "%s: No more than %d gateways\n",
+                                   prog, NGATEWAYS);
                                exit(1);
                        }
+                       getaddr(gwlist + lsrr, optarg);
+                       ++lsrr;
+                       break;
+
+               case 'i':
+                       device = optarg;
                        break;
+
+               case 'I':
+                       proto = setproto("icmp");
+                       break;
+
+               case 'm':
+                       max_ttl = str2val(optarg, "max ttl", 1, 255);
+                       break;
+
                case 'n':
-                       nflag++;
+                       ++nflag;
                        break;
+
+               case 'P':
+                       proto = setproto(optarg);
+                       break;
+
                case 'p':
-                       port = atoi(optarg);
-                       if (port < 1) {
-                               Fprintf(stderr,
-                                   "traceroute: port must be >0.\n");
-                               exit(1);
-                       }
+                       requestPort = (u_short)str2val(optarg, "port",
+                           1, (1 << 16) - 1);
                        break;
+
                case 'q':
-                       nprobes = atoi(optarg);
-                       if (nprobes < 1) {
-                               Fprintf(stderr,
-                                   "traceroute: nprobes must be >0.\n");
-                               exit(1);
-                       }
+                       nprobes = str2val(optarg, "nprobes", 1, -1);
                        break;
+
                case 'r':
                        options |= SO_DONTROUTE;
                        break;
+
                case 's':
                        /*
                         * set the ip source address of the outbound
@@ -376,267 +646,646 @@ main(argc, argv)
                         */
                        source = optarg;
                        break;
+
+               case 'S':
+                       sump = 1;
+                       break;
+
                case 't':
-                       tos = atoi(optarg);
-                       if (tos < 0 || tos > 255) {
-                               Fprintf(stderr,
-                                   "traceroute: tos must be 0 to 255.\n");
-                               exit(1);
-                       }
+                       tos = str2val(optarg, "tos", 0, 255);
+                       ++settos;
                        break;
+
                case 'v':
-                       verbose++;
+                       ++verbose;
+                       break;
+
+               case 'x':
+                       doipcksum = (doipcksum == 0);
                        break;
+
                case 'w':
-                       waittime = atoi(optarg);
-                       if (waittime <= 1) {
-                               Fprintf(stderr,
-                                   "traceroute: wait must be >1 sec.\n");
-                               exit(1);
-                       }
+                       waittime = str2val(optarg, "wait time",
+                           1, 24 * 60 * 60);
+                       break;
+
+               case 'z':
+                       pausemsecs = str2val(optarg, "pause msecs",
+                           0, 60 * 60 * 1000);
                        break;
+
                default:
                        usage();
                }
-       argc -= optind;
-       argv += optind;
 
-       if (argc < 1)
-               usage();
+       /* Set requested port, if any, else default for this protocol */
+       port = (requestPort != -1) ? requestPort : proto->port;
 
-       setlinebuf (stdout);
+       if (nprobes == -1)
+               nprobes = printdiff ? 1 : 3;
 
-       (void) bzero((char *)&whereto, sizeof(struct sockaddr));
-       to->sin_family = AF_INET;
-       to->sin_addr.s_addr = inet_addr(*argv);
-       if (to->sin_addr.s_addr != -1)
-               hostname = *argv;
-       else {
-               hp = gethostbyname(*argv);
-               if (hp) {
-                       to->sin_family = hp->h_addrtype;
-                       bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
-                       hostname = hp->h_name;
-               } else {
-                       (void)fprintf(stderr,
-                           "traceroute: unknown host %s\n", *argv);
-                       exit(1);
-               }
-       }
-       if (*++argv)
-               datalen = atoi(*argv);
-       if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) {
+       if (first_ttl > max_ttl) {
                Fprintf(stderr,
-                   "traceroute: packet size must be 0 <= s < %ld.\n",
-                   MAXPACKET - sizeof(struct opacket));
+                   "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
+                   prog, first_ttl, max_ttl);
                exit(1);
        }
-       datalen += sizeof(struct opacket);
-       outpacket = (struct opacket *)malloc((unsigned)datalen);
-       if (! outpacket) {
-               perror("traceroute: malloc");
+
+       if (!doipcksum)
+               Fprintf(stderr, "%s: Warning: ip checksums disabled\n", prog);
+
+       if (lsrr > 0)
+               optlen = (lsrr + 1) * sizeof(gwlist[0]);
+       minpacket = sizeof(*outip) + proto->hdrlen + sizeof(struct outdata) + optlen;
+       packlen = minpacket;                    /* minimum sized packet */
+
+       /* Process destination and optional packet size */
+       switch (argc - optind) {
+
+       case 2:
+               packlen = str2val(argv[optind + 1],
+                   "packet length", minpacket, maxpacket);
+               /* Fall through */
+
+       case 1:
+               hostname = argv[optind];
+               hi = gethostinfo(hostname);
+               setsin(to, hi->addrs[0]);
+               if (hi->n > 1)
+                       Fprintf(stderr,
+                   "%s: Warning: %s has multiple addresses; using %s\n",
+                               prog, hostname, inet_ntoa(to->sin_addr));
+               hostname = hi->name;
+               hi->name = NULL;
+               freehostinfo(hi);
+               break;
+
+       default:
+               usage();
+       }
+
+#ifdef HAVE_SETLINEBUF
+       setlinebuf (stdout);
+#else
+       setvbuf(stdout, NULL, _IOLBF, 0);
+#endif
+
+       protlen = packlen - sizeof(*outip) - optlen;
+
+       outip = (struct ip *)malloc((unsigned)packlen);
+       if (outip == NULL) {
+               Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
                exit(1);
        }
-       (void) bzero((char *)outpacket, datalen);
-       outpacket->ip.ip_dst = to->sin_addr;
-       outpacket->ip.ip_tos = tos;
-       outpacket->ip.ip_v = IPVERSION;
-       outpacket->ip.ip_id = 0;
+       memset((char *)outip, 0, packlen);
+
+       outip->ip_v = IPVERSION;
+       if (settos)
+               outip->ip_tos = tos;
+#ifdef BYTESWAP_IP_HDR
+       outip->ip_len = htons(packlen);
+       outip->ip_off = htons(off);
+#else
+       outip->ip_len = packlen;
+       outip->ip_off = off;
+#endif
+       outip->ip_p = proto->num;
+       outp = (u_char *)(outip + 1);
+#ifdef HAVE_RAW_OPTIONS
+       if (lsrr > 0) {
+               register u_char *optlist;
+
+               optlist = outp;
+               outp += optlen;
+
+               /* final hop */
+               gwlist[lsrr] = to->sin_addr.s_addr;
+
+               outip->ip_dst.s_addr = gwlist[0];
+
+               /* force 4 byte alignment */
+               optlist[0] = IPOPT_NOP;
+               /* loose source route option */
+               optlist[1] = IPOPT_LSRR;
+               i = lsrr * sizeof(gwlist[0]);
+               optlist[2] = i + 3;
+               /* Pointer to LSRR addresses */
+               optlist[3] = IPOPT_MINOFF;
+               memcpy(optlist + 4, gwlist + 1, i);
+       } else
+#endif
+               outip->ip_dst = to->sin_addr;
 
+       outip->ip_hl = (outp - (u_char *)outip) >> 2;
        ident = (getpid() & 0xffff) | 0x8000;
 
-       if ((pe = getprotobyname("icmp")) == NULL) {
-               Fprintf(stderr, "icmp: unknown protocol\n");
-               exit(10);
+       if (pe == NULL) {
+               Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
+               exit(1);
        }
-       if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
-               perror("traceroute: icmp socket");
-               exit(5);
+       if (s < 0) {
+               errno = sockerrno;
+               Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
+               exit(1);
        }
+       (void) setsockopt(s, SOL_SOCKET, SO_RECV_ANYIF, (char *)&on,
+           sizeof(on));
        if (options & SO_DEBUG)
-               (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
-                                 (char *)&on, sizeof(on));
+               (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
+                   sizeof(on));
        if (options & SO_DONTROUTE)
-               (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
-                                 (char *)&on, sizeof(on));
+               (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+                   sizeof(on));
+
+#if    defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+       if (setpolicy(s, "in bypass") < 0)
+               errx(1, "%s", ipsec_strerror());
+
+       if (setpolicy(s, "out bypass") < 0)
+               errx(1, "%s", ipsec_strerror());
+#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */
+
+       if (sndsock < 0) {
+               errno = sockerrno;
+               Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
+               exit(1);
+       }
 
-       if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
-               perror("traceroute: raw socket");
-               exit(5);
+#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
+       if (lsrr > 0) {
+               u_char optlist[MAX_IPOPTLEN];
+
+               cp = "ip";
+               if ((pe = getprotobyname(cp)) == NULL) {
+                       Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
+                       exit(1);
+               }
+
+               /* final hop */
+               gwlist[lsrr] = to->sin_addr.s_addr;
+               ++lsrr;
+
+               /* force 4 byte alignment */
+               optlist[0] = IPOPT_NOP;
+               /* loose source route option */
+               optlist[1] = IPOPT_LSRR;
+               i = lsrr * sizeof(gwlist[0]);
+               optlist[2] = i + 3;
+               /* Pointer to LSRR addresses */
+               optlist[3] = IPOPT_MINOFF;
+               memcpy(optlist + 4, gwlist, i);
+
+               if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS,
+                   (char *)optlist, i + sizeof(gwlist[0]))) < 0) {
+                       Fprintf(stderr, "%s: IP_OPTIONS: %s\n",
+                           prog, strerror(errno));
+                       exit(1);
+                   }
        }
+#endif
+
 #ifdef SO_SNDBUF
-       if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
-                      sizeof(datalen)) < 0) {
-               perror("traceroute: SO_SNDBUF");
-               exit(6);
+       if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
+           sizeof(packlen)) < 0) {
+               Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
+               exit(1);
        }
-#endif SO_SNDBUF
+#endif
 #ifdef IP_HDRINCL
        if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
-                      sizeof(on)) < 0) {
-               perror("traceroute: IP_HDRINCL");
-               exit(6);
+           sizeof(on)) < 0) {
+               Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
+               exit(1);
+       }
+#else
+#ifdef IP_TOS
+       if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
+           (char *)&tos, sizeof(tos)) < 0) {
+               Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
+                   prog, tos, strerror(errno));
+               exit(1);
        }
-#endif IP_HDRINCL
+#endif
+#endif
        if (options & SO_DEBUG)
-               (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
-                                 (char *)&on, sizeof(on));
+               (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
+                   sizeof(on));
        if (options & SO_DONTROUTE)
-               (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
-                                 (char *)&on, sizeof(on));
-
-       if (source) {
-               (void) bzero((char *)&from, sizeof(struct sockaddr));
-               from.sin_family = AF_INET;
-               from.sin_addr.s_addr = inet_addr(source);
-               if (from.sin_addr.s_addr == -1) {
-                       Printf("traceroute: unknown host %s\n", source);
+               (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+                   sizeof(on));
+
+       /* Get the interface address list */
+       n = ifaddrlist(&al, errbuf, sizeof(errbuf));
+       if (n < 0) {
+               Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
+               exit(1);
+       }
+       if (n == 0) {
+               Fprintf(stderr,
+                   "%s: Can't find any network interfaces\n", prog);
+               exit(1);
+       }
+
+       /* Look for a specific device */
+       if (device != NULL) {
+               for (i = n; i > 0; --i, ++al)
+                       if (strcmp(device, al->device) == 0)
+                               break;
+               if (i <= 0) {
+                       Fprintf(stderr, "%s: Can't find interface %.32s\n",
+                           prog, device);
+                       exit(1);
+               }
+       }
+
+       /* Determine our source address */
+       if (source == NULL) {
+               /*
+                * If a device was specified, use the interface address.
+                * Otherwise, try to determine our source address.
+                */
+               if (device != NULL)
+                       setsin(from, al->addr);
+               else if ((err = findsaddr(to, from)) != NULL) {
+                       Fprintf(stderr, "%s: findsaddr: %s\n",
+                           prog, err);
                        exit(1);
                }
-               outpacket->ip.ip_src = from.sin_addr;
-#ifndef IP_HDRINCL
-               if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) {
-                       perror ("traceroute: bind:");
-                       exit (1);
+       } else {
+               hi = gethostinfo(source);
+               source = hi->name;
+               hi->name = NULL;
+               /*
+                * If the device was specified make sure it
+                * corresponds to the source address specified.
+                * Otherwise, use the first address (and warn if
+                * there are more than one).
+                */
+               if (device != NULL) {
+                       for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
+                               if (*ap == al->addr)
+                                       break;
+                       if (i <= 0) {
+                               Fprintf(stderr,
+                                   "%s: %s is not on interface %.32s\n",
+                                   prog, source, device);
+                               exit(1);
+                       }
+                       setsin(from, *ap);
+               } else {
+                       setsin(from, hi->addrs[0]);
+                       if (hi->n > 1)
+                               Fprintf(stderr,
+                       "%s: Warning: %s has multiple addresses; using %s\n",
+                                   prog, source, inet_ntoa(from->sin_addr));
+               }
+               freehostinfo(hi);
+       }
+
+       outip->ip_src = from->sin_addr;
+
+       /* Check the source address (-s), if any, is valid */
+       if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
+               Fprintf(stderr, "%s: bind: %s\n",
+                   prog, strerror(errno));
+               exit (1);
+       }
+
+       if (as_path) {
+               asn = as_setup(as_server);
+               if (asn == NULL) {
+                       Fprintf(stderr, "%s: as_setup failed, AS# lookups"
+                           " disabled\n", prog);
+                       (void)fflush(stderr);
+                       as_path = 0;
                }
-#endif IP_HDRINCL
        }
+       
+#if    defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+       if (setpolicy(sndsock, "in bypass") < 0)
+               errx(1, "%s", ipsec_strerror());
+
+       if (setpolicy(sndsock, "out bypass") < 0)
+               errx(1, "%s", ipsec_strerror());
+#endif /* defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) */
 
-       Fprintf(stderr, "traceroute to %s (%s)", hostname,
-               inet_ntoa(to->sin_addr));
+       Fprintf(stderr, "%s to %s (%s)",
+           prog, hostname, inet_ntoa(to->sin_addr));
        if (source)
                Fprintf(stderr, " from %s", source);
-       Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
-       (void) fflush(stderr);
+       Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
+       (void)fflush(stderr);
 
-       for (ttl = 1; ttl <= max_ttl; ++ttl) {
-               u_long lastaddr = 0;
+       for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+               u_int32_t lastaddr = 0;
+               int gotlastaddr = 0;
                int got_there = 0;
                int unreachable = 0;
+               int sentfirst = 0;
+               int loss;
 
                Printf("%2d ", ttl);
-               for (probe = 0; probe < nprobes; ++probe) {
-                       int cc;
+               for (probe = 0, loss = 0; probe < nprobes; ++probe) {
+                       register int cc;
                        struct timeval t1, t2;
                        struct timezone tz;
-                       struct ip *ip;
-
-                       (void) gettimeofday(&t1, &tz);
-                       send_probe(++seq, ttl);
-                       while (cc = wait_for_reply(s, &from)) {
-                               (void) gettimeofday(&t2, &tz);
-                               if ((i = packet_ok(packet, cc, &from, seq))) {
-                                       if (from.sin_addr.s_addr != lastaddr) {
-                                               print(packet, cc, &from);
-                                               lastaddr = from.sin_addr.s_addr;
-                                       }
-                                       Printf("  %g ms", deltaT(&t1, &t2));
-                                       switch(i - 1) {
-                                       case ICMP_UNREACH_PORT:
+                       register struct ip *ip;
+                       struct outdata outdata;
+
+                       if (sentfirst && pausemsecs > 0)
+                               usleep(pausemsecs * 1000);
+                       /* Prepare outgoing data */
+                       outdata.seq = ++seq;
+                       outdata.ttl = ttl;
+
+                       /* Avoid alignment problems by copying bytewise: */
+                       (void)gettimeofday(&t1, &tz);
+                       memcpy(&outdata.tv, &t1, sizeof(outdata.tv));
+
+                       /* Finalize and send packet */
+                       (*proto->prepare)(&outdata);
+                       send_probe(seq, ttl);
+                       ++sentfirst;
+
+                       /* Wait for a reply */
+                       while ((cc = wait_for_reply(s, from, &t1)) != 0) {
+                               double T;
+                               int precis;
+
+                               (void)gettimeofday(&t2, &tz);
+                               i = packet_ok(packet, cc, from, seq);
+                               /* Skip short packet */
+                               if (i == 0)
+                                       continue;
+                               if (!gotlastaddr ||
+                                   from->sin_addr.s_addr != lastaddr) {
+                                       if (gotlastaddr) printf("\n   ");
+                                       print(packet, cc, from);
+                                       lastaddr = from->sin_addr.s_addr;
+                                       ++gotlastaddr;
+                               }
+                               T = deltaT(&t1, &t2);
+#ifdef SANE_PRECISION
+                               if (T >= 1000.0)
+                                       precis = 0;
+                               else if (T >= 100.0)
+                                       precis = 1;
+                               else if (T >= 10.0)
+                                       precis = 2;
+                               else
+#endif
+                                       precis = 3;
+                               Printf("  %.*f ms", precis, T);
+                               if (printdiff) {
+                                       Printf("\n");
+                                       Printf("%*.*s%s\n",
+                                           -(outip->ip_hl << 3),
+                                           outip->ip_hl << 3,
+                                           ip_hdr_key,
+                                           proto->key);
+                                       pkt_compare((void *)outip, packlen,
+                                           (void *)hip, hiplen);
+                               }
+                               if (i == -2) {
 #ifndef ARCHAIC
-                                               ip = (struct ip *)packet;
-                                               if (ip->ip_ttl <= 1)
-                                                       Printf(" !");
-#endif ARCHAIC
-                                               ++got_there;
-                                               break;
-                                       case ICMP_UNREACH_NET:
-                                               ++unreachable;
-                                               Printf(" !N");
-                                               break;
-                                       case ICMP_UNREACH_HOST:
-                                               ++unreachable;
-                                               Printf(" !H");
-                                               break;
-                                       case ICMP_UNREACH_PROTOCOL:
-                                               ++got_there;
-                                               Printf(" !P");
-                                               break;
-                                       case ICMP_UNREACH_NEEDFRAG:
-                                               ++unreachable;
-                                               Printf(" !F");
-                                               break;
-                                       case ICMP_UNREACH_SRCFAIL:
-                                               ++unreachable;
-                                               Printf(" !S");
-                                               break;
-                                       }
+                                       ip = (struct ip *)packet;
+                                       if (ip->ip_ttl <= 1)
+                                               Printf(" !");
+#endif
+                                       ++got_there;
                                        break;
                                }
+                               /* time exceeded in transit */
+                               if (i == -1)
+                                       break;
+                               code = i - 1;
+                               switch (code) {
+
+                               case ICMP_UNREACH_PORT:
+#ifndef ARCHAIC
+                                       ip = (struct ip *)packet;
+                                       if (ip->ip_ttl <= 1)
+                                               Printf(" !");
+#endif
+                                       ++got_there;
+                                       break;
+
+                               case ICMP_UNREACH_NET:
+                                       ++unreachable;
+                                       Printf(" !N");
+                                       break;
+
+                               case ICMP_UNREACH_HOST:
+                                       ++unreachable;
+                                       Printf(" !H");
+                                       break;
+
+                               case ICMP_UNREACH_PROTOCOL:
+                                       ++got_there;
+                                       Printf(" !P");
+                                       break;
+
+                               case ICMP_UNREACH_NEEDFRAG:
+                                       ++unreachable;
+                                       Printf(" !F-%d", pmtu);
+                                       break;
+
+                               case ICMP_UNREACH_SRCFAIL:
+                                       ++unreachable;
+                                       Printf(" !S");
+                                       break;
+
+                               case ICMP_UNREACH_NET_UNKNOWN:
+                                       ++unreachable;
+                                       Printf(" !U");
+                                       break;
+
+                               case ICMP_UNREACH_HOST_UNKNOWN:
+                                       ++unreachable;
+                                       Printf(" !W");
+                                       break;
+
+                               case ICMP_UNREACH_ISOLATED:
+                                       ++unreachable;
+                                       Printf(" !I");
+                                       break;
+
+                               case ICMP_UNREACH_NET_PROHIB:
+                                       ++unreachable;
+                                       Printf(" !A");
+                                       break;
+
+                               case ICMP_UNREACH_HOST_PROHIB:
+                                       ++unreachable;
+                                       Printf(" !Z");
+                                       break;
+
+                               case ICMP_UNREACH_TOSNET:
+                                       ++unreachable;
+                                       Printf(" !Q");
+                                       break;
+
+                               case ICMP_UNREACH_TOSHOST:
+                                       ++unreachable;
+                                       Printf(" !T");
+                                       break;
+
+                               case ICMP_UNREACH_FILTER_PROHIB:
+                                       ++unreachable;
+                                       Printf(" !X");
+                                       break;
+
+                               case ICMP_UNREACH_HOST_PRECEDENCE:
+                                       ++unreachable;
+                                       Printf(" !V");
+                                       break;
+
+                               case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+                                       ++unreachable;
+                                       Printf(" !C");
+                                       break;
+
+                               default:
+                                       ++unreachable;
+                                       Printf(" !<%d>", code);
+                                       break;
+                               }
+                               break;
                        }
-                       if (cc == 0)
+                       if (cc == 0) {
+                               loss++;
                                Printf(" *");
-                       (void) fflush(stdout);
+                       }
+                       (void)fflush(stdout);
+               }
+               if (sump) {
+                       Printf(" (%d%% loss)", (loss * 100) / nprobes);
                }
                putchar('\n');
-               if (got_there || unreachable >= nprobes-1)
-                       exit(0);
+               if (got_there ||
+                   (unreachable > 0 && unreachable >= nprobes - 1))
+                       break;
        }
+       if (as_path)
+               as_shutdown(asn);
+       exit(0);
 }
 
 int
-wait_for_reply(sock, from)
-       int sock;
-       struct sockaddr_in *from;
+wait_for_reply(register int sock, register struct sockaddr_in *fromp,
+    register const struct timeval *tp)
 {
-       fd_set fds;
-       struct timeval wait;
-       int cc = 0;
-       int fromlen = sizeof (*from);
-
-       FD_ZERO(&fds);
-       FD_SET(sock, &fds);
-       wait.tv_sec = waittime; wait.tv_usec = 0;
+       fd_set *fdsp;
+       size_t nfds;
+       struct timeval now, wait;
+       struct timezone tz;
+       register int cc = 0;
+       register int error;
+       socklen_t fromlen = sizeof(*fromp);
+
+       nfds = howmany(sock + 1, NFDBITS);
+       if ((fdsp = malloc(nfds * sizeof(fd_set))) == NULL)
+               err(1, "malloc");
+       memset(fdsp, 0, nfds * sizeof(fd_set));
+       FD_SET(sock, fdsp);
+
+       wait.tv_sec = tp->tv_sec + waittime;
+       wait.tv_usec = tp->tv_usec;
+       (void)gettimeofday(&now, &tz);
+       tvsub(&wait, &now);
+       if (wait.tv_sec < 0) {
+               wait.tv_sec = 0;
+               wait.tv_usec = 1;
+       }
 
-       if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
-               cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
-                           (struct sockaddr *)from, &fromlen);
+       error = select(sock + 1, fdsp, NULL, NULL, &wait);
+       if (error == -1 && errno == EINVAL) {
+               Fprintf(stderr, "%s: botched select() args\n", prog);
+               exit(1);
+       }
+       if (error > 0)
+               cc = recvfrom(sock, (char *)packet, sizeof(packet), 0,
+                           (struct sockaddr *)fromp, &fromlen);
 
+       free(fdsp);
        return(cc);
 }
 
-
 void
-send_probe(seq, ttl)
-       int seq, ttl;
+send_probe(int seq, int ttl)
 {
-       struct opacket *op = outpacket;
-       struct ip *ip = &op->ip;
-       struct udphdr *up = &op->udp;
-       int i;
+       register int cc;
+
+       outip->ip_ttl = ttl;
+       outip->ip_id = htons(ident + seq);
+
+       /* XXX undocumented debugging hack */
+       if (verbose > 1) {
+               register const u_short *sp;
+               register int nshorts, i;
+
+               sp = (u_short *)outip;
+               nshorts = (u_int)packlen / sizeof(u_short);
+               i = 0;
+               Printf("[ %d bytes", packlen);
+               while (--nshorts >= 0) {
+                       if ((i++ % 8) == 0)
+                               Printf("\n\t");
+                       Printf(" %04x", ntohs(*sp++));
+               }
+               if (packlen & 1) {
+                       if ((i % 8) == 0)
+                               Printf("\n\t");
+                       Printf(" %02x", *(u_char *)sp);
+               }
+               Printf("]\n");
+       }
+
+#if !defined(IP_HDRINCL) && defined(IP_TTL)
+       if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
+           (char *)&ttl, sizeof(ttl)) < 0) {
+               Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
+                   prog, ttl, strerror(errno));
+               exit(1);
+       }
+#endif
 
-       ip->ip_off = 0;
-       ip->ip_hl = sizeof(*ip) >> 2;
-       ip->ip_p = IPPROTO_UDP;
-       ip->ip_len = datalen;
-       ip->ip_ttl = ttl;
-       ip->ip_v = IPVERSION;
-       ip->ip_id = htons(ident+seq);
-
-       up->uh_sport = htons(ident);
-       up->uh_dport = htons(port+seq);
-       up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip)));
-       up->uh_sum = 0;
-
-       op->seq = seq;
-       op->ttl = ttl;
-       (void) gettimeofday(&op->tv, &tz);
-
-       i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
-                  sizeof(struct sockaddr));
-       if (i < 0 || i != datalen)  {
-               if (i<0)
-                       perror("sendto");
-               Printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
-                       datalen, i);
-               (void) fflush(stdout);
+       cc = sendto(sndsock, (char *)outip,
+           packlen, 0, &whereto, sizeof(whereto));
+       if (cc < 0 || cc != packlen)  {
+               if (cc < 0)
+                       Fprintf(stderr, "%s: sendto: %s\n",
+                           prog, strerror(errno));
+               Printf("%s: wrote %s %d chars, ret=%d\n",
+                   prog, hostname, packlen, cc);
+               (void)fflush(stdout);
        }
 }
 
+#if    defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+int
+setpolicy(so, policy)
+       int so;
+       char *policy;
+{
+       char *buf;
+
+       buf = ipsec_set_policy(policy, strlen(policy));
+       if (buf == NULL) {
+               warnx("%s", ipsec_strerror());
+               return -1;
+       }
+       (void)setsockopt(so, IPPROTO_IP, IP_IPSEC_POLICY,
+               buf, ipsec_get_policylen(buf));
+
+       free(buf);
+
+       return 0;
+}
+#endif
 
 double
-deltaT(t1p, t2p)
-       struct timeval *t1p, *t2p;
+deltaT(struct timeval *t1p, struct timeval *t2p)
 {
        register double dt;
 
@@ -645,13 +1294,11 @@ deltaT(t1p, t2p)
        return (dt);
 }
 
-
 /*
  * Convert an ICMP "type" field to a printable string.
  */
 char *
-pr_type(t)
-       u_char t;
+pr_type(register u_char t)
 {
        static char *ttab[] = {
        "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
@@ -661,25 +1308,21 @@ pr_type(t)
        "Info Reply"
        };
 
-       if(t > 16)
+       if (t > 16)
                return("OUT-OF-RANGE");
 
        return(ttab[t]);
 }
 
-
 int
-packet_ok(buf, cc, from, seq)
-       u_char *buf;
-       int cc;
-       struct sockaddr_in *from;
-       int seq;
+packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
+    register int seq)
 {
        register struct icmp *icp;
-       u_char type, code;
-       int hlen;
+       register u_char type, code;
+       register int hlen;
 #ifndef ARCHAIC
-       struct ip *ip;
+       register struct ip *ip;
 
        ip = (struct ip *) buf;
        hlen = ip->ip_hl << 2;
@@ -693,70 +1336,218 @@ packet_ok(buf, cc, from, seq)
        icp = (struct icmp *)(buf + hlen);
 #else
        icp = (struct icmp *)buf;
-#endif ARCHAIC
-       type = icp->icmp_type; code = icp->icmp_code;
+#endif
+       type = icp->icmp_type;
+       code = icp->icmp_code;
+       /* Path MTU Discovery (RFC1191) */
+       if (code != ICMP_UNREACH_NEEDFRAG)
+               pmtu = 0;
+       else {
+#ifdef HAVE_ICMP_NEXTMTU
+               pmtu = ntohs(icp->icmp_nextmtu);
+#else
+               pmtu = ntohs(((struct my_pmtu *)&icp->icmp_void)->ipm_nextmtu);
+#endif
+       }
+       if (type == ICMP_ECHOREPLY
+           && proto->num == IPPROTO_ICMP
+           && (*proto->check)((u_char *)icp, (u_char)seq))
+               return -2;
        if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
            type == ICMP_UNREACH) {
-               struct ip *hip;
-               struct udphdr *up;
+               u_char *inner;
 
                hip = &icp->icmp_ip;
+               hiplen = ((u_char *)icp + cc) - (u_char *)hip;
                hlen = hip->ip_hl << 2;
-               up = (struct udphdr *)((u_char *)hip + hlen);
-               if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
-                   up->uh_sport == htons(ident) &&
-                   up->uh_dport == htons(port+seq))
-                       return (type == ICMP_TIMXCEED? -1 : code+1);
+               inner = (u_char *)((u_char *)hip + hlen);
+               if (hlen + 12 <= cc
+                   && hip->ip_p == proto->num
+                   && (*proto->check)(inner, (u_char)seq))
+                       return (type == ICMP_TIMXCEED ? -1 : code + 1);
        }
 #ifndef ARCHAIC
        if (verbose) {
-               int i;
-               u_long *lp = (u_long *)&icp->icmp_ip;
-
-               Printf("\n%d bytes from %s to %s", cc,
-                       inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst));
-               Printf(": icmp type %d (%s) code %d\n", type, pr_type(type),
-                      icp->icmp_code);
-               for (i = 4; i < cc ; i += sizeof(long))
-                       Printf("%2d: x%8.8lx\n", i, *lp++);
+               register int i;
+               u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
+
+               Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
+               Printf("%s: icmp type %d (%s) code %d\n",
+                   inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
+               for (i = 4; i < cc ; i += sizeof(*lp))
+                       Printf("%2d: x%8.8x\n", i, *lp++);
        }
-#endif ARCHAIC
+#endif
        return(0);
 }
 
+void
+icmp_prep(struct outdata *outdata)
+{
+       struct icmp *const icmpheader = (struct icmp *) outp;
+
+       icmpheader->icmp_type = ICMP_ECHO;
+       icmpheader->icmp_id = htons(ident);
+       icmpheader->icmp_seq = htons(outdata->seq);
+       icmpheader->icmp_cksum = 0;
+       icmpheader->icmp_cksum = in_cksum((u_short *)icmpheader, protlen);
+       if (icmpheader->icmp_cksum == 0)
+               icmpheader->icmp_cksum = 0xffff;
+}
+
+int
+icmp_check(const u_char *data, int seq)
+{
+       struct icmp *const icmpheader = (struct icmp *) data;
+
+       return (icmpheader->icmp_id == htons(ident)
+           && icmpheader->icmp_seq == htons(seq));
+}
+
+void
+udp_prep(struct outdata *outdata)
+{
+       struct udphdr *const outudp = (struct udphdr *) outp;
+
+       outudp->uh_sport = htons(ident + (fixedPort ? outdata->seq : 0));
+       outudp->uh_dport = htons(port + (fixedPort ? 0 : outdata->seq));
+       outudp->uh_ulen = htons((u_short)protlen);
+       outudp->uh_sum = 0;
+       if (doipcksum) {
+           u_short sum = p_cksum(outip, (u_short*)outudp, protlen);
+           outudp->uh_sum = (sum) ? sum : 0xffff;
+       }
+
+       return;
+}
+
+int
+udp_check(const u_char *data, int seq)
+{
+       struct udphdr *const udp = (struct udphdr *) data;
+
+       return (ntohs(udp->uh_sport) == ident + (fixedPort ? seq : 0) &&
+           ntohs(udp->uh_dport) == port + (fixedPort ? 0 : seq));
+}
+
+void
+tcp_prep(struct outdata *outdata)
+{
+       struct tcphdr *const tcp = (struct tcphdr *) outp;
+
+       tcp->th_sport = htons(ident);
+       tcp->th_dport = htons(port + (fixedPort ? 0 : outdata->seq));
+       tcp->th_seq = (tcp->th_sport << 16) | (tcp->th_dport +
+           (fixedPort ? outdata->seq : 0));
+       tcp->th_ack = 0;
+       tcp->th_off = 5;
+       tcp->th_flags = TH_SYN;
+       tcp->th_sum = 0;
+
+       if (doipcksum) {
+           u_short sum = p_cksum(outip, (u_short*)tcp, protlen);
+           tcp->th_sum = (sum) ? sum : 0xffff;
+       }
+}
+
+int
+tcp_check(const u_char *data, int seq)
+{
+       struct tcphdr *const tcp = (struct tcphdr *) data;
+
+       return (ntohs(tcp->th_sport) == ident
+           && ntohs(tcp->th_dport) == port + (fixedPort ? 0 : seq))
+           && tcp->th_seq == ((ident << 16) | (port + seq));
+}
 
 void
-print(buf, cc, from)
-       u_char *buf;
-       int cc;
-       struct sockaddr_in *from;
+gre_prep(struct outdata *outdata)
 {
-       struct ip *ip;
-       int hlen;
+       struct grehdr *const gre = (struct grehdr *) outp;
+
+       gre->flags = htons(0x2001);
+       gre->proto = htons(port);
+       gre->length = 0;
+       gre->callId = htons(ident + outdata->seq);
+}
+
+int
+gre_check(const u_char *data, int seq)
+{
+       struct grehdr *const gre = (struct grehdr *) data;
+
+       return(ntohs(gre->proto) == port
+           && ntohs(gre->callId) == ident + seq);
+}
+
+void
+gen_prep(struct outdata *outdata)
+{
+       u_int16_t *const ptr = (u_int16_t *) outp;
+
+       ptr[0] = htons(ident);
+       ptr[1] = htons(port + outdata->seq);
+}
+
+int
+gen_check(const u_char *data, int seq)
+{
+       u_int16_t *const ptr = (u_int16_t *) data;
+
+       return(ntohs(ptr[0]) == ident
+           && ntohs(ptr[1]) == port + seq);
+}
+
+void
+print(register u_char *buf, register int cc, register struct sockaddr_in *from)
+{
+       register struct ip *ip;
+       register int hlen;
 
        ip = (struct ip *) buf;
        hlen = ip->ip_hl << 2;
        cc -= hlen;
 
+       if (as_path)
+               Printf(" [AS%d]", as_lookup(asn, &from->sin_addr));
+
        if (nflag)
                Printf(" %s", inet_ntoa(from->sin_addr));
        else
                Printf(" %s (%s)", inetname(from->sin_addr),
-                      inet_ntoa(from->sin_addr));
+                   inet_ntoa(from->sin_addr));
 
        if (verbose)
-               Printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
+               Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
 }
 
+/*
+ * Checksum routine for UDP and TCP headers.
+ */
+u_short 
+p_cksum(struct ip *ip, u_short *data, int len)
+{
+       static struct ipovly ipo;
+       u_short sumh, sumd;
+       u_int32_t sumt;
+
+       ipo.ih_pr = ip->ip_p;
+       ipo.ih_len = htons(len);
+       ipo.ih_src = ip->ip_src;
+       ipo.ih_dst = ip->ip_dst;
+
+       sumh = in_cksum((u_short*)&ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
+       sumd = in_cksum((u_short*)data, len);         /* payload data cksum */
+       sumt = (sumh << 16) | (sumd);
+
+       return ~in_cksum((u_short*)&sumt, sizeof(sumt));
+}
 
-#ifdef notyet
 /*
  * Checksum routine for Internet Protocol family headers (C Version)
  */
 u_short
-in_cksum(addr, len)
-       u_short *addr;
-       int len;
+in_cksum(register u_short *addr, register int len)
 {
        register int nleft = len;
        register u_short *w = addr;
@@ -786,73 +1577,246 @@ in_cksum(addr, len)
        answer = ~sum;                          /* truncate to 16 bits */
        return (answer);
 }
-#endif notyet
 
 /*
  * Subtract 2 timeval structs:  out = out - in.
- * Out is assumed to be >= in.
+ * Out is assumed to be within about LONG_MAX seconds of in.
  */
 void
-tvsub(out, in)
-       register struct timeval *out, *in;
+tvsub(register struct timeval *out, register struct timeval *in)
 {
+
        if ((out->tv_usec -= in->tv_usec) < 0)   {
-               out->tv_sec--;
+               --out->tv_sec;
                out->tv_usec += 1000000;
        }
        out->tv_sec -= in->tv_sec;
 }
 
-
 /*
  * Construct an Internet address representation.
  * If the nflag has been supplied, give
  * numeric value, otherwise try for symbolic name.
  */
 char *
-inetname(in)
-       struct in_addr in;
+inetname(struct in_addr in)
 {
        register char *cp;
-       static char line[50];
-       struct hostent *hp;
-       static char domain[MAXHOSTNAMELEN + 1];
+       register struct hostent *hp;
        static int first = 1;
+       static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
 
        if (first && !nflag) {
                first = 0;
-               if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
-                   (cp = index(domain, '.')))
-                       (void) strcpy(domain, cp + 1);
-               else
-                       domain[0] = 0;
+               if (gethostname(domain, sizeof(domain) - 1) < 0)
+                       domain[0] = '\0';
+               else {
+                       cp = strchr(domain, '.');
+                       if (cp == NULL) {
+                               hp = gethostbyname(domain);
+                               if (hp != NULL)
+                                       cp = strchr(hp->h_name, '.');
+                       }
+                       if (cp == NULL)
+                               domain[0] = '\0';
+                       else {
+                               ++cp;
+                               memmove(domain, cp, strlen(cp) + 1);
+                       }
+               }
        }
-       cp = 0;
        if (!nflag && in.s_addr != INADDR_ANY) {
-               hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
-               if (hp) {
-                       if ((cp = index(hp->h_name, '.')) &&
-                           !strcmp(cp + 1, domain))
-                               *cp = 0;
-                       cp = hp->h_name;
+               hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
+               if (hp != NULL) {
+                       if ((cp = strchr(hp->h_name, '.')) != NULL &&
+                           strcmp(cp + 1, domain) == 0)
+                               *cp = '\0';
+                       (void)strlcpy(line, hp->h_name, sizeof(line));
+                       return (line);
                }
        }
-       if (cp)
-               (void) strcpy(line, cp);
-       else {
-               in.s_addr = ntohl(in.s_addr);
-#define C(x)   ((x) & 0xff)
-               Sprintf(line, "%lu.%lu.%lu.%lu", C(in.s_addr >> 24),
-                       C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
+       return (inet_ntoa(in));
+}
+
+struct hostinfo *
+gethostinfo(register char *hostname)
+{
+       register int n;
+       register struct hostent *hp;
+       register struct hostinfo *hi;
+       register char **p;
+       register u_int32_t addr, *ap;
+
+       if (strlen(hostname) > 64) {
+               Fprintf(stderr, "%s: hostname \"%.32s...\" is too long\n",
+                   prog, hostname);
+               exit(1);
+       }
+       hi = calloc(1, sizeof(*hi));
+       if (hi == NULL) {
+               Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
+               exit(1);
+       }
+       addr = inet_addr(hostname);
+       if ((int32_t)addr != -1) {
+               hi->name = strdup(hostname);
+               hi->n = 1;
+               hi->addrs = calloc(1, sizeof(hi->addrs[0]));
+               if (hi->addrs == NULL) {
+                       Fprintf(stderr, "%s: calloc %s\n",
+                           prog, strerror(errno));
+                       exit(1);
+               }
+               hi->addrs[0] = addr;
+               return (hi);
+       }
+
+       hp = gethostbyname(hostname);
+       if (hp == NULL) {
+               Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
+               exit(1);
+       }
+       if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
+               Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
+               exit(1);
        }
-       return (line);
+       hi->name = strdup(hp->h_name);
+       for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
+               continue;
+       hi->n = n;
+       hi->addrs = calloc(n, sizeof(hi->addrs[0]));
+       if (hi->addrs == NULL) {
+               Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
+               exit(1);
+       }
+       for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
+               memcpy(ap, *p, sizeof(*ap));
+       return (hi);
+}
+
+void
+freehostinfo(register struct hostinfo *hi)
+{
+       if (hi->name != NULL) {
+               free(hi->name);
+               hi->name = NULL;
+       }
+       free((char *)hi->addrs);
+       free((char *)hi);
+}
+
+void
+getaddr(register u_int32_t *ap, register char *hostname)
+{
+       register struct hostinfo *hi;
+
+       hi = gethostinfo(hostname);
+       *ap = hi->addrs[0];
+       freehostinfo(hi);
 }
 
 void
-usage()
+setsin(register struct sockaddr_in *sin, register u_int32_t addr)
 {
-       (void)fprintf(stderr,
-"usage: traceroute [-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n\t\
-[-s src_addr] [-t tos] [-w wait] host [data size]\n");
+
+       memset(sin, 0, sizeof(*sin));
+#ifdef HAVE_SOCKADDR_SA_LEN
+       sin->sin_len = sizeof(*sin);
+#endif
+       sin->sin_family = AF_INET;
+       sin->sin_addr.s_addr = addr;
+}
+
+/* String to value with optional min and max. Handles decimal and hex. */
+int
+str2val(register const char *str, register const char *what,
+    register int mi, register int ma)
+{
+       register const char *cp;
+       register int val;
+       char *ep;
+
+       if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+               cp = str + 2;
+               val = (int)strtol(cp, &ep, 16);
+       } else
+               val = (int)strtol(str, &ep, 10);
+       if (*ep != '\0') {
+               Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
+                   prog, str, what);
+               exit(1);
+       }
+       if (val < mi && mi >= 0) {
+               if (mi == 0)
+                       Fprintf(stderr, "%s: %s must be >= %d\n",
+                           prog, what, mi);
+               else
+                       Fprintf(stderr, "%s: %s must be > %d\n",
+                           prog, what, mi - 1);
+               exit(1);
+       }
+       if (val > ma && ma >= 0) {
+               Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
+               exit(1);
+       }
+       return (val);
+}
+
+struct outproto *
+setproto(char *pname)
+{
+       struct outproto *proto;
+       int i;
+
+       for (i = 0; protos[i].name != NULL; i++) {
+               if (strcasecmp(protos[i].name, pname) == 0) {
+                       break;
+               }
+       }
+       proto = &protos[i];
+       if (proto->name == NULL) {      /* generic handler */
+               struct protoent *pe;
+               u_int32_t pnum;
+
+               /* Determine the IP protocol number */
+               if ((pe = getprotobyname(pname)) != NULL)
+                       pnum = pe->p_proto;
+               else
+                       pnum = str2val(optarg, "proto number", 1, 255);
+               proto->num = pnum;
+       }
+       return proto;
+}
+
+void
+pkt_compare(const u_char *a, int la, const u_char *b, int lb) {
+       int l;
+       int i;
+
+       for (i = 0; i < la; i++)
+               Printf("%02x", (unsigned int)a[i]);
+       Printf("\n");
+       l = (la <= lb) ? la : lb;
+       for (i = 0; i < l; i++)
+               if (a[i] == b[i])
+                       Printf("__");
+               else
+                       Printf("%02x", (unsigned int)b[i]);
+       for (; i < lb; i++)
+               Printf("%02x", (unsigned int)b[i]);
+       Printf("\n");
+}
+
+
+void
+usage(void)
+{
+       extern char version[];
+
+       Fprintf(stderr, "Version %s\n", version);
+       Fprintf(stderr,
+           "Usage: %s [-adDeFInrSvx] [-A as_server] [-f first_ttl] [-g gateway] [-i iface]\n"
+           "\t[-M first_ttl] [-m max_ttl] [-p port] [-P proto] [-q nqueries] [-s src_addr]\n"
+           "\t[-t tos] [-w waittime] [-z pausemsecs] host [packetlen]\n", prog);
        exit(1);
 }