diff options
Diffstat (limited to 'ares_init.c')
-rw-r--r-- | ares_init.c | 224 |
1 files changed, 184 insertions, 40 deletions
diff --git a/ares_init.c b/ares_init.c index cb541af..051887d 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 @@ -248,20 +249,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,6 +284,29 @@ 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; + /* IPv4 nameservers were copied in ares_save_options, we + * can only copy v6 servers */ + if (src->nservers > (*dest)->nservers) + { + int i; + (*dest)->servers = + realloc((*dest)->servers, + src->nservers * sizeof(struct server_state)); + if (!(*dest)->servers) + return ARES_ENOMEM; + + for (i = 0; i < src->nservers; i++) + { + if (src->servers[i].addr.family == AF_INET6) + { + memcpy(&((*dest)->servers[(*dest)->nservers].addr), + &(src->servers[i].addr), + sizeof(struct ares_addr)); + (*dest)->nservers++; + } + } + init_servers(*dest); + } return ARES_SUCCESS; /* everything went fine */ @@ -334,17 +345,33 @@ int ares_save_options(ares_channel channel, struct ares_options *options, options->tcp_port = (unsigned short)channel->tcp_port; options->sock_state_cb = channel->sock_state_cb; options->sock_state_cb_data = channel->sock_state_cb_data; + options->nservers = 0; /* Copy servers */ if (channel->nservers) { + int numv4 = 0; options->servers = malloc(channel->nservers * sizeof(struct server_state)); - if (!options->servers && channel->nservers != 0) + if (!options->servers) return ARES_ENOMEM; for (i = 0; i < channel->nservers; i++) - options->servers[i] = channel->servers[i].addr; + { + if (channel->servers[i].addr.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 + */ + options->servers[numv4++] = + channel->servers[i].addr.addrV4; + } + } + options->servers = + realloc(options->servers, numv4 * sizeof(struct server_state)); + if (numv4 && !options->servers) + return ARES_ENOMEM; + options->nservers = numv4; } - options->nservers = channel->nservers; /* copy domains */ if (channel->ndomains) { @@ -431,7 +458,15 @@ 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]; + { + /* 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.family = AF_INET; + channel->servers[i].addr.addrV4 = options->servers[i]; + } } channel->nservers = options->nservers; } @@ -1001,7 +1036,9 @@ static int init_by_defaults(ares_channel channel) rc = ARES_ENOMEM; goto error; } - channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK); + + channel->servers[0].addr.family = AF_INET; + channel->servers[0].addr.addrV4.s_addr = htonl(INADDR_LOOPBACK); channel->nservers = 1; } @@ -1146,11 +1183,64 @@ 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; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; /* suppresses a lookup */ + hints.ai_family = AF_UNSPEC; + + newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state)); + if (!newserv) + { + freeaddrinfo(res); + return ARES_ENOMEM; + } + +#ifdef HAVE_GETADDRINFO + if (getaddrinfo(str, NULL, &hints, &res) != 0) + return ARES_SUCCESS; + + newserv[*nservers].addr.family = res->ai_family; + if (res->ai_family == AF_INET) + { + newserv[*nservers].addr.addrV4.s_addr = + ((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr; + } + else + { + memcpy(newserv[*nservers].addr.addrV6.s6_addr, + ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr.s6_addr, + 16); + } + + freeaddrinfo(res); +#else + struct in_addr addr; + + /* Ignore addresses we cannot parse, like V6 */ + addr.s_addr = inet_addr(str); + if (addr.s_addr == INADDR_NONE) + return ARES_SUCCESS; + + newserv[*nservers].addr.family = AF_INET; + newserv[*nservers].addr.addrV4 = addr; +#endif + + *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 +1268,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 +1278,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 +1658,69 @@ 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))); + } + free(channel->servers); + } + + channel->servers = NULL; + channel->nservers = -1; +} + +int ares_set_nameservers(ares_channel channel, + struct ares_addr *servers, + int num_servers) +{ + int i; + + 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++) + { + struct server_state *ss = channel->servers + i; + memset(ss, 0, sizeof(struct server_state)); + + ss->channel = channel; + memcpy(&ss->addr, &servers[i], sizeof(struct ares_addr)); + } + + init_servers(channel); + return ARES_SUCCESS; +} |