File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / ui / mtr.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 21 14:25:31 2019 UTC (4 years, 9 months ago) by misho
Branches: mtr, MAIN
CVS tags: v0_92, HEAD
mtr ver 0.92

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>