|  | /* The Inner Net License, Version 2.00 | 
|  |  | 
|  | The author(s) grant permission for redistribution and use in source and | 
|  | binary forms, with or without modification, of the software and documentation | 
|  | provided that the following conditions are met: | 
|  |  | 
|  | 0. If you receive a version of the software that is specifically labelled | 
|  | as not being for redistribution (check the version message and/or README), | 
|  | you are not permitted to redistribute that version of the software in any | 
|  | way or form. | 
|  | 1. All terms of the all other applicable copyrights and licenses must be | 
|  | followed. | 
|  | 2. Redistributions of source code must retain the authors' copyright | 
|  | notice(s), this list of conditions, and the following disclaimer. | 
|  | 3. Redistributions in binary form must reproduce the authors' copyright | 
|  | notice(s), this list of conditions, and the following disclaimer in the | 
|  | documentation and/or other materials provided with the distribution. | 
|  | 4. [The copyright holder has authorized the removal of this clause.] | 
|  | 5. Neither the name(s) of the author(s) nor the names of its contributors | 
|  | may be used to endorse or promote products derived from this software | 
|  | without specific prior written permission. | 
|  |  | 
|  | THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY | 
|  | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
|  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY | 
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
|  | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
|  | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | 
|  | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
|  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | If these license terms cause you a real problem, contact the author.  */ | 
|  |  | 
|  | /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <ctype.h> | 
|  | #include <errno.h> | 
|  | #include <ifaddrs.h> | 
|  | #include <netdb.h> | 
|  | #include <nss.h> | 
|  | #include <resolv.h> | 
|  | #include <stdbool.h> | 
|  | #include <stdio.h> | 
|  | #include <stdio_ext.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <stdint.h> | 
|  | #include <arpa/inet.h> | 
|  | #include <net/if.h> | 
|  | #include <netinet/in.h> | 
|  | #include <sys/socket.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/un.h> | 
|  | #include <sys/utsname.h> | 
|  | #include <unistd.h> | 
|  | #include <nsswitch.h> | 
|  | #include <libc-lock.h> | 
|  | #include <not-cancel.h> | 
|  | #include <nscd/nscd-client.h> | 
|  | #include <nscd/nscd_proto.h> | 
|  | #include <resolv/res_hconf.h> | 
|  |  | 
|  | #ifdef HAVE_LIBIDN | 
|  | extern int __idna_to_ascii_lz (const char *input, char **output, int flags); | 
|  | extern int __idna_to_unicode_lzlz (const char *input, char **output, | 
|  | int flags); | 
|  | # include <libidn/idna.h> | 
|  | #endif | 
|  |  | 
|  | struct gaih_service | 
|  | { | 
|  | const char *name; | 
|  | int num; | 
|  | }; | 
|  |  | 
|  | struct gaih_servtuple | 
|  | { | 
|  | struct gaih_servtuple *next; | 
|  | int socktype; | 
|  | int protocol; | 
|  | int port; | 
|  | }; | 
|  |  | 
|  | static const struct gaih_servtuple nullserv; | 
|  |  | 
|  |  | 
|  | struct gaih_typeproto | 
|  | { | 
|  | int socktype; | 
|  | int protocol; | 
|  | uint8_t protoflag; | 
|  | bool defaultflag; | 
|  | char name[8]; | 
|  | }; | 
|  |  | 
|  | /* Values for `protoflag'.  */ | 
|  | #define GAI_PROTO_NOSERVICE	1 | 
|  | #define GAI_PROTO_PROTOANY	2 | 
|  |  | 
|  | static const struct gaih_typeproto gaih_inet_typeproto[] = | 
|  | { | 
|  | { 0, 0, 0, false, "" }, | 
|  | { SOCK_STREAM, IPPROTO_TCP, 0, true, "tcp" }, | 
|  | { SOCK_DGRAM, IPPROTO_UDP, 0, true, "udp" }, | 
|  | #if defined SOCK_DCCP && defined IPPROTO_DCCP | 
|  | { SOCK_DCCP, IPPROTO_DCCP, 0, false, "dccp" }, | 
|  | #endif | 
|  | #ifdef IPPROTO_UDPLITE | 
|  | { SOCK_DGRAM, IPPROTO_UDPLITE, 0, false, "udplite" }, | 
|  | #endif | 
|  | #ifdef IPPROTO_SCTP | 
|  | { SOCK_STREAM, IPPROTO_SCTP, 0, false, "sctp" }, | 
|  | { SOCK_SEQPACKET, IPPROTO_SCTP, 0, false, "sctp" }, | 
|  | #endif | 
|  | { SOCK_RAW, 0, GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, true, "raw" }, | 
|  | { 0, 0, 0, false, "" } | 
|  | }; | 
|  |  | 
|  | static const struct addrinfo default_hints = | 
|  | { | 
|  | .ai_flags = AI_DEFAULT, | 
|  | .ai_family = PF_UNSPEC, | 
|  | .ai_socktype = 0, | 
|  | .ai_protocol = 0, | 
|  | .ai_addrlen = 0, | 
|  | .ai_addr = NULL, | 
|  | .ai_canonname = NULL, | 
|  | .ai_next = NULL | 
|  | }; | 
|  |  | 
|  |  | 
|  | static int | 
|  | gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, | 
|  | const struct addrinfo *req, struct gaih_servtuple *st) | 
|  | { | 
|  | struct servent *s; | 
|  | size_t tmpbuflen = 1024; | 
|  | struct servent ts; | 
|  | char *tmpbuf; | 
|  | int r; | 
|  |  | 
|  | do | 
|  | { | 
|  | tmpbuf = __alloca (tmpbuflen); | 
|  |  | 
|  | r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen, | 
|  | &s); | 
|  | if (r != 0 || s == NULL) | 
|  | { | 
|  | if (r == ERANGE) | 
|  | tmpbuflen *= 2; | 
|  | else | 
|  | return -EAI_SERVICE; | 
|  | } | 
|  | } | 
|  | while (r); | 
|  |  | 
|  | st->next = NULL; | 
|  | st->socktype = tp->socktype; | 
|  | st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) | 
|  | ? req->ai_protocol : tp->protocol); | 
|  | st->port = s->s_port; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define gethosts(_family, _type) \ | 
|  | {									      \ | 
|  | int i;								      \ | 
|  | int herrno;								      \ | 
|  | struct hostent th;							      \ | 
|  | struct hostent *h;							      \ | 
|  | char *localcanon = NULL;						      \ | 
|  | no_data = 0;								      \ | 
|  | while (1) {								      \ | 
|  | rc = 0;								      \ | 
|  | status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, tmpbuflen,	      \ | 
|  | &rc, &herrno, NULL, &localcanon));	      \ | 
|  | if (rc != ERANGE || herrno != NETDB_INTERNAL)			      \ | 
|  | break;								      \ | 
|  | if (!malloc_tmpbuf && __libc_use_alloca (alloca_used + 2 * tmpbuflen))    \ | 
|  | tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, 2 * tmpbuflen,	      \ | 
|  | alloca_used);			      \ | 
|  | else								      \ | 
|  | {									      \ | 
|  | char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL,		      \ | 
|  | 2 * tmpbuflen);				      \ | 
|  | if (newp == NULL)						      \ | 
|  | {								      \ | 
|  | result = -EAI_MEMORY;					      \ | 
|  | goto free_and_return;					      \ | 
|  | }								      \ | 
|  | tmpbuf = newp;							      \ | 
|  | malloc_tmpbuf = true;						      \ | 
|  | tmpbuflen = 2 * tmpbuflen;					      \ | 
|  | }									      \ | 
|  | }									      \ | 
|  | if (status == NSS_STATUS_SUCCESS && rc == 0)				      \ | 
|  | h = &th;								      \ | 
|  | else									      \ | 
|  | h = NULL;								      \ | 
|  | if (rc != 0)								      \ | 
|  | {									      \ | 
|  | if (herrno == NETDB_INTERNAL)					      \ | 
|  | {								      \ | 
|  | __set_h_errno (herrno);					      \ | 
|  | _res.options |= old_res_options & RES_USE_INET6;		      \ | 
|  | result = -EAI_SYSTEM;						      \ | 
|  | goto free_and_return;						      \ | 
|  | }								      \ | 
|  | if (herrno == TRY_AGAIN)						      \ | 
|  | no_data = EAI_AGAIN;						      \ | 
|  | else								      \ | 
|  | no_data = herrno == NO_DATA;					      \ | 
|  | }									      \ | 
|  | else if (h != NULL)							      \ | 
|  | {									      \ | 
|  | for (i = 0; h->h_addr_list[i]; i++)				      \ | 
|  | {								      \ | 
|  | if (*pat == NULL)						      \ | 
|  | {								      \ | 
|  | *pat = __alloca (sizeof (struct gaih_addrtuple));		      \ | 
|  | (*pat)->scopeid = 0;					      \ | 
|  | }								      \ | 
|  | uint32_t *addr = (*pat)->addr;				      \ | 
|  | (*pat)->next = NULL;						      \ | 
|  | (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL;		      \ | 
|  | if (_family == AF_INET && req->ai_family == AF_INET6)		      \ | 
|  | {								      \ | 
|  | (*pat)->family = AF_INET6;				      \ | 
|  | addr[3] = *(uint32_t *) h->h_addr_list[i];		      \ | 
|  | addr[2] = htonl (0xffff);					      \ | 
|  | addr[1] = 0;						      \ | 
|  | addr[0] = 0;						      \ | 
|  | }								      \ | 
|  | else								      \ | 
|  | {								      \ | 
|  | (*pat)->family = _family;					      \ | 
|  | memcpy (addr, h->h_addr_list[i], sizeof(_type));		      \ | 
|  | }								      \ | 
|  | pat = &((*pat)->next);					      \ | 
|  | }								      \ | 
|  | \ | 
|  | if (localcanon !=	NULL && canon == NULL)				      \ | 
|  | canon = strdupa (localcanon);					      \ | 
|  | \ | 
|  | if (_family == AF_INET6 && i > 0)					      \ | 
|  | got_ipv6 = true;						      \ | 
|  | }									      \ | 
|  | } | 
|  |  | 
|  |  | 
|  | typedef enum nss_status (*nss_gethostbyname4_r) | 
|  | (const char *name, struct gaih_addrtuple **pat, | 
|  | char *buffer, size_t buflen, int *errnop, | 
|  | int *h_errnop, int32_t *ttlp); | 
|  | typedef enum nss_status (*nss_gethostbyname3_r) | 
|  | (const char *name, int af, struct hostent *host, | 
|  | char *buffer, size_t buflen, int *errnop, | 
|  | int *h_errnop, int32_t *ttlp, char **canonp); | 
|  | typedef enum nss_status (*nss_getcanonname_r) | 
|  | (const char *name, char *buffer, size_t buflen, char **result, | 
|  | int *errnop, int *h_errnop); | 
|  | extern service_user *__nss_hosts_database attribute_hidden; | 
|  |  | 
|  |  | 
|  | static int | 
|  | gaih_inet (const char *name, const struct gaih_service *service, | 
|  | const struct addrinfo *req, struct addrinfo **pai, | 
|  | unsigned int *naddrs) | 
|  | { | 
|  | const struct gaih_typeproto *tp = gaih_inet_typeproto; | 
|  | struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; | 
|  | struct gaih_addrtuple *at = NULL; | 
|  | int rc; | 
|  | bool got_ipv6 = false; | 
|  | const char *canon = NULL; | 
|  | const char *orig_name = name; | 
|  | size_t alloca_used = 0; | 
|  |  | 
|  | if (req->ai_protocol || req->ai_socktype) | 
|  | { | 
|  | ++tp; | 
|  |  | 
|  | while (tp->name[0] | 
|  | && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype) | 
|  | || (req->ai_protocol != 0 | 
|  | && !(tp->protoflag & GAI_PROTO_PROTOANY) | 
|  | && req->ai_protocol != tp->protocol))) | 
|  | ++tp; | 
|  |  | 
|  | if (! tp->name[0]) | 
|  | { | 
|  | if (req->ai_socktype) | 
|  | return -EAI_SOCKTYPE; | 
|  | else | 
|  | return -EAI_SERVICE; | 
|  | } | 
|  | } | 
|  |  | 
|  | int port = 0; | 
|  | if (service != NULL) | 
|  | { | 
|  | if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) | 
|  | return -EAI_SERVICE; | 
|  |  | 
|  | if (service->num < 0) | 
|  | { | 
|  | if (tp->name[0]) | 
|  | { | 
|  | st = (struct gaih_servtuple *) | 
|  | alloca_account (sizeof (struct gaih_servtuple), alloca_used); | 
|  |  | 
|  | if ((rc = gaih_inet_serv (service->name, tp, req, st))) | 
|  | return rc; | 
|  | } | 
|  | else | 
|  | { | 
|  | struct gaih_servtuple **pst = &st; | 
|  | for (tp++; tp->name[0]; tp++) | 
|  | { | 
|  | struct gaih_servtuple *newp; | 
|  |  | 
|  | if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) | 
|  | continue; | 
|  |  | 
|  | if (req->ai_socktype != 0 | 
|  | && req->ai_socktype != tp->socktype) | 
|  | continue; | 
|  | if (req->ai_protocol != 0 | 
|  | && !(tp->protoflag & GAI_PROTO_PROTOANY) | 
|  | && req->ai_protocol != tp->protocol) | 
|  | continue; | 
|  |  | 
|  | newp = (struct gaih_servtuple *) | 
|  | alloca_account (sizeof (struct gaih_servtuple), | 
|  | alloca_used); | 
|  |  | 
|  | if ((rc = gaih_inet_serv (service->name, tp, req, newp))) | 
|  | { | 
|  | if (rc) | 
|  | continue; | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | *pst = newp; | 
|  | pst = &(newp->next); | 
|  | } | 
|  | if (st == (struct gaih_servtuple *) &nullserv) | 
|  | return -EAI_SERVICE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | port = htons (service->num); | 
|  | goto got_port; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | got_port: | 
|  |  | 
|  | if (req->ai_socktype || req->ai_protocol) | 
|  | { | 
|  | st = alloca_account (sizeof (struct gaih_servtuple), alloca_used); | 
|  | st->next = NULL; | 
|  | st->socktype = tp->socktype; | 
|  | st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) | 
|  | ? req->ai_protocol : tp->protocol); | 
|  | st->port = port; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Neither socket type nor protocol is set.  Return all socket types | 
|  | we know about.  */ | 
|  | struct gaih_servtuple **lastp = &st; | 
|  | for (++tp; tp->name[0]; ++tp) | 
|  | if (tp->defaultflag) | 
|  | { | 
|  | struct gaih_servtuple *newp; | 
|  |  | 
|  | newp = alloca_account (sizeof (struct gaih_servtuple), | 
|  | alloca_used); | 
|  | newp->next = NULL; | 
|  | newp->socktype = tp->socktype; | 
|  | newp->protocol = tp->protocol; | 
|  | newp->port = port; | 
|  |  | 
|  | *lastp = newp; | 
|  | lastp = &newp->next; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool malloc_name = false; | 
|  | bool malloc_addrmem = false; | 
|  | struct gaih_addrtuple *addrmem = NULL; | 
|  | bool malloc_canonbuf = false; | 
|  | char *canonbuf = NULL; | 
|  | bool malloc_tmpbuf = false; | 
|  | char *tmpbuf = NULL; | 
|  | int result = 0; | 
|  | if (name != NULL) | 
|  | { | 
|  | at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); | 
|  | at->family = AF_UNSPEC; | 
|  | at->scopeid = 0; | 
|  | at->next = NULL; | 
|  |  | 
|  | #ifdef HAVE_LIBIDN | 
|  | if (req->ai_flags & AI_IDN) | 
|  | { | 
|  | int idn_flags = 0; | 
|  | if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED) | 
|  | idn_flags |= IDNA_ALLOW_UNASSIGNED; | 
|  | if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES) | 
|  | idn_flags |= IDNA_USE_STD3_ASCII_RULES; | 
|  |  | 
|  | char *p = NULL; | 
|  | rc = __idna_to_ascii_lz (name, &p, idn_flags); | 
|  | if (rc != IDNA_SUCCESS) | 
|  | { | 
|  | /* No need to jump to free_and_return here.  */ | 
|  | if (rc == IDNA_MALLOC_ERROR) | 
|  | return -EAI_MEMORY; | 
|  | if (rc == IDNA_DLOPEN_ERROR) | 
|  | return -EAI_SYSTEM; | 
|  | return -EAI_IDN_ENCODE; | 
|  | } | 
|  | /* In case the output string is the same as the input string | 
|  | no new string has been allocated.  */ | 
|  | if (p != name) | 
|  | { | 
|  | name = p; | 
|  | malloc_name = true; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (__inet_aton (name, (struct in_addr *) at->addr) != 0) | 
|  | { | 
|  | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) | 
|  | at->family = AF_INET; | 
|  | else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) | 
|  | { | 
|  | at->addr[3] = at->addr[0]; | 
|  | at->addr[2] = htonl (0xffff); | 
|  | at->addr[1] = 0; | 
|  | at->addr[0] = 0; | 
|  | at->family = AF_INET6; | 
|  | } | 
|  | else | 
|  | { | 
|  | result = -EAI_ADDRFAMILY; | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | if (req->ai_flags & AI_CANONNAME) | 
|  | canon = name; | 
|  | } | 
|  | else if (at->family == AF_UNSPEC) | 
|  | { | 
|  | char *scope_delim = strchr (name, SCOPE_DELIMITER); | 
|  | int e; | 
|  |  | 
|  | { | 
|  | bool malloc_namebuf = false; | 
|  | char *namebuf = (char *) name; | 
|  |  | 
|  | if (__glibc_unlikely (scope_delim != NULL)) | 
|  | { | 
|  | if (malloc_name) | 
|  | *scope_delim = '\0'; | 
|  | else | 
|  | { | 
|  | if (__libc_use_alloca (alloca_used | 
|  | + scope_delim - name + 1)) | 
|  | { | 
|  | namebuf = alloca_account (scope_delim - name + 1, | 
|  | alloca_used); | 
|  | *((char *) __mempcpy (namebuf, name, | 
|  | scope_delim - name)) = '\0'; | 
|  | } | 
|  | else | 
|  | { | 
|  | namebuf = strndup (name, scope_delim - name); | 
|  | if (namebuf == NULL) | 
|  | { | 
|  | assert (!malloc_name); | 
|  | return -EAI_MEMORY; | 
|  | } | 
|  | malloc_namebuf = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | e = inet_pton (AF_INET6, namebuf, at->addr); | 
|  |  | 
|  | if (malloc_namebuf) | 
|  | free (namebuf); | 
|  | else if (scope_delim != NULL && malloc_name) | 
|  | /* Undo what we did above.  */ | 
|  | *scope_delim = SCOPE_DELIMITER; | 
|  | } | 
|  | if (e > 0) | 
|  | { | 
|  | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) | 
|  | at->family = AF_INET6; | 
|  | else if (req->ai_family == AF_INET | 
|  | && IN6_IS_ADDR_V4MAPPED (at->addr)) | 
|  | { | 
|  | at->addr[0] = at->addr[3]; | 
|  | at->family = AF_INET; | 
|  | } | 
|  | else | 
|  | { | 
|  | result = -EAI_ADDRFAMILY; | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | if (scope_delim != NULL) | 
|  | { | 
|  | int try_numericscope = 0; | 
|  | if (IN6_IS_ADDR_LINKLOCAL (at->addr) | 
|  | || IN6_IS_ADDR_MC_LINKLOCAL (at->addr)) | 
|  | { | 
|  | at->scopeid = if_nametoindex (scope_delim + 1); | 
|  | if (at->scopeid == 0) | 
|  | try_numericscope = 1; | 
|  | } | 
|  | else | 
|  | try_numericscope = 1; | 
|  |  | 
|  | if (try_numericscope != 0) | 
|  | { | 
|  | char *end; | 
|  | assert (sizeof (uint32_t) <= sizeof (unsigned long)); | 
|  | at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end, | 
|  | 10); | 
|  | if (*end != '\0') | 
|  | { | 
|  | result = -EAI_NONAME; | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (req->ai_flags & AI_CANONNAME) | 
|  | canon = name; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0) | 
|  | { | 
|  | struct gaih_addrtuple **pat = &at; | 
|  | int no_data = 0; | 
|  | int no_inet6_data = 0; | 
|  | service_user *nip; | 
|  | enum nss_status inet6_status = NSS_STATUS_UNAVAIL; | 
|  | enum nss_status status = NSS_STATUS_UNAVAIL; | 
|  | int no_more; | 
|  | int old_res_options; | 
|  |  | 
|  | /* If we do not have to look for IPv6 addresses or the canonical | 
|  | name, use the simple, old functions, which do not support | 
|  | IPv6 scope ids, nor retrieving the canonical name.  */ | 
|  | if (req->ai_family == AF_INET | 
|  | && (req->ai_flags & AI_CANONNAME) == 0) | 
|  | { | 
|  | /* Allocate additional room for struct host_data.  */ | 
|  | size_t tmpbuflen = (512 + MAX_NR_ALIASES * sizeof(char*) | 
|  | + 16 * sizeof(char)); | 
|  | assert (tmpbuf == NULL); | 
|  | tmpbuf = alloca_account (tmpbuflen, alloca_used); | 
|  | int rc; | 
|  | struct hostent th; | 
|  | struct hostent *h; | 
|  | int herrno; | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf, | 
|  | tmpbuflen, &h, &herrno); | 
|  | if (rc != ERANGE || herrno != NETDB_INTERNAL) | 
|  | break; | 
|  |  | 
|  | if (!malloc_tmpbuf | 
|  | && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) | 
|  | tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, | 
|  | 2 * tmpbuflen, | 
|  | alloca_used); | 
|  | else | 
|  | { | 
|  | char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, | 
|  | 2 * tmpbuflen); | 
|  | if (newp == NULL) | 
|  | { | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | tmpbuf = newp; | 
|  | malloc_tmpbuf = true; | 
|  | tmpbuflen = 2 * tmpbuflen; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (rc == 0) | 
|  | { | 
|  | if (h != NULL) | 
|  | { | 
|  | int i; | 
|  | /* We found data, count the number of addresses.  */ | 
|  | for (i = 0; h->h_addr_list[i]; ++i) | 
|  | ; | 
|  | if (i > 0 && *pat != NULL) | 
|  | --i; | 
|  |  | 
|  | if (__libc_use_alloca (alloca_used | 
|  | + i * sizeof (struct gaih_addrtuple))) | 
|  | addrmem = alloca_account (i * sizeof (struct gaih_addrtuple), | 
|  | alloca_used); | 
|  | else | 
|  | { | 
|  | addrmem = malloc (i | 
|  | * sizeof (struct gaih_addrtuple)); | 
|  | if (addrmem == NULL) | 
|  | { | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | malloc_addrmem = true; | 
|  | } | 
|  |  | 
|  | /* Now convert it into the list.  */ | 
|  | struct gaih_addrtuple *addrfree = addrmem; | 
|  | for (i = 0; h->h_addr_list[i]; ++i) | 
|  | { | 
|  | if (*pat == NULL) | 
|  | { | 
|  | *pat = addrfree++; | 
|  | (*pat)->scopeid = 0; | 
|  | } | 
|  | (*pat)->next = NULL; | 
|  | (*pat)->family = AF_INET; | 
|  | memcpy ((*pat)->addr, h->h_addr_list[i], | 
|  | h->h_length); | 
|  | pat = &((*pat)->next); | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (herrno == NETDB_INTERNAL) | 
|  | { | 
|  | __set_h_errno (herrno); | 
|  | result = -EAI_SYSTEM; | 
|  | } | 
|  | else if (herrno == TRY_AGAIN) | 
|  | result = -EAI_AGAIN; | 
|  | else | 
|  | /* We made requests but they turned out no data. | 
|  | The name is known, though.  */ | 
|  | result = -EAI_NODATA; | 
|  |  | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | goto process_list; | 
|  | } | 
|  |  | 
|  | #ifdef USE_NSCD | 
|  | if (__nss_not_use_nscd_hosts > 0 | 
|  | && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) | 
|  | __nss_not_use_nscd_hosts = 0; | 
|  |  | 
|  | if (!__nss_not_use_nscd_hosts | 
|  | && !__nss_database_custom[NSS_DBSIDX_hosts]) | 
|  | { | 
|  | /* Try to use nscd.  */ | 
|  | struct nscd_ai_result *air = NULL; | 
|  | int herrno; | 
|  | int err = __nscd_getai (name, &air, &herrno); | 
|  | if (air != NULL) | 
|  | { | 
|  | /* Transform into gaih_addrtuple list.  */ | 
|  | bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; | 
|  | char *addrs = air->addrs; | 
|  |  | 
|  | if (__libc_use_alloca (alloca_used | 
|  | + air->naddrs * sizeof (struct gaih_addrtuple))) | 
|  | addrmem = alloca_account (air->naddrs | 
|  | * sizeof (struct gaih_addrtuple), | 
|  | alloca_used); | 
|  | else | 
|  | { | 
|  | addrmem = malloc (air->naddrs | 
|  | * sizeof (struct gaih_addrtuple)); | 
|  | if (addrmem == NULL) | 
|  | { | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | malloc_addrmem = true; | 
|  | } | 
|  |  | 
|  | struct gaih_addrtuple *addrfree = addrmem; | 
|  | for (int i = 0; i < air->naddrs; ++i) | 
|  | { | 
|  | socklen_t size = (air->family[i] == AF_INET | 
|  | ? INADDRSZ : IN6ADDRSZ); | 
|  |  | 
|  | if (!((air->family[i] == AF_INET | 
|  | && req->ai_family == AF_INET6 | 
|  | && (req->ai_flags & AI_V4MAPPED) != 0) | 
|  | || req->ai_family == AF_UNSPEC | 
|  | || air->family[i] == req->ai_family)) | 
|  | { | 
|  | /* Skip over non-matching result.  */ | 
|  | addrs += size; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (*pat == NULL) | 
|  | { | 
|  | *pat = addrfree++; | 
|  | (*pat)->scopeid = 0; | 
|  | } | 
|  | uint32_t *pataddr = (*pat)->addr; | 
|  | (*pat)->next = NULL; | 
|  | if (added_canon || air->canon == NULL) | 
|  | (*pat)->name = NULL; | 
|  | else if (canonbuf == NULL) | 
|  | { | 
|  | size_t canonlen = strlen (air->canon) + 1; | 
|  | if ((req->ai_flags & AI_CANONIDN) != 0 | 
|  | && __libc_use_alloca (alloca_used + canonlen)) | 
|  | canonbuf = alloca_account (canonlen, alloca_used); | 
|  | else | 
|  | { | 
|  | canonbuf = malloc (canonlen); | 
|  | if (canonbuf == NULL) | 
|  | { | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | malloc_canonbuf = true; | 
|  | } | 
|  | canon = (*pat)->name = memcpy (canonbuf, air->canon, | 
|  | canonlen); | 
|  | } | 
|  |  | 
|  | if (air->family[i] == AF_INET | 
|  | && req->ai_family == AF_INET6 | 
|  | && (req->ai_flags & AI_V4MAPPED)) | 
|  | { | 
|  | (*pat)->family = AF_INET6; | 
|  | pataddr[3] = *(uint32_t *) addrs; | 
|  | pataddr[2] = htonl (0xffff); | 
|  | pataddr[1] = 0; | 
|  | pataddr[0] = 0; | 
|  | pat = &((*pat)->next); | 
|  | added_canon = true; | 
|  | } | 
|  | else if (req->ai_family == AF_UNSPEC | 
|  | || air->family[i] == req->ai_family) | 
|  | { | 
|  | (*pat)->family = air->family[i]; | 
|  | memcpy (pataddr, addrs, size); | 
|  | pat = &((*pat)->next); | 
|  | added_canon = true; | 
|  | if (air->family[i] == AF_INET6) | 
|  | got_ipv6 = true; | 
|  | } | 
|  | addrs += size; | 
|  | } | 
|  |  | 
|  | free (air); | 
|  |  | 
|  | if (at->family == AF_UNSPEC) | 
|  | { | 
|  | result = -EAI_NONAME; | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | goto process_list; | 
|  | } | 
|  | else if (err == 0) | 
|  | /* The database contains a negative entry.  */ | 
|  | goto free_and_return; | 
|  | else if (__nss_not_use_nscd_hosts == 0) | 
|  | { | 
|  | if (herrno == NETDB_INTERNAL && errno == ENOMEM) | 
|  | result = -EAI_MEMORY; | 
|  | else if (herrno == TRY_AGAIN) | 
|  | result = -EAI_AGAIN; | 
|  | else | 
|  | result = -EAI_SYSTEM; | 
|  |  | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (__nss_hosts_database == NULL) | 
|  | no_more = __nss_database_lookup ("hosts", NULL, | 
|  | "dns [!UNAVAIL=return] files", | 
|  | &__nss_hosts_database); | 
|  | else | 
|  | no_more = 0; | 
|  | nip = __nss_hosts_database; | 
|  |  | 
|  | /* Initialize configurations.  */ | 
|  | if (__glibc_unlikely (!_res_hconf.initialized)) | 
|  | _res_hconf_init (); | 
|  | if (__res_maybe_init (&_res, 0) == -1) | 
|  | no_more = 1; | 
|  |  | 
|  | /* If we are looking for both IPv4 and IPv6 address we don't | 
|  | want the lookup functions to automatically promote IPv4 | 
|  | addresses to IPv6 addresses.  Currently this is decided | 
|  | by setting the RES_USE_INET6 bit in _res.options.  */ | 
|  | old_res_options = _res.options; | 
|  | _res.options &= ~RES_USE_INET6; | 
|  |  | 
|  | size_t tmpbuflen = 1024 + sizeof(struct gaih_addrtuple); | 
|  | malloc_tmpbuf = !__libc_use_alloca (alloca_used + tmpbuflen); | 
|  | assert (tmpbuf == NULL); | 
|  | if (!malloc_tmpbuf) | 
|  | tmpbuf = alloca_account (tmpbuflen, alloca_used); | 
|  | else | 
|  | { | 
|  | tmpbuf = malloc (tmpbuflen); | 
|  | if (tmpbuf == NULL) | 
|  | { | 
|  | _res.options |= old_res_options & RES_USE_INET6; | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  |  | 
|  | while (!no_more) | 
|  | { | 
|  | no_data = 0; | 
|  | nss_gethostbyname4_r fct4 = NULL; | 
|  |  | 
|  | /* gethostbyname4_r sends out parallel A and AAAA queries and | 
|  | is thus only suitable for PF_UNSPEC.  */ | 
|  | if (req->ai_family == PF_UNSPEC) | 
|  | fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); | 
|  |  | 
|  | if (fct4 != NULL) | 
|  | { | 
|  | int herrno; | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | rc = 0; | 
|  | status = DL_CALL_FCT (fct4, (name, pat, tmpbuf, | 
|  | tmpbuflen, &rc, &herrno, | 
|  | NULL)); | 
|  | if (status == NSS_STATUS_SUCCESS) | 
|  | break; | 
|  | if (status != NSS_STATUS_TRYAGAIN | 
|  | || rc != ERANGE || herrno != NETDB_INTERNAL) | 
|  | { | 
|  | if (herrno == TRY_AGAIN) | 
|  | no_data = EAI_AGAIN; | 
|  | else | 
|  | no_data = herrno == NO_DATA; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!malloc_tmpbuf | 
|  | && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) | 
|  | tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, | 
|  | 2 * tmpbuflen, | 
|  | alloca_used); | 
|  | else | 
|  | { | 
|  | char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, | 
|  | 2 * tmpbuflen); | 
|  | if (newp == NULL) | 
|  | { | 
|  | _res.options |= old_res_options & RES_USE_INET6; | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | tmpbuf = newp; | 
|  | malloc_tmpbuf = true; | 
|  | tmpbuflen = 2 * tmpbuflen; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (status == NSS_STATUS_SUCCESS) | 
|  | { | 
|  | assert (!no_data); | 
|  | no_data = 1; | 
|  |  | 
|  | if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) | 
|  | canon = (*pat)->name; | 
|  |  | 
|  | while (*pat != NULL) | 
|  | { | 
|  | if ((*pat)->family == AF_INET | 
|  | && req->ai_family == AF_INET6 | 
|  | && (req->ai_flags & AI_V4MAPPED) != 0) | 
|  | { | 
|  | uint32_t *pataddr = (*pat)->addr; | 
|  | (*pat)->family = AF_INET6; | 
|  | pataddr[3] = pataddr[0]; | 
|  | pataddr[2] = htonl (0xffff); | 
|  | pataddr[1] = 0; | 
|  | pataddr[0] = 0; | 
|  | pat = &((*pat)->next); | 
|  | no_data = 0; | 
|  | } | 
|  | else if (req->ai_family == AF_UNSPEC | 
|  | || (*pat)->family == req->ai_family) | 
|  | { | 
|  | pat = &((*pat)->next); | 
|  |  | 
|  | no_data = 0; | 
|  | if (req->ai_family == AF_INET6) | 
|  | got_ipv6 = true; | 
|  | } | 
|  | else | 
|  | *pat = ((*pat)->next); | 
|  | } | 
|  | } | 
|  |  | 
|  | no_inet6_data = no_data; | 
|  | } | 
|  | else | 
|  | { | 
|  | nss_gethostbyname3_r fct = NULL; | 
|  | if (req->ai_flags & AI_CANONNAME) | 
|  | /* No need to use this function if we do not look for | 
|  | the canonical name.  The function does not exist in | 
|  | all NSS modules and therefore the lookup would | 
|  | often fail.  */ | 
|  | fct = __nss_lookup_function (nip, "gethostbyname3_r"); | 
|  | if (fct == NULL) | 
|  | /* We are cheating here.  The gethostbyname2_r | 
|  | function does not have the same interface as | 
|  | gethostbyname3_r but the extra arguments the | 
|  | latter takes are added at the end.  So the | 
|  | gethostbyname2_r code will just ignore them.  */ | 
|  | fct = __nss_lookup_function (nip, "gethostbyname2_r"); | 
|  |  | 
|  | if (fct != NULL) | 
|  | { | 
|  | if (req->ai_family == AF_INET6 | 
|  | || req->ai_family == AF_UNSPEC) | 
|  | { | 
|  | gethosts (AF_INET6, struct in6_addr); | 
|  | no_inet6_data = no_data; | 
|  | inet6_status = status; | 
|  | } | 
|  | if (req->ai_family == AF_INET | 
|  | || req->ai_family == AF_UNSPEC | 
|  | || (req->ai_family == AF_INET6 | 
|  | && (req->ai_flags & AI_V4MAPPED) | 
|  | /* Avoid generating the mapped addresses if we | 
|  | know we are not going to need them.  */ | 
|  | && ((req->ai_flags & AI_ALL) || !got_ipv6))) | 
|  | { | 
|  | gethosts (AF_INET, struct in_addr); | 
|  |  | 
|  | if (req->ai_family == AF_INET) | 
|  | { | 
|  | no_inet6_data = no_data; | 
|  | inet6_status = status; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If we found one address for AF_INET or AF_INET6, | 
|  | don't continue the search.  */ | 
|  | if (inet6_status == NSS_STATUS_SUCCESS | 
|  | || status == NSS_STATUS_SUCCESS) | 
|  | { | 
|  | if ((req->ai_flags & AI_CANONNAME) != 0 | 
|  | && canon == NULL) | 
|  | { | 
|  | /* If we need the canonical name, get it | 
|  | from the same service as the result.  */ | 
|  | nss_getcanonname_r cfct; | 
|  | int herrno; | 
|  |  | 
|  | cfct = __nss_lookup_function (nip, | 
|  | "getcanonname_r"); | 
|  | if (cfct != NULL) | 
|  | { | 
|  | const size_t max_fqdn_len = 256; | 
|  | if ((req->ai_flags & AI_CANONIDN) != 0 | 
|  | && __libc_use_alloca (alloca_used | 
|  | + max_fqdn_len)) | 
|  | canonbuf = alloca_account (max_fqdn_len, | 
|  | alloca_used); | 
|  | else | 
|  | { | 
|  | canonbuf = malloc (max_fqdn_len); | 
|  | if (canonbuf == NULL) | 
|  | { | 
|  | _res.options | 
|  | |= old_res_options & RES_USE_INET6; | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | malloc_canonbuf = true; | 
|  | } | 
|  | char *s; | 
|  |  | 
|  | if (DL_CALL_FCT (cfct, (at->name ?: name, | 
|  | canonbuf, | 
|  | max_fqdn_len, | 
|  | &s, &rc, &herrno)) | 
|  | == NSS_STATUS_SUCCESS) | 
|  | canon = s; | 
|  | else | 
|  | { | 
|  | /* If the canonical name cannot be | 
|  | determined, use the passed in | 
|  | string.  */ | 
|  | if (malloc_canonbuf) | 
|  | { | 
|  | free (canonbuf); | 
|  | malloc_canonbuf = false; | 
|  | } | 
|  | canon = name; | 
|  | } | 
|  | } | 
|  | } | 
|  | status = NSS_STATUS_SUCCESS; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* We can have different states for AF_INET and | 
|  | AF_INET6.  Try to find a useful one for both.  */ | 
|  | if (inet6_status == NSS_STATUS_TRYAGAIN) | 
|  | status = NSS_STATUS_TRYAGAIN; | 
|  | else if (status == NSS_STATUS_UNAVAIL | 
|  | && inet6_status != NSS_STATUS_UNAVAIL) | 
|  | status = inet6_status; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | status = NSS_STATUS_UNAVAIL; | 
|  | /* Could not load any of the lookup functions.  Indicate | 
|  | an internal error if the failure was due to a system | 
|  | error other than the file not being found.  We use the | 
|  | errno from the last failed callback.  */ | 
|  | if (errno != 0 && errno != ENOENT) | 
|  | __set_h_errno (NETDB_INTERNAL); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (nss_next_action (nip, status) == NSS_ACTION_RETURN) | 
|  | break; | 
|  |  | 
|  | if (nip->next == NULL) | 
|  | no_more = -1; | 
|  | else | 
|  | nip = nip->next; | 
|  | } | 
|  |  | 
|  | _res.options |= old_res_options & RES_USE_INET6; | 
|  |  | 
|  | if (h_errno == NETDB_INTERNAL) | 
|  | { | 
|  | result = -EAI_SYSTEM; | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | if (no_data != 0 && no_inet6_data != 0) | 
|  | { | 
|  | /* If both requests timed out report this.  */ | 
|  | if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) | 
|  | result = -EAI_AGAIN; | 
|  | else | 
|  | /* We made requests but they turned out no data.  The name | 
|  | is known, though.  */ | 
|  | result = -EAI_NODATA; | 
|  |  | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  |  | 
|  | process_list: | 
|  | if (at->family == AF_UNSPEC) | 
|  | { | 
|  | result = -EAI_NONAME; | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | struct gaih_addrtuple *atr; | 
|  | atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); | 
|  | memset (at, '\0', sizeof (struct gaih_addrtuple)); | 
|  |  | 
|  | if (req->ai_family == AF_UNSPEC) | 
|  | { | 
|  | at->next = __alloca (sizeof (struct gaih_addrtuple)); | 
|  | memset (at->next, '\0', sizeof (struct gaih_addrtuple)); | 
|  | } | 
|  |  | 
|  | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) | 
|  | { | 
|  | at->family = AF_INET6; | 
|  | if ((req->ai_flags & AI_PASSIVE) == 0) | 
|  | memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); | 
|  | atr = at->next; | 
|  | } | 
|  |  | 
|  | if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) | 
|  | { | 
|  | atr->family = AF_INET; | 
|  | if ((req->ai_flags & AI_PASSIVE) == 0) | 
|  | atr->addr[0] = htonl (INADDR_LOOPBACK); | 
|  | } | 
|  | } | 
|  |  | 
|  | { | 
|  | struct gaih_servtuple *st2; | 
|  | struct gaih_addrtuple *at2 = at; | 
|  | size_t socklen; | 
|  | sa_family_t family; | 
|  |  | 
|  | /* | 
|  | buffer is the size of an unformatted IPv6 address in printable format. | 
|  | */ | 
|  | while (at2 != NULL) | 
|  | { | 
|  | /* Only the first entry gets the canonical name.  */ | 
|  | if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) | 
|  | { | 
|  | if (canon == NULL) | 
|  | /* If the canonical name cannot be determined, use | 
|  | the passed in string.  */ | 
|  | canon = orig_name; | 
|  |  | 
|  | #ifdef HAVE_LIBIDN | 
|  | if (req->ai_flags & AI_CANONIDN) | 
|  | { | 
|  | int idn_flags = 0; | 
|  | if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED) | 
|  | idn_flags |= IDNA_ALLOW_UNASSIGNED; | 
|  | if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES) | 
|  | idn_flags |= IDNA_USE_STD3_ASCII_RULES; | 
|  |  | 
|  | char *out; | 
|  | int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags); | 
|  | if (rc != IDNA_SUCCESS) | 
|  | { | 
|  | if (rc == IDNA_MALLOC_ERROR) | 
|  | result = -EAI_MEMORY; | 
|  | else if (rc == IDNA_DLOPEN_ERROR) | 
|  | result = -EAI_SYSTEM; | 
|  | else | 
|  | result = -EAI_IDN_ENCODE; | 
|  | goto free_and_return; | 
|  | } | 
|  | /* In case the output string is the same as the input | 
|  | string no new string has been allocated and we | 
|  | make a copy.  */ | 
|  | if (out == canon) | 
|  | goto make_copy; | 
|  | canon = out; | 
|  | } | 
|  | else | 
|  | #endif | 
|  | { | 
|  | #ifdef HAVE_LIBIDN | 
|  | make_copy: | 
|  | #endif | 
|  | if (malloc_canonbuf) | 
|  | /* We already allocated the string using malloc.  */ | 
|  | malloc_canonbuf = false; | 
|  | else | 
|  | { | 
|  | canon = strdup (canon); | 
|  | if (canon == NULL) | 
|  | { | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | family = at2->family; | 
|  | if (family == AF_INET6) | 
|  | { | 
|  | socklen = sizeof (struct sockaddr_in6); | 
|  |  | 
|  | /* If we looked up IPv4 mapped address discard them here if | 
|  | the caller isn't interested in all address and we have | 
|  | found at least one IPv6 address.  */ | 
|  | if (got_ipv6 | 
|  | && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED | 
|  | && IN6_IS_ADDR_V4MAPPED (at2->addr)) | 
|  | goto ignore; | 
|  | } | 
|  | else | 
|  | socklen = sizeof (struct sockaddr_in); | 
|  |  | 
|  | for (st2 = st; st2 != NULL; st2 = st2->next) | 
|  | { | 
|  | struct addrinfo *ai; | 
|  | ai = *pai = malloc (sizeof (struct addrinfo) + socklen); | 
|  | if (ai == NULL) | 
|  | { | 
|  | free ((char *) canon); | 
|  | result = -EAI_MEMORY; | 
|  | goto free_and_return; | 
|  | } | 
|  |  | 
|  | ai->ai_flags = req->ai_flags; | 
|  | ai->ai_family = family; | 
|  | ai->ai_socktype = st2->socktype; | 
|  | ai->ai_protocol = st2->protocol; | 
|  | ai->ai_addrlen = socklen; | 
|  | ai->ai_addr = (void *) (ai + 1); | 
|  |  | 
|  | /* We only add the canonical name once.  */ | 
|  | ai->ai_canonname = (char *) canon; | 
|  | canon = NULL; | 
|  |  | 
|  | #ifdef _HAVE_SA_LEN | 
|  | ai->ai_addr->sa_len = socklen; | 
|  | #endif /* _HAVE_SA_LEN */ | 
|  | ai->ai_addr->sa_family = family; | 
|  |  | 
|  | /* In case of an allocation error the list must be NULL | 
|  | terminated.  */ | 
|  | ai->ai_next = NULL; | 
|  |  | 
|  | if (family == AF_INET6) | 
|  | { | 
|  | struct sockaddr_in6 *sin6p = | 
|  | (struct sockaddr_in6 *) ai->ai_addr; | 
|  |  | 
|  | sin6p->sin6_port = st2->port; | 
|  | sin6p->sin6_flowinfo = 0; | 
|  | memcpy (&sin6p->sin6_addr, | 
|  | at2->addr, sizeof (struct in6_addr)); | 
|  | sin6p->sin6_scope_id = at2->scopeid; | 
|  | } | 
|  | else | 
|  | { | 
|  | struct sockaddr_in *sinp = | 
|  | (struct sockaddr_in *) ai->ai_addr; | 
|  | sinp->sin_port = st2->port; | 
|  | memcpy (&sinp->sin_addr, | 
|  | at2->addr, sizeof (struct in_addr)); | 
|  | memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); | 
|  | } | 
|  |  | 
|  | pai = &(ai->ai_next); | 
|  | } | 
|  |  | 
|  | ++*naddrs; | 
|  |  | 
|  | ignore: | 
|  | at2 = at2->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | free_and_return: | 
|  | if (malloc_name) | 
|  | free ((char *) name); | 
|  | if (malloc_addrmem) | 
|  | free (addrmem); | 
|  | if (malloc_canonbuf) | 
|  | free (canonbuf); | 
|  | if (malloc_tmpbuf) | 
|  | free (tmpbuf); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | struct sort_result | 
|  | { | 
|  | struct addrinfo *dest_addr; | 
|  | /* Using sockaddr_storage is for now overkill.  We only support IPv4 | 
|  | and IPv6 so far.  If this changes at some point we can adjust the | 
|  | type here.  */ | 
|  | struct sockaddr_in6 source_addr; | 
|  | uint8_t source_addr_len; | 
|  | bool got_source_addr; | 
|  | uint8_t source_addr_flags; | 
|  | uint8_t prefixlen; | 
|  | uint32_t index; | 
|  | int32_t native; | 
|  | }; | 
|  |  | 
|  | struct sort_result_combo | 
|  | { | 
|  | struct sort_result *results; | 
|  | int nresults; | 
|  | }; | 
|  |  | 
|  |  | 
|  | #if __BYTE_ORDER == __BIG_ENDIAN | 
|  | # define htonl_c(n) n | 
|  | #else | 
|  | # define htonl_c(n) __bswap_constant_32 (n) | 
|  | #endif | 
|  |  | 
|  | static const struct scopeentry | 
|  | { | 
|  | union | 
|  | { | 
|  | char addr[4]; | 
|  | uint32_t addr32; | 
|  | }; | 
|  | uint32_t netmask; | 
|  | int32_t scope; | 
|  | } default_scopes[] = | 
|  | { | 
|  | /* Link-local addresses: scope 2.  */ | 
|  | { { { 169, 254, 0, 0 } }, htonl_c (0xffff0000), 2 }, | 
|  | { { { 127, 0, 0, 0 } }, htonl_c (0xff000000), 2 }, | 
|  | /* Default: scope 14.  */ | 
|  | { { { 0, 0, 0, 0 } }, htonl_c (0x00000000), 14 } | 
|  | }; | 
|  |  | 
|  | /* The label table.  */ | 
|  | static const struct scopeentry *scopes; | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_scope (const struct sockaddr_in6 *in6) | 
|  | { | 
|  | int scope; | 
|  | if (in6->sin6_family == PF_INET6) | 
|  | { | 
|  | if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr)) | 
|  | { | 
|  | if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr) | 
|  | /* RFC 4291 2.5.3 says that the loopback address is to be | 
|  | treated like a link-local address.  */ | 
|  | || IN6_IS_ADDR_LOOPBACK (&in6->sin6_addr)) | 
|  | scope = 2; | 
|  | else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr)) | 
|  | scope = 5; | 
|  | else | 
|  | /* XXX Is this the correct default behavior?  */ | 
|  | scope = 14; | 
|  | } | 
|  | else | 
|  | scope = in6->sin6_addr.s6_addr[1] & 0xf; | 
|  | } | 
|  | else if (in6->sin6_family == PF_INET) | 
|  | { | 
|  | const struct sockaddr_in *in = (const struct sockaddr_in *) in6; | 
|  |  | 
|  | size_t cnt = 0; | 
|  | while (1) | 
|  | { | 
|  | if ((in->sin_addr.s_addr & scopes[cnt].netmask) | 
|  | == scopes[cnt].addr32) | 
|  | return scopes[cnt].scope; | 
|  |  | 
|  | ++cnt; | 
|  | } | 
|  | /* NOTREACHED */ | 
|  | } | 
|  | else | 
|  | /* XXX What is a good default?  */ | 
|  | scope = 15; | 
|  |  | 
|  | return scope; | 
|  | } | 
|  |  | 
|  |  | 
|  | struct prefixentry | 
|  | { | 
|  | struct in6_addr prefix; | 
|  | unsigned int bits; | 
|  | int val; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* The label table.  */ | 
|  | static const struct prefixentry *labels; | 
|  |  | 
|  | /* Default labels.  */ | 
|  | static const struct prefixentry default_labels[] = | 
|  | { | 
|  | /* See RFC 3484 for the details.  */ | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } | 
|  | }, 128, 0 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 16, 2 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 96, 3 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 96, 4 }, | 
|  | /* The next two entries differ from RFC 3484.  We need to treat | 
|  | IPv6 site-local addresses special because they are never NATed, | 
|  | unlike site-locale IPv4 addresses.  If this would not happen, on | 
|  | machines which have only IPv4 and IPv6 site-local addresses, the | 
|  | sorting would prefer the IPv6 site-local addresses, causing | 
|  | unnecessary delays when trying to connect to a global IPv6 address | 
|  | through a site-local IPv6 address.  */ | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 10, 5 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 7, 6 }, | 
|  | /* Additional rule for Teredo tunnels.  */ | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 32, 7 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 0, 1 } | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* The precedence table.  */ | 
|  | static const struct prefixentry *precedence; | 
|  |  | 
|  | /* The default precedences.  */ | 
|  | static const struct prefixentry default_precedence[] = | 
|  | { | 
|  | /* See RFC 3484 for the details.  */ | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } | 
|  | }, 128, 50 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 16, 30 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 96, 20 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 96, 10 }, | 
|  | { { .__in6_u | 
|  | = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
|  | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } | 
|  | }, 0, 40 } | 
|  | }; | 
|  |  | 
|  |  | 
|  | static int | 
|  | match_prefix (const struct sockaddr_in6 *in6, | 
|  | const struct prefixentry *list, int default_val) | 
|  | { | 
|  | int idx; | 
|  | struct sockaddr_in6 in6_mem; | 
|  |  | 
|  | if (in6->sin6_family == PF_INET) | 
|  | { | 
|  | const struct sockaddr_in *in = (const struct sockaddr_in *) in6; | 
|  |  | 
|  | /* Construct a V4-to-6 mapped address.  */ | 
|  | in6_mem.sin6_family = PF_INET6; | 
|  | in6_mem.sin6_port = in->sin_port; | 
|  | in6_mem.sin6_flowinfo = 0; | 
|  | memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr)); | 
|  | in6_mem.sin6_addr.s6_addr16[5] = 0xffff; | 
|  | in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr; | 
|  | in6_mem.sin6_scope_id = 0; | 
|  |  | 
|  | in6 = &in6_mem; | 
|  | } | 
|  | else if (in6->sin6_family != PF_INET6) | 
|  | return default_val; | 
|  |  | 
|  | for (idx = 0; ; ++idx) | 
|  | { | 
|  | unsigned int bits = list[idx].bits; | 
|  | const uint8_t *mask = list[idx].prefix.s6_addr; | 
|  | const uint8_t *val = in6->sin6_addr.s6_addr; | 
|  |  | 
|  | while (bits >= 8) | 
|  | { | 
|  | if (*mask != *val) | 
|  | break; | 
|  |  | 
|  | ++mask; | 
|  | ++val; | 
|  | bits -= 8; | 
|  | } | 
|  |  | 
|  | if (bits < 8) | 
|  | { | 
|  | if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits))) | 
|  | /* Match!  */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return list[idx].val; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_label (const struct sockaddr_in6 *in6) | 
|  | { | 
|  | /* XXX What is a good default value?  */ | 
|  | return match_prefix (in6, labels, INT_MAX); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_precedence (const struct sockaddr_in6 *in6) | 
|  | { | 
|  | /* XXX What is a good default value?  */ | 
|  | return match_prefix (in6, precedence, 0); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Find last bit set in a word.  */ | 
|  | static int | 
|  | fls (uint32_t a) | 
|  | { | 
|  | uint32_t mask; | 
|  | int n; | 
|  | for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n) | 
|  | if ((a & mask) != 0) | 
|  | break; | 
|  | return n; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | rfc3484_sort (const void *p1, const void *p2, void *arg) | 
|  | { | 
|  | const size_t idx1 = *(const size_t *) p1; | 
|  | const size_t idx2 = *(const size_t *) p2; | 
|  | struct sort_result_combo *src = (struct sort_result_combo *) arg; | 
|  | struct sort_result *a1 = &src->results[idx1]; | 
|  | struct sort_result *a2 = &src->results[idx2]; | 
|  |  | 
|  | /* Rule 1: Avoid unusable destinations. | 
|  | We have the got_source_addr flag set if the destination is reachable.  */ | 
|  | if (a1->got_source_addr && ! a2->got_source_addr) | 
|  | return -1; | 
|  | if (! a1->got_source_addr && a2->got_source_addr) | 
|  | return 1; | 
|  |  | 
|  |  | 
|  | /* Rule 2: Prefer matching scope.  Only interesting if both | 
|  | destination addresses are IPv6.  */ | 
|  | int a1_dst_scope | 
|  | = get_scope ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); | 
|  |  | 
|  | int a2_dst_scope | 
|  | = get_scope ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); | 
|  |  | 
|  | if (a1->got_source_addr) | 
|  | { | 
|  | int a1_src_scope = get_scope (&a1->source_addr); | 
|  | int a2_src_scope = get_scope (&a2->source_addr); | 
|  |  | 
|  | if (a1_dst_scope == a1_src_scope && a2_dst_scope != a2_src_scope) | 
|  | return -1; | 
|  | if (a1_dst_scope != a1_src_scope && a2_dst_scope == a2_src_scope) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Rule 3: Avoid deprecated addresses.  */ | 
|  | if (a1->got_source_addr) | 
|  | { | 
|  | if (!(a1->source_addr_flags & in6ai_deprecated) | 
|  | && (a2->source_addr_flags & in6ai_deprecated)) | 
|  | return -1; | 
|  | if ((a1->source_addr_flags & in6ai_deprecated) | 
|  | && !(a2->source_addr_flags & in6ai_deprecated)) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Rule 4: Prefer home addresses.  */ | 
|  | if (a1->got_source_addr) | 
|  | { | 
|  | if (!(a1->source_addr_flags & in6ai_homeaddress) | 
|  | && (a2->source_addr_flags & in6ai_homeaddress)) | 
|  | return 1; | 
|  | if ((a1->source_addr_flags & in6ai_homeaddress) | 
|  | && !(a2->source_addr_flags & in6ai_homeaddress)) | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Rule 5: Prefer matching label.  */ | 
|  | if (a1->got_source_addr) | 
|  | { | 
|  | int a1_dst_label | 
|  | = get_label ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); | 
|  | int a1_src_label = get_label (&a1->source_addr); | 
|  |  | 
|  | int a2_dst_label | 
|  | = get_label ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); | 
|  | int a2_src_label = get_label (&a2->source_addr); | 
|  |  | 
|  | if (a1_dst_label == a1_src_label && a2_dst_label != a2_src_label) | 
|  | return -1; | 
|  | if (a1_dst_label != a1_src_label && a2_dst_label == a2_src_label) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Rule 6: Prefer higher precedence.  */ | 
|  | int a1_prec | 
|  | = get_precedence ((struct sockaddr_in6 *) a1->dest_addr->ai_addr); | 
|  | int a2_prec | 
|  | = get_precedence ((struct sockaddr_in6 *) a2->dest_addr->ai_addr); | 
|  |  | 
|  | if (a1_prec > a2_prec) | 
|  | return -1; | 
|  | if (a1_prec < a2_prec) | 
|  | return 1; | 
|  |  | 
|  |  | 
|  | /* Rule 7: Prefer native transport.  */ | 
|  | if (a1->got_source_addr) | 
|  | { | 
|  | /* The same interface index means the same interface which means | 
|  | there is no difference in transport.  This should catch many | 
|  | (most?) cases.  */ | 
|  | if (a1->index != a2->index) | 
|  | { | 
|  | int a1_native = a1->native; | 
|  | int a2_native = a2->native; | 
|  |  | 
|  | if (a1_native == -1 || a2_native == -1) | 
|  | { | 
|  | uint32_t a1_index; | 
|  | if (a1_native == -1) | 
|  | { | 
|  | /* If we do not have the information use 'native' as | 
|  | the default.  */ | 
|  | a1_native = 0; | 
|  | a1_index = a1->index; | 
|  | } | 
|  | else | 
|  | a1_index = 0xffffffffu; | 
|  |  | 
|  | uint32_t a2_index; | 
|  | if (a2_native == -1) | 
|  | { | 
|  | /* If we do not have the information use 'native' as | 
|  | the default.  */ | 
|  | a2_native = 0; | 
|  | a2_index = a2->index; | 
|  | } | 
|  | else | 
|  | a2_index = 0xffffffffu; | 
|  |  | 
|  | __check_native (a1_index, &a1_native, a2_index, &a2_native); | 
|  |  | 
|  | /* Fill in the results in all the records.  */ | 
|  | for (int i = 0; i < src->nresults; ++i) | 
|  | if (a1_index != -1 && src->results[i].index == a1_index) | 
|  | { | 
|  | assert (src->results[i].native == -1 | 
|  | || src->results[i].native == a1_native); | 
|  | src->results[i].native = a1_native; | 
|  | } | 
|  | else if (a2_index != -1 && src->results[i].index == a2_index) | 
|  | { | 
|  | assert (src->results[i].native == -1 | 
|  | || src->results[i].native == a2_native); | 
|  | src->results[i].native = a2_native; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (a1_native && !a2_native) | 
|  | return -1; | 
|  | if (!a1_native && a2_native) | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Rule 8: Prefer smaller scope.  */ | 
|  | if (a1_dst_scope < a2_dst_scope) | 
|  | return -1; | 
|  | if (a1_dst_scope > a2_dst_scope) | 
|  | return 1; | 
|  |  | 
|  |  | 
|  | /* Rule 9: Use longest matching prefix.  */ | 
|  | if (a1->got_source_addr | 
|  | && a1->dest_addr->ai_family == a2->dest_addr->ai_family) | 
|  | { | 
|  | int bit1 = 0; | 
|  | int bit2 = 0; | 
|  |  | 
|  | if (a1->dest_addr->ai_family == PF_INET) | 
|  | { | 
|  | assert (a1->source_addr.sin6_family == PF_INET); | 
|  | assert (a2->source_addr.sin6_family == PF_INET); | 
|  |  | 
|  | /* Outside of subnets, as defined by the network masks, | 
|  | common address prefixes for IPv4 addresses make no sense. | 
|  | So, define a non-zero value only if source and | 
|  | destination address are on the same subnet.  */ | 
|  | struct sockaddr_in *in1_dst | 
|  | = (struct sockaddr_in *) a1->dest_addr->ai_addr; | 
|  | in_addr_t in1_dst_addr = ntohl (in1_dst->sin_addr.s_addr); | 
|  | struct sockaddr_in *in1_src | 
|  | = (struct sockaddr_in *) &a1->source_addr; | 
|  | in_addr_t in1_src_addr = ntohl (in1_src->sin_addr.s_addr); | 
|  | in_addr_t netmask1 = 0xffffffffu << (32 - a1->prefixlen); | 
|  |  | 
|  | if ((in1_src_addr & netmask1) == (in1_dst_addr & netmask1)) | 
|  | bit1 = fls (in1_dst_addr ^ in1_src_addr); | 
|  |  | 
|  | struct sockaddr_in *in2_dst | 
|  | = (struct sockaddr_in *) a2->dest_addr->ai_addr; | 
|  | in_addr_t in2_dst_addr = ntohl (in2_dst->sin_addr.s_addr); | 
|  | struct sockaddr_in *in2_src | 
|  | = (struct sockaddr_in *) &a2->source_addr; | 
|  | in_addr_t in2_src_addr = ntohl (in2_src->sin_addr.s_addr); | 
|  | in_addr_t netmask2 = 0xffffffffu << (32 - a2->prefixlen); | 
|  |  | 
|  | if ((in2_src_addr & netmask2) == (in2_dst_addr & netmask2)) | 
|  | bit2 = fls (in2_dst_addr ^ in2_src_addr); | 
|  | } | 
|  | else if (a1->dest_addr->ai_family == PF_INET6) | 
|  | { | 
|  | assert (a1->source_addr.sin6_family == PF_INET6); | 
|  | assert (a2->source_addr.sin6_family == PF_INET6); | 
|  |  | 
|  | struct sockaddr_in6 *in1_dst; | 
|  | struct sockaddr_in6 *in1_src; | 
|  | struct sockaddr_in6 *in2_dst; | 
|  | struct sockaddr_in6 *in2_src; | 
|  |  | 
|  | in1_dst = (struct sockaddr_in6 *) a1->dest_addr->ai_addr; | 
|  | in1_src = (struct sockaddr_in6 *) &a1->source_addr; | 
|  | in2_dst = (struct sockaddr_in6 *) a2->dest_addr->ai_addr; | 
|  | in2_src = (struct sockaddr_in6 *) &a2->source_addr; | 
|  |  | 
|  | int i; | 
|  | for (i = 0; i < 4; ++i) | 
|  | if (in1_dst->sin6_addr.s6_addr32[i] | 
|  | != in1_src->sin6_addr.s6_addr32[i] | 
|  | || (in2_dst->sin6_addr.s6_addr32[i] | 
|  | != in2_src->sin6_addr.s6_addr32[i])) | 
|  | break; | 
|  |  | 
|  | if (i < 4) | 
|  | { | 
|  | bit1 = fls (ntohl (in1_dst->sin6_addr.s6_addr32[i] | 
|  | ^ in1_src->sin6_addr.s6_addr32[i])); | 
|  | bit2 = fls (ntohl (in2_dst->sin6_addr.s6_addr32[i] | 
|  | ^ in2_src->sin6_addr.s6_addr32[i])); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (bit1 > bit2) | 
|  | return -1; | 
|  | if (bit1 < bit2) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Rule 10: Otherwise, leave the order unchanged.  To ensure this | 
|  | compare with the value indicating the order in which the entries | 
|  | have been received from the services.  NB: no two entries can have | 
|  | the same order so the test will never return zero.  */ | 
|  | return idx1 < idx2 ? -1 : 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | in6aicmp (const void *p1, const void *p2) | 
|  | { | 
|  | struct in6addrinfo *a1 = (struct in6addrinfo *) p1; | 
|  | struct in6addrinfo *a2 = (struct in6addrinfo *) p2; | 
|  |  | 
|  | return memcmp (a1->addr, a2->addr, sizeof (a1->addr)); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Name of the config file for RFC 3484 sorting (for now).  */ | 
|  | #define GAICONF_FNAME "/etc/gai.conf" | 
|  |  | 
|  |  | 
|  | /* Non-zero if we are supposed to reload the config file automatically | 
|  | whenever it changed.  */ | 
|  | static int gaiconf_reload_flag; | 
|  |  | 
|  | /* Non-zero if gaiconf_reload_flag was ever set to true.  */ | 
|  | static int gaiconf_reload_flag_ever_set; | 
|  |  | 
|  | /* Last modification time.  */ | 
|  | #ifdef _STATBUF_ST_NSEC | 
|  |  | 
|  | static struct timespec gaiconf_mtime; | 
|  |  | 
|  | static inline void | 
|  | save_gaiconf_mtime (const struct stat64 *st) | 
|  | { | 
|  | gaiconf_mtime = st->st_mtim; | 
|  | } | 
|  |  | 
|  | static inline bool | 
|  | check_gaiconf_mtime (const struct stat64 *st) | 
|  | { | 
|  | return (st->st_mtim.tv_sec == gaiconf_mtime.tv_sec | 
|  | && st->st_mtim.tv_nsec == gaiconf_mtime.tv_nsec); | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | static time_t gaiconf_mtime; | 
|  |  | 
|  | static inline void | 
|  | save_gaiconf_mtime (const struct stat64 *st) | 
|  | { | 
|  | gaiconf_mtime = st->st_mtime; | 
|  | } | 
|  |  | 
|  | static inline bool | 
|  | check_gaiconf_mtime (const struct stat64 *st) | 
|  | { | 
|  | return st->st_mtime == gaiconf_mtime; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  |  | 
|  | libc_freeres_fn(fini) | 
|  | { | 
|  | if (labels != default_labels) | 
|  | { | 
|  | const struct prefixentry *old = labels; | 
|  | labels = default_labels; | 
|  | free ((void *) old); | 
|  | } | 
|  |  | 
|  | if (precedence != default_precedence) | 
|  | { | 
|  | const struct prefixentry *old = precedence; | 
|  | precedence = default_precedence; | 
|  | free ((void *) old); | 
|  | } | 
|  |  | 
|  | if (scopes != default_scopes) | 
|  | { | 
|  | const struct scopeentry *old = scopes; | 
|  | scopes = default_scopes; | 
|  | free ((void *) old); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | struct prefixlist | 
|  | { | 
|  | struct prefixentry entry; | 
|  | struct prefixlist *next; | 
|  | }; | 
|  |  | 
|  |  | 
|  | struct scopelist | 
|  | { | 
|  | struct scopeentry entry; | 
|  | struct scopelist *next; | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void | 
|  | free_prefixlist (struct prefixlist *list) | 
|  | { | 
|  | while (list != NULL) | 
|  | { | 
|  | struct prefixlist *oldp = list; | 
|  | list = list->next; | 
|  | free (oldp); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | free_scopelist (struct scopelist *list) | 
|  | { | 
|  | while (list != NULL) | 
|  | { | 
|  | struct scopelist *oldp = list; | 
|  | list = list->next; | 
|  | free (oldp); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | prefixcmp (const void *p1, const void *p2) | 
|  | { | 
|  | const struct prefixentry *e1 = (const struct prefixentry *) p1; | 
|  | const struct prefixentry *e2 = (const struct prefixentry *) p2; | 
|  |  | 
|  | if (e1->bits < e2->bits) | 
|  | return 1; | 
|  | if (e1->bits == e2->bits) | 
|  | return 0; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | scopecmp (const void *p1, const void *p2) | 
|  | { | 
|  | const struct scopeentry *e1 = (const struct scopeentry *) p1; | 
|  | const struct scopeentry *e2 = (const struct scopeentry *) p2; | 
|  |  | 
|  | if (e1->netmask > e2->netmask) | 
|  | return -1; | 
|  | if (e1->netmask == e2->netmask) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | gaiconf_init (void) | 
|  | { | 
|  | struct prefixlist *labellist = NULL; | 
|  | size_t nlabellist = 0; | 
|  | bool labellist_nullbits = false; | 
|  | struct prefixlist *precedencelist = NULL; | 
|  | size_t nprecedencelist = 0; | 
|  | bool precedencelist_nullbits = false; | 
|  | struct scopelist *scopelist =  NULL; | 
|  | size_t nscopelist = 0; | 
|  | bool scopelist_nullbits = false; | 
|  |  | 
|  | FILE *fp = fopen (GAICONF_FNAME, "rce"); | 
|  | if (fp != NULL) | 
|  | { | 
|  | struct stat64 st; | 
|  | if (__fxstat64 (_STAT_VER, fileno (fp), &st) != 0) | 
|  | { | 
|  | fclose (fp); | 
|  | goto no_file; | 
|  | } | 
|  |  | 
|  | char *line = NULL; | 
|  | size_t linelen = 0; | 
|  |  | 
|  | __fsetlocking (fp, FSETLOCKING_BYCALLER); | 
|  |  | 
|  | while (!feof_unlocked (fp)) | 
|  | { | 
|  | ssize_t n = __getline (&line, &linelen, fp); | 
|  | if (n <= 0) | 
|  | break; | 
|  |  | 
|  | /* Handle comments.  No escaping possible so this is easy.  */ | 
|  | char *cp = strchr (line, '#'); | 
|  | if (cp != NULL) | 
|  | *cp = '\0'; | 
|  |  | 
|  | cp = line; | 
|  | while (isspace (*cp)) | 
|  | ++cp; | 
|  |  | 
|  | char *cmd = cp; | 
|  | while (*cp != '\0' && !isspace (*cp)) | 
|  | ++cp; | 
|  | size_t cmdlen = cp - cmd; | 
|  |  | 
|  | if (*cp != '\0') | 
|  | *cp++ = '\0'; | 
|  | while (isspace (*cp)) | 
|  | ++cp; | 
|  |  | 
|  | char *val1 = cp; | 
|  | while (*cp != '\0' && !isspace (*cp)) | 
|  | ++cp; | 
|  | size_t val1len = cp - cmd; | 
|  |  | 
|  | /* We always need at least two values.  */ | 
|  | if (val1len == 0) | 
|  | continue; | 
|  |  | 
|  | if (*cp != '\0') | 
|  | *cp++ = '\0'; | 
|  | while (isspace (*cp)) | 
|  | ++cp; | 
|  |  | 
|  | char *val2 = cp; | 
|  | while (*cp != '\0' && !isspace (*cp)) | 
|  | ++cp; | 
|  |  | 
|  | /*  Ignore the rest of the line.  */ | 
|  | *cp = '\0'; | 
|  |  | 
|  | struct prefixlist **listp; | 
|  | size_t *lenp; | 
|  | bool *nullbitsp; | 
|  | switch (cmdlen) | 
|  | { | 
|  | case 5: | 
|  | if (strcmp (cmd, "label") == 0) | 
|  | { | 
|  | struct in6_addr prefix; | 
|  | unsigned long int bits; | 
|  | unsigned long int val; | 
|  | char *endp; | 
|  |  | 
|  | listp = &labellist; | 
|  | lenp = &nlabellist; | 
|  | nullbitsp = &labellist_nullbits; | 
|  |  | 
|  | new_elem: | 
|  | bits = 128; | 
|  | __set_errno (0); | 
|  | cp = strchr (val1, '/'); | 
|  | if (cp != NULL) | 
|  | *cp++ = '\0'; | 
|  | if (inet_pton (AF_INET6, val1, &prefix) | 
|  | && (cp == NULL | 
|  | || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && bits <= 128 | 
|  | && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && val <= INT_MAX) | 
|  | { | 
|  | struct prefixlist *newp = malloc (sizeof (*newp)); | 
|  | if (newp == NULL) | 
|  | { | 
|  | free (line); | 
|  | fclose (fp); | 
|  | goto no_file; | 
|  | } | 
|  |  | 
|  | memcpy (&newp->entry.prefix, &prefix, sizeof (prefix)); | 
|  | newp->entry.bits = bits; | 
|  | newp->entry.val = val; | 
|  | newp->next = *listp; | 
|  | *listp = newp; | 
|  | ++*lenp; | 
|  | *nullbitsp |= bits == 0; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 6: | 
|  | if (strcmp (cmd, "reload") == 0) | 
|  | { | 
|  | gaiconf_reload_flag = strcmp (val1, "yes") == 0; | 
|  | if (gaiconf_reload_flag) | 
|  | gaiconf_reload_flag_ever_set = 1; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 7: | 
|  | if (strcmp (cmd, "scopev4") == 0) | 
|  | { | 
|  | struct in6_addr prefix; | 
|  | unsigned long int bits; | 
|  | unsigned long int val; | 
|  | char *endp; | 
|  |  | 
|  | bits = 32; | 
|  | __set_errno (0); | 
|  | cp = strchr (val1, '/'); | 
|  | if (cp != NULL) | 
|  | *cp++ = '\0'; | 
|  | if (inet_pton (AF_INET6, val1, &prefix)) | 
|  | { | 
|  | bits = 128; | 
|  | if (IN6_IS_ADDR_V4MAPPED (&prefix) | 
|  | && (cp == NULL | 
|  | || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && bits >= 96 | 
|  | && bits <= 128 | 
|  | && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && val <= INT_MAX) | 
|  | { | 
|  | struct scopelist *newp; | 
|  | new_scope: | 
|  | newp = malloc (sizeof (*newp)); | 
|  | if (newp == NULL) | 
|  | { | 
|  | free (line); | 
|  | fclose (fp); | 
|  | goto no_file; | 
|  | } | 
|  |  | 
|  | newp->entry.netmask = htonl (bits != 96 | 
|  | ? (0xffffffff | 
|  | << (128 - bits)) | 
|  | : 0); | 
|  | newp->entry.addr32 = (prefix.s6_addr32[3] | 
|  | & newp->entry.netmask); | 
|  | newp->entry.scope = val; | 
|  | newp->next = scopelist; | 
|  | scopelist = newp; | 
|  | ++nscopelist; | 
|  | scopelist_nullbits |= bits == 96; | 
|  | } | 
|  | } | 
|  | else if (inet_pton (AF_INET, val1, &prefix.s6_addr32[3]) | 
|  | && (cp == NULL | 
|  | || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && bits <= 32 | 
|  | && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX | 
|  | || errno != ERANGE) | 
|  | && *endp == '\0' | 
|  | && val <= INT_MAX) | 
|  | { | 
|  | bits += 96; | 
|  | goto new_scope; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 10: | 
|  | if (strcmp (cmd, "precedence") == 0) | 
|  | { | 
|  | listp = &precedencelist; | 
|  | lenp = &nprecedencelist; | 
|  | nullbitsp = &precedencelist_nullbits; | 
|  | goto new_elem; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | free (line); | 
|  |  | 
|  | fclose (fp); | 
|  |  | 
|  | /* Create the array for the labels.  */ | 
|  | struct prefixentry *new_labels; | 
|  | if (nlabellist > 0) | 
|  | { | 
|  | if (!labellist_nullbits) | 
|  | ++nlabellist; | 
|  | new_labels = malloc (nlabellist * sizeof (*new_labels)); | 
|  | if (new_labels == NULL) | 
|  | goto no_file; | 
|  |  | 
|  | int i = nlabellist; | 
|  | if (!labellist_nullbits) | 
|  | { | 
|  | --i; | 
|  | memset (&new_labels[i].prefix, '\0', sizeof (struct in6_addr)); | 
|  | new_labels[i].bits = 0; | 
|  | new_labels[i].val = 1; | 
|  | } | 
|  |  | 
|  | struct prefixlist *l = labellist; | 
|  | while (i-- > 0) | 
|  | { | 
|  | new_labels[i] = l->entry; | 
|  | l = l->next; | 
|  | } | 
|  | free_prefixlist (labellist); | 
|  |  | 
|  | /* Sort the entries so that the most specific ones are at | 
|  | the beginning.  */ | 
|  | qsort (new_labels, nlabellist, sizeof (*new_labels), prefixcmp); | 
|  | } | 
|  | else | 
|  | new_labels = (struct prefixentry *) default_labels; | 
|  |  | 
|  | struct prefixentry *new_precedence; | 
|  | if (nprecedencelist > 0) | 
|  | { | 
|  | if (!precedencelist_nullbits) | 
|  | ++nprecedencelist; | 
|  | new_precedence = malloc (nprecedencelist * sizeof (*new_precedence)); | 
|  | if (new_precedence == NULL) | 
|  | { | 
|  | if (new_labels != default_labels) | 
|  | free (new_labels); | 
|  | goto no_file; | 
|  | } | 
|  |  | 
|  | int i = nprecedencelist; | 
|  | if (!precedencelist_nullbits) | 
|  | { | 
|  | --i; | 
|  | memset (&new_precedence[i].prefix, '\0', | 
|  | sizeof (struct in6_addr)); | 
|  | new_precedence[i].bits = 0; | 
|  | new_precedence[i].val = 40; | 
|  | } | 
|  |  | 
|  | struct prefixlist *l = precedencelist; | 
|  | while (i-- > 0) | 
|  | { | 
|  | new_precedence[i] = l->entry; | 
|  | l = l->next; | 
|  | } | 
|  | free_prefixlist (precedencelist); | 
|  |  | 
|  | /* Sort the entries so that the most specific ones are at | 
|  | the beginning.  */ | 
|  | qsort (new_precedence, nprecedencelist, sizeof (*new_precedence), | 
|  | prefixcmp); | 
|  | } | 
|  | else | 
|  | new_precedence = (struct prefixentry *) default_precedence; | 
|  |  | 
|  | struct scopeentry *new_scopes; | 
|  | if (nscopelist > 0) | 
|  | { | 
|  | if (!scopelist_nullbits) | 
|  | ++nscopelist; | 
|  | new_scopes = malloc (nscopelist * sizeof (*new_scopes)); | 
|  | if (new_scopes == NULL) | 
|  | { | 
|  | if (new_labels != default_labels) | 
|  | free (new_labels); | 
|  | if (new_precedence != default_precedence) | 
|  | free (new_precedence); | 
|  | goto no_file; | 
|  | } | 
|  |  | 
|  | int i = nscopelist; | 
|  | if (!scopelist_nullbits) | 
|  | { | 
|  | --i; | 
|  | new_scopes[i].addr32 = 0; | 
|  | new_scopes[i].netmask = 0; | 
|  | new_scopes[i].scope = 14; | 
|  | } | 
|  |  | 
|  | struct scopelist *l = scopelist; | 
|  | while (i-- > 0) | 
|  | { | 
|  | new_scopes[i] = l->entry; | 
|  | l = l->next; | 
|  | } | 
|  | free_scopelist (scopelist); | 
|  |  | 
|  | /* Sort the entries so that the most specific ones are at | 
|  | the beginning.  */ | 
|  | qsort (new_scopes, nscopelist, sizeof (*new_scopes), | 
|  | scopecmp); | 
|  | } | 
|  | else | 
|  | new_scopes = (struct scopeentry *) default_scopes; | 
|  |  | 
|  | /* Now we are ready to replace the values.  */ | 
|  | const struct prefixentry *old = labels; | 
|  | labels = new_labels; | 
|  | if (old != default_labels) | 
|  | free ((void *) old); | 
|  |  | 
|  | old = precedence; | 
|  | precedence = new_precedence; | 
|  | if (old != default_precedence) | 
|  | free ((void *) old); | 
|  |  | 
|  | const struct scopeentry *oldscope = scopes; | 
|  | scopes = new_scopes; | 
|  | if (oldscope != default_scopes) | 
|  | free ((void *) oldscope); | 
|  |  | 
|  | save_gaiconf_mtime (&st); | 
|  | } | 
|  | else | 
|  | { | 
|  | no_file: | 
|  | free_prefixlist (labellist); | 
|  | free_prefixlist (precedencelist); | 
|  | free_scopelist (scopelist); | 
|  |  | 
|  | /* If we previously read the file but it is gone now, free the | 
|  | old data and use the builtin one.  Leave the reload flag | 
|  | alone.  */ | 
|  | fini (); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | gaiconf_reload (void) | 
|  | { | 
|  | struct stat64 st; | 
|  | if (__xstat64 (_STAT_VER, GAICONF_FNAME, &st) != 0 | 
|  | || !check_gaiconf_mtime (&st)) | 
|  | gaiconf_init (); | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | getaddrinfo (const char *name, const char *service, | 
|  | const struct addrinfo *hints, struct addrinfo **pai) | 
|  | { | 
|  | int i = 0, last_i = 0; | 
|  | int nresults = 0; | 
|  | struct addrinfo *p = NULL; | 
|  | struct gaih_service gaih_service, *pservice; | 
|  | struct addrinfo local_hints; | 
|  |  | 
|  | if (name != NULL && name[0] == '*' && name[1] == 0) | 
|  | name = NULL; | 
|  |  | 
|  | if (service != NULL && service[0] == '*' && service[1] == 0) | 
|  | service = NULL; | 
|  |  | 
|  | if (name == NULL && service == NULL) | 
|  | return EAI_NONAME; | 
|  |  | 
|  | if (hints == NULL) | 
|  | hints = &default_hints; | 
|  |  | 
|  | if (hints->ai_flags | 
|  | & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED | 
|  | #ifdef HAVE_LIBIDN | 
|  | |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED | 
|  | |AI_IDN_USE_STD3_ASCII_RULES | 
|  | #endif | 
|  | |AI_NUMERICSERV|AI_ALL)) | 
|  | return EAI_BADFLAGS; | 
|  |  | 
|  | if ((hints->ai_flags & AI_CANONNAME) && name == NULL) | 
|  | return EAI_BADFLAGS; | 
|  |  | 
|  | struct in6addrinfo *in6ai = NULL; | 
|  | size_t in6ailen = 0; | 
|  | bool seen_ipv4 = false; | 
|  | bool seen_ipv6 = false; | 
|  | bool check_pf_called = false; | 
|  |  | 
|  | if (hints->ai_flags & AI_ADDRCONFIG) | 
|  | { | 
|  | /* We might need information about what interfaces are available. | 
|  | Also determine whether we have IPv4 or IPv6 interfaces or both.  We | 
|  | cannot cache the results since new interfaces could be added at | 
|  | any time.  */ | 
|  | __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); | 
|  | check_pf_called = true; | 
|  |  | 
|  | /* Now make a decision on what we return, if anything.  */ | 
|  | if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6)) | 
|  | { | 
|  | /* If we haven't seen both IPv4 and IPv6 interfaces we can | 
|  | narrow down the search.  */ | 
|  | if ((! seen_ipv4 || ! seen_ipv6) && (seen_ipv4 || seen_ipv6)) | 
|  | { | 
|  | local_hints = *hints; | 
|  | local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6; | 
|  | hints = &local_hints; | 
|  | } | 
|  | } | 
|  | else if ((hints->ai_family == PF_INET && ! seen_ipv4) | 
|  | || (hints->ai_family == PF_INET6 && ! seen_ipv6)) | 
|  | { | 
|  | /* We cannot possibly return a valid answer.  */ | 
|  | __free_in6ai (in6ai); | 
|  | return EAI_NONAME; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (service && service[0]) | 
|  | { | 
|  | char *c; | 
|  | gaih_service.name = service; | 
|  | gaih_service.num = strtoul (gaih_service.name, &c, 10); | 
|  | if (*c != '\0') | 
|  | { | 
|  | if (hints->ai_flags & AI_NUMERICSERV) | 
|  | { | 
|  | __free_in6ai (in6ai); | 
|  | return EAI_NONAME; | 
|  | } | 
|  |  | 
|  | gaih_service.num = -1; | 
|  | } | 
|  |  | 
|  | pservice = &gaih_service; | 
|  | } | 
|  | else | 
|  | pservice = NULL; | 
|  |  | 
|  | struct addrinfo **end = &p; | 
|  |  | 
|  | unsigned int naddrs = 0; | 
|  | if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET | 
|  | || hints->ai_family == AF_INET6) | 
|  | { | 
|  | last_i = gaih_inet (name, pservice, hints, end, &naddrs); | 
|  | if (last_i != 0) | 
|  | { | 
|  | freeaddrinfo (p); | 
|  | __free_in6ai (in6ai); | 
|  |  | 
|  | return -last_i; | 
|  | } | 
|  | while (*end) | 
|  | { | 
|  | end = &((*end)->ai_next); | 
|  | ++nresults; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | __free_in6ai (in6ai); | 
|  | return EAI_FAMILY; | 
|  | } | 
|  |  | 
|  | if (naddrs > 1) | 
|  | { | 
|  | /* Read the config file.  */ | 
|  | __libc_once_define (static, once); | 
|  | __typeof (once) old_once = once; | 
|  | __libc_once (once, gaiconf_init); | 
|  | /* Sort results according to RFC 3484.  */ | 
|  | struct sort_result *results; | 
|  | size_t *order; | 
|  | struct addrinfo *q; | 
|  | struct addrinfo *last = NULL; | 
|  | char *canonname = NULL; | 
|  | bool malloc_results; | 
|  | size_t alloc_size = nresults * (sizeof (*results) + sizeof (size_t)); | 
|  |  | 
|  | malloc_results | 
|  | = !__libc_use_alloca (alloc_size); | 
|  | if (malloc_results) | 
|  | { | 
|  | results = malloc (alloc_size); | 
|  | if (results == NULL) | 
|  | { | 
|  | __free_in6ai (in6ai); | 
|  | return EAI_MEMORY; | 
|  | } | 
|  | } | 
|  | else | 
|  | results = alloca (alloc_size); | 
|  | order = (size_t *) (results + nresults); | 
|  |  | 
|  | /* Now we definitely need the interface information.  */ | 
|  | if (! check_pf_called) | 
|  | __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); | 
|  |  | 
|  | /* If we have information about deprecated and temporary addresses | 
|  | sort the array now.  */ | 
|  | if (in6ai != NULL) | 
|  | qsort (in6ai, in6ailen, sizeof (*in6ai), in6aicmp); | 
|  |  | 
|  | int fd = -1; | 
|  | int af = AF_UNSPEC; | 
|  |  | 
|  | for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next) | 
|  | { | 
|  | results[i].dest_addr = q; | 
|  | results[i].native = -1; | 
|  | order[i] = i; | 
|  |  | 
|  | /* If we just looked up the address for a different | 
|  | protocol, reuse the result.  */ | 
|  | if (last != NULL && last->ai_addrlen == q->ai_addrlen | 
|  | && memcmp (last->ai_addr, q->ai_addr, q->ai_addrlen) == 0) | 
|  | { | 
|  | memcpy (&results[i].source_addr, &results[i - 1].source_addr, | 
|  | results[i - 1].source_addr_len); | 
|  | results[i].source_addr_len = results[i - 1].source_addr_len; | 
|  | results[i].got_source_addr = results[i - 1].got_source_addr; | 
|  | results[i].source_addr_flags = results[i - 1].source_addr_flags; | 
|  | results[i].prefixlen = results[i - 1].prefixlen; | 
|  | results[i].index = results[i - 1].index; | 
|  | } | 
|  | else | 
|  | { | 
|  | results[i].got_source_addr = false; | 
|  | results[i].source_addr_flags = 0; | 
|  | results[i].prefixlen = 0; | 
|  | results[i].index = 0xffffffffu; | 
|  |  | 
|  | /* We overwrite the type with SOCK_DGRAM since we do not | 
|  | want connect() to connect to the other side.  If we | 
|  | cannot determine the source address remember this | 
|  | fact.  */ | 
|  | if (fd == -1 || (af == AF_INET && q->ai_family == AF_INET6)) | 
|  | { | 
|  | if (fd != -1) | 
|  | close_retry: | 
|  | close_not_cancel_no_status (fd); | 
|  | af = q->ai_family; | 
|  | fd = __socket (af, SOCK_DGRAM, IPPROTO_IP); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Reset the connection.  */ | 
|  | struct sockaddr sa = { .sa_family = AF_UNSPEC }; | 
|  | __connect (fd, &sa, sizeof (sa)); | 
|  | } | 
|  |  | 
|  | socklen_t sl = sizeof (results[i].source_addr); | 
|  | if (fd != -1 | 
|  | && __connect (fd, q->ai_addr, q->ai_addrlen) == 0 | 
|  | && __getsockname (fd, | 
|  | (struct sockaddr *) &results[i].source_addr, | 
|  | &sl) == 0) | 
|  | { | 
|  | results[i].source_addr_len = sl; | 
|  | results[i].got_source_addr = true; | 
|  |  | 
|  | if (in6ai != NULL) | 
|  | { | 
|  | /* See whether the source address is on the list of | 
|  | deprecated or temporary addresses.  */ | 
|  | struct in6addrinfo tmp; | 
|  |  | 
|  | if (q->ai_family == AF_INET && af == AF_INET) | 
|  | { | 
|  | struct sockaddr_in *sinp | 
|  | = (struct sockaddr_in *) &results[i].source_addr; | 
|  | tmp.addr[0] = 0; | 
|  | tmp.addr[1] = 0; | 
|  | tmp.addr[2] = htonl (0xffff); | 
|  | /* Special case for lo interface, the source address | 
|  | being possibly different than the interface | 
|  | address. */ | 
|  | if ((ntohl(sinp->sin_addr.s_addr) & 0xff000000) | 
|  | == 0x7f000000) | 
|  | tmp.addr[3] = htonl(0x7f000001); | 
|  | else | 
|  | tmp.addr[3] = sinp->sin_addr.s_addr; | 
|  | } | 
|  | else | 
|  | { | 
|  | struct sockaddr_in6 *sin6p | 
|  | = (struct sockaddr_in6 *) &results[i].source_addr; | 
|  | memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ); | 
|  | } | 
|  |  | 
|  | struct in6addrinfo *found | 
|  | = bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai), | 
|  | in6aicmp); | 
|  | if (found != NULL) | 
|  | { | 
|  | results[i].source_addr_flags = found->flags; | 
|  | results[i].prefixlen = found->prefixlen; | 
|  | results[i].index = found->index; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (q->ai_family == AF_INET && af == AF_INET6) | 
|  | { | 
|  | /* We have to convert the address.  The socket is | 
|  | IPv6 and the request is for IPv4.  */ | 
|  | struct sockaddr_in6 *sin6 | 
|  | = (struct sockaddr_in6 *) &results[i].source_addr; | 
|  | struct sockaddr_in *sin | 
|  | = (struct sockaddr_in *) &results[i].source_addr; | 
|  | assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32)); | 
|  | sin->sin_family = AF_INET; | 
|  | /* We do not have to initialize sin_port since this | 
|  | fields has the same position and size in the IPv6 | 
|  | structure.  */ | 
|  | assert (offsetof (struct sockaddr_in, sin_port) | 
|  | == offsetof (struct sockaddr_in6, sin6_port)); | 
|  | assert (sizeof (sin->sin_port) | 
|  | == sizeof (sin6->sin6_port)); | 
|  | memcpy (&sin->sin_addr, | 
|  | &sin6->sin6_addr.s6_addr32[3], INADDRSZ); | 
|  | results[i].source_addr_len = sizeof (struct sockaddr_in); | 
|  | } | 
|  | } | 
|  | else if (errno == EAFNOSUPPORT && af == AF_INET6 | 
|  | && q->ai_family == AF_INET) | 
|  | /* This could mean IPv6 sockets are IPv6-only.  */ | 
|  | goto close_retry; | 
|  | else | 
|  | /* Just make sure that if we have to process the same | 
|  | address again we do not copy any memory.  */ | 
|  | results[i].source_addr_len = 0; | 
|  | } | 
|  |  | 
|  | /* Remember the canonical name.  */ | 
|  | if (q->ai_canonname != NULL) | 
|  | { | 
|  | assert (canonname == NULL); | 
|  | canonname = q->ai_canonname; | 
|  | q->ai_canonname = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (fd != -1) | 
|  | close_not_cancel_no_status (fd); | 
|  |  | 
|  | /* We got all the source addresses we can get, now sort using | 
|  | the information.  */ | 
|  | struct sort_result_combo src | 
|  | = { .results = results, .nresults = nresults }; | 
|  | if (__glibc_unlikely (gaiconf_reload_flag_ever_set)) | 
|  | { | 
|  | __libc_lock_define_initialized (static, lock); | 
|  |  | 
|  | __libc_lock_lock (lock); | 
|  | if (__libc_once_get (old_once) && gaiconf_reload_flag) | 
|  | gaiconf_reload (); | 
|  | __qsort_r (order, nresults, sizeof (order[0]), rfc3484_sort, &src); | 
|  | __libc_lock_unlock (lock); | 
|  | } | 
|  | else | 
|  | __qsort_r (order, nresults, sizeof (order[0]), rfc3484_sort, &src); | 
|  |  | 
|  | /* Queue the results up as they come out of sorting.  */ | 
|  | q = p = results[order[0]].dest_addr; | 
|  | for (i = 1; i < nresults; ++i) | 
|  | q = q->ai_next = results[order[i]].dest_addr; | 
|  | q->ai_next = NULL; | 
|  |  | 
|  | /* Fill in the canonical name into the new first entry.  */ | 
|  | p->ai_canonname = canonname; | 
|  |  | 
|  | if (malloc_results) | 
|  | free (results); | 
|  | } | 
|  |  | 
|  | __free_in6ai (in6ai); | 
|  |  | 
|  | if (p) | 
|  | { | 
|  | *pai = p; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return last_i ? -last_i : EAI_NONAME; | 
|  | } | 
|  | libc_hidden_def (getaddrinfo) | 
|  |  | 
|  | nss_interface_function (getaddrinfo) | 
|  |  | 
|  | void | 
|  | freeaddrinfo (struct addrinfo *ai) | 
|  | { | 
|  | struct addrinfo *p; | 
|  |  | 
|  | while (ai != NULL) | 
|  | { | 
|  | p = ai; | 
|  | ai = ai->ai_next; | 
|  | free (p->ai_canonname); | 
|  | free (p); | 
|  | } | 
|  | } | 
|  | libc_hidden_def (freeaddrinfo) |