X-Git-Url: https://git.saurik.com/apple/network_cmds.git/blobdiff_plain/b7080c8e96625177072137d504eb8e9c9d748e49..HEAD:/traceroute.tproj/traceroute.c?ds=sidebyside diff --git a/traceroute.tproj/traceroute.c b/traceroute.tproj/traceroute.c index 1f8a5fa..a411d0a 100644 --- a/traceroute.tproj/traceroute.c +++ b/traceroute.tproj/traceroute.c @@ -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 #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 + * 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 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 -#include -#include #include #include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif +#include #include #include #include +#include #include #include +#include +#include +#include #include +#ifdef IPSEC +#include +#include /* XXX */ +#endif /* IPSEC */ + +#include +#include +#include +#include +#ifdef HAVE_MALLOC_H +#include +#endif +#include #include #include -#include #include #include #include -#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); }