summaryrefslogtreecommitdiffstats
path: root/ares_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'ares_init.c')
-rw-r--r--ares_init.c232
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;
+}