Blame ui/mtr.c

Packit b802ec
/*
Packit b802ec
    mtr  --  a network diagnostic tool
Packit b802ec
    Copyright (C) 1997,1998  Matt Kimball
Packit b802ec
Packit b802ec
    This program is free software; you can redistribute it and/or modify
Packit b802ec
    it under the terms of the GNU General Public License version 2 as
Packit b802ec
    published by the Free Software Foundation.
Packit b802ec
Packit b802ec
    This program is distributed in the hope that it will be useful,
Packit b802ec
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b802ec
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b802ec
    GNU General Public License for more details.
Packit b802ec
Packit b802ec
    You should have received a copy of the GNU General Public License
Packit b802ec
    along with this program; if not, write to the Free Software
Packit b802ec
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Packit b802ec
*/
Packit b802ec
Packit b802ec
#include "config.h"
Packit b802ec
Packit b802ec
#include <sys/types.h>
Packit b802ec
#include <stdio.h>
Packit b802ec
#include <stdlib.h>
Packit b802ec
#include <string.h>
Packit b802ec
#include <unistd.h>
Packit b802ec
#include <errno.h>
Packit b802ec
#include <string.h>
Packit b802ec
#include <strings.h>
Packit b802ec
#ifdef HAVE_ERROR_H
Packit b802ec
#include <error.h>
Packit b802ec
#else
Packit b802ec
#include "portability/error.h"
Packit b802ec
#endif
Packit b802ec
#ifdef HAVE_VALUES_H
Packit b802ec
#include <values.h>
Packit b802ec
#endif
Packit b802ec
#ifdef HAVE_SYS_LIMITS_H
Packit b802ec
#include <sys/limits.h>
Packit b802ec
#endif
Packit b802ec
Packit b802ec
#include <netdb.h>
Packit b802ec
#include <netinet/in.h>
Packit b802ec
#include <sys/socket.h>
Packit b802ec
#include <ctype.h>
Packit b802ec
#include <assert.h>
Packit b802ec
#include <fcntl.h>
Packit b802ec
#include <limits.h>
Packit b802ec
#include <sys/stat.h>
Packit b802ec
#include <sys/time.h>
Packit b802ec
Packit b802ec
#include "mtr.h"
Packit b802ec
#include "mtr-curses.h"
Packit b802ec
#include "display.h"
Packit b802ec
#include "dns.h"
Packit b802ec
#include "report.h"
Packit b802ec
#include "net.h"
Packit b802ec
#include "asn.h"
Packit b802ec
#include "utils.h"
Packit b802ec
Packit b802ec
#ifdef HAVE_GETOPT
Packit b802ec
#include <getopt.h>
Packit b802ec
#else
Packit b802ec
#include "portability/getopt.h"
Packit b802ec
#endif
Packit b802ec
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
#define DEFAULT_AF AF_UNSPEC
Packit b802ec
#else
Packit b802ec
#define DEFAULT_AF AF_INET
Packit b802ec
#endif
Packit b802ec
Packit b802ec
Packit b802ec
const struct fields data_fields[MAXFLD] = {
Packit b802ec
    /* key, Remark, Header, Format, Width, CallBackFunc */
Packit b802ec
    {' ', "<sp>: Space between fields", " ", " ", 1, &net_drop},
Packit b802ec
    {'L', "L: Loss Ratio", "Loss%", " %4.1f%%", 6, &net_loss},
Packit b802ec
    {'D', "D: Dropped Packets", "Drop", " %4d", 5, &net_drop},
Packit b802ec
    {'R', "R: Received Packets", "Rcv", " %5d", 6, &net_returned},
Packit b802ec
    {'S', "S: Sent Packets", "Snt", " %5d", 6, &net_xmit},
Packit b802ec
    {'N', "N: Newest RTT(ms)", "Last", " %5.1f", 6, &net_last},
Packit b802ec
    {'B', "B: Min/Best RTT(ms)", "Best", " %5.1f", 6, &net_best},
Packit b802ec
    {'A', "A: Average RTT(ms)", "Avg", " %5.1f", 6, &net_avg},
Packit b802ec
    {'W', "W: Max/Worst RTT(ms)", "Wrst", " %5.1f", 6, &net_worst},
Packit b802ec
    {'V', "V: Standard Deviation", "StDev", " %5.1f", 6, &net_stdev},
Packit b802ec
    {'G', "G: Geometric Mean", "Gmean", " %5.1f", 6, &net_gmean},
Packit b802ec
    {'J', "J: Current Jitter", "Jttr", " %4.1f", 5, &net_jitter},
Packit b802ec
    {'M', "M: Jitter Mean/Avg.", "Javg", " %4.1f", 5, &net_javg},
Packit b802ec
    {'X', "X: Worst Jitter", "Jmax", " %4.1f", 5, &net_jworst},
Packit b802ec
    {'I', "I: Interarrival Jitter", "Jint", " %4.1f", 5, &net_jinta},
Packit b802ec
    {'\0', NULL, NULL, NULL, 0, NULL}
Packit b802ec
};
Packit b802ec
Packit b802ec
typedef struct names {
Packit b802ec
    char *name;
Packit b802ec
    struct names *next;
Packit b802ec
} names_t;
Packit b802ec
Packit b802ec
static void __attribute__ ((__noreturn__)) usage(FILE * out)
Packit b802ec
{
Packit b802ec
    fputs("\nUsage:\n", out);
Packit b802ec
    fputs(" mtr [options] hostname\n", out);
Packit b802ec
    fputs("\n", out);
Packit b802ec
    fputs(" -F, --filename FILE        read hostname(s) from a file\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -4                         use IPv4 only\n", out);
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
    fputs(" -6                         use IPv6 only\n", out);
Packit b802ec
#endif
Packit b802ec
    fputs(" -u, --udp                  use UDP instead of ICMP echo\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -T, --tcp                  use TCP instead of ICMP echo\n",
Packit b802ec
          out);
Packit b802ec
    fputs
Packit b802ec
        (" -a, --address ADDRESS      bind the outgoing socket to ADDRESS\n",
Packit b802ec
         out);
Packit b802ec
    fputs(" -f, --first-ttl NUMBER     set what TTL to start\n", out);
Packit b802ec
    fputs(" -m, --max-ttl NUMBER       maximum number of hops\n", out);
Packit b802ec
    fputs(" -U, --max-unknown NUMBER   maximum unknown host\n", out);
Packit b802ec
    fputs
Packit b802ec
        (" -P, --port PORT            target port number for TCP, SCTP, or UDP\n",
Packit b802ec
         out);
Packit b802ec
    fputs(" -L, --localport LOCALPORT  source port number for UDP\n", out);
Packit b802ec
    fputs
Packit b802ec
        (" -s, --psize PACKETSIZE     set the packet size used for probing\n",
Packit b802ec
         out);
Packit b802ec
    fputs
Packit b802ec
        (" -B, --bitpattern NUMBER    set bit pattern to use in payload\n",
Packit b802ec
         out);
Packit b802ec
    fputs(" -i, --interval SECONDS     ICMP echo request interval\n", out);
Packit b802ec
    fputs
Packit b802ec
        (" -G, --gracetime SECONDS    number of seconds to wait for responses\n",
Packit b802ec
         out);
Packit b802ec
    fputs
Packit b802ec
        (" -Q, --tos NUMBER           type of service field in IP header\n",
Packit b802ec
         out);
Packit b802ec
    fputs
Packit b802ec
        (" -e, --mpls                 display information from ICMP extensions\n",
Packit b802ec
         out);
Packit b802ec
    fputs
Packit b802ec
        (" -Z, --timeout SECONDS      seconds to keep probe sockets open\n",
Packit b802ec
         out);
Packit b802ec
#ifdef SO_MARK
Packit b802ec
    fputs(" -M, --mark MARK            mark each sent packet\n", out);
Packit b802ec
#endif
Packit b802ec
    fputs(" -r, --report               output using report mode\n", out);
Packit b802ec
    fputs(" -w, --report-wide          output wide report\n", out);
Packit b802ec
    fputs(" -c, --report-cycles COUNT  set the number of pings sent\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -j, --json                 output json\n", out);
Packit b802ec
    fputs(" -x, --xml                  output xml\n", out);
Packit b802ec
    fputs(" -C, --csv                  output comma separated values\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -l, --raw                  output raw format\n", out);
Packit b802ec
    fputs(" -p, --split                split output\n", out);
Packit b802ec
#ifdef HAVE_CURSES
Packit b802ec
    fputs(" -t, --curses               use curses terminal interface\n",
Packit b802ec
          out);
Packit b802ec
#endif
Packit b802ec
    fputs("     --displaymode MODE     select initial display mode\n",
Packit b802ec
          out);
Packit b802ec
#ifdef HAVE_GTK
Packit b802ec
    fputs(" -g, --gtk                  use GTK+ xwindow interface\n", out);
Packit b802ec
#endif
Packit b802ec
    fputs(" -n, --no-dns               do not resove host names\n", out);
Packit b802ec
    fputs(" -b, --show-ips             show IP numbers and host names\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -o, --order FIELDS         select output fields\n", out);
Packit b802ec
#ifdef HAVE_IPINFO
Packit b802ec
    fputs(" -y, --ipinfo NUMBER        select IP information in output\n",
Packit b802ec
          out);
Packit b802ec
    fputs(" -z, --aslookup             display AS number\n", out);
Packit b802ec
#endif
Packit b802ec
    fputs(" -h, --help                 display this help and exit\n", out);
Packit b802ec
    fputs
Packit b802ec
        (" -v, --version              output version information and exit\n",
Packit b802ec
         out);
Packit b802ec
    fputs("\n", out);
Packit b802ec
    fputs("See the 'man 8 mtr' for details.\n", out);
Packit b802ec
    exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
Packit b802ec
}
Packit b802ec
Packit b802ec
Packit b802ec
static void append_to_names(
Packit b802ec
    names_t ** names_head,
Packit b802ec
    const char *item)
