mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-30 05:11:14 +00:00
+65
-37
@@ -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);
|
||||
|
||||
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);
|
||||
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 {
|
||||
ev_io_stop(default_loop, &ctx->io);
|
||||
ev_io_set(&ctx->io, -1, 0);
|
||||
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 (events) {
|
||||
ev_io_set(&ctx->ios[i], s, events);
|
||||
ev_io_start(default_loop, &ctx->ios[i]);
|
||||
} else {
|
||||
ev_io_set(&ctx->ios[i], SS_INVALID_FD, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user