EXTRA_DIST = SECURITY mtr.8 Makefile Makefile.dist
distclean-local:
- cp Makefile.dist Makefile
+ (sleep 3; cp Makefile.dist Makefile) &
rm -f *.orig
DISTCLEANFILES = *~
#
firstrule:
- ./configure
+ ./configure
$(MAKE)
clean:
WHAT'S NEW?
+ v0.75 Feelgood patch to move sprintf to snprintf. People might think
+ that sprintf might cause a buffer overflow. Now it's clean.
+ cut-paste patches: you can now copy an intermediate host to the
+ clipboard.
v0.74 Martin Pels' patch to allow UDP probes.
KES reported a build problem. Turns out I need to install gtk-1.2
on my development sytem, otherwise my release script causes the
- Keep all packets and make the "best" and "worst" columns show the
xx-th percentile....
+ - Can the reports generated also include any secondary servers? In
+ the interactive mode, any new servers that are found in the
+ traceroute are added to the list, but it seems to only include
+ one set of servers when using the -r option.
+
+ - Being able to expand the "column width" of the hosts listed would
+ be nice, too.
+
- Bugs to fix?
AC_INIT(mtr.c)
-AM_INIT_AUTOMAKE(mtr, 0.74)
+AM_INIT_AUTOMAKE(mtr, 0.75)
AC_SUBST(GTK_OBJ)
AC_CHECK_SIZEOF(unsigned int, 4)
AC_CHECK_SIZEOF(unsigned long, 4)
+AC_CHECK_HEADERS(ncurses.h ncurses/curses.h curses.h cursesX.h sys/types.h fcntl.h)
+AC_CHECK_HEADERS(sys/xti.h)
+
# Some doc I found somewhere. :-) -- REW
# - Macro: AC_CHECK_FUNC (FUNCTION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
# - Macro: AC_CHECK_LIB (LIBRARY, FUNCTION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]])
AC_DEFINE(NO_CURSES, 1, Define if you don't have the curses libraries available.)
CURSES_OBJ=))))
-AC_CHECK_FUNCS(attron)
-
-
-AC_CHECK_HEADERS(ncurses.h ncurses/curses.h curses.h cursesX.h sys/types.h)
-AC_CHECK_HEADERS(sys/xti.h)
+AC_CHECK_FUNCS(attron fcntl)
AC_CHECK_LIB(m, floor, , AC_MSG_ERROR(No math library found))
if (type == T_A) {
dorequest(rp->hostname,type,rp->id);
if (debug) {
- sprintf(tempstring,"Resolver: Sent reverse authentication request for \"%s\".",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Sent reverse authentication request for \"%s\".",
rp->hostname);
restell(tempstring);
}
}
dorequest(tempstring,type,rp->id);
if (debug) {
- sprintf(tempstring,"Resolver: Sent domain lookup request for \"%s\".",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Sent domain lookup request for \"%s\".",
strlongip( &(rp->ip) ));
restell(tempstring);
}
rp->expiretime = sweeptime + (double)ttl;
untieresolve(rp);
if (debug) {
- sprintf(tempstring,"Resolver: Lookup successful: %s\n",rp->hostname);
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Lookup successful: %s\n",rp->hostname);
restell(tempstring);
}
}
case NOERROR:
if (hp->ancount) {
if (debug) {
- sprintf(tempstring,"Resolver: Received nameserver reply. (qd:%u an:%u ns:%u ar:%u)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Received nameserver reply. (qd:%u an:%u ns:%u ar:%u)",
hp->qdcount,hp->ancount,hp->nscount,hp->arcount);
restell(tempstring);
}
namestring[strlen(stackstring)] = '\0';
if (strcasecmp(stackstring,namestring)) {
if (debug) {
- sprintf(tempstring,"Resolver: Unknown query packet dropped. (\"%s\" does not match \"%s\")",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Unknown query packet dropped. (\"%s\" does not match \"%s\")",
stackstring,namestring);
restell(tempstring);
}
return;
}
if (debug) {
- sprintf(tempstring,"Resolver: Queried domain name: \"%s\"",namestring);
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Queried domain name: \"%s\"",namestring);
restell(tempstring);
}
c+= r;
qdatatype = sucknetword(c);
qclass = sucknetword(c);
if (qclass != C_IN) {
- sprintf(tempstring,"Resolver error: Received unsupported query class: %u (%s)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver error: Received unsupported query class: %u (%s)",
qclass,qclass < ClasstypeCount ? classtypes[qclass] :
classtypes[ClasstypeCount]);
restell(tempstring);
}
break;
default:
- sprintf(tempstring,"Resolver error: Received unimplemented query type: %u (%s)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver error: Received unimplemented query type: %u (%s)",
qdatatype,qdatatype < ResourcetypeCount ?
resourcetypes[qdatatype] : resourcetypes[ResourcetypeCount]);
restell(tempstring);
else
usefulanswer = 1;
if (debug) {
- sprintf(tempstring,"Resolver: answered domain query: \"%s\"",namestring);
+ snprintf(tempstring, sizeof(tempstring), "Resolver: answered domain query: \"%s\"",namestring);
restell(tempstring);
}
c+= r;
ttl = sucknetlong(c);
rdatalength = sucknetword(c);
if (class != qclass) {
- sprintf(tempstring,"query class: %u (%s)",qclass,qclass < ClasstypeCount ?
+ snprintf(tempstring, sizeof(tempstring), "query class: %u (%s)",qclass,qclass < ClasstypeCount ?
classtypes[qclass] : classtypes[ClasstypeCount]);
restell(tempstring);
- sprintf(tempstring,"rr class: %u (%s)",class,class < ClasstypeCount ?
+ snprintf(tempstring, sizeof(tempstring), "rr class: %u (%s)",class,class < ClasstypeCount ?
classtypes[class] : classtypes[ClasstypeCount]);
restell(tempstring);
restell("Resolver error: Answered class does not match queried class.");
}
if (datatype == qdatatype || datatype == T_CNAME) {
if (debug) {
- sprintf(tempstring,"Resolver: TTL: %s",strtdiff(sendstring,ttl));
+ snprintf(tempstring, sizeof(tempstring), "Resolver: TTL: %s",strtdiff(sendstring,ttl));
restell(tempstring);
}
if (usefulanswer)
switch (datatype) {
case T_A:
if (rdatalength != 4) {
- sprintf(tempstring,"Resolver error: Unsupported rdata format for \"A\" type. (%u bytes)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver error: Unsupported rdata format for \"A\" type. (%u bytes)",
rdatalength);
restell(tempstring);
return;
}
if ( addrcmp( (void *) &(rp->ip), (void *) c, af ) == 0 ) {
- sprintf(tempstring,"Resolver: Reverse authentication failed: %s != ",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Reverse authentication failed: %s != ",
strlongip( &(rp->ip) ));
addrcpy( (void *) &alignedip, (void *) c, af );
strcat(tempstring,strlongip( &alignedip ));
res_hostipmismatch++;
failrp(rp);
} else {
- sprintf(tempstring,"Resolver: Reverse authentication complete: %s == \"%s\".",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Reverse authentication complete: %s == \"%s\".",
strlongip( &(rp->ip) ),nonull(rp->hostname));
restell(tempstring);
res_reversesuccess++;
return;
}
if (debug) {
- sprintf(tempstring,"Resolver: Answered domain: \"%s\"",namestring);
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Answered domain: \"%s\"",namestring);
restell(tempstring);
}
if (r > HostnameLength) {
}
break;
default:
- sprintf(tempstring,"Resolver error: Received unimplemented data type: %u (%s)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver error: Received unimplemented data type: %u (%s)",
datatype,datatype < ResourcetypeCount ?
resourcetypes[datatype] : resourcetypes[ResourcetypeCount]);
restell(tempstring);
}
} else {
if (debug) {
- sprintf(tempstring,"Resolver: Ignoring resource type %u. (%s)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Ignoring resource type %u. (%s)",
datatype,datatype < ResourcetypeCount ?
resourcetypes[datatype] : resourcetypes[ResourcetypeCount]);
restell(tempstring);
failrp(rp);
break;
default:
- sprintf(tempstring,"Resolver: Received error response %u. (%s)",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Received error response %u. (%s)",
getheader_rcode(hp),getheader_rcode(hp) < ResponsecodeCount ?
responsecodes[getheader_rcode(hp)] : responsecodes[ResponsecodeCount]);
restell(tempstring);
(void *) &(from4->sin_addr), AF_INET ) == 0 )
break;
if (i == _res.nscount) {
- sprintf(tempstring,"Resolver error: Received reply from unknown source: %s",
+ snprintf(tempstring, sizeof(tempstring), "Resolver error: Received reply from unknown source: %s",
inet_ntoa(from4->sin_addr ));
restell(tempstring);
} else
parserespacket((byte *)resrecvbuf,r);
} else {
- sprintf(tempstring,"Resolver: Socket error: %s",strerror(errno));
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Socket error: %s",strerror(errno));
restell(tempstring);
}
}
case STATE_FINISHED: /* TTL has expired */
case STATE_FAILED: /* Fake TTL has expired */
if (debug) {
- sprintf(tempstring,"Resolver: Cache record for \"%s\" (%s) has expired. (state: %u) Marked for expire at: %g, time: %g.",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Cache record for \"%s\" (%s) has expired. (state: %u) Marked for expire at: %g, time: %g.",
nonull(rp->hostname), strlongip( &(rp->ip) ),
rp->state, rp->expiretime, sweeptime);
restell(tempstring);
if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED)) {
if ((rp->state == STATE_FINISHED) && (rp->hostname)) {
if (debug) {
- sprintf(tempstring,"Resolver: Used cached record: %s == \"%s\".\n",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Used cached record: %s == \"%s\".\n",
strlongip(ip),rp->hostname);
restell(tempstring);
}
return rp->hostname;
} else {
if (debug) {
- sprintf(tempstring,"Resolver: Used failed record: %s == ???\n",
+ snprintf(tempstring, sizeof(tempstring), "Resolver: Used failed record: %s == ???\n",
strlongip(ip));
restell(tempstring);
}
#endif
gint gtk_ping(gpointer data);
+gint Copy_activate(GtkWidget *widget, gpointer data);
+gint NewDestination_activate(GtkWidget *widget, gpointer data);
+gboolean ReportTreeView_clicked(GtkWidget *Tree, GdkEventButton *event);
+gchar* getSelectedHost(GtkTreePath *path);
+
extern char *Hostname;
extern int af;
static int tag;
static GtkWidget *Pause_Button;
-
+static GtkWidget *Entry;
void gtk_add_ping_timeout (void)
{
{
GtkWidget *Button;
GtkWidget *Label;
- GtkWidget *Entry;
GtkAdjustment *Adjustment;
Button = gtk_button_new_from_stock(GTK_STOCK_QUIT);
ReportTreeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ReportStore));
+ g_signal_connect(GTK_OBJECT(ReportTreeView), "button_press_event",
+ G_CALLBACK(ReportTreeView_clicked),NULL);
+
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("Hostname",
renderer,
gtk_main();
}
+gboolean NewDestination_activate(GtkWidget *widget, gpointer data)
+{
+ gchar *hostname;
+ GtkTreePath *path = (GtkTreePath*)data;
+
+ hostname = getSelectedHost(path);
+ if (hostname) {
+ gtk_entry_set_text (GTK_ENTRY(Entry), hostname);
+ Host_activate(Entry, NULL);
+ g_free(hostname);
+ }
+ return TRUE;
+}
+
+
+gboolean Copy_activate(GtkWidget *widget, gpointer data)
+{
+ gchar *hostname;
+ GtkTreePath *path = (GtkTreePath*)data;
+
+ hostname = getSelectedHost(path);
+ if (hostname != NULL) {
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(clipboard, hostname, -1);
+
+ clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+ gtk_clipboard_set_text(clipboard, hostname, -1);
+
+ g_free(hostname);
+ }
+
+ return TRUE;
+}
+
+gchar *getSelectedHost(GtkTreePath *path)
+{
+ GtkTreeIter iter;
+ gchar *name = NULL;
+
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ReportStore), &iter, path)) {
+ gtk_tree_model_get (GTK_TREE_MODEL(ReportStore), &iter, COL_HOSTNAME, &name, -1);
+ }
+ gtk_tree_path_free(path);
+ return name;
+}
+
+
+gboolean ReportTreeView_clicked(GtkWidget *Tree, GdkEventButton *event)
+{
+ GtkWidget* popup_menu;
+ GtkWidget* copy_item;
+ GtkWidget* newdestination_item;
+ GtkTreePath *path;
+
+ if (event->type != GDK_BUTTON_PRESS || event->button != 3)
+ return FALSE;
+
+ if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(ReportTreeView),
+ event->x, event->y, &path, NULL, NULL, NULL))
+ return FALSE;
+
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(ReportTreeView), path, NULL, FALSE);
+
+ // Single right click: prepare and show the popup menu
+ popup_menu = gtk_menu_new ();
+
+ copy_item = gtk_menu_item_new_with_label ("Copy to clipboard");
+ newdestination_item = gtk_menu_item_new_with_label ("Set as new destination");
+
+ gtk_menu_append (GTK_MENU (popup_menu), copy_item);
+ gtk_menu_append (GTK_MENU (popup_menu), newdestination_item);
+
+ g_signal_connect(GTK_OBJECT(copy_item),"activate",
+ GTK_SIGNAL_FUNC(Copy_activate), path);
+
+ g_signal_connect(GTK_OBJECT(newdestination_item),"activate",
+ GTK_SIGNAL_FUNC(NewDestination_activate), path);
+
+ gtk_widget_show (copy_item);
+ gtk_widget_show (newdestination_item);
+
+ gtk_menu_popup (GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
+ 0, event->time);
+ return TRUE;
+}
.B \-\-report\c
]
[\c
+.B \-\-report-wide\c
+]
+[\c
.B \-\-report\-cycles\ COUNT\c
]
[\c
to measure the quality of your network may result in decreased
network performance.
+.TP
+.B \-w
+.TP
+.B \-\-report-wide
+.br
+This option puts
+.B mtr
+into
+.B wide report
+mode. When in this mode,
+.B mtr
+will not cut hostnames in the report.
+
.TP
.B \-c\ COUNT
.TP
int cpacketsize = 64; /* default packet size */
int bitpattern = 0;
int tos = 0;
+int reportwide = 0;
int af = DEFAULT_AF;
int mtrtype = IPPROTO_ICMP; /* Use ICMP as default packet type */
{ "help", 0, 0, 'h' },
{ "report", 0, 0, 'r' },
+ { "report-wide", 0, 0, 'w' },
{ "xml", 0, 0, 'x' },
{ "curses", 0, 0, 't' },
{ "gtk", 0, 0, 'g' },
while(1) {
/* added f:m:o: byMin */
opt = getopt_long(argc, argv,
- "vhrxtglpo:i:c:s:b:Q:na:f:m:u46", long_options, NULL);
+ "vhrwxtglpo:i:c:s:b:Q:na:f:m:u46", long_options, NULL);
if(opt == -1)
break;
case 'r':
DisplayMode = DisplayReport;
break;
+ case 'w':
+ reportwide = 1;
+ break;
case 't':
DisplayMode = DisplayCurses;
break;
}
if (PrintHelp) {
- printf("usage: %s [-hvrctglspniu46] [--help] [--version] [--report]\n"
- "\t\t[--report-cycles=COUNT] [--curses] [--gtk]\n"
+ printf("usage: %s [-hvrwctglspniu46] [--help] [--version] [--report]\n"
+ "\t\t[--report-wide] [--report-cycles=COUNT] [--curses] [--gtk]\n"
"\t\t[--raw] [--split] [--no-dns] [--address interface]\n" /* BL */
"\t\t[--psize=bytes/-s bytes]\n" /* ok */
"\t\t[--interval=SECONDS] HOSTNAME [PACKETSIZE]\n", argv[0]);
bzero( &hints, sizeof hints );
hints.ai_family = af;
hints.ai_socktype = SOCK_DGRAM;
- error = getaddrinfo( Hostname, "0", &hints, &res );
+ error = getaddrinfo( Hostname, NULL, &hints, &res );
if ( error ) {
perror( gai_strerror(error) );
exit( EXIT_FAILURE );
/* Typedefs */
-#if 0
-
-// Neat trick! However, on my system, "config.h" already defines these types
-// so I get a compiler error if I leave this in. -- REW
-
/* Find the proper type for 8 bits */
#if SIZEOF_UNSIGNED_CHAR == 1
typedef unsigned char uint8;
#error No 32 bit type
#endif
-#endif
-
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
#include <netinet/in.h>
#include <memory.h>
#include <unistd.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
return 0;
}
+static void set_fd_flags(int fd)
+{
+#if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
+ int oldflags;
+
+ oldflags = fcntl(fd, F_GETFD);
+ if (oldflags == -1) {
+ perror("Couldn't get fd's flags");
+ return;
+ }
+ if (fcntl(fd, F_SETFD, oldflags | FD_CLOEXEC))
+ perror("Couldn't set fd's flags");
+#endif
+}
int net_preopen(void)
{
recvsock4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (recvsock4 < 0)
return -1;
+ set_fd_flags(recvsock4);
#ifdef ENABLE_IPV6
recvsock6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
#endif
+ set_fd_flags(recvsock6);
return 0;
}
sendsock6 = sendsock6_udp;
break;
}
- if (sendsock6 < 0)
+ if ((sendsock6 < 0) && (sendsock4 < 0))
return -1;
#endif
extern int tos;
extern int MaxPing;
extern int af;
+extern int reportwide;
void report_open(void)
char buf[1024];
char fmt[16];
int len=0;
+ int len_hosts = 33;
struct hostent *host;
- sprintf(buf, "HOST: %-33s", LocalHostname);
+ if (reportwide)
+ {
+ // get the longest hostname
+ len_hosts = strlen(LocalHostname);
+ max = net_max();
+ at = net_min();
+ for (; at < max; at++) {
+ addr = net_addr(at);
+ if( addrcmp( (void *) addr, (void *) &unspec_addr, af ) != 0 ) {
+ host = dns ? addr2host( (void *) addr, af ) : NULL;
+ if (host != NULL) {
+ strncpy( name, host->h_name, (sizeof name) - 1 );
+ name[ (sizeof name) - 1 ] = '\0';
+ } else {
+ snprintf(name, sizeof(name), "%s", strlongip( addr ) );
+ }
+ if (len_hosts < strlen(name)) {
+ len_hosts = strlen(name);
+ }
+ }
+ }
+ }
+
+ snprintf( fmt, sizeof(fmt), "HOST: %%-%ds", len_hosts);
+ snprintf(buf, sizeof(buf), fmt, LocalHostname);
+ len = reportwide ? strlen(buf) : len_hosts;
for( i=0; i<MAXFLD; i++ ) {
j = fld_index[fld_active[i]];
if (j < 0) continue;
- sprintf( fmt, "%%%ds", data_fields[j].length );
- sprintf( buf +33+ len, fmt, data_fields[j].title );
+ snprintf( fmt, sizeof(fmt), "%%%ds", data_fields[j].length );
+ snprintf( buf + len, sizeof(buf), fmt, data_fields[j].title );
len += data_fields[j].length;
}
printf("%s\n",buf);
host = dns ? addr2host( (void *) addr, af ) : NULL;
if (host != NULL) {
- strncpy( name, host->h_name, (sizeof name) - 1 );
- name[ (sizeof name) - 1 ] = '\0';
+ strncpy( name, host->h_name, (sizeof name) - 1 );
+ name[ (sizeof name) - 1 ] = '\0';
} else {
- sprintf(name, "%s", strlongip( addr ) );
+ snprintf(name, sizeof(name), "%s", strlongip( addr ) );
}
}
- len=0;
- sprintf( buf, " %2d. %-33s", at+1, name);
+ snprintf( fmt, sizeof(fmt), " %%2d. %%-%ds", len_hosts);
+ snprintf(buf, sizeof(buf), fmt, at+1, name);
+ len = reportwide ? strlen(buf) : len_hosts;
for( i=0; i<MAXFLD; i++ ) {
j = fld_index[fld_active [i]];
if (j < 0) continue;
/* 1000.0 is a temporay hack for stats usec to ms, impacted net_loss. */
if( index( data_fields[j].format, 'f' ) ) {
- sprintf( buf +33+ len, data_fields[j].format,
+ snprintf( buf + len, sizeof(buf), data_fields[j].format,
data_fields[j].net_xxx(at) /1000.0 );
} else {
- sprintf( buf +33+ len, data_fields[j].format,
+ snprintf( buf + len, sizeof(buf), data_fields[j].format,
data_fields[j].net_xxx(at) );
}
len += data_fields[j].length;