mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Fix parallel DNS synchronization issues due to reads arriving before writes are done.
This commit is contained in:
63
nmap_dns.cc
63
nmap_dns.cc
@@ -234,6 +234,11 @@ struct dns_server {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct request {
|
struct request {
|
||||||
|
enum status_t {
|
||||||
|
READY,
|
||||||
|
WRITE_PENDING,
|
||||||
|
DONE
|
||||||
|
};
|
||||||
DNS::Request *targ;
|
DNS::Request *targ;
|
||||||
struct timeval sent;
|
struct timeval sent;
|
||||||
int tries;
|
int tries;
|
||||||
@@ -241,7 +246,13 @@ struct request {
|
|||||||
dns_server *first_server;
|
dns_server *first_server;
|
||||||
dns_server *curr_server;
|
dns_server *curr_server;
|
||||||
u16 id;
|
u16 id;
|
||||||
|
status_t status;
|
||||||
bool alt_req;
|
bool alt_req;
|
||||||
|
request() : targ(NULL), tries(0), servers_tried(0), first_server(NULL),
|
||||||
|
curr_server(NULL), id(0), status(READY), alt_req(false)
|
||||||
|
{
|
||||||
|
memset(&sent, 0, sizeof(sent));
|
||||||
|
}
|
||||||
~request() {
|
~request() {
|
||||||
if (alt_req && targ) {
|
if (alt_req && targ) {
|
||||||
delete targ;
|
delete targ;
|
||||||
@@ -474,11 +485,15 @@ static void do_possible_writes() {
|
|||||||
if (!new_reqs.empty()) {
|
if (!new_reqs.empty()) {
|
||||||
tpreq = new_reqs.front();
|
tpreq = new_reqs.front();
|
||||||
assert(tpreq != NULL);
|
assert(tpreq != NULL);
|
||||||
|
assert(tpreq->targ != NULL);
|
||||||
tpreq->first_server = tpreq->curr_server = &*servI;
|
tpreq->first_server = tpreq->curr_server = &*servI;
|
||||||
new_reqs.pop_front();
|
new_reqs.pop_front();
|
||||||
} else if (!servI->to_process.empty()) {
|
} else if (!servI->to_process.empty()) {
|
||||||
tpreq = servI->to_process.front();
|
tpreq = servI->to_process.front();
|
||||||
servI->to_process.pop_front();
|
servI->to_process.pop_front();
|
||||||
|
assert(tpreq != NULL);
|
||||||
|
assert(tpreq->targ != NULL);
|
||||||
|
assert(tpreq->curr_server == &*servI);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tpreq) {
|
if (tpreq) {
|
||||||
@@ -497,26 +512,31 @@ static void do_possible_writes() {
|
|||||||
// nsock write handler
|
// nsock write handler
|
||||||
static void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v) {
|
static void write_evt_handler(nsock_pool nsp, nsock_event evt, void *req_v) {
|
||||||
assert(nse_type(evt) == NSE_TYPE_WRITE);
|
assert(nse_type(evt) == NSE_TYPE_WRITE);
|
||||||
info record;
|
|
||||||
request *req = (request *) req_v;
|
request *req = (request *) req_v;
|
||||||
|
|
||||||
if (nse_status(evt) == NSE_STATUS_SUCCESS) {
|
if (nse_status(evt) == NSE_STATUS_SUCCESS) {
|
||||||
req->curr_server->in_process.push_front(req);
|
|
||||||
record.tpreq = req;
|
|
||||||
record.server = req->curr_server;
|
|
||||||
records[req->id] = record;
|
|
||||||
do_possible_writes();
|
do_possible_writes();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (o.debugging) {
|
if (o.debugging) {
|
||||||
log_write(LOG_STDOUT, "mass_dns: WRITE error: %s", nse_status2str(nse_status(evt)));
|
log_write(LOG_STDOUT, "mass_dns: WRITE error: %s", nse_status2str(nse_status(evt)));
|
||||||
}
|
}
|
||||||
|
// We don't delete from records in case a response to an earlier probe comes in.
|
||||||
|
req->curr_server->in_process.remove(req);
|
||||||
req->curr_server->to_process.push_front(req);
|
req->curr_server->to_process.push_front(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid runaway recursion: when we call do_possible_writes above,
|
// Avoid runaway recursion: when we call do_possible_writes above,
|
||||||
// make sure we still are "busy"
|
// make sure we still are "busy"
|
||||||
req->curr_server->write_busy = 0;
|
req->curr_server->write_busy = 0;
|
||||||
|
|
||||||
|
if (req->status == request::DONE) {
|
||||||
|
delete req;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(req->status == request::WRITE_PENDING);
|
||||||
|
req->status = request::READY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DNS::RECORD_TYPE wire_type(DNS::RECORD_TYPE t) {
|
static DNS::RECORD_TYPE wire_type(DNS::RECORD_TYPE t) {
|
||||||
@@ -533,9 +553,11 @@ static void put_dns_packet_on_wire(request *req) {
|
|||||||
static const size_t maxlen = 512;
|
static const size_t maxlen = 512;
|
||||||
u8 packet[maxlen];
|
u8 packet[maxlen];
|
||||||
size_t plen=0;
|
size_t plen=0;
|
||||||
|
dns_server *srv = req->curr_server;
|
||||||
|
info record;
|
||||||
|
|
||||||
req->curr_server->write_busy = 1;
|
srv->write_busy = 1;
|
||||||
req->curr_server->reqs_on_wire++;
|
srv->reqs_on_wire++;
|
||||||
DNS::Request &reqt = *req->targ;
|
DNS::Request &reqt = *req->targ;
|
||||||
|
|
||||||
switch(reqt.type) {
|
switch(reqt.type) {
|
||||||
@@ -554,9 +576,15 @@ static void put_dns_packet_on_wire(request *req) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srv->in_process.push_front(req);
|
||||||
|
record.tpreq = req;
|
||||||
|
record.server = srv;
|
||||||
|
records[req->id] = record;
|
||||||
memcpy(&req->sent, nsock_gettimeofday(), sizeof(struct timeval));
|
memcpy(&req->sent, nsock_gettimeofday(), sizeof(struct timeval));
|
||||||
|
|
||||||
nsock_write(dnspool, req->curr_server->nsd, write_evt_handler, WRITE_TIMEOUT, req, reinterpret_cast<const char *>(packet), plen);
|
req->status = request::WRITE_PENDING;
|
||||||
|
nsock_write(dnspool, srv->nsd, write_evt_handler, WRITE_TIMEOUT, req,
|
||||||
|
reinterpret_cast<const char *>(packet), plen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes DNS packets that have timed out
|
// Processes DNS packets that have timed out
|
||||||
@@ -604,7 +632,7 @@ static int deal_with_timedout_reads(bool adjust_timing) {
|
|||||||
if (tpreq->tries > MAX_DNS_TRIES)
|
if (tpreq->tries > MAX_DNS_TRIES)
|
||||||
tpreq->tries = MAX_DNS_TRIES;
|
tpreq->tries = MAX_DNS_TRIES;
|
||||||
servI->in_process.erase(reqI);
|
servI->in_process.erase(reqI);
|
||||||
records.erase(tpreq->id);
|
// We don't erase timed-out probes from records in case a late response comes in.
|
||||||
servI->reqs_on_wire--;
|
servI->reqs_on_wire--;
|
||||||
|
|
||||||
// If we've tried this server enough times, move to the next one
|
// If we've tried this server enough times, move to the next one
|
||||||
@@ -638,12 +666,21 @@ static int deal_with_timedout_reads(bool adjust_timing) {
|
|||||||
stat_dropped++;
|
stat_dropped++;
|
||||||
total_reqs--;
|
total_reqs--;
|
||||||
records.erase(tpreq->id);
|
records.erase(tpreq->id);
|
||||||
|
if (tpreq->status != request::WRITE_PENDING) {
|
||||||
delete tpreq;
|
delete tpreq;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tpreq->status = request::DONE;
|
||||||
|
}
|
||||||
tpreq = NULL;
|
tpreq = NULL;
|
||||||
|
|
||||||
// **** OR We start at the back of this server's queue
|
// **** OR We start at the back of this server's queue
|
||||||
//servItemp->to_process.push_back(tpreq);
|
//servItemp->to_process.push_back(tpreq);
|
||||||
} else {
|
} else {
|
||||||
|
info record;
|
||||||
|
record.tpreq = tpreq;
|
||||||
|
record.server = &*servItemp;
|
||||||
|
records[tpreq->id] = record;
|
||||||
servItemp->to_process.push_back(tpreq);
|
servItemp->to_process.push_back(tpreq);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -695,13 +732,19 @@ static void process_request(int action, info &reqinfo) {
|
|||||||
}
|
}
|
||||||
records.erase(tpreq->id);
|
records.erase(tpreq->id);
|
||||||
server->in_process.remove(tpreq);
|
server->in_process.remove(tpreq);
|
||||||
|
server->to_process.remove(tpreq);
|
||||||
server->reqs_on_wire--;
|
server->reqs_on_wire--;
|
||||||
total_reqs--;
|
total_reqs--;
|
||||||
if (action == ACTION_SYSTEM_RESOLVE && is_primary_req(tpreq)) {
|
if (action == ACTION_SYSTEM_RESOLVE && is_primary_req(tpreq)) {
|
||||||
deferred_reqs.push_back(tpreq);
|
deferred_reqs.push_back(tpreq);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (tpreq->status != request::WRITE_PENDING) {
|
||||||
delete tpreq;
|
delete tpreq;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tpreq->status = request::DONE;
|
||||||
|
}
|
||||||
tpreq = NULL;
|
tpreq = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,7 +866,9 @@ static void read_evt_handler(nsock_pool nsp, nsock_event evt, void *ctx) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info &reqinfo = infoI->second;
|
info &reqinfo = infoI->second;
|
||||||
|
assert(p.id == reqinfo.tpreq->id);
|
||||||
DNS::Request *reqt = reqinfo.tpreq->targ;
|
DNS::Request *reqt = reqinfo.tpreq->targ;
|
||||||
|
assert(reqt != NULL);
|
||||||
bool processing_successful = false;
|
bool processing_successful = false;
|
||||||
|
|
||||||
if (DNS_HAS_ERR(f, DNS::ERR_NAME) || p.answers.empty())
|
if (DNS_HAS_ERR(f, DNS::ERR_NAME) || p.answers.empty())
|
||||||
|
|||||||
Reference in New Issue
Block a user