diff --git a/README.md b/README.md index 16dc49c..b088ee7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ e.g. Ubuntu, Debian or Linux Mint, you can build the binary like this: ```bash # Debian / Ubuntu -sudo apt-get install --no-install-recommends build-essential autoconf libtool libssl-dev libpcre3-dev libudns-dev libev-dev asciidoc xmlto automake +sudo apt-get install --no-install-recommends build-essential autoconf libtool libssl-dev libpcre3-dev libc-ares-dev libev-dev asciidoc xmlto automake # CentOS / Fedora / RHEL sudo yum install gcc autoconf libtool automake make zlib-devel openssl-devel asciidoc xmlto # Arch diff --git a/configure.ac b/configure.ac index 23f021b..a307f27 100755 --- a/configure.ac +++ b/configure.ac @@ -173,6 +173,8 @@ case $host in #include #endif ]]) + dnl Checks for c-ares headers + AC_CHECK_HEADERS([ares.h], [], [AC_MSG_ERROR([Couldn't find libcares. Try installing libc-ares-dev or c-ares-devel.])], []) ;; *) # These are POSIX-like systems using BSD-like sockets API. @@ -236,7 +238,7 @@ AC_CHECK_LIB(socket, connect) dnl Checks for library functions. AC_CHECK_FUNCS([malloc memset socket]) -AC_CHECK_LIB([udns], [dns_dnlen], [LIBS="-ludns $LIBS"], [AC_MSG_ERROR([Couldn't find libudns. Try installing libudns-dev or udns-devel.])]) +AC_CHECK_LIB([cares], [ares_library_init], [LIBS="-lcares $LIBS"], [AC_MSG_ERROR([Couldn't find libc-ares. Try installing libc-ares-dev or c-ares-devel.])]) AC_CHECK_LIB([ev], [ev_loop_destroy], [LIBS="-lev $LIBS"], [AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])]) diff --git a/src/Makefile.am b/src/Makefile.am index 516be05..0d56760 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,8 +38,8 @@ obfs_server_SOURCES = utils.c \ obfs_local_LDADD = $(OBFS_COMMON_LIBS) obfs_server_LDADD = $(OBFS_COMMON_LIBS) -obfs_local_LDADD += -ludns -obfs_server_LDADD += -ludns +obfs_local_LDADD += -lcares +obfs_server_LDADD += -lcares obfs_local_CFLAGS = $(AM_CFLAGS) -DMODULE_LOCAL obfs_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE diff --git a/src/local.c b/src/local.c index 4193e17..190cefa 100644 --- a/src/local.c +++ b/src/local.c @@ -49,7 +49,6 @@ #endif #include -#include #ifdef __MINGW32__ #include "win32.h" diff --git a/src/netutils.c b/src/netutils.c index 5a75617..5b7239b 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -23,7 +23,6 @@ #include #include -#include #ifdef HAVE_CONFIG_H #include "config.h" @@ -34,6 +33,7 @@ #define sleep(n) Sleep(1000 * (n)) #else #include +#include #include #include #include @@ -103,12 +103,12 @@ bind_to_address(int socket_fd, const char *host) if (cork_ip_init(&ip, host) != -1) { if (ip.version == 4) { struct sockaddr_in *addr = (struct sockaddr_in *)&storage; - dns_pton(AF_INET, host, &addr->sin_addr); + inet_pton(AF_INET, host, &addr->sin_addr); addr->sin_family = AF_INET; return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); } else if (ip.version == 6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; - dns_pton(AF_INET6, host, &addr->sin6_addr); + inet_pton(AF_INET6, host, &addr->sin6_addr); addr->sin6_family = AF_INET6; return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); } @@ -127,14 +127,14 @@ get_sockaddr(char *host, char *port, if (ip.version == 4) { struct sockaddr_in *addr = (struct sockaddr_in *)storage; addr->sin_family = AF_INET; - dns_pton(AF_INET, host, &(addr->sin_addr)); + inet_pton(AF_INET, host, &(addr->sin_addr)); if (port != NULL) { addr->sin_port = htons(atoi(port)); } } else if (ip.version == 6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; addr->sin6_family = AF_INET6; - dns_pton(AF_INET6, host, &(addr->sin6_addr)); + inet_pton(AF_INET6, host, &(addr->sin6_addr)); if (port != NULL) { addr->sin6_port = htons(atoi(port)); } diff --git a/src/resolv.c b/src/resolv.c index 7c694c0..93c62b4 100644 --- a/src/resolv.c +++ b/src/resolv.c @@ -33,39 +33,57 @@ #include #include #include -#include #ifdef __MINGW32__ #include "win32.h" #else +#include #include #include #include #include #endif +#include + +#include + #include "resolv.h" #include "utils.h" #include "netutils.h" /* - * Implement DNS resolution interface using libudns + * Implement DNS resolution interface using libc-ares */ -struct ResolvQuery { - void (*client_cb)(struct sockaddr *, void *); - void (*client_free_cb)(void *); - void *client_cb_data; - struct dns_query *queries[2]; +struct resolv_ctx { + struct ev_io io; + struct ev_timer tw; + + ares_channel channel; + struct ares_options options; +}; + +struct resolv_query { + int requests[2]; size_t response_count; struct sockaddr **responses; + + void (*client_cb)(struct sockaddr *, void *); + void (*free_cb)(void*); + uint16_t port; + + void *data; + + int is_closed; }; extern int verbose; -static struct ev_io resolv_io_watcher; -static struct ev_timer resolv_timeout_watcher; +struct resolv_ctx default_ctx; +static struct ev_loop* default_loop; + static const int MODE_IPV4_ONLY = 0; static const int MODE_IPV6_ONLY = 1; static const int MODE_IPV4_FIRST = 2; @@ -74,371 +92,386 @@ static int resolv_mode = 0; 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 dns_query_v4_cb(struct dns_ctx *, struct dns_rr_a4 *, void *); -static void dns_query_v6_cb(struct dns_ctx *, struct dns_rr_a6 *, void *); -static void dns_timer_setup_cb(struct dns_ctx *, int, void *); -static void process_client_callback(struct ResolvQuery *); -static inline int all_queries_are_null(struct ResolvQuery *); -static struct sockaddr *choose_ipv4_first(struct ResolvQuery *); -static struct sockaddr *choose_ipv6_first(struct ResolvQuery *); -static struct sockaddr *choose_any(struct ResolvQuery *); +static void resolv_sock_state_cb(void *, int, int, int); + +static void dns_query_v4_cb(void *, int, int, struct hostent *); +static void dns_query_v6_cb(void *, int, int, struct hostent *); + +static void process_client_callback(struct resolv_query *); +static inline int all_requests_are_null(struct resolv_query *); +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) + rfd = w->fd; + if (revents & EV_WRITE) + wfd = w->fd; + + ares_process_fd(ctx->channel, rfd, wfd); + + reset_timer(); +} int -resolv_init(struct ev_loop *loop, char **nameservers, int nameserver_num, int ipv6first) +resolv_init(struct ev_loop *loop, char *nameservers, int ipv6first) { + int status; + if (ipv6first) resolv_mode = MODE_IPV6_FIRST; else resolv_mode = MODE_IPV4_FIRST; - struct dns_ctx *ctx = &dns_defctx; - if (nameservers == NULL) { - /* Nameservers not specified, use system resolver config */ - dns_init(ctx, 0); - } else { - dns_reset(ctx); + default_loop = loop; - for (int i = 0; i < nameserver_num; i++) { - char *server = nameservers[i]; - dns_add_serv(ctx, server); - } + if ((status = ares_library_init(ARES_LIB_INIT_ALL) )!= ARES_SUCCESS) { + LOGE("c-ares error: %s", ares_strerror(status)); + FATAL("failed to initialize c-ares"); } - int sockfd = dns_open(ctx); - if (sockfd < 0) { - FATAL("Failed to open DNS resolver socket"); - } + memset(&default_ctx, 0, sizeof(struct resolv_ctx)); - if (nameserver_num == 1 && nameservers != NULL) { - if (strncmp("127.0.0.1", nameservers[0], 9) == 0 - || strncmp("::1", nameservers[0], 3) == 0) { - if (verbose) { - LOGI("bind UDP resolver to %s", nameservers[0]); - } - if (bind_to_address(sockfd, nameservers[0]) == -1) - ERROR("bind_to_address"); - } - } + default_ctx.options.sock_state_cb_data = &default_ctx; + default_ctx.options.sock_state_cb = resolv_sock_state_cb; + default_ctx.options.timeout = 3000; + default_ctx.options.tries = 2; #ifdef __MINGW32__ setnonblocking(sockfd); #else - int flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); -#endif + status = ares_init_options(&default_ctx.channel, &default_ctx.options, + ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB); - ev_io_init(&resolv_io_watcher, resolv_sock_cb, sockfd, EV_READ); - resolv_io_watcher.data = ctx; + if (status != ARES_SUCCESS) { + FATAL("failed to initialize c-ares"); + } +#endif - ev_io_start(loop, &resolv_io_watcher); + if (nameservers != NULL) { +#if ARES_VERSION_MINOR >= 11 + status = ares_set_servers_ports_csv(default_ctx.channel, nameservers); +#else + status = ares_set_servers_csv(default_ctx.channel, nameservers); +#endif + } - ev_timer_init(&resolv_timeout_watcher, resolv_timeout_cb, 0.0, 0.0); - resolv_timeout_watcher.data = ctx; + if (status != ARES_SUCCESS) { + FATAL("failed to set nameservers"); + } - dns_set_tmcbck(ctx, dns_timer_setup_cb, loop); + ev_init(&default_ctx.io, resolv_sock_cb); + ev_timer_init(&default_ctx.tw, resolv_timeout_cb, 0.0, 0.0); - return sockfd; + return 0; } void resolv_shutdown(struct ev_loop *loop) { - struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; - - ev_io_stop(loop, &resolv_io_watcher); + ares_cancel(default_ctx.channel); + ares_destroy(default_ctx.channel); - if (ev_is_active(&resolv_timeout_watcher)) { - ev_timer_stop(loop, &resolv_timeout_watcher); - } - - dns_close(ctx); + ares_library_cleanup(); } -struct ResolvQuery * -resolv_query(const char *hostname, void (*client_cb)(struct sockaddr *, void *), - void (*client_free_cb)(void *), void *client_cb_data, - uint16_t port) +struct resolv_query * +resolv_start(const char *hostname, uint16_t port, + void (*client_cb)(struct sockaddr *, void *), + void (*free_cb)(void*), void *data) { - struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; - /* - * Wrap udns's call back in our own + * Wrap c-ares's call back in our own */ - struct ResolvQuery *cb_data = ss_malloc(sizeof(struct ResolvQuery)); - if (cb_data == NULL) { - LOGE("Failed to allocate memory for DNS query callback data."); + struct resolv_query *query = ss_malloc(sizeof(struct resolv_query)); + + if (query == NULL) { + LOGE("failed to allocate memory for DNS query callback data."); return NULL; } - memset(cb_data, 0, sizeof(struct ResolvQuery)); + memset(query, 0, sizeof(struct resolv_query)); - cb_data->client_cb = client_cb; - cb_data->client_free_cb = client_free_cb; - cb_data->client_cb_data = client_cb_data; - memset(cb_data->queries, 0, sizeof(cb_data->queries)); - cb_data->response_count = 0; - cb_data->responses = NULL; - cb_data->port = port; + query->port = port; + query->client_cb = client_cb; + query->response_count = 0; + query->responses = NULL; + query->data = data; + query->free_cb = free_cb; - /* Submit A and AAAA queries */ + /* Submit A and AAAA requests */ if (resolv_mode != MODE_IPV6_ONLY) { - cb_data->queries[0] = dns_submit_a4(ctx, - hostname, 0, - dns_query_v4_cb, cb_data); - if (cb_data->queries[0] == NULL) { - LOGE("Failed to submit DNS query: %s", - dns_strerror(dns_status(ctx))); - } + ares_gethostbyname(default_ctx.channel, hostname, AF_INET, dns_query_v4_cb, query); + query->requests[0] = AF_INET; } if (resolv_mode != MODE_IPV4_ONLY) { - cb_data->queries[1] = dns_submit_a6(ctx, - hostname, 0, - dns_query_v6_cb, cb_data); - if (cb_data->queries[1] == NULL) { - LOGE("Failed to submit DNS query: %s", - dns_strerror(dns_status(ctx))); - } + ares_gethostbyname(default_ctx.channel, hostname, AF_INET6, dns_query_v6_cb, query); + query->requests[1] = AF_INET6; } - if (all_queries_are_null(cb_data)) { - if (cb_data->client_free_cb != NULL) { - cb_data->client_free_cb(cb_data->client_cb_data); - } - ss_free(cb_data); - } - - return cb_data; -} - -void -resolv_cancel(struct ResolvQuery *query_handle) -{ - struct ResolvQuery *cb_data = (struct ResolvQuery *)query_handle; - struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; - - for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); - i++) - if (cb_data->queries[i] != NULL) { - dns_cancel(ctx, cb_data->queries[i]); - ss_free(cb_data->queries[i]); - } - - if (cb_data->client_free_cb != NULL) { - cb_data->client_free_cb(cb_data->client_cb_data); - } + reset_timer(); - ss_free(cb_data); + return query; } /* - * DNS UDP socket activity callback + * Wrapper for client callback we provide to c-ares */ static void -resolv_sock_cb(struct ev_loop *loop, struct ev_io *w, int revents) +dns_query_v4_cb(void *arg, int status, int timeouts, struct hostent *he) { - struct dns_ctx *ctx = (struct dns_ctx *)w->data; + int i, n; + struct resolv_query *query = (struct resolv_query *)arg; - if (revents & EV_READ) { - dns_ioevent(ctx, ev_now(loop)); + if (status == ARES_EDESTRUCTION) { + return; } -} - -/* - * Wrapper for client callback we provide to udns - */ -static void -dns_query_v4_cb(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) -{ - struct ResolvQuery *cb_data = (struct ResolvQuery *)data; - if (result == NULL) { + if(!he || status != ARES_SUCCESS){ if (verbose) { - LOGI("IPv4 resolv: %s", dns_strerror(dns_status(ctx))); + LOGI("failed to lookup v4 address %s", ares_strerror(status)); } - } else if (result->dnsa4_nrr > 0) { - struct sockaddr **new_responses = ss_realloc(cb_data->responses, - (cb_data->response_count + - result->dnsa4_nrr) * - sizeof(struct sockaddr *)); + goto CLEANUP; + } + + if (verbose) { + LOGI("found address name v4 address %s", he->h_name); + } + + n = 0; + while (he->h_addr_list[n]) { + n++; + } + + if (n > 0) { + struct sockaddr **new_responses = ss_realloc(query->responses, + (query->response_count + n) + * sizeof(struct sockaddr *)); + if (new_responses == NULL) { LOGE("Failed to allocate memory for additional DNS responses"); } else { - cb_data->responses = new_responses; + query->responses = new_responses; - for (int i = 0; i < result->dnsa4_nrr; i++) { + for (i = 0; i < n; i++) { struct sockaddr_in *sa = ss_malloc(sizeof(struct sockaddr_in)); memset(sa, 0, sizeof(struct sockaddr_in)); sa->sin_family = AF_INET; - sa->sin_port = cb_data->port; - sa->sin_addr = result->dnsa4_addr[i]; - - cb_data->responses[cb_data->response_count] = - (struct sockaddr *)sa; - if (cb_data->responses[cb_data->response_count] == NULL) { - LOGE( - "Failed to allocate memory for DNS query result address"); + sa->sin_port = query->port; + memcpy(&sa->sin_addr, he->h_addr_list[i], he->h_length); + + query->responses[query->response_count] = (struct sockaddr*)sa; + if (query->responses[query->response_count] == NULL) { + LOGE("Failed to allocate memory for DNS query result address"); } else { - cb_data->response_count++; + query->response_count++; } } } } - ss_free(result); - cb_data->queries[0] = NULL; /* mark A query as being completed */ +CLEANUP: + + query->requests[0] = 0; /* mark A query as being completed */ - /* Once all queries have completed, call client callback */ - if (all_queries_are_null(cb_data)) { - return process_client_callback(cb_data); + /* Once all requests have completed, call client callback */ + if (all_requests_are_null(query)) { + return process_client_callback(query); } } static void -dns_query_v6_cb(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) +dns_query_v6_cb(void *arg, int status, int timeouts, struct hostent *he) { - struct ResolvQuery *cb_data = (struct ResolvQuery *)data; + int i, n; + struct resolv_query *query = (struct resolv_query *)arg; + + if (status == ARES_EDESTRUCTION) { + return; + } - if (result == NULL) { + if(!he || status != ARES_SUCCESS){ if (verbose) { - LOGI("IPv6 resolv: %s", dns_strerror(dns_status(ctx))); + LOGI("Failed to lookup v6 address %s", ares_strerror(status)); } - } else if (result->dnsa6_nrr > 0) { - struct sockaddr **new_responses = ss_realloc(cb_data->responses, - (cb_data->response_count + - result->dnsa6_nrr) * - sizeof(struct sockaddr *)); + goto CLEANUP; + } + + if (verbose) { + LOGI("found address name v6 address %s", he->h_name); + } + + n = 0; + while (he->h_addr_list[n]) { + n++; + } + + if (n > 0) { + struct sockaddr **new_responses = ss_realloc(query->responses, + (query->response_count + n) + * sizeof(struct sockaddr *)); + if (new_responses == NULL) { LOGE("Failed to allocate memory for additional DNS responses"); } else { - cb_data->responses = new_responses; + query->responses = new_responses; - for (int i = 0; i < result->dnsa6_nrr; i++) { + for (i = 0; i < n; i++) { struct sockaddr_in6 *sa = ss_malloc(sizeof(struct sockaddr_in6)); memset(sa, 0, sizeof(struct sockaddr_in6)); sa->sin6_family = AF_INET6; - sa->sin6_port = cb_data->port; - sa->sin6_addr = result->dnsa6_addr[i]; - - cb_data->responses[cb_data->response_count] = - (struct sockaddr *)sa; - if (cb_data->responses[cb_data->response_count] == NULL) { - LOGE( - "Failed to allocate memory for DNS query result address"); + sa->sin6_port = query->port; + memcpy(&sa->sin6_addr, he->h_addr_list[i], he->h_length); + + query->responses[query->response_count] = (struct sockaddr*)sa; + if (query->responses[query->response_count] == NULL) { + LOGE("Failed to allocate memory for DNS query result address"); } else { - cb_data->response_count++; + query->response_count++; } } } } - ss_free(result); - cb_data->queries[1] = NULL; /* mark AAAA query as being completed */ +CLEANUP: + query->requests[1] = 0; /* mark A query as being completed */ - /* Once all queries have completed, call client callback */ - if (all_queries_are_null(cb_data)) { - return process_client_callback(cb_data); + /* Once all requests have completed, call client callback */ + if (all_requests_are_null(query)) { + return process_client_callback(query); } } /* - * Called once all queries have been completed + * * Called once all requests have been completed */ static void -process_client_callback(struct ResolvQuery *cb_data) +process_client_callback(struct resolv_query *query) { struct sockaddr *best_address = NULL; if (resolv_mode == MODE_IPV4_FIRST) { - best_address = choose_ipv4_first(cb_data); + best_address = choose_ipv4_first(query); } else if (resolv_mode == MODE_IPV6_FIRST) { - best_address = choose_ipv6_first(cb_data); + best_address = choose_ipv6_first(query); } else { - best_address = choose_any(cb_data); + best_address = choose_any(query); } - cb_data->client_cb(best_address, cb_data->client_cb_data); + query->client_cb(best_address, query->data); - for (int i = 0; i < cb_data->response_count; i++) - ss_free(cb_data->responses[i]); + for (int i = 0; i < query->response_count; i++) + ss_free(query->responses[i]); - ss_free(cb_data->responses); - if (cb_data->client_free_cb != NULL) { - cb_data->client_free_cb(cb_data->client_cb_data); - } - ss_free(cb_data); + ss_free(query->responses); + + if (query->free_cb != NULL) + query->free_cb(query->data); + else + ss_free(query->data); + + ss_free(query); } static struct sockaddr * -choose_ipv4_first(struct ResolvQuery *cb_data) +choose_ipv4_first(struct resolv_query *query) { - for (int i = 0; i < cb_data->response_count; i++) - if (cb_data->responses[i]->sa_family == AF_INET) { - return cb_data->responses[i]; + for (int i = 0; i < query->response_count; i++) + if (query->responses[i]->sa_family == AF_INET) { + return query->responses[i]; } - return choose_any(cb_data); + return choose_any(query); } static struct sockaddr * -choose_ipv6_first(struct ResolvQuery *cb_data) +choose_ipv6_first(struct resolv_query *query) { - for (int i = 0; i < cb_data->response_count; i++) - if (cb_data->responses[i]->sa_family == AF_INET6) { - return cb_data->responses[i]; + for (int i = 0; i < query->response_count; i++) + if (query->responses[i]->sa_family == AF_INET6) { + return query->responses[i]; } - return choose_any(cb_data); + return choose_any(query); } static struct sockaddr * -choose_any(struct ResolvQuery *cb_data) +choose_any(struct resolv_query *query) { - if (cb_data->response_count >= 1) { - return cb_data->responses[0]; + if (query->response_count >= 1) { + return query->responses[0]; } return NULL; } +static inline int +all_requests_are_null(struct resolv_query *query) +{ + int result = 1; + + for (int i = 0; i < sizeof(query->requests) / sizeof(query->requests[0]); + i++) + result = result && query->requests[i] == 0; + + return result; +} + /* * DNS timeout callback */ static void resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) { - struct dns_ctx *ctx = (struct dns_ctx *)w->data; + struct resolv_ctx *ctx= cork_container_of(w, struct resolv_ctx, tw); - if (revents & EV_TIMER) { - dns_timeouts(ctx, 30, ev_now(loop)); - } + ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + + reset_timer(); } -/* - * Callback to setup DNS timeout callback - */ static void -dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) +reset_timer() { - struct ev_loop *loop = (struct ev_loop *)data; - - if (ev_is_active(&resolv_timeout_watcher)) { - ev_timer_stop(loop, &resolv_timeout_watcher); - } - - if (ctx != NULL && timeout >= 0) { - ev_timer_set(&resolv_timeout_watcher, timeout, 0.0); - ev_timer_start(loop, &resolv_timeout_watcher); + struct timeval tvout; + struct timeval *tv = ares_timeout(default_ctx.channel, NULL, &tvout); + if (tv == NULL) { + return; } + float repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9; + ev_timer_set(&default_ctx.tw, repeat, repeat); + ev_timer_again(default_loop, &default_ctx.tw); } -static inline int -all_queries_are_null(struct ResolvQuery *cb_data) -{ - int result = 1; +/* + * Handle c-ares events + */ +static void +resolv_sock_state_cb(void *data, int s, int read, int write) { - for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); - i++) - result = result && cb_data->queries[i] == NULL; + struct resolv_ctx *ctx = (struct resolv_ctx *) data; + int io_active = ev_is_active(&ctx->io); - return result; + if (read || write) { + if (io_active && ctx->io.fd != s) { + 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); + } else { + ev_io_stop(default_loop, &ctx->io); + ev_io_set(&ctx->io, -1, 0); + } } diff --git a/src/resolv.h b/src/resolv.h index 0552922..d615b7f 100644 --- a/src/resolv.h +++ b/src/resolv.h @@ -38,13 +38,12 @@ #include #endif -struct ResolvQuery; +struct resolv_query; -int resolv_init(struct ev_loop *, char **, int, int); -struct ResolvQuery *resolv_query(const char *, void (*)(struct sockaddr *, - void *), void (*)( - void *), void *, uint16_t); -void resolv_cancel(struct ResolvQuery *); +int resolv_init(struct ev_loop *, char *, int); +struct resolv_query *resolv_start(const char *hostname, uint16_t port, + void (*client_cb)(struct sockaddr *, void *), + void (*free_cb)(void*), void *data); void resolv_shutdown(struct ev_loop *); #endif diff --git a/src/server.c b/src/server.c index 650b8db..8ef8ce5 100644 --- a/src/server.c +++ b/src/server.c @@ -46,7 +46,6 @@ #endif #include -#include #ifdef __MINGW32__ #include "win32.h" @@ -99,8 +98,8 @@ static void free_remote(remote_t *remote); static void close_and_free_remote(EV_P_ remote_t *remote); static void free_server(server_t *server); static void close_and_free_server(EV_P_ server_t *server); -static void server_resolve_cb(struct sockaddr *addr, void *data); -static void query_free_cb(void *data); +static void resolv_cb(struct sockaddr *addr, void *data); +static void resolv_free_cb(void *data); int verbose = 0; @@ -429,7 +428,7 @@ perform_handshake(EV_P_ server_t *server) info.ai_protocol = IPPROTO_TCP; if (ip.version == 4) { struct sockaddr_in *addr = (struct sockaddr_in *)&storage; - dns_pton(AF_INET, host, &(addr->sin_addr)); + inet_pton(AF_INET, host, &(addr->sin_addr)); addr->sin_port = port; addr->sin_family = AF_INET; info.ai_family = AF_INET; @@ -437,7 +436,7 @@ perform_handshake(EV_P_ server_t *server) info.ai_addr = (struct sockaddr *)addr; } else if (ip.version == 6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; - dns_pton(AF_INET6, host, &(addr->sin6_addr)); + inet_pton(AF_INET6, host, &(addr->sin6_addr)); addr->sin6_port = port; addr->sin6_family = AF_INET6; info.ai_family = AF_INET6; @@ -484,11 +483,19 @@ perform_handshake(EV_P_ server_t *server) query_t *query = ss_malloc(sizeof(query_t)); memset(query, 0, sizeof(query_t)); query->server = server; + server->query = query; snprintf(query->hostname, 256, "%s", host); server->stage = STAGE_RESOLVE; - server->query = resolv_query(host, server_resolve_cb, - query_free_cb, query, port); + struct resolv_query *q = resolv_start(host, port, + resolv_cb, resolv_free_cb, query); + + if (q == NULL) { + if (query != NULL) ss_free(query); + server->query = NULL; + close_and_free_server(EV_A_ server); + return; + } } @@ -722,21 +729,25 @@ server_timeout_cb(EV_P_ ev_timer *watcher, int revents) } static void -query_free_cb(void *data) +resolv_free_cb(void *data) { - if (data != NULL) { - ss_free(data); + query_t *query = (query_t *)data; + + if (query != NULL) { + if (query->server != NULL) + query->server->query = NULL; + ss_free(query); } } static void -server_resolve_cb(struct sockaddr *addr, void *data) +resolv_cb(struct sockaddr *addr, void *data) { query_t *query = (query_t *)data; server_t *server = query->server; - struct ev_loop *loop = server->listen_ctx->loop; + if (server == NULL) return; - server->query = NULL; + struct ev_loop *loop = server->listen_ctx->loop; if (addr == NULL) { LOGE("unable to resolve %s", query->hostname); @@ -1086,7 +1097,7 @@ close_and_free_server(EV_P_ server_t *server) { if (server != NULL) { if (server->query != NULL) { - resolv_cancel(server->query); + server->query->server = NULL; server->query = NULL; } ev_io_stop(EV_A_ & server->send_ctx->io); @@ -1154,8 +1165,7 @@ main(int argc, char **argv) int server_num = 0; const char *server_host[MAX_REMOTE_NUM]; - char *nameservers[MAX_DNS_NUM + 1]; - int nameserver_num = 0; + char *nameservers = NULL; ss_addr_t dst_addr = { .host = NULL, .port = NULL }; char *dst_addr_str = NULL; @@ -1322,9 +1332,7 @@ main(int argc, char **argv) iface = optarg; break; case 'd': - if (nameserver_num < MAX_DNS_NUM) { - nameservers[nameserver_num++] = optarg; - } + nameservers = optarg; break; case 'a': user = optarg; @@ -1400,8 +1408,8 @@ main(int argc, char **argv) nofile = conf->nofile; } #endif - if (conf->nameserver != NULL) { - nameservers[nameserver_num++] = conf->nameserver; + if (nameservers == NULL) { + nameservers = conf->nameserver; } if (ipv6first == 0) { ipv6first = conf->ipv6_first; @@ -1496,19 +1504,15 @@ main(int argc, char **argv) struct ev_loop *loop = EV_DEFAULT; // setup udns - if (nameserver_num == 0) { #ifdef __MINGW32__ nameservers[nameserver_num++] = "8.8.8.8"; resolv_init(loop, nameservers, nameserver_num, ipv6first); #else - resolv_init(loop, NULL, 0, ipv6first); + resolv_init(loop, nameservers, ipv6first); #endif - } else { - resolv_init(loop, nameservers, nameserver_num, ipv6first); - } - for (int i = 0; i < nameserver_num; i++) - LOGI("using nameserver: %s", nameservers[i]); + if (nameservers != NULL) + LOGI("using nameserver: %s", nameservers); // initialize listen context listen_ctx_t listen_ctx_list[server_num]; @@ -1575,6 +1579,9 @@ main(int argc, char **argv) } // Clean up + + resolv_shutdown(loop); + for (int i = 0; i <= server_num; i++) { listen_ctx_t *listen_ctx = &listen_ctx_list[i]; ev_io_stop(loop, &listen_ctx->io); @@ -1583,8 +1590,6 @@ main(int argc, char **argv) free_connections(loop); - resolv_shutdown(loop); - #ifdef __MINGW32__ winsock_cleanup(); #endif diff --git a/src/server.h b/src/server.h index ee27bc2..9cb48ef 100644 --- a/src/server.h +++ b/src/server.h @@ -51,6 +51,8 @@ typedef struct server_ctx { struct server *server; } server_ctx_t; +struct query; + typedef struct server { int fd; int stage; @@ -66,7 +68,7 @@ typedef struct server { struct listen_ctx *listen_ctx; struct remote *remote; - struct ResolvQuery *query; + struct query *query; struct cork_dllist_item entries; } server_t;