diff --git a/README.md b/README.md index 58e68c65..a00faaf2 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ If you are using CentOS 7, you need to install these prequirement to build from ```bash yum install epel-release -y -yum install gcc gettext autoconf libtool automake make pcre-devel asciidoc xmlto udns-devel libev-devel libsodium-devel mbedtls-devel -y +yum install gcc gettext autoconf libtool automake make pcre-devel asciidoc xmlto c-ares-devel libev-devel libsodium-devel mbedtls-devel -y ``` #### Install from repository @@ -245,7 +245,7 @@ In general, you need the following build dependencies: * libsodium * libpcre3 (old pcre library) * libev -* libudns +* libc-ares * asciidoc (for documentation only) * xmlto (for documentation only) @@ -260,11 +260,11 @@ For some of the distributions, you might install build dependencies like this: ```bash # Installation of basic build dependencies ## Debian / Ubuntu -sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libudns-dev automake libmbedtls-dev libsodium-dev +sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev ## CentOS / Fedora / RHEL -sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto udns-devel libev-devel +sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto c-ares-devel libev-devel ## Arch -sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto udns libev +sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev # Installation of Libsodium export LIBSODIUM_VER=1.0.13 diff --git a/cmake/configure.cmake b/cmake/configure.cmake index 693b1e57..97fb814a 100644 --- a/cmake/configure.cmake +++ b/cmake/configure.cmake @@ -67,7 +67,7 @@ check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) check_include_files(sys/stat.h HAVE_SYS_STAT_H) check_include_files(sys/types.h HAVE_SYS_TYPES_H) check_include_files(sys/wait.h HAVE_SYS_WAIT_H) -check_include_files(udns.h HAVE_UDNS_H) +check_include_files(ares.h HAVE_ARES_H) check_include_files(unistd.h HAVE_UNISTD_H) check_function_exists(fork HAVE_FORK) diff --git a/configure.ac b/configure.ac index eb2a2075..10f81616 100755 --- a/configure.ac +++ b/configure.ac @@ -218,12 +218,9 @@ AC_CHECK_LIB(socket, connect) dnl Checks for library functions. AC_CHECK_FUNCS([malloc memset posix_memalign socket]) -dnl Add define for libudns to enable IPv6 support -dnl This is an option defined in the origin configure script -AC_DEFINE([HAVE_IPv6], [1], [Enable IPv6 support in libudns]) +AC_CHECK_HEADERS([ares.h], [], [AC_MSG_ERROR([Couldn't find libcares. Try installing libc-ares-dev or c-ares-devel.])]) -AC_CHECK_HEADERS([udns.h], [], [AC_MSG_ERROR([Couldn't find libudns. Try installing libudns-dev or udns-devel.])]) -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_HEADERS([ev.h libev/ev.h], [], []) 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 1b77974b..e3964c9d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,10 +78,10 @@ ss_local_LDADD = $(SS_COMMON_LIBS) ss_tunnel_LDADD = $(SS_COMMON_LIBS) ss_server_LDADD = $(SS_COMMON_LIBS) ss_manager_LDADD = $(SS_COMMON_LIBS) -ss_local_LDADD += -ludns -ss_tunnel_LDADD += -ludns -ss_server_LDADD += -ludns -ss_manager_LDADD += -ludns +ss_local_LDADD += -lcares +ss_tunnel_LDADD += -lcares +ss_server_LDADD += -lcares +ss_manager_LDADD += -lcares ss_local_CFLAGS = $(AM_CFLAGS) -DMODULE_LOCAL ss_tunnel_CFLAGS = $(AM_CFLAGS) -DMODULE_TUNNEL @@ -104,7 +104,7 @@ ss_redir_SOURCES = utils.c \ ss_redir_CFLAGS = $(AM_CFLAGS) -DMODULE_REDIR ss_redir_LDADD = $(SS_COMMON_LIBS) -ss_redir_LDADD += -ludns +ss_redir_LDADD += -lcares endif lib_LTLIBRARIES = libshadowsocks-libev.la diff --git a/src/local.c b/src/local.c index e4bd4778..da0c46d7 100644 --- a/src/local.c +++ b/src/local.c @@ -52,7 +52,6 @@ #endif #include -#include #include "netutils.h" #include "utils.h" @@ -582,7 +581,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (acl || verbose) { uint16_t p = ntohs(*(uint16_t *)(buf->data + request_len + in_addr_len)); - dns_ntop(AF_INET, (const void *)(buf->data + request_len), + inet_ntop(AF_INET, (const void *)(buf->data + request_len), ip, INET_ADDRSTRLEN); sprintf(port, "%d", p); } @@ -615,7 +614,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (acl || verbose) { uint16_t p = ntohs(*(uint16_t *)(buf->data + request_len + in6_addr_len)); - dns_ntop(AF_INET6, (const void *)(buf->data + request_len), + inet_ntop(AF_INET6, (const void *)(buf->data + request_len), ip, INET6_ADDRSTRLEN); sprintf(port, "%d", p); } @@ -705,12 +704,12 @@ server_recv_cb(EV_P_ ev_io *w, int revents) switch(((struct sockaddr*)&storage)->sa_family) { case AF_INET: { struct sockaddr_in *addr_in = (struct sockaddr_in *)&storage; - dns_ntop(AF_INET, &(addr_in->sin_addr), ip, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(addr_in->sin_addr), ip, INET_ADDRSTRLEN); break; } case AF_INET6: { struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&storage; - dns_ntop(AF_INET6, &(addr_in6->sin6_addr), ip, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip, INET6_ADDRSTRLEN); break; } default: diff --git a/src/netutils.c b/src/netutils.c index 59a8e409..6333f0ba 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -23,12 +23,12 @@ #include #include -#include #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include #include #include #include @@ -97,12 +97,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)); } @@ -121,14 +121,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/redir.c b/src/redir.c index 5ca3d20e..c09f7711 100644 --- a/src/redir.c +++ b/src/redir.c @@ -41,7 +41,6 @@ #include #include -#include #include #ifdef HAVE_CONFIG_H @@ -228,11 +227,11 @@ server_recv_cb(EV_P_ ev_io *w, int revents) if (AF_INET == server->destaddr.ss_family) { struct sockaddr_in *sa = (struct sockaddr_in *)&(server->destaddr); - dns_ntop(AF_INET, &(sa->sin_addr), ipstr, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(sa->sin_addr), ipstr, INET_ADDRSTRLEN); port = ntohs(sa->sin_port); } else { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&(server->destaddr); - dns_ntop(AF_INET6, &(sa->sin6_addr), ipstr, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(sa->sin6_addr), ipstr, INET6_ADDRSTRLEN); port = ntohs(sa->sin6_port); } diff --git a/src/resolv.c b/src/resolv.c index baa3ab9d..a1f3c3ce 100644 --- a/src/resolv.c +++ b/src/resolv.c @@ -33,12 +33,13 @@ #include #include +#include #include #include #include #include -#include +#include #ifdef HAVE_LIBEV_EV_H #include @@ -46,28 +47,44 @@ #include #endif +#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; @@ -76,367 +93,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; - int flags = fcntl(sockfd, F_GETFL, 0); - fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + status = ares_init_options(&default_ctx.channel, &default_ctx.options, + ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB); + + if (status != ARES_SUCCESS) { + FATAL("failed to initialize c-ares"); + } - ev_io_init(&resolv_io_watcher, resolv_sock_cb, sockfd, EV_READ); - resolv_io_watcher.data = ctx; + 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_io_start(loop, &resolv_io_watcher); + if (status != ARES_SUCCESS) { + FATAL("failed to set nameservers"); + } - ev_timer_init(&resolv_timeout_watcher, resolv_timeout_cb, 0.0, 0.0); - resolv_timeout_watcher.data = ctx; + ev_init(&default_ctx.io, resolv_sock_cb); + ev_timer_init(&default_ctx.tw, resolv_timeout_cb, 0.0, 0.0); - dns_set_tmcbck(ctx, dns_timer_setup_cb, loop); - return sockfd; + return 0; } void resolv_shutdown(struct ev_loop *loop) { - struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + ares_cancel(default_ctx.channel); + ares_destroy(default_ctx.channel); - ev_io_stop(loop, &resolv_io_watcher); - - 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)); - 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; + memset(query, 0, sizeof(struct resolv_query)); + + 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"); + 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: - /* Once all queries have completed, call client callback */ - if (all_queries_are_null(cb_data)) { - return process_client_callback(cb_data); + query->requests[0] = 0; /* mark A query as being completed */ + + /* 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"); + 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: - /* Once all queries have completed, call client callback */ - if (all_queries_are_null(cb_data)) { - return process_client_callback(cb_data); + query->requests[1] = 0; /* mark A query as being completed */ + + /* 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; } -/* - * DNS timeout callback - */ -static void -resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) +static inline int +all_requests_are_null(struct resolv_query *query) { - struct dns_ctx *ctx = (struct dns_ctx *)w->data; + int result = 1; - if (revents & EV_TIMER) { - dns_timeouts(ctx, 30, ev_now(loop)); - } + for (int i = 0; i < sizeof(query->requests) / sizeof(query->requests[0]); + i++) + result = result && query->requests[i] == 0; + + return result; } /* - * Callback to setup DNS timeout callback + * DNS timeout callback */ static void -dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) +resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) { - struct ev_loop *loop = (struct ev_loop *)data; + struct resolv_ctx *ctx= cork_container_of(w, struct resolv_ctx, tw); - if (ev_is_active(&resolv_timeout_watcher)) { - ev_timer_stop(loop, &resolv_timeout_watcher); - } + ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - if (ctx != NULL && timeout >= 0) { - ev_timer_set(&resolv_timeout_watcher, timeout, 0.0); - ev_timer_start(loop, &resolv_timeout_watcher); - } + reset_timer(); } -static inline int -all_queries_are_null(struct ResolvQuery *cb_data) +static void +reset_timer() { - int result = 1; + 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); +} - for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); - i++) - result = result && cb_data->queries[i] == NULL; +/* + * Handle c-ares events + */ +static void +resolv_sock_state_cb(void *data, int s, int read, int write) { - return result; + struct resolv_ctx *ctx = (struct resolv_ctx *) data; + int io_active = ev_is_active(&ctx->io); + + 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 80154620..9bf62531 100644 --- a/src/resolv.h +++ b/src/resolv.h @@ -33,13 +33,12 @@ #include #include -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 83bc8316..ffe312e5 100644 --- a/src/server.c +++ b/src/server.c @@ -44,7 +44,6 @@ #include #include -#include #if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) #include @@ -108,8 +107,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; int reuse_port = 0; @@ -242,10 +241,10 @@ get_peer_name(int fd) if (err == 0) { if (addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&addr; - dns_ntop(AF_INET, &s->sin_addr, peer_name, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &s->sin_addr, peer_name, INET_ADDRSTRLEN); } else if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; - dns_ntop(AF_INET6, &s->sin6_addr, peer_name, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &s->sin6_addr, peer_name, INET6_ADDRSTRLEN); } } else { return NULL; @@ -454,10 +453,10 @@ connect_to_remote(EV_P_ struct addrinfo *res, if (res->ai_addr->sa_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)res->ai_addr; - dns_ntop(AF_INET, &s->sin_addr, ipstr, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &s->sin_addr, ipstr, INET_ADDRSTRLEN); } else if (res->ai_addr->sa_family == AF_INET6) { struct sockaddr_in6 *s = (struct sockaddr_in6 *)res->ai_addr; - dns_ntop(AF_INET6, &s->sin6_addr, ipstr, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, INET6_ADDRSTRLEN); } if (outbound_block_match_host(ipstr) == 1) { @@ -759,7 +758,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) addr->sin_family = AF_INET; if (server->buf->len >= in_addr_len + 3) { addr->sin_addr = *(struct in_addr *)(server->buf->data + offset); - dns_ntop(AF_INET, (const void *)(server->buf->data + offset), + inet_ntop(AF_INET, (const void *)(server->buf->data + offset), host, INET_ADDRSTRLEN); offset += in_addr_len; } else { @@ -796,7 +795,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) 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 = *(uint16_t *)(server->buf->data + offset); addr->sin_family = AF_INET; info.ai_family = AF_INET; @@ -804,7 +803,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) 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 = *(uint16_t *)(server->buf->data + offset); addr->sin6_family = AF_INET6; info.ai_family = AF_INET6; @@ -826,7 +825,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) addr->sin6_family = AF_INET6; if (server->buf->len >= in6_addr_len + 3) { addr->sin6_addr = *(struct in6_addr *)(server->buf->data + offset); - dns_ntop(AF_INET6, (const void *)(server->buf->data + offset), + inet_ntop(AF_INET6, (const void *)(server->buf->data + offset), host, INET6_ADDRSTRLEN); offset += in6_addr_len; } else { @@ -899,11 +898,19 @@ server_recv_cb(EV_P_ ev_io *w, int revents) 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; + } ev_io_stop(EV_A_ & server_recv_ctx->io); } @@ -992,21 +999,26 @@ 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; - server->query = NULL; + if (server == NULL) return; + + struct ev_loop *loop = server->listen_ctx->loop; if (addr == NULL) { LOGE("unable to resolve %s", query->hostname); @@ -1375,7 +1387,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); @@ -1484,8 +1496,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; static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, @@ -1574,9 +1585,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; @@ -1675,8 +1684,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; @@ -1776,15 +1785,11 @@ main(int argc, char **argv) // initialize ev loop struct ev_loop *loop = EV_DEFAULT; - // setup udns - if (nameserver_num == 0) { - resolv_init(loop, NULL, 0, ipv6first); - } else { - resolv_init(loop, nameservers, nameserver_num, ipv6first); - } + // setup dns + resolv_init(loop, nameservers, ipv6first); - for (int i = 0; i < nameserver_num; i++) - LOGI("using nameserver: %s", nameservers[i]); + if (nameservers != NULL) + LOGI("using nameserver: %s", nameservers); // Start plugin server if (plugin != NULL) { @@ -1909,6 +1914,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]; if (mode != UDP_ONLY) { @@ -1926,7 +1934,5 @@ main(int argc, char **argv) free_udprelay(); } - resolv_shutdown(loop); - return 0; } diff --git a/src/server.h b/src/server.h index 90cf051d..4eb50934 100644 --- a/src/server.h +++ b/src/server.h @@ -67,6 +67,8 @@ struct dscptracker { #endif +struct query; + typedef struct server { int fd; int stage; @@ -81,7 +83,7 @@ typedef struct server { struct listen_ctx *listen_ctx; struct remote *remote; - struct ResolvQuery *query; + struct query *query; struct cork_dllist_item entries; #ifdef USE_NFCONNTRACK_TOS diff --git a/src/tunnel.c b/src/tunnel.c index 2419fa0e..11600b69 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -47,7 +47,6 @@ #endif #include -#include #include "netutils.h" #include "utils.h" @@ -416,7 +415,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) memset(&host, 0, sizeof(struct in_addr)); int host_len = sizeof(struct in_addr); - if (dns_pton(AF_INET, sa->host, &host) == -1) { + if (inet_pton(AF_INET, sa->host, &host) == -1) { FATAL("IP parser error"); } abuf->data[abuf->len++] = 1; @@ -428,7 +427,7 @@ remote_send_cb(EV_P_ ev_io *w, int revents) memset(&host, 0, sizeof(struct in6_addr)); int host_len = sizeof(struct in6_addr); - if (dns_pton(AF_INET6, sa->host, &host) == -1) { + if (inet_pton(AF_INET6, sa->host, &host) == -1) { FATAL("IP parser error"); } abuf->data[abuf->len++] = 4; diff --git a/src/udprelay.c b/src/udprelay.c index c55efc08..37e6a7d5 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -47,7 +47,6 @@ #endif #include -#include #include "utils.h" #include "netutils.h" @@ -80,7 +79,8 @@ static void remote_timeout_cb(EV_P_ ev_timer *watcher, int revents); static char *hash_key(const int af, const struct sockaddr_storage *addr); #ifdef MODULE_REMOTE -static void query_resolve_cb(struct sockaddr *addr, void *data); +static void resolv_free_cb(void *data); +static void resolv_cb(struct sockaddr *addr, void *data); #endif static void close_and_free_remote(EV_P_ remote_ctx_t *ctx); static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); @@ -241,7 +241,7 @@ parse_udprelay_header(const char *buf, const size_t buf_len, addr->sin_port = *(uint16_t *)(buf + offset + in_addr_len); } if (host != NULL) { - dns_ntop(AF_INET, (const void *)(buf + offset), + inet_ntop(AF_INET, (const void *)(buf + offset), host, INET_ADDRSTRLEN); } offset += in_addr_len; @@ -257,12 +257,12 @@ parse_udprelay_header(const char *buf, const size_t buf_len, if (cork_ip_init(&ip, tmp) != -1) { if (ip.version == 4) { struct sockaddr_in *addr = (struct sockaddr_in *)storage; - dns_pton(AF_INET, tmp, &(addr->sin_addr)); + inet_pton(AF_INET, tmp, &(addr->sin_addr)); addr->sin_port = *(uint16_t *)(buf + offset + 1 + name_len); addr->sin_family = AF_INET; } else if (ip.version == 6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; - dns_pton(AF_INET, tmp, &(addr->sin6_addr)); + inet_pton(AF_INET, tmp, &(addr->sin6_addr)); addr->sin6_port = *(uint16_t *)(buf + offset + 1 + name_len); addr->sin6_family = AF_INET6; } @@ -284,7 +284,7 @@ parse_udprelay_header(const char *buf, const size_t buf_len, addr->sin6_port = *(uint16_t *)(buf + offset + in6_addr_len); } if (host != NULL) { - dns_ntop(AF_INET6, (const void *)(buf + offset), + inet_ntop(AF_INET6, (const void *)(buf + offset), host, INET6_ADDRSTRLEN); } offset += in6_addr_len; @@ -315,14 +315,14 @@ get_addr_str(const struct sockaddr *sa) switch (sa->sa_family) { case AF_INET: - dns_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), + inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), addr, INET_ADDRSTRLEN); p = ntohs(((struct sockaddr_in *)sa)->sin_port); sprintf(port, "%d", p); break; case AF_INET6: - dns_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, INET6_ADDRSTRLEN); p = ntohs(((struct sockaddr_in *)sa)->sin_port); sprintf(port, "%d", p); @@ -535,10 +535,6 @@ void close_and_free_query(EV_P_ struct query_ctx *ctx) { if (ctx != NULL) { - if (ctx->query != NULL) { - resolv_cancel(ctx->query); - ctx->query = NULL; - } if (ctx->buf != NULL) { bfree(ctx->buf); ss_free(ctx->buf); @@ -576,19 +572,24 @@ remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) #ifdef MODULE_REMOTE static void -query_resolve_cb(struct sockaddr *addr, void *data) +resolv_free_cb(void *data) { - struct query_ctx *query_ctx = (struct query_ctx *)data; - struct ev_loop *loop = query_ctx->server_ctx->loop; - - if (verbose) { - LOGI("[udp] udns resolved"); + struct query_ctx *ctx = (struct query_ctx *)data; + if (ctx->buf != NULL) { + bfree(ctx->buf); + ss_free(ctx->buf); } + ss_free(ctx); +} - query_ctx->query = NULL; +static void +resolv_cb(struct sockaddr *addr, void *data) +{ + struct query_ctx *query_ctx = (struct query_ctx *)data; + struct ev_loop *loop = query_ctx->server_ctx->loop; if (addr == NULL) { - LOGE("[udp] udns returned an error"); + LOGE("[udp] unable to resolve"); } else { remote_ctx_t *remote_ctx = query_ctx->remote_ctx; int cache_hit = 0; @@ -634,7 +635,10 @@ query_resolve_cb(struct sockaddr *addr, void *data) } if (remote_ctx != NULL) { - memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_storage)); + if (addr->sa_family == AF_INET) + memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_in)); + else + memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_in6)); size_t addr_len = get_sockaddr_len(addr); int s = sendto(remote_ctx->fd, query_ctx->buf->data, query_ctx->buf->len, @@ -656,9 +660,6 @@ query_resolve_cb(struct sockaddr *addr, void *data) } } } - - // clean up - close_and_free_query(EV_A_ query_ctx); } #endif @@ -1007,7 +1008,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) memset(&host_addr, 0, sizeof(struct in_addr)); int host_len = sizeof(struct in_addr); - if (dns_pton(AF_INET, host, &host_addr) == -1) { + if (inet_pton(AF_INET, host, &host_addr) == -1) { FATAL("IP parser error"); } addr_header[addr_header_len++] = 1; @@ -1019,7 +1020,7 @@ server_recv_cb(EV_P_ ev_io *w, int revents) memset(&host_addr, 0, sizeof(struct in6_addr)); int host_len = sizeof(struct in6_addr); - if (dns_pton(AF_INET6, host, &host_addr) == -1) { + if (inet_pton(AF_INET6, host, &host_addr) == -1) { FATAL("IP parser error"); } addr_header[addr_header_len++] = 4; @@ -1295,13 +1296,15 @@ server_recv_cb(EV_P_ ev_io *w, int revents) query_ctx->remote_ctx = remote_ctx; } - struct ResolvQuery *query = resolv_query(host, query_resolve_cb, - NULL, query_ctx, htons(atoi(port))); + struct resolv_query *query = resolv_start(host, htons(atoi(port)), + resolv_cb, resolv_free_cb, query_ctx); + if (query == NULL) { ERROR("[udp] unable to create DNS query"); close_and_free_query(EV_A_ query_ctx); goto CLEAN_UP; } + query_ctx->query = query; } #endif diff --git a/src/udprelay.h b/src/udprelay.h index 20d9dab2..61137f9e 100644 --- a/src/udprelay.h +++ b/src/udprelay.h @@ -67,7 +67,7 @@ typedef struct server_ctx { #ifdef MODULE_REMOTE typedef struct query_ctx { - struct ResolvQuery *query; + struct resolv_query *query; struct sockaddr_storage src_addr; buffer_t *buf; int addr_header_len;