|
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 |
}
|