diff -u traceroute-1.4a5/Makefile.in traceroute-1.4a5-mea/Makefile.in --- traceroute-1.4a5/Makefile.in Thu Aug 31 19:35:33 2000 +++ traceroute-1.4a5-mea/Makefile.in Thu Aug 31 19:21:36 2000 @@ -48,7 +48,7 @@ CFLAGS = $(CCOPT) $(DEFS) $(INCLS) # Standard LIBS -LIBS = @LIBS@ +LIBS = -lresolv @LIBS@ INSTALL = @INSTALL@ Only in traceroute-1.4a5-mea: Makefile.in~ Only in traceroute-1.4a5: config.cache Only in traceroute-1.4a5: config.log Common subdirectories: traceroute-1.4a5/lbl and traceroute-1.4a5-mea/lbl Common subdirectories: traceroute-1.4a5/linux-include and traceroute-1.4a5-mea/linux-include diff -u traceroute-1.4a5/traceroute.8 traceroute-1.4a5-mea/traceroute.8 --- traceroute-1.4a5/traceroute.8 Tue Apr 22 23:45:16 1997 +++ traceroute-1.4a5-mea/traceroute.8 Thu Aug 31 19:34:09 2000 @@ -15,7 +15,7 @@ .\" .\" $Header: traceroute.8,v 1.11 97/04/22 13:45:13 leres Exp $ .\" -.TH TRACEROUTE 8 "22 April 1997" +.TH TRACEROUTE 8 "31 Aug 2000" .UC 6 .SH NAME traceroute \- print the route packets take to network host @@ -23,7 +23,7 @@ .na .B traceroute [ -.B \-dFInrvx +.B \-AdFInOrvx ] [ .B \-f .I first_ttl @@ -82,6 +82,12 @@ .PP Other options are: .TP +.B \-A +Query +.I Autonomous System (AS) +numbers of each intermediate step. +This tells of routing in the network. +.TP .B \-f Set the initial time-to-live used in the first outgoing probe packet. .TP @@ -113,6 +119,10 @@ Print hop addresses numerically rather than symbolically and numerically (saves a nameserver address-to-name lookup for each gateway found on the path). +.TP +.B \-O +Print the IP-reversal SOA owner (email address) for the reversed +IP address of the returning echo. .TP .B \-p Set the base UDP port number used in probes (default is 33434). Only in traceroute-1.4a5-mea: traceroute.8~ diff -u traceroute-1.4a5/traceroute.c traceroute-1.4a5-mea/traceroute.c --- traceroute-1.4a5/traceroute.c Thu Aug 31 19:35:33 2000 +++ traceroute-1.4a5-mea/traceroute.c Thu Aug 31 19:25:36 2000 @@ -199,6 +199,7 @@ */ #include +#include #include #include #ifdef HAVE_SYS_SELECT_H @@ -216,6 +217,8 @@ #include #include +#include +#include #include #include @@ -301,6 +304,14 @@ int docksum = 1; /* calculate checksums */ #endif int optlen; /* length of ip options */ +int as_lookup; /* Look up AS path in routing registries */ +int dns_owner_lookup; + + + +#ifndef NO_SOA_RECORD +#define NO_SOA_RECORD "no SOA record" +#endif extern int optind; extern int opterr; @@ -379,9 +390,13 @@ setuid(getuid()); opterr = 0; - while ((op = getopt(argc, argv, "dFInrvxf:g:i:m:p:q:s:t:w:")) != EOF) + while ((op = getopt(argc, argv, "AdFInrvxf:g:i:m:p:Oq:s:t:w:")) != EOF) switch (op) { + case 'A': + as_lookup = 1; + break; + case 'd': options |= SO_DEBUG; break; @@ -430,6 +445,10 @@ ++nflag; break; + case 'O': + dns_owner_lookup = 1; + break; + case 'p': port = str2val(optarg, "port", 1, -1); break; @@ -787,6 +806,7 @@ print(packet, cc, from); lastaddr = from->sin_addr.s_addr; } + Printf(" %.3f ms", deltaT(&t1, &t2)); if (i == -2) { #ifndef ARCHAIC @@ -1101,6 +1121,10 @@ } +char *lookup_owner(); +char *doresolve (); +char *lookup_as(); + void print(register u_char *buf, register int cc, register struct sockaddr_in *from) { @@ -1117,6 +1141,12 @@ Printf(" %s (%s)", inetname(from->sin_addr), inet_ntoa(from->sin_addr)); + if (as_lookup) + Printf(" [%s]", lookup_as(from->sin_addr)); + + if (dns_owner_lookup) + Printf(" %s", lookup_owner(from->sin_addr)); + if (verbose) Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); } @@ -1332,7 +1362,7 @@ extern char version[]; Fprintf(stderr, "Version %s\n", version); - Fprintf(stderr, "Usage: %s [-dFInrvx] [-g gateway] [-i iface] \ + Fprintf(stderr, "Usage: %s [-AdFInOrvx] [-g gateway] [-i iface] \ [-f first_ttl] [-m max_ttl]\n\t[ -p port] [-q nqueries] [-s src_addr] [-t tos] \ [-w waittime]\n\thost [packetlen]\n", prog); @@ -1437,4 +1467,647 @@ } #endif + + + +/* + * Lookup owner of the net in DNS. + */ + + +char *lookup_owner(in) + struct in_addr in; +{ + char dns_query[100]; + char *owner, *dot_ptr; + unsigned char *addr_ptr; + + addr_ptr = (unsigned char *) (&in.s_addr); + + /* Try /24 */ + sprintf (dns_query, "%d.%d.%d.in-addr.arpa", addr_ptr[2], addr_ptr[1], addr_ptr[0]); + if (!(owner = doresolve(dns_query))) { + /* Failed, try /16 */ + sprintf (dns_query, "%d.%d.in-addr.arpa", addr_ptr[1], addr_ptr[0]); + if (!(owner = doresolve(dns_query))) { + /* Failed. If eligible try /8 */ + if (addr_ptr[0] < 128) { + sprintf (dns_query, "%d.in-addr.arpa", addr_ptr[0]); + owner = doresolve(dns_query); + } /* tried /8 for A's */ + } /* tried /16 */ + } /* tried /24 */ + + /* reformat slightly */ + if (owner == NULL) { + owner = NO_SOA_RECORD; + } else { + dot_ptr = (char *)strchr (owner, (int)'.'); + if (dot_ptr != NULL) + *dot_ptr = '@'; + + if (strlen(owner) > 0) { + dot_ptr=owner + strlen (owner) - 1; + while (*dot_ptr == ' ' || *dot_ptr == '.' ) { + *dot_ptr = 0; + dot_ptr--; + } + } + } + + return (owner); + +} + + +/* + * Lookup origin of the net in radb. + */ + +char *lookup_as(in) +struct in_addr in; +{ + static char query[100]; + static unsigned char *addr_ptr; + static char *sp; + char *get_origin(); + + addr_ptr = (unsigned char *) (&in.s_addr); + +#ifdef FORCE_NATURAL_MASK + if (addr_ptr[0] >= 192) { + sprintf (query, "%d.%d.%d.0",addr_ptr[0],addr_ptr[1],addr_ptr[2]); + } else if (addr_ptr[0] >= 128) { + sprintf (query, "%d.%d.0.0",addr_ptr[0],addr_ptr[1]); + } else { + sprintf (query, "%d.0.0.0",addr_ptr[0]); + } +#else + sprintf (query,"%d.%d.%d.%d",addr_ptr[0],addr_ptr[1],addr_ptr[2],addr_ptr[3]); +#endif /* FORCE_NATURAL_MASK */ + + return(get_origin(query)); +} + + +/* + * get_origin - Return origin (ASnnnn) given a network designation + * + * char *get_origin(char *net_designation) + * + * Returns: 0 - Error occurred, unable to get origin + * !0 Pointer to origin string + * + * Define STANDALONE to use this as a client. Also define EXAMPLE_NET for + * an example for the truly clueless... + * + * 20-May-1995 Ehud Gavron gavron@aces.com + * 28-Apr-2000 Return error if no string + */ + + +/* The following are used to determine which service at which host to + connect to. A getenv() of the following elements occurs at run-time, + which may override these values. */ + +#define RA_SERVER "whois.ra.net" +#define RA_SERVICE "whois" + +/* The following determines what fields will be returned for the -A value + (/AS_LOOKUP for VMS). This is the "origin" of the route entry in the + RADB. */ + +#define DATA_DELIMITER "origin:" + +/* Since now the RADB has multiple route objects, we will list only the + origin of the most specific one. To do so we actually have to parse + the route lines and look for the most specific route. To do so we + parse: + net.net.net.net/prefix + + and use the most specific (largest) prefix. The following determine + how we get this. */ + +#define ROUTE_DELIMITER "route:" +#define PREFIX_DELIMITER "/" + +#ifdef STANDALONE +#ifdef __vms +#include "multinet_root:[multinet.include.sys]types.h" +#include "multinet_root:[multinet.include.sys]socket.h" +#include "multinet_root:[multinet.include.netinet]in.h" +#include +#include "multinet_root:[multinet.include]netdb.h" +#define perror socket_perror +#define write socket_write +#define read socket_read +#define close socket_close +#else /* not VMS */ +#include +#include +#include +#include +#include +#endif /* VMS */ +#endif /* STANDALONE */ + +#ifndef boolean +#define boolean int +#endif + +#ifndef TRUE +#define TRUE (1==1) +#endif + +#ifndef FALSE +#define FALSE (!(TRUE)) +#endif +#define MAXREPLYLEN 8192 + +#ifdef STANDALONE +main(argc,argv) +int argc; +char **argv; +{ + char buffer[100]; + char *p; + char *get_origin(); +#ifdef EXAMPLE_NET + strcpy(buffer,"192.195.240.0/24"); +#else + strcpy(buffer,argv[1]); +#endif /* EXAMPLE_NET */ + p = get_origin(buffer); + if (p) { + strcpy(buffer,p); + Fprintf(stdout,"origin is: %s\n",buffer); + } else { + Fprintf(stderr,"unable to get origin.\n"); + } +} +#endif /* STANDALONE */ + +char *get_origin(net) +char *net; +{ + char *i,*j,*k; + char tmp[100],tmp2[100],tmp3[100]; /* store string delimiters */ + char tmp4[100]; /* here's where we store the AS */ + static char origin[100]; /* the returned route origin */ + char *rp; /* pointer to route: line */ + char *pp; /* pointer to /prefix part of route */ + int prefix; /* prefix off this line (decimal) */ + int best_prefix; /* best prefix thus far */ + int s, n, count; + char buf[256]; + boolean done; + static char reply[MAXREPLYLEN]; + struct sockaddr_in sin; + struct hostent *hp; + struct servent *sp; + char *getenv(); + + /* + * Get the IP address of the host which serves the routing arbiter + * database. We use RA_SERVER. On the offchance that someone wants + * to query another database, we check for the environment variable + * RA_SERVER to have been set. + */ + if ((i = getenv("RA_SERVER")) == 0) { + strcpy(tmp,RA_SERVER); + } else { + strncpy(tmp,i,sizeof(tmp)); + tmp[(sizeof(tmp))-1] = '\0'; /* strncpy may not null term */ + } + + hp = gethostbyname(tmp); + if (hp == NULL) { + Printf( "get_origin: localhost unknown\n"); + return(0); + } + + /* + * Create an IP-family socket on which to make the connection + */ + + s = socket(hp->h_addrtype, SOCK_STREAM, 0); + if (s < 0) { + perror("get_origin: socket"); + return(0); + } + + /* + * Get the TCP port number of the "whois" server. + * Again if this needs to be updated, the environment variable + * RA_SERVICE should be set. + */ + if ((i = getenv("RA_SERVICE")) == 0) { + strcpy(tmp,RA_SERVICE); + } else { + strncpy(tmp,i,sizeof(tmp)); + tmp[(sizeof(tmp))-1] = '\0'; /* strncpy may not null term */ + } + + sp = getservbyname(tmp,"tcp"); + if (sp == NULL) { + Printf("get_origin: getservbyname: unknown service\n"); + return(0); + } + + /* + * Create a "sockaddr_in" structure which describes the remote + * IP address we want to connect to (from gethostbyname()) and + * the remote TCP port number (from getservbyname()). + */ + + sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); + sin.sin_port = sp->s_port; + + /* + * Connect to that address... + */ + + if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + perror("get_origin: connect"); + return(0); + } + + /* + * Now send the request out to the server... + */ + + done = FALSE; + sprintf(buf,"%s\r\n",net); + write(s, buf, strlen(buf)); + + /* + * Now get the entire answer in one long buffer... + */ + count = 0; + while ((n = read(s, buf, sizeof(buf))) > 0) { + strcpy((char *)&reply[count],(char *)buf); + count += n; + } + + if (n < 0) { + perror("get_origin: read"); + return(0); + } + + reply[count] = '\0'; /* Terminate it - thanks Joey! */ + + /* + * sometimes there's no answer + */ + if (strncmp(reply, "%% No entries found for the selected source(s).", + strlen("%% No entries found for the selected source(s).")) ==0) { + return "NONE"; + } + + /* + * So now we have a large string, somewhere in which we can + * find origin:*AS%%%%%%. We parse this into AS%%%%%. + */ + + if ((i = getenv("DATA_DELIMITER")) == 0) { + strcpy(tmp,DATA_DELIMITER); + } else { + strncpy(tmp,i,sizeof(tmp)); + tmp[(sizeof(tmp))-1] = '\0'; /* strncpy may not null term */ + } + + /* TMP2 will have the route delimiter... */ + + if ((i = getenv("ROUTE_DELIMITER")) == 0) { + strcpy(tmp2,ROUTE_DELIMITER); + } else { + strncpy(tmp,i,sizeof(tmp)); + tmp[(sizeof(tmp))-1] = '\0'; /* strncpy may not null term */ + } + + if ((i = getenv("PREFIX_DELIMITER")) == 0) { + strcpy(tmp3,PREFIX_DELIMITER); + } else { + strncpy(tmp3,i,sizeof(tmp3)); + tmp3[(sizeof(tmp3))-1] = '\0'; /* strncpy may not null term */ + } + +/* + * The next while statement was put in because of SPRINTLINK's ingeneous + * Reasonable Default announcement project. They registered nets in the + * RADB of the ilk of 0.0.0.0/1, 128.0.0.0/1, 192..../2, etc... just so + * that ANS wouldn't be such a pain in the butt. + * + * For us this means instead of taking the first origin...we take the best... + */ + +/* + * Initialize it so far as we've seen no prefixes, and are still looking + * for route entries... + */ + best_prefix = 0; /* 0 bits is not very specific */ + done = FALSE; /* not done finding route: entries */ + + rp = (char *)reply; /* initialize main pointer to buffer */ + origin[0]='\0'; /* initialize returned string */ + reply[MAXREPLYLEN-1]='\0'; + + rp = (char *)strstr(rp,tmp2); /* Find route: in the string */ + while (rp != 0) { /* If there is such a thing... */ + /* find it again later */ + pp = (char *)strstr(rp,tmp3); /* Find / in the route entry */ + if (pp == 0) { /* No prefix... */ + prefix = 0; /* So we bias it out of here */ + } else { + prefix = atoi(pp+1); /* convert to decimal*/ + } + + if (prefix >= best_prefix) { /* it's equal to or better */ + i = (char *)strstr(pp,tmp); /* find origin: delimiter */ + if (i != 0) { /* it's nice if there is one */ + i += strlen(DATA_DELIMITER); /* skip delimiter... */ + i++; /* and the colon... */ + while (*i == ' ') i++; /* skip spaces */ + /* i now points to start of origin AS string */ + j = i; /* terminate... */ + while (*j >= '0') j++; + if (prefix > best_prefix) { + strcpy(origin,"/"); /* put a slash in */ + best_prefix = prefix; /* update best */ + } else { + strcat(origin,"/"); /* put a mutiple as separator*/ + } + strncpy(tmp4,i,(j-i)); /* copy new origin */ + tmp4[j-i] = '\0'; /* null terminate it */ + if (!(strstr(origin,tmp4))) { /* if it's not a dup */ + strncat(origin,i,(j-i)); /* stick it in */ + } else { + if (prefix == best_prefix) /* Otherwise remove slash */ + origin[strlen(origin)-1] = '\0'; + } /* end if not a dup */ + } /* end if origin found */ + } /* endif prefix > best_prefix */ + rp = (char *)strstr(rp+1,tmp2); /* Find route: in the string */ + } /* end while */ + /* + * Go home... + */ + close(s); + if (best_prefix != 0) { /* did we get anything? */ + return((char *)&origin[1]); /* strip off leading slash */ + } else { + return(0); + } +} + + + + +short getshort(ptr) +char *ptr; +{ + union { + short retval; + char ch[2]; + } foo; + + foo.ch[0] = (*ptr & 0xff); + foo.ch[1] = (*(ptr+1) & 0xff); + + return (foo.retval); +} + +char *doresolve (name) +char *name; +{ + int query=QUERY; + int qtype=T_SOA; + int qclass=C_IN; + unsigned char buf[256]; + char *ans; + int blen, alen, got; + int anssiz, i; + short shrt; + HEADER *h; + char *contact_ptr; + int ptr; + + anssiz = 512; + ans = (char *)malloc(anssiz); + if (!ans) { + return(0); + } + + blen = res_mkquery(query,name,qclass,qtype,NULL,0,NULL,(u_char *)buf,sizeof(buf)); + if (blen < 0) { + return (0); + } + + alen = res_send((unsigned char *)buf,blen,(unsigned char *)ans,anssiz); + if (alen == -1) { + return (0); + } + + if (alen < 12) { + return (0); + } + + h = (HEADER *)ans; + + h->id = ntohs(h->id); + h->qdcount = ntohs(h->qdcount); + h->ancount = ntohs(h->ancount); + h->nscount = ntohs(h->nscount); + h->arcount = ntohs(h->arcount); + + if (h->ancount == 0) return(0); + + ptr = 12; /* point at first question field */ + for (i=0; i< (int)h->qdcount && ptrancount && ptr