Hello, Here comes another feature patch for the reSIProcate transition from ares to c-ares. The embedded copy supports querying IPv6 nameservers and setting these up via the options structure.
The reSIProcate developers substituted the in_addr field by an union contruct to hold the in6_addr. As this would break API compatibility, I have added a second nservers and servers field and suffixed it with a '6'. For the internal part I've declared the mf_address structure: struct mf_address { sa_family_t family; union { struct in_addr in4; struct in6_addr in6; } mf_addr; }; Another approach would be the usage of sockaddr_storage. But all the casting to sockaddr_in and sockaddr_in6 made the code very ugly and hard to read. Maybe some macros would help here. The other drawback of sockaddr_storage is it's relativly young age, so that it's (probably) not available on all target platforms. Things that needs to be done: * Guard the AF_INET6 specific part by the preprocessor. What (config-)defines would be optimal for this task? A combination of HAVE_AF_INET6, HAVE_STRUCT_SOCKADDR_STORAGE, HAVE_STRUCT_SOCKADDR_IN6? * Test on all supported target platforms * Documentation But before I continue I'd like to get some feedback on this issue. Thanks, Gregor
Index: adig.c =================================================================== RCS file: /cvsroot/curl/curl/ares/adig.c,v retrieving revision 1.36 diff -u -3 -p -r1.36 adig.c --- adig.c 17 Oct 2008 19:04:53 -0000 1.36 +++ adig.c 23 Nov 2008 17:29:36 -0000 @@ -152,7 +152,6 @@ static const char *opcodes[] = { "UPDATEA", "UPDATED", "UPDATEDA", "UPDATEM", "UPDATEMA", "ZONEINIT", "ZONEREF" }; - struct in_addr inaddr; static const char *rcodes[] = { "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", @@ -180,6 +179,8 @@ int main(int argc, char **argv) struct hostent *hostent; fd_set read_fds, write_fds; struct timeval *tvp, tv; + struct in_addr inaddr; + struct in6_addr in6addr; #ifdef USE_WINSOCK WORD wVersionRequested = MAKEWORD(USE_WINSOCK,USE_WINSOCK); @@ -190,7 +191,9 @@ int main(int argc, char **argv) options.flags = ARES_FLAG_NOCHECKRESP; options.servers = NULL; options.nservers = 0; - while ((c = ares_getopt(argc, argv, "df:s:c:t:T:U:")) != -1) + options.servers6 = NULL; + options.nservers6 = 0; + while ((c = ares_getopt(argc, argv, "df:s:S:c:t:T:U:")) != -1) { switch (c) { @@ -237,6 +240,26 @@ int main(int argc, char **argv) optmask |= ARES_OPT_SERVERS; break; + case 'S': + /* Add a server, and specify servers in the option mask. */ + if (ares_inet_pton(AF_INET6, optarg, &in6addr) <= 0) + { + fprintf(stderr, "adig: inet_pton returned an error on server %s.\n", optarg); + return 1; + } + options.servers6 = realloc(options.servers6, (options.nservers6 + 1) + * sizeof(struct in6_addr)); + if (!options.servers6) + { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + memcpy(&options.servers6[options.nservers6], &in6addr, + sizeof(struct in_addr)); + options.nservers6++; + optmask |= ARES_OPT_SERVERS6; + break; + case 'c': /* Set the query class. */ for (i = 0; i < nclasses; i++) @@ -715,7 +738,7 @@ static const char *class_name(int dnscla static void usage(void) { - fprintf(stderr, "usage: adig [-f flag] [-s server] [-c class] " + fprintf(stderr, "usage: adig [-f flag] [-s IPv4 server] [-S IPv6 server] [-c class] " "[-t type] [-p port] name ...\n"); exit(1); } Index: ares.h =================================================================== RCS file: /cvsroot/curl/curl/ares/ares.h,v retrieving revision 1.42 diff -u -3 -p -r1.42 ares.h --- ares.h 19 Nov 2008 15:16:16 -0000 1.42 +++ ares.h 23 Nov 2008 17:29:36 -0000 @@ -115,6 +115,7 @@ extern "C" { #define ARES_OPT_SOCK_RCVBUF (1 << 12) #define ARES_OPT_TIMEOUTMS (1 << 13) #define ARES_OPT_ROTATE (1 << 14) +#define ARES_OPT_SERVERS6 (1 << 15) /* Nameinfo flag values */ #define ARES_NI_NOFQDN (1 << 0) @@ -180,6 +181,15 @@ typedef void (*ares_sock_state_cb)(void struct apattern; +#if !defined(HAVE_STRUCT_IN6_ADDR) && !defined(s6_addr) +struct in6_addr { + union { + unsigned char _S6_u8[16]; + } _S6_un; +}; +#define s6_addr _S6_un._S6_u8 +#endif + struct ares_options { int flags; int timeout; /* in seconds or milliseconds, depending on options */ @@ -199,6 +209,8 @@ struct ares_options { void *sock_state_cb_data; struct apattern *sortlist; int nsort; + struct in6_addr *servers6; + int nservers6; }; struct hostent; @@ -251,14 +263,6 @@ int ares_expand_name(const unsigned char int ares_expand_string(const unsigned char *encoded, const unsigned char *abuf, int alen, unsigned char **s, long *enclen); -#if !defined(HAVE_STRUCT_IN6_ADDR) && !defined(s6_addr) -struct in6_addr { - union { - unsigned char _S6_u8[16]; - } _S6_un; -}; -#define s6_addr _S6_un._S6_u8 -#endif struct addrttl { struct in_addr ipaddr; Index: ares_init.c =================================================================== RCS file: /cvsroot/curl/curl/ares/ares_init.c,v retrieving revision 1.81 diff -u -3 -p -r1.81 ares_init.c --- ares_init.c 20 Nov 2008 07:50:48 -0000 1.81 +++ ares_init.c 23 Nov 2008 17:29:36 -0000 @@ -261,7 +261,7 @@ int ares_init_options(ares_channel *chan int ares_save_options(ares_channel channel, struct ares_options *options, int *optmask) { - int i; + int i, s; /* Zero everything out */ memset(options, 0, sizeof(struct ares_options)); @@ -272,7 +272,7 @@ int ares_save_options(ares_channel chann (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS| ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB| ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS| - ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS); + ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS|ARES_OPT_SERVERS6); /* Copy easy stuff */ options->flags = channel->flags; @@ -287,16 +287,38 @@ int ares_save_options(ares_channel chann options->sock_state_cb = channel->sock_state_cb; options->sock_state_cb_data = channel->sock_state_cb_data; - /* Copy servers */ - if (channel->nservers) { + /* Count servers */ + options->nservers = options->nservers6 = 0; + for (i = 0; i < channel->nservers; i++) + switch (channel->servers[i].address.family) + { + case AF_INET: options->nservers++; break; + case AF_INET6: options->nservers6++; break; + } + + /* Copy IPv4 servers */ + if (options->nservers) { options->servers = - malloc(channel->nservers * sizeof(struct server_state)); - if (!options->servers && channel->nservers != 0) + malloc(options->nservers * sizeof(struct in_addr)); + if (!options->servers) + return ARES_ENOMEM; + for (i = s = 0; i < channel->nservers; i++) + if (channel->servers[i].address.family != AF_INET) + continue; + options->servers[s++] = channel->servers[i].address.mf_addr.in4; + } + + /* Copy IPv6 servers */ + if (options->nservers6) { + options->servers6 = + malloc(options->nservers6 * sizeof(struct in6_addr)); + if (!options->servers6) return ARES_ENOMEM; - for (i = 0; i < channel->nservers; i++) - options->servers[i] = channel->servers[i].addr; + for (i = s = 0; i < channel->nservers; i++) + if (channel->servers[i].address.family != AF_INET6) + continue; + options->servers6[s++] = channel->servers[i].address.mf_addr.in6; } - options->nservers = channel->nservers; /* copy domains */ if (channel->ndomains) { @@ -373,19 +395,41 @@ static int init_by_options(ares_channel channel->socket_receive_buffer_size = options->socket_receive_buffer_size; /* Copy the servers, if given. */ - if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1) + if ((optmask & (ARES_OPT_SERVERS|ARES_OPT_SERVERS6)) && channel->nservers == -1) { + int n = 0; + if ((optmask & ARES_OPT_SERVERS) && options->nservers > 0) + n += options->nservers; + if ((optmask & ARES_OPT_SERVERS6) && options->nservers6 > 0) + n += options->nservers6; + /* Avoid zero size allocations at any cost */ - if (options->nservers > 0) + if (n > 0) { channel->servers = - malloc(options->nservers * sizeof(struct server_state)); + calloc(n, sizeof(struct server_state)); if (!channel->servers) return ARES_ENOMEM; - for (i = 0; i < options->nservers; i++) - channel->servers[i].addr = options->servers[i]; + + n = 0; + + /* copy IPv4 */ + if ((optmask & ARES_OPT_SERVERS) && options->nservers > 0) + for (i = 0; i < options->nservers; i++, n++) + { + channel->servers[n].address.family = AF_INET; + channel->servers[n].address.mf_addr.in4 = options->servers[i]; + } + + /* copy IPv6 */ + if ((optmask & ARES_OPT_SERVERS6) && options->nservers6 > 0) + for (i = 0; i < options->nservers6; i++, n++) + { + channel->servers[n].address.family = AF_INET6; + channel->servers[n].address.mf_addr.in6 = options->servers6[i]; + } } - channel->nservers = options->nservers; + channel->nservers = n; } /* Copy the domains, if given. Keep channel->ndomains consistent so @@ -950,7 +994,8 @@ static int init_by_defaults(ares_channel rc = ARES_ENOMEM; goto error; } - channel->servers[0].addr.s_addr = htonl(INADDR_LOOPBACK); + channel->servers[0].address.family = AF_INET; + channel->servers[0].address.mf_addr.in4.s_addr = htonl(INADDR_LOOPBACK); channel->nservers = 1; } @@ -1139,7 +1184,8 @@ static int config_nameserver(struct serv newserv = realloc(*servers, (*nservers + 1) * sizeof(struct server_state)); if (!newserv) return ARES_ENOMEM; - newserv[*nservers].addr = addr; + newserv[*nservers].address.family = AF_INET; + newserv[*nservers].address.mf_addr.in4 = addr; *servers = newserv; (*nservers)++; #endif Index: ares_private.h =================================================================== RCS file: /cvsroot/curl/curl/ares/ares_private.h,v retrieving revision 1.39 diff -u -3 -p -r1.39 ares_private.h --- ares_private.h 20 Nov 2008 07:50:48 -0000 1.39 +++ ares_private.h 23 Nov 2008 17:29:36 -0000 @@ -131,8 +131,17 @@ struct send_request { struct send_request *next; }; +struct mf_address { + sa_family_t family; + union + { + struct in_addr in4; + struct in6_addr in6; + } mf_addr; +}; + struct server_state { - struct in_addr addr; + struct mf_address address; ares_socket_t udp_socket; ares_socket_t tcp_socket; Index: ares_process.c =================================================================== RCS file: /cvsroot/curl/curl/ares/ares_process.c,v retrieving revision 1.71 diff -u -3 -p -r1.71 ares_process.c --- ares_process.c 13 Nov 2008 18:56:56 -0000 1.71 +++ ares_process.c 23 Nov 2008 17:29:37 -0000 @@ -97,6 +97,7 @@ static int open_tcp_socket(ares_channel static int open_udp_socket(ares_channel channel, struct server_state *server); static int same_questions(const unsigned char *qbuf, int qlen, const unsigned char *abuf, int alen); +static int same_address(const struct sockaddr_storage *from, const struct mf_address *address); static void end_query(ares_channel channel, struct query *query, int status, unsigned char *abuf, int alen); @@ -428,7 +429,7 @@ static void read_udp_packets(ares_channe ssize_t count; unsigned char buf[PACKETSZ + 1]; #ifdef HAVE_RECVFROM - struct sockaddr_in from; + struct sockaddr_storage from; socklen_t fromlen; #endif @@ -466,6 +467,7 @@ static void read_udp_packets(ares_channe do { #ifdef HAVE_RECVFROM fromlen = sizeof(from); + fprintf(stderr, "Size: %u\n", fromlen); count = (ssize_t)recvfrom(server->udp_socket, (void *)buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen); #else @@ -476,7 +478,7 @@ static void read_udp_packets(ares_channe else if (count <= 0) handle_error(channel, i, now); #ifdef HAVE_RECVFROM - else if (from.sin_addr.s_addr != server->addr.s_addr) + else if (!same_address(&from, &server->address)) /* Address response came from did not match the address * we sent the request to. Someone may be attempting * to perform a cache poisoning attack */ @@ -883,10 +885,33 @@ static int open_tcp_socket(ares_channel { ares_socket_t s; int opt; - struct sockaddr_in sockin; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + const struct sockaddr *sa; + socklen_t sock_len; + + switch (server->address.family) + { + case AF_INET: + memset(&sin, 0, sizeof(sin)); + sin.sin_addr = server->address.mf_addr.in4; + sin.sin_port = (unsigned short)(channel->tcp_port & 0xffff); + sock_len = sizeof(sin); + sa = (const struct sockaddr *)&sin; + break; + case AF_INET6: + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_addr = server->address.mf_addr.in6; + sin6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff); + sock_len = sizeof(sin6); + sa = (const struct sockaddr *)&sin6; + break; + default: + return -1; + } /* Acquire a socket. */ - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(server->address.family, SOCK_STREAM, 0); if (s == ARES_SOCKET_BAD) return -1; @@ -912,11 +937,7 @@ static int open_tcp_socket(ares_channel } /* 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, sa, sock_len) == -1) { int err = SOCKERRNO; @@ -937,10 +958,35 @@ static int open_tcp_socket(ares_channel static int open_udp_socket(ares_channel channel, struct server_state *server) { ares_socket_t s; - struct sockaddr_in sockin; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + const struct sockaddr *sa; + socklen_t sock_len; + + switch (server->address.family) + { + case AF_INET: + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr = server->address.mf_addr.in4; + sin.sin_port = (unsigned short)(channel->tcp_port & 0xffff); + sock_len = sizeof(sin); + sa = (const struct sockaddr *)&sin; + break; + case AF_INET6: + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = server->address.mf_addr.in6; + sin6.sin6_port = (unsigned short)(channel->tcp_port & 0xffff); + sock_len = sizeof(sin6); + sa = (const struct sockaddr *)&sin6; + break; + default: + return -1; + } /* Acquire a socket. */ - s = socket(AF_INET, SOCK_DGRAM, 0); + s = socket(server->address.family, SOCK_DGRAM, 0); if (s == ARES_SOCKET_BAD) return -1; @@ -952,11 +998,7 @@ static int open_udp_socket(ares_channel } /* 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, sa, sock_len) == -1) { int err = SOCKERRNO; @@ -1052,6 +1094,22 @@ static int same_questions(const unsigned return 1; } +static int same_address(const struct sockaddr_storage *from, const struct mf_address *address) +{ + if (from->ss_family == AF_INET && address->family == AF_INET) + { + const struct sockaddr_in *from_in = (const struct sockaddr_in*)from; + return from_in->sin_addr.s_addr == address->mf_addr.in4.s_addr; + } + else if (from->ss_family == AF_INET6 && address->family == AF_INET6) + { + const struct sockaddr_in6 *from_in6 = (const struct sockaddr_in6*)from; + return from_in6->sin6_addr.s6_addr == address->mf_addr.in6.s6_addr; + } + + return 0; +} + static void end_query (ares_channel channel, struct query *query, int status, unsigned char *abuf, int alen) {