enum entry for unarchive to be used by tar
[oweals/busybox.git] / traceroute.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Van Jacobson.
7  *
8  * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 /*
39  * traceroute host  - trace the route ip packets follow going to "host".
40  * Notes
41  * -----
42  * This program must be run by root or be setuid.  (I suggest that
43  * you *don't* make it setuid -- casual use could result in a lot
44  * of unnecessary traffic on our poor, congested nets.)
45  *
46  * I stole the idea for this program from Steve Deering.  Since
47  * the first release, I've learned that had I attended the right
48  * IETF working group meetings, I also could have stolen it from Guy
49  * Almes or Matt Mathis.  I don't know (or care) who came up with
50  * the idea first.  I envy the originators' perspicacity and I'm
51  * glad they didn't keep the idea a secret.
52  *
53  * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
54  * enhancements to the original distribution.
55  *
56  * I've hacked up a round-trip-route version of this that works by
57  * sending a loose-source-routed udp datagram through the destination
58  * back to yourself.  Unfortunately, SO many gateways botch source
59  * routing, the thing is almost worthless.  Maybe one day...
60  *
61  *  -- Van Jacobson (van@helios.ee.lbl.gov)
62  *     Tue Dec 20 03:50:13 PST 1988
63  */
64
65 #undef BB_FEATURE_TRACEROUTE_VERBOSE
66 //#define BB_FEATURE_TRACEROUTE_VERBOSE
67 #undef BB_FEATURE_TRACEROUTE_SO_DEBUG   /* not in documentation man */
68
69 #include <stdio.h>
70 #include <errno.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <sys/time.h>
75 #include <netdb.h>
76 #include <endian.h>
77 #include <arpa/inet.h>
78 #include <netinet/udp.h>
79 #include <netinet/ip.h>
80 #include <netinet/ip_icmp.h>
81
82
83 #define MAXPACKET       65535   /* max ip packet size */
84 #ifndef MAXHOSTNAMELEN
85 #define MAXHOSTNAMELEN  64
86 #endif
87
88 /*
89  * format of a (udp) probe packet.
90  */
91 struct opacket {
92         struct ip ip;
93         struct udphdr udp;
94         u_char seq;             /* sequence number of this packet */
95         u_char ttl;             /* ttl packet left with */
96         struct timeval tv;      /* time packet left */
97 };
98
99 /*
100  * Definitions for internet protocol version 4.
101  * Per RFC 791, September 1981.
102  */
103 #define IPVERSION       4
104
105
106 #include "busybox.h"
107
108 static u_char  packet[512];            /* last inbound (icmp) packet */
109 static struct opacket  *outpacket;     /* last output (udp) packet */
110
111 static int s;                          /* receive (icmp) socket file descriptor */
112 static int sndsock;                    /* send (udp) socket file descriptor */
113
114 static struct sockaddr whereto;        /* Who to try to reach */
115 static int datalen;                    /* How much data */
116
117 static char *hostname;
118
119 static int max_ttl = 30;
120 static u_short ident;
121 static u_short port = 32768+666;       /* start udp dest port # for probe packets */
122
123 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
124 static int verbose;
125 #endif
126 static int waittime = 5;               /* time to wait for response (in seconds) */
127 static int nflag;                      /* print addresses numerically */
128
129 /*
130  * Construct an Internet address representation.
131  * If the nflag has been supplied, give
132  * numeric value, otherwise try for symbolic name.
133  */
134 static inline char *
135 inetname(struct in_addr in)
136 {
137         char *cp;
138         static char line[50];
139         struct hostent *hp;
140         static char domain[MAXHOSTNAMELEN + 1];
141         static int first = 1;
142
143         if (first && !nflag) {
144                 first = 0;
145                 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
146                     (cp = index(domain, '.')))
147                         (void) strcpy(domain, cp + 1);
148                 else
149                         domain[0] = 0;
150         }
151         cp = 0;
152         if (!nflag && in.s_addr != INADDR_ANY) {
153                 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
154                 if (hp) {
155                         if ((cp = index(hp->h_name, '.')) &&
156                             !strcmp(cp + 1, domain))
157                                 *cp = 0;
158                         cp = (char *)hp->h_name;
159                 }
160         }
161         if (cp)
162                 (void) strcpy(line, cp);
163         else {
164                 in.s_addr = ntohl(in.s_addr);
165                 strcpy(line, inet_ntoa(in));
166         }
167         return (line);
168 }
169
170 static inline void
171 print(u_char *buf, int cc, struct sockaddr_in *from)
172 {
173         struct ip *ip;
174         int hlen;
175
176         ip = (struct ip *) buf;
177         hlen = ip->ip_hl << 2;
178         cc -= hlen;
179
180         if (nflag)
181                 printf(" %s", inet_ntoa(from->sin_addr));
182         else
183                 printf(" %s (%s)", inetname(from->sin_addr),
184                        inet_ntoa(from->sin_addr));
185
186 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
187         if (verbose)
188                 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
189 #endif
190 }
191
192 static inline double
193 deltaT(struct timeval *t1p, struct timeval *t2p)
194 {
195         double dt;
196
197         dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
198              (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
199         return (dt);
200 }
201
202 static inline int
203 wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
204 {
205         fd_set fds;
206         static struct timeval wait;
207         int cc = 0;
208         int fromlen = sizeof (*from);
209
210         FD_ZERO(&fds);
211         FD_SET(sock, &fds);
212         if (reset_timer) {
213                 /*
214                  * traceroute could hang if someone else has a ping
215                  * running and our ICMP reply gets dropped but we don't
216                  * realize it because we keep waking up to handle those
217                  * other ICMP packets that keep coming in.  To fix this,
218                  * "reset_timer" will only be true if the last packet that
219                  * came in was for us or if this is the first time we're
220                  * waiting for a reply since sending out a probe.  Note
221                  * that this takes advantage of the select() feature on
222                  * Linux where the remaining timeout is written to the
223                  * struct timeval area.
224                  */
225                 wait.tv_sec = waittime;
226                 wait.tv_usec = 0;
227         }
228
229         if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
230                 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
231                             (struct sockaddr *)from, &fromlen);
232
233         return(cc);
234 }
235
236 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
237 /*
238  * Convert an ICMP "type" field to a printable string.
239  */
240 static inline const char *
241 pr_type(t)
242         u_char t;
243 {
244         static const char * const ttab[] = {
245         "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
246         "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
247         "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
248         "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
249         "Info Reply"
250         };
251
252         if(t > 16)
253                 return("OUT-OF-RANGE");
254
255         return(ttab[t]);
256 }
257 #endif
258
259 static inline int
260 packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
261 {
262         struct icmp *icp;
263         u_char type, code;
264         int hlen;
265         struct ip *ip;
266
267         ip = (struct ip *) buf;
268         hlen = ip->ip_hl << 2;
269         if (cc < hlen + ICMP_MINLEN) {
270 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
271                 if (verbose)
272                         printf("packet too short (%d bytes) from %s\n", cc,
273                                 inet_ntoa(from->sin_addr));
274 #endif
275                 return (0);
276         }
277         cc -= hlen;
278         icp = (struct icmp *)(buf + hlen);
279         type = icp->icmp_type; code = icp->icmp_code;
280         if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
281             type == ICMP_UNREACH) {
282                 struct ip *hip;
283                 struct udphdr *up;
284
285                 hip = &icp->icmp_ip;
286                 hlen = hip->ip_hl << 2;
287                 up = (struct udphdr *)((u_char *)hip + hlen);
288                 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
289                     up->source == htons(ident) &&
290                     up->dest == htons(port+seq))
291                         return (type == ICMP_TIMXCEED? -1 : code+1);
292         }
293 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
294         if (verbose) {
295                 int i;
296                 u_long *lp = (u_long *)&icp->icmp_ip;
297
298                 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
299                         cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
300                         type, pr_type(type), icp->icmp_code);
301                 for (i = 4; i < cc ; i += sizeof(long))
302                         printf("%2d: x%8.8lx\n", i, *lp++);
303         }
304 #endif
305         return(0);
306 }
307
308 static void             /* not inline */
309 send_probe(int seq, int ttl)
310 {
311         struct opacket *op = outpacket;
312         struct ip *ip = &op->ip;
313         struct udphdr *up = &op->udp;
314         int i;
315         struct timezone tz;
316
317         ip->ip_off = 0;
318         ip->ip_hl = sizeof(*ip) >> 2;
319         ip->ip_p = IPPROTO_UDP;
320         ip->ip_len = datalen;
321         ip->ip_ttl = ttl;
322         ip->ip_v = IPVERSION;
323         ip->ip_id = htons(ident+seq);
324
325         up->source = htons(ident);
326         up->dest = htons(port+seq);
327         up->len = htons((u_short)(datalen - sizeof(struct ip)));
328         up->check = 0;
329
330         op->seq = seq;
331         op->ttl = ttl;
332         (void) gettimeofday(&op->tv, &tz);
333
334         i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
335                    sizeof(struct sockaddr));
336         if (i < 0 || i != datalen)  {
337                 if (i<0)
338                         perror("sendto");
339                 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
340                         datalen, i);
341                 (void) fflush(stdout);
342         }
343 }
344
345
346 int
347 #ifndef BB_TRACEROUTE
348 main(argc, argv)
349 #else
350 traceroute_main(argc, argv)
351 #endif
352         int argc;
353         char *argv[];
354 {
355         extern char *optarg;
356         extern int optind;
357         struct hostent *hp;
358         struct sockaddr_in from, *to;
359         int ch, i, on, probe, seq, tos, ttl;
360
361         int options = 0;                /* socket options */
362         char *source = 0;
363         int nprobes = 3;
364
365         on = 1;
366         seq = tos = 0;
367         to = (struct sockaddr_in *)&whereto;
368         while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
369                 switch(ch) {
370                 case 'd':
371 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
372                         options |= SO_DEBUG;
373 #endif
374                         break;
375                 case 'm':
376                         max_ttl = atoi(optarg);
377                         if (max_ttl <= 1)
378                                 error_msg_and_die("max ttl must be >1.");
379                         break;
380                 case 'n':
381                         nflag++;
382                         break;
383                 case 'p':
384                         port = atoi(optarg);
385                         if (port < 1)
386                                 error_msg_and_die("port must be >0.");
387                         break;
388                 case 'q':
389                         nprobes = atoi(optarg);
390                         if (nprobes < 1)
391                                 error_msg_and_die("nprobes must be >0.");
392                         break;
393                 case 'r':
394                         options |= SO_DONTROUTE;
395                         break;
396                 case 's':
397                         /*
398                          * set the ip source address of the outbound
399                          * probe (e.g., on a multi-homed host).
400                          */
401                         source = optarg;
402                         break;
403                 case 't':
404                         tos = atoi(optarg);
405                         if (tos < 0 || tos > 255)
406                                 error_msg_and_die("tos must be 0 to 255.");
407                         break;
408                 case 'v':
409 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
410                         verbose++;
411 #endif
412                         break;
413                 case 'w':
414                         waittime = atoi(optarg);
415                         if (waittime <= 1)
416                                 error_msg_and_die("wait must be >1 sec.");
417                         break;
418                 default:
419                         show_usage();
420                 }
421         argc -= optind;
422         argv += optind;
423
424         if (argc < 1)
425                 show_usage();
426
427         setlinebuf (stdout);
428
429         memset(&whereto, 0, sizeof(struct sockaddr));
430         hp = xgethostbyname(*argv);
431                         to->sin_family = hp->h_addrtype;
432         memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
433                         hostname = (char *)hp->h_name;
434         if (*++argv)
435                 datalen = atoi(*argv);
436         if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
437                 error_msg_and_die("packet size must be 0 <= s < %d.",
438                     MAXPACKET - sizeof(struct opacket));
439         datalen += sizeof(struct opacket);
440         outpacket = (struct opacket *)xmalloc((unsigned)datalen);
441         memset(outpacket, 0, datalen);
442         outpacket->ip.ip_dst = to->sin_addr;
443         outpacket->ip.ip_tos = tos;
444         outpacket->ip.ip_v = IPVERSION;
445         outpacket->ip.ip_id = 0;
446
447         ident = (getpid() & 0xffff) | 0x8000;
448
449         if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
450                 perror_msg_and_die(can_not_create_raw_socket);
451
452         s = create_icmp_socket();
453
454 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
455         if (options & SO_DEBUG)
456                 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
457                                   (char *)&on, sizeof(on));
458 #endif
459         if (options & SO_DONTROUTE)
460                 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
461                                   (char *)&on, sizeof(on));
462 #ifdef SO_SNDBUF
463         if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
464                        sizeof(datalen)) < 0)
465                 perror_msg_and_die("SO_SNDBUF");
466 #endif SO_SNDBUF
467 #ifdef IP_HDRINCL
468         if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
469                        sizeof(on)) < 0)
470                 perror_msg_and_die("IP_HDRINCL");
471 #endif IP_HDRINCL
472 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
473         if (options & SO_DEBUG)
474                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
475                                   (char *)&on, sizeof(on));
476 #endif
477         if (options & SO_DONTROUTE)
478                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
479                                   (char *)&on, sizeof(on));
480
481         if (source) {
482                 memset(&from, 0, sizeof(struct sockaddr));
483                 from.sin_family = AF_INET;
484                 from.sin_addr.s_addr = inet_addr(source);
485                 if (from.sin_addr.s_addr == -1)
486                         error_msg_and_die("unknown host %s", source);
487                 outpacket->ip.ip_src = from.sin_addr;
488 #ifndef IP_HDRINCL
489                 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
490                         perror_msg_and_die("bind");
491 #endif IP_HDRINCL
492         }
493
494         fprintf(stderr, "traceroute to %s (%s)", hostname,
495                 inet_ntoa(to->sin_addr));
496         if (source)
497                 fprintf(stderr, " from %s", source);
498         fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
499
500         for (ttl = 1; ttl <= max_ttl; ++ttl) {
501                 u_long lastaddr = 0;
502                 int got_there = 0;
503                 int unreachable = 0;
504
505                 printf("%2d ", ttl);
506                 for (probe = 0; probe < nprobes; ++probe) {
507                         int cc, reset_timer;
508                         struct timeval t1, t2;
509                         struct timezone tz;
510                         struct ip *ip;
511
512                         (void) gettimeofday(&t1, &tz);
513                         send_probe(++seq, ttl);
514                         reset_timer = 1;
515                         while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
516                                 (void) gettimeofday(&t2, &tz);
517                                 if ((i = packet_ok(packet, cc, &from, seq))) {
518                                         reset_timer = 1;
519                                         if (from.sin_addr.s_addr != lastaddr) {
520                                                 print(packet, cc, &from);
521                                                 lastaddr = from.sin_addr.s_addr;
522                                         }
523                                         printf("  %g ms", deltaT(&t1, &t2));
524                                         switch(i - 1) {
525                                         case ICMP_UNREACH_PORT:
526                                                 ip = (struct ip *)packet;
527                                                 if (ip->ip_ttl <= 1)
528                                                         printf(" !");
529                                                 ++got_there;
530                                                 break;
531                                         case ICMP_UNREACH_NET:
532                                                 ++unreachable;
533                                                 printf(" !N");
534                                                 break;
535                                         case ICMP_UNREACH_HOST:
536                                                 ++unreachable;
537                                                 printf(" !H");
538                                                 break;
539                                         case ICMP_UNREACH_PROTOCOL:
540                                                 ++got_there;
541                                                 printf(" !P");
542                                                 break;
543                                         case ICMP_UNREACH_NEEDFRAG:
544                                                 ++unreachable;
545                                                 printf(" !F");
546                                                 break;
547                                         case ICMP_UNREACH_SRCFAIL:
548                                                 ++unreachable;
549                                                 printf(" !S");
550                                                 break;
551                                         }
552                                         break;
553                                 } else
554                                         reset_timer = 0;
555                         }
556                         if (cc == 0)
557                                 printf(" *");
558                         (void) fflush(stdout);
559                 }
560                 putchar('\n');
561                 if (got_there || unreachable >= nprobes-1)
562                         exit(0);
563         }
564
565         return 0;
566 }