diff options
-rw-r--r-- | ares.h | 4 | ||||
-rw-r--r-- | ares_destroy.c | 10 | ||||
-rw-r--r-- | ares_init.c | 232 | ||||
-rw-r--r-- | ares_private.h | 4 | ||||
-rw-r--r-- | ares_process.c | 90 |
5 files changed, 268 insertions, 72 deletions
@@ -318,6 +318,10 @@ CARES_EXTERN void ares_set_socket_callback(ares_channel channel, ares_sock_create_callback callback, void *user_data); +CARES_EXTERN int ares_set_nameservers(ares_channel channel, + struct sockaddr_storage *servers, + int num_servers); + CARES_EXTERN void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen, diff --git a/ares_destroy.c b/ares_destroy.c index 0044a71..4040971 100644 --- a/ares_destroy.c +++ b/ares_destroy.c @@ -67,15 +67,7 @@ void ares_destroy(ares_channel channel) } #endif - if (channel->servers) { - for (i = 0; i < channel->nservers; i++) - { - struct server_state *server = &channel->servers[i]; - ares__close_sockets(channel, server); - assert(ares__is_list_empty(&(server->queries_to_server))); - } - free(channel->servers); - } + ares__free_servers(channel); if (channel->domains) { for (i = 0; i < channel->ndomains; i++) diff --git a/ares_init.c b/ares_init.c index cb541af..abe7a6b 100644 --- a/ares_init.c +++ b/ares_init.c @@ -65,6 +65,7 @@ #include <ctype.h> #include <time.h> #include <errno.h> +#include <assert.h> #include "ares.h" #include "inet_net_pton.h" #include "ares_library_init.h" @@ -88,6 +89,7 @@ static int set_search(ares_channel channel, const char *str); static int set_options(ares_channel channel, const char *str); static const char *try_option(const char *p, const char *q, const char *opt); static int init_id_key(rc4_key* key,int key_data_len); +static void init_servers(ares_channel channel); #if !defined(WIN32) && !defined(WATT32) static int sortlist_alloc(struct apattern **sortlist, int *nsort, struct apattern *pat); @@ -118,7 +120,6 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, ares_channel channel; int i; int status = ARES_SUCCESS; - struct server_state *server; struct timeval now; #ifdef CURLDEBUG @@ -227,6 +228,10 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, if (status != ARES_SUCCESS) { /* Something failed; clean up memory we may have allocated. */ + for (i = 0; i < channel->nservers; i++) + { + freeaddrinfo(channel->servers[i].addr); + } if (channel->servers) free(channel->servers); if (channel->domains) @@ -248,20 +253,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options, channel->nservers = 1; /* Initialize server states. */ - for (i = 0; i < channel->nservers; i++) - { - server = &channel->servers[i]; - server->udp_socket = ARES_SOCKET_BAD; - server->tcp_socket = ARES_SOCKET_BAD; - server->tcp_connection_generation = ++channel->tcp_connection_generation; - server->tcp_lenbuf_pos = 0; - server->tcp_buffer = NULL; - server->qhead = NULL; - server->qtail = NULL; - ares__init_list_head(&(server->queries_to_server)); - server->channel = channel; - server->is_broken = 0; - } + init_servers(channel); *channelptr = channel; return ARES_SUCCESS; @@ -296,7 +288,6 @@ int ares_dup(ares_channel *dest, ares_channel src) (*dest)->sock_create_cb = src->sock_create_cb; (*dest)->sock_create_cb_data = src->sock_create_cb_data; - return ARES_SUCCESS; /* everything went fine */ } @@ -342,7 +333,18 @@ int ares_save_options(ares_channel channel, struct ares_options *options, if (!options->servers && channel->nservers != 0) return ARES_ENOMEM; for (i = 0; i < channel->nservers; i++) - options->servers[i] = channel->servers[i].addr; + { + if (channel->servers[i].addr->ai_family == AF_INET) + { + /* Because struct ares_options should stay the same + * only IPv4 nameservers are saved in this patch. + * ares_dup() works for both v4 and v6, though + */ + struct sockaddr_in *si; + si = (struct sockaddr_in *) channel->servers[i].addr->ai_addr; + options->servers[i] = si->sin_addr; + } + } } options->nservers = channel->nservers; @@ -431,7 +433,30 @@ static int init_by_options(ares_channel channel, if (!channel->servers) return ARES_ENOMEM; for (i = 0; i < options->nservers; i++) - channel->servers[i].addr = options->servers[i]; + { + struct sockaddr_in *si = NULL; + + /* This is a rather crude way to allow usage of + * protocol-independent struct addrinfo while not + * breaking the public struct ares_options which is forbidden + * in favor of providing ares_set_XXX() functions + */ + channel->servers[i].addr = malloc(sizeof(struct addrinfo)); + if (!channel->servers[i].addr) + return ARES_ENOMEM; + + channel->servers[i].addr->ai_addr = malloc(sizeof(struct sockaddr)); + if (!channel->servers[i].addr->ai_addr) + return ARES_ENOMEM; + + si = (struct sockaddr_in *) channel->servers[i].addr->ai_addr; + si->sin_family = AF_INET; + si->sin_addr = options->servers[i]; + + channel->servers[i].addr->ai_family = AF_INET; + channel->servers[i].addr->ai_next = NULL; + channel->servers[i].addr->ai_addrlen = sizeof(struct sockaddr_in); + } } channel->nservers = options->nservers; } @@ -978,6 +1003,8 @@ static int init_by_defaults(ares_channel channel) #ifdef HAVE_GETHOSTNAME char *dot; #endif + struct addrinfo *res = NULL; + struct addrinfo hints; if (channel->flags == -1) channel->flags = 0; @@ -1001,7 +1028,15 @@ static int init_by_defaults(ares_channel channel) rc = ARES_ENOMEM; goto error; } - channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + rc = getaddrinfo(NULL, "53", &hints, &res); + if (rc != 0) + return ARES_SUCCESS; + + channel->servers[0].addr = res; channel->nservers = 1; } @@ -1146,11 +1181,37 @@ static int config_lookup(ares_channel channel, const char *str, #endif /* !WIN32 & !WATT32 */ #ifndef WATT32 +static int set_nameserver(struct server_state **servers, int *nservers, + char *str) +{ + struct server_state *newserv; + struct addrinfo *res = NULL; + struct addrinfo hints; + int ret; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; /* suppresses a lookup */ + hints.ai_family = AF_UNSPEC; + + ret = getaddrinfo(str, NULL, &hints, &res); + if (ret != 0) + return ARES_SUCCESS; + + newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state)); + if (!newserv) + return ARES_ENOMEM; + newserv[*nservers].addr = res; + *servers = newserv; + (*nservers)++; + + return ARES_SUCCESS; +} + static int config_nameserver(struct server_state **servers, int *nservers, char *str) { - struct in_addr addr; - struct server_state *newserv; + int ret; + /* On Windows, there may be more than one nameserver specified in the same * registry key, so we parse it as a space or comma seperated list. */ @@ -1178,15 +1239,9 @@ static int config_nameserver(struct server_state **servers, int *nservers, } /* This is the part that actually sets the nameserver */ - addr.s_addr = inet_addr(begin); - if (addr.s_addr == INADDR_NONE) - continue; - newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state)); - if (!newserv) - return ARES_ENOMEM; - newserv[*nservers].addr = addr; - *servers = newserv; - (*nservers)++; + ret = set_nameserver(servers, nservers, begin); + if (ret) + return ret; if (!more) break; @@ -1194,15 +1249,9 @@ static int config_nameserver(struct server_state **servers, int *nservers, } #else /* Add a nameserver entry, if this is a valid address. */ - addr.s_addr = inet_addr(str); - if (addr.s_addr == INADDR_NONE) - return ARES_SUCCESS; - newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state)); - if (!newserv) - return ARES_ENOMEM; - newserv[*nservers].addr = addr; - *servers = newserv; - (*nservers)++; + ret = set_nameserver(servers, nservers, str); + if (ret) + return ret; #endif return ARES_SUCCESS; } @@ -1580,3 +1629,108 @@ void ares_set_socket_callback(ares_channel channel, channel->sock_create_cb = cb; channel->sock_create_cb_data = data; } + +static void init_servers(ares_channel channel) +{ + struct server_state *server; + int i; + + for (i = 0; i < channel->nservers; i++) + { + server = &channel->servers[i]; + server->udp_socket = ARES_SOCKET_BAD; + server->tcp_socket = ARES_SOCKET_BAD; + server->tcp_connection_generation = ++channel->tcp_connection_generation; + server->tcp_lenbuf_pos = 0; + server->tcp_buffer = NULL; + server->qhead = NULL; + server->qtail = NULL; + ares__init_list_head(&(server->queries_to_server)); + server->channel = channel; + server->is_broken = 0; + } +} + +void ares__free_servers(ares_channel channel) +{ + int i; + + if (channel->servers) { + for (i = 0; i < channel->nservers; i++) + { + struct server_state *server = &channel->servers[i]; + ares__close_sockets(channel, server); + assert(ares__is_list_empty(&(server->queries_to_server))); + freeaddrinfo(server->addr); + } + free(channel->servers); + } + + channel->servers = NULL; + channel->nservers = -1; +} + +int ares_set_nameservers(ares_channel channel, + struct sockaddr_storage *servers, + int num_servers) +{ + int i; + int rc = ARES_SUCCESS; + struct sockaddr_in6 *s6 = NULL; + struct sockaddr_in *s4 = NULL; + + ares__free_servers(channel); + + channel->nservers = num_servers; + channel->servers = + malloc(channel->nservers * sizeof(struct server_state)); + if (!channel->servers) + return ARES_ENOMEM; + + for (i = 0; i < num_servers; i++) + { + memset(&channel->servers[i], 0, sizeof(struct server_state)); + + channel->servers[i].channel = channel; + channel->servers[i].addr = malloc(sizeof(struct addrinfo)); + if (!channel->servers[i].addr) + return ARES_ENOMEM; + memset(channel->servers[i].addr, 0, sizeof(struct addrinfo)); + + if (servers[i].ss_family == AF_INET) + { + s4 = (struct sockaddr_in *) &servers[i]; + channel->servers[i].addr->ai_family = AF_INET; + channel->servers[i].addr->ai_addrlen = sizeof(struct sockaddr_in); + channel->servers[i].addr->ai_addr = + malloc(sizeof(struct sockaddr_in)); + if (!channel->servers[i].addr->ai_addr) + return ARES_ENOMEM; + + memcpy(channel->servers[i].addr->ai_addr, s4, + sizeof(struct sockaddr_in)); + } + else if (servers[i].ss_family == AF_INET6) + { + s6 = (struct sockaddr_in6 *) &servers[i]; + + channel->servers[i].addr->ai_family = AF_INET6; + channel->servers[i].addr->ai_addrlen = sizeof(struct sockaddr_in6); + channel->servers[i].addr->ai_addr = + malloc(sizeof(struct sockaddr_in6)); + if (!channel->servers[i].addr->ai_addr) + return ARES_ENOMEM; + + memcpy(channel->servers[i].addr->ai_addr, s6, + sizeof(struct sockaddr_in6)); + } + else + { + /* This should never happen */ + return ARES_EBADFAMILY; + } + } + + init_servers(channel); + return rc; +} diff --git a/ares_private.h b/ares_private.h index 4726d7a..e4ebe73 100644 --- a/ares_private.h +++ b/ares_private.h @@ -137,7 +137,7 @@ struct send_request { }; struct server_state { - struct in_addr addr; + struct addrinfo *addr; ares_socket_t udp_socket; ares_socket_t tcp_socket; @@ -319,6 +319,8 @@ struct timeval ares__tvnow(void); int ares__expand_name_for_response(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen); +void ares__free_servers(ares_channel channel); + #if 0 /* Not used */ long ares__tvdiff(struct timeval t1, struct timeval t2); #endif diff --git a/ares_process.c b/ares_process.c index 71f9394..689d1c1 100644 --- a/ares_process.c +++ b/ares_process.c @@ -434,7 +434,7 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds, ssize_t count; unsigned char buf[PACKETSZ + 1]; #ifdef HAVE_RECVFROM - struct sockaddr_in from; + struct sockaddr_storage from; ares_socklen_t fromlen; #endif @@ -480,16 +480,32 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds, if (count == -1 && try_again(SOCKERRNO)) continue; else if (count <= 0) - handle_error(channel, i, now); + { + handle_error(channel, i, now); + break; + } #ifdef HAVE_RECVFROM - else if (from.sin_addr.s_addr != server->addr.s_addr) - /* Address response came from did not match the address - * we sent the request to. Someone may be attempting - * to perform a cache poisoning attack */ - break; + /* Check if address response came from did match the address + * we sent the request to. Someone may be attempting + * to perform a cache poisoning attack */ + else if (from.ss_family == AF_INET) + { + struct sockaddr_in *from4 = (struct sockaddr_in *) &from; + struct sockaddr_in *srv4 = (struct sockaddr_in *) server->addr->ai_addr; + if (from4->sin_addr.s_addr != srv4->sin_addr.s_addr) + break; + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *from6 = (struct sockaddr_in6 *) &from; + struct sockaddr_in6 *srv6 = (struct sockaddr_in6 *) server->addr->ai_addr; + if (memcmp(from6->sin6_addr.s6_addr, + srv6->sin6_addr.s6_addr, + 16)) /* FIXME - is there any portable constant? */ + break; + } #endif - else - process_answer(channel, buf, (int)count, i, 0, now); + process_answer(channel, buf, (int)count, i, 0, now); } while (count > 0); } } @@ -889,14 +905,39 @@ static int configure_socket(ares_socket_t s, ares_channel channel) return 0; } +static int addrinfo_set_port(struct addrinfo *addr, int port) +{ + int rc = ARES_SUCCESS; + struct sockaddr_in *sockin = NULL; + struct sockaddr_in6 *sockin6 = NULL; + + switch (addr->ai_family) + { + case AF_INET: + sockin = (struct sockaddr_in *) addr->ai_addr; + sockin->sin_port = (unsigned short)(port & 0xffff); + break; + + case AF_INET6: + sockin6 = (struct sockaddr_in6 *) addr->ai_addr; + sockin6->sin6_port = (unsigned short)(port & 0xffff); + break; + + default: + rc = ARES_EBADFAMILY; + break; + } + + return rc; +} + static int open_tcp_socket(ares_channel channel, struct server_state *server) { ares_socket_t s; int opt; - struct sockaddr_in sockin; /* Acquire a socket. */ - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(server->addr->ai_family, SOCK_STREAM, 0); if (s == ARES_SOCKET_BAD) return -1; @@ -923,12 +964,14 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) } #endif + if (addrinfo_set_port(server->addr, channel->tcp_port) != ARES_SUCCESS) + { + sclose(s); + return -1; + } + /* Connect to the server. */ - memset(&sockin, 0, sizeof(sockin)); - sockin.sin_family = AF_INET; - sockin.sin_addr = server->addr; - sockin.sin_port = (unsigned short)(channel->tcp_port & 0xffff); - if (connect(s, (struct sockaddr *) &sockin, sizeof(sockin)) == -1) + if (connect(s, server->addr->ai_addr, server->addr->ai_addrlen) == -1) { int err = SOCKERRNO; @@ -960,10 +1003,9 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server) static int open_udp_socket(ares_channel channel, struct server_state *server) { ares_socket_t s; - struct sockaddr_in sockin; /* Acquire a socket. */ - s = socket(AF_INET, SOCK_DGRAM, 0); + s = socket(server->addr->ai_family, SOCK_DGRAM, 0); if (s == ARES_SOCKET_BAD) return -1; @@ -974,12 +1016,14 @@ static int open_udp_socket(ares_channel channel, struct server_state *server) return -1; } + if (addrinfo_set_port(server->addr, channel->udp_port) != ARES_SUCCESS) + { + sclose(s); + return -1; + } + /* Connect to the server. */ - memset(&sockin, 0, sizeof(sockin)); - sockin.sin_family = AF_INET; - sockin.sin_addr = server->addr; - sockin.sin_port = (unsigned short)(channel->udp_port & 0xffff); - if (connect(s, (struct sockaddr *) &sockin, sizeof(sockin)) == -1) + if (connect(s, server->addr->ai_addr, server->addr->ai_addrlen) == -1) { int err = SOCKERRNO; |