diff options
Diffstat (limited to 'ares_init.c')
-rw-r--r-- | ares_init.c | 232 |
1 files changed, 193 insertions, 39 deletions
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; +} |