-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 02/04/2010 02:32 PM, Yang Tse wrote: > I won't be able to look into this at least until February 10. Besides > the code changes it would also help if you described here the intended > functional changes, even if not complete yet, so we don't have to > discover them directly in the source code. >
A new patch is attached that also fixes ares_dup() and adds the manpage. Apart from the high-level summary in the patch itself, here's the changes: * struct "server state" no longer holds the address in "struct in_addr" but rather "struct addrinfo *" * when initializing from resolv.conf, getaddrinfo() is used in place of inet_addr() * a new API call ares_set_nameserves which can set the nameservers used * in order not to break "struct ares_options", ares_save_options and ares_init_options still works with v4 nameservers only. ares_dup() works for both v4 and v6 servers. More info is, of course, in the patch :-) Jakub -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/ iEYEARECAAYFAkt1neoACgkQHsardTLnvCXYwgCgwh5Y9k3FcovX8a14Qd7/ZXPQ hFAAn30MAtep6FkKRIVuNsDOPEDZedZE =StmA -----END PGP SIGNATURE-----
From 407d01fa8f6e2016c07701c314ff4908c3525751 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Mon, 1 Feb 2010 21:23:59 +0100 Subject: [PATCH] Allow the use of IPv6 nameservers This patch allows the use of IPv6 addresses for nameserves in both /etc/resolv.conf and by using the ares_set_nameservers() API. --- Makefile.inc | 1 + ares.h | 4 + ares_destroy.c | 10 +-- ares_init.c | 283 +++++++++++++++++++++++++++++++++++++++++------- ares_private.h | 6 +- ares_process.c | 84 +++++++++++---- ares_set_nameservers.3 | 41 +++++++ 7 files changed, 356 insertions(+), 73 deletions(-) create mode 100644 ares_set_nameservers.3 diff --git a/Makefile.inc b/Makefile.inc index 3227858..5139aa1 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 diff --git a/ares.h b/ares.h index b1c2c22..ad5b17f 100644 --- a/ares.h +++ b/ares.h @@ -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..38acc6a 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,6 +288,48 @@ 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->ai_family == AF_INET6) + { + (*dest)->servers[(*dest)->nservers].addr = + malloc(sizeof(struct addrinfo)); + if (!(*dest)->servers[(*dest)->nservers].addr) + return ARES_ENOMEM; + (*dest)->servers[(*dest)->nservers].free_addr = 1; + memset((*dest)->servers[(*dest)->nservers].addr, + 0, sizeof(struct addrinfo)); + + (*dest)->servers[(*dest)->nservers].addr->ai_family = AF_INET6; + (*dest)->servers[(*dest)->nservers].addr->ai_addrlen = + sizeof(struct sockaddr_in6); + (*dest)->servers[(*dest)->nservers].addr->ai_addr = + malloc(sizeof(struct sockaddr_in6)); + if (!(*dest)->servers[(*dest)->nservers].addr->ai_addr) + return ARES_ENOMEM; + + memcpy((*dest)->servers[(*dest)->nservers].addr->ai_addr, + (struct sockaddr_in6 *) src->servers[i].addr->ai_addr, + sizeof(struct sockaddr_in6)); + + (*dest)->nservers++; + } + else return ARES_EBADFAMILY; /* should not happen */ + + init_servers(*dest); + } + } return ARES_SUCCESS; /* everything went fine */ @@ -334,17 +368,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->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 + */ + options->servers[numv4++] = + ((struct sockaddr_in *) channel->servers[i].addr->ai_addr)->sin_addr; + } + } + 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 +481,28 @@ 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 = 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; + channel->servers[i].free_addr = 1; + + ((struct sockaddr_in *) channel->servers[i].addr->ai_addr)->sin_family = AF_INET; + ((struct sockaddr_in *) channel->servers[i].addr->ai_addr)->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 +1049,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 +1074,16 @@ 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->servers[0].free_addr = 0; channel->nservers = 1; } @@ -1146,11 +1228,38 @@ 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; + newserv[*nservers].free_addr = 0; + *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 +1287,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 +1297,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 +1677,109 @@ 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))); + if (server->free_addr) + { + free(server->addr->ai_addr); + server->free_addr = 0; + } + 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; + + 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; + ss->addr = malloc(sizeof(struct addrinfo)); + if (!ss->addr) + return ARES_ENOMEM; + ss->free_addr = 1; + memset(ss->addr, 0, sizeof(struct addrinfo)); + + if (servers[i].ss_family == AF_INET) + { + ss->addr->ai_family = AF_INET; + ss->addr->ai_addrlen = sizeof(struct sockaddr_in); + ss->addr->ai_addr = + malloc(sizeof(struct sockaddr_in)); + if (!ss->addr->ai_addr) + return ARES_ENOMEM; + + memcpy(ss->addr->ai_addr, (struct sockaddr_in *) &servers[i], + sizeof(struct sockaddr_in)); + } + else if (servers[i].ss_family == AF_INET6) + { + ss->addr->ai_family = AF_INET6; + ss->addr->ai_addrlen = sizeof(struct sockaddr_in6); + ss->addr->ai_addr = + malloc(sizeof(struct sockaddr_in6)); + if (!ss->addr->ai_addr) + return ARES_ENOMEM; + + memcpy(ss->addr->ai_addr, (struct sockaddr_in6 *) &servers[i], + sizeof(struct sockaddr_in6)); + } + else + { + /* This should never happen */ + return ARES_EBADFAMILY; + } + } + + init_servers(channel); + return ARES_SUCCESS; +} diff --git a/ares_private.h b/ares_private.h index 4726d7a..0e0bfaf 100644 --- a/ares_private.h +++ b/ares_private.h @@ -137,7 +137,9 @@ struct send_request { }; struct server_state { - struct in_addr addr; + struct addrinfo *addr; + int free_addr; + ares_socket_t udp_socket; ares_socket_t tcp_socket; @@ -319,6 +321,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..5bd16a8 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 != + ((struct sockaddr_in *) server->addr->ai_addr)->sin_addr.s_addr) + break; + } + else if (from.ss_family == AF_INET6) + { + if (memcmp(((struct sockaddr_in6 *) &from)->sin6_addr.s6_addr, + ((struct sockaddr_in6 *) server->addr->ai_addr)->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 +902,36 @@ 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; + const unsigned short sh_port = (unsigned short)(port & 0xffff); + + switch (addr->ai_family) + { + case AF_INET: + ((struct sockaddr_in *) addr->ai_addr)->sin_port = sh_port; + break; + + case AF_INET6: + ((struct sockaddr_in6 *) addr->ai_addr)->sin6_port = sh_port; + 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 +958,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 +997,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 +1010,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; diff --git a/ares_set_nameservers.3 b/ares_set_nameservers.3 new file mode 100644 index 0000000..ca7219d --- /dev/null +++ b/ares_set_nameservers.3 @@ -0,0 +1,41 @@ +.TH ARES_SET_SOCKET_CALLBACK 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 sockaddr_storage *\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. +.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 <jhro...@redhat.com>, +on behalf of Red Hat, Inc http://www.redhat.com + -- 1.6.6
0001-Allow-the-use-of-IPv6-nameservers.patch.sig
Description: PGP signature