1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 1997,1998 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License along
15: with this program; if not, write to the Free Software Foundation, Inc.,
16: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17: */
18:
19: #include "config.h"
20:
21: #include <sys/types.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
26: #include <errno.h>
27: #include <string.h>
28: #include <strings.h>
29: #ifdef HAVE_ERROR_H
30: #include <error.h>
31: #else
32: #include "portability/error.h"
33: #endif
34: #ifdef HAVE_VALUES_H
35: #include <values.h>
36: #endif
37: #ifdef HAVE_SYS_LIMITS_H
38: #include <sys/limits.h>
39: #endif
40:
41: #include <netinet/in.h>
42: #include <sys/socket.h>
43: #include <ctype.h>
44: #include <assert.h>
45: #include <fcntl.h>
46: #include <limits.h>
47: #include <sys/stat.h>
48: #include <sys/time.h>
49:
50: #include "mtr.h"
51: #include "mtr-curses.h"
52: #include "display.h"
53: #include "dns.h"
54: #include "report.h"
55: #include "net.h"
56: #include "asn.h"
57: #include "utils.h"
58:
59: #ifdef HAVE_GETOPT
60: #include <getopt.h>
61: #else
62: #include "portability/getopt.h"
63: #endif
64:
65: char *myname;
66:
67: const struct fields data_fields[MAXFLD] = {
68: /* key, Remark, Header, Format, Width, CallBackFunc */
69: {' ', "<sp>: Space between fields", " ", " ", 1, &net_drop},
70: {'L', "L: Loss Ratio", "Loss%", " %4.1f%%", 6, &net_loss},
71: {'D', "D: Dropped Packets", "Drop", " %4d", 5, &net_drop},
72: {'R', "R: Received Packets", "Rcv", " %5d", 6, &net_returned},
73: {'S', "S: Sent Packets", "Snt", " %5d", 6, &net_xmit},
74: {'N', "N: Newest RTT(ms)", "Last", " %5.1f", 6, &net_last},
75: {'B', "B: Min/Best RTT(ms)", "Best", " %5.1f", 6, &net_best},
76: {'A', "A: Average RTT(ms)", "Avg", " %5.1f", 6, &net_avg},
77: {'W', "W: Max/Worst RTT(ms)", "Wrst", " %5.1f", 6, &net_worst},
78: {'V', "V: Standard Deviation", "StDev", " %5.1f", 6, &net_stdev},
79: {'G', "G: Geometric Mean", "Gmean", " %5.1f", 6, &net_gmean},
80: {'J', "J: Current Jitter", "Jttr", " %4.1f", 5, &net_jitter},
81: {'M', "M: Jitter Mean/Avg.", "Javg", " %4.1f", 5, &net_javg},
82: {'X', "X: Worst Jitter", "Jmax", " %4.1f", 5, &net_jworst},
83: {'I', "I: Interarrival Jitter", "Jint", " %4.1f", 5, &net_jinta},
84: {'\0', NULL, NULL, NULL, 0, NULL}
85: };
86:
87: typedef struct names {
88: char *name;
89: struct names *next;
90: } names_t;
91:
92: static void __attribute__ ((__noreturn__)) usage(FILE * out)
93: {
94: fputs("\nUsage:\n", out);
95: fputs(" mtr [options] hostname\n", out);
96: fputs("\n", out);
97: fputs(" -F, --filename FILE read hostname(s) from a file\n",
98: out);
99: fputs(" -4 use IPv4 only\n", out);
100: #ifdef ENABLE_IPV6
101: fputs(" -6 use IPv6 only\n", out);
102: #endif
103: fputs(" -u, --udp use UDP instead of ICMP echo\n",
104: out);
105: fputs(" -T, --tcp use TCP instead of ICMP echo\n",
106: out);
107: fputs(" -I, --interface NAME use named network interface\n",
108: out);
109: fputs
110: (" -a, --address ADDRESS bind the outgoing socket to ADDRESS\n",
111: out);
112: fputs(" -f, --first-ttl NUMBER set what TTL to start\n", out);
113: fputs(" -m, --max-ttl NUMBER maximum number of hops\n", out);
114: fputs(" -U, --max-unknown NUMBER maximum unknown host\n", out);
115: fputs
116: (" -P, --port PORT target port number for TCP, SCTP, or UDP\n",
117: out);
118: fputs(" -L, --localport LOCALPORT source port number for UDP\n", out);
119: fputs
120: (" -s, --psize PACKETSIZE set the packet size used for probing\n",
121: out);
122: fputs
123: (" -B, --bitpattern NUMBER set bit pattern to use in payload\n",
124: out);
125: fputs(" -i, --interval SECONDS ICMP echo request interval\n", out);
126: fputs
127: (" -G, --gracetime SECONDS number of seconds to wait for responses\n",
128: out);
129: fputs
130: (" -Q, --tos NUMBER type of service field in IP header\n",
131: out);
132: fputs
133: (" -e, --mpls display information from ICMP extensions\n",
134: out);
135: fputs
136: (" -Z, --timeout SECONDS seconds to keep probe sockets open\n",
137: out);
138: #ifdef SO_MARK
139: fputs(" -M, --mark MARK mark each sent packet\n", out);
140: #endif
141: fputs(" -r, --report output using report mode\n", out);
142: fputs(" -w, --report-wide output wide report\n", out);
143: fputs(" -c, --report-cycles COUNT set the number of pings sent\n",
144: out);
145: #ifdef HAVE_JANSSON
146: fputs(" -j, --json output json\n", out);
147: #endif
148: fputs(" -x, --xml output xml\n", out);
149: fputs(" -C, --csv output comma separated values\n",
150: out);
151: fputs(" -l, --raw output raw format\n", out);
152: fputs(" -p, --split split output\n", out);
153: #ifdef HAVE_CURSES
154: fputs(" -t, --curses use curses terminal interface\n",
155: out);
156: #endif
157: fputs(" --displaymode MODE select initial display mode\n",
158: out);
159: #ifdef HAVE_GTK
160: fputs(" -g, --gtk use GTK+ xwindow interface\n", out);
161: #endif
162: fputs(" -n, --no-dns do not resolve host names\n", out);
163: fputs(" -b, --show-ips show IP numbers and host names\n",
164: out);
165: fputs(" -o, --order FIELDS select output fields\n", out);
166: #ifdef HAVE_IPINFO
167: fputs(" -y, --ipinfo NUMBER select IP information in output\n",
168: out);
169: fputs(" -z, --aslookup display AS number\n", out);
170: #endif
171: fputs(" -h, --help display this help and exit\n", out);
172: fputs
173: (" -v, --version output version information and exit\n",
174: out);
175: fputs("\n", out);
176: fputs("See the 'man 8 mtr' for details.\n", out);
177: exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
178: }
179:
180:
181: static void append_to_names(
182: names_t ** names_head,
183: const char *item)
184: {
185: names_t **name_tail = names_head;
186:
187: while (*name_tail) {
188: name_tail = &(*name_tail)->next;
189: }
190:
191: names_t *name = calloc(1, sizeof(names_t));
192: if (name == NULL) {
193: error(EXIT_FAILURE, errno, "memory allocation failure");
194: }
195: name->name = xstrdup(item);
196: name->next = NULL;
197:
198: *name_tail = name;
199: }
200:
201: static void read_from_file(
202: names_t ** names,
203: const char *filename)
204: {
205:
206: FILE *in;
207: char line[512];
208:
209: if (!filename || strcmp(filename, "-") == 0) {
210: clearerr(stdin);
211: in = stdin;
212: } else {
213: in = fopen(filename, "r");
214: if (!in) {
215: error(EXIT_FAILURE, errno, "open %s", filename);
216: }
217: }
218:
219: while (fgets(line, sizeof(line), in)) {
220: char *name = trim(line, '\0');
221: append_to_names(names, name);
222: }
223:
224: if (ferror(in)) {
225: error(EXIT_FAILURE, errno, "ferror %s", filename);
226: }
227:
228: if (in != stdin)
229: fclose(in);
230: }
231:
232: /*
233: * If the file stream is associated with a regular file, lock the file
234: * in order coordinate writes to a common file from multiple mtr
235: * instances. This is useful if, for example, multiple mtr instances
236: * try to append results to a common file.
237: */
238:
239: static void lock(
240: FILE * f)
241: {
242: int fd;
243: struct stat buf;
244: static struct flock lock;
245:
246: assert(f);
247:
248: lock.l_type = F_WRLCK;
249: lock.l_start = 0;
250: lock.l_whence = SEEK_END;
251: lock.l_len = 0;
252: lock.l_pid = getpid();
253:
254: fd = fileno(f);
255: if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
256: if (fcntl(fd, F_SETLKW, &lock) == -1) {
257: error(0, errno, "fcntl (ignored)");
258: }
259: }
260: }
261:
262: /*
263: * If the file stream is associated with a regular file, unlock the
264: * file (which presumably has previously been locked).
265: */
266:
267: static void unlock(
268: FILE * f)
269: {
270: int fd;
271: struct stat buf;
272: static struct flock lock;
273:
274: assert(f);
275:
276: lock.l_type = F_UNLCK;
277: lock.l_start = 0;
278: lock.l_whence = SEEK_END;
279: lock.l_len = 0;
280: lock.l_pid = getpid();
281:
282: fd = fileno(f);
283: if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
284: if (fcntl(fd, F_SETLKW, &lock) == -1) {
285: error(0, errno, "fcntl (ignored)");
286: }
287: }
288: }
289:
290:
291: static void init_fld_options(
292: struct mtr_ctl *ctl)
293: {
294: int i;
295:
296: memset(ctl->fld_index, -1, FLD_INDEX_SZ*sizeof(ctl->fld_index[0]));
297:
298: for (i = 0; data_fields[i].key != 0; i++) {
299: ctl->available_options[i] = data_fields[i].key;
300: ctl->fld_index[data_fields[i].key] = i;
301: }
302: ctl->available_options[i++] = '_';
303: ctl->available_options[i] = 0;
304: }
305:
306:
307: static void parse_arg(
308: struct mtr_ctl *ctl,
309: names_t ** names,
310: int argc,
311: char **argv)
312: {
313: int opt;
314: int i;
315: /* IMPORTANT: when adding or modifying an option:
316: 0/ try to find a somewhat logical order;
317: 1/ add the long option name in "long_options" below;
318: 2/ update the man page (use the same order);
319: 3/ update the help message (see usage() function).
320: */
321: enum {
322: OPT_DISPLAYMODE = CHAR_MAX + 1
323: };
324: static const struct option long_options[] = {
325: /* option name, has argument, NULL, short name */
326: {"help", 0, NULL, 'h'},
327: {"version", 0, NULL, 'v'},
328:
329: {"inet", 0, NULL, '4'}, /* IPv4 only */
330: #ifdef ENABLE_IPV6
331: {"inet6", 0, NULL, '6'}, /* IPv6 only */
332: #endif
333: {"filename", 1, NULL, 'F'},
334:
335: {"report", 0, NULL, 'r'},
336: {"report-wide", 0, NULL, 'w'},
337: {"xml", 0, NULL, 'x'},
338: #ifdef HAVE_CURSES
339: {"curses", 0, NULL, 't'},
340: #endif
341: #ifdef HAVE_GTK
342: {"gtk", 0, NULL, 'g'},
343: #endif
344: {"raw", 0, NULL, 'l'},
345: {"csv", 0, NULL, 'C'},
346: #ifdef HAVE_JANSSON
347: {"json", 0, NULL, 'j'},
348: #endif
349: {"displaymode", 1, NULL, OPT_DISPLAYMODE},
350: {"split", 0, NULL, 'p'}, /* BL */
351: /* maybe above should change to -d 'x' */
352:
353: {"no-dns", 0, NULL, 'n'},
354: {"show-ips", 0, NULL, 'b'},
355: {"order", 1, NULL, 'o'}, /* fields to display & their order */
356: #ifdef HAVE_IPINFO
357: {"ipinfo", 1, NULL, 'y'}, /* IP info lookup */
358: {"aslookup", 0, NULL, 'z'}, /* Do AS lookup (--ipinfo 0) */
359: #endif
360:
361: {"interval", 1, NULL, 'i'},
362: {"report-cycles", 1, NULL, 'c'},
363: {"psize", 1, NULL, 's'}, /* overload psize<0, ->rand(min,max) */
364: {"bitpattern", 1, NULL, 'B'}, /* overload B>255, ->rand(0,255) */
365: {"tos", 1, NULL, 'Q'}, /* typeof service (0,255) */
366: {"mpls", 0, NULL, 'e'},
367: {"interface", 1, NULL, 'I'},
368: {"address", 1, NULL, 'a'},
369: {"first-ttl", 1, NULL, 'f'}, /* -f & -m are borrowed from traceroute */
370: {"max-ttl", 1, NULL, 'm'},
371: {"max-unknown", 1, NULL, 'U'},
372: {"udp", 0, NULL, 'u'}, /* UDP (default is ICMP) */
373: {"tcp", 0, NULL, 'T'}, /* TCP (default is ICMP) */
374: #ifdef HAS_SCTP
375: {"sctp", 0, NULL, 'S'}, /* SCTP (default is ICMP) */
376: #endif
377: {"port", 1, NULL, 'P'}, /* target port number for TCP/SCTP/UDP */
378: {"localport", 1, NULL, 'L'}, /* source port number for UDP */
379: {"timeout", 1, NULL, 'Z'}, /* timeout for probe sockets */
380: {"gracetime", 1, NULL, 'G'}, /* gracetime for replies after last probe */
381: #ifdef SO_MARK
382: {"mark", 1, NULL, 'M'}, /* use SO_MARK */
383: #endif
384: {NULL, 0, NULL, 0}
385: };
386: enum { num_options = sizeof(long_options) / sizeof(struct option) };
387: char short_options[num_options * 2];
388: size_t n, p;
389:
390: for (n = p = 0; n < num_options; n++) {
391: if (CHAR_MAX < long_options[n].val) {
392: continue;
393: }
394: short_options[p] = long_options[n].val;
395: p++;
396: if (long_options[n].has_arg == 1) {
397: short_options[p] = ':';
398: p++;
399: }
400: /* optional options need two ':', but ignore them now as they are not in use */
401: }
402:
403: opt = 0;
404: while (1) {
405: opt = getopt_long(argc, argv, short_options, long_options, NULL);
406: if (opt == -1)
407: break;
408:
409: switch (opt) {
410: case 'v':
411: printf("mtr " PACKAGE_VERSION "\n");
412: exit(EXIT_SUCCESS);
413: break;
414: case 'h':
415: usage(stdout);
416: break;
417:
418: case 'r':
419: ctl->DisplayMode = DisplayReport;
420: break;
421: case 'w':
422: ctl->reportwide = 1;
423: ctl->DisplayMode = DisplayReport;
424: break;
425: #ifdef HAVE_CURSES
426: case 't':
427: ctl->DisplayMode = DisplayCurses;
428: break;
429: #endif
430: #ifdef HAVE_GTK
431: case 'g':
432: ctl->DisplayMode = DisplayGTK;
433: break;
434: #endif
435: case 'p': /* BL */
436: ctl->DisplayMode = DisplaySplit;
437: break;
438: case 'l':
439: ctl->DisplayMode = DisplayRaw;
440: break;
441: case 'C':
442: ctl->DisplayMode = DisplayCSV;
443: break;
444: #ifdef HAVE_JANSSON
445: case 'j':
446: ctl->DisplayMode = DisplayJSON;
447: break;
448: #endif
449: case 'x':
450: ctl->DisplayMode = DisplayXML;
451: break;
452:
453: case OPT_DISPLAYMODE:
454: ctl->display_mode =
455: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
456: if ((DisplayModeMAX - 1) < ctl->display_mode)
457: error(EXIT_FAILURE, 0, "value out of range (%d - %d): %s",
458: DisplayModeDefault, (DisplayModeMAX - 1), optarg);
459: break;
460: case 'c':
461: ctl->MaxPing =
462: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
463: ctl->ForceMaxPing = 1;
464: break;
465: case 's':
466: ctl->cpacketsize =
467: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
468: break;
469: case 'I':
470: ctl->InterfaceName = optarg;
471: break;
472: case 'a':
473: ctl->InterfaceAddress = optarg;
474: break;
475: case 'e':
476: ctl->enablempls = 1;
477: break;
478: case 'n':
479: ctl->dns = 0;
480: break;
481: case 'i':
482: ctl->WaitTime = strtofloat_or_err(optarg, "invalid argument");
483: if (ctl->WaitTime <= 0.0) {
484: error(EXIT_FAILURE, 0, "wait time must be positive");
485: }
486: if (!running_as_root() && ctl->WaitTime < 1.0) {
487: error(EXIT_FAILURE, 0,
488: "non-root users cannot request an interval < 1.0 seconds");
489: }
490: break;
491: case 'f':
492: ctl->fstTTL =
493: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
494: if (ctl->fstTTL > ctl->maxTTL) {
495: ctl->fstTTL = ctl->maxTTL;
496: }
497: if (ctl->fstTTL < 1) { /* prevent 0 hop */
498: ctl->fstTTL = 1;
499: }
500: break;
501: case 'F':
502: read_from_file(names, optarg);
503: break;
504: case 'm':
505: ctl->maxTTL =
506: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
507: if (ctl->maxTTL > (MaxHost - 1)) {
508: ctl->maxTTL = MaxHost - 1;
509: }
510: if (ctl->maxTTL < 1) { /* prevent 0 hop */
511: ctl->maxTTL = 1;
512: }
513: if (ctl->fstTTL > ctl->maxTTL) { /* don't know the pos of -m or -f */
514: ctl->fstTTL = ctl->maxTTL;
515: }
516: break;
517: case 'U':
518: ctl->maxUnknown =
519: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
520: if (ctl->maxUnknown < 1) {
521: ctl->maxUnknown = 1;
522: }
523: break;
524: case 'o':
525: /* Check option before passing it on to fld_active. */
526: if (strlen(optarg) > MAXFLD) {
527: error(EXIT_FAILURE, 0, "Too many fields: %s", optarg);
528: }
529: for (i = 0; optarg[i]; i++) {
530: if (!strchr(ctl->available_options, optarg[i])) {
531: error(EXIT_FAILURE, 0, "Unknown field identifier: %c",
532: optarg[i]);
533: }
534: }
535: xstrncpy(ctl->fld_active, optarg, 2 * MAXFLD);
536: break;
537: case 'B':
538: ctl->bitpattern =
539: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
540: if (ctl->bitpattern > 255)
541: ctl->bitpattern = -1;
542: break;
543: case 'G':
544: ctl->GraceTime = strtofloat_or_err(optarg, "invalid argument");
545: if (ctl->GraceTime <= 0.0) {
546: error(EXIT_FAILURE, 0, "wait time must be positive");
547: }
548: break;
549: case 'Q':
550: ctl->tos =
551: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
552: if (ctl->tos > 255 || ctl->tos < 0) {
553: /* error message, should do more checking for valid values,
554: * details in rfc2474 */
555: ctl->tos = 0;
556: }
557: break;
558: case 'u':
559: if (ctl->mtrtype != IPPROTO_ICMP) {
560: error(EXIT_FAILURE, 0,
561: "-u , -T and -S are mutually exclusive");
562: }
563: ctl->mtrtype = IPPROTO_UDP;
564: break;
565: case 'T':
566: if (ctl->mtrtype != IPPROTO_ICMP) {
567: error(EXIT_FAILURE, 0,
568: "-u , -T and -S are mutually exclusive");
569: }
570: if (!ctl->remoteport) {
571: ctl->remoteport = 80;
572: }
573: ctl->mtrtype = IPPROTO_TCP;
574: break;
575: #ifdef HAS_SCTP
576: case 'S':
577: if (ctl->mtrtype != IPPROTO_ICMP) {
578: error(EXIT_FAILURE, 0,
579: "-u , -T and -S are mutually exclusive");
580: }
581: if (!ctl->remoteport) {
582: ctl->remoteport = 80;
583: }
584: ctl->mtrtype = IPPROTO_SCTP;
585: break;
586: #endif
587: case 'b':
588: ctl->show_ips = 1;
589: break;
590: case 'P':
591: ctl->remoteport =
592: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
593: if (ctl->remoteport < 1 || MaxPort < ctl->remoteport) {
594: error(EXIT_FAILURE, 0, "Illegal port number: %d",
595: ctl->remoteport);
596: }
597: break;
598: case 'L':
599: ctl->localport =
600: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
601: if (ctl->localport < MinPort || MaxPort < ctl->localport) {
602: error(EXIT_FAILURE, 0, "Illegal port number: %d",
603: ctl->localport);
604: }
605: break;
606: case 'Z':
607: ctl->probe_timeout =
608: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
609: ctl->probe_timeout *= 1000000;
610: break;
611: case '4':
612: ctl->af = AF_INET;
613: break;
614: #ifdef ENABLE_IPV6
615: case '6':
616: ctl->af = AF_INET6;
617: break;
618: #endif
619: #ifdef HAVE_IPINFO
620: case 'y':
621: ctl->ipinfo_no =
622: strtonum_or_err(optarg, "invalid argument", STRTO_INT);
623: if (ctl->ipinfo_no < 0 || 4 < ctl->ipinfo_no) {
624: error(EXIT_FAILURE, 0, "value %d out of range (0 - 4)",
625: ctl->ipinfo_no);
626: }
627: break;
628: case 'z':
629: ctl->ipinfo_no = 0;
630: break;
631: #endif
632: #ifdef SO_MARK
633: case 'M':
634: ctl->mark =
635: strtonum_or_err(optarg, "invalid argument", STRTO_U32INT);
636: break;
637: #endif
638: default:
639: usage(stderr);
640: }
641: }
642:
643: if (ctl->DisplayMode == DisplayReport ||
644: ctl->DisplayMode == DisplayTXT ||
645: #ifdef HAVE_JANSSON
646: ctl->DisplayMode == DisplayJSON ||
647: #endif
648: ctl->DisplayMode == DisplayXML ||
649: ctl->DisplayMode == DisplayRaw || ctl->DisplayMode == DisplayCSV)
650: ctl->Interactive = 0;
651:
652: if (optind > argc - 1)
653: return;
654:
655: }
656:
657:
658: static void parse_mtr_options(
659: struct mtr_ctl *ctl,
660: names_t ** names,
661: char *string)
662: {
663: int argc = 1;
664: char *argv[128], *p;
665:
666: if (!string)
667: return;
668: argv[0] = xstrdup(PACKAGE_NAME);
669: argc = 1;
670: p = strtok(string, " \t");
671: while (p != NULL && ((size_t) argc < (sizeof(argv) / sizeof(argv[0])))) {
672: argv[argc++] = p;
673: p = strtok(NULL, " \t");
674: }
675: if (p != NULL) {
676: error(0, 0, "Warning: extra arguments ignored: %s", p);
677: }
678:
679: parse_arg(ctl, names, argc, argv);
680: free(argv[0]);
681: optind = 0;
682: }
683:
684: static void init_rand(
685: void)
686: {
687: struct timeval tv;
688:
689: gettimeofday(&tv, NULL);
690: srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
691: }
692:
693: /*
694: For historical reasons, we need a hostent structure to represent
695: our remote target for probing. The obsolete way of doing this
696: would be to use gethostbyname(). We'll use getaddrinfo() instead
697: to generate the hostent.
698: */
699: int get_addrinfo_from_name(
700: struct mtr_ctl *ctl,
701: struct addrinfo **res,
702: const char *name)
703: {
704: int gai_error;
705: struct addrinfo hints;
706:
707: /* gethostbyname2() is deprecated so we'll use getaddrinfo() instead. */
708: memset(&hints, 0, sizeof hints);
709: hints.ai_family = ctl->af;
710: hints.ai_socktype = SOCK_DGRAM;
711: gai_error = getaddrinfo(name, NULL, &hints, res);
712: if (gai_error) {
713: if (gai_error == EAI_SYSTEM)
714: error(0, 0, "Failed to resolve host: %s", name);
715: else
716: error(0, 0, "Failed to resolve host: %s: %s", name,
717: gai_strerror(gai_error));
718:
719: return -1;
720: }
721:
722: ctl->af = (*res)->ai_family;
723: return 0;
724: }
725:
726:
727: int main(
728: int argc,
729: char **argv)
730: {
731: names_t *names_head = NULL;
732: names_t *names_walk;
733:
734: myname = argv[0];
735: struct mtr_ctl ctl;
736: memset(&ctl, 0, sizeof(ctl));
737: /* initialize non-null values */
738: ctl.Interactive = 1;
739: ctl.MaxPing = 10;
740: ctl.WaitTime = 1.0;
741: ctl.GraceTime = 5.0;
742: ctl.dns = 1;
743: ctl.use_dns = 1;
744: ctl.cpacketsize = 64;
745: ctl.af = DEFAULT_AF;
746: ctl.mtrtype = IPPROTO_ICMP;
747: ctl.fstTTL = 1;
748: ctl.maxTTL = 30;
749: ctl.maxUnknown = 12;
750: ctl.probe_timeout = 10 * 1000000;
751: ctl.ipinfo_no = -1;
752: ctl.ipinfo_max = -1;
753: xstrncpy(ctl.fld_active, "LS NABWV", 2 * MAXFLD);
754:
755: /*
756: mtr used to be suid root. It should not be with this version.
757: We'll check so that we can notify people using installation
758: mechanisms with obsolete assumptions.
759: */
760: if ((geteuid() != getuid()) || (getegid() != getgid())) {
761: error(EXIT_FAILURE, errno, "mtr should not run suid");
762: }
763:
764: /* This will check if stdout/stderr writing is successful */
765: atexit(close_stdout);
766:
767: /* reset the random seed */
768: init_rand();
769:
770: display_detect(&ctl, &argc, &argv);
771: ctl.display_mode = DisplayModeDefault;
772:
773: /* The field options are now in a static array all together,
774: but that requires a run-time initialization. */
775: init_fld_options(&ctl);
776:
777: parse_mtr_options(&ctl, &names_head, getenv("MTR_OPTIONS"));
778:
779: parse_arg(&ctl, &names_head, argc, argv);
780:
781: while (optind < argc) {
782: char *name = argv[optind++];
783: append_to_names(&names_head, name);
784: }
785:
786: /* default: localhost. */
787: if (!names_head)
788: append_to_names(&names_head, "localhost");
789:
790: names_walk = names_head;
791: while (names_walk != NULL) {
792:
793: ctl.Hostname = names_walk->name;
794: if (gethostname(ctl.LocalHostname, sizeof(ctl.LocalHostname))) {
795: xstrncpy(ctl.LocalHostname, "UNKNOWNHOST",
796: sizeof(ctl.LocalHostname));
797: }
798:
799: struct addrinfo *res = NULL;
800: if (get_addrinfo_from_name(&ctl, &res, ctl.Hostname) != 0) {
801: if (ctl.Interactive)
802: exit(EXIT_FAILURE);
803: else {
804: names_walk = names_walk->next;
805: continue;
806: }
807: }
808:
809: if (net_open(&ctl, res) != 0) {
810: error(0, 0, "Unable to start net module");
811: if (ctl.Interactive)
812: exit(EXIT_FAILURE);
813: else {
814: names_walk = names_walk->next;
815: continue;
816: }
817: }
818:
819: freeaddrinfo(res);
820:
821: lock(stdout);
822: dns_open();
823: display_open(&ctl);
824:
825: display_loop(&ctl);
826:
827: net_end_transit();
828: display_close(&ctl);
829: unlock(stdout);
830:
831: if (ctl.Interactive)
832: break;
833: else
834: names_walk = names_walk->next;
835:
836: }
837:
838: net_close();
839:
840: while (names_head != NULL) {
841: names_t *item = names_head;
842: free(item->name);
843: item->name = NULL;
844: names_head = item->next;
845: free(item);
846: item = NULL;
847: }
848:
849: return 0;
850: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>