Packit b802ec
{
Packit b802ec
    names_t **name_tail = names_head;
Packit b802ec
Packit b802ec
    while (*name_tail) {
Packit b802ec
        name_tail = &(*name_tail)->next;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    names_t *name = calloc(1, sizeof(names_t));
Packit b802ec
    if (name == NULL) {
Packit b802ec
        error(EXIT_FAILURE, errno, "memory allocation failure");
Packit b802ec
    }
Packit b802ec
    name->name = xstrdup(item);
Packit b802ec
    name->next = NULL;
Packit b802ec
Packit b802ec
    *name_tail = name;
Packit b802ec
}
Packit b802ec
Packit b802ec
static void read_from_file(
Packit b802ec
    names_t ** names,
Packit b802ec
    const char *filename)
Packit b802ec
{
Packit b802ec
Packit b802ec
    FILE *in;
Packit b802ec
    char line[512];
Packit b802ec
Packit b802ec
    if (!filename || strcmp(filename, "-") == 0) {
Packit b802ec
        clearerr(stdin);
Packit b802ec
        in = stdin;
Packit b802ec
    } else {
Packit b802ec
        in = fopen(filename, "r");
Packit b802ec
        if (!in) {
Packit b802ec
            error(EXIT_FAILURE, errno, "open %s", filename);
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    while (fgets(line, sizeof(line), in)) {
Packit b802ec
        char *name = trim(line, '\0');
Packit b802ec
        append_to_names(names, name);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (ferror(in)) {
Packit b802ec
        error(EXIT_FAILURE, errno, "ferror %s", filename);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (in != stdin)
Packit b802ec
        fclose(in);
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
 * If the file stream is associated with a regular file, lock the file
Packit b802ec
 * in order coordinate writes to a common file from multiple mtr
Packit b802ec
 * instances. This is useful if, for example, multiple mtr instances
Packit b802ec
 * try to append results to a common file.
Packit b802ec
 */
Packit b802ec
Packit b802ec
static void lock(
Packit b802ec
    FILE * f)
Packit b802ec
{
Packit b802ec
    int fd;
Packit b802ec
    struct stat buf;
Packit b802ec
    static struct flock lock;
Packit b802ec
Packit b802ec
    assert(f);
Packit b802ec
Packit b802ec
    lock.l_type = F_WRLCK;
Packit b802ec
    lock.l_start = 0;
Packit b802ec
    lock.l_whence = SEEK_END;
Packit b802ec
    lock.l_len = 0;
Packit b802ec
    lock.l_pid = getpid();
Packit b802ec
Packit b802ec
    fd = fileno(f);
Packit b802ec
    if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
Packit b802ec
        if (fcntl(fd, F_SETLKW, &lock) == -1) {
Packit b802ec
            error(0, errno, "fcntl (ignored)");
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
 * If the file stream is associated with a regular file, unlock the
Packit b802ec
 * file (which presumably has previously been locked).
Packit b802ec
 */
Packit b802ec
Packit b802ec
static void unlock(
Packit b802ec
    FILE * f)
Packit b802ec
{
Packit b802ec
    int fd;
Packit b802ec
    struct stat buf;
Packit b802ec
    static struct flock lock;
Packit b802ec
Packit b802ec
    assert(f);
Packit b802ec
Packit b802ec
    lock.l_type = F_UNLCK;
Packit b802ec
    lock.l_start = 0;
Packit b802ec
    lock.l_whence = SEEK_END;
Packit b802ec
    lock.l_len = 0;
Packit b802ec
    lock.l_pid = getpid();
Packit b802ec
Packit b802ec
    fd = fileno(f);
Packit b802ec
    if ((fstat(fd, &buf) == 0) && S_ISREG(buf.st_mode)) {
Packit b802ec
        if (fcntl(fd, F_SETLKW, &lock) == -1) {
Packit b802ec
            error(0, errno, "fcntl (ignored)");
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
Packit b802ec
static void init_fld_options(
Packit b802ec
    struct mtr_ctl *ctl)
Packit b802ec
{
Packit b802ec
    int i;
Packit b802ec
Packit b802ec
    memset(ctl->fld_index, -1, FLD_INDEX_SZ);
Packit b802ec
Packit b802ec
    for (i = 0; data_fields[i].key != 0; i++) {
Packit b802ec
        ctl->available_options[i] = data_fields[i].key;
Packit b802ec
        ctl->fld_index[data_fields[i].key] = i;
Packit b802ec
    }
Packit b802ec
    ctl->available_options[i] = 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
Packit b802ec
static void parse_arg(
Packit b802ec
    struct mtr_ctl *ctl,
Packit b802ec
    names_t ** names,
Packit b802ec
    int argc,
Packit b802ec
    char **argv)
Packit b802ec
{
Packit b802ec
    int opt;
Packit b802ec
    int i;
Packit b802ec
    /* IMPORTANT: when adding or modifying an option:
Packit b802ec
       0/ try to find a somewhat logical order;
Packit b802ec
       1/ add the long option name in "long_options" below;
Packit b802ec
       2/ update the man page (use the same order);
Packit b802ec
       3/ update the help message (see usage() function).
Packit b802ec
     */
Packit b802ec
    enum {
Packit b802ec
        OPT_DISPLAYMODE = CHAR_MAX + 1
Packit b802ec
    };
Packit b802ec
    static const struct option long_options[] = {
Packit b802ec
        /* option name, has argument, NULL, short name */
Packit b802ec
        {"help", 0, NULL, 'h'},
Packit b802ec
        {"version", 0, NULL, 'v'},
Packit b802ec
Packit b802ec
        {"inet", 0, NULL, '4'}, /* IPv4 only */
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
        {"inet6", 0, NULL, '6'},        /* IPv6 only */
Packit b802ec
#endif
Packit b802ec
        {"filename", 1, NULL, 'F'},
Packit b802ec
Packit b802ec
        {"report", 0, NULL, 'r'},
Packit b802ec
        {"report-wide", 0, NULL, 'w'},
Packit b802ec
        {"xml", 0, NULL, 'x'},
Packit b802ec
#ifdef HAVE_CURSES
Packit b802ec
        {"curses", 0, NULL, 't'},
Packit b802ec
#endif
Packit b802ec
#ifdef HAVE_GTK
Packit b802ec
        {"gtk", 0, NULL, 'g'},
Packit b802ec
#endif
Packit b802ec
        {"raw", 0, NULL, 'l'},
Packit b802ec
        {"csv", 0, NULL, 'C'},
Packit b802ec
        {"json", 0, NULL, 'j'},
Packit b802ec
        {"displaymode", 1, NULL, OPT_DISPLAYMODE},
Packit b802ec
        {"split", 0, NULL, 'p'},        /* BL */
Packit b802ec
        /* maybe above should change to -d 'x' */
Packit b802ec
Packit b802ec
        {"no-dns", 0, NULL, 'n'},
Packit b802ec
        {"show-ips", 0, NULL, 'b'},
Packit b802ec
        {"order", 1, NULL, 'o'},        /* fields to display & their order */
Packit b802ec
#ifdef HAVE_IPINFO
Packit b802ec
        {"ipinfo", 1, NULL, 'y'},       /* IP info lookup */
Packit b802ec
        {"aslookup", 0, NULL, 'z'},     /* Do AS lookup (--ipinfo 0) */
Packit b802ec
#endif
Packit b802ec
Packit b802ec
        {"interval", 1, NULL, 'i'},
Packit b802ec
        {"report-cycles", 1, NULL, 'c'},
Packit b802ec
        {"psize", 1, NULL, 's'},        /* overload psize<0, ->rand(min,max) */
Packit b802ec
        {"bitpattern", 1, NULL, 'B'},   /* overload B>255, ->rand(0,255) */
Packit b802ec
        {"tos", 1, NULL, 'Q'},  /* typeof service (0,255) */
Packit b802ec
        {"mpls", 0, NULL, 'e'},
Packit b802ec
        {"address", 1, NULL, 'a'},
Packit b802ec
        {"first-ttl", 1, NULL, 'f'},    /* -f & -m are borrowed from traceroute */
Packit b802ec
        {"max-ttl", 1, NULL, 'm'},
Packit b802ec
        {"max-unknown", 1, NULL, 'U'},
Packit b802ec
        {"udp", 0, NULL, 'u'},  /* UDP (default is ICMP) */
Packit b802ec
        {"tcp", 0, NULL, 'T'},  /* TCP (default is ICMP) */
Packit b802ec
#ifdef HAS_SCTP
Packit b802ec
        {"sctp", 0, NULL, 'S'}, /* SCTP (default is ICMP) */
Packit b802ec
#endif
Packit b802ec
        {"port", 1, NULL, 'P'}, /* target port number for TCP/SCTP/UDP */
Packit b802ec
        {"localport", 1, NULL, 'L'},    /* source port number for UDP */
Packit b802ec
        {"timeout", 1, NULL, 'Z'},      /* timeout for probe sockets */
Packit b802ec
        {"gracetime", 1, NULL, 'G'},    /* gracetime for replies after last probe */
Packit b802ec
#ifdef SO_MARK
Packit b802ec
        {"mark", 1, NULL, 'M'}, /* use SO_MARK */
Packit b802ec
#endif
Packit b802ec
        {NULL, 0, NULL, 0}
Packit b802ec
    };
Packit b802ec
    enum { num_options = sizeof(long_options) / sizeof(struct option) };
Packit b802ec
    char short_options[num_options * 2];
Packit b802ec
    size_t n, p;
Packit b802ec
Packit b802ec
    for (n = p = 0; n < num_options; n++) {
Packit b802ec
        if (CHAR_MAX < long_options[n].val) {
Packit b802ec
            continue;
Packit b802ec
        }
Packit b802ec
        short_options[p] = long_options[n].val;
Packit b802ec
        p++;
Packit b802ec
        if (long_options[n].has_arg == 1) {
Packit b802ec
            short_options[p] = ':';
Packit b802ec
            p++;
Packit b802ec
        }
Packit b802ec
        /* optional options need two ':', but ignore them now as they are not in use */
Packit b802ec
    }
Packit b802ec
Packit b802ec
    opt = 0;
Packit b802ec
    while (1) {
Packit b802ec
        opt = getopt_long(argc, argv, short_options, long_options, NULL);
Packit b802ec
        if (opt == -1)
Packit b802ec
            break;
Packit b802ec
Packit b802ec
        switch (opt) {
Packit b802ec
        case 'v':
Packit b802ec
            printf("mtr " PACKAGE_VERSION "\n");
Packit b802ec
            exit(EXIT_SUCCESS);
Packit b802ec
            break;
Packit b802ec
        case 'h':
Packit b802ec
            usage(stdout);
Packit b802ec
            break;
Packit b802ec
Packit b802ec
        case 'r':
Packit b802ec
            ctl->DisplayMode = DisplayReport;
Packit b802ec
            break;
Packit b802ec
        case 'w':
Packit b802ec
            ctl->reportwide = 1;
Packit b802ec
            ctl->DisplayMode = DisplayReport;
Packit b802ec
            break;
Packit b802ec
#ifdef HAVE_CURSES
Packit b802ec
        case 't':
Packit b802ec
            ctl->DisplayMode = DisplayCurses;
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
#ifdef HAVE_GTK
Packit b802ec
        case 'g':
Packit b802ec
            ctl->DisplayMode = DisplayGTK;
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
        case 'p':              /* BL */
Packit b802ec
            ctl->DisplayMode = DisplaySplit;
Packit b802ec
            break;
Packit b802ec
        case 'l':
Packit b802ec
            ctl->DisplayMode = DisplayRaw;
Packit b802ec
            break;
Packit b802ec
        case 'C':
Packit b802ec
            ctl->DisplayMode = DisplayCSV;
Packit b802ec
            break;
Packit b802ec
        case 'j':
Packit b802ec
            ctl->DisplayMode = DisplayJSON;
Packit b802ec
            break;
Packit b802ec
        case 'x':
Packit b802ec
            ctl->DisplayMode = DisplayXML;
Packit b802ec
            break;
Packit b802ec
Packit b802ec
        case OPT_DISPLAYMODE:
Packit b802ec
            ctl->display_mode =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if ((DisplayModeMAX - 1) < ctl->display_mode)
Packit b802ec
                error(EXIT_FAILURE, 0, "value out of range (%d - %d): %s",
Packit b802ec
                      DisplayModeDefault, (DisplayModeMAX - 1), optarg);
Packit b802ec
            break;
Packit b802ec
        case 'c':
Packit b802ec
            ctl->MaxPing =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            ctl->ForceMaxPing = 1;
Packit b802ec
            break;
Packit b802ec
        case 's':
Packit b802ec
            ctl->cpacketsize =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            break;
Packit b802ec
        case 'a':
Packit b802ec
            ctl->InterfaceAddress = optarg;
Packit b802ec
            break;
Packit b802ec
        case 'e':
Packit b802ec
            ctl->enablempls = 1;
Packit b802ec
            break;
Packit b802ec
        case 'n':
Packit b802ec
            ctl->dns = 0;
Packit b802ec
            break;
Packit b802ec
        case 'i':
Packit b802ec
            ctl->WaitTime = strtofloat_or_err(optarg, "invalid argument");
Packit b802ec
            if (ctl->WaitTime <= 0.0) {
Packit b802ec
                error(EXIT_FAILURE, 0, "wait time must be positive");
Packit b802ec
            }
Packit b802ec
            if (getuid() != 0 && ctl->WaitTime < 1.0) {
Packit b802ec
                error(EXIT_FAILURE, 0,
Packit b802ec
                      "non-root users cannot request an interval < 1.0 seconds");
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'f':
Packit b802ec
            ctl->fstTTL =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->fstTTL > ctl->maxTTL) {
Packit b802ec
                ctl->fstTTL = ctl->maxTTL;
Packit b802ec
            }
Packit b802ec
            if (ctl->fstTTL < 1) {      /* prevent 0 hop */
Packit b802ec
                ctl->fstTTL = 1;
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'F':
Packit b802ec
            read_from_file(names, optarg);
Packit b802ec
            break;
Packit b802ec
        case 'm':
Packit b802ec
            ctl->maxTTL =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->maxTTL > (MaxHost - 1)) {
Packit b802ec
                ctl->maxTTL = MaxHost - 1;
Packit b802ec
            }
Packit b802ec
            if (ctl->maxTTL < 1) {      /* prevent 0 hop */
Packit b802ec
                ctl->maxTTL = 1;
Packit b802ec
            }
Packit b802ec
            if (ctl->fstTTL > ctl->maxTTL) {    /* don't know the pos of -m or -f */
Packit b802ec
                ctl->fstTTL = ctl->maxTTL;
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'U':
Packit b802ec
            ctl->maxUnknown =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->maxUnknown < 1) {
Packit b802ec
                ctl->maxUnknown = 1;
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'o':
Packit b802ec
            /* Check option before passing it on to fld_active. */
Packit b802ec
            if (strlen(optarg) > MAXFLD) {
Packit b802ec
                error(EXIT_FAILURE, 0, "Too many fields: %s", optarg);
Packit b802ec
            }
Packit b802ec
            for (i = 0; optarg[i]; i++) {
Packit b802ec
                if (!strchr(ctl->available_options, optarg[i])) {
Packit b802ec
                    error(EXIT_FAILURE, 0, "Unknown field identifier: %c",
Packit b802ec
                          optarg[i]);
Packit b802ec
                }
Packit b802ec
            }
Packit b802ec
            xstrncpy(ctl->fld_active, optarg, 2 * MAXFLD);
Packit b802ec
            break;
Packit b802ec
        case 'B':
Packit b802ec
            ctl->bitpattern =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->bitpattern > 255)
Packit b802ec
                ctl->bitpattern = -1;
Packit b802ec
            break;
Packit b802ec
        case 'G':
Packit b802ec
            ctl->GraceTime = strtofloat_or_err(optarg, "invalid argument");
Packit b802ec
            if (ctl->GraceTime <= 0.0) {
Packit b802ec
                error(EXIT_FAILURE, 0, "wait time must be positive");
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'Q':
Packit b802ec
            ctl->tos =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->tos > 255 || ctl->tos < 0) {
Packit b802ec
                /* error message, should do more checking for valid values,
Packit b802ec
                 * details in rfc2474 */
Packit b802ec
                ctl->tos = 0;
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'u':
Packit b802ec
            if (ctl->mtrtype != IPPROTO_ICMP) {
Packit b802ec
                error(EXIT_FAILURE, 0,
Packit b802ec
                      "-u , -T and -S are mutually exclusive");
Packit b802ec
            }
Packit b802ec
            ctl->mtrtype = IPPROTO_UDP;
Packit b802ec
            break;
Packit b802ec
        case 'T':
Packit b802ec
            if (ctl->mtrtype != IPPROTO_ICMP) {
Packit b802ec
                error(EXIT_FAILURE, 0,
Packit b802ec
                      "-u , -T and -S are mutually exclusive");
Packit b802ec
            }
Packit b802ec
            if (!ctl->remoteport) {
Packit b802ec
                ctl->remoteport = 80;
Packit b802ec
            }
Packit b802ec
            ctl->mtrtype = IPPROTO_TCP;
Packit b802ec
            break;
Packit b802ec
#ifdef HAS_SCTP
Packit b802ec
        case 'S':
Packit b802ec
            if (ctl->mtrtype != IPPROTO_ICMP) {
Packit b802ec
                error(EXIT_FAILURE, 0,
Packit b802ec
                      "-u , -T and -S are mutually exclusive");
Packit b802ec
            }
Packit b802ec
            if (!ctl->remoteport) {
Packit b802ec
                ctl->remoteport = 80;
Packit b802ec
            }
Packit b802ec
            ctl->mtrtype = IPPROTO_SCTP;
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
        case 'b':
Packit b802ec
            ctl->show_ips = 1;
Packit b802ec
            break;
Packit b802ec
        case 'P':
Packit b802ec
            ctl->remoteport =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->remoteport < 1 || MaxPort < ctl->remoteport) {
Packit b802ec
                error(EXIT_FAILURE, 0, "Illegal port number: %d",
Packit b802ec
                      ctl->remoteport);
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'L':
Packit b802ec
            ctl->localport =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->localport < MinPort || MaxPort < ctl->localport) {
Packit b802ec
                error(EXIT_FAILURE, 0, "Illegal port number: %d",
Packit b802ec
                      ctl->localport);
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'Z':
Packit b802ec
            ctl->probe_timeout =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            ctl->probe_timeout *= 1000000;
Packit b802ec
            break;
Packit b802ec
        case '4':
Packit b802ec
            ctl->af = AF_INET;
Packit b802ec
            break;
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
        case '6':
Packit b802ec
            ctl->af = AF_INET6;
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
#ifdef HAVE_IPINFO
Packit b802ec
        case 'y':
Packit b802ec
            ctl->ipinfo_no =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_INT);
Packit b802ec
            if (ctl->ipinfo_no < 0 || 4 < ctl->ipinfo_no) {
Packit b802ec
                error(EXIT_FAILURE, 0, "value %d out of range (0 - 4)",
Packit b802ec
                      ctl->ipinfo_no);
Packit b802ec
            }
Packit b802ec
            break;
Packit b802ec
        case 'z':
Packit b802ec
            ctl->ipinfo_no = 0;
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
#ifdef SO_MARK
Packit b802ec
        case 'M':
Packit b802ec
            ctl->mark =
Packit b802ec
                strtonum_or_err(optarg, "invalid argument", STRTO_U32INT);
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
        default:
Packit b802ec
            usage(stderr);
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    if (ctl->DisplayMode == DisplayReport ||
Packit b802ec
        ctl->DisplayMode == DisplayTXT ||
Packit b802ec
        ctl->DisplayMode == DisplayJSON ||
Packit b802ec
        ctl->DisplayMode == DisplayXML ||
Packit b802ec
        ctl->DisplayMode == DisplayRaw || ctl->DisplayMode == DisplayCSV)
Packit b802ec
        ctl->Interactive = 0;
Packit b802ec
Packit b802ec
    if (optind > argc - 1)
Packit b802ec
        return;
Packit b802ec
Packit b802ec
}
Packit b802ec
Packit b802ec
Packit b802ec
static void parse_mtr_options(
Packit b802ec
    struct mtr_ctl *ctl,
Packit b802ec
    names_t ** names,
Packit b802ec
    char *string)
Packit b802ec
{
Packit b802ec
    int argc = 1;
Packit b802ec
    char *argv[128], *p;
Packit b802ec
Packit b802ec
    if (!string)
Packit b802ec
        return;
Packit b802ec
    argv[0] = xstrdup(PACKAGE_NAME);
Packit b802ec
    argc = 1;
Packit b802ec
    p = strtok(string, " \t");
Packit b802ec
    while (p != NULL && ((size_t) argc < (sizeof(argv) / sizeof(argv[0])))) {
Packit b802ec
        argv[argc++] = p;
Packit b802ec
        p = strtok(NULL, " \t");
Packit b802ec
    }
Packit b802ec
    if (p != NULL) {
Packit b802ec
        error(0, 0, "Warning: extra arguments ignored: %s", p);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    parse_arg(ctl, names, argc, argv);
Packit b802ec
    free(argv[0]);
Packit b802ec
    optind = 0;
Packit b802ec
}
Packit b802ec
Packit b802ec
static void init_rand(
Packit b802ec
    void)
Packit b802ec
{
Packit b802ec
    struct timeval tv;
Packit b802ec
Packit b802ec
    gettimeofday(&tv, NULL);
Packit b802ec
    srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
Packit b802ec
}
Packit b802ec
Packit b802ec
int main(
Packit b802ec
    int argc,
Packit b802ec
    char **argv)
Packit b802ec
{
Packit b802ec
    struct hostent *host = NULL;
Packit b802ec
    struct addrinfo hints, *res;
Packit b802ec
    int gai_error;
Packit b802ec
    struct hostent trhost;
Packit b802ec
    char *alptr[2];
Packit b802ec
    struct sockaddr_in *sa4;
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
    struct sockaddr_in6 *sa6;
Packit b802ec
#endif
Packit b802ec
    names_t *names_head = NULL;
Packit b802ec
    names_t *names_walk;
Packit b802ec
Packit b802ec
    struct mtr_ctl ctl;
Packit b802ec
    memset(&ctl, 0, sizeof(ctl));
Packit b802ec
    /* initialize non-null values */
Packit b802ec
    ctl.Interactive = 1;
Packit b802ec
    ctl.MaxPing = 10;
Packit b802ec
    ctl.WaitTime = 1.0;
Packit b802ec
    ctl.GraceTime = 5.0;
Packit b802ec
    ctl.dns = 1;
Packit b802ec
    ctl.use_dns = 1;
Packit b802ec
    ctl.cpacketsize = 64;
Packit b802ec
    ctl.af = DEFAULT_AF;
Packit b802ec
    ctl.mtrtype = IPPROTO_ICMP;
Packit b802ec
    ctl.fstTTL = 1;
Packit b802ec
    ctl.maxTTL = 30;
Packit b802ec
    ctl.maxUnknown = 12;
Packit b802ec
    ctl.probe_timeout = 10 * 1000000;
Packit b802ec
    ctl.ipinfo_no = -1;
Packit b802ec
    ctl.ipinfo_max = -1;
Packit b802ec
    xstrncpy(ctl.fld_active, "LS NABWV", 2 * MAXFLD);
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       mtr used to be suid root.  It should not be with this version.
Packit b802ec
       We'll check so that we can notify people using installation
Packit b802ec
       mechanisms with obsolete assumptions.
Packit b802ec
     */
Packit b802ec
    if ((geteuid() != getuid()) || (getegid() != getgid())) {
Packit b802ec
        error(EXIT_FAILURE, errno, "mtr should not run suid");
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /* This will check if stdout/stderr writing is successful */
Packit b802ec
    atexit(close_stdout);
Packit b802ec
Packit b802ec
    /* reset the random seed */
Packit b802ec
    init_rand();
Packit b802ec
Packit b802ec
    display_detect(&ctl, &argc, &argv);
Packit b802ec
    ctl.display_mode = DisplayModeDefault;
Packit b802ec
Packit b802ec
    /* The field options are now in a static array all together,
Packit b802ec
       but that requires a run-time initialization. */
Packit b802ec
    init_fld_options(&ctl;;
Packit b802ec
Packit b802ec
    parse_mtr_options(&ctl, &names_head, getenv("MTR_OPTIONS"));
Packit b802ec
Packit b802ec
    parse_arg(&ctl, &names_head, argc, argv);
Packit b802ec
Packit b802ec
    while (optind < argc) {
Packit b802ec
        char *name = argv[optind++];
Packit b802ec
        append_to_names(&names_head, name);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /* default: localhost. */
Packit b802ec
    if (!names_head)
Packit b802ec
        append_to_names(&names_head, "localhost");
Packit b802ec
Packit b802ec
    names_walk = names_head;
Packit b802ec
    while (names_walk != NULL) {
Packit b802ec
Packit b802ec
        ctl.Hostname = names_walk->name;
Packit b802ec
        if (gethostname(ctl.LocalHostname, sizeof(ctl.LocalHostname))) {
Packit b802ec
            xstrncpy(ctl.LocalHostname, "UNKNOWNHOST",
Packit b802ec
                     sizeof(ctl.LocalHostname));
Packit b802ec
        }
Packit b802ec
Packit b802ec
        /* gethostbyname2() is deprecated so we'll use getaddrinfo() instead. */
Packit b802ec
        memset(&hints, 0, sizeof hints);
Packit b802ec
        hints.ai_family = ctl.af;
Packit b802ec
        hints.ai_socktype = SOCK_DGRAM;
Packit b802ec
        gai_error = getaddrinfo(ctl.Hostname, NULL, &hints, &res;;
Packit b802ec
        if (gai_error) {
Packit b802ec
            if (gai_error == EAI_SYSTEM)
Packit b802ec
                error(0, 0, "Failed to resolve host: %s", ctl.Hostname);
Packit b802ec
            else
Packit b802ec
                error(0, 0, "Failed to resolve host: %s: %s", ctl.Hostname,
Packit b802ec
                      gai_strerror(gai_error));
Packit b802ec
Packit b802ec
            if (ctl.Interactive)
Packit b802ec
                exit(EXIT_FAILURE);
Packit b802ec
            else {
Packit b802ec
                names_walk = names_walk->next;
Packit b802ec
                continue;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
        /* Convert the first addrinfo into a hostent. */
Packit b802ec
        host = &trhost;
Packit b802ec
        memset(host, 0, sizeof trhost);
Packit b802ec
        host->h_name = res->ai_canonname;
Packit b802ec
        host->h_aliases = NULL;
Packit b802ec
        host->h_addrtype = res->ai_family;
Packit b802ec
        ctl.af = res->ai_family;
Packit b802ec
        host->h_length = res->ai_addrlen;
Packit b802ec
        host->h_addr_list = alptr;
Packit b802ec
        switch (ctl.af) {
Packit b802ec
        case AF_INET:
Packit b802ec
            sa4 = (struct sockaddr_in *) res->ai_addr;
Packit b802ec
            alptr[0] = (void *) &(sa4->sin_addr);
Packit b802ec
            break;
Packit b802ec
#ifdef ENABLE_IPV6
Packit b802ec
        case AF_INET6:
Packit b802ec
            sa6 = (struct sockaddr_in6 *) res->ai_addr;
Packit b802ec
            alptr[0] = (void *) &(sa6->sin6_addr);
Packit b802ec
            break;
Packit b802ec
#endif
Packit b802ec
        default:
Packit b802ec
            error(0, 0, "unknown address type");
Packit b802ec
            if (ctl.Interactive)
Packit b802ec
                exit(EXIT_FAILURE);
Packit b802ec
            else {
Packit b802ec
                names_walk = names_walk->next;
Packit b802ec
                continue;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
        alptr[1] = NULL;
Packit b802ec
Packit b802ec
        if (net_open(&ctl, host) != 0) {
Packit b802ec
            error(0, 0, "Unable to start net module");
Packit b802ec
            if (ctl.Interactive)
Packit b802ec
                exit(EXIT_FAILURE);
Packit b802ec
            else {
Packit b802ec
                names_walk = names_walk->next;
Packit b802ec
                continue;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
Packit b802ec
        lock(stdout);
Packit b802ec
        dns_open(&ctl;;
Packit b802ec
        display_open(&ctl;;
Packit b802ec
Packit b802ec
        display_loop(&ctl;;
Packit b802ec
Packit b802ec
        net_end_transit();
Packit b802ec
        display_close(&ctl;;
Packit b802ec
        unlock(stdout);
Packit b802ec
Packit b802ec
        if (ctl.Interactive)
Packit b802ec
            break;
Packit b802ec
        else
Packit b802ec
            names_walk = names_walk->next;
Packit b802ec
Packit b802ec
    }
Packit b802ec
Packit b802ec
    net_close();
Packit b802ec
Packit b802ec
    while (names_head != NULL) {
Packit b802ec
        names_t *item = names_head;
Packit b802ec
        free(item->name);
Packit b802ec
        item->name = NULL;
Packit b802ec
        names_head = item->next;
Packit b802ec
        free(item);
Packit b802ec
        item = NULL;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}