Browse Source

Merge pull request #2045 from xnoreq/master

Fix DNS timeouts
pull/2049/head
Max Lv 6 years ago
committed by GitHub
parent
commit
a0a56e713b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 65 additions and 37 deletions
  1. 102
      src/resolv.c

102
src/resolv.c

@ -65,9 +65,15 @@
* Implement DNS resolution interface using libc-ares
*/
#define SS_NUM_IOS 6
#define SS_INVALID_FD -1
#define SS_TIMER_AFTER 1.0
struct resolv_ctx {
struct ev_io io;
struct ev_timer tw;
struct ev_io ios[SS_NUM_IOS];
struct ev_timer timer;
ev_tstamp last_tick;
ares_channel channel;
struct ares_options options;
@ -90,7 +96,7 @@ struct resolv_query {
extern int verbose;
struct resolv_ctx default_ctx;
static struct resolv_ctx default_ctx;
static struct ev_loop *default_loop;
enum {
@ -101,7 +107,7 @@ enum {
static int resolv_mode = MODE_IPV4_FIRST;
static void resolv_sock_cb(struct ev_loop *, struct ev_io *, int);
static void resolv_timeout_cb(struct ev_loop *, struct ev_timer *, int);
static void resolv_timer_cb(struct ev_loop *, struct ev_timer *, int);
static void resolv_sock_state_cb(void *, int, int, int);
static void dns_query_v4_cb(void *, int, int, struct hostent *);
@ -113,16 +119,12 @@ static struct sockaddr *choose_ipv4_first(struct resolv_query *);
static struct sockaddr *choose_ipv6_first(struct resolv_query *);
static struct sockaddr *choose_any(struct resolv_query *);
static void reset_timer();
/*
* DNS UDP socket activity callback
*/
static void
resolv_sock_cb(EV_P_ ev_io *w, int revents)
{
struct resolv_ctx *ctx = (struct resolv_ctx *)w;
ares_socket_t rfd = ARES_SOCKET_BAD, wfd = ARES_SOCKET_BAD;
if (revents & EV_READ)
@ -130,9 +132,9 @@ resolv_sock_cb(EV_P_ ev_io *w, int revents)
if (revents & EV_WRITE)
wfd = w->fd;
ares_process_fd(ctx->channel, rfd, wfd);
default_ctx.last_tick = ev_now(default_loop);
reset_timer();
ares_process_fd(default_ctx.channel, rfd, wfd);
}
int
@ -181,8 +183,13 @@ resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first)
FATAL("failed to set nameservers");
}
ev_init(&default_ctx.io, resolv_sock_cb);
ev_timer_init(&default_ctx.tw, resolv_timeout_cb, 0.0, 0.0);
for (int i = 0; i < SS_NUM_IOS; i++) {
ev_io_init(&default_ctx.ios[i], resolv_sock_cb, SS_INVALID_FD, 0);
}
default_ctx.last_tick = ev_now(default_loop);
ev_init(&default_ctx.timer, resolv_timer_cb);
resolv_timer_cb(default_loop, &default_ctx.timer, 0);
return 0;
}
@ -190,6 +197,11 @@ resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first)
void
resolv_shutdown(struct ev_loop *loop)
{
ev_timer_stop(default_loop, &default_ctx.timer);
for (int i = 0; i < SS_NUM_IOS; i++) {
ev_io_stop(default_loop, &default_ctx.ios[i]);
}
ares_cancel(default_ctx.channel);
ares_destroy(default_ctx.channel);
@ -220,8 +232,6 @@ resolv_start(const char *hostname, uint16_t port,
ares_gethostbyname(default_ctx.channel, hostname, AF_INET, dns_query_v4_cb, query);
ares_gethostbyname(default_ctx.channel, hostname, AF_INET6, dns_query_v6_cb, query);
reset_timer();
}
/*
@ -427,30 +437,26 @@ all_requests_are_null(struct resolv_query *query)
}
/*
* DNS timeout callback
* Timer callback
*/
static void
resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
resolv_timer_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
{
struct resolv_ctx *ctx = cork_container_of(w, struct resolv_ctx, tw);
struct resolv_ctx *ctx = cork_container_of(w, struct resolv_ctx, timer);
ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
ev_tstamp now = ev_now(default_loop);
ev_tstamp after = ctx->last_tick - now + SS_TIMER_AFTER;
reset_timer();
}
if (after < 0.0) {
ctx->last_tick = now;
ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
static void
reset_timer()
{
struct timeval tvout;
struct timeval *tv = ares_timeout(default_ctx.channel, NULL, &tvout);
if (tv == NULL) {
return;
ev_timer_set(w, SS_TIMER_AFTER, 0.0);
} else {
ev_timer_set(w, after, 0.0);
}
float repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
repeat = repeat < 1.f ? 1.f : repeat;
ev_timer_set(&default_ctx.tw, repeat, repeat);
ev_timer_again(default_loop, &default_ctx.tw);
ev_timer_start(loop, w);
}
/*
@ -460,13 +466,35 @@ static void
resolv_sock_state_cb(void *data, int s, int read, int write)
{
struct resolv_ctx *ctx = (struct resolv_ctx *)data;
int events = (read ? EV_READ : 0) | (write ? EV_WRITE : 0);
int i = 0, ffi = -1;
for (; i < SS_NUM_IOS; i++) {
if (ctx->ios[i].fd == s) {
break;
}
if (ffi < 0 && ctx->ios[i].fd == SS_INVALID_FD) {
// first free index
ffi = i;
}
}
if (i < SS_NUM_IOS) {
ev_io_stop(default_loop, &ctx->ios[i]);
} else if (ffi > -1) {
i = ffi;
} else {
LOGE("failed to find free I/O watcher slot for DNS query");
// last resort: stop io and re-use slot, will cause timeout
i = 0;
ev_io_stop(default_loop, &ctx->ios[i]);
}
if (read || write) {
ev_io_stop(default_loop, &ctx->io);
ev_io_set(&ctx->io, s, (read ? EV_READ : 0) | (write ? EV_WRITE : 0));
ev_io_start(default_loop, &ctx->io);
if (events) {
ev_io_set(&ctx->ios[i], s, events);
ev_io_start(default_loop, &ctx->ios[i]);
} else {
ev_io_stop(default_loop, &ctx->io);
ev_io_set(&ctx->io, -1, 0);
ev_io_set(&ctx->ios[i], SS_INVALID_FD, 0);
}
}
Loading…
Cancel
Save