diff --git a/CHANGES b/CHANGES index 8b9c8b323..b0063a76b 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,11 @@ Changelog +Daniel Stenberg (9 Jul 2008) +- Andreas Schuldei improved Phil Blundell's patch for IPv6 using c-ares, and I + edited it slightly. Now you should be able to use IPv6 addresses fine even + with libcurl built to use c-ares. + Daniel Fandrich (9 Jul 2008) - Fixed an OOM handling problem that cause test 11 to fail the torture test. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 32119ecba..f375a8848 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -31,6 +31,7 @@ This release includes the following bugfixes: o range numbers could be made to wrongly get output as signed o unexpected 1xx responses hung transfers o FTP transfers segfault when using different CURLOPT_FTP_FILEMETHOD + o c-ares powered libcurls can resolve/use IPv6 addresses This release includes the following known bugs: @@ -50,7 +51,7 @@ advice from friends like these: Lenny Rachitsky, Axel Tillequin, Arnaud Ebalard, Yang Tse, Dan Fandrich, Rob Crittenden, Dengminwen, Christopher Palow, Hans-Jurgen May, Phil Pellouchoud, Eduard Bloch, John Lightsey, Stephen Collyer, Tor Arntsen, - Rolland Dudemaine, Phil Blundell, Scott Barrett + Rolland Dudemaine, Phil Blundell, Scott Barrett, Andreas Schuldei Thanks! (and sorry if I forgot to mention someone) diff --git a/lib/hostares.c b/lib/hostares.c index b6de83027..231235242 100644 --- a/lib/hostares.c +++ b/lib/hostares.c @@ -297,6 +297,70 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, return rc; } +#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ +/* + * Curl_ip2addr6() takes an ipv6 internet address as input parameter + * together with a pointer to the string version of the address, and it + * returns a Curl_addrinfo chain filled in correctly with information for this + * address/host. + * + * The input parameters ARE NOT checked for validity but they are expected + * to have been checked already when this is called. + */ +Curl_addrinfo *Curl_ip2addr6(struct in6_addr *in, + const char *hostname, int port) +{ + Curl_addrinfo *ai; + +#if defined(VMS) && defined(__INITIAL_POINTER_SIZE) && \ + (__INITIAL_POINTER_SIZE == 64) +#pragma pointer_size save +#pragma pointer_size short +#pragma message disable PTRMISMATCH +#endif + + struct hostent *h; + struct in6_addr *addrentry; + struct namebuf6 { + struct hostent hostentry; + char *h_addr_list[2]; + struct in6_addr addrentry; + char hostname[1]; + }; + struct namebuf6 *buf = malloc(sizeof (struct namebuf6) + strlen(hostname)); + + if(!buf) + return NULL; + + h = &buf->hostentry; + h->h_addr_list = &buf->h_addr_list[0]; + addrentry = &buf->addrentry; + memcpy(addrentry, in, sizeof (*in)); + h->h_addr_list[0] = (char*)addrentry; + h->h_addr_list[1] = NULL; /* terminate list of entries */ + h->h_name = &buf->hostname[0]; + h->h_aliases = NULL; + h->h_addrtype = AF_INET6; + + /* Now store the dotted version of the address */ + strcpy (h->h_name, hostname); + +#if defined(VMS) && defined(__INITIAL_POINTER_SIZE) && \ + (__INITIAL_POINTER_SIZE == 64) +#pragma pointer_size restore +#pragma message enable PTRMISMATCH +#endif + + ai = Curl_he2ai(h, port); + + free(buf); + + return ai; +} +#endif /* CURLRES_IPV6 */ + + + /* * Curl_getaddrinfo() - when using ares * @@ -313,7 +377,10 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, char *bufp; struct SessionHandle *data = conn->data; in_addr_t in = inet_addr(hostname); - + int family = PF_INET; +#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ + struct in6_addr in6; +#endif /* CURLRES_IPV6 */ *waitp = FALSE; if(in != CURL_INADDR_NONE) { @@ -321,6 +388,23 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, return Curl_ip2addr(in, hostname, port); } +#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ + if (inet_pton (AF_INET6, hostname, &in6) > 0) { + /* This must be an IPv6 address literal. */ + return Curl_ip2addr6(&in6, hostname, port); + } + + switch(data->set.ip_version) { + case CURL_IPRESOLVE_V4: + family = PF_INET; + break; + default: /* by default we try ipv6, as PF_UNSPEC isn't supported by (c-)ares */ + case CURL_IPRESOLVE_V6: + family = PF_INET6; + break; + } +#endif /* CURLRES_IPV6 */ + bufp = strdup(hostname); if(bufp) { @@ -332,7 +416,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, conn->async.dns = NULL; /* clear */ /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname(data->state.areschannel, hostname, PF_INET, + ares_gethostbyname(data->state.areschannel, hostname, family, (ares_host_callback)Curl_addrinfo4_callback, conn); *waitp = TRUE; /* please wait for the response */ diff --git a/lib/hostip.c b/lib/hostip.c index 82ea6a6ab..4e0df2546 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -545,7 +545,8 @@ Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port) #endif /* CURLRES_ADDRINFO_COPY */ /*********************************************************************** - * Only for plain-ipv4 and c-ares builds + * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6 + * enabled) **********************************************************************/ #if defined(CURLRES_IPV4) || defined(CURLRES_ARES) @@ -630,6 +631,118 @@ Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port) return ai; } + +/* + * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct. + * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6 + * stacks, but for all hosts and environments. + * + * Curl_addrinfo defined in "lib/hostip.h" + * + * struct Curl_addrinfo { + * int ai_flags; + * int ai_family; + * int ai_socktype; + * int ai_protocol; + * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo * + * char *ai_canonname; + * struct sockaddr *ai_addr; + * struct Curl_addrinfo *ai_next; + * }; + * + * hostent defined in + * + * struct hostent { + * char *h_name; + * char **h_aliases; + * int h_addrtype; + * int h_length; + * char **h_addr_list; + * }; + * + * for backward compatibility: + * + * #define h_addr h_addr_list[0] + */ + +Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) +{ + Curl_addrinfo *ai; + Curl_addrinfo *prevai = NULL; + Curl_addrinfo *firstai = NULL; + struct sockaddr_in *addr; +#ifdef CURLRES_IPV6 + struct sockaddr_in6 *addr6; +#endif /* CURLRES_IPV6 */ + int i; + struct in_addr *curr; + + if(!he) + /* no input == no output! */ + return NULL; + + for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) { + + int ss_size; +#ifdef CURLRES_IPV6 + if (he->h_addrtype == AF_INET6) + ss_size = sizeof (struct sockaddr_in6); + else +#endif /* CURLRES_IPV6 */ + ss_size = sizeof (struct sockaddr_in); + + ai = calloc(1, sizeof(Curl_addrinfo) + ss_size); + + if(!ai) + break; + + if(!firstai) + /* store the pointer we want to return from this function */ + firstai = ai; + + if(prevai) + /* make the previous entry point to this */ + prevai->ai_next = ai; + + ai->ai_family = he->h_addrtype; + + /* we return all names as STREAM, so when using this address for TFTP + the type must be ignored and conn->socktype be used instead! */ + ai->ai_socktype = SOCK_STREAM; + + ai->ai_addrlen = ss_size; + /* make the ai_addr point to the address immediately following this struct + and use that area to store the address */ + ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo)); + + /* need to free this eventually */ + ai->ai_canonname = strdup(he->h_name); + + /* leave the rest of the struct filled with zero */ + + switch (ai->ai_family) { + case AF_INET: + addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */ + + memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr)); + addr->sin_family = (unsigned short)(he->h_addrtype); + addr->sin_port = htons((unsigned short)port); + break; + +#ifdef CURLRES_IPV6 + case AF_INET6: + addr6 = (struct sockaddr_in6 *)ai->ai_addr; /* storage area for this info */ + + memcpy((char *)&(addr6->sin6_addr), curr, sizeof(struct in6_addr)); + addr6->sin6_family = (unsigned short)(he->h_addrtype); + addr6->sin6_port = htons((unsigned short)port); + break; +#endif /* CURLRES_IPV6 */ + } + + prevai = ai; + } + return firstai; +} + #endif /* CURLRES_IPV4 || CURLRES_ARES */ - - diff --git a/lib/hostip.h b/lib/hostip.h index 66d1771a3..d37203cc5 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -104,6 +104,10 @@ */ #ifdef CURLRES_IPV6 typedef struct addrinfo Curl_addrinfo; +#ifdef CURLRES_ARES +Curl_addrinfo *Curl_ip2addr6(struct in6_addr *in, + const char *hostname, int port); +#endif #else /* OK, so some ipv4-only include tree probably have the addrinfo struct, but to work even on those that don't, we provide our own look-alike! */ diff --git a/lib/hostip4.c b/lib/hostip4.c index 4016227de..b3c358838 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -307,91 +307,3 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, #endif /* CURLRES_SYNCH */ #endif /* CURLRES_IPV4 */ -/* - * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct. - * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6 - * stacks, but for all hosts and environments. - * - * Curl_addrinfo defined in "lib/hostip.h" - * - * struct Curl_addrinfo { - * int ai_flags; - * int ai_family; - * int ai_socktype; - * int ai_protocol; - * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo * - * char *ai_canonname; - * struct sockaddr *ai_addr; - * struct Curl_addrinfo *ai_next; - * }; - * - * hostent defined in - * - * struct hostent { - * char *h_name; - * char **h_aliases; - * int h_addrtype; - * int h_length; - * char **h_addr_list; - * }; - * - * for backward compatibility: - * - * #define h_addr h_addr_list[0] - */ - -Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) -{ - Curl_addrinfo *ai; - Curl_addrinfo *prevai = NULL; - Curl_addrinfo *firstai = NULL; - struct sockaddr_in *addr; - int i; - struct in_addr *curr; - - if(!he) - /* no input == no output! */ - return NULL; - - for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) { - - ai = calloc(1, sizeof(Curl_addrinfo) + sizeof(struct sockaddr_in)); - - if(!ai) - break; - - if(!firstai) - /* store the pointer we want to return from this function */ - firstai = ai; - - if(prevai) - /* make the previous entry point to this */ - prevai->ai_next = ai; - - ai->ai_family = AF_INET; /* we only support this */ - - /* we return all names as STREAM, so when using this address for TFTP - the type must be ignored and conn->socktype be used instead! */ - ai->ai_socktype = SOCK_STREAM; - - ai->ai_addrlen = sizeof(struct sockaddr_in); - /* make the ai_addr point to the address immediately following this struct - and use that area to store the address */ - ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo)); - - /* FIXME: need to free this eventually */ - ai->ai_canonname = strdup(he->h_name); - - /* leave the rest of the struct filled with zero */ - - addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */ - - memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr)); - addr->sin_family = (unsigned short)(he->h_addrtype); - addr->sin_port = htons((unsigned short)port); - - prevai = ai; - } - return firstai; -} -