From 136b8fa28048d123939c4366ecc9ccc933f62a40 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 26 Feb 2013 03:39:24 +0000 Subject: [PATCH] Add a defer buffer to HostGroupState. This allows some targets to be skipped over yet remembered so they can be dealt with later. The idea is that because ping groups are not allowed to have duplicate IPs, we continue searching for non-duplicates in order to fill up a ping group, then return to what were formerly duplicates. This prevents potentially large ping groups from being split into small groups. For example, if the list of targets is A B C D A B E A F G the ping groups used to be (A B C D) (A B E) (A F G) but now they are (A B C D E F G) (A B) (A] A similar thing can be done for port scan hostgroups, but this already does most of the work because ping groups are generally bigger than hostgroups and have pretty much the same restrictions. --- targets.cc | 154 +++++++++++++++++++++++++++++------------------------ targets.h | 17 ++++-- 2 files changed, 95 insertions(+), 76 deletions(-) diff --git a/targets.cc b/targets.cc index 65de9f31f..1dd199046 100644 --- a/targets.cc +++ b/targets.cc @@ -327,13 +327,6 @@ int TargetGroup::parse_expr(const char *target_expr, int af) { fills in ss if successful. ss must point to a pre-allocated sockaddr_storage structure */ int TargetGroup::get_next_host(struct sockaddr_storage *ss, size_t *sslen) { - if (this->pushback.ss_family != AF_UNSPEC) { - *ss = this->pushback; - *sslen = sizeof(*ss); - this->pushback.ss_family = AF_UNSPEC; - return 0; - } - if (this->netblock == NULL) return -1; @@ -359,15 +352,6 @@ int TargetGroup::get_next_host(struct sockaddr_storage *ss, size_t *sslen) { return -1; } -/* Returns the given host, so that it will be given the next time - get_next_host is called. Does nothing if ss is NULL. */ -void TargetGroup::return_host(const struct sockaddr_storage *ss) { - if (ss == NULL) - return; - assert(this->pushback.ss_family == AF_UNSPEC); - this->pushback = *ss; -} - /* Returns true iff the given address is the one that was resolved to create this target group; i.e., not one of the addresses derived from it with a netmask. */ @@ -405,6 +389,8 @@ HostGroupState::HostGroupState(int lookahead, int rnd, int argc, const char **ar this->argc = argc; this->argv = argv; hostbatch = (Target **) safe_zalloc(sizeof(Target *) * lookahead); + defer_buffer = std::list(); + undeferred = std::list(); max_batch_sz = lookahead; current_batch_sz = 0; next_batch_no = 0; @@ -415,6 +401,15 @@ HostGroupState::~HostGroupState() { free(hostbatch); } +bool HostGroupState::defer(Target *t) { + this->defer_buffer.push_back(t); + return true; +} + +void HostGroupState::undefer() { + this->undeferred.splice(this->undeferred.end(), this->defer_buffer); +} + const char *HostGroupState::next_expression() { static char buf[1024]; @@ -525,74 +520,83 @@ bail: return NULL; } -Target *nexthost(HostGroupState *hs, const addrset *exclude_group, - struct scan_lists *ports, int pingtype) { - int i; +static Target *next_target(HostGroupState *hs, const addrset *exclude_group, + struct scan_lists *ports, int pingtype) { struct sockaddr_storage ss; size_t sslen; + Target *t; + + /* First handle targets deferred in the last batch. */ + if (!hs->undeferred.empty()) { + t = hs->undeferred.front(); + hs->undeferred.pop_front(); + return t; + } + +tryagain: + + if (hs->current_group.get_next_host(&ss, &sslen) != 0) { + const char *expr; + /* We are going to have to pop in another expression. */ + for (;;) { + expr = hs->next_expression(); + if (expr == NULL) + /* That's the last of them. */ + return NULL; + if (hs->current_group.parse_expr(expr, o.af()) == 0) + break; + else + log_bogus_target(expr); + } + goto tryagain; + } + + /* If we are resuming from a previous scan, we have already finished scanning + up to o.resume_ip. */ + if (ss.ss_family == AF_INET && o.resume_ip.s_addr) { + if (o.resume_ip.s_addr == ((struct sockaddr_in *) &ss)->sin_addr.s_addr) + /* We will continue starting with the next IP. */ + o.resume_ip.s_addr = 0; + goto tryagain; + } + + /* Check exclude list. */ + if (hostInExclude((struct sockaddr *) &ss, sslen, exclude_group)) + goto tryagain; + + t = setup_target(hs, &ss, sslen, pingtype); + if (t == NULL) + goto tryagain; + + return t; +} + +static void refresh_hostbatch(HostGroupState *hs, const addrset *exclude_group, + struct scan_lists *ports, int pingtype) { + int i; bool arpping_done = false; struct timeval now; - if (hs->next_batch_no < hs->current_batch_sz) { - /* Woop! This is easy -- we just pass back the next host struct */ - return hs->hostbatch[hs->next_batch_no++]; - } - - /* Doh, we need to refresh our array */ hs->current_batch_sz = hs->next_batch_no = 0; - for (;;) { - /* Grab anything we have in our current_group */ - while (hs->current_batch_sz < hs->max_batch_sz && - hs->current_group.get_next_host(&ss, &sslen) == 0) { - Target *t; + hs->undefer(); + while (hs->current_batch_sz < hs->max_batch_sz) { + Target *t; - /* If we are resuming from a previous scan, we have already finished - scans up to o.resume_ip. */ - if (ss.ss_family == AF_INET && o.resume_ip.s_addr) { - if (o.resume_ip.s_addr == ((struct sockaddr_in *) &ss)->sin_addr.s_addr) - o.resume_ip.s_addr = 0; /* So that we will KEEP the next one */ - continue; /* Try again */ - } + t = next_target(hs, exclude_group, ports, pingtype); + if (t == NULL) + break; - if (hostInExclude((struct sockaddr *)&ss, sslen, exclude_group)) { - continue; /* Skip any hosts the user asked to exclude */ - } - t = setup_target(hs, &ss, sslen, pingtype); - if (t == NULL) + /* Does this target need to go in a separate host group? */ + if (target_needs_new_hostgroup(hs, t)) { + if (hs->defer(t)) continue; - - /* Does this target need to go in a separate host group? */ - if (target_needs_new_hostgroup(hs, t)) { - /* Cancel everything! This guy must go in the next group and we are - out of here */ - hs->current_group.return_host(t->TargetSockAddr()); - delete t; - goto batchfull; - } - - hs->hostbatch[hs->current_batch_sz++] = t; + else + break; } - if (hs->current_batch_sz < hs->max_batch_sz) { - const char *expr; - /* We are going to have to pop in another expression. */ - for (;;) { - expr = hs->next_expression(); - if (expr == NULL) - goto batchfull; - if (hs->current_group.parse_expr(expr, o.af()) != 0) - log_bogus_target(expr); - else - break; - } - } else break; + hs->hostbatch[hs->current_batch_sz++] = t; } -batchfull: - - if (hs->current_batch_sz == 0) - return NULL; - /* OK, now we have our complete batch of entries. The next step is to randomize them (if requested) */ if (hs->randomize) { @@ -654,6 +658,14 @@ batchfull: if (!o.noresolve) nmap_mass_rdns(hs->hostbatch, hs->current_batch_sz); +} + +Target *nexthost(HostGroupState *hs, const addrset *exclude_group, + struct scan_lists *ports, int pingtype) { + if (hs->next_batch_no >= hs->current_batch_sz) + refresh_hostbatch(hs, exclude_group, ports, pingtype); + if (hs->next_batch_no >= hs->current_batch_sz) + return NULL; return hs->hostbatch[hs->next_batch_no++]; } diff --git a/targets.h b/targets.h index 083e85b63..d65700fd6 100644 --- a/targets.h +++ b/targets.h @@ -123,11 +123,9 @@ class TargetGroup { public: NetBlock *netblock; - struct sockaddr_storage pushback; TargetGroup() { this->netblock = NULL; - this->pushback.ss_family = AF_UNSPEC; } ~TargetGroup() { @@ -144,9 +142,6 @@ public: fills in ss if successful. ss must point to a pre-allocated sockaddr_storage structure */ int get_next_host(struct sockaddr_storage *ss, size_t *sslen); - /* Returns the given host, so that it will be given the next time - get_next_host is called. Does nothing if ss is NULL. */ - void return_host(const struct sockaddr_storage *ss); /* Returns true iff the given address is the one that was resolved to create this target group; i.e., not one of the addresses derived from it with a netmask. */ @@ -165,6 +160,15 @@ public: HostGroupState(int lookahead, int randomize, int argc, const char *argv[]); ~HostGroupState(); Target **hostbatch; + + /* The defer_buffer is a place to store targets that have previously been + returned but that can't be used right now. They wait in defer_buffer until + HostGroupState::undefer is called, at which point they all move to the end + of the undeferred list. HostGroupState::next_target always pulls from the + undeferred list before returning anything new. */ + std::list defer_buffer; + std::list undeferred; + int argc; const char **argv; int max_batch_sz; /* The size of the hostbatch[] array */ @@ -176,7 +180,10 @@ public: at a time to the client program */ TargetGroup current_group; /* For batch chunking -- targets in queue */ + bool defer(Target *t); + void undefer(); const char *next_expression(); + Target *next_target(); }; /* Ports is the list of ports the user asked to be scanned (0 terminated),