Index: NmapOps.h =================================================================== --- NmapOps.h (revision 37608) +++ NmapOps.h (working copy) @@ -354,6 +354,7 @@ int xmasscan; bool noresolve; bool noportscan; + bool nocommonports; bool append_output; /* Append to any output files rather than overwrite */ FILE *logfd[LOG_NUM_FILES]; FILE *nmap_stdout; /* Nmap standard output */ Index: Target.cc =================================================================== --- Target.cc (revision 37608) +++ Target.cc (working copy) @@ -182,6 +182,7 @@ state_reason_init(&reason); memset(&pingprobe, 0, sizeof(pingprobe)); pingprobe_state = PORT_UNKNOWN; + memset(&target_ports, 0, sizeof(target_ports)); } @@ -216,6 +217,8 @@ if (targetname) free(targetname); + free_scan_lists(&target_ports); + if (nameIPBuf) { free(nameIPBuf); nameIPBuf = NULL; Index: Target.h =================================================================== --- Target.h (revision 37608) +++ Target.h (working copy) @@ -318,6 +318,7 @@ enum dist_calc_method distance_calculation_method; FingerPrintResults *FPR; /* FP results get by the OS scan system. */ PortList ports; + struct scan_lists target_ports; int weird_responses; /* echo responses from other addresses, Ie a network broadcast address */ unsigned int flags; /* HOST_UNKNOWN, HOST_UP, or HOST_DOWN. */ Index: TargetGroup.cc =================================================================== --- TargetGroup.cc (revision 37608) +++ TargetGroup.cc (working copy) @@ -839,7 +839,25 @@ int TargetGroup::parse_expr(const char *target_expr, int af) { if (this->netblock != NULL) delete this->netblock; - this->netblock = NetBlock::parse_expr(target_expr, af); + + free_scan_lists(&group_ports); + memset(&group_ports, 0, sizeof(group_ports)); + + const char* par = strrchr(target_expr, '^'); + + if (par != NULL) + { + getpts(par + 1, &this->group_ports); + + char* expr_without_ports = mkstr(target_expr, par); + this->netblock = NetBlock::parse_expr(expr_without_ports, af); + free(expr_without_ports); + } + else { + this->netblock = NetBlock::parse_expr(target_expr, af); + } + + if (this->netblock != NULL) return 0; else Index: TargetGroup.h =================================================================== --- TargetGroup.h (revision 37608) +++ TargetGroup.h (working copy) @@ -137,6 +137,7 @@ #include #include +#include "scan_lists.h" class NetBlock; @@ -143,9 +144,11 @@ class TargetGroup { public: NetBlock *netblock; + struct scan_lists group_ports; TargetGroup() { this->netblock = NULL; + memset(&this->group_ports, 0, sizeof(this->group_ports)); } ~TargetGroup(); Index: nmap.cc =================================================================== --- nmap.cc (revision 37608) +++ nmap.cc (working copy) @@ -260,6 +260,8 @@ "TARGET SPECIFICATION:\n" " Can pass hostnames, IP addresses, networks, etc.\n" " Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254\n" + " Also can contain ports which will be scanned in addition to\n" + " common ports. Ex: scanme.nmap.org^443, 10.0.0-255.1-254^22\n" " -iL : Input from list of hosts/networks\n" " -iR : Choose random targets\n" " --exclude : Exclude hosts/networks\n" @@ -285,7 +287,7 @@ " -sO: IP protocol scan\n" " -b : FTP bounce scan\n" "PORT SPECIFICATION AND SCAN ORDER:\n" - " -p : Only scan specified ports\n" + " -p : Scan specified ports on each target\n" " Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9\n" " --exclude-ports : Exclude the specified ports from scanning\n" " -F: Fast mode - Scan fewer ports than the default scan\n" @@ -387,52 +389,8 @@ } #endif -static void insert_port_into_merge_list(unsigned short *mlist, - int *merged_port_count, - unsigned short p) { - int i; - // make sure the port isn't already in the list - for (i = 0; i < *merged_port_count; i++) { - if (mlist[i] == p) { - return; - } - } - mlist[*merged_port_count] = p; - (*merged_port_count)++; -} -static unsigned short *merge_port_lists(unsigned short *port_list1, int count1, - unsigned short *port_list2, int count2, - int *merged_port_count) { - int i; - unsigned short *merged_port_list = NULL; - *merged_port_count = 0; - - merged_port_list = - (unsigned short *) safe_zalloc((count1 + count2) * sizeof(unsigned short)); - - for (i = 0; i < count1; i++) { - insert_port_into_merge_list(merged_port_list, - merged_port_count, - port_list1[i]); - } - for (i = 0; i < count2; i++) { - insert_port_into_merge_list(merged_port_list, - merged_port_count, - port_list2[i]); - } - - // if there were duplicate ports then we can save some memory - if (*merged_port_count < (count1 + count2)) { - merged_port_list = (unsigned short*) - safe_realloc(merged_port_list, - (*merged_port_count) * sizeof(unsigned short)); - } - - return merged_port_list; -} - void validate_scan_lists(scan_lists &ports, NmapOps &o) { if (o.pingtype == PINGTYPE_UNKNOWN) { if (o.isr00t) { @@ -1585,15 +1543,19 @@ if (o.portlist && o.fastscan) fatal("You cannot use -F (fast scan) with -p (explicit port selection) but see --top-ports and --port-ratio to fast scan a range of ports"); + if (o.ipprotscan) { if (o.portlist) getpts(o.portlist, &ports); else - getpts((char *) (o.fastscan ? "[P:0-]" : "0-"), &ports); // Default protocols to scan - } else if (!o.noportscan) { + getpts((char *)(o.fastscan ? "[P:0-]" : "0-"), &ports); // Default protocols to scan + } + else if (!o.noportscan) { gettoppts(o.topportlevel, o.portlist, &ports, o.exclude_portlist); } + + // Uncomment the following line to use the common lisp port spec test suite //printf("port spec: (%d %d %d %d)\n", ports.tcp_count, ports.udp_count, ports.sctp_count, ports.prot_count); exit(0); @@ -1664,16 +1626,19 @@ * (such as OS ident scan) might break cause no ports were specified, but * we've given our warning... */ - if ((o.TCPScan()) && ports.tcp_count == 0) - error("WARNING: a TCP scan type was requested, but no tcp ports were specified. Skipping this scan type."); - if (o.SCTPScan() && ports.sctp_count == 0) - error("WARNING: a SCTP scan type was requested, but no sctp ports were specified. Skipping this scan type."); - if (o.UDPScan() && ports.udp_count == 0) - error("WARNING: UDP scan was requested, but no udp ports were specified. Skipping this scan type."); - if (o.ipprotscan && ports.prot_count == 0) - error("WARNING: protocol scan was requested, but no protocols were specified to be scanned. Skipping this scan type."); + if (!o.nocommonports) + { + if ((o.TCPScan()) && ports.tcp_count == 0) + error("WARNING: a TCP scan type was requested, but no common tcp ports were specified. Unless you specified per-target ports, the scan will be useless."); + if (o.SCTPScan() && ports.sctp_count == 0) + error("WARNING: a SCTP scan type was requested, but no common sctp ports were specified. Unless you specified per-target ports, the scan will be useless."); + if (o.UDPScan() && ports.udp_count == 0) + error("WARNING: UDP scan was requested, but no common udp ports were specified. Unless you specified per-target ports, the scan will be useless."); + if (o.ipprotscan && ports.prot_count == 0) + error("WARNING: protocol scan was requested, but no common protocols were specified to be scanned. Unless you specified per-target ports, the scan will be useless."); + } - if (o.pingtype & PINGTYPE_TCP && ports.syn_ping_count+ports.ack_ping_count == 0) + if (o.pingtype & PINGTYPE_TCP && ports.syn_ping_count + ports.ack_ping_count == 0) error("WARNING: a TCP ping scan was requested, but after excluding requested TCP ports, none remain. Skipping this scan type."); if (o.pingtype & PINGTYPE_UDP && ports.udp_ping_count == 0) error("WARNING: a UDP ping scan was requested, but after excluding requested UDP ports, none remain. Skipping this scan type."); @@ -1783,7 +1748,7 @@ // Free some global memory allocations. // This is used for detecting memory leaks. void nmap_free_mem() { - PortList::freePortMap(); + PortList::freeCommonPortMap(); cp_free(); free_services(); AllProbes::service_scan_free(); @@ -1947,7 +1912,7 @@ /* Before we randomize the ports scanned, lets output them to machine parseable output */ if (o.verbose) - output_ports_to_machine_parseable_output(&ports); + output_common_ports_to_machine_parseable_output(&ports); #if defined(HAVE_SIGNAL) && defined(SIGPIPE) signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE so our program doesn't crash because @@ -1976,13 +1941,13 @@ /* Before we randomize the ports scanned, we must initialize PortList class. */ if (o.ipprotscan) - PortList::initializePortMap(IPPROTO_IP, ports.prots, ports.prot_count); + PortList::initializeCommonPortMap(IPPROTO_IP, ports.prots, ports.prot_count); if (o.TCPScan()) - PortList::initializePortMap(IPPROTO_TCP, ports.tcp_ports, ports.tcp_count); + PortList::initializeCommonPortMap(IPPROTO_TCP, ports.tcp_ports, ports.tcp_count); if (o.UDPScan()) - PortList::initializePortMap(IPPROTO_UDP, ports.udp_ports, ports.udp_count); + PortList::initializeCommonPortMap(IPPROTO_UDP, ports.udp_ports, ports.udp_count); if (o.SCTPScan()) - PortList::initializePortMap(IPPROTO_SCTP, ports.sctp_ports, ports.sctp_count); + PortList::initializeCommonPortMap(IPPROTO_SCTP, ports.sctp_ports, ports.sctp_count); if (o.randomize_ports) { if (ports.tcp_count) { @@ -2047,6 +2012,7 @@ if (!currenths) break; + if (currenths->flags & HOST_UP && !o.listscan) o.numhosts_up++; @@ -2187,12 +2153,19 @@ /* These lame functions can only handle one target at a time */ if (o.idlescan) { for (targetno = 0; targetno < Targets.size(); targetno++) { + unsigned short * merged_tcp_ports = NULL; + int merged_tcp_ports_count = 0; + merged_tcp_ports = merge_port_lists(ports.tcp_ports, ports.tcp_count, Targets[targetno]->target_ports.tcp_ports, Targets[targetno]->target_ports.tcp_count, &merged_tcp_ports_count); o.current_scantype = IDLE_SCAN; keyWasPressed(); // Check if a status message should be printed - idle_scan(Targets[targetno], ports.tcp_ports, - ports.tcp_count, o.idleProxy, &ports); + + //The ports structure at the end is only used for probing the proxy, so we don't need to merge that. + idle_scan(Targets[targetno], merged_tcp_ports, + merged_tcp_ports_count, o.idleProxy, &ports); + free(merged_tcp_ports); } } + if (o.bouncescan) { for (targetno = 0; targetno < Targets.size(); targetno++) { o.current_scantype = BOUNCE_SCAN; @@ -2200,7 +2173,13 @@ if (ftp.sd <= 0) ftp_anon_connect(&ftp); if (ftp.sd > 0) - bounce_scan(Targets[targetno], ports.tcp_ports, ports.tcp_count, &ftp); + { + unsigned short * merged_tcp_ports = NULL; + int merged_tcp_ports_count = 0; + merged_tcp_ports = merge_port_lists(ports.tcp_ports, ports.tcp_count, Targets[targetno]->target_ports.tcp_ports, Targets[targetno]->target_ports.tcp_count, &merged_tcp_ports_count); + bounce_scan(Targets[targetno], merged_tcp_ports, merged_tcp_ports_count, &ftp); + free(merged_tcp_ports); + } } } Index: output.cc =================================================================== --- output.cc (revision 37608) +++ output.cc (working copy) @@ -878,6 +878,9 @@ xml_end_tag(); /* ports */ xml_newline(); + + + if (o.defeat_rst_ratelimit && o.TCPScan() && plist->getStateCounts(PORT_FILTERED) > 0) { log_write(LOG_PLAIN, "Some closed ports may be reported as filtered due to --defeat-rst-ratelimit\n"); } @@ -1176,7 +1179,7 @@ int udpportsscanned = ports->udp_count; int sctpportsscanned = ports->sctp_count; int protsscanned = ports->prot_count; - log_write(LOG_MACHINE, "# Ports scanned: TCP(%d;", tcpportsscanned); + log_write(LOG_MACHINE, "TCP(%d;", tcpportsscanned); if (tcpportsscanned) output_rangelist_given_ports(LOG_MACHINE, ports->tcp_ports, tcpportsscanned); log_write(LOG_MACHINE, ") UDP(%d;", udpportsscanned); @@ -1189,9 +1192,26 @@ if (protsscanned) output_rangelist_given_ports(LOG_MACHINE, ports->prots, protsscanned); log_write(LOG_MACHINE, ")\n"); +} + +void output_common_ports_to_machine_parseable_output(struct scan_lists *ports) +{ + log_write(LOG_MACHINE, "# Ports scanned : "); + output_ports_to_machine_parseable_output(ports); log_flush_all(); } +/* Output the list of ports scanned to the top of machine parseable +logs (in a comment, unfortunately). The items in ports should be +in sequential order for space savings and easier to read output */ +void output_target_ports_to_machine_parseable_output(Target *currenths) { + if (currenths->target_ports.tcp_count == 0 && currenths->target_ports.udp_count == 0 && currenths->target_ports.sctp_count == 0 && currenths->target_ports.prot_count == 0) + return; + log_write(LOG_MACHINE, "Host: %s (%s)\tAdditional ports scanned: ", + currenths->targetipstr(), currenths->HostName()); + output_ports_to_machine_parseable_output(¤ths->target_ports); +} + // A simple helper function for doscaninfo handles the c14n of o.scanflags static void doscanflags() { struct { @@ -1361,6 +1381,8 @@ xml_end_tag(); xml_newline(); } + xml_newline(); + output_xml_scaninfo_records(¤ths->target_ports); log_flush_all(); } @@ -1465,6 +1487,9 @@ log_write(LOG_PLAIN, "rDNS record for %s: %s\n", currenths->targetipstr(), currenths->HostName()); } + + if (o.verbose) + output_target_ports_to_machine_parseable_output(currenths); } /* Writes host status info to the log streams (including STDOUT). An Index: output.h =================================================================== --- output.h (revision 37608) +++ output.h (working copy) @@ -230,10 +230,16 @@ it already exists. If the file does not exist, it will be created */ int log_open(int logt, bool append, char *filename); -/* Output the list of ports scanned to the top of machine parseable + +/* Output the list of ports scanned for this specific target + to machine parseable logs When using per-target ports, this information + is more valuable than ports used in a regular scan, so it's not a comment.*/ +void output_target_ports_to_machine_parseable_output(Target *currenths); + +/* Output the list of common ports scanned to the top of machine parseable logs (in a comment, unfortunately). The items in ports should be in sequential order for space savings and easier to read output */ -void output_ports_to_machine_parseable_output(struct scan_lists *ports); +void output_common_ports_to_machine_parseable_output(struct scan_lists *ports); /* Return a std::string containing all n strings separated by whitespace, and individually quoted if needed. */ Index: portlist.cc =================================================================== --- portlist.cc (revision 37608) +++ portlist.cc (working copy) @@ -471,13 +471,19 @@ memset(port_list, 0, sizeof(port_list)); for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) { - if(port_list_count[proto] > 0) - port_list[proto] = (Port**) safe_zalloc(sizeof(Port*)*port_list_count[proto]); + if(common_port_list_count[proto] > 0) + port_list[proto] = (Port**) safe_zalloc(sizeof(Port*)*common_port_list_count[proto]); default_port_state[proto].proto = PORTLISTPROTO2INPROTO(proto); default_port_state[proto].reason.reason_id = ER_NORESPONSE; - state_counts_proto[proto][default_port_state[proto].state] = port_list_count[proto]; + state_counts_proto[proto][default_port_state[proto].state] = common_port_list_count[proto]; + + has_specific_ports[proto] = false; + port_map[proto] = common_port_map[proto]; + port_map_rev[proto] = common_port_map_rev[proto]; + port_list_count[proto] = common_port_list_count[proto]; } + numscriptresults = 0; idstr = NULL; } @@ -743,49 +749,51 @@ /* Just free memory used by PortList::port_map[]. Should be done somewhere * before closing nmap. */ -void PortList::freePortMap() { +void PortList::freeCommonPortMap() { int proto; for (proto=0; proto < PORTLIST_PROTO_MAX; proto++) { - if (port_map[proto]) { - free(port_map[proto]); - port_map[proto] = NULL; + if (common_port_map[proto]) { + free(common_port_map[proto]); + common_port_map[proto] = NULL; } - if (port_map_rev[proto]) { - free(port_map_rev[proto]); - port_map_rev[proto] = NULL; + if (common_port_map_rev[proto]) { + free(common_port_map_rev[proto]); + common_port_map_rev[proto] = NULL; } - port_list_count[proto] = 0; + common_port_list_count[proto] = 0; } } -u16 *PortList::port_map[PORTLIST_PROTO_MAX]; -u16 *PortList::port_map_rev[PORTLIST_PROTO_MAX]; -int PortList::port_list_count[PORTLIST_PROTO_MAX]; +u16 *PortList::common_port_map[PORTLIST_PROTO_MAX]; +u16 *PortList::common_port_map_rev[PORTLIST_PROTO_MAX]; +int PortList::common_port_list_count[PORTLIST_PROTO_MAX]; /* This function must be run before any PortList object is created. * It must be run for every used protocol. The data in "ports" * should be sorted. */ -void PortList::initializePortMap(int protocol, u16 *ports, int portcount) { +void PortList::initializeCommonPortMap(int protocol, u16 *ports, int portcount) { + if (portcount == 0) + return; int i; int ports_max = (protocol == IPPROTO_IP) ? 256 : 65536; int proto = INPROTO2PORTLISTPROTO(protocol); - if (port_map[proto] != NULL || port_map_rev[proto] != NULL) + if (common_port_map[proto] != NULL || common_port_map_rev[proto] != NULL) fatal("%s: portmap for protocol %i already initialized", __func__, protocol); - assert(port_list_count[proto]==0); + assert(common_port_list_count[proto]==0); /* this memory will never be freed, but this is the way it has to be. */ - port_map[proto] = (u16 *) safe_zalloc(sizeof(u16) * ports_max); - port_map_rev[proto] = (u16 *) safe_zalloc(sizeof(u16) * portcount); + common_port_map[proto] = (u16 *) safe_zalloc(sizeof(u16) * ports_max); + common_port_map_rev[proto] = (u16 *) safe_zalloc(sizeof(u16) * portcount); - port_list_count[proto] = portcount; + common_port_list_count[proto] = portcount; for(i=0; i < portcount; i++) { - port_map[proto][ports[i]] = i; - port_map_rev[proto][i] = ports[i]; + common_port_map[proto][ports[i]] = i; + common_port_map_rev[proto][i] = ports[i]; } /* So now port_map should have such structure (lets scan 2nd,4th and 6th port): * port_map[0,0,1,0,2,0,3,...] <- indexes to port_list structure @@ -792,6 +800,84 @@ * port_list[port_2,port_4,port_6] */ } +void PortList::mergeHostSpecificPorts(int protocol, u16 *ports, int portcount) +{ + if (portcount == 0) return; + + int i; + int ports_max = (protocol == IPPROTO_IP) ? 256 : 65536; + int proto = INPROTO2PORTLISTPROTO(protocol); + + if (port_map[proto] == NULL) + { + port_map[proto] = (u16 *)safe_zalloc(sizeof(u16) * ports_max); + port_map_rev[proto] = (u16 *)safe_zalloc(sizeof(u16) * portcount); + + port_list_count[proto] = portcount; + + for (i = 0; i < portcount; i++) { + port_map[proto][ports[i]] = i; + port_map_rev[proto][i] = ports[i]; + } + + has_specific_ports[proto] = true; + } + else { + + int new_ports_count = 0; + for (i = 0; i < portcount; i++) { + if (port_map[proto][i] == 0 && port_map_rev[proto][0] != i) + new_ports_count++; + } + + int old_count = port_list_count[proto]; + int new_count = old_count + new_ports_count; + u16* new_port_map = (u16 *)safe_zalloc(sizeof(u16) * ports_max); + u16* new_port_map_rev = (u16 *)safe_zalloc(sizeof(u16) * new_count); + + memcpy(new_port_map, port_map[proto], sizeof(u16) * ports_max); + memcpy(new_port_map_rev, port_map_rev[proto], sizeof(u16) * old_count); + + if (has_specific_ports[proto]) + { + free(port_map[proto]); + free(port_map_rev[proto]); + } + + has_specific_ports[proto] = true; + + for (i = 0; i < portcount; i++) { + if (new_port_map[ports[i]] != 0 || new_port_map_rev[0] == i) + continue; + + new_port_map[ports[i]] = old_count; + new_port_map_rev[old_count] = ports[i]; + old_count++; + } + + port_map[proto] = new_port_map; + port_map_rev[proto] = new_port_map_rev; + port_list_count[proto] = new_count; + } + + for (proto = 0; proto < PORTLIST_PROTO_MAX; proto++) { + + if (port_list[proto]) + free(port_list[proto]); + + if (port_list_count[proto] > 0) + port_list[proto] = (Port**)safe_zalloc(sizeof(Port*)*port_list_count[proto]); + + default_port_state[proto].proto = PORTLISTPROTO2INPROTO(proto); + default_port_state[proto].reason.reason_id = ER_NORESPONSE; + state_counts_proto[proto][default_port_state[proto].state] = port_list_count[proto]; + } + + numscriptresults = 0; + idstr = NULL; + +} + /* Cycles through the 0 or more "ignored" ports which should be consolidated for Nmap output. They are returned sorted by the number of ports in the state, starting with the most common. It @@ -1004,4 +1090,3 @@ } return "unknown"; } - Index: portlist.h =================================================================== --- portlist.h (revision 37608) +++ portlist.h (working copy) @@ -246,9 +246,10 @@ ~PortList(); /* Set ports that will be scanned for each protocol. This function * must be called before any PortList object will be created. */ - static void initializePortMap(int protocol, u16 *ports, int portcount); + static void initializeCommonPortMap(int protocol, u16 *ports, int portcount); + void mergeHostSpecificPorts(int protocol, u16 *ports, int portcount); /* Free memory used by port_map. It should be done somewhere before quitting*/ - static void freePortMap(); + static void freeCommonPortMap(); void setDefaultPortState(u8 protocol, int state); void setPortState(u16 portno, u8 protocol, int state); @@ -346,14 +347,20 @@ /* Number of ports in each state per each protocol. */ int state_counts_proto[PORTLIST_PROTO_MAX][PORT_HIGHEST_STATE]; Port **port_list[PORTLIST_PROTO_MAX]; + + u16 *port_map[PORTLIST_PROTO_MAX]; + u16 *port_map_rev[PORTLIST_PROTO_MAX]; + int port_list_count[PORTLIST_PROTO_MAX]; + + bool has_specific_ports[PORTLIST_PROTO_MAX]; protected: /* Maps port_number to index in port_list array. * Only functions: getPortEntry, setPortEntry, initializePortMap and * nextPort should access this structure directly. */ - static u16 *port_map[PORTLIST_PROTO_MAX]; - static u16 *port_map_rev[PORTLIST_PROTO_MAX]; + static u16 *common_port_map[PORTLIST_PROTO_MAX]; + static u16 *common_port_map_rev[PORTLIST_PROTO_MAX]; /* Number of allocated elements in port_list per each protocol. */ - static int port_list_count[PORTLIST_PROTO_MAX]; + static int common_port_list_count[PORTLIST_PROTO_MAX]; Port default_port_state[PORTLIST_PROTO_MAX]; }; Index: scan_engine.cc =================================================================== --- scan_engine.cc (revision 37608) +++ scan_engine.cc (working copy) @@ -335,7 +335,7 @@ to.timeout = MAX(o.minRttTimeout(), MIN(o.initialRttTimeout(), INITIAL_ARP_RTT_TIMEOUT)) * 1000; num_probes_active = 0; numtargets = USI->numIncompleteHosts(); // They are all incomplete at the beginning - numprobes = USI->numProbesPerHost(); + numprobes = USI->maxNumProbesPerHost(); if (USI->scantype == CONNECT_SCAN || USI->ptech.connecttcpscan) CSI = new ConnectScanInfo; @@ -846,7 +846,7 @@ // This is inexact (maxtries - 1) because numprobes_sent includes // at least one try of ports_finished. thishostpercdone = host->ports_finished * (maxtries - 1) + host->numprobes_sent; - thishostpercdone /= maxtries * gstats->numprobes; + thishostpercdone /= maxtries * numProbesPerHost(host); if (thishostpercdone >= 0.9999) thishostpercdone = 0.9999; total += thishostpercdone; @@ -861,6 +861,7 @@ std::vector::iterator target; for (target = targets.begin(); target != targets.end(); target++) { + switch (scantype) { case SYN_SCAN: case ACK_SCAN: @@ -1034,19 +1035,28 @@ } } +#define max_macro(x, y) (((x) > (y)) ? (x) : (y)) +#define collect(A,B) for (host = incompleteHosts.begin(); host != incompleteHosts.end(); host++) A = max_macro(A, ports->B + (*host)->target->target_ports.B); \ + for (host = completedHosts.begin(); host != completedHosts.end(); host++) A = max_macro(A, ports->B + (*host)->target->target_ports.B); + /* Return the total number of probes that may be sent to each host. This never changes after initialization. */ -unsigned int UltraScanInfo::numProbesPerHost() { - unsigned int numprobes = 0; +unsigned int UltraScanInfo::maxNumProbesPerHost() { + int numprobes = 0; + std::multiset::iterator host; if (tcp_scan) { numprobes = ports->tcp_count; + collect(numprobes, tcp_count); } else if (udp_scan) { numprobes = ports->udp_count; + collect(numprobes, udp_count); } else if (sctp_scan) { numprobes = ports->sctp_count; + collect(numprobes, sctp_count); } else if (prot_scan) { numprobes = ports->prot_count; + collect(numprobes, prot_count); } else if (ping_scan_arp) { numprobes = 1; } else if (ping_scan_nd) { @@ -1077,9 +1087,64 @@ numprobes += ports->syn_ping_count; } else assert(0); + assert(numprobes >= 0); + return numprobes; } +/* Return the total number of probes that will be sent to each host. */ +unsigned int UltraScanInfo::numProbesPerHost(HostScanStats* host) { + int numprobes; + + + if (tcp_scan) { + numprobes = ports->tcp_count + host->target->target_ports.tcp_count; + } + else if (udp_scan) { + numprobes = ports->udp_count + host->target->target_ports.udp_count; + } + else if (sctp_scan) { + numprobes = ports->sctp_count + host->target->target_ports.sctp_count; + } + else if (prot_scan) { + numprobes = ports->prot_count + host->target->target_ports.prot_count; + } + else if (ping_scan_arp) { + numprobes = 1; + } + else if (ping_scan_nd) { + numprobes = 1; + } + else if (ping_scan) { + numprobes = 0; + if (ptech.rawtcpscan) { + if (o.pingtype & PINGTYPE_TCP_USE_ACK) + numprobes += ports->ack_ping_count; + if (o.pingtype & PINGTYPE_TCP_USE_SYN) + numprobes += ports->syn_ping_count; + } + if (ptech.rawudpscan) + numprobes += ports->udp_ping_count; + if (ptech.rawsctpscan) + numprobes += ports->sctp_ping_count; + if (ptech.rawicmpscan) { + if (o.pingtype & PINGTYPE_ICMP_PING) + numprobes++; + if (o.pingtype & PINGTYPE_ICMP_MASK) + numprobes++; + if (o.pingtype & PINGTYPE_ICMP_TS) + numprobes++; + } + if (ptech.rawprotoscan) + numprobes += ports->proto_ping_count; + if (ptech.connecttcpscan) + numprobes += ports->syn_ping_count; + } + else assert(0); + + return numprobes; +} + /* Consults with the group stats, and the hstats for every incomplete hosts to determine whether any probes may be sent. Returns true if they can be sent immediately. If when is @@ -1349,7 +1414,7 @@ assert(pspec); if (USI->tcp_scan) { - if (hss->next_portidx >= USI->ports->tcp_count) + if (hss->next_portidx >= USI->ports->tcp_count + hss->target->target_ports.tcp_count) return -1; if (USI->scantype == CONNECT_SCAN) pspec->type = PS_CONNECTTCP; @@ -1357,7 +1422,14 @@ pspec->type = PS_TCP; pspec->proto = IPPROTO_TCP; - pspec->pd.tcp.dport = USI->ports->tcp_ports[hss->next_portidx++]; + if (hss->next_portidx >= USI->ports->tcp_count) + { + pspec->pd.tcp.dport = hss->target->target_ports.tcp_ports[(hss->next_portidx++)-(USI->ports->tcp_count)]; + } + else { + pspec->pd.tcp.dport = USI->ports->tcp_ports[hss->next_portidx++]; + } + if (USI->scantype == CONNECT_SCAN) pspec->pd.tcp.flags = TH_SYN; else if (o.scanflags != -1) @@ -1392,18 +1464,30 @@ } return 0; } else if (USI->udp_scan) { - if (hss->next_portidx >= USI->ports->udp_count) + if (hss->next_portidx >= USI->ports->udp_count + hss->target->target_ports.udp_count) return -1; pspec->type = PS_UDP; pspec->proto = IPPROTO_UDP; - pspec->pd.udp.dport = USI->ports->udp_ports[hss->next_portidx++]; + if (hss->next_portidx >= USI->ports->udp_count) + { + pspec->pd.udp.dport = hss->target->target_ports.udp_ports[(hss->next_portidx++) - (USI->ports->udp_count)]; + } + else { + pspec->pd.udp.dport = USI->ports->udp_ports[hss->next_portidx++]; + } return 0; } else if (USI->sctp_scan) { - if (hss->next_portidx >= USI->ports->sctp_count) + if (hss->next_portidx >= USI->ports->sctp_count + hss->target->target_ports.sctp_count) return -1; pspec->type = PS_SCTP; pspec->proto = IPPROTO_SCTP; - pspec->pd.sctp.dport = USI->ports->sctp_ports[hss->next_portidx++]; + if (hss->next_portidx >= USI->ports->sctp_count) + { + pspec->pd.sctp.dport = hss->target->target_ports.sctp_ports[(hss->next_portidx++) - (USI->ports->sctp_count)]; + } + else { + pspec->pd.sctp.dport = USI->ports->sctp_ports[hss->next_portidx++]; + } switch (USI->scantype) { case SCTP_INIT_SCAN: pspec->pd.sctp.chunktype = SCTP_INIT; @@ -1416,9 +1500,16 @@ } return 0; } else if (USI->prot_scan) { - if (hss->next_portidx >= USI->ports->prot_count) + if (hss->next_portidx >= USI->ports->prot_count + hss->target->target_ports.prot_count) return -1; pspec->type = PS_PROTO; + if (hss->next_portidx >= USI->ports->prot_count) + { + pspec->proto = hss->target->target_ports.prots[(hss->next_portidx++) - (USI->ports->prot_count)]; + } + else { + pspec->proto = USI->ports->prots[hss->next_portidx++]; + } pspec->proto = USI->ports->prots[hss->next_portidx++]; return 0; } else if (USI->ping_scan_arp) { @@ -1527,21 +1618,21 @@ /* Returns the number of ports remaining to probe */ int HostScanStats::freshPortsLeft() { if (USI->tcp_scan) { - if (next_portidx >= USI->ports->tcp_count) + if (next_portidx >= USI->ports->tcp_count + target->target_ports.tcp_count) return 0; - return USI->ports->tcp_count - next_portidx; + return USI->ports->tcp_count + target->target_ports.tcp_count - next_portidx; } else if (USI->udp_scan) { - if (next_portidx >= USI->ports->udp_count) + if (next_portidx >= USI->ports->udp_count + target->target_ports.udp_count) return 0; - return USI->ports->udp_count - next_portidx; + return USI->ports->udp_count + target->target_ports.udp_count - next_portidx; } else if (USI->sctp_scan) { - if (next_portidx >= USI->ports->sctp_count) + if (next_portidx >= USI->ports->sctp_count + target->target_ports.sctp_count) return 0; - return USI->ports->sctp_count - next_portidx; + return USI->ports->sctp_count + target->target_ports.sctp_count - next_portidx; } else if (USI->prot_scan) { - if (next_portidx >= USI->ports->prot_count) + if (next_portidx >= USI->ports->prot_count + target->target_ports.prot_count) return 0; - return USI->ports->prot_count - next_portidx; + return USI->ports->prot_count + target->target_ports.prot_count - next_portidx; } else if (USI->ping_scan_arp) { if (sent_arp) return 0; @@ -2747,7 +2838,7 @@ if (!plural) { (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr)); } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) Targets.size()); - log_write(LOG_STDOUT, "Scanning %s [%d port%s%s]\n", targetstr, USI.gstats->numprobes, (USI.gstats->numprobes != 1) ? "s" : "", plural ? "/host" : ""); + log_write(LOG_STDOUT, "Scanning %s [max %d port%s%s]\n", targetstr, USI.gstats->numprobes, (USI.gstats->numprobes != 1) ? "s" : "", plural ? "/host" : ""); } if (USI.isRawScan()) @@ -2797,8 +2888,8 @@ Snprintf(additional_info, sizeof(additional_info), "%lu total hosts", (unsigned long) Targets.size()); } else { - Snprintf(additional_info, sizeof(additional_info), "%lu total ports", - (unsigned long) USI.gstats->numprobes * Targets.size()); + Snprintf(additional_info, sizeof(additional_info), "%lu total ports max", + (unsigned long) USI.gstats->totalprobes * Targets.size()); } else Snprintf(additional_info, sizeof(additional_info), "%d %s timed out", USI.gstats->num_hosts_timedout, Index: scan_engine.h =================================================================== --- scan_engine.h (revision 37608) +++ scan_engine.h (working copy) @@ -360,6 +360,7 @@ struct timeout_info to; /* Group-wide packet rtt/timeout info */ int numtargets; /* Total # of targets scanned -- includes finished and incomplete hosts */ int numprobes; /* Number of probes/ports scanned on each host */ + int totalprobes; /* The last time waitForResponses finished (initialized to GSS creation time */ int probes_sent; /* Number of probes sent in total. This DOES include pings and retransmissions */ @@ -628,7 +629,8 @@ /* Must call Init if you create object with default constructor */ void Init(std::vector &Targets, struct scan_lists *pts, stype scantp); - unsigned int numProbesPerHost(); + unsigned int numProbesPerHost(HostScanStats* host); + unsigned int maxNumProbesPerHost(); /* Consults with the group stats, and the hstats for every incomplete hosts to determine whether any probes may be sent. Index: scan_lists.cc =================================================================== --- scan_lists.cc (revision 37608) +++ scan_lists.cc (working copy) @@ -239,7 +239,11 @@ } if (range_type != 0 && 0 == (ports->tcp_count + ports->udp_count + ports->sctp_count + ports->prot_count)) - fatal("No ports specified -- If you really don't want to scan any ports use ping scan..."); + { + error("No common ports specified - Unless you specified ports for specific targets, you should use ping scan..."); + o.nocommonports = true; + } + if (ports->tcp_count) { ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short)); @@ -593,8 +597,46 @@ free(ports->proto_ping_ports); } +struct scan_lists clone_scan_lists(const struct scan_lists *ports) +{ + scan_lists s = { 0 }; + if (ports->tcp_ports) + { + s.tcp_count = ports->tcp_count; + size_t tcp_size = ports->tcp_count * sizeof(unsigned short); + s.tcp_ports = (unsigned short *)safe_zalloc(tcp_size); + memcpy(s.tcp_ports, ports->tcp_ports, tcp_size); + } + if (ports->udp_ports) + { + s.udp_count = ports->udp_count; + size_t udp_size = ports->udp_count * sizeof(unsigned short); + s.udp_ports = (unsigned short *)safe_zalloc(udp_size); + memcpy(s.udp_ports, ports->udp_ports, udp_size); + } + if (ports->sctp_ports) + { + s.sctp_count = ports->sctp_count; + size_t sctp_size = ports->sctp_count * sizeof(unsigned short); + s.sctp_ports = (unsigned short *)safe_zalloc(sctp_size); + memcpy(s.sctp_ports, ports->sctp_ports, sctp_size); + } + + if (ports->prots) + { + s.prot_count = ports->prot_count; + size_t prots_size = ports->prot_count * sizeof(unsigned short); + s.prots = (unsigned short *)safe_zalloc(prots_size); + memcpy(s.prots, ports->prots, prots_size); + } + + return s; +} + + + /* Just a routine for obtaining a string for printing based on the scantype */ const char *scantype2str(stype scantype) { @@ -683,3 +725,48 @@ } +static void insert_port_into_merge_list(unsigned short *mlist, + int *merged_port_count, + unsigned short p) { + int i; + // make sure the port isn't already in the list + for (i = 0; i < *merged_port_count; i++) { + if (mlist[i] == p) { + return; + } + } + mlist[*merged_port_count] = p; + (*merged_port_count)++; +} + +unsigned short *merge_port_lists(unsigned short *port_list1, int count1, + unsigned short *port_list2, int count2, + int *merged_port_count) { + int i; + unsigned short *merged_port_list = NULL; + + *merged_port_count = 0; + + merged_port_list = + (unsigned short *)safe_zalloc((count1 + count2) * sizeof(unsigned short)); + + for (i = 0; i < count1; i++) { + insert_port_into_merge_list(merged_port_list, + merged_port_count, + port_list1[i]); + } + for (i = 0; i < count2; i++) { + insert_port_into_merge_list(merged_port_list, + merged_port_count, + port_list2[i]); + } + + // if there were duplicate ports then we can save some memory + if (*merged_port_count < (count1 + count2)) { + merged_port_list = (unsigned short*) + safe_realloc(merged_port_list, + (*merged_port_count) * sizeof(unsigned short)); + } + + return merged_port_list; +} \ No newline at end of file Index: scan_lists.h =================================================================== --- scan_lists.h (revision 37608) +++ scan_lists.h (working copy) @@ -190,8 +190,10 @@ unsigned short **list, int *count); void removepts(const char *expr, struct scan_lists * ports); void free_scan_lists(struct scan_lists *ports); +struct scan_lists clone_scan_lists(const struct scan_lists *ports); /* general helper functions */ const char *scantype2str(stype scantype); +unsigned short *merge_port_lists(unsigned short *port_list1, int count1, unsigned short *port_list2, int count2, int *merged_port_count); #endif /* SCAN_LISTS_H */ Index: targets.cc =================================================================== --- targets.cc (revision 37608) +++ targets.cc (working copy) @@ -446,6 +446,13 @@ t->unscanned_addrs = hs->current_group.get_unscanned_addrs(); } + const scan_lists* target_ports = &hs->current_group.group_ports; + t->target_ports = clone_scan_lists(&hs->current_group.group_ports); + t->ports.mergeHostSpecificPorts(IPPROTO_IP, target_ports->prots, target_ports->prot_count); + t->ports.mergeHostSpecificPorts(IPPROTO_TCP, target_ports->tcp_ports, target_ports->tcp_count); + t->ports.mergeHostSpecificPorts(IPPROTO_UDP, target_ports->udp_ports, target_ports->udp_count); + t->ports.mergeHostSpecificPorts(IPPROTO_SCTP, target_ports->sctp_ports, target_ports->sctp_count); + /* We figure out the source IP/device IFF * the scan type requires us to */ if (o.RawScan()) {