From 9b556a6e251ceaaa5300081d20fb6faab72673f1 Mon Sep 17 00:00:00 2001 From: Darafei Praliaskouski Date: Fri, 8 May 2026 16:38:23 +0400 Subject: [PATCH] options: accept target ports in host arguments --- man/mtr.8.in | 7 ++++++- ui/mtr.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/man/mtr.8.in b/man/mtr.8.in index 7a70065..c5d6d8a 100644 --- a/man/mtr.8.in +++ b/man/mtr.8.in @@ -121,7 +121,7 @@ mtr \- a network diagnostic tool [\c .BI \-M \ MARK\c ] -.I HOSTNAME +.IR HOSTNAME [ :PORT ] .SH DESCRIPTION .B mtr combines the functionality of the @@ -466,6 +466,11 @@ The target port number for TCP/SCTP/UDP traces. When this option is not given, TCP and SCTP traces use port 80. UDP traces encode the probe sequence number in the destination port instead, so the destination port changes for each probe. +For TCP/SCTP/UDP traces, the target can also be written as +.IR HOSTNAME : PORT . +Use +.IR [ ADDRESS ] : PORT +when specifying a port for a literal IPv6 address. .TP .B \-L \fILOCALPORT\fR, \fB\-\-localport \fILOCALPORT The source port number for UDP traces. When this option is not given, diff --git a/ui/mtr.c b/ui/mtr.c index 29f0432..33cdf75 100644 --- a/ui/mtr.c +++ b/ui/mtr.c @@ -88,13 +88,14 @@ const struct fields data_fields[MAXFLD] = { typedef struct names { char *name; + int remoteport; struct names *next; } names_t; static void __attribute__ ((__noreturn__)) usage(FILE * out) { fputs("\nUsage:\n", out); - fputs(" mtr [options] hostname\n", out); + fputs(" mtr [options] hostname[:port]\n", out); fputs("\n", out); fputs(" -F, --filename FILE read hostname(s) from a file\n", out); @@ -180,6 +181,54 @@ static void append_to_names( *name_tail = name; } +static int parse_target_port( + const char *port) +{ + int remoteport = strtoint_or_err(port, "invalid argument"); + + if (remoteport < 1 || MaxPort < remoteport) { + error(EXIT_FAILURE, 0, "Illegal port number: %d", remoteport); + } + + return remoteport; +} + +static void split_target_port( + names_t *names, + int mtrtype) +{ + if (mtrtype == IPPROTO_ICMP) { + return; + } + + while (names != NULL) { + char *port = NULL; + + if (names->name[0] == '[') { + char *close = strchr(names->name, ']'); + + if (close && close[1] == ':' && close[2] != '\0') { + port = close + 2; + *close = '\0'; + memmove(names->name, names->name + 1, close - names->name); + } + } else { + char *colon = strchr(names->name, ':'); + + if (colon && strchr(colon + 1, ':') == NULL && colon[1] != '\0') { + port = colon + 1; + *colon = '\0'; + } + } + + if (port) { + names->remoteport = parse_target_port(port); + } + + names = names->next; + } +} + static void read_from_file( names_t ** names, const char *filename) @@ -934,15 +983,20 @@ int main( if (!names_head) append_to_names(&names_head, "localhost"); + split_target_port(names_head, ctl.mtrtype); + if (!ctl.Interactive && count_names(names_head) > 1 && validate_report_targets(&ctl, names_head) != 0) { return EXIT_FAILURE; } + int default_remoteport = ctl.remoteport; names_walk = names_head; while (names_walk != NULL) { ctl.Hostname = names_walk->name; + ctl.remoteport = + names_walk->remoteport ? names_walk->remoteport : default_remoteport; if (gethostname(ctl.LocalHostname, sizeof(ctl.LocalHostname))) { xstrncpy(ctl.LocalHostname, "UNKNOWNHOST", sizeof(ctl.LocalHostname)); -- 2.47.3