summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2010-02-01 21:23:59 +0100
committerJakub Hrozek <jhrozek@redhat.com>2010-02-19 18:04:37 +0100
commitbe7c4f5806a29298988601f9bac5a4ebab6c000c (patch)
treed6f7a07643b8e8f2509da2b563cb27ca56fe0572
parent228e2c0a62faaedaf98a9dad10095f0980a07dfc (diff)
downloadc-ares-be7c4f5806a29298988601f9bac5a4ebab6c000c.tar.gz
c-ares-be7c4f5806a29298988601f9bac5a4ebab6c000c.tar.xz
c-ares-be7c4f5806a29298988601f9bac5a4ebab6c000c.zip
Allow the use of IPv6 nameserversHEADmaster
This patch allows the use of IPv6 addresses for nameserves in both /etc/resolv.conf and by using the ares_set_nameservers() API.
-rw-r--r--Makefile.inc3
-rw-r--r--ares.h14
-rw-r--r--ares_destroy.c10
-rw-r--r--ares_init.c224
-rw-r--r--ares_private.h15
-rw-r--r--ares_process.c95
-rw-r--r--ares_set_nameservers.368
7 files changed, 346 insertions, 83 deletions
diff --git a/Makefile.inc b/Makefile.inc
index 3227858..8e575fd 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -92,6 +92,7 @@ MANPAGES = ares_cancel.3 \
ares_search.3 \
ares_send.3 \
ares_set_socket_callback.3 \
+ ares_set_nameservers.3 \
ares_strerror.3 \
ares_timeout.3 \
ares_version.3
@@ -128,6 +129,7 @@ HTMLPAGES = ares_cancel.html \
ares_search.html \
ares_send.html \
ares_set_socket_callback.html \
+ ares_set_nameservers.html \
ares_strerror.html \
ares_timeout.html \
ares_version.html
@@ -164,6 +166,7 @@ PDFPAGES = ares_cancel.pdf \
ares_search.pdf \
ares_send.pdf \
ares_set_socket_callback.pdf \
+ ares_set_nameservers.pdf \
ares_strerror.pdf \
ares_timeout.pdf \
ares_version.pdf
diff --git a/ares.h b/ares.h
index b1c2c22..a3fa6a8 100644
--- a/ares.h
+++ b/ares.h
@@ -268,6 +268,16 @@ struct ares_channeldata;
typedef struct ares_channeldata *ares_channel;
+struct ares_addr {
+ int family;
+ union {
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ } addr;
+};
+#define addrV4 addr.addr4
+#define addrV6 addr.addr6
+
typedef void (*ares_callback)(void *arg,
int status,
int timeouts,
@@ -318,6 +328,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 ares_addr *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..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;
+}
diff --git a/ares_private.h b/ares_private.h
index 4726d7a..083de5a 100644
--- a/ares_private.h
+++ b/ares_private.h
@@ -110,16 +110,6 @@
# define writev(s,ptr,cnt) ares_writev(s,ptr,cnt)
#endif
-struct ares_addr {
- int family;
- union {
- struct in_addr addr4;
- struct in6_addr addr6;
- } addr;
-};
-#define addrV4 addr.addr4
-#define addrV6 addr.addr6
-
struct query;
struct send_request {
@@ -137,7 +127,8 @@ struct send_request {
};
struct server_state {
- struct in_addr addr;
+ struct ares_addr addr;
+
ares_socket_t udp_socket;
ares_socket_t tcp_socket;
@@ -319,6 +310,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..65b95f4 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,29 @@ 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)
+ {
+ if (((struct sockaddr_in *) &from)->sin_addr.s_addr !=
+ server->addr.addrV4.s_addr)
+ break;
+ }
+ else if (from.ss_family == AF_INET6)
+ {
+ if (memcmp(((struct sockaddr_in6 *) &from)->sin6_addr.s6_addr,
+ server->addr.addrV6.s6_addr,
+ 16))
+ 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 +902,44 @@ static int configure_socket(ares_socket_t s, ares_channel channel)
return 0;
}
+static int prepare_addrinfo(struct server_state *server, int port,
+ struct sockaddr_storage *addr)
+{
+ const unsigned short sh_port = (unsigned short)(port & 0xffff);
+ int rc = ARES_SUCCESS;
+
+ switch (server->addr.family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *) addr)->sin_port = sh_port;
+ ((struct sockaddr_in *) addr)->sin_addr.s_addr = server->addr.addrV4.s_addr;
+ break;
+
+ case AF_INET6:
+ ((struct sockaddr_in6 *) addr)->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *) addr)->sin6_port = sh_port;
+ memcpy(((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+ server->addr.addrV6.s6_addr,
+ 16);
+ 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;
+ struct sockaddr_storage addr;
/* Acquire a socket. */
- s = socket(AF_INET, SOCK_STREAM, 0);
+ s = socket(server->addr.family, SOCK_STREAM, 0);
if (s == ARES_SOCKET_BAD)
return -1;
@@ -923,12 +966,15 @@ static int open_tcp_socket(ares_channel channel, struct server_state *server)
}
#endif
+ if (prepare_addrinfo(server, channel->tcp_port, &addr) != 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, (struct sockaddr *) &addr,
+ sizeof(struct sockaddr_storage)) == -1)
{
int err = SOCKERRNO;
@@ -960,10 +1006,10 @@ 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;
+ struct sockaddr_storage addr;
/* Acquire a socket. */
- s = socket(AF_INET, SOCK_DGRAM, 0);
+ s = socket(server->addr.family, SOCK_DGRAM, 0);
if (s == ARES_SOCKET_BAD)
return -1;
@@ -974,12 +1020,15 @@ static int open_udp_socket(ares_channel channel, struct server_state *server)
return -1;
}
+ if (prepare_addrinfo(server, channel->udp_port, &addr) != 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, (struct sockaddr *) &addr,
+ sizeof(struct sockaddr_storage)) == -1)
{
int err = SOCKERRNO;
diff --git a/ares_set_nameservers.3 b/ares_set_nameservers.3
new file mode 100644
index 0000000..9795c98
--- /dev/null
+++ b/ares_set_nameservers.3
@@ -0,0 +1,68 @@
+.TH ARES_SET_NAMESERVERS 3 "12 Feb 2010"
+.SH NAME
+ares_set_nameservers - Set nameservers
+.SH SYNOPSIS
+.nf
+.B #include <ares.h>
+.PP
+.B int ares_set_nameservers(ares_channel \fIchannel\fP,
+ struct ares_addr *\fIservers\fP,
+ int \fInum_servers\fP)
+.PP
+.B cc file.c -lcares
+.fi
+.SH DESCRIPTION
+.PP
+This function sets nameservers for the given ares channel handle.
+The array
+.I servers
+contains the addresses of nameservers, the length of the array
+is stored in the
+.I num_servers
+parameter.
+Contrary to initializing nameservers with
+.B ares_init_options
+this function can be used to set IPv6 nameservers.
+
+The structure
+.I ares_addr
+contains the following fields:
+.sp
+.in +4n
+.nf
+struct ares_addr {
+ int family;
+ union {
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ } addr;
+};
+.fi
+.in
+
+Two shortcuts for accessing members of the union
+.I addr
+are defined:
+.sp
+.in +4n
+.nf
+#define addrV4 addr.addr4
+#define addrV6 addr.addr6
+.fi
+.in
+.PP
+.SH RETURN VALUES
+.B ares_set_nameservers
+can return any of the following values:
+.TP 15
+.B ARES_SUCCESS
+The response was successfully parsed.
+.TP 15
+.B ARES_ENOMEM
+Memory was exhausted.
+.SH SEE ALSO
+.BR ares_init_options (3)
+.SH AUTHOR
+Written by Jakub Hrozek <jhrozek@redhat.com>,
+on behalf of Red Hat, Inc http://www.redhat.com
+