From 149e6a3e967a16c2d631605df5ec762477cc6ae4 Mon Sep 17 00:00:00 2001 From: fyodor Date: Thu, 15 Mar 2007 17:59:35 +0000 Subject: [PATCH] check in traceroute.cc and traceroute.h --- traceroute.cc | 1482 +++++++++++++++++++++++++++++++++++++++++++++++++ traceroute.h | 340 ++++++++++++ 2 files changed, 1822 insertions(+) create mode 100644 traceroute.cc create mode 100644 traceroute.h diff --git a/traceroute.cc b/traceroute.cc new file mode 100644 index 000000000..759336322 --- /dev/null +++ b/traceroute.cc @@ -0,0 +1,1482 @@ +/*************************************************************************** + * Traceroute.cc -- Parallel multi-protocol traceroute feature * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2004 Insecure.Com LLC. Nmap * + * is also a registered trademark of Insecure.Com LLC. This program is * + * free software; you may redistribute and/or modify it under the * + * terms of the GNU General Public License as published by the Free * + * Software Foundation; Version 2. This guarantees your right to use, * + * modify, and redistribute this software under certain conditions. If * + * you wish to embed Nmap technology into proprietary software, we may be * + * willing to sell alternative licenses (contact sales@insecure.com). * + * Many security scanner vendors already license Nmap technology such as * + * our remote OS fingerprinting database and code, service/version * + * detection system, and port scanning code. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://www.insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to many * + * security vendors, and generally include a perpetual license as well as * + * providing for priority support and updates as well as helping to fund * + * the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + ***************************************************************************/ + + +/* + * Written by Eddie Bell as part of SoC2006 + * A multi-protocol parallel traceroute implementation for nmap. + * + * For more information on how traceroutes work: + * http://en.wikipedia.org/wiki/Traceroute + * + * Traceroute takes in a list of scanned targets and determines a valid + * responsive port to trace to based on the scan results, scan protocol and + * various pieces of protocol data. + * + * Nmap first sends a probe to the target port, from the reply traceroute is able + * to infer how many hops away the target is. Nmap starts the trace by sending + * a packet with a TTL equal to that of the hop distance guess. If it gets an + * ICMP_TTL_EXCCEDED message back it know the hop distance guess was under so + * nmap will continue sending probes with incremental TTLs until it receives a + * reply from the target host. + * + * Once a reply from the host is received nmap sets the TTL to one below the + * hop guess and continues to send probes with decremental TTLs until it reaches + * TTL 0. Then we have a complete trace to the target. If nmap does not get a + * hop distance probe reply, the trace TTL starts at one and is incremented + * until it hits the target host. + * + * Forwards/Backwards tracing example + * hop guess:20 + * send:20 --> ICMP_TTL_EXCCEDED + * send:21 --> ICMP_TTL_EXCCEDED + * send:22 --> Reply from host + * send:19 --> ICMP_TTL_EXCCEDED + * .... + * send:1 --> ICMP_TTL_EXECCEDED + * + * The forward/backwards tracing method seems a little convoluted at first but + * there is a reason for it. The first host traced in a Target group is + * designated as the reference trace. All other traces + * (once they have reached their destination host) are compared against the + * reference trace. If a match is found the trace is ended prematurely and the + * remaining hops are assumed to be the same as the reference trace. This + * normally only happens in the lower TTls, which rarely change. On average nmap + * sends 5 less packets per host. If nmap is tracing related hosts + * (EG. 1.2.3.0/24) it will send a lot less packets. Depending on the network + * topology it may only have to send a single packet to each host. + * + * Nmap's traceroute employs a dynamic timing model similar to nmap's scanning engine + * but a little more light weight. It keeps track of sent, received and dropped + * packet, then adjusts timing parameters accordingly. The parameters are; number of + * retransmissions, delay between each sent packet and the amount of time to wait + * for a reply. This are initially based on the timing level (-T0 to -T5). + * Traceroute also has to watch out for rate-limiting of ICMP TTL EXCCEDED + * messages, sometimes there is nothing we can do and just have to settle with a + * timedout hop. + * + * The output from each trace is consolidated to save space, XML logging and debug + * mode ignore consolidation. There are two type of consolidation time-out and + * reference trace. + * + * Timed out + * 1 ... Hop 1 timed out + * 1 ... 6 Hop 1 to 6 timed out + * + * Reference trace + * 1 -- Hop 1 has been taken from the reference trace + * 1 --> 6 Hop 1 to 6 have been taken from the reference trace + * + * Traceroute does not work with connect scans or idle scans and has trouble + * with ICMP_TIMESTAMP and ICMP_ADDRESSMASK scans because so many host filter + * them out. The quickest seems to be SYN scan. + * + * Bugs + * ---- + * o The code, currently, only works with ipv4. + * o Should send both UDP and TCP hop distance probes no matter what the + * scan protocol + */ + +#include "traceroute.h" +#include "NmapOps.h" +#include "NmapOutputTable.h" +#include "nmap_tty.h" +#include "nmap_dns.h" +#include "osscan2.h" +#include +#include + +using namespace std; +extern NmapOps o; + +static void enforce_scan_delay (struct timeval *, int); +static char *hostStr (u32 ip); + +/* Each target group has a single reference trace. All + * other traces are compared to it and if a match is + * found the trace is ended prematurely and the + * remaining hops are assumed to match the reference + * trace */ +unsigned long commonPath[MAX_TTL + 1]; + +Traceroute::Traceroute (const char *device_name, devtype type) { + fd = -1; + ethsd = NULL; + hops = NULL; + total_size = 0; + + /* open various socks to send and read from on windows and + * unix */ + if ((o.sendpref & PACKET_SEND_ETH) && type == devt_ethernet) { + /* We'll send ethernet packets with dnet */ + ethsd = eth_open_cached (device_name); + if (ethsd == NULL) + fatal ("dnet: Failed to open device %s", device_name); + } else { + if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + pfatal ("Traceroute: socket troubles"); + broadcast_socket (fd); +#ifndef WIN32 + sethdrinclude (fd); +#endif + } + + /* rely on each group using the same device */ + pd = my_pcap_open_live (device_name, 100, o.spoofsource ? 1 : 0, 2); + SPM = new ScanProgressMeter ("Traceroute"); + + scaninfo.initial_proto = IPPROTO_IP; + scaninfo.open_response = 0; + scaninfo.open_state = PORT_OPEN; + scaninfo.closed_state = PORT_CLOSED; + + /* Set up which protocols, tcp flags and responsive + * states to use with the current scan type. + * + * Horribly messy but it is better then peppering + * the nmap source code with references to traceroute */ + if (o.synscan) { + scaninfo.scan_flags = TH_SYN; + scaninfo.open_response = TH_SYN | TH_ACK; + scaninfo.closed_response = TH_RST; + } else if (o.ackscan) { + scaninfo.scan_flags = TH_ACK; + scaninfo.open_response = TH_RST; + scaninfo.closed_response = TH_RST; + scaninfo.open_state = PORT_UNFILTERED; + scaninfo.closed_state = PORT_UNFILTERED; + } else if (o.finscan) { + scaninfo.scan_flags = TH_FIN; + scaninfo.closed_response = TH_RST; + } else if (o.xmasscan) { + scaninfo.scan_flags = TH_FIN | TH_URG | TH_PUSH; + scaninfo.closed_response = TH_RST; + } else if (o.nullscan) { + scaninfo.scan_flags = 0; + scaninfo.closed_response = TH_RST; + } else if (o.windowscan) { + scaninfo.scan_flags = TH_ACK; + scaninfo.open_response = TH_RST; + scaninfo.closed_response = TH_RST; + } else if (o.maimonscan) { + scaninfo.scan_flags = TH_FIN | TH_ACK; + scaninfo.open_response = TH_RST; + scaninfo.closed_response = TH_RST; + } + + if (o.udpscan) + scaninfo.initial_proto = IPPROTO_UDP; + if (o.synscan || o.finscan || o.xmasscan || o.nullscan || + o.ackscan || o.windowscan || o.maimonscan) + scaninfo.initial_proto = IPPROTO_TCP; + + if(o.pingscan) { + scaninfo.open_state = HOST_UP; + if (o.pingtype & PINGTYPE_TCP_USE_SYN) { + scaninfo.scan_flags = TH_SYN; + scaninfo.open_response = TH_SYN | TH_ACK; + scaninfo.closed_response = TH_RST; + scaninfo.initial_proto = IPPROTO_TCP; + } else if (o.pingtype & PINGTYPE_TCP_USE_ACK) { + scaninfo.scan_flags = TH_ACK; + scaninfo.open_response = TH_RST; + scaninfo.closed_response = TH_RST; + scaninfo.initial_proto = IPPROTO_TCP; + } else if (o.pingtype & PINGTYPE_UDP) { + scaninfo.initial_proto = IPPROTO_UDP; + } else if (o.pingtype & PINGTYPE_ICMP_PING) { + scaninfo.initial_proto = IPPROTO_ICMP; + scaninfo.icmp_type = ICMP_ECHO; + } else if (o.pingtype & PINGTYPE_ICMP_TS) { + scaninfo.initial_proto = IPPROTO_ICMP; + scaninfo.icmp_type = ICMP_TIMESTAMP; + } else if(o.pingtype & PINGTYPE_ICMP_MASK) { + scaninfo.initial_proto = IPPROTO_ICMP; + scaninfo.icmp_type = ICMP_ADDRESS; + } + } + + if (o.scanflags != -1) + scaninfo.scan_flags = o.scanflags; + memset (commonPath, 0, sizeof (commonPath)); +} + +Traceroute::~Traceroute () { + map < u32, TraceGroup * >::iterator it = TraceGroups.begin (); + while ((--total_size) >= 0) + delete(hops[total_size]); + if(hops) + free(hops); + for (; it != TraceGroups.end (); ++it) + delete (it->second); + assert(SPM != NULL); + delete (SPM); + if (ethsd) + ethsd = NULL; + close (fd); + pcap_close (pd); +} + +/* get an open or closed port from the portlist. Traceroute requires a positive response, + * positive responses are generated by different port states depending on the type of scan */ +inline int +Traceroute::getTracePort (u8 proto, Target * t) { + u16 open_port = 1; + u16 closed_port = 1; + u16 filtered_port = 1; + u16 state = 0; + u16 port = 0; + + /* Use the first specified port for ping traceroutes */ + if (o.pingscan) { + if (o.pingtype & PINGTYPE_TCP_USE_SYN) + return o.ping_synprobes[0]; + else if (o.pingtype & PINGTYPE_TCP_USE_ACK) + return o.ping_ackprobes[0]; + else if (o.pingtype & PINGTYPE_UDP) + return o.ping_udpprobes[0]; + else + return 0; + } + + if (proto == IPPROTO_TCP) { + /* can't use filtered ports for tcp */ + filtered_port = 0; + open_port = (!scaninfo.open_response) ? 0 : 1; + } + + /* First we try to find an open port, if not we try to find a closed + * port and lastly we try to find a filtered port */ + if (open_port && t->ports.getStateCounts (proto, scaninfo.open_state)) + state = scaninfo.open_state; + else if (closed_port && t->ports.getStateCounts (proto, scaninfo.closed_state)) + state = scaninfo.closed_state; + else if (filtered_port && t->ports.getStateCounts (proto, PORT_FILTERED)) { + state = PORT_FILTERED; + if (o.verbose) + log_write (LOG_PLAIN, "%s: only filtered %s available, results may be incorrect\n", + t->targetipstr (), o.ipprotscan ? "protocols" : "ports"); + } else { + return -1; + } + + port = t->ports.nextPort (NULL, proto, state)->portno; + + /* If this is a protocol scan traceroute and we are using + * one of the major protocols, set up the required information + * so we include the correct protocol headers */ + if (proto == IPPROTO_IP) { + if (port == IPPROTO_TCP) { + scaninfo.initial_proto = IPPROTO_TCP; + scaninfo.scan_flags = TH_ACK; + scaninfo.open_response = TH_RST; + scaninfo.closed_response = TH_RST; + } else if (port == IPPROTO_UDP) { + scaninfo.initial_proto = IPPROTO_UDP; + } else if (port == IPPROTO_ICMP) { + scaninfo.initial_proto = IPPROTO_ICMP; + scaninfo.icmp_type = ICMP_ECHO; + } + } + return port; +} + +/* finite state machine that reads all incoming packets + * and attempts to match them with sent probes */ +inline bool +Traceroute::readTraceResponses () { + struct ip *ip = NULL; + struct ip *ip2 = NULL; + struct icmp *icmp = NULL; + struct icmp *icmp2 = NULL; + struct tcp_hdr *tcp = NULL; + struct udp_hdr *udp = NULL; + struct link_header linkhdr; + unsigned int bytes; + struct timeval rcvdtime; + TraceProbe *tp = NULL; + TraceGroup *tg = NULL; + u16 sport; + u32 ipaddr; + + /* Got to look into readip_pcap's timeout value, perhaps make it dynamic */ + ip = (struct ip *) readip_pcap (pd, &bytes, 10000, &rcvdtime, &linkhdr); + + if (ip == NULL) + return finished (ip); + if ((unsigned) ip->ip_hl * 4 + 20 > bytes) + return finished (ip); + + switch (ip->ip_p) { + case IPPROTO_ICMP: + icmp = (struct icmp *) ((char *) ip + 4 * ip->ip_hl); + ipaddr = ip->ip_src.s_addr; + sport = ntohs(icmp->icmp_id); + + /* Process ICMP replies that encapsulate our original probe */ + if (icmp->icmp_type == ICMP_DEST_UNREACH || icmp->icmp_type == ICMP_TIME_EXCEEDED) { + ip2 = (struct ip *) (((char *) ip) + 4 * ip->ip_hl + 8); + if (ip2->ip_p == IPPROTO_TCP) { + tcp = (struct tcp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + sport = htons (tcp->th_sport); + } else if (ip2->ip_p == IPPROTO_UDP) { + udp = (struct udp_hdr *) ((u8 *) ip2 + ip2->ip_hl * 4); + sport = htons (udp->uh_sport); + } else if (ip2->ip_p == IPPROTO_ICMP) { + icmp2 = (struct icmp *) ((char *) ip2 + 4 * ip2->ip_hl); + sport = ntohs(icmp2->icmp_id); + } else { + sport = htons(ip2->ip_id); + } + ipaddr = ip2->ip_dst.s_addr; + } + + if (TraceGroups.find (ipaddr) != TraceGroups.end ()) + tg = TraceGroups[ipaddr]; + else + break; + + if (tg->TraceProbes.find (sport) != tg->TraceProbes.end ()) + tp = tg->TraceProbes[sport]; + else + break; + + if (tp->ipreplysrc.s_addr) + break; + + if ((tg->proto == IPPROTO_UDP && ip2->ip_p == IPPROTO_UDP) || + (icmp->icmp_type == ICMP_DEST_UNREACH)) { + switch (icmp->icmp_code) { + /* reply from a closed port */ + case ICMP_PORT_UNREACH: + /* replies from a filtered port */ + case ICMP_HOST_UNREACH: + case ICMP_PROT_UNREACH: + case ICMP_NET_ANO: + case ICMP_HOST_ANO: + case ICMP_PKT_FILTERED: + if (tp->probeType () == PROBE_TTL) { + tg->setHopDistance (o.ttl - ip2->ip_ttl, 0); + tg->start_ttl = tg->ttl = --tg->hopDistance; + } else { + tg->gotReply = true; + if (tg->start_ttl < tg->ttl) + tg->ttl = tg->start_ttl + 1; + } + } + } + /* icmp ping scan replies */ + else if (tg->proto == IPPROTO_ICMP && (icmp->icmp_type == ICMP_ECHOREPLY || + icmp->icmp_type == ICMP_ADDRESSREPLY || icmp->icmp_type == ICMP_TIMESTAMPREPLY)) { + if (tp->probeType () == PROBE_TTL) { + tg->setHopDistance (get_initial_ttl_guess (ip->ip_ttl), ip->ip_ttl); + tg->start_ttl = tg->ttl = --tg->hopDistance; + } else { + tg->gotReply = true; + if (tg->start_ttl < tg->ttl) + tg->ttl = tg->start_ttl + 1; + } + } + + if (tp->timing.getState () == P_TIMEDOUT) + tp->timing.setState (P_OK); + else + tg->decRemaining (); + + tg->repliedPackets++; + tg->consecTimeouts = 0; + tp->timing.adjustTimeouts (&rcvdtime, tg->scanDelay); + tp->ipreplysrc.s_addr = ip->ip_src.s_addr; + + /* check to see if this hop is in the referece trace. If + * it is then we stop tracing this target and assume + * all subsequent hops match the common path */ + if (commonPath[tp->ttl] == tp->ipreplysrc.s_addr && + tp->ttl > 1 && tg->gotReply && tg->getState () != G_FINISH) { + tg->setState (G_FINISH); + tg->consolidation_start = tp->ttl+1; + break; + } else if (commonPath[tp->ttl] == 0) + commonPath[tp->ttl] = tp->ipreplysrc.s_addr; + + break; + case IPPROTO_TCP: + if ((unsigned) ip->ip_hl * 4 + 20 > bytes) + break; + + tcp = (struct tcp_hdr *) ((char *) ip + 4 * ip->ip_hl); + + if (TraceGroups.find (ip->ip_src.s_addr) != TraceGroups.end ()) + tg = TraceGroups[ip->ip_src.s_addr]; + else + break; + + if (tg->TraceProbes.find (htons (tcp->th_dport)) != tg->TraceProbes.end ()) + tp = tg->TraceProbes[htons (tcp->th_dport)]; + else + break; + + /* already got the tcp packet for this group, + * could be a left over rst or syn-ack */ + if (tp->ipreplysrc.s_addr) + break; + + /* We have reached the destination host and the + * trace can stop for this target */ + if (tcp->th_flags & scaninfo.open_response || tcp->th_flags & scaninfo.closed_response) { + + /* We might have got a late reply */ + if (tp->timing.getState () == P_TIMEDOUT) + tp->timing.setState (P_OK); + else + tg->decRemaining (); + + tp->timing.recvTime = rcvdtime; + tp->ipreplysrc = ip->ip_src; + tg->repliedPackets++; + /* The probe was the reply from a ttl guess */ + if (tp->probeType () == PROBE_TTL) { + tg->setHopDistance (get_initial_ttl_guess (ip->ip_ttl), ip->ip_ttl); + tg->start_ttl = tg->ttl = --tg->hopDistance; + } else { + tg->gotReply = true; + if (tg->start_ttl < tg->ttl) + tg->ttl = tg->start_ttl + 1; + } + } + break; + case IPPROTO_UDP: + if ((unsigned) ip->ip_hl * 4 + 8 > bytes) + break; + udp = (udp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + + if (TraceGroups.find (ip->ip_src.s_addr) != TraceGroups.end ()) + tg = TraceGroups[ip->ip_src.s_addr]; + else + break; + + if (tg->TraceProbes.find (htons (udp->uh_dport)) != tg->TraceProbes.end ()) + tp = tg->TraceProbes[htons (udp->uh_dport)]; + else + break; + + if (tp->ipreplysrc.s_addr) + break; + + /* We might have got a late reply */ + if (tp->timing.getState () == P_TIMEDOUT) + tp->timing.setState (P_OK); + else + tg->decRemaining (); + + tp->timing.recvTime = rcvdtime; + tp->ipreplysrc.s_addr = ip->ip_src.s_addr; + tg->repliedPackets++; + + if (tp->probeType () == PROBE_TTL) { + tg->setHopDistance (get_initial_ttl_guess (ip->ip_ttl), ip->ip_ttl); + tg->setState (G_OK); + tg->start_ttl = tg->ttl = --tg->hopDistance; + } else { + tg->gotReply = true; + if (tg->start_ttl < tg->ttl) + tg->ttl = tg->start_ttl + 1; + } + break; + default: + ; + } + return finished (ip); +} + +/* Estimate how many hops away a host is by actively probing it. + * + * If the scan protocol isn't udp we guesstimate how many hops away + * the target is by sending a probe to an open or closed port and + * calculating a possible hop distance based on the returned ttl + * + * If the scan protocol is udp then we send a probe to a closed, + * filtered or open port. Closed ports are more accurate because + * we can exactly determine the hop distance based on the packet + * return in the icmp port unreachable's payload. Open ports use + * the same estimation method as tcp probes. Filtered ports are + * only used as a last resort, although the hop distance guess is + * accurate, the filtered response may not be from the destination + * target, it may be from a node filtering the target */ +inline void +Traceroute::sendTTLProbes (vector < Target * >&Targets, vector < Target * >&valid_targets) { + Target *t = NULL; + long dport = 0; + u16 sport = 0; + u8 proto; + TraceProbe *tp; + TraceGroup *tg = NULL; + vector < Target * >::iterator it = Targets.begin (); + + for (; it != Targets.end (); ++it) { + t = *it; + proto = scaninfo.initial_proto; + + /* No point in tracing directly connected nodes */ + if (t->directlyConnected ()) + continue; + + /* This node has already been sent a hop distance probe */ + if (TraceGroups.find (t->v4hostip ()->s_addr) != TraceGroups.end ()) { + valid_targets.push_back (t); + continue; + } + + /* Determine active port to probe */ + if ((dport = getTracePort (proto, t)) == -1) { + /* If we could not find a responsive tcp port then try + * to find a responsive udp port */ + if (o.udpscan && proto != IPPROTO_UDP) { + proto = IPPROTO_UDP; + dport = getTracePort (proto, t); + } + } + + if (dport == -1) { + if (o.verbose > 1) + log_write (LOG_STDOUT, "%s: no responsive %s\n", + t->targetipstr (), o.ipprotscan ? "protocols" : "ports"); + continue; + } + + /* If this is a protocol scan getTracePort() returns + * a protocol number for so we need a random destination + * port */ + if (o.ipprotscan) { + proto = dport; + dport = get_random_u16 (); + scaninfo.initial_proto = IPPROTO_IP; + } + + /* start of with a random source port and increment + * it for each probes sent. The source port is the + * distinguishing value used to identify each probe */ + sport = get_random_u16 (); + tg = new TraceGroup (t->v4hostip ()->s_addr, sport, dport, proto); + tg->src_mac_addr = t->SrcMACAddress (); + tg->nxt_mac_addr = t->NextHopMACAddress (); + tg->sport++; + TraceGroups[tg->ipdst] = tg; + + /* OS fingerprint engine may already has the distance so + * we don't need to calculate it */ + if (t->distance != -1) { + tg->setHopDistance (0, t->distance); + } else { + tp = new TraceProbe (proto, t->v4hostip ()->s_addr, + t->v4sourceip ()->s_addr, sport, dport); + tp->setProbeType (PROBE_TTL); + tp->ttl = o.ttl; + tg->TraceProbes[sport] = tp; + tg->incRemaining (); + sendProbe (tp); + } + valid_targets.push_back (t); + } +} + +/* Send a single traceprobe object */ +int +Traceroute::sendProbe (TraceProbe * tp) { + u8 *tcpopts = NULL; + int tcpoptslen = 0; + u32 ack = 0; + u8 *packet = NULL; + u32 packetlen = 0; + TraceGroup *tg = NULL; + int decoy = 0; + struct in_addr source; + struct eth_nfo eth; + struct eth_nfo *ethptr = NULL; + + if (scaninfo.scan_flags & TH_ACK) + ack = rand (); + if (scaninfo.scan_flags & TH_SYN) { + tcpopts = (u8 *) "\x02\x04\x05\xb4"; + tcpoptslen = 4; + } + + if (TraceGroups.find (tp->ipdst.s_addr) == TraceGroups.end ()) + return -1; + tg = TraceGroups[tp->ipdst.s_addr]; + + /* required to send raw packets in windows */ + if (ethsd) { + memcpy (eth.srcmac, tg->src_mac_addr, 6); + memcpy (eth.dstmac, tg->nxt_mac_addr, 6); + eth.ethsd = ethsd; + eth.devname[0] = '\0'; + ethptr = ð + } + + if (tg->TraceProbes.find (tp->sport) == tg->TraceProbes.end ()) { + tg->nextTTL (); + if (!tg->ttl || tg->gotReply && tg->noDistProbe) { + tg->setState (G_FINISH); + return 0; + } + tg->sport++; + tp->ttl = tg->ttl; + tp->dport = tg->dport; + tg->incRemaining (); + } else { + /* this probe is a retransmission */ + tp->timing.setState (P_OK); + } + + if (tg->TraceProbes.size () >= MAX_TTL) + tg->setState (G_TTL); + tg->TraceProbes[tp->sport] = tp; + + for (decoy = 0; decoy < o.numdecoys; decoy++) { + enforce_scan_delay (&tp->timing.sendTime, tg->scanDelay); + + if (decoy == o.decoyturn) + source = tp->ipsrc; + else + source = o.decoys[decoy]; + + switch (tp->proto) { + case IPPROTO_TCP: + packet = build_tcp_raw (&source, &tp->ipdst, tp->ttl, get_random_u16 (), + get_random_u8 (), 0, NULL, 0, tp->sport, tp->dport, + get_random_u32 (), ack, 0, scaninfo.scan_flags, + get_random_u16 (), 0, NULL, 0, + o.extra_payload, o.extra_payload_length, &packetlen); + break; + case IPPROTO_UDP: + packet = build_udp_raw (&source, &tp->ipdst, tp->ttl, get_random_u16 (), + get_random_u8 (), false, + NULL, 0, tp->sport, + tp->dport, o.extra_payload, o.extra_payload_length, &packetlen); + break; + case IPPROTO_ICMP: + packet = build_icmp_raw (&source, &tp->ipdst, tp->ttl, 0, 0, false, + NULL, 0, get_random_u16 (), tp->sport, scaninfo.icmp_type, 0, + o.extra_payload, o.extra_payload_length, &packetlen); + break; + default: + packet = build_ip_raw (&source, &tp->ipdst, tp->proto, tp->ttl, tp->sport, + get_random_u8 (), false, NULL, 0, o.extra_payload, + o.extra_payload_length, &packetlen); + } + send_ip_packet (fd, ethptr, packet, packetlen); + free (packet); + } + return 0; +} + +/* true if all groups have finished or failed */ +bool +Traceroute::finished (struct ip *ips) { + map < u32, TraceGroup * >::iterator it = TraceGroups.begin (); + for (; it != TraceGroups.end (); ++it) { + if (it->second->getState () == G_OK || it->second->getRemaining ()) + return false; + } + if(ips) + free(ips); + return true; +} + +/* Main parallel send and recv loop */ +void +Traceroute::trace (vector < Target * >&Targets) { + map < u32, TraceGroup * >::iterator it; + vector < Target * >::iterator targ; + vector < Target * >valid_targets; + vector < Target * >reference; + vector < TraceProbe * >retrans_probes; + vector < TraceGroup * >::size_type pcount; + TraceProbe *tp = NULL; + TraceGroup *tg = NULL; + Target *t = NULL; + u16 total_size, total_complete; + + if (o.af () == AF_INET6) { + error ("Traceroute does not support ipv6\n"); + return; + } + + /* perform the reference trace first */ + if (Targets.size () > 1) { + o.current_scantype = TRACEROUTE; + for (targ = Targets.begin (); targ != Targets.end (); ++targ) { + reference.push_back (*targ); + sendTTLProbes (reference, valid_targets); + if (valid_targets.size ()) { + o.current_scantype = REF_TRACEROUTE; + this->trace (valid_targets); + o.current_scantype = TRACEROUTE; + break; + } + } + } + + /* guess hop distance to targets. valid_targets + * is populated with all Target object that are + * legitimate to trace to */ + sendTTLProbes (Targets, valid_targets); + + while (!readTraceResponses ()) { + for (targ = valid_targets.begin (); targ != valid_targets.end (); ++targ) { + t = *targ; + tg = TraceGroups[t->v4host ().s_addr]; + + /* Check for any timedout probes and + * retransmit them. If too many probes + * are outstanding we wait for replies or + * timeouts before sending any more */ + if (tg->getRemaining ()) { + tg->retransmissions (retrans_probes); + for (pcount = 0; pcount < retrans_probes.size (); pcount++) + sendProbe (retrans_probes[pcount]); + retrans_probes.clear (); + /* Max number of packets outstanding is 2 if we don't have a reply yet + * otherwise it is equal to o.timing_level. If the timing level it 0 + * it is equal to 1 */ + if (tg->getRemaining () >= + tg->gotReply ? (!o.timing_level ? 1 : o.timing_level) : 2) + continue; + } + if (tg->getState () != G_OK || !tg->hopDistance) + continue; + + tp = new TraceProbe (tg->proto, t->v4hostip ()->s_addr, + t->v4sourceip ()->s_addr, tg->sport, 0); + sendProbe (tp); + } + + if (!keyWasPressed ()) + continue; + + total_size = total_complete = 0; + for (it = TraceGroups.begin (); it != TraceGroups.end (); ++it) { + total_complete += it->second->size (); + total_size += it->second->hopDistance; + } + + if (!total_size) + continue; + + if (total_size < total_complete) + swap (total_complete, total_size); + SPM->printStats (MIN ((double) total_complete / total_size, 0.99), NULL); + } + } + +/* Resolves traceroute hops through nmaps + * parallel caching rdns infrastructure. + * The argument should be NULL and needs + * freeing after the hostnames are finished + * with + * + * N.B TraceProbes contain pointers into the Target + * argument, if it is free'ed prematurely something + * nasty will happen */ +void Traceroute::resolveHops () { + map::iterator tg_iter; + map::iterator tp_iter; + int count = 0; + struct sockaddr_storage ss; + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + + if(o.noresolve) + return; + + assert(hops == NULL); + + memset(&ss, '\0', sizeof(ss)); + sin->sin_family = o.af(); + + for(tg_iter = TraceGroups.begin(); tg_iter != TraceGroups.end(); ++tg_iter) + total_size += tg_iter->second->size(); + if(!total_size) + return; + hops = (Target **) safe_zalloc(sizeof(Target *) * total_size); + + /* Move hop IP address to Target structures and point TraceProbes to + * Targets hostname */ + for(tg_iter = TraceGroups.begin(); tg_iter != TraceGroups.end(); ++tg_iter) { + tp_iter = tg_iter->second->TraceProbes.begin(); + for(; tp_iter != tg_iter->second->TraceProbes.end(); ++tp_iter) { + if(tp_iter->second->ipreplysrc.s_addr && tp_iter->second->probeType() != PROBE_TTL) { + sin->sin_addr = tp_iter->second->ipreplysrc; + hops[count] = new Target(); + hops[count]->setTargetSockAddr(&ss, sizeof(ss)); + hops[count]->flags = HOST_UP; + tp_iter->second->hostname = &hops[count]->hostname; + count++; + } + } + } + /* resolve all hops in this group at onces */ + nmap_mass_rdns(hops, count); +} + +/* print a trace in plain text format */ +void +Traceroute::outputTarget (Target * t) { + map < u16, TraceProbe * >::const_iterator it; + map < u16, TraceProbe * >::size_type ttl_count; + map < u16, TraceProbe * >sortedProbes; + TraceProbe *tp = NULL; + TraceGroup *tg = NULL; + NmapOutputTable *Tbl = NULL; + + struct protoent *proto; + bool last_consolidation = false; + bool common_consolidation = false; + char row_count = 0; + char timebuf[16]; + u8 consol_count = 0; + u16 size; + + if ((TraceGroups.find (t->v4host ().s_addr)) == TraceGroups.end ()) + return; + tg = TraceGroups[t->v4host ().s_addr]; + + /* sort into ttl order */ + for (it = tg->TraceProbes.begin (); it != tg->TraceProbes.end (); ++it) + sortedProbes[it->second->ttl] = it->second; + sortedProbes.swap (tg->TraceProbes); + + /* clean up and consolidate traces */ + tg->consolidateHops (); + /* calculate length of table, post-consolidation */ + size = tg->tableSize (); + this->outputXMLTrace(tg); + + /* table headers */ + Tbl = new NmapOutputTable (size, 3); + Tbl->addItem (row_count, HOP_COL, false, "HOP", 3); + Tbl->addItem (row_count, RTT_COL, false, "RTT", 3); + Tbl->addItem (row_count, HOST_COL, false, "ADDRESS", 7); + + for (ttl_count = 1; ttl_count <= tg->hopDistance; ttl_count++) { + + if (row_count >= size-1) + break; + + /* consolidate hops based on the reference trace (commonPath) */ + if(commonPath[ttl_count] && ttl_count <= tg->consolidation_start) { + /* do not consolidate in debug mode */ + if(o.debugging) { + row_count++; + Tbl->addItemFormatted(row_count, HOP_COL, false, "%d", ttl_count); + Tbl->addItemFormatted(row_count, RTT_COL, false, "--"); + Tbl->addItemFormatted(row_count, HOST_COL, false, "%s", hostStr(commonPath[ttl_count])); + } else if(!common_consolidation) { + row_count++; + Tbl->addItemFormatted(row_count, HOP_COL, false, "%d", ttl_count); + common_consolidation = true; + } + } + + /* If we cannot find a traceprobe we are probably still consolidating */ + if ((it = tg->TraceProbes.find (ttl_count)) == tg->TraceProbes.end ()) + continue; + /* Here we consolidate the probe that first matched the common path */ + if (ttl_count <= tg->consolidation_start) + continue; + + tp = tg->TraceProbes[ttl_count]; + + /* end of reference trace consolidation */ + if(common_consolidation) { + if(ttl_count-1 == 1) { + Tbl->addItemFormatted(row_count, RTT_COL, false, "--", ttl_count-1); + Tbl->addItemFormatted(row_count, HOST_COL,false, "%s", hostStr(commonPath[ttl_count-1])); + } else + Tbl->addItemFormatted(row_count, RTT_COL, false, "--> %d", ttl_count-1); + common_consolidation = false; + } + + row_count++; + + /* timeout consolidation */ + if(tp->timing.consolidated) { + consol_count++; + if(!last_consolidation) { + last_consolidation = true; + Tbl->addItemFormatted(row_count, HOP_COL, false, "%d", tp->ttl); + } + row_count--; + } else if(!tp->timing.consolidated && last_consolidation) { + if(consol_count>1) + Tbl->addItemFormatted(row_count, RTT_COL, false, "... %d", tp->ttl-1); + else + Tbl->addItemFormatted(row_count, RTT_COL, false, "..."); + row_count++; + last_consolidation = false; + consol_count = 0; + } + + /* normal hop output (rtt, ip and hostname) */ + if (!tp->timing.consolidated && !last_consolidation) { + snprintf(timebuf, 16, "%.2f", (float) + TIMEVAL_SUBTRACT (tp->timing.recvTime, tp->timing.sendTime) / 1000); + Tbl->addItemFormatted (row_count, HOP_COL, false, "%d", tp->ttl); + if (tp->timing.getState () != P_TIMEDOUT) { + Tbl->addItem (row_count, RTT_COL, true, timebuf); + Tbl->addItem (row_count, HOST_COL, true, tp->nameIP ()); + } else + Tbl->addItemFormatted (row_count, RTT_COL, false, "..."); + } + + } + + if(tg->getState() == G_TTL) + Tbl->addItem (row_count, RTT_COL, false, "... 50"); + + /* Traceroute header and footer */ + proto = nmap_getprotbynum(htons(tg->proto)); + if(o.ipprotscan || (o.pingscan && !(o.pingtype & PINGTYPE_TCP || o.pingtype & PINGTYPE_UDP))) + log_write(LOG_PLAIN, "\nTRACEROUTE (using port %d/%s)\n", tg->proto, proto?proto->p_name:"unknown"); + else + log_write(LOG_PLAIN, "\nTRACEROUTE (using port %d/%s)\n", tg->dport, proto2ascii(tg->proto)); + log_write (LOG_PLAIN, "%s", Tbl->printableTable(NULL)); + + if(tg->getState() == G_TTL) + log_write(LOG_PLAIN, "! maximum TTL reached (50)\n"); + else if(!tg->gotReply || (tp && (tp->ipreplysrc.s_addr != tg->ipdst))) + log_write(LOG_PLAIN, "! destination not reached (%s)\n", inet_ntoa(tp->ipdst)); + + log_flush (LOG_PLAIN); + delete Tbl; +} + +/* print a trace in xml */ +void +Traceroute::outputXMLTrace(TraceGroup * tg) { + map < u16, TraceProbe * >::const_iterator it; + TraceProbe *tp = NULL; + const char *hostname_tmp = NULL; + struct protoent *proto; + struct in_addr addr; + long timediff; + short ttl_count; + + /* XML traceroute header */ + log_write(LOG_XML, "dport); + if((proto = nmap_getprotbynum(htons(tg->proto)))) + log_write(LOG_XML, "proto=\"%s\"", proto->p_name); + else + log_write(LOG_XML, "proto=\"%d\"", tg->proto); + log_write(LOG_XML, ">\n"); + + /* add missing hosts host from the common path */ + for(ttl_count = 1 ; ttl_count < tg->TraceProbes.begin()->second->ttl; ttl_count++) { + addr.s_addr = commonPath[ttl_count]; + log_write(LOG_XML, "\n"); + } + + /* display normal traceroute nodes. Consolidation based on the + * common path is not performed */ + for(it = tg->TraceProbes.begin() ;it != tg->TraceProbes.end(); it++) { + tp = it->second; + + if(tp->probeType() == PROBE_TTL) + break; + + log_write(LOG_XML, "ttl); + + if(tp->timing.getState() == P_TIMEDOUT) { + log_write(LOG_XML, "/>\n"); + continue; + } + + timediff = TIMEVAL_SUBTRACT (tp->timing.recvTime, tp->timing.sendTime); + log_write(LOG_XML, " rtt=\"%.2f\" ipaddr=\"%s\"", + (float)timediff/1000, tp->ipReplyStr()); + if(tp->HostName() != NULL) + log_write(LOG_XML, " host=\"%s\"", tp->HostName()); + log_write(LOG_XML, "/>\n"); + } + + if(tg->getState() == G_TTL) + log_write(LOG_XML, "\n"); + else if(!tg->gotReply || (tp && (tp->ipreplysrc.s_addr != tg->ipdst))) + log_write(LOG_XML, "\n", inet_ntoa(tp->ipdst)); + + /* traceroute XML footer */ + log_write(LOG_XML, "\n"); + log_flush(LOG_XML); + +} + +TraceGroup::TraceGroup (u32 dip, u16 sport, u16 dport, u8 proto) { + this->ipdst = dip; + this->dport = dport; + this->sport = sport; + this->proto = proto; + ttl = 0; + state = G_OK; + remaining = 0; + hopDistance = 0; + start_ttl = 0; + TraceProbes.clear (); + gotReply = false; + noDistProbe = false; + scanDelay = o.scan_delay ? o.scan_delay : 0; + maxRetransmissions = (o.getMaxRetransmissions () < 2) ? 2 : o.getMaxRetransmissions () / 2; + droppedPackets = 0; + repliedPackets = 0; + consecTimeouts = 0; + consolidation_start = 0; +} + +TraceGroup::~TraceGroup () { + map < u16, TraceProbe * >::const_iterator it = TraceProbes.begin (); + for (; it != TraceProbes.end (); ++it) + delete (it->second); +} + +/* go through all probes in a group and check if any have timedout. + * If too many packets have been dropped then the groups scan delay + * is increased */ +void +TraceGroup::retransmissions (vector < TraceProbe * >&retrans) { + map < u16, TraceProbe * >::iterator it; + u32 timediff; + struct timeval now; + double threshold = (o.timing_level >= 4) ? 0.40 : 0.30; + + for (it = TraceProbes.begin (); it != TraceProbes.end (); ++it) { + if (it->second->timing.gotReply () || it->second->timing.getState () == P_TIMEDOUT) + continue; + + gettimeofday (&now, NULL); + timediff = TIMEVAL_SUBTRACT (now, it->second->timing.sendTime); + + if (timediff < it->second->timing.probeTimeout ()) + continue; + + if (it->second->timing.retranLimit () >= maxRetransmissions) { + /* this probe has timedout */ + it->second->timing.setState (P_TIMEDOUT); + decRemaining (); + + if ((++consecTimeouts) > 5 && maxRetransmissions > 2) + maxRetransmissions = 2; + if (it->second->probeType () == PROBE_TTL) { + hopDistance = 1; + noDistProbe = true; + if (o.verbose) + log_write (LOG_STDOUT, "%s: no reply to our hop distance probe!\n", IPStr ()); + } + } else { + droppedPackets++; + it->second->timing.setState (P_RETRANS); + retrans.push_back (it->second); + } + + /* Calculate dynamic timing adjustments */ + if (repliedPackets > droppedPackets / 5) + maxRetransmissions = (maxRetransmissions == 2) ? 2 : maxRetransmissions - 1; + else + maxRetransmissions = MIN (o.getMaxRetransmissions (), maxRetransmissions + 1); + + if (droppedPackets > 10 && (droppedPackets / + ((double) droppedPackets + repliedPackets) > threshold)) { + if (!scanDelay) + scanDelay = (proto == IPPROTO_TCP) ? 5 : 50; + else + scanDelay = MIN (scanDelay * 2, MAX (scanDelay, 800)); + droppedPackets = 0; + repliedPackets = 0; + } else { + scanDelay = MAX (scanDelay - (scanDelay / 5), 5); + } + } +} + +/* nmap's traceroute output can be consolidated, timeouts + * and hops from the common path are collapsed to save space. + * This function calculates the number lines the trace will + * occupy after all consolidation. If debug mode is active + * no consolidation will be applied */ +u8 TraceGroup::tableSize () { + map < u16, TraceProbe * >::iterator it; + TraceProbe *last_probe = NULL; + u8 pathCount = 255; + u8 size = this->size (); + + /* count timeouts and common path elements */ + for (it = TraceProbes.begin (); it != TraceProbes.end (); ++it) { + pathCount = MIN (pathCount, it->second->ttl); + if(last_probe && last_probe->timing.consolidated && it->second->timing.consolidated) + size--; + last_probe = it->second; + } + + /* If consolidation has been performed, we + * need to adjust the table size to reflect + * the consolidated nodes. */ + if(pathCount > 1 && !o.debugging) + size--; + + /* no consolidation in debug mode */ + if(o.debugging) + return size + pathCount; + /* trace has been consolidated based on commonpath */ + if(pathCount > 1) + return size; + /* no common path elements have been used */ + return size; +} + +/* Remove uneeded probes and mark timed out probes for consolidation */ +void TraceGroup::consolidateHops () { + map < u16, TraceProbe * >::size_type ttl_count; + map < u16, u32 >::iterator com_iter; + TraceProbe *tp; + int timeout_count = 0; + + /* remove any superfluous probes */ + for (ttl_count = hopDistance + 1; ttl_count <= TraceProbes.size () + 1; ttl_count++) + TraceProbes.erase (ttl_count); + + for (ttl_count = 1; ttl_count <= hopDistance; ttl_count++) { + tp = TraceProbes[ttl_count]; + if(!tp) { + TraceProbes.erase (ttl_count); + continue; + } + + /* timeout consolidation flags, ignore if in debugging more */ + if (tp->timing.getState () != P_TIMEDOUT) { + timeout_count = 0; + } else { + if (++timeout_count > 1 && !o.debugging) { + TraceProbes[(ttl_count == 1) ? 1 : ttl_count - 1]->timing.consolidated = true; + TraceProbes[(ttl_count == 1) ? 1 : ttl_count]->timing.consolidated = true; + } + } + + if (tp->ipreplysrc.s_addr == ipdst) + break; + } + + /* we may have accidently shot past the intended destination */ + while (ttl_count <= hopDistance) + TraceProbes.erase (++ttl_count); +} + +u8 +TraceGroup::setState (u8 state) { + if (state <= G_FINISH || state >= G_OK) + this->state = state; + else if (o.debugging) + log_write (LOG_STDOUT, "%s: invalid tracegroup state %d\n", IPStr (), state); + return this->state; +} + +u8 +TraceGroup::setHopDistance (u8 hop_distance, u8 ttl) { + if (this->hopDistance) + return 0; + + this->hopDistance = hop_distance; + + if (hopDistance && ttl) { + this->hopDistance -= ttl; + /* Hop distance seems too small */ + if (this->hopDistance <= 2) + this->hopDistance = hop_distance; + } + + if (!this->hopDistance && ttl) + this->hopDistance = ttl; + + if (o.verbose) + log_write (LOG_STDOUT, "%s: guessing hop distance at %d\n", IPStr (), this->hopDistance); + + if (this->hopDistance >= MAX_TTL - 1) + this->hopDistance = MAX_TTL - 1; + return this->hopDistance; +} + +TraceProbe::TraceProbe (u8 proto, u32 dip, u32 sip, u16 sport, u16 dport) { + this->proto = proto; + this->sport = sport; + this->dport = dport; + ipdst.s_addr = dip; + ipsrc.s_addr = sip; + ipreplysrc.s_addr = 0; + hostnameip = NULL; + hostname = NULL; + probetype = PROBE_TRACE; +} + +TraceProbe::~TraceProbe () { + if (hostnameip) + free (hostnameip); +} + +const char *TraceProbe::nameIP(void) { + if((hostnameip = (char *) safe_zalloc(NAMEIPLEN)) == NULL) { + perror("traceroute malloc()"); + return NULL; + } + + if(hostname == NULL || *hostname == NULL) + snprintf(hostnameip, NAMEIPLEN, "%s", inet_ntoa(ipreplysrc)); + else + snprintf(hostnameip, NAMEIPLEN, "%s (%s)",*hostname, inet_ntoa(ipreplysrc)); + + return hostnameip; +} + +TimeInfo::TimeInfo () { + memset (&sendTime, 0, sizeof (struct timeval)); + memset (&recvTime, 0, sizeof (struct timeval)); + retransmissions = 0; + state = P_OK; + consolidated = false; + initialize_timeout_info (&to); +} + +u8 +TimeInfo::setState (u8 state) { + if (state <= P_OK) + this->state = state; + else if (o.debugging) + log_write (LOG_STDOUT, ": invalid traceprobe state %d\n", state); + return state; +} + +int +TimeInfo::retranLimit () { + return ++this->retransmissions; +} + +void +TimeInfo::adjustTimeouts (struct timeval *received, u16 scan_delay) { + long delta = 0; + + if (received) + recvTime = *received; + + if (o.debugging > 3) { + log_write (LOG_STDOUT, "Timeout vals: srtt: %d rttvar: %d to: %d ", to.srtt, to.rttvar, + to.timeout); + } + + delta = TIMEVAL_SUBTRACT (*received, sendTime); + + /* Argh ... pcap receive time is sometimes a little off my + getimeofday() results on various platforms :(. So a packet may + appear to be received as much as a hundredth of a second before + it was sent. So I will allow small negative RTT numbers */ + if (delta < 0 && delta > -50000) { + if (o.debugging > 2) + log_write (LOG_STDOUT, "Small negative delta - adjusting from %lius to %dus\n", delta, + 10000); + delta = 10000; + } + + + if (to.srtt == -1 && to.rttvar == -1) { + /* We need to initialize the sucker ... */ + to.srtt = delta; + to.rttvar = MAX (5000, MIN (to.srtt, 2000000)); + to.timeout = to.srtt + (to.rttvar << 2); + } else { + if (delta >= 8000000 || delta < 0) { + if (o.verbose) + error + ("adjust_timeout: packet supposedly had rtt of %lu microseconds. Ignoring time.", + delta); + return; + } + delta -= to.srtt; + /* sanity check 2 */ + if (delta > 1500000 && delta > 3 * to.srtt + 2 * to.rttvar) { + if (o.debugging) + log_write (LOG_STDOUT, "Bogus delta: %ld (srtt %d) ... ignoring\n", delta, to.srtt); + return; + } + + to.srtt += delta >> 3; + to.rttvar += (ABS (delta) - to.rttvar) >> 2; + to.timeout = to.srtt + (to.rttvar << 2); + } + + if (to.rttvar > 2300000) { + log_write (LOG_STDOUT, "RTTVAR has grown to over 2.3 seconds, decreasing to 2.0\n"); + to.rttvar = 2000000; + to.rttvar = 2000000; + } + + /* It hurts to do this ... it really does ... but otherwise we are being + too risky */ + to.timeout = box (o.minRttTimeout () * 1000, o.maxRttTimeout () * 1000, to.timeout); + + if (scan_delay) + to.timeout = MAX (to.timeout, scan_delay * 1000); + + if (o.debugging > 3) { + log_write (LOG_STDOUT, "delta %ld ==> srtt: %d rttvar: %d to: %d\n", + delta, to.srtt, to.rttvar, to.timeout); + } +} + +/* Sleeps if necessary to ensure that it isn't called twice within less + * time than send_delay. If it is passed a non-null tv, the POST-SLEEP + * time is recorded in it */ +static void +enforce_scan_delay (struct timeval *tv, int scan_delay) { + static int init = -1; + static struct timeval lastcall; + struct timeval now; + int time_diff; + + if (!scan_delay) { + if (tv) + gettimeofday (tv, NULL); + return; + } + + if (init == -1) { + gettimeofday (&lastcall, NULL); + init = 0; + if (tv) + memcpy (tv, &lastcall, sizeof (struct timeval)); + return; + } + + gettimeofday (&now, NULL); + time_diff = TIMEVAL_MSEC_SUBTRACT (now, lastcall); + if (time_diff < (int) scan_delay) { + if (o.debugging > 2) + log_write (LOG_STDOUT, "Sleeping for %d milliseconds in enforce_scan_delay()\n", + scan_delay - time_diff); + usleep ((scan_delay - time_diff) * 1000); + gettimeofday (&lastcall, NULL); + } else + memcpy (&lastcall, &now, sizeof (struct timeval)); + if (tv) { + memcpy (tv, &lastcall, sizeof (struct timeval)); + } + return; +} + +static char * +hostStr (u32 ip) { + static char nameipbuf[MAXHOSTNAMELEN + INET6_ADDRSTRLEN] = { '0' }; + const char *hname; + struct in_addr addr; + + memset (nameipbuf, '\0', MAXHOSTNAMELEN + INET6_ADDRSTRLEN); + addr.s_addr = ip; + if((hname = lookup_cached_host(ip)) == "") + snprintf(nameipbuf, MAXHOSTNAMELEN+INET6_ADDRSTRLEN, "%s", inet_ntoa(addr)); + else + snprintf (nameipbuf, MAXHOSTNAMELEN + INET6_ADDRSTRLEN, "%s (%s)", hname, inet_ntoa (addr)); + return nameipbuf; +} diff --git a/traceroute.h b/traceroute.h new file mode 100644 index 000000000..7b7ac59d8 --- /dev/null +++ b/traceroute.h @@ -0,0 +1,340 @@ +/*************************************************************************** + * Traceroute.h -- Traces the route a packet takes to a host * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2004 Insecure.Com LLC. Nmap * + * is also a registered trademark of Insecure.Com LLC. This program is * + * free software; you may redistribute and/or modify it under the * + * terms of the GNU General Public License as published by the Free * + * Software Foundation; Version 2. This guarantees your right to use, * + * modify, and redistribute this software under certain conditions. If * + * you wish to embed Nmap technology into proprietary software, we may be * + * willing to sell alternative licenses (contact sales@insecure.com). * + * Many security scanner vendors already license Nmap technology such as * + * our remote OS fingerprinting database and code, service/version * + * detection system, and port scanning code. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://www.insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to many * + * security vendors, and generally include a perpetual license as well as * + * providing for priority support and updates as well as helping to fund * + * the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + *************************************************************************** + * + * Eddie Bell + * See Traceroute.cc for an indepth explanation + */ + +#include "Target.h" + +/* Probe types */ +#define PROBE_TRACE 0 +#define PROBE_TTL 1 + +/* Probe states */ +#define P_TIMEDOUT 0 /* probe has timedout */ +#define P_RETRANS 1 /* probe needs to be retransmitted */ +#define P_OK 2 /* waiting for response or received response */ + +/* Group states */ +#define G_OK P_OK +#define G_TTL 3 /* TTL has reached MAX_TTL */ +#define G_FINISH 4 /* tracing has complete successfully */ + +#define MAX_TTL 50 + +#define LOG_PLAIN LOG_NORMAL|LOG_SKID|LOG_STDOUT +#define HOP_COL 0 +#define RTT_COL 1 +#define HOST_COL 2 + +#define NAMEIPLEN MAXHOSTNAMELEN+INET6_ADDRSTRLEN + +#ifdef WIN32 + #define ICMP_ECHOREPLY 0 + #define ICMP_DEST_UNREACH 3 + #define ICMP_ECHO 8 + #define ICMP_TIME_EXCEEDED 11 + #define ICMP_TIMESTAMP 13 + #define ICMP_TIMESTAMPREPLY 14 + #define ICMP_ADDRESS 17 + #define ICMP_ADDRESSREPLY 18 + #define ICMP_HOST_UNREACH 1 + #define ICMP_PROT_UNREACH 2 + #define ICMP_PORT_UNREACH 3 + #define ICMP_NET_ANO 9 + #define ICMP_HOST_ANO 10 + #define ICMP_PKT_FILTERED 13 +#endif + +/* various pieces of scan data used by + * traceroute to find responsive ports + * and match probes */ +struct scan_info { + u8 initial_proto; + u8 icmp_type; + u8 scan_flags; + u8 open_response; + u8 open_state; + u8 closed_response; + u8 closed_state; +}; + +/* Keeps track of each probes timing state */ +class TimeInfo { + public: + TimeInfo (); + int retranLimit (); + void adjustTimeouts (struct timeval *recv, u16 scan_delay); + + unsigned long probeTimeout () { return MIN (10000000, to.timeout * 10); } + /* true if this probe has been replied to */ + u8 gotReply () { return (recvTime.tv_usec != 0 && recvTime.tv_sec != 0); } + u8 getState () { return state; } + u8 setState (u8 state); + + struct timeout_info to; + /* set to true if this probe is going to + * consolidated because it has timed out */ + bool consolidated; + + /* Rtt and timeout calculation */ + struct timeval recvTime; + struct timeval sendTime; + + private: + u8 retransmissions; + u8 state; +}; + +/* traceprobes represent a single packet at a specific + * ttl. Traceprobes are stored inside tracegroups. */ +class TraceProbe { + public: + TraceProbe (u8 proto, u32 dip, u32 sip, u16 sport, u16 dport); + ~TraceProbe (); + + /* Return the ip address and resolved hostname in a string + * EG + * host.com (1.2.3.4) + * Or + * 6.6.6.6 + */ + const char *nameIP (); + const char *HostName () + { if(!hostname || !(*hostname)) + return NULL; + else + return *hostname; + } + /* probe type is either a standard probe (PROBE_TRACE) or + * a hop distance probe (PROBE_TTL) */ + void setProbeType (u8 type) { this->probetype = type; } + u8 probeType () { return probetype; } + char *ipReplyStr () { return inet_ntoa (ipreplysrc); } + + /* protocol information for this probe */ + TimeInfo timing; + struct in_addr ipdst; + struct in_addr ipsrc; + struct in_addr ipreplysrc; + u16 sport; + u16 dport; + u8 proto; + u8 ttl; + char **hostname; + + private: + u8 probetype; + char *hostnameip; +}; + +/* each trace group represents a target ip and contains + * a map of probes that have been sent/recv'ed to/from + * the ip */ +class TraceGroup { + public: + TraceGroup (u32 dip, u16 dport, u16 sport, u8 proto); + ~TraceGroup (); + /* map of all probes sent to this TraceGroups IP address. The map + * is keyed by the source port of the probe */ + std::map < u16, TraceProbe * >TraceProbes; + std::map < u16, TraceProbe * >::size_type size () { return TraceProbes.size ();} + /* checks for timedout probes and retransmits them + * Any probe that exceeds the timing limits is + * considered non-responsive */ + void retransmissions (std::vector < TraceProbe * >&retrans); + /* consolidate timeouts, remove common paths elements + * and performs general upkeep on a finished trace */ + void consolidateHops (); + /* the number of table rows a trace will use + * when printed. */ + u8 tableSize (); + /* the next ttl to send, if the destination has replied + * the ttl is decremented, if it hasn't it is incremented */ + void nextTTL () { if (gotReply) ttl--; else { ttl++; hopDistance++;}} + /* number of probes currently waiting for replies */ + void incRemaining () { if (remaining < 255) ++remaining; } + void decRemaining () { if (remaining > 0) --remaining; } + char *IPStr () { struct in_addr s; s.s_addr = ipdst; return inet_ntoa (s);} + u8 getRemaining () { return remaining;} + u8 getState () { return state; } + u8 setState (u8 state); + u8 setHopDistance (u8 hop_distance, u8 ttl); + + bool gotReply; + bool noDistProbe; + + /* Group wide timing */ + int scanDelay; + int maxRetransmissions; + u16 droppedPackets; + u16 repliedPackets; + u8 consecTimeouts; + /* protocol information */ + u8 proto; + u16 sport; + u16 dport; + u32 ipdst; + /* estimated ttl distance to target */ + u8 hopDistance; + /* largest ttl send so far */ + u8 ttl; + /* the initial ttl guess. This is needed because the ttl + * may have to be incremented to reach the destination host. + * Once nmap has reached the destination it needs to + * start decrementing the ttl from the original value + * so no duplicate probes are sent + * + * EG. If the guess is 20 but the target is at 23. We will + * start tracing backwards at 19 + */ + u8 start_ttl; + u8 consolidation_start; + const u8 *src_mac_addr; + const u8 *nxt_mac_addr; + + private: + /* the number of probes sent but and not yet replied to */ + u8 remaining; + /* default state is G_OK, set to G_FINISH when + * complete or one of the G_* error codes if this + * group fails */ + u8 state; +}; + +/* Public interface to traceroute functionality */ +class Traceroute { + public: + Traceroute (const char *device_name, devtype type); + ~Traceroute (); + + /* perform the traceroute on a list of targets */ + void trace (std::vector < Target * >&Targets); + /* Use nmaps rDNS functions to mass resolve the hops ip addresses */ + void resolveHops (); + /* display plain and XML traceroutes for target t */ + void outputTarget (Target * t); + + private: + /* map of all TraceGroups, keyed by + * the groups destination IP address */ + std::map < u32, TraceGroup * >TraceGroups; + + + struct scan_info scaninfo; + ScanProgressMeter *SPM; + Target **hops; + pcap_t *pd; + eth_t *ethsd; + int fd, total_size; + + /* called by outputTarget to log XML data */ + void outputXMLTrace (TraceGroup * tg); + /* find a responsive port for t based on scan results */ + int getTracePort (u8 proto, Target * t); + /* sendTTLProbes() guesses the hop distance to a + * target by actively probing the host. */ + void sendTTLProbes (std::vector < Target * >&Targets, std::vector < Target * >&vaild_targets); + int sendProbe (TraceProbe * tp); + /* reads probe replies for all protocols. + * returns finished(), which returns true + * when all groups have finished or failed */ + bool readTraceResponses (); + bool finished (struct ip *ips); + +};