📄 eventdns.c
字号:
} else {
/* retransmit it */
evdns_request_transmit(req);
}
}
/* try to send a request to a given server. */
/* */
/* return: */
/* 0 ok */
/* 1 temporary failure */
/* 2 other failure */
static int
evdns_request_transmit_to(struct request *req, struct nameserver *server) {
const ssize_t r = send(server->socket, req->request, req->request_len, 0);
if (r < 0) {
int err = last_error(server->socket);
if (error_is_eagain(err)) return 1;
nameserver_failed(req->ns, strerror(err));
return 2;
} else if (r != (ssize_t)req->request_len) {
return 1; /* short write */
} else {
return 0;
}
}
/* try to send a request, updating the fields of the request */
/* as needed */
/* */
/* return: */
/* 0 ok */
/* 1 failed */
static int
evdns_request_transmit(struct request *req) {
int retcode = 0, r;
/* if we fail to send this packet then this flag marks it */
/* for evdns_transmit */
req->transmit_me = 1;
if (req->trans_id == 0xffff) abort();
if (req->ns->choked) {
/* don't bother trying to write to a socket */
/* which we have had EAGAIN from */
return 1;
}
r = evdns_request_transmit_to(req, req->ns);
switch (r) {
case 1:
/* temp failure */
req->ns->choked = 1;
nameserver_write_waiting(req->ns, 1);
return 1;
case 2:
/* failed in some other way */
retcode = 1;
break;
default:
/* transmitted; we need to check for timeout. */
log(EVDNS_LOG_DEBUG,
"Setting timeout for request %lx", (unsigned long) req);
evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req);
if (evtimer_add(&req->timeout_event, &global_timeout) < 0) {
log(EVDNS_LOG_WARN,
"Error from libevent when adding timer for request %lx",
(unsigned long) req);
/* ???? Do more? */
}
}
req->tx_count++;
req->transmit_me = 0;
return retcode;
}
static void
nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
struct nameserver *const ns = (struct nameserver *) arg;
(void) type;
(void) count;
(void) ttl;
(void) addresses;
if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
/* this is a good reply */
nameserver_up(ns);
} else nameserver_probe_failed(ns);
}
static void
nameserver_send_probe(struct nameserver *const ns) {
struct request *req;
/* here we need to send a probe to a given nameserver */
/* in the hope that it is up now. */
log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address));
req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns);
if (!req) return;
/* we force this into the inflight queue no matter what */
request_trans_id_set(req, transaction_id_pick());
req->ns = ns;
request_submit(req);
}
/* returns: */
/* 0 didn't try to transmit anything */
/* 1 tried to transmit something */
static int
evdns_transmit(void) {
char did_try_to_transmit = 0;
if (req_head) {
struct request *const started_at = req_head, *req = req_head;
/* first transmit all the requests which are currently waiting */
do {
if (req->transmit_me) {
did_try_to_transmit = 1;
evdns_request_transmit(req);
}
req = req->next;
} while (req != started_at);
}
return did_try_to_transmit;
}
/* exported function */
int
evdns_count_nameservers(void)
{
const struct nameserver *server = server_head;
int n = 0;
if (!server)
return 0;
do {
++n;
server = server->next;
} while (server != server_head);
return n;
}
/* exported function */
int
evdns_clear_nameservers_and_suspend(void)
{
struct nameserver *server = server_head, *started_at = server_head;
struct request *req = req_head, *req_started_at = req_head;
if (!server)
return 0;
while (1) {
struct nameserver *next = server->next;
(void) event_del(&server->event);
CLEAR(&server->event);
(void) evtimer_del(&server->timeout_event);
CLEAR(&server->timeout_event);
if (server->socket >= 0)
CLOSE_SOCKET(server->socket);
CLEAR(server);
free(server);
if (next == started_at)
break;
server = next;
}
server_head = NULL;
global_good_nameservers = 0;
while (req) {
struct request *next = req->next;
req->tx_count = req->reissue_count = 0;
req->ns = NULL;
/* ???? What to do about searches? */
(void) evtimer_del(&req->timeout_event);
CLEAR(&req->timeout_event);
req->trans_id = 0;
req->transmit_me = 0;
global_requests_waiting++;
evdns_request_insert(req, &req_waiting_head);
/* We want to insert these suspended elements at the front of
* the waiting queue, since they were pending before any of
* the waiting entries were added. This is a circular list,
* so we can just shift the start back by one.*/
req_waiting_head = req_waiting_head->prev;
if (next == req_started_at)
break;
req = next;
}
req_head = NULL;
global_requests_inflight = 0;
return 0;
}
/* exported function */
int
evdns_resume(void)
{
evdns_requests_pump_waiting_queue();
return 0;
}
static int
_evdns_nameserver_add_impl(u32 address, int port) {
/* first check to see if we already have this nameserver */
const struct nameserver *server = server_head, *const started_at = server_head;
struct nameserver *ns;
struct sockaddr_in sin;
int err = 0;
if (server) {
do {
if (server->address == address) return 3;
server = server->next;
} while (server != started_at);
}
ns = (struct nameserver *) malloc(sizeof(struct nameserver));
if (!ns) return -1;
memset(ns, 0, sizeof(struct nameserver));
ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
if (ns->socket < 0) { err = 1; goto out1; }
#ifdef WIN32
{
u_long nonblocking = 1;
ioctlsocket(ns->socket, FIONBIO, &nonblocking);
}
#else
fcntl(ns->socket, F_SETFL, O_NONBLOCK);
#endif
memset(&sin, 0, sizeof(sin));
sin.sin_addr.s_addr = address;
sin.sin_port = htons(port);
sin.sin_family = AF_INET;
if (connect(ns->socket, (struct sockaddr *) &sin,
(socklen_t)sizeof(sin)) != 0) {
err = 2;
goto out2;
}
ns->address = address;
ns->state = 1;
event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
if (event_add(&ns->event, NULL) < 0) {
err = 2;
goto out2;
}
log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address));
/* insert this nameserver into the list of them */
if (!server_head) {
ns->next = ns->prev = ns;
server_head = ns;
} else {
ns->next = server_head->next;
ns->prev = server_head;
server_head->next = ns;
if (server_head->prev == server_head) {
server_head->prev = ns;
}
}
global_good_nameservers++;
return 0;
out2:
CLOSE_SOCKET(ns->socket);
out1:
CLEAR(ns);
free(ns);
log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err);
return err;
}
/* exported function */
int
evdns_nameserver_add(unsigned long int address) {
return _evdns_nameserver_add_impl((u32)address, 53);
}
/* exported function */
int
evdns_nameserver_ip_add(const char *ip_as_string) {
struct in_addr ina;
int port;
char buf[20];
const char *cp;
cp = strchr(ip_as_string, ':');
if (! cp) {
cp = ip_as_string;
port = 53;
} else {
port = strtoint(cp+1);
if (port < 0 || port > 65535) {
return 4;
}
if ((cp-ip_as_string) >= (int)sizeof(buf)) {
return 4;
}
assert(cp >= ip_as_string);
memcpy(buf, ip_as_string, cp-ip_as_string);
buf[cp-ip_as_string] = '\0';
cp = buf;
}
if (!inet_aton(cp, &ina)) {
return 4;
}
return _evdns_nameserver_add_impl(ina.s_addr, port);
}
/* insert into the tail of the queue */
static void
evdns_request_insert(struct request *req, struct request **head) {
if (!*head) {
*head = req;
req->next = req->prev = req;
return;
}
req->prev = (*head)->prev;
req->prev->next = req;
req->next = *head;
(*head)->prev = req;
}
static int
string_num_dots(const char *s) {
int count = 0;
while ((s = strchr(s, '.'))) {
s++;
count++;
}
return count;
}
static struct request *
request_new(int type, const char *name, int flags,
evdns_callback_type callback, void *user_ptr) {
const char issuing_now =
(global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
const size_t name_len = strlen(name);
const size_t request_max_len = evdns_request_len(name_len);
const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
/* the request data is alloced in a single block with the header */
struct request *const req =
(struct request *) malloc(sizeof(struct request) + request_max_len);
int rlen;
(void) flags;
if (!req) return NULL;
memset(req, 0, sizeof(struct request));
/* request data lives just after the header */
req->request = ((u8 *) req) + sizeof(struct request);
/* denotes that the request data shouldn't be free()ed */
req->request_appended = 1;
rlen = evdns_request_data_build(name, name_len, trans_id,
type, CLASS_INET, req->request, request_max_len);
if (rlen < 0)
goto err1;
req->request_len = rlen;
req->trans_id = trans_id;
req->tx_count = 0;
req->request_type = type;
req->user_pointer = user_ptr;
req->user_callback = callback;
req->ns = issuing_now ? nameserver_pick() : NULL;
req->next = req->prev = NULL;
return req;
err1:
CLEAR(req);
_free(req);
return NULL;
}
static void
request_submit(struct request *const req) {
if (req->ns) {
/* if it has a nameserver assigned then this is going */
/* straight into the inflight queue */
evdns_request_insert(req, &req_head);
global_requests_inflight++;
evdns_request_transmit(req);
} else {
evdns_request_insert(req, &req_waiting_head);
global_requests_waiting++;
}
}
/* exported function */
int evdns_resolve_ipv4(const char *name, int flags,
evdns_callback_type callback, void *ptr) {
log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
if (flags & DNS_QUERY_NO_SEARCH) {
struct request *const req =
request_new(TYPE_A, name, flags, callback, ptr);
if (req == NULL)
return (1);
request_submit(req);
return (0);
} else {
return (search_request_new(TYPE_A, name, flags, callback, ptr));
}
}
/* exported function */
int evdns_resolve_ipv6(const char *name, int flags,
evdns_callback_type callback, void *ptr) {
log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
if (flags & DNS_QUERY_NO_SEARCH) {
struct request *const req =
request_new(TYPE_AAAA, name, flags, callback, ptr);
if (req == NULL)
return (1);
request_submit(req);
return (0);
} else {
return (search_request_new(TYPE_AAAA, name, flags, callback, ptr));
}
}
int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) {
char buf[32];
struct request *req;
u32 a;
assert(in);
a = ntohl(in->s_addr);
snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
(int)(u8)((a )&0xff),
(int)(u8)((a>>8 )&0xff),
(int)(u8)((a>>16)&0xff),
(int)(u8)((a>>24)&0xff));
log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
req = request_new(TYPE_PTR, buf, flags, callback, ptr);
if (!req) return 1;
request_submit(req);
return 0;
}
int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) {
/* 32 nybbles, 32 periods, "ip6.arpa", NUL. */
char buf[73];
char *cp;
struct request *req;
int i;
assert(in);
cp = buf;
for (i=15; i >= 0; --i) {
u8 byte = in->s6_addr[i];
*cp++ = "0123456789abcdef"[byte & 0x0f];
*cp++ = '.';
*cp++ = "0123456789abcdef"[byte >> 4];
*cp++ = '.';
}
assert(cp + strlen("ip6.arpa") < buf+sizeof(buf));
memcpy(cp, "ip6.arpa", strlen("ip6.arpa")+1);
log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
req = request_new(TYPE_PTR
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -