📄 eventdns.c
字号:
timeout =
&global_nameserver_timeouts[MIN(ns->failed_times,
global_nameserver_timeouts_length - 1)];
ns->failed_times++;
evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) {
log(EVDNS_LOG_WARN,
"Error from libevent when adding timer event for %s",
debug_ntoa(ns->address));
/* ???? Do more? */
}
}
/* called when a nameserver has been deemed to have failed. For example, too */
/* many packets have timed out etc */
static void
nameserver_failed(struct nameserver *const ns, const char *msg) {
struct request *req, *started_at;
/* if this nameserver has already been marked as failed */
/* then don't do anything */
if (!ns->state) return;
log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s",
debug_ntoa(ns->address), msg);
global_good_nameservers--;
assert(global_good_nameservers >= 0);
if (global_good_nameservers == 0) {
log(EVDNS_LOG_WARN, "All nameservers have failed");
}
ns->state = 0;
ns->failed_times = 1;
evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) {
log(EVDNS_LOG_WARN,
"Error from libevent when adding timer event for %s",
debug_ntoa(ns->address));
/* ???? Do more? */
}
/* walk the list of inflight requests to see if any can be reassigned to */
/* a different server. Requests in the waiting queue don't have a */
/* nameserver assigned yet */
/* if we don't have *any* good nameservers then there's no point */
/* trying to reassign requests to one */
if (!global_good_nameservers) return;
req = req_head;
started_at = req_head;
if (req) {
do {
if (req->tx_count == 0 && req->ns == ns) {
/* still waiting to go out, can be moved */
/* to another server */
req->ns = nameserver_pick();
}
req = req->next;
} while (req != started_at);
}
}
static void
nameserver_up(struct nameserver *const ns) {
if (ns->state) return;
log(EVDNS_LOG_WARN, "Nameserver %s is back up",
debug_ntoa(ns->address));
evtimer_del(&ns->timeout_event);
CLEAR(&ns->timeout_event);
ns->state = 1;
ns->failed_times = 0;
ns->timedout = 0;
global_good_nameservers++;
}
static void
request_trans_id_set(struct request *const req, const u16 trans_id) {
req->trans_id = trans_id;
*((u16 *) req->request) = htons(trans_id);
}
/* Called to remove a request from a list and dealloc it. */
/* head is a pointer to the head of the list it should be */
/* removed from or NULL if the request isn't in a list. */
static void
request_finished(struct request *const req, struct request **head) {
if (head) {
if (req->next == req) {
/* only item in the list */
*head = NULL;
} else {
req->next->prev = req->prev;
req->prev->next = req->next;
if (*head == req) *head = req->next;
}
}
log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx",
(unsigned long) req);
evtimer_del(&req->timeout_event);
CLEAR(&req->timeout_event);
search_request_finished(req);
global_requests_inflight--;
if (!req->request_appended) {
/* need to free the request data on it's own */
free(req->request);
} else {
/* the request data is appended onto the header */
/* so everything gets free()ed when we: */
}
CLEAR(req);
_free(req);
evdns_requests_pump_waiting_queue();
}
/* This is called when a server returns a funny error code. */
/* We try the request again with another server. */
/* */
/* return: */
/* 0 ok */
/* 1 failed/reissue is pointless */
static int
request_reissue(struct request *req) {
const struct nameserver *const last_ns = req->ns;
/* the last nameserver should have been marked as failing */
/* by the caller of this function, therefore pick will try */
/* not to return it */
req->ns = nameserver_pick();
if (req->ns == last_ns) {
/* ... but pick did return it */
/* not a lot of point in trying again with the */
/* same server */
return 1;
}
req->reissue_count++;
req->tx_count = 0;
req->transmit_me = 1;
return 0;
}
/* this function looks for space on the inflight queue and promotes */
/* requests from the waiting queue if it can. */
static void
evdns_requests_pump_waiting_queue(void) {
while (global_requests_inflight < global_max_requests_inflight &&
global_requests_waiting) {
struct request *req;
/* move a request from the waiting queue to the inflight queue */
assert(req_waiting_head);
if (req_waiting_head->next == req_waiting_head) {
/* only one item in the queue */
req = req_waiting_head;
req_waiting_head = NULL;
} else {
req = req_waiting_head;
req->next->prev = req->prev;
req->prev->next = req->next;
req_waiting_head = req->next;
}
global_requests_waiting--;
global_requests_inflight++;
req->ns = nameserver_pick();
request_trans_id_set(req, transaction_id_pick());
evdns_request_insert(req, &req_head);
evdns_request_transmit(req);
evdns_transmit();
}
}
static void
reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) {
switch (req->request_type) {
case TYPE_A:
if (reply)
req->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
reply->data.a.addrcount, ttl,
reply->data.a.addresses,
req->user_pointer);
else
req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
return;
case TYPE_PTR:
if (reply) {
char *name = reply->data.ptr.name;
req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl,
&name, req->user_pointer);
} else {
req->user_callback(err, 0, 0, 0, NULL,
req->user_pointer);
}
return;
case TYPE_AAAA:
if (reply)
req->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA,
reply->data.aaaa.addrcount, ttl,
reply->data.aaaa.addresses,
req->user_pointer);
else
req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
return;
}
assert(0);
}
/* this processes a parsed reply packet */
static void
reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) {
int error;
static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED};
if (flags & 0x020f || !reply || !reply->have_answer) {
/* there was an error */
if (flags & 0x0200) {
error = DNS_ERR_TRUNCATED;
} else {
u16 error_code = (flags & 0x000f) - 1;
if (error_code > 4) {
error = DNS_ERR_UNKNOWN;
} else {
error = error_codes[error_code];
}
}
switch(error) {
case DNS_ERR_NOTIMPL:
case DNS_ERR_REFUSED:
/* we regard these errors as marking a bad nameserver */
if (req->reissue_count < global_max_reissues) {
char msg[64];
snprintf(msg, sizeof(msg), "Bad response %d (%s)",
error, evdns_err_to_string(error));
nameserver_failed(req->ns, msg);
if (!request_reissue(req)) return;
}
break;
case DNS_ERR_SERVERFAILED:
/* rcode 2 (servfailed) sometimes means "we are broken" and
* sometimes (with some binds) means "that request was very
* confusing." Treat this as a timeout, not a failure.
*/
/*XXXX refactor the parts of */
log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; "
"will allow the request to time out.",
debug_ntoa(req->ns->address));
break;
default:
/* we got a good reply from the nameserver */
nameserver_up(req->ns);
}
if (req->search_state && req->request_type != TYPE_PTR) {
/* if we have a list of domains to search in, try the next one */
if (!search_try_next(req)) {
/* a new request was issued so this request is finished and */
/* the user callback will be made when that request (or a */
/* child of it) finishes. */
request_finished(req, &req_head);
return;
}
}
/* all else failed. Pass the failure up */
reply_callback(req, 0, error, NULL);
request_finished(req, &req_head);
} else {
/* all ok, tell the user */
reply_callback(req, ttl, 0, reply);
nameserver_up(req->ns);
request_finished(req, &req_head);
}
}
static INLINE int
name_parse(u8 *packet, int length, int *idx, char *name_out, size_t name_out_len) {
int name_end = -1;
int j = *idx;
int ptr_count = 0;
#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0)
#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0)
#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0)
char *cp = name_out;
const char *const end = name_out + name_out_len;
/* Normally, names are a series of length prefixed strings terminated */
/* with a length of 0 (the lengths are u8's < 63). */
/* However, the length can start with a pair of 1 bits and that */
/* means that the next 14 bits are a pointer within the current */
/* packet. */
for(;;) {
u8 label_len;
if (j >= length) return -1;
GET8(label_len);
if (!label_len) break;
if (label_len & 0xc0) {
u8 ptr_low;
GET8(ptr_low);
if (name_end < 0) name_end = j;
j = (((int)label_len & 0x3f) << 8) + ptr_low;
/* Make sure that the target offset is in-bounds. */
if (j < 0 || j >= length) return -1;
/* If we've jumped more times than there are characters in the
* message, we must have a loop. */
if (++ptr_count > length) return -1;
continue;
}
if (label_len > 63) return -1;
if (cp != name_out) {
if (cp + 1 >= end) return -1;
*cp++ = '.';
}
if (cp + label_len >= end) return -1;
memcpy(cp, packet + j, label_len);
cp += label_len;
j += label_len;
}
if (cp >= end) return -1;
*cp = '\0';
if (name_end < 0)
*idx = j;
else
*idx = name_end;
return 0;
err:
return -1;
}
/* parses a raw reply from a nameserver. */
static int
reply_parse(u8 *packet, int length) {
int j = 0; /* index into packet */
u16 _t; /* used by the macros */
u32 _t32; /* used by the macros */
char tmp_name[256]; /* used by the macros */
u16 trans_id, questions, answers, authority, additional, datalength;
u16 flags = 0;
u32 ttl, ttl_r = 0xffffffff;
struct reply reply;
struct request *req = NULL;
unsigned int i;
GET16(trans_id);
GET16(flags);
GET16(questions);
GET16(answers);
GET16(authority);
GET16(additional);
(void) authority; /* suppress "unused variable" warnings. */
(void) additional; /* suppress "unused variable" warnings. */
req = request_find_from_trans_id(trans_id);
/* if no request, can't do anything. */
if (!req) return -1;
memset(&reply, 0, sizeof(reply));
/* If it's not an answer, it doesn't go with any of our requests. */
if (!(flags & 0x8000)) return -1; /* must be an answer */
if (flags & 0x020f) {
/* there was an error */
goto err;
}
/* if (!answers) return; */ /* must have an answer of some form */
/* This macro skips a name in the DNS reply. */
#define SKIP_NAME \
do { tmp_name[0] = '\0'; \
if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
goto err; \
} while(0);
reply.type = req->request_type;
/* skip over each question in the reply */
for (i = 0; i < questions; ++i) {
/* the question looks like
* <label:name><u16:type><u16:class>
*/
SKIP_NAME;
j += 4;
if (j >= length) goto err;
}
/* now we have the answer section which looks like
* <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
*/
for (i = 0; i < answers; ++i) {
u16 type, class;
/* XXX I'd be more comfortable if we actually checked the name */
/* here. -NM */
SKIP_NAME;
GET16(type);
GET16(class);
GET32(ttl);
GET16(datalength);
if (type == TYPE_A && class == CLASS_INET) {
int addrcount, addrtocopy;
if (req->request_type != TYPE_A) {
j += datalength; continue;
}
if ((datalength & 3) != 0) /* not an even number of As. */
goto err;
addrcount = datalength >> 2;
addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
ttl_r = MIN(ttl_r, ttl);
/* we only bother with the first four addresses. */
if (j + 4*addrtocopy > length) goto err;
memcpy(&reply.data.a.addresses[reply.data.a.addrcount],
packet + j, 4*addrtocopy);
j += 4*addrtocopy;
reply.data.a.addrcount += addrtocopy;
reply.have_answer = 1;
if (reply.data.a.addrcount == MAX_ADDRS) break;
} else if (type == TYPE_PTR && class == CLASS_INET) {
if (req->request_type != TYPE_PTR) {
j += datalength; continue;
}
if (name_parse(packet, length, &j, reply.data.ptr.name,
sizeof(reply.data.ptr.name))<0)
goto err;
ttl_r = MIN(ttl_r, ttl);
reply.have_answer = 1;
break;
} else if (type == TYPE_AAAA && class == CLASS_INET) {
int addrcount, addrtocopy;
if (req->request_type != TYPE_AAAA) {
j += datalength; continue;
}
if ((datalength & 15) != 0) /* not an even number of AAAAs. */
goto err;
addrcount = datalength >> 4; /* each address is 16 bytes long */
addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount);
ttl_r = MIN(ttl_r, ttl);
/* we only bother with the first four addresses. */
if (j + 16*addrtocopy > length) goto err;
memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount],
packet + j, 16*addrtocopy);
reply.data.aaaa.addrcount += addrtocopy;
j += 16*addrtocopy;
reply.have_answer = 1;
if (reply.data.aaaa.addrcount == MAX_ADDRS) break;
} else {
/* skip over any other type of resource */
j += datalength;
}
}
reply_handle(req, flags, ttl_r, &reply);
return 0;
err:
if (req)
reply_handle(req, flags, 0, NULL);
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -