source: trunk/third/traceroute/traceroute.c @ 15096

Revision 15096, 35.1 KB checked in by ghudson, 24 years ago (diff)
From kolya: always compute ICMP checksums.
Line 
1/*
2 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#ifndef lint
23static const char copyright[] =
24    "@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997\n\
25The Regents of the University of California.  All rights reserved.\n";
26static const char rcsid[] =
27    "@(#)$Header: /afs/dev.mit.edu/source/repository/third/traceroute/traceroute.c,v 1.6 2000-09-21 15:03:34 ghudson Exp $ (LBL)";
28#endif
29
30/*
31 * traceroute host  - trace the route ip packets follow going to "host".
32 *
33 * Attempt to trace the route an ip packet would follow to some
34 * internet host.  We find out intermediate hops by launching probe
35 * packets with a small ttl (time to live) then listening for an
36 * icmp "time exceeded" reply from a gateway.  We start our probes
37 * with a ttl of one and increase by one until we get an icmp "port
38 * unreachable" (which means we got to "host") or hit a max (which
39 * defaults to 30 hops & can be changed with the -m flag).  Three
40 * probes (change with -q flag) are sent at each ttl setting and a
41 * line is printed showing the ttl, address of the gateway and
42 * round trip time of each probe.  If the probe answers come from
43 * different gateways, the address of each responding system will
44 * be printed.  If there is no response within a 5 sec. timeout
45 * interval (changed with the -w flag), a "*" is printed for that
46 * probe.
47 *
48 * Probe packets are UDP format.  We don't want the destination
49 * host to process them so the destination port is set to an
50 * unlikely value (if some clod on the destination is using that
51 * value, it can be changed with the -p flag).
52 *
53 * A sample use might be:
54 *
55 *     [yak 71]% traceroute nis.nsf.net.
56 *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
57 *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
58 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
59 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
60 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
61 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
62 *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
63 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
64 *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
65 *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
66 *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
67 *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
68 *
69 * Note that lines 2 & 3 are the same.  This is due to a buggy
70 * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
71 * packets with a zero ttl.
72 *
73 * A more interesting example is:
74 *
75 *     [yak 72]% traceroute allspice.lcs.mit.edu.
76 *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
77 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
78 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
79 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
80 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
81 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
82 *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
83 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
84 *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
85 *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
86 *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
87 *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
88 *     12  * * *
89 *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
90 *     14  * * *
91 *     15  * * *
92 *     16  * * *
93 *     17  * * *
94 *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
95 *
96 * (I start to see why I'm having so much trouble with mail to
97 * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
98 * either don't send ICMP "time exceeded" messages or send them
99 * with a ttl too small to reach us.  14 - 17 are running the
100 * MIT C Gateway code that doesn't send "time exceeded"s.  God
101 * only knows what's going on with 12.
102 *
103 * The silent gateway 12 in the above may be the result of a bug in
104 * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
105 * sends an unreachable message using whatever ttl remains in the
106 * original datagram.  Since, for gateways, the remaining ttl is
107 * zero, the icmp "time exceeded" is guaranteed to not make it back
108 * to us.  The behavior of this bug is slightly more interesting
109 * when it appears on the destination system:
110 *
111 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
112 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
113 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
114 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
115 *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
116 *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
117 *      7  * * *
118 *      8  * * *
119 *      9  * * *
120 *     10  * * *
121 *     11  * * *
122 *     12  * * *
123 *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
124 *
125 * Notice that there are 12 "gateways" (13 is the final
126 * destination) and exactly the last half of them are "missing".
127 * What's really happening is that rip (a Sun-3 running Sun OS3.5)
128 * is using the ttl from our arriving datagram as the ttl in its
129 * icmp reply.  So, the reply will time out on the return path
130 * (with no notice sent to anyone since icmp's aren't sent for
131 * icmp's) until we probe with a ttl that's at least twice the path
132 * length.  I.e., rip is really only 7 hops away.  A reply that
133 * returns with a ttl of 1 is a clue this problem exists.
134 * Traceroute prints a "!" after the time if the ttl is <= 1.
135 * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
136 * non-standard (HPUX) software, expect to see this problem
137 * frequently and/or take care picking the target host of your
138 * probes.
139 *
140 * Other possible annotations after the time are !H, !N, !P (got a host,
141 * network or protocol unreachable, respectively), !S or !F (source
142 * route failed or fragmentation needed -- neither of these should
143 * ever occur and the associated gateway is busted if you see one).  If
144 * almost all the probes result in some kind of unreachable, traceroute
145 * will give up and exit.
146 *
147 * Notes
148 * -----
149 * This program must be run by root or be setuid.  (I suggest that
150 * you *don't* make it setuid -- casual use could result in a lot
151 * of unnecessary traffic on our poor, congested nets.)
152 *
153 * This program requires a kernel mod that does not appear in any
154 * system available from Berkeley:  A raw ip socket using proto
155 * IPPROTO_RAW must interpret the data sent as an ip datagram (as
156 * opposed to data to be wrapped in a ip datagram).  See the README
157 * file that came with the source to this program for a description
158 * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
159 * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
160 * MODIFIED TO RUN THIS PROGRAM.
161 *
162 * The udp port usage may appear bizarre (well, ok, it is bizarre).
163 * The problem is that an icmp message only contains 8 bytes of
164 * data from the original datagram.  8 bytes is the size of a udp
165 * header so, if we want to associate replies with the original
166 * datagram, the necessary information must be encoded into the
167 * udp header (the ip id could be used but there's no way to
168 * interlock with the kernel's assignment of ip id's and, anyway,
169 * it would have taken a lot more kernel hacking to allow this
170 * code to set the ip id).  So, to allow two or more users to
171 * use traceroute simultaneously, we use this task's pid as the
172 * source port (the high bit is set to move the port number out
173 * of the "likely" range).  To keep track of which probe is being
174 * replied to (so times and/or hop counts don't get confused by a
175 * reply that was delayed in transit), we increment the destination
176 * port number before each probe.
177 *
178 * Don't use this as a coding example.  I was trying to find a
179 * routing problem and this code sort-of popped out after 48 hours
180 * without sleep.  I was amazed it ever compiled, much less ran.
181 *
182 * I stole the idea for this program from Steve Deering.  Since
183 * the first release, I've learned that had I attended the right
184 * IETF working group meetings, I also could have stolen it from Guy
185 * Almes or Matt Mathis.  I don't know (or care) who came up with
186 * the idea first.  I envy the originators' perspicacity and I'm
187 * glad they didn't keep the idea a secret.
188 *
189 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
190 * enhancements to the original distribution.
191 *
192 * I've hacked up a round-trip-route version of this that works by
193 * sending a loose-source-routed udp datagram through the destination
194 * back to yourself.  Unfortunately, SO many gateways botch source
195 * routing, the thing is almost worthless.  Maybe one day...
196 *
197 *  -- Van Jacobson (van@ee.lbl.gov)
198 *     Tue Dec 20 03:50:13 PST 1988
199 */
200
201#include <sys/param.h>
202#include <sys/file.h>
203#include <sys/ioctl.h>
204#ifdef HAVE_SYS_SELECT_H
205#include <sys/select.h>
206#endif
207#include <sys/socket.h>
208#include <sys/time.h>
209
210#include <netinet/in_systm.h>
211#include <netinet/in.h>
212#include <netinet/ip.h>
213#include <netinet/ip_var.h>
214#include <netinet/ip_icmp.h>
215#include <netinet/udp.h>
216#include <netinet/udp_var.h>
217
218#include <arpa/inet.h>
219
220#include <ctype.h>
221#include <errno.h>
222#ifdef HAVE_MALLOC_H
223#include <malloc.h>
224#endif
225#include <memory.h>
226#include <netdb.h>
227#include <stdio.h>
228#include <stdlib.h>
229#include <string.h>
230#include <unistd.h>
231
232#include "gnuc.h"
233#ifdef HAVE_OS_PROTO_H
234#include "os-proto.h"
235#endif
236
237#include "ifaddrlist.h"
238
239/* Maximum number of gateways (include room for one noop) */
240#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
241
242#ifndef MAXHOSTNAMELEN
243#define MAXHOSTNAMELEN  64
244#endif
245
246#define Fprintf (void)fprintf
247#define Printf (void)printf
248
249/* Host name and address list */
250struct hostinfo {
251        char *name;
252        int n;
253        u_int32_t *addrs;
254};
255
256/* Data section of the probe packet */
257struct outdata {
258        u_char seq;             /* sequence number of this packet */
259        u_char ttl;             /* ttl packet left with */
260        struct timeval tv;      /* time packet left */
261};
262
263u_char  packet[512];            /* last inbound (icmp) packet */
264
265struct ip *outip;               /* last output (udp) packet */
266struct udphdr *outudp;          /* last output (udp) packet */
267struct outdata *outdata;        /* last output (udp) packet */
268
269struct icmp *outicmp;           /* last output (icmp) packet */
270
271/* loose source route gateway list (including room for final destination) */
272u_int32_t gwlist[NGATEWAYS + 1];
273
274int s;                          /* receive (icmp) socket file descriptor */
275int sndsock;                    /* send (udp/icmp) socket file descriptor */
276
277struct sockaddr whereto;        /* Who to try to reach */
278struct sockaddr_in wherefrom;   /* Who we are */
279int packlen;                    /* total length of packet */
280int minpacket;                  /* min ip packet size */
281int maxpacket = 32 * 1024;      /* max ip packet size */
282
283char *prog;
284char *source;
285char *hostname;
286char *device;
287
288int nprobes = 3;
289int max_ttl = 30;
290int first_ttl = 1;
291u_short ident;
292u_short port = 32768 + 666;     /* start udp dest port # for probe packets */
293
294int options;                    /* socket options */
295int verbose;
296int waittime = 5;               /* time to wait for response (in seconds) */
297int nflag;                      /* print addresses numerically */
298int useicmp;                    /* use icmp echo instead of udp packets */
299#ifdef CANT_HACK_CKSUM
300int docksum = 0;                /* don't calculate checksums */
301#else
302int docksum = 1;                /* calculate checksums */
303#endif
304int optlen;                     /* length of ip options */
305
306extern int optind;
307extern int opterr;
308extern char *optarg;
309
310/* Forwards */
311double  deltaT(struct timeval *, struct timeval *);
312void    freehostinfo(struct hostinfo *);
313void    getaddr(u_int32_t *, char *);
314struct  hostinfo *gethostinfo(char *);
315u_short in_cksum(u_short *, int);
316char    *inetname(struct in_addr);
317int     main(int, char **);
318int     packet_ok(u_char *, int, struct sockaddr_in *, int);
319char    *pr_type(u_char);
320void    print(u_char *, int, struct sockaddr_in *);
321void    send_probe(int, int, struct timeval *);
322void    setsin(struct sockaddr_in *, u_int32_t);
323int     str2val(const char *, const char *, int, int);
324void    tvsub(struct timeval *, struct timeval *);
325__dead  void usage(void);
326int     wait_for_reply(int, struct sockaddr_in *, struct timeval *);
327
328int
329main(int argc, char **argv)
330{
331        register int op, code, n;
332        register char *cp;
333        register u_char *outp;
334        register u_int32_t *ap;
335        register struct sockaddr_in *from = &wherefrom;
336        register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
337        register struct hostinfo *hi;
338        int on = 1;
339        register struct protoent *pe;
340        register int ttl, probe, i;
341        register int seq = 0;
342        int tos = 0, settos = 0;
343        register int lsrr = 0;
344        register u_short off = 0;
345        struct ifaddrlist *al;
346        char errbuf[132];
347
348        if ((cp = strrchr(argv[0], '/')) != NULL)
349                prog = cp + 1;
350        else
351                prog = argv[0];
352
353        opterr = 0;
354        while ((op = getopt(argc, argv, "dFInrvxf:g:i:m:p:q:s:t:w:")) != EOF)
355                switch (op) {
356
357                case 'd':
358                        options |= SO_DEBUG;
359                        break;
360
361                case 'f':
362                        first_ttl = str2val(optarg, "first ttl", 1, 255);
363                        break;
364
365                case 'F':
366                        off = IP_DF;
367                        break;
368
369                case 'g':
370                        if (lsrr >= NGATEWAYS) {
371                                Fprintf(stderr,
372                                    "%s: No more than %d gateways\n",
373                                    prog, NGATEWAYS);
374                                exit(1);
375                        }
376                        getaddr(gwlist + lsrr, optarg);
377                        ++lsrr;
378                        break;
379
380                case 'i':
381                        device = optarg;
382                        break;
383
384                case 'I':
385                        ++useicmp;
386                        break;
387
388                case 'm':
389                        max_ttl = str2val(optarg, "max ttl", 1, 255);
390                        break;
391
392                case 'n':
393                        ++nflag;
394                        break;
395
396                case 'p':
397                        port = str2val(optarg, "port", 1, -1);
398                        break;
399
400                case 'q':
401                        nprobes = str2val(optarg, "nprobes", 1, -1);
402                        break;
403
404                case 'r':
405                        options |= SO_DONTROUTE;
406                        break;
407
408                case 's':
409                        /*
410                         * set the ip source address of the outbound
411                         * probe (e.g., on a multi-homed host).
412                         */
413                        source = optarg;
414                        break;
415
416                case 't':
417                        tos = str2val(optarg, "tos", 0, 255);
418                        ++settos;
419                        break;
420
421                case 'v':
422                        ++verbose;
423                        break;
424
425                case 'x':
426                        docksum = (docksum == 0);
427                        break;
428
429                case 'w':
430                        waittime = str2val(optarg, "wait time", 2, 24 * 3600);
431                        break;
432
433                default:
434                        usage();
435                }
436
437        if (first_ttl > max_ttl) {
438                Fprintf(stderr,
439                    "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
440                    prog, first_ttl, max_ttl);
441                exit(1);
442        }
443
444        if (!docksum)
445                Fprintf(stderr, "%s: Warning: checksums disabled\n", prog);
446
447        if (lsrr > 0)
448                optlen = (lsrr + 1) * sizeof(gwlist[0]);
449        minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
450        if (useicmp)
451                minpacket += 8;                 /* XXX magic number */
452        else
453                minpacket += sizeof(*outudp);
454        if (packlen == 0)
455                packlen = minpacket;            /* minimum sized packet */
456        else if (minpacket > packlen || packlen > maxpacket) {
457                Fprintf(stderr, "%s: packet size must be %d <= s <= %d\n",
458                    prog, minpacket, maxpacket);
459                exit(1);
460        }
461
462        /* Process destination and optional packet size */
463        switch (argc - optind) {
464
465        case 2:
466                packlen = str2val(argv[optind + 1],
467                    "packet length", minpacket, -1);
468                /* Fall through */
469
470        case 1:
471                hostname = argv[optind];
472                hi = gethostinfo(hostname);
473                setsin(to, hi->addrs[0]);
474                if (hi->n > 1)
475                        Fprintf(stderr,
476                    "%s: Warning: %s has multiple addresses; using %s\n",
477                                prog, hostname, inet_ntoa(to->sin_addr));
478                hostname = hi->name;
479                hi->name = NULL;
480                freehostinfo(hi);
481                break;
482
483        default:
484                usage();
485        }
486
487#ifdef HAVE_SETLINEBUF
488        setlinebuf (stdout);
489#else
490        setvbuf(stdout, NULL, _IOLBF, 0);
491#endif
492
493        outip = (struct ip *)malloc((unsigned)packlen);
494        if (outip == NULL) {
495                Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
496                exit(1);
497        }
498        memset((char *)outip, 0, packlen);
499
500        outip->ip_v = IPVERSION;
501        if (settos)
502                outip->ip_tos = tos;
503#ifdef BYTESWAP_IP_LEN
504        outip->ip_len = htons(packlen);
505#else
506        outip->ip_len = packlen;
507#endif
508        outip->ip_off = off;
509        outp = (u_char *)(outip + 1);
510#ifdef HAVE_RAW_OPTIONS
511        if (lsrr > 0) {
512                register u_char *optlist;
513
514                optlist = outp;
515                outp += optlen;
516
517                /* final hop */
518                gwlist[lsrr] = to->sin_addr.s_addr;
519
520                outip->ip_dst.s_addr = gwlist[0];
521
522                /* force 4 byte alignment */
523                optlist[0] = IPOPT_NOP;
524                /* loose source route option */
525                optlist[1] = IPOPT_LSRR;
526                i = lsrr * sizeof(gwlist[0]);
527                optlist[2] = i + 3;
528                /* Pointer to LSRR addresses */
529                optlist[3] = IPOPT_MINOFF;
530                memcpy(optlist + 4, gwlist + 1, i);
531        } else
532#endif
533                outip->ip_dst = to->sin_addr;
534
535        outip->ip_hl = (outp - (u_char *)outip) >> 2;
536        ident = (getpid() & 0xffff) | 0x8000;
537        if (useicmp) {
538                outip->ip_p = IPPROTO_ICMP;
539
540                outicmp = (struct icmp *)outp;
541                outicmp->icmp_type = ICMP_ECHO;
542                outicmp->icmp_id = htons(ident);
543
544                outdata = (struct outdata *)(outp + 8); /* XXX magic number */
545        } else {
546                outip->ip_p = IPPROTO_UDP;
547
548                outudp = (struct udphdr *)outp;
549                outudp->uh_sport = htons(ident);
550                outudp->uh_ulen =
551                    htons((u_short)(packlen - (sizeof(*outip) + optlen)));
552                outdata = (struct outdata *)(outudp + 1);
553        }
554
555        cp = "icmp";
556        if ((pe = getprotobyname(cp)) == NULL) {
557                Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
558                exit(1);
559        }
560        if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
561                Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
562                exit(1);
563        }
564        if (options & SO_DEBUG)
565                (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
566                    sizeof(on));
567        if (options & SO_DONTROUTE)
568                (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
569                    sizeof(on));
570
571#ifndef __hpux
572        sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
573#else
574        sndsock = socket(AF_INET, SOCK_RAW,
575            useicmp ? IPPROTO_ICMP : IPPROTO_UDP);
576#endif
577        if (sndsock < 0) {
578                Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
579                exit(1);
580        }
581
582        /* Revert to non-privileged user after opening sockets */
583        setuid(getuid());
584
585#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
586        if (lsrr > 0) {
587                u_char optlist[MAX_IPOPTLEN];
588
589                cp = "ip";
590                if ((pe = getprotobyname(cp)) == NULL) {
591                        Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
592                        exit(1);
593                }
594
595                /* final hop */
596                gwlist[lsrr] = to->sin_addr.s_addr;
597                ++lsrr;
598
599                /* force 4 byte alignment */
600                optlist[0] = IPOPT_NOP;
601                /* loose source route option */
602                optlist[1] = IPOPT_LSRR;
603                i = lsrr * sizeof(gwlist[0]);
604                optlist[2] = i + 3;
605                /* Pointer to LSRR addresses */
606                optlist[3] = IPOPT_MINOFF;
607                memcpy(optlist + 4, gwlist, i);
608
609                if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, optlist,
610                    i + sizeof(gwlist[0]))) < 0) {
611                        Fprintf(stderr, "%s: IP_OPTIONS: %s\n",
612                            prog, strerror(errno));
613                        exit(1);
614                    }
615        }
616#endif
617
618#ifdef SO_SNDBUF
619        if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
620            sizeof(packlen)) < 0) {
621                Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
622                exit(1);
623        }
624#endif
625#ifdef IP_HDRINCL
626        if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
627            sizeof(on)) < 0) {
628                Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
629                exit(1);
630        }
631#else
632#ifdef IP_TOS
633        if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
634            (char *)&tos, sizeof(tos)) < 0) {
635                Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
636                    prog, tos, strerror(errno));
637                exit(1);
638        }
639#endif
640#endif
641        if (options & SO_DEBUG)
642                (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
643                    sizeof(on));
644        if (options & SO_DONTROUTE)
645                (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
646                    sizeof(on));
647
648        /* Get the interface address list */
649        n = ifaddrlist(&al, errbuf, sizeof errbuf);
650        if (n < 0) {
651                Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
652                exit(1);
653        }
654        if (n == 0) {
655                Fprintf(stderr,
656                    "%s: Can't find any network interfaces\n", prog);
657                exit(1);
658        }
659
660        /* Look for a specific device */
661        if (device != NULL) {
662                for (i = n; i > 0; --i, ++al)
663                        if (strcmp(device, al->device) == 0)
664                                break;
665                if (i <= 0) {
666                        Fprintf(stderr, "%s: Can't find interface %s\n",
667                            prog, device);
668                        exit(1);
669                }
670        }
671
672        /* Determine our source address */
673        if (source == NULL) {
674                /*
675                 * If a device was specified, use the interface address.
676                 * Otherwise, use the first interface found.
677                 * Warn if there are more than one.
678                 */
679                setsin(from, al->addr);
680                if (n > 1 && device == NULL) {
681                        Fprintf(stderr,
682                    "%s: Warning: Multiple interfaces found; using %s @ %s\n",
683                            prog, inet_ntoa(from->sin_addr), al->device);
684                }
685        } else {
686                hi = gethostinfo(source);
687                source = hi->name;
688                hi->name = NULL;
689                if (device == NULL) {
690                        /*
691                         * Use the first interface found.
692                         * Warn if there are more than one.
693                         */
694                        setsin(from, hi->addrs[0]);
695                        if (hi->n > 1)
696                                Fprintf(stderr,
697                        "%s: Warning: %s has multiple addresses; using %s\n",
698                                    prog, source, inet_ntoa(from->sin_addr));
699                } else {
700                        /*
701                         * Make sure the source specified matches the
702                         * interface address.
703                         */
704                        for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
705                                if (*ap == al->addr)
706                                        break;
707                        if (i <= 0) {
708                                Fprintf(stderr,
709                                    "%s: %s is not on interface %s\n",
710                                    prog, source, device);
711                                exit(1);
712                        }
713                        setsin(from, *ap);
714                }
715                freehostinfo(hi);
716        }
717        outip->ip_src = from->sin_addr;
718#ifndef IP_HDRINCL
719        if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
720                Fprintf(stderr, "%s: bind: %s\n",
721                    prog, strerror(errno));
722                exit (1);
723        }
724#endif
725
726        Fprintf(stderr, "%s to %s (%s)",
727            prog, hostname, inet_ntoa(to->sin_addr));
728        if (source)
729                Fprintf(stderr, " from %s", source);
730        Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
731        (void)fflush(stderr);
732
733        for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
734                u_int32_t lastaddr = 0;
735                int got_there = 0;
736                int unreachable = 0;
737
738                Printf("%2d ", ttl);
739                for (probe = 0; probe < nprobes; ++probe) {
740                        register int cc;
741                        struct timeval t1, t2;
742                        struct timezone tz;
743                        register struct ip *ip;
744
745                        (void)gettimeofday(&t1, &tz);
746                        send_probe(++seq, ttl, &t1);
747                        while ((cc = wait_for_reply(s, from, &t1)) != 0) {
748                                (void)gettimeofday(&t2, &tz);
749                                i = packet_ok(packet, cc, from, seq);
750                                /* Skip short packet */
751                                if (i == 0)
752                                        continue;
753                                if (from->sin_addr.s_addr != lastaddr) {
754                                        print(packet, cc, from);
755                                        lastaddr = from->sin_addr.s_addr;
756                                }
757                                Printf("  %.3f ms", deltaT(&t1, &t2));
758                                if (i == -2) {
759#ifndef ARCHAIC
760                                        ip = (struct ip *)packet;
761                                        if (ip->ip_ttl <= 1)
762                                                Printf(" !");
763#endif
764                                        ++got_there;
765                                        break;
766                                }
767                                /* time exceeded in transit */
768                                if (i == -1)
769                                        break;
770                                code = i - 1;
771                                switch (code) {
772
773                                case ICMP_UNREACH_PORT:
774#ifndef ARCHAIC
775                                        ip = (struct ip *)packet;
776                                        if (ip->ip_ttl <= 1)
777                                                Printf(" !");
778#endif
779                                        ++got_there;
780                                        break;
781
782                                case ICMP_UNREACH_NET:
783                                        ++unreachable;
784                                        Printf(" !N");
785                                        break;
786
787                                case ICMP_UNREACH_HOST:
788                                        ++unreachable;
789                                        Printf(" !H");
790                                        break;
791
792                                case ICMP_UNREACH_PROTOCOL:
793                                        ++got_there;
794                                        Printf(" !P");
795                                        break;
796
797                                case ICMP_UNREACH_NEEDFRAG:
798                                        ++unreachable;
799                                        Printf(" !F");
800                                        break;
801
802                                case ICMP_UNREACH_SRCFAIL:
803                                        ++unreachable;
804                                        Printf(" !S");
805                                        break;
806
807/* rfc1716 */
808#ifndef ICMP_UNREACH_FILTER_PROHIB
809#define ICMP_UNREACH_FILTER_PROHIB      13      /* admin prohibited filter */
810#endif
811                                case ICMP_UNREACH_FILTER_PROHIB:
812                                        ++unreachable;
813                                        Printf(" !X");
814                                        break;
815
816                                default:
817                                        ++unreachable;
818                                        Printf(" !<%d>", code);
819                                        break;
820                                }
821                                break;
822                        }
823                        if (cc == 0)
824                                Printf(" *");
825                        (void)fflush(stdout);
826                }
827                putchar('\n');
828                if (got_there ||
829                    (unreachable > 0 && unreachable >= nprobes - 1))
830                        break;
831        }
832        exit(0);
833}
834
835int
836wait_for_reply(register int sock, register struct sockaddr_in *fromp,
837    register struct timeval *tp)
838{
839        fd_set fds;
840        struct timeval now, wait;
841        struct timezone tz;
842        register int cc = 0;
843        int fromlen = sizeof(*fromp);
844        int retval;
845
846        FD_ZERO(&fds);
847        FD_SET(sock, &fds);
848
849        wait.tv_sec = tp->tv_sec + waittime;
850        wait.tv_usec = tp->tv_usec;
851        (void)gettimeofday(&now, &tz);
852        tvsub(&wait, &now);
853
854        retval = select(sock + 1, &fds, NULL, NULL, &wait);
855        if (retval < 0)  {
856                /* If we continue, we probably just flood the remote host. */
857                Fprintf(stderr, "%s: select: %s\n", prog, strerror(errno));
858                exit(1);
859        }
860        if (retval > 0)  {
861                cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
862                            (struct sockaddr *)fromp, &fromlen);
863        }
864
865        return(cc);
866}
867
868void
869send_probe(register int seq, int ttl, register struct timeval *tp)
870{
871        register int cc;
872        register struct udpiphdr * ui;
873        struct ip tip;
874
875        outip->ip_ttl = ttl;
876#ifndef __hpux
877        outip->ip_id = htons(ident + seq);
878#endif
879
880        /*
881         * In most cases, the kernel will recalculate the ip checksum.
882         * But we must do it anyway so that the udp checksum comes out
883         * right.
884         */
885        if (docksum) {
886                outip->ip_sum =
887                    in_cksum((u_short *)outip, sizeof(*outip) + optlen);
888                if (outip->ip_sum == 0)
889                        outip->ip_sum = 0xffff;
890        }
891
892        /* Payload */
893        outdata->seq = seq;
894        outdata->ttl = ttl;
895        outdata->tv = *tp;
896
897        if (useicmp)
898                outicmp->icmp_seq = htons(seq);
899        else
900                outudp->uh_dport = htons(port + seq);
901
902        if (useicmp) {
903                /* Always compute ICMP checksum -- kernel does not compute
904                 * it for us.
905                 */
906                outicmp->icmp_cksum = 0;
907                outicmp->icmp_cksum = in_cksum((u_short *)outicmp,
908                    packlen - (sizeof(*outip) + optlen));
909                if (outicmp->icmp_cksum == 0)
910                        outicmp->icmp_cksum = 0xffff;
911        } else {
912                /* (We can only do the checksum if we know our ip address) */
913                if (docksum) {
914                        /* Checksum (must save and restore ip header) */
915                        tip = *outip;
916                        ui = (struct udpiphdr *)outip;
917                        ui->ui_next = 0;
918                        ui->ui_prev = 0;
919                        ui->ui_x1 = 0;
920                        ui->ui_len = outudp->uh_ulen;
921                        outudp->uh_sum = 0;
922                        outudp->uh_sum = in_cksum((u_short *)ui, packlen);
923                        if (outudp->uh_sum == 0)
924                                outudp->uh_sum = 0xffff;
925                        *outip = tip;
926                }
927        }
928
929        /* XXX undocumented debugging hack */
930        if (verbose > 1) {
931                register const u_short *sp;
932                register int nshorts, i;
933
934                sp = (u_short *)outip;
935                nshorts = (u_int)packlen / sizeof(u_short);
936                i = 0;
937                Printf("[ %d bytes", packlen);
938                while (--nshorts >= 0) {
939                        if ((i++ % 8) == 0)
940                                Printf("\n\t");
941                        Printf(" %04x", ntohs(*sp++));
942                }
943                if (packlen & 1) {
944                        if ((i % 8) == 0)
945                                Printf("\n\t");
946                        Printf(" %02x", *(u_char *)sp);
947                }
948                Printf("]\n");
949        }
950
951#if !defined(IP_HDRINCL) && defined(IP_TTL)
952        if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
953            (char *)&ttl, sizeof(ttl)) < 0) {
954                Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
955                    prog, ttl, strerror(errno));
956                exit(1);
957        }
958#endif
959
960#ifdef __hpux
961        cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp,
962            packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto));
963        if (cc > 0)
964                cc += sizeof(*outip) + optlen;
965#else
966        cc = sendto(sndsock, (char *)outip,
967            packlen, 0, &whereto, sizeof(whereto));
968#endif
969        if (cc < 0 || cc != packlen)  {
970                if (cc < 0)
971                        Fprintf(stderr, "%s: sendto: %s\n",
972                            prog, strerror(errno));
973                Printf("%s: wrote %s %d chars, ret=%d\n",
974                    prog, hostname, packlen, cc);
975                (void)fflush(stdout);
976        }
977}
978
979double
980deltaT(struct timeval *t1p, struct timeval *t2p)
981{
982        register double dt;
983
984        dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
985             (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
986        return (dt);
987}
988
989/*
990 * Convert an ICMP "type" field to a printable string.
991 */
992char *
993pr_type(register u_char t)
994{
995        static char *ttab[] = {
996        "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
997        "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
998        "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
999        "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
1000        "Info Reply"
1001        };
1002
1003        if (t > 16)
1004                return("OUT-OF-RANGE");
1005
1006        return(ttab[t]);
1007}
1008
1009int
1010packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
1011    register int seq)
1012{
1013        register struct icmp *icp;
1014        register u_char type, code;
1015        register int hlen;
1016#ifndef ARCHAIC
1017        register struct ip *ip;
1018
1019        ip = (struct ip *) buf;
1020        hlen = ip->ip_hl << 2;
1021        if (cc < hlen + ICMP_MINLEN) {
1022                if (verbose)
1023                        Printf("packet too short (%d bytes) from %s\n", cc,
1024                                inet_ntoa(from->sin_addr));
1025                return (0);
1026        }
1027        cc -= hlen;
1028        icp = (struct icmp *)(buf + hlen);
1029#else
1030        icp = (struct icmp *)buf;
1031#endif
1032        type = icp->icmp_type;
1033        code = icp->icmp_code;
1034        if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
1035            type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
1036                register struct ip *hip;
1037                register struct udphdr *up;
1038                register struct icmp *hicmp;
1039
1040                hip = &icp->icmp_ip;
1041                hlen = hip->ip_hl << 2;
1042                if (useicmp) {
1043                        /* XXX */
1044                        if (type == ICMP_ECHOREPLY &&
1045                            icp->icmp_id == htons(ident) &&
1046                            icp->icmp_seq == htons(seq))
1047                                return (-2);
1048
1049                        hicmp = (struct icmp *)((u_char *)hip + hlen);
1050                        /* XXX 8 is a magic number */
1051                        if (hlen + 8 <= cc &&
1052                            hip->ip_p == IPPROTO_ICMP &&
1053                            hicmp->icmp_id == htons(ident) &&
1054                            hicmp->icmp_seq == htons(seq))
1055                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
1056                } else {
1057                        up = (struct udphdr *)((u_char *)hip + hlen);
1058                        /* XXX 8 is a magic number */
1059                        if (hlen + 12 <= cc &&
1060                            hip->ip_p == IPPROTO_UDP &&
1061                            up->uh_sport == htons(ident) &&
1062                            up->uh_dport == htons(port + seq))
1063                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
1064                }
1065        }
1066#ifndef ARCHAIC
1067        if (verbose) {
1068                register int i;
1069                u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
1070
1071                Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
1072                Printf("%s: icmp type %d (%s) code %d\n",
1073                    inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
1074                for (i = 4; i < cc ; i += sizeof(*lp))
1075                        Printf("%2d: x%8.8x\n", i, *lp++);
1076        }
1077#endif
1078        return(0);
1079}
1080
1081
1082void
1083print(register u_char *buf, register int cc, register struct sockaddr_in *from)
1084{
1085        register struct ip *ip;
1086        register int hlen;
1087
1088        ip = (struct ip *) buf;
1089        hlen = ip->ip_hl << 2;
1090        cc -= hlen;
1091
1092        if (nflag)
1093                Printf(" %s", inet_ntoa(from->sin_addr));
1094        else
1095                Printf(" %s (%s)", inetname(from->sin_addr),
1096                    inet_ntoa(from->sin_addr));
1097
1098        if (verbose)
1099                Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
1100}
1101
1102/*
1103 * Checksum routine for Internet Protocol family headers (C Version)
1104 */
1105u_short
1106in_cksum(register u_short *addr, register int len)
1107{
1108        register int nleft = len;
1109        register u_short *w = addr;
1110        register u_short answer;
1111        register int sum = 0;
1112
1113        /*
1114         *  Our algorithm is simple, using a 32 bit accumulator (sum),
1115         *  we add sequential 16 bit words to it, and at the end, fold
1116         *  back all the carry bits from the top 16 bits into the lower
1117         *  16 bits.
1118         */
1119        while (nleft > 1)  {
1120                sum += *w++;
1121                nleft -= 2;
1122        }
1123
1124        /* mop up an odd byte, if necessary */
1125        if (nleft == 1)
1126                sum += *(u_char *)w;
1127
1128        /*
1129         * add back carry outs from top 16 bits to low 16 bits
1130         */
1131        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
1132        sum += (sum >> 16);                     /* add carry */
1133        answer = ~sum;                          /* truncate to 16 bits */
1134        return (answer);
1135}
1136
1137/*
1138 * Subtract 2 timeval structs:  out = out - in.
1139 * Out is assumed to be >= in.
1140 */
1141void
1142tvsub(register struct timeval *out, register struct timeval *in)
1143{
1144
1145        if ((out->tv_usec -= in->tv_usec) < 0)   {
1146                --out->tv_sec;
1147                out->tv_usec += 1000000;
1148        }
1149        out->tv_sec -= in->tv_sec;
1150}
1151
1152/*
1153 * Construct an Internet address representation.
1154 * If the nflag has been supplied, give
1155 * numeric value, otherwise try for symbolic name.
1156 */
1157char *
1158inetname(struct in_addr in)
1159{
1160        register char *cp;
1161        register struct hostent *hp;
1162        static int first = 1;
1163        static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
1164
1165        if (first && !nflag) {
1166                first = 0;
1167                if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
1168                    (cp = strchr(domain, '.')) != NULL) {
1169                        (void)strncpy(domain, cp + 1, sizeof(domain) - 1);
1170                        domain[sizeof(domain) - 1] = '\0';
1171                } else
1172                        domain[0] = '\0';
1173        }
1174        if (!nflag && in.s_addr != INADDR_ANY) {
1175                hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
1176                if (hp != NULL) {
1177                        if ((cp = strchr(hp->h_name, '.')) != NULL &&
1178                            strcmp(cp + 1, domain) == 0)
1179                                *cp = '\0';
1180                        (void)strncpy(line, hp->h_name, sizeof(line) - 1);
1181                        line[sizeof(line) - 1] = '\0';
1182                        return (line);
1183                }
1184        }
1185        return (inet_ntoa(in));
1186}
1187
1188struct hostinfo *
1189gethostinfo(register char *hostname)
1190{
1191        register int n;
1192        register struct hostent *hp;
1193        register struct hostinfo *hi;
1194        register char **p;
1195        register u_int32_t addr, *ap;
1196
1197        hi = calloc(1, sizeof(*hi));
1198        if (hi == NULL) {
1199                Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1200                exit(1);
1201        }
1202        addr = inet_addr(hostname);
1203        if ((int32_t)addr != -1) {
1204                hi->name = strdup(hostname);
1205                hi->n = 1;
1206                hi->addrs = calloc(1, sizeof(hi->addrs[0]));
1207                if (hi->addrs == NULL) {
1208                        Fprintf(stderr, "%s: calloc %s\n",
1209                            prog, strerror(errno));
1210                        exit(1);
1211                }
1212                hi->addrs[0] = addr;
1213                return (hi);
1214        }
1215
1216        hp = gethostbyname(hostname);
1217        if (hp == NULL) {
1218                Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
1219                exit(1);
1220        }
1221        if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
1222                Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
1223                exit(1);
1224        }
1225        hi->name = strdup(hp->h_name);
1226        for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
1227                continue;
1228        hi->n = n;
1229        hi->addrs = calloc(n, sizeof(hi->addrs[0]));
1230        if (hi->addrs == NULL) {
1231                Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1232                exit(1);
1233        }
1234        for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
1235                memcpy(ap, *p, sizeof(*ap));
1236        return (hi);
1237}
1238
1239void
1240freehostinfo(register struct hostinfo *hi)
1241{
1242        if (hi->name != NULL) {
1243                free(hi->name);
1244                hi->name = NULL;
1245        }
1246        free((char *)hi->addrs);
1247        free((char *)hi);
1248}
1249
1250void
1251getaddr(register u_int32_t *ap, register char *hostname)
1252{
1253        register struct hostinfo *hi;
1254
1255        hi = gethostinfo(hostname);
1256        *ap = hi->addrs[0];
1257        freehostinfo(hi);
1258}
1259
1260void
1261setsin(register struct sockaddr_in *sin, register u_int32_t addr)
1262{
1263
1264        memset(sin, 0, sizeof(*sin));
1265#ifdef HAVE_SOCKADDR_SA_LEN
1266        sin->sin_len = sizeof(*sin);
1267#endif
1268        sin->sin_family = AF_INET;
1269        sin->sin_addr.s_addr = addr;
1270}
1271
1272/* String to value with optional min and max. Handles decimal and hex. */
1273int
1274str2val(register const char *str, register const char *what,
1275    register int mi, register int ma)
1276{
1277        register const char *cp;
1278        register int val;
1279        char *ep;
1280
1281        if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
1282                cp = str + 2;
1283                val = (int)strtol(cp, &ep, 16);
1284        } else
1285                val = (int)strtol(str, &ep, 10);
1286        if (*ep != '\0') {
1287                Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
1288                    prog, str, what);
1289                exit(1);
1290        }
1291        if (val < mi && mi >= 0) {
1292                if (mi == 0)
1293                        Fprintf(stderr, "%s: %s must be >= %d\n",
1294                            prog, what, mi);
1295                else
1296                        Fprintf(stderr, "%s: %s must be > %d\n",
1297                            prog, what, mi - 1);
1298                exit(1);
1299        }
1300        if (val > ma && ma >= 0) {
1301                Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
1302                exit(1);
1303        }
1304        return (val);
1305}
1306
1307__dead void
1308usage(void)
1309{
1310        extern char version[];
1311
1312        Fprintf(stderr, "Version %s\n", version);
1313        Fprintf(stderr, "Usage: %s [-dFInrvx] [-g gateway] [-i iface] \
1314[-f first_ttl] [-m max_ttl]\n\t[ -p port] [-q nqueries] [-s src_addr] [-t tos] \
1315[-w waittime]\n\thost [packetlen]\n",
1316            prog);
1317        exit(1);
1318}
Note: See TracBrowser for help on using the repository browser.