diff --git a/Makefile.in b/Makefile.in index 47d6edf58..3556c06ee 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -export NMAP_VERSION = 3.82 +export NMAP_VERSION = 3.82CSW NMAP_NAME= nmap NMAP_URL= http://www.insecure.org/nmap/ NMAP_PLATFORM=@host@ diff --git a/Target.cc b/Target.cc index da743d359..cae3f0ebc 100644 --- a/Target.cc +++ b/Target.cc @@ -99,6 +99,7 @@ ***************************************************************************/ /* $Id$ */ +#include #include "Target.h" #include "osscan.h" @@ -125,7 +126,8 @@ void Target::Initialize() { targetipstring[0] = '\0'; nameIPBuf = NULL; memset(&MACaddress, 0, sizeof(MACaddress)); - MACaddress_set = false; + memset(&SrcMACaddress, 0, sizeof(SrcMACaddress)); + MACaddress_set = SrcMACaddress_set = false; htn.msecs_used = 0; htn.toclock_running = false; } @@ -351,7 +353,18 @@ int Target::setMACAddress(const u8 *addy) { return 0; } +int Target::setSrcMACAddress(const u8 *addy) { + if (!addy) return 1; + memcpy(SrcMACaddress, addy, 6); + SrcMACaddress_set = 1; + return 0; +} + /* Returns the 6-byte long MAC address, or NULL if none has been set */ const u8 *Target::MACAddress() { return (MACaddress_set)? MACaddress : NULL; } + +const u8 *Target::SrcMACAddress() { + return (SrcMACaddress_set)? SrcMACaddress : NULL; +} diff --git a/Target.h b/Target.h index 94d7ff01a..41ac66316 100644 --- a/Target.h +++ b/Target.h @@ -176,8 +176,10 @@ class Target { /* Takes a 6-byte MAC address */ int setMACAddress(const u8 *addy); + int setSrcMACAddress(const u8 *addy); /* Returns a pointer to 6-byte MAC address, or NULL if none is set */ const u8 *MACAddress(); + const u8 *SrcMACAddress(); struct seq_info seq; FingerPrintResults *FPR; @@ -189,7 +191,7 @@ class Target { int wierd_responses; /* echo responses from other addresses, Ie a network broadcast address */ unsigned int flags; /* HOST_UP, HOST_DOWN, HOST_FIREWALLED, HOST_BROADCAST (instead of HOST_BROADCAST use wierd_responses */ struct timeout_info to; - char device[64]; /* The device we transmit on -- make sure to adjust some str* calls if I ever change this*/ + char device[64]; /* The device we transmit on -- make sure to adjust some str* calls if I ever change this size*/ private: char *hostname; // Null if unable to resolve or unset @@ -206,6 +208,8 @@ class Target { char *nameIPBuf; /* for the NameIP(void) function to return */ u8 MACaddress[6]; bool MACaddress_set; + u8 SrcMACaddress[6]; + bool SrcMACaddress_set; struct host_timeout_nfo htn; }; diff --git a/global_structures.h b/global_structures.h index 3b67b4b8d..6c8be39da 100644 --- a/global_structures.h +++ b/global_structures.h @@ -228,6 +228,6 @@ struct scan_lists { int prot_count; }; -typedef enum { ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN } stype; +typedef enum { ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN, PING_SCAN, PING_SCAN_ARP} stype; #endif /*GLOBAL_STRUCTURES_H */ diff --git a/nmap-os-fingerprints b/nmap-os-fingerprints index aebd58cef..bc029bef7 100644 --- a/nmap-os-fingerprints +++ b/nmap-os-fingerprints @@ -908,8 +908,9 @@ T7(Resp=Y%DF=N%W=0%ACK=O%Flags=R%Ops=) PU(Resp=N) # Apple AirPort Express (Apple Base Station V6.0) -Fingerprint Apple AirPort Express WAP +Fingerprint Apple AirPort Express WAP or Dell Fiber Channel Bridge Module Class Apple | embedded || WAP +Class Dell | embedded || storage-misc TSeq(Class=RI%gcd=<6%SI=1000%TS=2HZ) T1(DF=Y%W=2000%ACK=S++%Flags=AS%Ops=MNWNNT) T2(Resp=N) diff --git a/nmap-service-probes b/nmap-service-probes index dfb1759c7..a09bb3db0 100644 --- a/nmap-service-probes +++ b/nmap-service-probes @@ -1976,7 +1976,7 @@ match afs m|^[\d\D]{28}\s*(OpenAFS)\s+stable\s+([\d\.]+)\s+([^\0]+)\0| v/$1/$2/$ match afs m|^[\d\D]{28}\s*(OpenAFS)([\d\.]{3}[^\s\0]*)\s+([^\0]+)\0| v/$1/$2/$3/ match afs m|^[\d\D]{28}\s*(OpenAFS)([\d\.]{3}[^\s\0]*)\0| v/$1/$2// # Transarc AFS -match afs m|^[\d\D]{28}\s*Base\sconfiguration\safs([\d\.]+)\s+([^\s\0\;]+)[\0\;]| v/Transarc AFS/$1/$2/ +match afs m|^[\d\D]{28}\s*Base\sconfiguration\safs([\d\.]+)\s+[^\s\0\;]+[\0\;]| v/Transarc AFS/$1/$2/ # Arla match afs m|^[\d\D]{28}\s*arla-([\d\.]+)\0| v/Arla/$1// diff --git a/nmap.cc b/nmap.cc index 6e7995abb..791b3613b 100644 --- a/nmap.cc +++ b/nmap.cc @@ -601,6 +601,8 @@ int nmap_main(int argc, char *argv[]) { o.pingtype |= PINGTYPE_ICMP_TS; else if (*optarg == '0' || *optarg == 'N' || *optarg == 'D') o.pingtype = PINGTYPE_NONE; + else if (*optarg == 'R') + o.pingtype |= PINGTYPE_ARP; else if (*optarg == 'S') { o.pingtype |= (PINGTYPE_TCP|PINGTYPE_TCP_USE_SYN); if (isdigit((int) *(optarg+1))) @@ -1792,6 +1794,8 @@ char *scantype2str(stype scantype) { case RPC_SCAN: return "RPCGrind Scan"; break; case MAIMON_SCAN: return "Maimon Scan"; break; case IPPROT_SCAN: return "IPProto Scan"; break; + case PING_SCAN: return "Ping Scan"; break; + case PING_SCAN_ARP: return "ARP Ping Scan"; break; default: assert(0); break; } diff --git a/nmap.h b/nmap.h index 7bb2fdc44..14e1ef526 100644 --- a/nmap.h +++ b/nmap.h @@ -357,6 +357,7 @@ void *realloc(); #define PINGTYPE_RAWTCP 128 #define PINGTYPE_CONNECTTCP 256 #define PINGTYPE_UDP 512 +#define PINGTYPE_ARP 1024 /* TCP/IP ISN sequence prediction classes */ #define SEQ_UNKNOWN 0 diff --git a/osscan.cc b/osscan.cc index 0ac3d0309..15a9949f0 100644 --- a/osscan.cc +++ b/osscan.cc @@ -144,7 +144,7 @@ int testno; int timeout; int avnum; unsigned int sequence_base; -unsigned int openport; +unsigned long openport; unsigned int bytes; unsigned int closedport = 31337; Port *tport = NULL; @@ -227,7 +227,7 @@ snprintf(filter, sizeof(filter), "dst host %s and (icmp or (tcp and src host %s) } if (o.verbose && openport != (unsigned long) -1) - log_write(LOG_STDOUT, "For OSScan assuming port %d is open, %d is closed, and neither are firewalled\n", openport, closedport); + log_write(LOG_STDOUT, "For OSScan assuming port %lu is open, %d is closed, and neither are firewalled\n", openport, closedport); current_port = o.magic_port + NUM_SEQ_SAMPLES +1; @@ -422,7 +422,7 @@ if (o.verbose && openport != (unsigned long) -1) if ((tcp->th_flags & TH_RST)) { /* readtcppacket((char *) ip, ntohs(ip->ip_len));*/ if (si->responses == 0) { - fprintf(stderr, "WARNING: RST from port %hu -- is this port really open?\n", openport); + fprintf(stderr, "WARNING: RST from port %lu -- is this port really open?\n", openport); /* We used to quit in this case, but left-overs from a SYN scan or lame-ass TCP wrappers can cause this! */ } diff --git a/scan_engine.cc b/scan_engine.cc index 83ac605b0..a63737889 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -100,6 +100,8 @@ /* $Id$ */ +#include + #include "scan_engine.h" #include "timing.h" #include "NmapOps.h" @@ -148,6 +150,25 @@ struct ultra_timing_vals { struct timeval last_drop; }; +/* I could have used a union instead, but I don't think it is terribly + critical given the small size of these fields */ +typedef struct probespec { + enum PSType { PS_NONE=0, PS_TCP, PS_UDP, PS_PROTO, PS_ICMP, PS_ARP } type; + /* if type is PS_TCP or PS_UDP, portno is valid and contains what + you would expect */ + u16 portno; /* tcp or udp port number. Host byte order */ + // only valid for PS_TCP scans (TH_SYN should be used for connect() scans) + u8 tcp_flags; + /* Protocol number for PS_PROTO */ + u8 proto; + /* For PS_ICMP -- commented out until I actually implement it + u8 icmp_type; + u8 icmp_code; + */ + /* For PS_ARP scanning, noting really necessary (src mac and target ip + avail. from target structure anyway */ +} probespec; + class ConnectProbe { public: ConnectProbe(); @@ -162,17 +183,24 @@ class UltraProbe { public: UltraProbe(); ~UltraProbe(); - enum UPType { UP_UNSET, UP_IP, UP_CONNECT, UP_RPC } type; /* The type of probe this is */ - /* Sets this UltraProbe as type UP_IP and creates & initializes the internal - IPProbe */ - int setIP(u8 *ippacket, u32 iplen); + enum UPType { UP_UNSET, UP_IP, UP_CONNECT, UP_RPC, UP_ARP } type; /* The type of probe this is */ + + /* Sets this UltraProbe as type UP_IP and creates & initializes the + internal IPProbe. The relevent probespec is necessary for setIP + because pspec.type is ambiguous with just the ippacket (e.g. a + tcp packet could be PS_PROTO or PS_TCP). */ + int setIP(u8 *ippacket, u32 iplen, const probespec *pspec); /* Sets this UltraProbe as type UP_CONNECT, preparing to connect to given port number*/ void setConnect(u16 portno); + /* Pass an arp packet, including ethernet header. Must be 42bytes */ + void setARP(u8 *arppkt, u32 arplen); IPProbe *IP; /* filled out if type == UP_IP */ ConnectProbe *CP; /* Filled out if type == UP_CONNECT */ + ArpProbe *AP; /* Filled if UP_ARP */ unsigned int tryno; /* Try (retransmission) number of this probe */ - u16 portno(); /* The TCP or UDP dest port of this probe */ +/* Get general details about the probe */ + const probespec *pspec() { return &mypspec; } /* If true, probe is considered no longer active due to timeout, but it may be kept around a while, just in case a reply comes late */ bool timedout; @@ -185,7 +213,7 @@ public: bool isPing() { return pingseq > 0; } unsigned int pingseq; /* 0 if this is not a scanping. Otherwise a posative ping seq#. */ private: - u16 myPortno; + probespec mypspec; /* Filled in by the appropriate set* function */ }; /* Global info for the connect scan */ @@ -267,7 +295,7 @@ public: int freshPortsLeft(); /* Returns the number of ports remaining to probe */ int next_portidx; /* Index of the next port to probe in the relevent ports array in USI.ports */ - + bool sent_arp; /* Has an ARP probe been sent for the target yet? */ /* How long I am currently willing to wait for a probe response before considering it timed out. Uses the host values from target if they are available, otherwise from gstats. Results @@ -346,9 +374,9 @@ public: don't send too many pings when probes are going slowly. */ int lastping_sent_numprobes; struct timeval lastprobe_sent; /* Most recent probe send (including pings) by host. Init to scan begin time. */ - /* A valid portnumber for sending scanpings. -1 if none yet found */ - int pingport; - int pingportstate; /* PORT_UNKNOWN if no pingport yet found */ + /* A valid probe for sending scanpings. */ + probespec pingprobe; + int pingprobestate; /* PORT_UNKNOWN if no pingprobe yet found */ /* gives the maximum try number (try numbers start at zero and increments for each retransmission) that may be used, based on the scan type, observed network reliability, timing mode, etc. @@ -413,6 +441,8 @@ public: bool udp_scan; bool icmp_scan; bool prot_scan; + bool ping_scan; /* Includes trad. ping scan & arp scan */ + bool ping_scan_arp; /* ONLY includes arp ping scan */ struct timeval now; /* Updated after potentially meaningful delays. This can be used to save a call to gettimeofday() */ GroupScanStats *gstats; @@ -439,7 +469,7 @@ public: struct scan_lists *ports; int rawsd; /* raw socket descriptor */ pcap_t *pd; - + eth_t *ethsd; u32 seqmask; /* This mask value is used to encode values in sequence numbers. It is set randomly in UltraScanInfo::Init() */ private: @@ -473,6 +503,44 @@ void ultrascan_adjust_times(UltraScanInfo *USI, HostScanStats *hss, extern unsigned long flt_dsthost, flt_srchost; extern unsigned short flt_baseport; +/* Take a buffer, buf, of size bufsz (32 bytes is sufficient) and + writes a short description of the probe (arg1) into buf. It also returns + buf. */ +static char *probespec2ascii(probespec *pspec, char *buf, unsigned int bufsz) { + char flagbuf[32]; + char *f; + switch(pspec->type) { + case probespec::PS_TCP: + if (!pspec->tcp_flags) Strncpy(flagbuf, "(none)", sizeof(flagbuf)); + else { + f = flagbuf; + if (pspec->tcp_flags & TH_SYN) *f++ = 'S'; + if (pspec->tcp_flags & TH_FIN) *f++ = 'F'; + if (pspec->tcp_flags & TH_RST) *f++ = 'R'; + if (pspec->tcp_flags & TH_PUSH) *f++ = 'P'; + if (pspec->tcp_flags & TH_ACK) *f++ = 'A'; + if (pspec->tcp_flags & TH_URG) *f++ = 'U'; + if (pspec->tcp_flags & TH_ECE) *f++ = 'E'; /* rfc 2481/3168 */ + if (pspec->tcp_flags & TH_CWR) *f++ = 'C'; /* rfc 2481/3168 */ + *f++ = '\0'; + } + snprintf(buf, bufsz, "tcp to port %hu; flags: %s", pspec->portno, flagbuf); + break; + case probespec::PS_UDP: + snprintf(buf, bufsz, "udp to port %hu", pspec->portno); + break; + case probespec::PS_PROTO: + snprintf(buf, bufsz, "protocol %u", (unsigned int) pspec->proto); + break; + case probespec::PS_ARP: + snprintf(buf, bufsz, "ARP"); + break; + default: + fatal("Unexpected probespec2ascii type encountered"); + break; + } + return buf; +} ConnectProbe::ConnectProbe() { sd = -1; } @@ -490,7 +558,7 @@ UltraProbe::UltraProbe() { timedout = false; retransmitted = false; pingseq = 0; - myPortno = 0; + mypspec.type = probespec::PS_NONE; memset(&sent, 0, sizeof(prevSent)); memset(&prevSent, 0, sizeof(prevSent)); } @@ -498,20 +566,28 @@ UltraProbe::UltraProbe() { UltraProbe::~UltraProbe() { delete IP; delete CP; + delete AP; } - /* Sets this UltraProbe as type UP_IP and creates & initializes the internal - IPProbe */ -int UltraProbe::setIP(u8 *ippacket, u32 iplen) { +/* Pass an arp packet, including ethernet header. Must be 42bytes */ +void UltraProbe::setARP(u8 *arppkt, u32 arplen) { + type = UP_ARP; + AP = new ArpProbe; + AP->storePacket(arppkt, arplen); + mypspec.type = probespec::PS_ARP; + return; +} + + /* Sets this UltraProbe as type UP_IP and creates & initializes the + internal IPProbe. The relevent probespec is necessary for setIP + because pspec.type is ambiguous with just the ippacket (e.g. a + tcp packet could be PS_PROTO or PS_TCP). */ +int UltraProbe::setIP(u8 *ippacket, u32 iplen, const probespec *pspec) { int i; type = UP_IP; IP = new IPProbe; i = IP->storePacket(ippacket, iplen); - if (IP->ipv4->ip_p == IPPROTO_TCP && IP->tcp) { - myPortno = ntohs(IP->tcp->th_dport); - } else if (IP->ipv4->ip_p == IPPROTO_UDP && IP->udp) { - myPortno = ntohs(IP->udp->uh_dport); - } + mypspec = *pspec; return i; } @@ -520,12 +596,9 @@ int UltraProbe::setIP(u8 *ippacket, u32 iplen) { void UltraProbe::setConnect(u16 portno) { type = UP_CONNECT; CP = new ConnectProbe(); - myPortno = portno; -} - -/* The TCP or UDP dest port of this probe */ -u16 UltraProbe::portno() { - return myPortno; + mypspec.type = probespec::PS_TCP; + mypspec.portno = portno; + mypspec.tcp_flags = TH_SYN; } ConnectScanInfo::ConnectScanInfo() { @@ -579,6 +652,9 @@ GroupScanStats::GroupScanStats(UltraScanInfo *UltraSI) { USI = UltraSI; init_ultra_timing_vals(&timing, TIMING_GROUP, USI->numIncompleteHosts(), &(USI->perf), &USI->now); initialize_timeout_info(&to); + /* Default timout should be much lower for arp */ + if (USI->ping_scan_arp) + to.timeout = MIN(o.initialRttTimeout(), 100) * 1000; num_probes_active = 0; numtargets = USI->numIncompleteHosts(); // They are all incomplete at the beginning if (USI->tcp_scan) { @@ -587,7 +663,9 @@ GroupScanStats::GroupScanStats(UltraScanInfo *UltraSI) { numports = USI->ports->udp_count; } else if (USI->prot_scan) { numports = USI->ports->prot_count; - } else assert(0); /* TODO: RPC scan and maybe ping */ + } else if (USI->ping_scan_arp) { + numports = 1; + }else assert(0); /* TODO: RPC scan and maybe ping */ if (USI->scantype == CONNECT_SCAN) CSI = new ConnectScanInfo; @@ -642,12 +720,13 @@ HostScanStats::HostScanStats(Target *t, UltraScanInfo *UltraSI) { target = t; USI=UltraSI; next_portidx = 0; + sent_arp = false; num_probes_active = 0; num_probes_waiting_retransmit = 0; lastping_sent = lastprobe_sent = lastrcvd = USI->now; lastping_sent_numprobes = 0; - pingport = -1; - pingportstate = PORT_UNKNOWN; + bzero(&pingprobe, sizeof(pingprobe)); + pingprobestate = PORT_UNKNOWN; nxtpseq = 1; max_successful_tryno = 0; tryno_mayincrease = true; @@ -875,6 +954,7 @@ UltraScanInfo::~UltraScanInfo() { delete SPM; if (rawsd >= 0) { close(rawsd); rawsd = -1; } if (pd) { pcap_close(pd); pd = NULL; } + if (ethsd) { eth_close(ethsd); ethsd = NULL; } } /* A circular buffer of the incompleteHosts. nextIncompleteHost() gives @@ -927,10 +1007,13 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp init_perf_values(&perf); for(targetno = 0; targetno < Targets.size(); targetno++) { - if (!Targets[targetno]->timedOut(&now)) { - hss = new HostScanStats(Targets[targetno], this); - incompleteHosts.push_back(hss); - } else num_timedout++; + if (Targets[targetno]->timedOut(&now)) { + num_timedout++; + continue; + } + + hss = new HostScanStats(Targets[targetno], this); + incompleteHosts.push_back(hss); } numInitialTargets = Targets.size(); @@ -938,11 +1021,11 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp nextI = incompleteHosts.begin(); - seqmask = get_random_u32(); scantype = scantp; SPM = new ScanProgressMeter(scantype2str(scantype)); - tcp_scan = udp_scan = icmp_scan = prot_scan = false; + tcp_scan = udp_scan = icmp_scan = prot_scan = ping_scan = false; + ping_scan_arp = false; switch(scantype) { case ACK_SCAN: case CONNECT_SCAN: @@ -960,6 +1043,13 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp case IPPROT_SCAN: prot_scan = true; break; + case PING_SCAN: + ping_scan = true; + break; + case PING_SCAN_ARP: + ping_scan = true; + ping_scan_arp = true; + break; default: break; /* TODO: Worry about ping scanning if I do that */ @@ -967,6 +1057,9 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp gstats = new GroupScanStats(this); /* Peeks at several elements in USI - careful of order */ gstats->num_hosts_timedout += num_timedout; pd = NULL; + rawsd = -1; + ethsd = NULL; + if ((tcp_scan || udp_scan || prot_scan) && scantype != CONNECT_SCAN) { /* Initialize a raw socket */ if ((rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 ) @@ -979,7 +1072,11 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp #ifndef WIN32 sethdrinclude(rawsd); #endif - } else rawsd = -1; + } else if (ping_scan_arp) { + ethsd = eth_open(Targets[0]->device); + if (ethsd == NULL) + fatal("dnet: Failed to open device %s", o.device); + } } @@ -1140,22 +1237,58 @@ static void init_ultra_timing_vals(ultra_timing_vals *timing, else gettimeofday(&timing->last_drop, NULL); } +/* Returns the next probe to try against target. Supports many + different types of probes (see probespec structure). Returns 0 and + fills in pspec if there is a new probe, -1 if there are none + left. */ +static int get_next_target_probe(UltraScanInfo *USI, HostScanStats *hss, + probespec *pspec) { + assert(pspec); -/* Returns the next target port to try for a host. If there are no more - remaining, returns -1. */ -static int get_next_target_port(UltraScanInfo *USI, HostScanStats *hss) { if (USI->tcp_scan) { if (hss->next_portidx >= USI->ports->tcp_count) return -1; - return USI->ports->tcp_ports[hss->next_portidx++]; + pspec->type = probespec::PS_TCP; + + pspec->portno = USI->ports->tcp_ports[hss->next_portidx++]; + if (USI->scantype == CONNECT_SCAN) + pspec->tcp_flags = TH_SYN; + else if (o.scanflags != -1) pspec->tcp_flags = o.scanflags; + else { + switch(USI->scantype) { + case SYN_SCAN: pspec->tcp_flags = TH_SYN; break; + case ACK_SCAN: pspec->tcp_flags = TH_ACK; break; + case XMAS_SCAN: pspec->tcp_flags = TH_FIN|TH_URG|TH_PUSH; break; + case NULL_SCAN: pspec->tcp_flags = 0; break; + case FIN_SCAN: pspec->tcp_flags = TH_FIN; break; + case MAIMON_SCAN: pspec->tcp_flags = TH_FIN|TH_ACK; break; + case WINDOW_SCAN: pspec->tcp_flags = TH_ACK; break; + default: + assert(0); + break; + } + } + return 0; } else if (USI->udp_scan) { if (hss->next_portidx >= USI->ports->udp_count) return -1; - return USI->ports->udp_ports[hss->next_portidx++]; + pspec->type = probespec::PS_UDP; + + pspec->portno = USI->ports->tcp_ports[hss->next_portidx++]; + + return 0; } else if (USI->prot_scan) { if (hss->next_portidx >= USI->ports->prot_count) return -1; - return USI->ports->prots[hss->next_portidx++]; + pspec->type = probespec::PS_PROTO; + pspec->proto = USI->ports->prots[hss->next_portidx++]; + return 0; + } else if (USI->ping_scan_arp) { + if (hss->sent_arp) + return -1; + pspec->type = probespec::PS_ARP; + hss->sent_arp = true; + return 0; } assert(0); /* TODO: need to handle other protocols */ return -1; @@ -1175,6 +1308,9 @@ int HostScanStats::freshPortsLeft() { if (next_portidx >= USI->ports->prot_count) return 0; return USI->ports->prot_count - next_portidx; + } else if (USI->ping_scan_arp) { + if (sent_arp) return 0; + return 1; } assert(0); return 0; @@ -1261,7 +1397,7 @@ void HostScanStats::getTiming(struct ultra_timing_vals *tmng) { /* Use the per-host value if a pingport has been found or very few probes have been sent */ - if (pingport != -1 || numprobes_sent < 80) { + if (pingprobestate != PORT_UNKNOWN || numprobes_sent < 80) { *tmng = timing; return; } @@ -1379,7 +1515,7 @@ void ultrascan_adjust_times(UltraScanInfo *USI, HostScanStats *hss, if (probe->tryno > 0 || !rcvdtime) { /* A previous probe must have been lost ... */ if (o.debugging > 1) - printf("Ultrascan DROPPED %sprobe packet to %s:%hi detected\n", probe->isPing()? "PING " : "", hss->target->targetipstr(), probe->portno()); + printf("Ultrascan DROPPED %sprobe packet to %s:%hi detected\n", probe->isPing()? "PING " : "", hss->target->targetipstr(), probe->pspec()->portno); // Drops often come in big batches, but we only want one decrease per batch. if (TIMEVAL_SUBTRACT(probe->sent, hss->timing.last_drop) > 0) { hss->timing.cwnd = USI->perf.low_cwnd; @@ -1452,6 +1588,36 @@ static void ultrascan_ping_update(UltraScanInfo *USI, HostScanStats *hss, hss->destroyOutstandingProbe(probeI); } + +/* Called when a new status is determined for host in hss (eg. it is + found to be up or down by a ping/ping_arp scan. The probe that led + to this new decision is in probeI. This function needs to update + timing information and other stats as appropriate.If rcvdtime is + NULL, packet stats are not updated. */ +static void ultrascan_host_update(UltraScanInfo *USI, HostScanStats *hss, + list::iterator probeI, + int newstate, struct timeval *rcvdtime) { + UltraProbe *probe = *probeI; + if (rcvdtime) ultrascan_adjust_times(USI, hss, probe, rcvdtime); + + /* Adjust the target flags to note the new state. */ + if ((hss->target->flags & HOST_UP) == 0) { + if (newstate == HOST_UP) { + /* Clear any HOST_DOWN or HOST_FIREWALLED flags */ + hss->target->flags &= ~(HOST_DOWN|HOST_FIREWALLED); + hss->target->flags |= HOST_UP; + } else if (newstate == HOST_DOWN) { + hss->target->flags &= ~HOST_FIREWALLED; + hss->target->flags |= HOST_DOWN; + } else assert(0); + } + + /* Kill outstanding probes */ + while(!hss->probes_outstanding.empty()) + hss->destroyOutstandingProbe(hss->probes_outstanding.begin()); +} + + /* This function is called when a new status is determined for a port. the port in the probeI of host hss is now in newstate. This function needs to update timing information, other stats, and the Nmap port @@ -1487,7 +1653,7 @@ static void ultrascan_port_update(UltraScanInfo *USI, HostScanStats *hss, if (USI->scantype == IPPROT_SCAN) portno = probe->IP->ipv4->ip_p; - else portno = probe->portno(); + else portno = probe->pspec()->portno; /* First figure out the current state */ currentp = hss->target->ports.lookupPort(portno, proto); @@ -1550,22 +1716,36 @@ static void ultrascan_port_update(UltraScanInfo *USI, HostScanStats *hss, } /* Consider changing the ping port */ - if (hss->pingportstate != newstate) { + if (hss->pingprobestate != newstate) { /* TODO: UDP scan and such will have different preferences -- add them */ if (noresp_open_scan) { - if (newstate == PORT_CLOSED || (hss->pingportstate == PORT_UNKNOWN && newstate == PORT_FILTERED)) + if (newstate == PORT_CLOSED || (hss->pingprobestate == PORT_UNKNOWN && newstate == PORT_FILTERED)) swappingport = true; } else { - if (hss->pingportstate == PORT_UNKNOWN && + if (hss->pingprobestate == PORT_UNKNOWN && (newstate == PORT_OPEN || newstate == PORT_CLOSED || newstate == PORT_UNFILTERED)) swappingport = true; - else if (hss->pingportstate == PORT_OPEN && (newstate == PORT_CLOSED || newstate == PORT_UNFILTERED)) + else if (hss->pingprobestate == PORT_OPEN && (newstate == PORT_CLOSED || newstate == PORT_UNFILTERED)) swappingport = true; } if (swappingport) { - hss->pingport = portno; - hss->pingportstate = newstate; + if (USI->tcp_scan) { + hss->pingprobe.type = probespec::PS_TCP; + hss->pingprobe.portno = portno; + if (USI->scantype = CONNECT_SCAN) + hss->pingprobe.tcp_flags = TH_SYN; + else hss->pingprobe.tcp_flags = probe->IP->tcp->th_flags; + } else if (USI->udp_scan) { + hss->pingprobe.type = probespec::PS_UDP; + hss->pingprobe.portno = portno; + } else if (USI->prot_scan) { + hss->pingprobe.type = probespec::PS_PROTO; + hss->pingprobe.proto = probe->IP->ipv4->ip_p; + } else if (USI->ping_scan_arp) { + hss->pingprobe.type = probespec::PS_ARP; + } else fatal("Unexpected ping probe type in ultrascan_port_update"); + hss->pingprobestate = newstate; } } @@ -1635,9 +1815,9 @@ static UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss, fatal("Failed to get target socket address in pos_scan"); } if (sin->sin_family == AF_INET) - sin->sin_port = htons(probe->portno()); + sin->sin_port = htons(probe->pspec()->portno); #if HAVE_IPV6 - else sin6->sin6_port = htons(probe->portno()); + else sin6->sin6_port = htons(probe->pspec()->portno); #endif hss->lastprobe_sent = probe->sent = USI->now; rc = connect(CP->sd, (struct sockaddr *)&sock, socklen); @@ -1690,14 +1870,48 @@ static UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss, } +/* If this is NOT a ping probe, set pingseq to 0. Otherwise it will be the + ping sequence number (they start at 1). The probe sent is returned. */ +static UltraProbe *sendArpScanProbe(UltraScanInfo *USI, HostScanStats *hss, + u8 tryno, u8 pingseq) { + int rc; + UltraProbe *probe = new UltraProbe(); + + /* 3 cheers for libdnet header files */ + u8 frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN]; + + eth_pack_hdr(frame, ETH_ADDR_BROADCAST, *hss->target->SrcMACAddress(), + ETH_TYPE_ARP); + arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST, + *hss->target->SrcMACAddress(), *hss->target->v4sourceip(), + ETH_ADDR_BROADCAST, *hss->target->v4hostip()); + gettimeofday(&USI->now, NULL); + hss->lastprobe_sent = probe->sent = USI->now; + if ((rc = eth_send(USI->ethsd, frame, sizeof(frame))) != sizeof(frame)) { + error("WARNING: eth_send of ARP packet returned %u rather than expected %d\n", rc, (int) sizeof(frame)); + } + PacketTrace::traceArp(PacketTrace::SENT, (u8 *) frame, sizeof(frame), &USI->now); + probe->tryno = tryno; + probe->pingseq = pingseq; + /* First build the probe */ + probe->setARP(frame, sizeof(frame)); + + /* Now that the probe has been sent, add it to the Queue for this host */ + hss->probes_outstanding.push_back(probe); + USI->gstats->num_probes_active++; + hss->num_probes_active++; + + gettimeofday(&USI->now, NULL); + return probe; +} + /* If this is NOT a ping probe, set pingseq to 0. Otherwise it will be the ping sequence number (they start at 1). The probe sent is returned. */ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, - u16 destport, u8 tryno, u8 pingseq) { + const probespec *pspec, u8 tryno, u8 pingseq) { u8 *packet = NULL; u32 packetlen = 0; UltraProbe *probe = new UltraProbe(); - int scanflags = 0; int decoy = 0; u32 seq = 0; u32 ack = 0; @@ -1718,32 +1932,17 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, if (USI->tcp_scan) { assert(USI->scantype != CONNECT_SCAN); - if (o.scanflags != -1) scanflags = o.scanflags; - else { - switch(USI->scantype) { - case SYN_SCAN: scanflags = TH_SYN; break; - case ACK_SCAN: scanflags = TH_ACK; break; - case XMAS_SCAN: scanflags = TH_FIN|TH_URG|TH_PUSH; break; - case NULL_SCAN: scanflags = 0; break; - case FIN_SCAN: scanflags = TH_FIN; break; - case MAIMON_SCAN: scanflags = TH_FIN|TH_ACK; break; - case WINDOW_SCAN: scanflags = TH_ACK; break; - default: - break; - } - } - seq = seq32_encode(USI, tryno, pingseq); - if (scanflags & TH_ACK) + if (pspec->tcp_flags & TH_ACK) ack = rand(); for(decoy = 0; decoy < o.numdecoys; decoy++) { packet = build_tcp_raw(&o.decoys[decoy], hss->target->v4hostip(), o.ttl, - ipid, sport, destport, seq, ack, scanflags, 0, - NULL, 0, o.extra_payload, o.extra_payload_length, - &packetlen); + ipid, sport, pspec->portno, seq, ack, + pspec->tcp_flags, 0, NULL, 0, o.extra_payload, + o.extra_payload_length, &packetlen); if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen); + probe->setIP(packet, packetlen, pspec); hss->lastprobe_sent = probe->sent = USI->now; } send_ip_packet(USI->rawsd, packet, packetlen); @@ -1752,11 +1951,11 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, } else if (USI->udp_scan) { for(decoy = 0; decoy < o.numdecoys; decoy++) { packet = build_udp_raw(&o.decoys[decoy], hss->target->v4hostip(), o.ttl, - sport, destport, ipid, + sport, pspec->portno, ipid, o.extra_payload, o.extra_payload_length, &packetlen); if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen); + probe->setIP(packet, packetlen, pspec); hss->lastprobe_sent = probe->sent = USI->now; } send_ip_packet(USI->rawsd, packet, packetlen); @@ -1764,7 +1963,7 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, } } else if (USI->prot_scan) { for(decoy = 0; decoy < o.numdecoys; decoy++) { - switch(destport) { + switch(pspec->proto) { case IPPROTO_TCP: packet = build_tcp_raw(&o.decoys[decoy], hss->target->v4hostip(), o.ttl, @@ -1787,13 +1986,13 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, break; default: packet = build_ip_raw(&o.decoys[decoy], hss->target->v4hostip(), o.ttl, - destport, ipid, + pspec->proto, ipid, o.extra_payload, o.extra_payload_length, &packetlen); break; } if (decoy == o.decoyturn) { - probe->setIP(packet, packetlen); + probe->setIP(packet, packetlen, pspec); hss->lastprobe_sent = probe->sent = USI->now; } send_ip_packet(USI->rawsd, packet, packetlen); @@ -1812,16 +2011,19 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss, static void sendNextScanProbe(UltraScanInfo *USI, HostScanStats *hss) { - u16 destport; - - destport = get_next_target_port(USI, hss); + probespec pspec; + + if (get_next_target_probe(USI, hss, &pspec) == -1) { + fatal("sendNextScanProbe: No more probes! Error in Nmap."); + } hss->numprobes_sent++; USI->gstats->probes_sent++; - if (USI->scantype == CONNECT_SCAN) - sendConnectScanProbe(USI, hss, destport, 0, 0); + if (USI->ping_scan_arp) + sendArpScanProbe(USI, hss, 0, 0); + else if (USI->scantype == CONNECT_SCAN) + sendConnectScanProbe(USI, hss, pspec.portno, 0, 0); else - sendIPScanProbe(USI, hss, destport, 0, 0); - + sendIPScanProbe(USI, hss, &pspec, 0, 0); } static void doAnyNewProbes(UltraScanInfo *USI) { @@ -1844,17 +2046,21 @@ static void doAnyNewProbes(UltraScanInfo *USI) { } /* Sends a ping probe to the host. Assumes that caller has already - checked that sending is OK w/congestion control and that pingport is + checked that sending is OK w/congestion control and that pingprobe is available */ void sendPingProbe(UltraScanInfo *USI, HostScanStats *hss) { - if (o.debugging > 1) - printf("Ultrascan PING SENT to %s:%hi\n", hss->target->targetipstr(), hss->pingport); + if (o.debugging > 1) { + char tmpbuf[32]; + printf("Ultrascan PING SENT to %s [%s]\n", hss->target->targetipstr(), + probespec2ascii(&hss->pingprobe, tmpbuf, sizeof(tmpbuf))); + } if (USI->scantype == CONNECT_SCAN) { - sendConnectScanProbe(USI, hss, hss->pingport, 0, hss->nextPingSeq(true)); + sendConnectScanProbe(USI, hss, hss->pingprobe.portno, 0, + hss->nextPingSeq(true)); } else if (USI->scantype == RPC_SCAN) { assert(0); /* TODO: fill out */ } else { - sendIPScanProbe(USI, hss, hss->pingport, 0, hss->nextPingSeq(true)); + sendIPScanProbe(USI, hss, &hss->pingprobe, 0, hss->nextPingSeq(true)); } hss->numpings_sent++; USI->gstats->probes_sent++; @@ -1870,7 +2076,8 @@ static void doAnyPings(UltraScanInfo *USI) { for(hostI = USI->incompleteHosts.begin(); hostI != USI->incompleteHosts.end(); hostI++) { hss = *hostI; - if (hss->pingport >= 0 && hss->rld.rld_waiting == false && + if (hss->pingprobestate != PORT_UNKNOWN && + hss->rld.rld_waiting == false && hss->numprobes_sent >= hss->lastping_sent_numprobes + 10 && TIMEVAL_SUBTRACT(USI->now, hss->lastrcvd) > USI->perf.pingtime && TIMEVAL_SUBTRACT(USI->now, hss->lastping_sent) > USI->perf.pingtime && @@ -1889,18 +2096,20 @@ static void retransmitProbe(UltraScanInfo *USI, HostScanStats *hss, UltraProbe *newProbe = NULL; if (probe->type == UltraProbe::UP_IP) { if (USI->prot_scan) - newProbe = sendIPScanProbe(USI, hss, probe->IP->ipv4->ip_p, + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), probe->tryno + 1, 0); else if (probe->IP->ipv4->ip_p == IPPROTO_TCP) { - newProbe = sendIPScanProbe(USI, hss, probe->portno(), probe->tryno + 1, + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), probe->tryno + 1, 0); } else { assert(probe->IP->ipv4->ip_p == IPPROTO_UDP); - newProbe = sendIPScanProbe(USI, hss, probe->portno(), probe->tryno + 1, + newProbe = sendIPScanProbe(USI, hss, probe->pspec(), probe->tryno + 1, 0); } } else if (probe->type == UltraProbe::UP_CONNECT) { - newProbe = sendConnectScanProbe(USI, hss, probe->portno(), probe->tryno + 1, 0); + newProbe = sendConnectScanProbe(USI, hss, probe->pspec()->portno, probe->tryno + 1, 0); + } else if (probe->type == UltraProbe::UP_ARP) { + newProbe = sendArpScanProbe(USI, hss, probe->tryno + 1, 0); } else assert(0); /* TODO: Support any other probe types */ if (newProbe) newProbe->prevSent = probe->sent; @@ -1987,7 +2196,7 @@ static void printAnyStats(UltraScanInfo *USI) { printf(" %s: %d/%d/%d/%d/%d %.2f/%d/%d %li/%d/%d\n", hss->target->targetipstr(), hss->num_probes_active, hss->freshPortsLeft(), hss->num_probes_outstanding(), - hss->num_probes_waiting_retransmit, hss->probe_bench.size(), + hss->num_probes_waiting_retransmit, (int) hss->probe_bench.size(), hosttm.cwnd, hosttm.ccthresh, hss->sdn.delayms, hss->probeTimeout(), hss->target->to.srtt, hss->target->to.rttvar); @@ -2111,34 +2320,34 @@ static bool do_one_select_round(UltraScanInfo *USI, struct timeval *stime) { if (res < 0 ) { if (o.debugging > 1) { - log_write(LOG_STDOUT, "Bad port %hi caught by 0-byte write: ", probe->portno()); + log_write(LOG_STDOUT, "Bad port %hi caught by 0-byte write: ", probe->pspec()->portno); perror(""); } newstate = PORT_CLOSED; } else { if (getpeername(sd, (struct sockaddr *) &sin, &sinlen) < 0) { - pfatal("error in getpeername of connect_results for port %hu", (u16) probe->portno()); + pfatal("error in getpeername of connect_results for port %hu", (u16) probe->pspec()->portno); } else { s_in = (struct sockaddr_in *) &sin; s_in6 = (struct sockaddr_in6 *) &sin; if ((o.af() == AF_INET && - probe->portno() != ntohs(s_in->sin_port)) + probe->pspec()->portno != ntohs(s_in->sin_port)) #ifdef HAVE_IPV6 - || (o.af() == AF_INET6 && probe->portno() != ntohs(s_in6->sin6_port)) + || (o.af() == AF_INET6 && probe->pspec()->portno != ntohs(s_in6->sin6_port)) #endif ) { - error("Mismatch!!!! we think we have port %hu but we really have a different one", (u16) probe->portno()); + error("Mismatch!!!! we think we have port %hu but we really have a different one", (u16) probe->pspec()->portno); } } if (getsockname(sd, (struct sockaddr *) &sout, &soutlen) < 0) { - pfatal("error in getsockname for port %hu", (u16) probe->portno()); + pfatal("error in getsockname for port %hu", (u16) probe->pspec()->portno); } s_in = (struct sockaddr_in *) &sout; s_in6 = (struct sockaddr_in6 *) &sout; - if ((o.af() == AF_INET && htons(s_in->sin_port) == probe->portno()) + if ((o.af() == AF_INET && htons(s_in->sin_port) == probe->pspec()->portno) #ifdef HAVE_IPV6 - || (o.af() == AF_INET6 && htons(s_in6->sin6_port) == probe->portno()) + || (o.af() == AF_INET6 && htons(s_in6->sin6_port) == probe->pspec()->portno) #endif ) { /* Linux 2.2 bug can lead to bogus successful connect()ions @@ -2236,6 +2445,70 @@ bool allow_ipid_match(u16 ipid_sent, u16 ipid_rcvd) { } +/* Tries to get one *good* (finishes a probe) ARP response with pcap + by the (absolute) time given in stime. Even if stime is now, try + an ultra-quick pcap read just in case. Returns true if a "good" + result was found, false if it timed out instead. */ +static bool get_arp_result(UltraScanInfo *USI, struct timeval *stime) { + + gettimeofday(&USI->now, NULL); + long to_usec; + int rc; + u8 rcvdmac[6]; + struct in_addr rcvdIP; + struct timeval rcvdtime; + bool timedout = false; + struct sockaddr_in sin; + HostScanStats *hss = NULL; + list::iterator probeI; + int gotone = 0; + + do { + to_usec = TIMEVAL_SUBTRACT(*stime, USI->now); + if (to_usec < 2000) to_usec = 2000; + rc = read_arp_reply_pcap(USI->pd, rcvdmac, &rcvdIP, to_usec, &rcvdtime); + gettimeofday(&USI->now, NULL); + if (rc == -1) fatal("Received -1 response from readarp_reply_pcap"); + if (rc == 0) { + if (TIMEVAL_SUBTRACT(*stime, USI->now) < 0) { + timedout = true; + break; + } else continue; + } + if (rc == 1) { + if (TIMEVAL_SUBTRACT(USI->now, *stime) > 200000) { + /* While packets are still being received, I'll be generous + and give an extra 1/5 sec. But we have to draw the line + somewhere. Hopefully this response will be a keeper so it + won't matter. */ + timedout = true; + } + + /* Yay, I got one. Find whether I asked for it */ + /* Search for this host on the incomplete list */ + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = rcvdIP.s_addr; + sin.sin_family = AF_INET; + hss = USI->findIncompleteHost((struct sockaddr_storage *) &sin); + if (!hss) continue; + /* Add found HW address for target */ + hss->target->setMACAddress(rcvdmac); + + assert(!hss->probes_outstanding.empty()); + probeI = hss->probes_outstanding.end(); + probeI--; + ultrascan_host_update(USI, hss, probeI, HOST_UP, &rcvdtime); + /* TODO: Set target mac */ + gotone = 1; + // printf("Marked host %s as up!", hss->target->NameIP()); + break; + } + } while(!timedout); + + return gotone; +} + + /* Tries to get one *good* (finishes a probe) pcap response by the (absolute) time given in stime. Even if stime is now, try an ultra-quick pcap read just in case. Returns true if a "good" result @@ -2269,7 +2542,6 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { static struct sockaddr_in protoscanicmphackaddy; gettimeofday(&USI->now, NULL); - do { to_usec = TIMEVAL_SUBTRACT(*stime, USI->now); if (to_usec < 2000) to_usec = 2000; @@ -2605,14 +2877,15 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) { static void waitForResponses(UltraScanInfo *USI) { struct timeval stime; bool gotone = false; - gettimeofday(&USI->now, NULL); USI->gstats->last_wait = USI->now; USI->gstats->probes_sent_at_last_wait = USI->gstats->probes_sent; do { USI->sendOK(&stime); - if (USI->pd) { + if (USI->ping_scan_arp) { + gotone = get_arp_result(USI, &stime); + } else if (USI->pd) { gotone = get_pcap_result(USI, &stime); } else if (USI->scantype == CONNECT_SCAN) { gotone = do_one_select_round(USI, &stime); @@ -2626,7 +2899,9 @@ static void waitForResponses(UltraScanInfo *USI) { /* Initiate libpcap or some other sniffer as appropriate to be able to catch responses */ static void begin_sniffer(UltraScanInfo *USI, vector &Targets) { - char pcap_filter[3072]; + char pcap_filter[2048]; + /* 20 IPv6 addresses is max (45 byte addy + 14 (" or src host ")) * 20 == 1180 */ + char dst_hosts[1200]; int filterlen = 0; int len; unsigned int targetno; @@ -2636,6 +2911,22 @@ static void begin_sniffer(UltraScanInfo *USI, vector &Targets) { if (USI->scantype == CONNECT_SCAN) return; /* No sniffer needed! */ + if (doIndividual) { + for(targetno = 0; targetno < Targets.size(); targetno++) { + len = snprintf(dst_hosts + filterlen, + sizeof(dst_hosts) - filterlen, + "%ssrc host %s", (targetno == 0)? "" : " or ", + Targets[targetno]->targetipstr()); + if (len < 0 || len + filterlen >= (int) sizeof(dst_hosts)) + fatal("ran out of space in dst_hosts"); + filterlen += len; + } + len = snprintf(dst_hosts + filterlen, sizeof(dst_hosts) - filterlen, ")))"); + if (len < 0 || len + filterlen >= (int) sizeof(dst_hosts)) + fatal("ran out of space in dst_hosts"); + } + filterlen = 0; + USI->pd = my_pcap_open_live(Targets[0]->device, 100, (o.spoofsource)? 1 : 0, 2); /* Windows nonsense */ flt_srchost = Targets[0]->v4host().s_addr; @@ -2644,49 +2935,37 @@ static void begin_sniffer(UltraScanInfo *USI, vector &Targets) { if (USI->tcp_scan || USI->udp_scan) { if (doIndividual) len = snprintf(pcap_filter, sizeof(pcap_filter), - "dst host %s and (icmp or (%s and (", - inet_ntoa(Targets[0]->v4source()), (USI->tcp_scan)? "tcp" : "udp"); + "dst host %s and (icmp or (%s and (%s", + inet_ntoa(Targets[0]->v4source()), + (USI->tcp_scan)? "tcp" : "udp", dst_hosts); else len = snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s and (icmp or %s)", inet_ntoa(Targets[0]->v4source()), (USI->tcp_scan)? "tcp" : "udp"); if (len < 0 || len >= (int) sizeof(pcap_filter)) fatal("ran out of space in pcap filter"); - filterlen += len; - if (doIndividual) { - for(targetno = 0; targetno < Targets.size(); targetno++) { - len = snprintf(pcap_filter + filterlen, sizeof(pcap_filter) - filterlen, - "%ssrc host %s", (targetno == 0)? "" : " or ", - Targets[targetno]->targetipstr()); - if (len < 0 || len + filterlen >= (int) sizeof(pcap_filter)) - fatal("ran out of space in pcap_filter"); - filterlen += len; - } - len = snprintf(pcap_filter + filterlen, sizeof(pcap_filter) - filterlen, ")))"); - if (len < 0 || len + filterlen >= (int) sizeof(pcap_filter)) - fatal("ran out of space in pcap_filter"); - } + filterlen = len; } else if (USI->prot_scan) { - len = snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s%s", - inet_ntoa(Targets[0]->v4source()), (doIndividual)? " and (icmp or (" : ""); + if (doIndividual) + len = snprintf(pcap_filter, sizeof(pcap_filter), + "dst host %s and (icmp or (%s", + inet_ntoa(Targets[0]->v4source()), dst_hosts); + else + len = snprintf(pcap_filter, sizeof(pcap_filter), "dst host %s", + inet_ntoa(Targets[0]->v4source())); if (len < 0 || len >= (int) sizeof(pcap_filter)) fatal("ran out of space in pcap filter"); - filterlen += len; - if (doIndividual) { - for(targetno = 0; targetno < Targets.size(); targetno++) { - len = snprintf(pcap_filter + filterlen, sizeof(pcap_filter) - filterlen, - "%ssrc host %s", (targetno == 0)? "" : " or ", - Targets[targetno]->targetipstr()); - if (len < 0 || len + filterlen >= (int) sizeof(pcap_filter)) - fatal("ran out of space in pcap_filter"); - filterlen += len; - } - len = snprintf(pcap_filter + filterlen, sizeof(pcap_filter) - filterlen, - "))"); - if (len < 0 || len + filterlen >= (int) sizeof(pcap_filter)) - fatal("ran out of space in pcap_filter"); - } - } else assert(0); /* Other scan types? */ + filterlen = len; + } else if (USI->ping_scan_arp) { + const u8 *mac = Targets[0]->SrcMACAddress(); + assert(mac); + len = snprintf(pcap_filter, sizeof(pcap_filter), + "arp and ether dst host %02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + if (len < 0 || len >= (int) sizeof(pcap_filter)) + fatal("ran out of space in pcap filter"); + filterlen = len; + } else assert(0); /* Other scan types? */ if (o.debugging > 2) printf("Pcap filter: %s\n", pcap_filter); set_pcap_filter(Targets[0], USI->pd, flt_all, pcap_filter); /* pcap_setnonblock(USI->pd, 1, NULL); */ @@ -2771,11 +3050,16 @@ void processData(UltraScanInfo *USI) { case MAIMON_SCAN: case XMAS_SCAN: newstate = PORT_OPENFILTERED; break; + case PING_SCAN_ARP: + newstate = HOST_DOWN; break; default: fatal("Unexpected scan type found in processData()"); break; } - ultrascan_port_update(USI, host, probeI, newstate, NULL); + if (USI->scantype == PING_SCAN_ARP) + ultrascan_host_update(USI, host, probeI, newstate, NULL); + else + ultrascan_port_update(USI, host, probeI, newstate, NULL); if (tryno_capped && lastRetryCappedWarning != USI) { /* Perhaps I should give this on a per-host basis. Oh well, hopefully it is rare anyway. */ @@ -2842,7 +3126,7 @@ void ultra_scan(vector &Targets, struct scan_lists *ports, bool plural = (Targets.size() != 1); if (!plural) { (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); - } else snprintf(targetstr, sizeof(targetstr), "%d hosts", Targets.size()); + } else snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) Targets.size()); starttime = USI->now.tv_sec; tm = localtime(&starttime); log_write(LOG_STDOUT, "Initiating %s against %s [%d port%s%s] at %02d:%02d\n", scantype2str(scantype), targetstr, USI->gstats->numports, (USI->gstats->numports != 1)? "s" : "", plural? "/host" : "", tm->tm_hour, tm->tm_min); @@ -2864,10 +3148,10 @@ void ultra_scan(vector &Targets, struct scan_lists *ports, if (o.verbose) { if (USI->gstats->num_hosts_timedout == 0) - log_write(LOG_STDOUT, "The %s took %.2fs to scan %d total ports.\n", + log_write(LOG_STDOUT, "The %s took %.2fs to scan %lu total ports.\n", scantype2str(scantype), TIMEVAL_MSEC_SUBTRACT(USI->now, USI->SPM->begin) / 1000.0, - USI->gstats->numports * Targets.size()); + (unsigned long) USI->gstats->numports * Targets.size()); else log_write(LOG_STDOUT, "Finished %s in %.2fs, but %d %s timed out.\n", scantype2str(scantype), TIMEVAL_MSEC_SUBTRACT(USI->now, USI->SPM->begin) / 1000.0, @@ -3218,7 +3502,7 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { target->to.rttvar = MIN(2000000, (int) (target->to.rttvar * 1.2)); } - if (o.debugging > 2) { log_write(LOG_STDOUT, "Moving port or prog %lu to the potentially firewalled list\n", current->portno); } + if (o.debugging > 2) { log_write(LOG_STDOUT, "Moving port or prog %lu to the potentially firewalled list\n", (unsigned long) current->portno); } current->state = PORT_FILTERED; /* For various reasons */ /* First delete from old list */ if (current->next > -1) scan[current->next].prev = current->prev; diff --git a/service_scan.cc b/service_scan.cc index b3961ee00..4e3c9d41a 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -2100,10 +2100,10 @@ int service_scan(vector &Targets) { bool plural = (Targets.size() != 1); if (!plural) { (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); - } else snprintf(targetstr, sizeof(targetstr), "%d hosts", Targets.size()); + } else snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size()); - log_write(LOG_STDOUT, "Initiating service scan against %d %s on %s at %02d:%02d\n", - SG->services_remaining.size(), + log_write(LOG_STDOUT, "Initiating service scan against %u %s on %s at %02d:%02d\n", + (unsigned) SG->services_remaining.size(), (SG->services_remaining.size() == 1)? "service" : "services", targetstr, tm->tm_hour, tm->tm_min); } @@ -2136,11 +2136,11 @@ int service_scan(vector &Targets) { if (o.verbose) { gettimeofday(&now, NULL); if (SG->num_hosts_timedout == 0) - log_write(LOG_STDOUT, "The service scan took %.2fs to scan %d %s on %d %s.\n", + log_write(LOG_STDOUT, "The service scan took %.2fs to scan %u %s on %u %s.\n", TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, - SG->services_finished.size(), + (unsigned) SG->services_finished.size(), (SG->services_finished.size() == 1)? "service" : "services", - Targets.size(), (Targets.size() == 1)? "host" : "hosts"); + (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts"); else log_write(LOG_STDOUT, "Finished service scan in %.2fs, but %d %s timed out.\n", TIMEVAL_MSEC_SUBTRACT(now, starttv) / 1000.0, diff --git a/targets.cc b/targets.cc index 8cacaba8d..ce1bb10c0 100644 --- a/targets.cc +++ b/targets.cc @@ -107,6 +107,7 @@ #include "NmapOps.h" #include "TargetGroup.h" #include "Target.h" +#include "scan_engine.h" extern NmapOps o; enum pingstyle { pingstyle_unknown, pingstyle_rawtcp, pingstyle_rawudp, pingstyle_connecttcp, @@ -116,6 +117,7 @@ enum pingstyle { pingstyle_unknown, pingstyle_rawtcp, pingstyle_rawudp, pingstyl extern unsigned long flt_dsthost, flt_srchost; extern unsigned short flt_baseport; + /* Gets the host number (index) of target in the hostbatch array of pointers. Note that the target MUST EXIST in the array or all heck will break loose. */ @@ -239,6 +241,44 @@ static int hostupdate(Target *hostbatch[], Target *target, return 0; } +/* Conducts an ARP ping sweep of the given hosts to determine which ones + are up on a local ethernet network */ +static void arpping(Target *hostbatch[], int num_hosts, + struct scan_lists *ports) { + /* First I change hostbatch into a vector, which is what ultra_scan + takes. I remove hosts that cannot be ARP scanned (such as localhost) */ + vector targets; + int targetno; + targets.reserve(num_hosts); + + for(targetno = 0; targetno < num_hosts; targetno++) { + initialize_timeout_info(&hostbatch[targetno]->to); + /* Default timout should be much lower for arp */ + hostbatch[targetno]->to.timeout = MIN(o.initialRttTimeout(), 100) * 1000; + if (!hostbatch[targetno]->SrcMACAddress()) { + bool islocal = islocalhost(hostbatch[targetno]->v4hostip()); + if (islocal) { + log_write(LOG_STDOUT|LOG_NORMAL, + "ARP ping: Considering %s UP because it is a local IP, despite no MAC address for device %s\n", + hostbatch[targetno]->NameIP(), hostbatch[targetno]->device); + hostbatch[targetno]->flags &= ~(HOST_DOWN|HOST_FIREWALLED); + hostbatch[targetno]->flags |= HOST_UP; + } else { + log_write(LOG_STDOUT|LOG_NORMAL, + "ARP ping: Considering %s DOWN because no MAC address found for device %s.\n", + hostbatch[targetno]->NameIP(), hostbatch[targetno]->device); + hostbatch[targetno]->flags &= ~HOST_FIREWALLED; + hostbatch[targetno]->flags |= HOST_DOWN; + } + continue; + } + targets.push_back(hostbatch[targetno]); + } + if (!targets.empty()) + ultra_scan(targets, ports, PING_SCAN_ARP); + return; +} + void hoststructfry(Target *hostbatch[], int nelem) { genfry((unsigned char *)hostbatch, sizeof(Target *), nelem); return; @@ -291,7 +331,7 @@ do { 3) We are doing a raw-mode portscan or osscan OR 4) We are on windows and doing ICMP ping */ if (o.isr00t && o.af() == AF_INET && - ((*pingtype & (PINGTYPE_TCP|PINGTYPE_UDP)) || o.RawScan() + ((*pingtype & (PINGTYPE_TCP|PINGTYPE_UDP|PINGTYPE_ARP)) || o.RawScan() #ifdef WIN32 || (*pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS)) #endif // WIN32 @@ -323,6 +363,11 @@ do { } } + /* If this is an ARP scan, we must determine the device's MAC address */ + if (*pingtype & PINGTYPE_ARP) { + setTargetSrcMACAddressFromDevName(hs->hostbatch[hidx]); + } + /* In some cases, we can only allow hosts that use the same device in a group. */ if (o.af() == AF_INET && o.isr00t && hidx > 0 && @@ -336,7 +381,7 @@ do { goto batchfull; } hs->current_batch_sz++; - } +} if (hs->current_batch_sz < hs->max_batch_sz && hs->next_expression < hs->num_expressions) { @@ -358,12 +403,19 @@ if (hs->randomize) { hoststructfry(hs->hostbatch, hs->current_batch_sz); } -/* Finally we do the mass ping (if required) */ - if ((*pingtype & +/* First I'll do the ARP ping if necessary */ + if (*pingtype & PINGTYPE_ARP) { + arpping(hs->hostbatch, hs->current_batch_sz, ports); + } + /* TODO: For efficiency, at some point it might be nice to usually do ARP + ping before mass ping (and so get rid of else below) */ + /* Then we do the mass ping (if required - IP-level pings) */ + else if ((*pingtype & (PINGTYPE_ICMP_PING|PINGTYPE_ICMP_MASK|PINGTYPE_ICMP_TS) ) || ((!o.isr00t || o.af() == AF_INET6 || hs->hostbatch[0]->v4host().s_addr) && (*pingtype != PINGTYPE_NONE))) massping(hs->hostbatch, hs->current_batch_sz, ports, *pingtype); + /* Otherwise -P0 so we just consider every host up */ else for(i=0; i < hs->current_batch_sz; i++) { initialize_timeout_info(&hs->hostbatch[i]->to); hs->hostbatch[i]->flags |= HOST_UP; /*hostbatch[i].up = 1;*/ diff --git a/tcpip.cc b/tcpip.cc index 5ce1aecee..89ce47c60 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -100,7 +100,7 @@ /* $Id$ */ - +#include #include "tcpip.h" #include "NmapOps.h" @@ -206,6 +206,53 @@ char *getFinalPacketStats(char *buf, int buflen) { return buf; } + /* Takes an ARP PACKET (including ethernet header) and prints it if + packet tracing is enabled. 'frame' must point to the 14-byte + ethernet header (e.g. starting with destination addr). The + direction must be PacketTrace::SENT or PacketTrace::RCVD . + Optional 'now' argument makes this function slightly more + efficient by avoiding a gettimeofday() call. */ +void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now) { + struct timeval tv; + char arpdesc[128]; + char who_has[INET_ADDRSTRLEN], tell[INET_ADDRSTRLEN]; + + + if (pdir == SENT) { + PktCt.sendPackets++; + PktCt.sendBytes += len; + } else { + PktCt.recvPackets++; + PktCt.recvBytes += len; + } + + if (!o.packetTrace()) return; + + if (now) + tv = *now; + else gettimeofday(&tv, NULL); + + if (len < 42) { + error("Packet tracer: Arp packets must be at least 42 bytes long. Should be exactly that length excl. ethernet padding."); + return; + } + + if (frame[21] == 1) /* arp REQUEST */ { + inet_ntop(AF_INET, frame+38, who_has, sizeof(who_has)); + inet_ntop(AF_INET, frame+28, tell, sizeof(who_has)); + snprintf(arpdesc, sizeof(arpdesc), "who-has %s tell %s", who_has, tell); + } else { /* ARP REPLY */ + inet_ntop(AF_INET, frame+28, who_has, sizeof(who_has)); + snprintf(arpdesc, sizeof(arpdesc), + "reply %s is-at %02X:%02X:%02X:%02X:%02X:%02X", who_has, + frame[22], frame[23], frame[24], frame[25], frame[26], frame[27]); + } + + log_write(LOG_STDOUT|LOG_NORMAL, "%s (%.4fs) ARP %s\n", (pdir == SENT)? "SENT" : "RCVD", o.TimeSinceStartMS(&tv) / 1000.0, arpdesc); + + return; +} /* Takes an IP PACKET and prints it if packet tracing is enabled. 'packet' must point to the IPv4 header. The direction must be @@ -294,6 +341,11 @@ void PacketTrace::traceConnect(u8 proto, const struct sockaddr *sock, errbuf); } + + + + + /* Converts an IP address given in a sockaddr_storage to an IPv4 or IPv6 IP address string. Since a static buffer is returned, this is not thread-safe and can only be used once in calls like printf() @@ -1321,96 +1373,6 @@ fcntl(sd, F_SETFL, options); return 1; } -/* Get the source address and interface name */ -#if 0 -char *getsourceif(struct in_addr *src, struct in_addr *dst) { -int sd, sd2; -u16 p1; -struct sockaddr_in sock; -int socklen = sizeof(struct sockaddr_in); -struct sockaddr sa; -recvfrom6_t sasize = sizeof(struct sockaddr); -int ports, res; -u8 buf[65536]; -struct timeval tv; -unsigned int start; -int data_offset, ihl, *intptr; -int done = 0; - - /* Get us some unreserved port numbers */ - get_random_bytes(&p1, 2); - if (p1 < 5000) p1 += 5000; - - if (!getuid()) { - if ((sd2 = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) == -1) - {perror("Linux Packet Socket troubles"); return 0;} - unblock_socket(sd2); - if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - {perror("Socket troubles"); return 0;} - sock.sin_family = AF_INET; - sock.sin_addr = *dst; - sock.sin_port = htons(p1); - if (connect(sd, (struct sockaddr *) &sock, sizeof(struct sockaddr_in)) == -1) - { perror("UDP connect()"); - close(sd); - close(sd2); - return NULL; - } - if (getsockname(sd, (SA *)&sock, &socklen) == -1) { - perror("getsockname"); - close(sd); - close(sd2); - return NULL; - } - ports = (ntohs(sock.sin_port) << 16) + p1; -#if ( TCPIP_DEBUGGING ) - printf("ports is %X\n", ports); -#endif - if (send(sd, "", 0, 0) == -1) - fatal("Could not send UDP packet"); - start = time(NULL); - do { - tv.tv_sec = 2; - tv.tv_usec = 0; - res = recvfrom(sd2, buf, 65535, 0, &sa, &sasize); - if (res < 0) { - if (socket_errno() != EWOULDBLOCK) - perror("recvfrom"); - } - if (res > 0) { -#if ( TCPIP_DEBUGGING ) - printf("Got packet!\n"); - printf("sa.sa_data: %s\n", sa.sa_data); - printf("Hex dump of packet (len %d):\n", res); - hdump(buf, res); -#endif - data_offset = get_link_offset(sa.sa_data); - ihl = (*(buf + data_offset) & 0xf) * 4; - /* If it is big enough and it is IPv4 */ - if (res >= data_offset + ihl + 4 && - (*(buf + data_offset) & 0x40)) { - intptr = (int *) ((char *) buf + data_offset + ihl); - if (*intptr == ntohl(ports)) { - intptr = (int *) ((char *) buf + data_offset + 12); -#if ( TCPIP_DEBUGGING ) - printf("We've found our packet [krad]\n"); -#endif - memcpy(src, buf + data_offset + 12, 4); - close(sd); - close(sd2); - return strdup(sa.sa_data); - } - } - } - } while(!done && time(NULL) - start < 2); - close(sd); - close(sd2); - } - -return NULL; -} -#endif /* 0 */ - int getsourceip(struct in_addr *src, const struct in_addr * const dst) { int sd; struct sockaddr_in sock; @@ -1442,48 +1404,6 @@ int getsourceip(struct in_addr *src, const struct in_addr * const dst) { return 1; /* Calling function responsible for checking validity */ } -#if 0 -int get_link_offset(char *device) { -int sd; -struct ifreq ifr; -sd = socket(AF_INET, SOCK_DGRAM, 0); -memset(&ifr, 0, sizeof(ifr)); -strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); -#if (defined(SIOCGIFHWADDR) && defined(ARPHRD_ETHER) && - defined(ARPHRD_METRICOM) && defined(ARPHRD_SLIP) && defined(ARPHRD_CSLIP) - && defined(ARPHRD_SLIP6) && defined(ARPHRD_PPP) && - defined(ARPHRD_LOOPBACK) ) -if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0 ) { - fatal("Can't obtain link offset. What kind of interface are you using?"); - } -close(sd); -switch (ifr.ifr_hwaddr.sa_family) { -case ARPHRD_ETHER: /* These two are standard ethernet */ -case ARPHRD_METRICOM: - return 14; - break; -case ARPHRD_SLIP: -case ARPHRD_CSLIP: -case ARPHRD_SLIP6: -case ARPHRD_CSLIP6: -case ARPHRD_PPP: - return 0; - break; -case ARPHRD_LOOPBACK: /* Loopback interface (obviously) */ - return 14; - break; -default: - fatal("Unknown link layer device: %d", ifr.ifr_hwaddr.sa_family); -} -#else -printf("get_link_offset called even though your host doesn't support it. Assuming Ethernet or Loopback connection (wild guess)\n"); -return 14; -#endif -/* Not reached */ -exit(1); -} -#endif - /* Read an IP packet using libpcap . We return the packet and take a pcap descripter and a pointer to the packet length (which we set in the function. If you want a maximum length returned, you @@ -1494,7 +1414,6 @@ exit(1); low values (and 0) degenerate to the timeout specified in pcap_open_live() */ - /* If rcvdtime is non-null and a packet is returned, rcvd will be filled with the time that packet was captured from the wire by pcap. If linknfo is not NULL, linknfo->headerlen and @@ -1687,6 +1606,106 @@ bool pcap_recv_timeval_valid() { #endif } +/* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no arp requests, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible, and -1 to block forever. Returns + -1 or exits if ther is an error. */ +int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, struct in_addr *senderIP, + long to_usec, struct timeval *rcvdtime) { + static int warning = 0; + int datalink; + struct pcap_pkthdr head; + u8 *p; + int timedout = 0; + int badcounter = 0; + struct timeval tv_start, tv_end; + + if (!pd) fatal("NULL packet device passed to readarp_reply_pcap"); + + if (to_usec < 0) { + if (!warning) { + warning = 1; + error("WARNING: Negative timeout value (%lu) passed to readip_pcap() -- using 0", to_usec); + } + to_usec = 0; + } + + /* New packet capture device, need to recompute offset */ + if ( (datalink = pcap_datalink(pd)) < 0) + fatal("Cannot obtain datalink information: %s", pcap_geterr(pd)); + + if (datalink != DLT_EN10MB) + fatal("readarp_reply_pcap called on interfaces that is datatype %d rather than DLT_EN10MB (%d)", datalink, DLT_EN10MB); + + if (to_usec > 0) { + gettimeofday(&tv_start, NULL); + } + + do { +#ifdef WIN32 + gettimeofday(&tv_end, NULL); + to_left = MAX(1, (to_usec - TIMEVAL_SUBTRACT(tv_end, tv_start)) / 1000); + // Set the timeout (BUGBUG: this is cheating) + PacketSetReadTimeout(pd->adapter, to_left); +#endif + + p = (u8 *) pcap_next(pd, &head); + + + if (p && head.caplen >= 42) { /* >= because Ethernet padding makes 60 */ + /* frame type 0x0806 (arp), hw type eth (0x0001), prot ip (0x0800), + hw size (0x06), prot size (0x04) */ + if (memcmp(p + 12, "\x08\x06\x00\x01\x08\x00\x06\x04\x00\x02", 10) == 0) { + memcpy(sendermac, p + 22, 6); + /* I think alignment should allow this ... */ + senderIP->s_addr = *(u32 *) (p + 28) ; + break; + } + } + + if (!p) { + /* Should we timeout? */ + if (to_usec == 0) { + timedout = 1; + } else if (to_usec > 0) { + gettimeofday(&tv_end, NULL); + if (TIMEVAL_SUBTRACT(tv_end, tv_start) >= to_usec) { + timedout = 1; + } + } + } else { + /* We'll be a bit patient if we're getting actual packets back, but + not indefinitely so */ + if (badcounter++ > 50) + timedout = 1; + } + } while(!timedout); + + if (timedout) return 0; + + if (rcvdtime) { + // FIXME: I eventually need to figure out why Windows head.ts time is sometimes BEFORE the time I + // sent the packet (which is according to gettimeofday() in nbase). For now, I will sadly have to + // use gettimeofday() for Windows in this case + // Actually I now allow .05 discrepancy. So maybe this isn't needed. I'll comment out for now. + // Nope: it is still needed at least for Windows. Sometimes the time from he pcap header is a + // COUPLE SECONDS before the gettimeofday() results :(. +#if defined(WIN32) || defined(__amigaos__) + gettimeofday(&tv_end, NULL); + *rcvdtime = tv_end; +#else + *rcvdtime = head.ts; + assert(head.ts.tv_sec); +#endif + } + PacketTrace::traceArp(PacketTrace::RCVD, (u8 *) p, 42, rcvdtime); + + return 1; +} + /* This function tries to determine the target's ethernet MAC address from a received packet as follows: @@ -2452,3 +2471,69 @@ int IPProbe::storePacket(u8 *ippacket, u32 len) { return 0; } +ArpProbe::ArpProbe() { + packetbuflen = 0; + packetbuf = NULL; + Reset(); +} + +void ArpProbe::Reset() { + if (packetbuf) + free(packetbuf); + packetbuflen = 0; + packetbuf = NULL; + ipquery = NULL; +} + +ArpProbe::~ArpProbe() { + if (packetbuf) { + free(packetbuf); + packetbuf = NULL; + packetbuflen = 0; + } + Reset(); +} + +int ArpProbe::storePacket(u8 *arppacket, u32 len) { + assert(packetbuf == NULL); + assert(len == 42); + packetbuf = (u8 *) safe_malloc(len); + memcpy(packetbuf, arppacket, len); + packetbuflen = len; + ipquery = (struct in_addr *) ((u8 *)arppacket + 38); + return 0; +} + + + +/* Finds MAC address of target->device and sets into target. Caches + the results so that it will be really quick if you have many targets + that send out with the same device, and you pass them roughly in + order (only caches one). Returns -1 if cannot find the MAC address + (often meaning this is localhost, or some other non-ethernet device. + Returns 0 upon success. */ +int setTargetSrcMACAddressFromDevName(Target *target) { + static u8 MAC_Cache[6]; + static char MAC_Cache_Dev[64] = {0}; + + assert(*target->device); + if (strcmp(target->device, MAC_Cache_Dev) == 0) + target->setSrcMACAddress(MAC_Cache); + else { + eth_t *e; + eth_addr_t et; + + assert(sizeof(et) >= 6); + e = eth_open(target->device); + if (!e) + fatal("dnet: Failed to open ethernet device %s\n", target->device); + if (eth_get(e, &et) == -1) + return -1; + // fatal("dnet: Failed to obtain HW MAC address for ethernet device %s\n", target->device); + eth_close(e); + Strncpy(MAC_Cache_Dev, target->device, sizeof(MAC_Cache_Dev)); + memcpy(MAC_Cache, (const char *) &et, 6); + target->setSrcMACAddress(MAC_Cache); + } + return 0; +} diff --git a/tcpip.h b/tcpip.h index 6ed69f1ac..0d4dc1aee 100644 --- a/tcpip.h +++ b/tcpip.h @@ -295,6 +295,14 @@ class PacketTrace { static void traceConnect(u8 proto, const struct sockaddr *sock, int socklen, int connectrc, int connect_errno, const struct timeval *now); + /* Takes an ARP PACKET (including ethernet header) and prints it if + packet tracing is enabled. 'frame' must point to the 14-byte + ethernet header (e.g. starting with destination addr). The + direction must be PacketTrace::SENT or PacketTrace::RCVD . + Optional 'now' argument makes this function slightly more + efficient by avoiding a gettimeofday() call. */ + static void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now); }; class PacketCounter { @@ -469,6 +477,26 @@ class IPProbe { void Reset(); private: +}; + +/* Handles an *IPv4* Arp probe */ +class ArpProbe { + public: + ArpProbe(); + ~ArpProbe(); +/* Takes an ARP packet and stores _a copy_ of it, in this Probe, + adjusting proper header pointers and such. Then length better + equal 42! */ + int storePacket(u8 *arppacket, u32 len); + u32 packetbuflen; /* Length of the whole packet */ + u8 *packetbuf; /* The packet itself */ + struct in_addr *ipquery; /* IP address this ARP seeks */ + /* Resets everything to NULL. Frees packetbuf if it is filled. You + can reuse a Probe by calling Reset() and then a new + storePacket(). */ + void Reset(); + private: + }; /* This ideally should be a port that isn't in use for any protocol on our machine or on the target */ @@ -634,6 +662,15 @@ char *getFinalPacketStats(char *buf, int buflen); int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, struct ip *ip, int overwrite); + +/* Finds MAC address of target->device and sets into target. Caches + the results so that it will be really quick if you have many targets + that send out with the same device, and you pass them roughly in + order (only caches one). Returns -1 if cannot find the MAC address + (often meaning this is localhost, or some other non-ethernet device. + Returns 0 upon success. */ +int setTargetSrcMACAddressFromDevName(Target *target); + int islocalhost(const struct in_addr * const addr); int unblock_socket(int sd); int Sendto(char *functionname, int sd, const unsigned char *packet, int len, @@ -653,6 +690,16 @@ int get_link_offset(char *device); lnkinfo->header will be filled with the appropriate values. */ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, struct timeval *rcvdtime, struct link_header *linknfo); +/* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no arp requests, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible, and -1 to block forever. Returns + -1 or exits if ther is an error. */ +int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, struct in_addr *senderIP, + long to_usec, struct timeval *rcvdtime); + #ifndef HAVE_INET_ATON int inet_aton(register const char *, struct in_addr *); #endif