Annotation of embedaddon/mtr/ui/mtr.c, revision 1.1.1.3

1.1       misho       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: 
1.1.1.2   misho      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.
1.1       misho      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: 
1.1.1.2   misho      65: char *myname;
                     66: 
1.1       misho      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);
1.1.1.2   misho     107:     fputs(" -I, --interface NAME       use named network interface\n",
                    108:          out);
1.1       misho     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);
1.1.1.3 ! misho     145: #ifdef HAVE_JANSSON
1.1       misho     146:     fputs(" -j, --json                 output json\n", out);
1.1.1.3 ! misho     147: #endif
1.1       misho     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
1.1.1.2   misho     162:     fputs(" -n, --no-dns               do not resolve host names\n", out);
1.1       misho     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: 
1.1.1.2   misho     296:     memset(ctl->fld_index, -1, FLD_INDEX_SZ*sizeof(ctl->fld_index[0]));
1.1       misho     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:     }
1.1.1.2   misho     302:     ctl->available_options[i++] = '_';
1.1       misho     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'},
1.1.1.2   misho     346: #ifdef HAVE_JANSSON
1.1       misho     347:         {"json", 0, NULL, 'j'},
1.1.1.2   misho     348: #endif
1.1       misho     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'},
1.1.1.2   misho     367:         {"interface", 1, NULL, 'I'},
1.1       misho     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;
1.1.1.2   misho     444: #ifdef HAVE_JANSSON
1.1       misho     445:         case 'j':
                    446:             ctl->DisplayMode = DisplayJSON;
                    447:             break;
1.1.1.2   misho     448: #endif
1.1       misho     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;
1.1.1.2   misho     469:         case 'I':
                    470:             ctl->InterfaceName = optarg;
                    471:             break;
1.1       misho     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:             }
1.1.1.2   misho     486:             if (!running_as_root() && ctl->WaitTime < 1.0) {
1.1       misho     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 ||
1.1.1.2   misho     645: #ifdef HAVE_JANSSON
1.1       misho     646:         ctl->DisplayMode == DisplayJSON ||
1.1.1.2   misho     647: #endif
1.1       misho     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: 
1.1.1.2   misho     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: */
1.1.1.3 ! misho     699: int get_addrinfo_from_name(
1.1.1.2   misho     700:     struct mtr_ctl *ctl,
1.1.1.3 ! misho     701:     struct addrinfo **res,
        !           702:     const char *name)
1.1.1.2   misho     703: {
                    704:     int gai_error;
1.1.1.3 ! misho     705:     struct addrinfo hints;
1.1.1.2   misho     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;
1.1.1.3 ! misho     711:     gai_error = getaddrinfo(name, NULL, &hints, res);
1.1.1.2   misho     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: 
1.1.1.3 ! misho     722:     ctl->af = (*res)->ai_family;
1.1.1.2   misho     723:     return 0;
                    724: }
                    725: 
                    726: 
1.1       misho     727: int main(
                    728:     int argc,
                    729:     char **argv)
                    730: {
                    731:     names_t *names_head = NULL;
                    732:     names_t *names_walk;
                    733: 
1.1.1.2   misho     734:     myname = argv[0];
1.1       misho     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: 
1.1.1.3 ! misho     799:         struct addrinfo *res = NULL;
        !           800:         if (get_addrinfo_from_name(&ctl, &res, ctl.Hostname) != 0) {
1.1       misho     801:             if (ctl.Interactive)
                    802:                 exit(EXIT_FAILURE);
                    803:             else {
                    804:                 names_walk = names_walk->next;
                    805:                 continue;
                    806:             }
                    807:         }
                    808: 
1.1.1.3 ! misho     809:         if (net_open(&ctl, res) != 0) {
1.1       misho     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: 
1.1.1.3 ! misho     819:         freeaddrinfo(res);
        !           820: 
1.1       misho     821:         lock(stdout);
1.1.1.3 ! misho     822:         dns_open();
1.1       misho     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>