Teach libc5 what a sighandler_t is
[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 <sys/types.h>
76 #include <sys/socket.h>
77 #include <netdb.h>
78 #include <endian.h>
79 #include <arpa/inet.h>
80 #include <netinet/udp.h>
81 #include <netinet/ip.h>
82 #include <netinet/ip_icmp.h>
83
84
85
86 #define MAXPACKET       65535   /* max ip packet size */
87 #ifndef MAXHOSTNAMELEN
88 #define MAXHOSTNAMELEN  64
89 #endif
90
91 /*
92  * format of a (udp) probe packet.
93  */
94 struct opacket {
95         struct ip ip;
96         struct udphdr udp;
97         u_char seq;             /* sequence number of this packet */
98         u_char ttl;             /* ttl packet left with */
99         struct timeval tv;      /* time packet left */
100 };
101
102 /*
103  * Definitions for internet protocol version 4.
104  * Per RFC 791, September 1981.
105  */
106 #define IPVERSION       4
107
108
109 #include "busybox.h"
110
111 static u_char  packet[512];            /* last inbound (icmp) packet */
112 static struct opacket  *outpacket;     /* last output (udp) packet */
113
114 static int s;                          /* receive (icmp) socket file descriptor */
115 static int sndsock;                    /* send (udp) socket file descriptor */
116
117 static struct sockaddr whereto;        /* Who to try to reach */
118 static int datalen;                    /* How much data */
119
120 static char *hostname;
121
122 static int max_ttl = 30;
123 static u_short ident;
124 static u_short port = 32768+666;       /* start udp dest port # for probe packets */
125
126 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
127 static int verbose;
128 #endif
129 static int waittime = 5;               /* time to wait for response (in seconds) */
130 static int nflag;                      /* print addresses numerically */
131
132 /*
133  * Construct an Internet address representation.
134  * If the nflag has been supplied, give
135  * numeric value, otherwise try for symbolic name.
136  */
137 static inline void
138 inetname(struct sockaddr_in *from)
139 {
140         char *cp;
141         struct hostent *hp;
142         static char domain[MAXHOSTNAMELEN + 1];
143         static int first = 1;
144         const char *ina;
145
146         if (first && !nflag) {
147                 first = 0;
148                 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
149                     (cp = strchr(domain, '.')))
150                         (void) strcpy(domain, cp + 1);
151                 else
152                         domain[0] = 0;
153         }
154         cp = 0;
155         if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
156                 hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (from->sin_addr), AF_INET);
157                 if (hp) {
158                         if ((cp = strchr(hp->h_name, '.')) &&
159                             !strcmp(cp + 1, domain))
160                                 *cp = 0;
161                         cp = (char *)hp->h_name;
162                 }
163         }
164         ina = inet_ntoa(from->sin_addr);
165         if (nflag)
166                 printf(" %s", ina);
167         else
168                 printf(" %s (%s)", (cp ? cp : ina), ina);
169 }
170
171 static inline void
172 print(u_char *buf, int cc, struct sockaddr_in *from)
173 {
174         struct ip *ip;
175         int hlen;
176
177         ip = (struct ip *) buf;
178         hlen = ip->ip_hl << 2;
179         cc -= hlen;
180
181         inetname(from);
182 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
183         if (verbose)
184                 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
185 #endif
186 }
187
188 static inline double
189 deltaT(struct timeval *t1p, struct timeval *t2p)
190 {
191         double dt;
192
193         dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
194              (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
195         return (dt);
196 }
197
198 static inline int
199 wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
200 {
201         fd_set fds;
202         static struct timeval wait;
203         int cc = 0;
204         int fromlen = sizeof (*from);
205
206         FD_ZERO(&fds);
207         FD_SET(sock, &fds);
208         if (reset_timer) {
209                 /*
210                  * traceroute could hang if someone else has a ping
211                  * running and our ICMP reply gets dropped but we don't
212                  * realize it because we keep waking up to handle those
213                  * other ICMP packets that keep coming in.  To fix this,
214                  * "reset_timer" will only be true if the last packet that
215                  * came in was for us or if this is the first time we're
216                  * waiting for a reply since sending out a probe.  Note
217                  * that this takes advantage of the select() feature on
218                  * Linux where the remaining timeout is written to the
219                  * struct timeval area.
220                  */
221                 wait.tv_sec = waittime;
222                 wait.tv_usec = 0;
223         }
224
225         if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
226                 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
227                             (struct sockaddr *)from, &fromlen);
228
229         return(cc);
230 }
231
232 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
233 /*
234  * Convert an ICMP "type" field to a printable string.
235  */
236 static inline const char *
237 pr_type(t)
238         u_char t;
239 {
240         static const char * const ttab[] = {
241         "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
242         "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
243         "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
244         "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
245         "Info Reply"
246         };
247
248         if(t > 16)
249                 return("OUT-OF-RANGE");
250
251         return(ttab[t]);
252 }
253 #endif
254
255 static inline int
256 packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
257 {
258         struct icmp *icp;
259         u_char type, code;
260         int hlen;
261         struct ip *ip;
262
263         ip = (struct ip *) buf;
264         hlen = ip->ip_hl << 2;
265         if (cc < hlen + ICMP_MINLEN) {
266 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
267                 if (verbose)
268                         printf("packet too short (%d bytes) from %s\n", cc,
269                                 inet_ntoa(from->sin_addr));
270 #endif
271                 return (0);
272         }
273         cc -= hlen;
274         icp = (struct icmp *)(buf + hlen);
275         type = icp->icmp_type; code = icp->icmp_code;
276         if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
277             type == ICMP_UNREACH) {
278                 struct ip *hip;
279                 struct udphdr *up;
280
281                 hip = &icp->icmp_ip;
282                 hlen = hip->ip_hl << 2;
283                 up = (struct udphdr *)((u_char *)hip + hlen);
284                 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
285                     up->source == htons(ident) &&
286                     up->dest == htons(port+seq))
287                         return (type == ICMP_TIMXCEED? -1 : code+1);
288         }
289 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
290         if (verbose) {
291                 int i;
292                 u_long *lp = (u_long *)&icp->icmp_ip;
293
294                 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
295                         cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
296                         type, pr_type(type), icp->icmp_code);
297                 for (i = 4; i < cc ; i += sizeof(long))
298                         printf("%2d: x%8.8lx\n", i, *lp++);
299         }
300 #endif
301         return(0);
302 }
303
304 static void             /* not inline */
305 send_probe(int seq, int ttl)
306 {
307         struct opacket *op = outpacket;
308         struct ip *ip = &op->ip;
309         struct udphdr *up = &op->udp;
310         int i;
311         struct timezone tz;
312
313         ip->ip_off = 0;
314         ip->ip_hl = sizeof(*ip) >> 2;
315         ip->ip_p = IPPROTO_UDP;
316         ip->ip_len = datalen;
317         ip->ip_ttl = ttl;
318         ip->ip_v = IPVERSION;
319         ip->ip_id = htons(ident+seq);
320
321         up->source = htons(ident);
322         up->dest = htons(port+seq);
323         up->len = htons((u_short)(datalen - sizeof(struct ip)));
324         up->check = 0;
325
326         op->seq = seq;
327         op->ttl = ttl;
328         (void) gettimeofday(&op->tv, &tz);
329
330         i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
331                    sizeof(struct sockaddr));
332         if (i < 0 || i != datalen)  {
333                 if (i<0)
334                         perror("sendto");
335                 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
336                         datalen, i);
337                 (void) fflush(stdout);
338         }
339 }
340
341
342 int
343 #ifndef BB_TRACEROUTE
344 main(argc, argv)
345 #else
346 traceroute_main(argc, argv)
347 #endif
348         int argc;
349         char *argv[];
350 {
351         extern char *optarg;
352         extern int optind;
353         struct hostent *hp;
354         struct sockaddr_in from, *to;
355         int ch, i, on, probe, seq, tos, ttl;
356
357         int options = 0;                /* socket options */
358         char *source = 0;
359         int nprobes = 3;
360
361         on = 1;
362         seq = tos = 0;
363         to = (struct sockaddr_in *)&whereto;
364         while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
365                 switch(ch) {
366                 case 'd':
367 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
368                         options |= SO_DEBUG;
369 #endif
370                         break;
371                 case 'm':
372                         max_ttl = atoi(optarg);
373                         if (max_ttl <= 1)
374                                 error_msg_and_die("max ttl must be >1.");
375                         break;
376                 case 'n':
377                         nflag++;
378                         break;
379                 case 'p':
380                         port = atoi(optarg);
381                         if (port < 1)
382                                 error_msg_and_die("port must be >0.");
383                         break;
384                 case 'q':
385                         nprobes = atoi(optarg);
386                         if (nprobes < 1)
387                                 error_msg_and_die("nprobes must be >0.");
388                         break;
389                 case 'r':
390                         options |= SO_DONTROUTE;
391                         break;
392                 case 's':
393                         /*
394                          * set the ip source address of the outbound
395                          * probe (e.g., on a multi-homed host).
396                          */
397                         source = optarg;
398                         break;
399                 case 't':
400                         tos = atoi(optarg);
401                         if (tos < 0 || tos > 255)
402                                 error_msg_and_die("tos must be 0 to 255.");
403                         break;
404                 case 'v':
405 #ifdef BB_FEATURE_TRACEROUTE_VERBOSE
406                         verbose++;
407 #endif
408                         break;
409                 case 'w':
410                         waittime = atoi(optarg);
411                         if (waittime <= 1)
412                                 error_msg_and_die("wait must be >1 sec.");
413                         break;
414                 default:
415                         show_usage();
416                 }
417         argc -= optind;
418         argv += optind;
419
420         if (argc < 1)
421                 show_usage();
422
423         setlinebuf (stdout);
424
425         memset(&whereto, 0, sizeof(struct sockaddr));
426         hp = xgethostbyname(*argv);
427                         to->sin_family = hp->h_addrtype;
428         memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
429                         hostname = (char *)hp->h_name;
430         if (*++argv)
431                 datalen = atoi(*argv);
432         if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
433                 error_msg_and_die("packet size must be 0 <= s < %d.",
434                     MAXPACKET - sizeof(struct opacket));
435         datalen += sizeof(struct opacket);
436         outpacket = (struct opacket *)xmalloc((unsigned)datalen);
437         memset(outpacket, 0, datalen);
438         outpacket->ip.ip_dst = to->sin_addr;
439         outpacket->ip.ip_tos = tos;
440         outpacket->ip.ip_v = IPVERSION;
441         outpacket->ip.ip_id = 0;
442
443         ident = (getpid() & 0xffff) | 0x8000;
444
445         if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
446                 perror_msg_and_die(can_not_create_raw_socket);
447
448         s = create_icmp_socket();
449
450 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
451         if (options & SO_DEBUG)
452                 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
453                                   (char *)&on, sizeof(on));
454 #endif
455         if (options & SO_DONTROUTE)
456                 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
457                                   (char *)&on, sizeof(on));
458 #ifdef SO_SNDBUF
459         if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
460                        sizeof(datalen)) < 0)
461                 perror_msg_and_die("SO_SNDBUF");
462 #endif SO_SNDBUF
463 #ifdef IP_HDRINCL
464         if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
465                        sizeof(on)) < 0)
466                 perror_msg_and_die("IP_HDRINCL");
467 #endif IP_HDRINCL
468 #ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
469         if (options & SO_DEBUG)
470                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
471                                   (char *)&on, sizeof(on));
472 #endif
473         if (options & SO_DONTROUTE)
474                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
475                                   (char *)&on, sizeof(on));
476
477         if (source) {
478                 memset(&from, 0, sizeof(struct sockaddr));
479                 from.sin_family = AF_INET;
480                 from.sin_addr.s_addr = inet_addr(source);
481                 if (from.sin_addr.s_addr == -1)
482                         error_msg_and_die("unknown host %s", source);
483                 outpacket->ip.ip_src = from.sin_addr;
484 #ifndef IP_HDRINCL
485                 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
486                         perror_msg_and_die("bind");
487 #endif IP_HDRINCL
488         }
489
490         fprintf(stderr, "traceroute to %s (%s)", hostname,
491                 inet_ntoa(to->sin_addr));
492         if (source)
493                 fprintf(stderr, " from %s", source);
494         fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
495
496         for (ttl = 1; ttl <= max_ttl; ++ttl) {
497                 u_long lastaddr = 0;
498                 int got_there = 0;
499                 int unreachable = 0;
500
501                 printf("%2d ", ttl);
502                 for (probe = 0; probe < nprobes; ++probe) {
503                         int cc, reset_timer;
504                         struct timeval t1, t2;
505                         struct timezone tz;
506                         struct ip *ip;
507
508                         (void) gettimeofday(&t1, &tz);
509                         send_probe(++seq, ttl);
510                         reset_timer = 1;
511                         while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
512                                 (void) gettimeofday(&t2, &tz);
513                                 if ((i = packet_ok(packet, cc, &from, seq))) {
514                                         reset_timer = 1;
515                                         if (from.sin_addr.s_addr != lastaddr) {
516                                                 print(packet, cc, &from);
517                                                 lastaddr = from.sin_addr.s_addr;
518                                         }
519                                         printf("  %g ms", deltaT(&t1, &t2));
520                                         switch(i - 1) {
521                                         case ICMP_UNREACH_PORT:
522                                                 ip = (struct ip *)packet;
523                                                 if (ip->ip_ttl <= 1)
524                                                         printf(" !");
525                                                 ++got_there;
526                                                 break;
527                                         case ICMP_UNREACH_NET:
528                                                 ++unreachable;
529                                                 printf(" !N");
530                                                 break;
531                                         case ICMP_UNREACH_HOST:
532                                                 ++unreachable;
533                                                 printf(" !H");
534                                                 break;
535                                         case ICMP_UNREACH_PROTOCOL:
536                                                 ++got_there;
537                                                 printf(" !P");
538                                                 break;
539                                         case ICMP_UNREACH_NEEDFRAG:
540                                                 ++unreachable;
541                                                 printf(" !F");
542                                                 break;
543                                         case ICMP_UNREACH_SRCFAIL:
544                                                 ++unreachable;
545                                                 printf(" !S");
546                                                 break;
547                                         }
548                                         break;
549                                 } else
550                                         reset_timer = 0;
551                         }
552                         if (cc == 0)
553                                 printf(" *");
554                         (void) fflush(stdout);
555                 }
556                 putchar('\n');
557                 if (got_there || unreachable >= nprobes-1)
558                         exit(0);
559         }
560
561         return 0;
562 }