summaryrefslogtreecommitdiffstats
path: root/newlib/libc/sys/linux/net/getaddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/sys/linux/net/getaddrinfo.c')
-rw-r--r--newlib/libc/sys/linux/net/getaddrinfo.c3416
1 files changed, 1568 insertions, 1848 deletions
diff --git a/newlib/libc/sys/linux/net/getaddrinfo.c b/newlib/libc/sys/linux/net/getaddrinfo.c
index 97ba36209..742d46c8f 100644
--- a/newlib/libc/sys/linux/net/getaddrinfo.c
+++ b/newlib/libc/sys/linux/net/getaddrinfo.c
@@ -1,1979 +1,1699 @@
-/* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */
-
-/*
- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project 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 THE PROJECT 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 PROJECT 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.
- */
-
-/*
- * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator.
- *
- * Issues to be discussed:
- * - Thread safe-ness must be checked.
- * - Return values. There are nonstandard return values defined and used
- * in the source code. This is because RFC2553 is silent about which error
- * code must be returned for which situation.
- * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is
- * invalid.
- * current code - SEGV on freeaddrinfo(NULL)
- * Note:
- * - We use getipnodebyname() just for thread-safeness. There's no intent
- * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to
- * getipnodebyname().
- * - The code filters out AFs that are not supported by the kernel,
- * when globbing NULL hostname (to loopback, or wildcard). Is it the right
- * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG
- * in ai_flags?
- * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague.
- * (1) what should we do against numeric hostname (2) what should we do
- * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready?
- * non-loopback address configured? global address configured?
- * - To avoid search order issue, we have a big amount of code duplicate
- * from gethnamaddr.c and some other places. The issues that there's no
- * lower layer function to lookup "IPv4 or IPv6" record. Calling
- * gethostbyname2 from getaddrinfo will end up in wrong search order, as
- * follows:
- * - The code makes use of following calls when asked to resolver with
- * ai_family = PF_UNSPEC:
- * getipnodebyname(host, AF_INET6);
- * getipnodebyname(host, AF_INET);
- * This will result in the following queries if the node is configure to
- * prefer /etc/hosts than DNS:
- * lookup /etc/hosts for IPv6 address
- * lookup DNS for IPv6 address
- * lookup /etc/hosts for IPv4 address
- * lookup DNS for IPv4 address
- * which may not meet people's requirement.
- * The right thing to happen is to have underlying layer which does
- * PF_UNSPEC lookup (lookup both) and return chain of addrinfos.
- * This would result in a bit of code duplicate with _dns_ghbyname() and
- * friends.
- */
-/*
- * diffs with other KAME platforms:
- * - other KAME platforms already nuked FAITH ($GAI), but as FreeBSD
- * 4.0-RELEASE supplies it, we still have the code here.
- * - AI_ADDRCONFIG support is supplied
- * - some of FreeBSD style (#define tabify and others)
- * - classful IPv4 numeric (127.1) is allowed.
- */
-
-#include <sys/cdefs.h>
-#include <machine/endian.h>
-
-#include "namespace.h"
+/* 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 <alloca.h>
#include <sys/types.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
+#include <assert.h>
+#include <errno.h>
+#include <ifaddrs.h>
#include <netdb.h>
#include <resolv.h>
-#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
-#include <ctype.h>
+#include <string.h>
#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#ifdef DEBUG
-#include <syslog.h>
-#endif
-
-#include <syslog.h>
-#include <stdarg.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+#include <net/if.h>
#include <nsswitch.h>
-#include "un-namespace.h"
-
-#if defined(__KAME__) && defined(INET6)
-# define FAITH
+#include <not-cancel.h>
+#include <nscd/nscd-client.h>
+#include <nscd/nscd_proto.h>
+#include "local.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
-#define SUCCESS 0
-#define ANY 0
-#define YES 1
-#define NO 0
+#define GAIH_OKIFUNSPEC 0x0100
+#define GAIH_EAI ~(GAIH_OKIFUNSPEC)
-static const char in_addrany[] = { 0, 0, 0, 0 };
-static const char in6_addrany[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-static const char in_loopback[] = { 127, 0, 0, 1 };
-static const char in6_loopback[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
-};
-
-static const struct afd {
- int a_af;
- int a_addrlen;
- int a_socklen;
- int a_off;
- const char *a_addrany;
- const char *a_loopback;
- int a_scoped;
-} afdl [] = {
-#ifdef INET6
-#define N_INET6 0
- {PF_INET6, sizeof(struct in6_addr),
- sizeof(struct sockaddr_in6),
- offsetof(struct sockaddr_in6, sin6_addr),
- in6_addrany, in6_loopback, 1},
-#define N_INET 1
-#else
-#define N_INET 0
-#endif
- {PF_INET, sizeof(struct in_addr),
- sizeof(struct sockaddr_in),
- offsetof(struct sockaddr_in, sin_addr),
- in_addrany, in_loopback, 0},
- {0, 0, 0, 0, NULL, NULL, 0},
-};
-
-struct explore {
- int e_af;
- int e_socktype;
- int e_protocol;
- const char *e_protostr;
- int e_wild;
-#define WILD_AF(ex) ((ex)->e_wild & 0x01)
-#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
-#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
-};
-
-static const struct explore explore[] = {
-#if 0
- { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },
-#endif
-#ifdef INET6
- { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
- { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
- { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
-#endif
- { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
- { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
- { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
- { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
- { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
- { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
- { -1, 0, 0, NULL, 0 },
-};
-
-#ifdef INET6
-#define PTON_MAX 16
-#else
-#define PTON_MAX 4
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 108
#endif
-static const ns_src default_dns_files[] = {
- { NSSRC_FILES, NS_SUCCESS },
- { NSSRC_DNS, NS_SUCCESS },
- { 0 }
+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_addrtuple
+ {
+ struct gaih_addrtuple *next;
+ char *name;
+ int family;
+ uint32_t addr[4];
+ uint32_t scopeid;
+ };
+
+struct gaih_typeproto
+ {
+ int socktype;
+ int protocol;
+ char name[4];
+ int protoflag;
+ };
+
+/* Values for `protoflag'. */
+#define GAI_PROTO_NOSERVICE 1
+#define GAI_PROTO_PROTOANY 2
+
+static const struct gaih_typeproto gaih_inet_typeproto[] =
+{
+ { 0, 0, "", 0 },
+ { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
+ { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
+ { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
+ { 0, 0, "", 0 }
};
-#if PACKETSZ > 1024
-#define MAXPACKET PACKETSZ
-#else
-#define MAXPACKET 1024
-#endif
+struct gaih
+ {
+ int family;
+ int (*gaih)(const char *name, const struct gaih_service *service,
+ const struct addrinfo *req, struct addrinfo **pai);
+ };
+
+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
+ };
+
+#define s6_addr32 __u6_addr.__u6_addr32
-typedef union {
- HEADER hdr;
- u_char buf[MAXPACKET];
-} querybuf;
-
-struct res_target {
- struct res_target *next;
- const char *name; /* domain name */
- int qclass, qtype; /* class and type of query */
- u_char *answer; /* buffer to put answer */
- int anslen; /* size of answer buffer */
- int n; /* result length */
-};
+#if 0
+/* Using Unix sockets this way is a security risk. */
+static int
+gaih_local (const char *name, const struct gaih_service *service,
+ const struct addrinfo *req, struct addrinfo **pai)
+{
+ struct utsname utsname;
+
+ if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
+ return GAIH_OKIFUNSPEC | -EAI_NONAME;
+
+ if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
+ if (uname (&utsname) < 0)
+ return -EAI_SYSTEM;
+
+ if (name != NULL)
+ {
+ if (strcmp(name, "localhost") &&
+ strcmp(name, "local") &&
+ strcmp(name, "unix") &&
+ strcmp(name, utsname.nodename))
+ return GAIH_OKIFUNSPEC | -EAI_NONAME;
+ }
+
+ if (req->ai_protocol || req->ai_socktype)
+ {
+ const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
+
+ while (tp->name[0]
+ && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 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 (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+ else
+ return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ }
+ }
+
+ *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
+ + ((req->ai_flags & AI_CANONNAME)
+ ? (strlen(utsname.nodename) + 1): 0));
+ if (*pai == NULL)
+ return -EAI_MEMORY;
+
+ (*pai)->ai_next = NULL;
+ (*pai)->ai_flags = req->ai_flags;
+ (*pai)->ai_family = AF_LOCAL;
+ (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
+ (*pai)->ai_protocol = req->ai_protocol;
+ (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
+ (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
+
+#if SALEN
+ ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
+ sizeof (struct sockaddr_un);
+#endif /* SALEN */
+
+ ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
+ memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
+
+ if (service)
+ {
+ struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
+
+ if (strchr (service->name, '/') != NULL)
+ {
+ if (strlen (service->name) >= sizeof (sunp->sun_path))
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
+
+ strcpy (sunp->sun_path, service->name);
+ }
+ else
+ {
+ if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
+ sizeof (sunp->sun_path))
+ return GAIH_OKIFUNSPEC | -EAI_SERVICE;
-static int str_isnumber(const char *);
-static int explore_fqdn(const struct addrinfo *, const char *,
- const char *, struct addrinfo **);
-static int explore_null(const struct addrinfo *,
- const char *, struct addrinfo **);
-static int explore_numeric(const struct addrinfo *, const char *,
- const char *, struct addrinfo **);
-static int explore_numeric_scope(const struct addrinfo *, const char *,
- const char *, struct addrinfo **);
-static int get_canonname(const struct addrinfo *,
- struct addrinfo *, const char *);
-static struct addrinfo *get_ai(const struct addrinfo *,
- const struct afd *, const char *);
-static int get_portmatch(const struct addrinfo *, const char *);
-static int get_port(struct addrinfo *, const char *, int);
-static const struct afd *find_afd(int);
-static int addrconfig(struct addrinfo *);
-#ifdef INET6
-static int ip6_str2scopeid(char *, struct sockaddr_in6 *);
-#endif
+ __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
+ }
+ }
+ else
+ {
+ /* This is a dangerous use of the interface since there is a time
+ window between the test for the file and the actual creation
+ (done by the caller) in which a file with the same name could
+ be created. */
+ char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
+
+ if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
+ 0) != 0
+ || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
+ return -EAI_SYSTEM;
+ }
+
+ if (req->ai_flags & AI_CANONNAME)
+ (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
+ + sizeof (struct sockaddr_un),
+ utsname.nodename);
+ else
+ (*pai)->ai_canonname = NULL;
+ return 0;
+}
+#endif /* 0 */
-static struct addrinfo *getanswer(const querybuf *, int, const char *, int,
- const struct addrinfo *);
-static int _dns_getaddrinfo(void *, void *, va_list);
-static void _sethtent(void);
-static void _endhtent(void);
-static struct addrinfo *_gethtent(const char *, const struct addrinfo *);
-static int _files_getaddrinfo(void *, void *, va_list);
-#ifdef YP
-static struct addrinfo *_yphostent(char *, const struct addrinfo *);
-static int _yp_getaddrinfo(void *, void *, va_list);
-extern int _yp_check(char **);
-#endif
+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 GAIH_OKIFUNSPEC | -EAI_SERVICE;
+ }
+ }
+ while (r);
-u_int16_t _getshort(const u_char *src);
-
-static int res_queryN(const char *, struct res_target *);
-static int res_searchN(const char *, struct res_target *);
-static int res_querydomainN(const char *, const char *,
- struct res_target *);
-
-static char *ai_errlist[] = {
- "Success",
- "Address family for hostname not supported", /* EAI_ADDRFAMILY */
- "Temporary failure in name resolution", /* EAI_AGAIN */
- "Invalid value for ai_flags", /* EAI_BADFLAGS */
- "Non-recoverable failure in name resolution", /* EAI_FAIL */
- "ai_family not supported", /* EAI_FAMILY */
- "Memory allocation failure", /* EAI_MEMORY */
- "No address associated with hostname", /* EAI_NODATA */
- "hostname nor servname provided, or not known", /* EAI_NONAME */
- "servname not supported for ai_socktype", /* EAI_SERVICE */
- "ai_socktype not supported", /* EAI_SOCKTYPE */
- "System error returned in errno", /* EAI_SYSTEM */
- "Invalid value for hints", /* EAI_BADHINTS */
- "Resolved protocol is unknown", /* EAI_PROTOCOL */
- "Unknown error", /* EAI_MAX */
-};
+ st->next = NULL;
+ st->socktype = tp->socktype;
+ st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+ ? req->ai_protocol : tp->protocol);
+ st->port = s->s_port;
-/* XXX macros that make external reference is BAD. */
-
-#define GET_AI(ai, afd, addr) \
-do { \
- /* external reference: pai, error, and label free */ \
- (ai) = get_ai(pai, (afd), (addr)); \
- if ((ai) == NULL) { \
- error = EAI_MEMORY; \
- goto free; \
- } \
-} while (/*CONSTCOND*/0)
-
-#define GET_PORT(ai, serv) \
-do { \
- /* external reference: error and label free */ \
- error = get_port((ai), (serv), 0); \
- if (error != 0) \
- goto free; \
-} while (/*CONSTCOND*/0)
-
-#define GET_CANONNAME(ai, str) \
-do { \
- /* external reference: pai, error and label free */ \
- error = get_canonname(pai, (ai), (str)); \
- if (error != 0) \
- goto free; \
-} while (/*CONSTCOND*/0)
-
-#define ERR(err) \
-do { \
- /* external reference: error, and label bad */ \
- error = (err); \
- goto bad; \
- /*NOTREACHED*/ \
-} while (/*CONSTCOND*/0)
-
-#define MATCH_FAMILY(x, y, w) \
- ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
-#define MATCH(x, y, w) \
- ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))
-
-char *
-gai_strerror(ecode)
- int ecode;
-{
- if (ecode < 0 || ecode > EAI_MAX)
- ecode = EAI_MAX;
- return ai_errlist[ecode];
+ return 0;
}
-void
-freeaddrinfo(ai)
- struct addrinfo *ai;
-{
- struct addrinfo *next;
-
- do {
- next = ai->ai_next;
- if (ai->ai_canonname)
- free(ai->ai_canonname);
- /* no need to free(ai->ai_addr) */
- free(ai);
- ai = next;
- } while (ai);
-}
+#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; \
+ tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
+ } \
+ if (status == NSS_STATUS_SUCCESS && rc == 0) \
+ h = &th; \
+ else \
+ h = NULL; \
+ if (rc != 0) \
+ { \
+ if (herrno == NETDB_INTERNAL) \
+ { \
+ h_errno = (herrno); \
+ return -EAI_SYSTEM; \
+ } \
+ 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; \
+ if (i == 0) \
+ { \
+ (*pat)->name = alloca (strlen (h->h_name) + 1); \
+ strcpy ((*pat)->name, h->h_name); \
+ } \
+ else \
+ (*pat)->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 = alloca (strlen (localcanon) + 1); \
+ strcpy (canon, localcanon); \
+ } \
+ \
+ if (_family == AF_INET6 && i > 0) \
+ got_ipv6 = true; \
+ } \
+ }
+
+
+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
-str_isnumber(p)
- const char *p;
+gaih_inet (const char *name, const struct gaih_service *service,
+ const struct addrinfo *req, struct addrinfo **pai)
{
- char *ep;
-
- if (*p == '\0')
- return NO;
- ep = NULL;
- (void)strtoul(p, &ep, 10);
- if (ep && *ep == '\0')
- return YES;
- else
- return NO;
-}
+ 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;
+
+ 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 (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
+ else
+ return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ }
+ }
+
+ if (service != NULL)
+ {
+ if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+ return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+
+ if (service->num < 0)
+ {
+ if (tp->name[0])
+ {
+ st = (struct gaih_servtuple *)
+ alloca (sizeof (struct gaih_servtuple));
+
+ 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 (sizeof (struct gaih_servtuple));
+
+ if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
+ {
+ if (rc & GAIH_OKIFUNSPEC)
+ continue;
+ return rc;
+ }
-int
-getaddrinfo(hostname, servname, hints, res)
- const char *hostname, *servname;
- const struct addrinfo *hints;
- struct addrinfo **res;
-{
- struct addrinfo sentinel;
- struct addrinfo *cur;
- int error = 0;
- struct addrinfo ai;
- struct addrinfo ai0;
- struct addrinfo *pai;
- const struct explore *ex;
-
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
- pai = &ai;
- pai->ai_flags = 0;
- pai->ai_family = PF_UNSPEC;
- pai->ai_socktype = ANY;
- pai->ai_protocol = ANY;
- pai->ai_addrlen = 0;
- pai->ai_canonname = NULL;
- pai->ai_addr = NULL;
- pai->ai_next = NULL;
-
- if (hostname == NULL && servname == NULL)
- return EAI_NONAME;
- if (hints) {
- /* error check for hints */
- if (hints->ai_addrlen || hints->ai_canonname ||
- hints->ai_addr || hints->ai_next)
- ERR(EAI_BADHINTS); /* xxx */
- if (hints->ai_flags & ~AI_MASK)
- ERR(EAI_BADFLAGS);
- switch (hints->ai_family) {
- case PF_UNSPEC:
- case PF_INET:
-#ifdef INET6
- case PF_INET6:
-#endif
- break;
- default:
- ERR(EAI_FAMILY);
- }
- memcpy(pai, hints, sizeof(*pai));
-
- /*
- * if both socktype/protocol are specified, check if they
- * are meaningful combination.
- */
- if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
- for (ex = explore; ex->e_af >= 0; ex++) {
- if (pai->ai_family != ex->e_af)
- continue;
- if (ex->e_socktype == ANY)
- continue;
- if (ex->e_protocol == ANY)
- continue;
- if (pai->ai_socktype == ex->e_socktype
- && pai->ai_protocol != ex->e_protocol) {
- ERR(EAI_BADHINTS);
- }
- }
+ *pst = newp;
+ pst = &(newp->next);
}
+ if (st == (struct gaih_servtuple *) &nullserv)
+ return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
+ }
}
-
- /*
- * post-2553: AI_ALL and AI_V4MAPPED are effective only against
- * AF_INET6 query. They needs to be ignored if specified in other
- * occassions.
- */
- switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) {
- case AI_V4MAPPED:
- case AI_ALL | AI_V4MAPPED:
- if (pai->ai_family != AF_INET6)
- pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
- break;
- case AI_ALL:
-#if 1
- /* illegal */
- ERR(EAI_BADFLAGS);
-#else
- pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
-#endif
- break;
+ else
+ {
+ if (req->ai_socktype || req->ai_protocol)
+ {
+ st = alloca (sizeof (struct gaih_servtuple));
+ st->next = NULL;
+ st->socktype = tp->socktype;
+ st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+ ? req->ai_protocol : tp->protocol);
+ st->port = htons (service->num);
+ }
+ else
+ {
+ /* Neither socket type nor protocol is set. Return all
+ socket types we know about. */
+ struct gaih_servtuple **lastp = &st;
+ for (tp = gaih_inet_typeproto + 1; tp->name[0]; ++tp)
+ if ((tp->protoflag & GAI_PROTO_NOSERVICE) == 0)
+ {
+ struct gaih_servtuple *newp;
+
+ newp = alloca (sizeof (struct gaih_servtuple));
+ newp->next = NULL;
+ newp->socktype = tp->socktype;
+ newp->protocol = tp->protocol;
+ newp->port = htons (service->num);
+
+ *lastp = newp;
+ lastp = &newp->next;
+ }
+ }
+ }
+ }
+ else if (req->ai_socktype || req->ai_protocol)
+ {
+ st = alloca (sizeof (struct gaih_servtuple));
+ st->next = NULL;
+ st->socktype = tp->socktype;
+ st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+ ? req->ai_protocol : tp->protocol);
+ st->port = 0;
+ }
+ 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)
+ {
+ struct gaih_servtuple *newp;
+
+ newp = alloca (sizeof (struct gaih_servtuple));
+ newp->next = NULL;
+ newp->socktype = tp->socktype;
+ newp->protocol = tp->protocol;
+ newp->port = 0;
+
+ *lastp = newp;
+ lastp = &newp->next;
+ }
+ }
+
+ if (name != NULL)
+ {
+ at = alloca (sizeof (struct gaih_addrtuple));
+
+ 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)
+ {
+ 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 = alloca (strlen (p) + 1);
+ strcpy (name, p);
+ free (p);
+ }
}
-
- /*
- * check for special cases. (1) numeric servname is disallowed if
- * socktype/protocol are left unspecified. (2) servname is disallowed
- * for raw and other inet{,6} sockets.
- */
- if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
-#ifdef PF_INET6
- || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
-#endif
- ) {
- ai0 = *pai; /* backup *pai */
-
- if (pai->ai_family == PF_UNSPEC) {
-#ifdef PF_INET6
- pai->ai_family = PF_INET6;
-#else
- pai->ai_family = PF_INET;
#endif
- }
- error = get_portmatch(pai, servname);
- if (error)
- ERR(error);
- *pai = ai0;
+ 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
+ return -EAI_ADDRFAMILY;
+
+ dupname:
+ if (req->ai_flags & AI_CANONNAME)
+ {
+ canon = strdup (name);
+ if (canon == NULL)
+ return -EAI_MEMORY;
+ }
}
- ai0 = *pai;
-
- /* NULL hostname, or numeric hostname */
- for (ex = explore; ex->e_af >= 0; ex++) {
- *pai = ai0;
-
- /* PF_UNSPEC entries are prepared for DNS queries only */
- if (ex->e_af == PF_UNSPEC)
- continue;
+ if (at->family == AF_UNSPEC)
+ {
+ char *scope_delim;
+ char *namebuf = alloca (strlen (name) + 1);
+ strcpy (namebuf, name);
+
+ scope_delim = strchr (namebuf, SCOPE_DELIMITER);
+ if (scope_delim != NULL)
+ *scope_delim = '\0';
+
+ if (inet_pton (AF_INET6, namebuf, at->addr) > 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 ((struct in6_addr *)at->addr))
+ {
+ at->addr[0] = at->addr[3];
+ at->family = AF_INET;
+ }
+ else
+ return -EAI_ADDRFAMILY;
+
+ if (scope_delim != NULL)
+ {
+ int try_numericscope = 0;
+ if (IN6_IS_ADDR_LINKLOCAL ((struct in6_addr *)at->addr)
+ || IN6_IS_ADDR_MC_LINKLOCAL ((struct in6_addr *)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')
+ return GAIH_OKIFUNSPEC | -EAI_NONAME;
+ }
+ }
- if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
- continue;
- if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
- continue;
- if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
- continue;
+ goto dupname;
+ }
+ }
- if (pai->ai_family == PF_UNSPEC)
- pai->ai_family = ex->e_af;
- if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
- pai->ai_socktype = ex->e_socktype;
- if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
- pai->ai_protocol = ex->e_protocol;
+ 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 = NULL;
+ 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 IPv4 and IPv6 together, use
+ the simple, old functions. */
+ if (req->ai_family == AF_INET || req->ai_family == AF_INET6)
+ {
+ int family = req->ai_family;
+ size_t tmpbuflen = 512;
+ char *tmpbuf = alloca (tmpbuflen);
+ int rc;
+ struct hostent th;
+ struct hostent *h;
+ int herrno;
+
+ simple_again:
+ while (1)
+ {
+ rc = __gethostbyname2_r (name, family, &th, tmpbuf,
+ tmpbuflen, &h, &herrno);
+ if (rc != ERANGE || herrno != NETDB_INTERNAL)
+ break;
+ tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
+ }
- if (hostname == NULL)
- error = explore_null(pai, servname, &cur->ai_next);
- else
- error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
+ if (rc == 0)
+ {
+ if (h == NULL)
+ {
+ if (req->ai_family == AF_INET6
+ && (req->ai_flags & AI_V4MAPPED)
+ && family == AF_INET6)
+ {
+ /* Try again, this time looking for IPv4
+ addresses. */
+ family = AF_INET;
+ goto simple_again;
+ }
+ }
+ else
+ {
+ /* We found data, now convert it into the list. */
+ int i = 0;
+ for (i = 0; h->h_addr_list[i]; ++i)
+ {
+ if (*pat == NULL)
+ {
+ *pat = alloca (sizeof (struct gaih_addrtuple));
+ (*pat)->scopeid = 0;
+ }
+ (*pat)->next = NULL;
+ (*pat)->family = req->ai_family;
+ if (family == req->ai_family)
+ memcpy ((*pat)->addr, h->h_addr_list[i],
+ h->h_length);
+ else
+ {
+ int32_t *addr = (uint32_t *) (*pat)->addr;
+ addr[3] = *(uint32_t *) h->h_addr_list[i];
+ addr[2] = htonl (0xffff);
+ addr[1] = 0;
+ addr[0] = 0;
+ }
+ pat = &((*pat)->next);
+ }
+ }
+ }
+ else
+ {
+ if (herrno == NETDB_INTERNAL)
+ {
+ h_errno = (herrno);
+ return -EAI_SYSTEM;
+ }
+ if (herrno == TRY_AGAIN)
+ {
+ return -EAI_AGAIN;
+ }
+ /* We made requests but they turned out no data.
+ The name is known, though. */
+ return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ }
- if (error)
- goto free;
+ 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)
+ {
+ /* 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;
+
+ for (int i = 0; i < air->naddrs; ++i)
+ {
+ socklen_t size = (air->family[i] == AF_INET
+ ? INADDRSZ : IN6ADDRSZ);
+ if (*pat == NULL)
+ {
+ *pat = alloca (sizeof (struct gaih_addrtuple));
+ (*pat)->scopeid = 0;
+ }
+ uint32_t *pataddr = (*pat)->addr;
+ (*pat)->next = NULL;
+ if (added_canon || air->canon == NULL)
+ (*pat)->name = NULL;
+ else {
+ canon = (*pat)->name = alloca (strlen (air->canon) + 1);
+ strcpy (canon, air->canon);
+ }
+
+ 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;
+ }
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
+ free (air);
- /*
- * XXX
- * If numreic representation of AF1 can be interpreted as FQDN
- * representation of AF2, we need to think again about the code below.
- */
- if (sentinel.ai_next)
- goto good;
-
- if (pai->ai_flags & AI_NUMERICHOST)
- ERR(EAI_NONAME);
- if (hostname == NULL)
- ERR(EAI_NODATA);
-
- if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0))
- ERR(EAI_FAIL);
-
- /*
- * hostname as alphabetical name.
- * we would like to prefer AF_INET6 than AF_INET, so we'll make a
- * outer loop by AFs.
- */
- for (ex = explore; ex->e_af >= 0; ex++) {
- *pai = ai0;
-
- /* require exact match for family field */
- if (pai->ai_family != ex->e_af)
- continue;
+ if (at->family == AF_UNSPEC)
+ return (GAIH_OKIFUNSPEC | -EAI_NONAME);
- if (!MATCH(pai->ai_socktype, ex->e_socktype,
- WILD_SOCKTYPE(ex))) {
- continue;
+ goto process_list;
}
- if (!MATCH(pai->ai_protocol, ex->e_protocol,
- WILD_PROTOCOL(ex))) {
- continue;
+ else if (err != 0 && __nss_not_use_nscd_hosts == 0)
+ {
+ if (herrno == NETDB_INTERNAL && errno == ENOMEM)
+ return -EAI_MEMORY;
+ if (herrno == TRY_AGAIN)
+ return -EAI_AGAIN;
+ return -EAI_SYSTEM;
}
+ }
+#endif
- if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
- pai->ai_socktype = ex->e_socktype;
- if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
- pai->ai_protocol = ex->e_protocol;
-
- error = explore_fqdn(pai, hostname, servname,
- &cur->ai_next);
-
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
+ if (__nss_hosts_database != NULL)
+ {
+ no_more = 0;
+ nip = __nss_hosts_database;
+ }
+ else
+ no_more = __nss_database_lookup ("hosts", NULL,
+ "dns [!UNAVAIL=return] files",
+ &nip);
+
+ 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 = 512;
+ char *tmpbuf = alloca (tmpbuflen);
+
+ while (!no_more)
+ {
+ 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;
+ char *buf = alloca (max_fqdn_len);
+ char *s;
+
+ if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
+ max_fqdn_len, &s, &rc,
+ &herrno))
+ == NSS_STATUS_SUCCESS)
+ canon = s;
+ else
+ /* Set to name now to avoid using
+ gethostbyaddr. */
+ canon = name;
+ }
+ }
- /* XXX */
- if (sentinel.ai_next)
- error = 0;
-
- if (error)
- goto free;
- if (error == 0) {
- if (sentinel.ai_next) {
- good:
- *res = sentinel.ai_next;
- return SUCCESS;
- } else
- error = EAI_FAIL;
- }
- free:
- bad:
- if (sentinel.ai_next)
- freeaddrinfo(sentinel.ai_next);
- *res = NULL;
- return error;
-}
+ break;
+ }
-/*
- * FQDN hostname, DNS lookup
- */
-static int
-explore_fqdn(pai, hostname, servname, res)
- const struct addrinfo *pai;
- const char *hostname;
- const char *servname;
- struct addrinfo **res;
-{
- struct addrinfo *result;
- struct addrinfo *cur;
- int error = 0;
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_files_getaddrinfo, NULL)
- { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */
- NS_NIS_CB(_yp_getaddrinfo, NULL)
- { 0 }
- };
-
- result = NULL;
-
- /*
- * if the servname does not match socktype/protocol, ignore it.
- */
- if (get_portmatch(pai, servname) != 0)
- return 0;
-
- switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",
- default_dns_files, hostname, pai)) {
- case NS_TRYAGAIN:
- error = EAI_AGAIN;
- goto free;
- case NS_UNAVAIL:
- error = EAI_FAIL;
- goto free;
- case NS_NOTFOUND:
- error = EAI_NODATA;
- goto free;
- case NS_SUCCESS:
- error = 0;
- for (cur = result; cur; cur = cur->ai_next) {
- GET_PORT(cur, servname);
- /* canonname should be filled already */
+ /* 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;
}
+
+ if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
break;
- }
- *res = result;
+ if (nip->next == NULL)
+ no_more = -1;
+ else
+ nip = nip->next;
+ }
- return 0;
+ _res.options = old_res_options;
-free:
- if (result)
- freeaddrinfo(result);
- return error;
-}
+ 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)
+ return -EAI_AGAIN;
-/*
- * hostname == NULL.
- * passive socket -> anyaddr (0.0.0.0 or ::)
- * non-passive socket -> localhost (127.0.0.1 or ::1)
- */
-static int
-explore_null(pai, servname, res)
- const struct addrinfo *pai;
- const char *servname;
- struct addrinfo **res;
-{
- int s;
- const struct afd *afd;
- struct addrinfo *cur;
- struct addrinfo sentinel;
- int error;
-
- *res = NULL;
- sentinel.ai_next = NULL;
- cur = &sentinel;
-
- /*
- * filter out AFs that are not supported by the kernel
- * XXX errno?
- */
- s = socket(pai->ai_family, SOCK_DGRAM, 0);
- if (s < 0) {
- if (errno != EMFILE)
- return 0;
- } else
- close(s);
-
- /*
- * if the servname does not match socktype/protocol, ignore it.
- */
- if (get_portmatch(pai, servname) != 0)
- return 0;
-
- afd = find_afd(pai->ai_family);
- if (afd == NULL)
- return 0;
-
- if (pai->ai_flags & AI_PASSIVE) {
- GET_AI(cur->ai_next, afd, afd->a_addrany);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "anyaddr");
- */
- GET_PORT(cur->ai_next, servname);
- } else {
- GET_AI(cur->ai_next, afd, afd->a_loopback);
- /* xxx meaningless?
- * GET_CANONNAME(cur->ai_next, "localhost");
- */
- GET_PORT(cur->ai_next, servname);
+ /* We made requests but they turned out no data. The name
+ is known, though. */
+ return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+ }
}
- cur = cur->ai_next;
-
- *res = sentinel.ai_next;
- return 0;
-free:
- if (sentinel.ai_next)
- freeaddrinfo(sentinel.ai_next);
- return error;
-}
-
-/*
- * numeric hostname
- */
-static int
-explore_numeric(pai, hostname, servname, res)
- const struct addrinfo *pai;
- const char *hostname;
- const char *servname;
- struct addrinfo **res;
-{
- const struct afd *afd;
- struct addrinfo *cur;
- struct addrinfo sentinel;
- int error;
- char pton[PTON_MAX];
-
- *res = NULL;
- sentinel.ai_next = NULL;
- cur = &sentinel;
-
- /*
- * if the servname does not match socktype/protocol, ignore it.
- */
- if (get_portmatch(pai, servname) != 0)
- return 0;
-
- afd = find_afd(pai->ai_family);
- if (afd == NULL)
- return 0;
-
- switch (afd->a_af) {
-#if 1 /*X/Open spec*/
- case AF_INET:
- if (inet_aton(hostname, (struct in_addr *)pton) == 1) {
- if (pai->ai_family == afd->a_af ||
- pai->ai_family == PF_UNSPEC /*?*/) {
- GET_AI(cur->ai_next, afd, pton);
- GET_PORT(cur->ai_next, servname);
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- } else
- ERR(EAI_FAMILY); /*xxx*/
- }
- break;
-#endif
- default:
- if (inet_pton(afd->a_af, hostname, pton) == 1) {
- if (pai->ai_family == afd->a_af ||
- pai->ai_family == PF_UNSPEC /*?*/) {
- GET_AI(cur->ai_next, afd, pton);
- GET_PORT(cur->ai_next, servname);
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- } else
- ERR(EAI_FAMILY); /*xxx*/
- }
- break;
+ process_list:
+ if (at->family == AF_UNSPEC)
+ return (GAIH_OKIFUNSPEC | -EAI_NONAME);
+ }
+ else
+ {
+ struct gaih_addrtuple *atr;
+ atr = at = alloca (sizeof (struct gaih_addrtuple));
+ 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));
}
- *res = sentinel.ai_next;
- return 0;
+ 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;
+ }
-free:
-bad:
- if (sentinel.ai_next)
- freeaddrinfo(sentinel.ai_next);
- return error;
+ 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);
+ }
+ }
+
+ if (pai == NULL)
+ return 0;
+
+ {
+ 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)
+ {
+ struct hostent *h = NULL;
+ int herrno;
+ struct hostent th;
+ size_t tmpbuflen = 512;
+ char *tmpbuf = NULL;
+
+ do
+ {
+ tmpbuf = extend_alloca (tmpbuf, tmpbuflen, tmpbuflen * 2);
+ rc = __gethostbyaddr_r (at2->addr,
+ ((at2->family == AF_INET6)
+ ? sizeof (struct in6_addr)
+ : sizeof (struct in_addr)),
+ at2->family, &th, tmpbuf,
+ tmpbuflen, &h, &herrno);
+ }
+ while (rc == ERANGE && herrno == NETDB_INTERNAL);
+
+ if (rc != 0 && herrno == NETDB_INTERNAL)
+ {
+ h_errno = (herrno);
+ return -EAI_SYSTEM;
+ }
+
+ if (h != NULL)
+ canon = h->h_name;
+ else
+ {
+ assert (orig_name != 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)
+ 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. Otherwise
+ make a copy. */
+ if (out == canon)
+ goto make_copy;
+ }
+ else
+#endif
+ {
+#ifdef HAVE_LIBIDN
+ make_copy:
+#endif
+ canon = strdup (canon);
+ if (canon == NULL)
+ return -EAI_MEMORY;
+ }
+ }
+
+ if (at2->family == AF_INET6)
+ {
+ 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 ((struct in6_addr *)at2->addr))
+ goto ignore;
+ }
+ else
+ {
+ family = AF_INET;
+ 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)
+ return -EAI_MEMORY;
+
+ 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;
+
+#if SALEN
+ ai->ai_addr->sa_len = socklen;
+#endif /* SALEN */
+ ai->ai_addr->sa_family = family;
+
+ 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);
+ }
+ *pai = NULL;
+
+ ignore:
+ at2 = at2->next;
+ }
+ }
+ return 0;
}
-/*
- * numeric hostname with scope
- */
-static int
-explore_numeric_scope(pai, hostname, servname, res)
- const struct addrinfo *pai;
- const char *hostname;
- const char *servname;
- struct addrinfo **res;
-{
-#if !defined(SCOPE_DELIMITER) || !defined(INET6)
- return explore_numeric(pai, hostname, servname, res);
-#else
- const struct afd *afd;
- struct addrinfo *cur;
- int error;
- char *cp, *hostname2 = NULL, *scope, *addr;
- struct sockaddr_in6 *sin6;
-
- /*
- * if the servname does not match socktype/protocol, ignore it.
- */
- if (get_portmatch(pai, servname) != 0)
- return 0;
-
- afd = find_afd(pai->ai_family);
- if (afd == NULL)
- return 0;
-
- if (!afd->a_scoped)
- return explore_numeric(pai, hostname, servname, res);
-
- cp = strchr(hostname, SCOPE_DELIMITER);
- if (cp == NULL)
- return explore_numeric(pai, hostname, servname, res);
-
- /*
- * Handle special case of <scoped_address><delimiter><scope id>
- */
- hostname2 = strdup(hostname);
- if (hostname2 == NULL)
- return EAI_MEMORY;
- /* terminate at the delimiter */
- hostname2[cp - hostname] = '\0';
- addr = hostname2;
- scope = cp + 1;
-
- error = explore_numeric(pai, addr, servname, res);
- if (error == 0) {
- int scopeid;
-
- for (cur = *res; cur; cur = cur->ai_next) {
- if (cur->ai_family != AF_INET6)
- continue;
- sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
- if ((scopeid = ip6_str2scopeid(scope, sin6)) == -1) {
- free(hostname2);
- return(EAI_NODATA); /* XXX: is return OK? */
- }
- sin6->sin6_scope_id = scopeid;
- }
- }
+static struct gaih gaih[] =
+ {
+ { PF_INET6, gaih_inet },
+ { PF_INET, gaih_inet },
+#if 0
+ { PF_LOCAL, gaih_local },
+#endif
+ { PF_UNSPEC, NULL }
+ };
- free(hostname2);
+struct sort_result
+{
+ struct addrinfo *dest_addr;
+ struct sockaddr_storage source_addr;
+ uint8_t source_addr_len;
+ bool got_source_addr;
+};
- return error;
-#endif
-}
static int
-get_canonname(pai, ai, str)
- const struct addrinfo *pai;
- struct addrinfo *ai;
- const char *str;
+get_scope (const struct sockaddr_storage *ss)
{
- if ((pai->ai_flags & AI_CANONNAME) != 0) {
- ai->ai_canonname = (char *)malloc(strlen(str) + 1);
- if (ai->ai_canonname == NULL)
- return EAI_MEMORY;
- strcpy(ai->ai_canonname, str);
+ int scope;
+ if (ss->ss_family == PF_INET6)
+ {
+ const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) ss;
+
+ if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
+ {
+ if (IN6_IS_ADDR_LINKLOCAL (&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;
}
- return 0;
+ else
+ scope = in6->sin6_addr.s6_addr[1] & 0xf;
+ }
+ else if (ss->ss_family == PF_INET)
+ {
+ const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
+ const uint8_t *addr = (const uint8_t *) &in->sin_addr;
+
+ /* RFC 3484 specifies how to map IPv6 addresses to scopes.
+ 169.254/16 and 127/8 are link-local. */
+ if ((addr[0] == 169 && addr[1] == 254) || addr[0] == 127)
+ scope = 2;
+ else if (addr[0] == 10 || (addr[0] == 172 && addr[1] == 16)
+ || (addr[0] == 192 && addr[1] == 168))
+ scope = 5;
+ else
+ scope = 14;
+ }
+ else
+ /* XXX What is a good default? */
+ scope = 15;
+
+ return scope;
}
-static struct addrinfo *
-get_ai(pai, afd, addr)
- const struct addrinfo *pai;
- const struct afd *afd;
- const char *addr;
-{
- char *p;
- struct addrinfo *ai;
-#ifdef FAITH
- struct in6_addr faith_prefix;
- char *fp_str;
- int translate = 0;
-#endif
-
-#ifdef FAITH
- /*
- * Transfrom an IPv4 addr into a special IPv6 addr format for
- * IPv6->IPv4 translation gateway. (only TCP is supported now)
- *
- * +-----------------------------------+------------+
- * | faith prefix part (12 bytes) | embedded |
- * | | IPv4 addr part (4 bytes)
- * +-----------------------------------+------------+
- *
- * faith prefix part is specified as ascii IPv6 addr format
- * in environmental variable GAI.
- * For FAITH to work correctly, routing to faith prefix must be
- * setup toward a machine where a FAITH daemon operates.
- * Also, the machine must enable some mechanizm
- * (e.g. faith interface hack) to divert those packet with
- * faith prefixed destination addr to user-land FAITH daemon.
- */
- fp_str = getenv("GAI");
- if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 &&
- afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) {
- u_int32_t v4a;
- u_int8_t v4a_top;
-
- memcpy(&v4a, addr, sizeof v4a);
- v4a_top = v4a >> IN_CLASSA_NSHIFT;
- if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) &&
- v4a_top != 0 && v4a != IN_LOOPBACKNET) {
- afd = &afdl[N_INET6];
- memcpy(&faith_prefix.s6_addr[12], addr,
- sizeof(struct in_addr));
- translate = 1;
- }
- }
-#endif
-
- ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)
- + (afd->a_socklen));
- if (ai == NULL)
- return NULL;
-
- memcpy(ai, pai, sizeof(struct addrinfo));
- ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
- memset(ai->ai_addr, 0, (size_t)afd->a_socklen);
- ai->ai_addrlen = afd->a_socklen;
- ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
- p = (char *)(void *)(ai->ai_addr);
-#ifdef FAITH
- if (translate == 1)
- memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen);
- else
-#endif
- memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);
- return ai;
-}
-static int
-get_portmatch(ai, servname)
- const struct addrinfo *ai;
- const char *servname;
+/* XXX The system administrator should be able to install other
+ tables. We need to make this configurable. The problem is that
+ the kernel is also involved since it needs the same table. */
+static const struct prefixlist
{
+ struct in6_addr prefix;
+ unsigned int bits;
+ int val;
+} default_labels[] =
+ {
+ /* See RFC 3484 for the details. */
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0001 } } },
+ 128, 0 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 16, 2 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 96, 3 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xffff, 0x0000, 0x0000 } } },
+ 96, 4 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 0, 1 }
+ };
+
+
+static const struct prefixlist default_precedence[] =
+ {
+ /* See RFC 3484 for the details. */
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0001 } } },
+ 128, 50 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x2002, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 16, 30 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 96, 20 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0xffff, 0x0000, 0x0000 } } },
+ 96, 10 },
+ { { .__u6_addr = { .__u6_addr16 = { 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000 } } },
+ 0, 40 }
+ };
- /* get_port does not touch first argument. when matchonly == 1. */
- /* LINTED const cast */
- return get_port((struct addrinfo *)ai, servname, 1);
-}
static int
-get_port(ai, servname, matchonly)
- struct addrinfo *ai;
- const char *servname;
- int matchonly;
+match_prefix (const struct sockaddr_storage *ss, const struct prefixlist *list,
+ int default_val)
{
- const char *proto;
- struct servent *sp;
- int port;
- int allownumeric;
-
- if (servname == NULL)
- return 0;
- switch (ai->ai_family) {
- case AF_INET:
-#ifdef AF_INET6
- case AF_INET6:
-#endif
- break;
- default:
- return 0;
+ int idx;
+ struct sockaddr_in6 in6_mem;
+ const struct sockaddr_in6 *in6;
+
+ if (ss->ss_family == PF_INET6)
+ in6 = (const struct sockaddr_in6 *) ss;
+ else if (ss->ss_family == PF_INET)
+ {
+ const struct sockaddr_in *in = (const struct sockaddr_in *) ss;
+
+ /* Convert to IPv6 address. */
+ in6_mem.sin6_family = PF_INET6;
+ in6_mem.sin6_port = in->sin_port;
+ in6_mem.sin6_flowinfo = 0;
+ if (in->sin_addr.s_addr == htonl (0x7f000001))
+ in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
+ else
+ {
+ /* Construct a V4-to-6 mapped address. */
+ memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
+ in6_mem.sin6_addr.__u6_addr.__u6_addr16[5] = 0xffff;
+ in6_mem.sin6_addr.__u6_addr.__u6_addr32[3] = in->sin_addr.s_addr;
+ in6_mem.sin6_scope_id = 0;
}
- switch (ai->ai_socktype) {
- case SOCK_RAW:
- return EAI_SERVICE;
- case SOCK_DGRAM:
- case SOCK_STREAM:
- allownumeric = 1;
- break;
- case ANY:
- allownumeric = 0;
- break;
- default:
- return EAI_SOCKTYPE;
- }
-
- if (str_isnumber(servname)) {
- if (!allownumeric)
- return EAI_SERVICE;
- port = htons(atoi(servname));
- if (port < 0 || port > 65535)
- return EAI_SERVICE;
- } else {
- switch (ai->ai_socktype) {
- case SOCK_DGRAM:
- proto = "udp";
- break;
- case SOCK_STREAM:
- proto = "tcp";
- break;
- default:
- proto = NULL;
- break;
- }
-
- if ((sp = getservbyname(servname, proto)) == NULL)
- return EAI_SERVICE;
- port = sp->s_port;
+ in6 = &in6_mem;
+ }
+ else
+ return default_val;
+
+ for (idx = 0; ; ++idx)
+ {
+ unsigned int bits = list[idx].bits;
+ uint8_t *mask = list[idx].prefix.s6_addr;
+ uint8_t *val = in6->sin6_addr.s6_addr;
+
+ while (bits > 8)
+ {
+ if (*mask != *val)
+ break;
+
+ ++mask;
+ ++val;
+ bits -= 8;
}
- if (!matchonly) {
- switch (ai->ai_family) {
- case AF_INET:
- ((struct sockaddr_in *)(void *)
- ai->ai_addr)->sin_port = port;
- break;
-#ifdef INET6
- case AF_INET6:
- ((struct sockaddr_in6 *)(void *)
- ai->ai_addr)->sin6_port = port;
- break;
-#endif
- }
+ if (bits < 8)
+ {
+ if ((*mask & (0xff00 >> bits)) == (*val & (0xff00 >> bits)))
+ /* Match! */
+ break;
}
+ }
- return 0;
+ return list[idx].val;
}
-static const struct afd *
-find_afd(af)
- int af;
-{
- const struct afd *afd;
- if (af == PF_UNSPEC)
- return NULL;
- for (afd = afdl; afd->a_af; afd++) {
- if (afd->a_af == af)
- return afd;
- }
- return NULL;
+static int
+get_label (const struct sockaddr_storage *ss)
+{
+ /* XXX What is a good default value? */
+ return match_prefix (ss, default_labels, INT_MAX);
}
-/*
- * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend
- * will take care of it.
- * the semantics of AI_ADDRCONFIG is not defined well. we are not sure
- * if the code is right or not.
- *
- * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with
- * _dns_getaddrinfo.
- */
+
static int
-addrconfig(pai)
- struct addrinfo *pai;
+get_precedence (const struct sockaddr_storage *ss)
{
- int s, af;
-
- /*
- * TODO:
- * Note that implementation dependent test for address
- * configuration should be done everytime called
- * (or apropriate interval),
- * because addresses will be dynamically assigned or deleted.
- */
- af = pai->ai_family;
- if (af == AF_UNSPEC) {
- if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- af = AF_INET;
- else {
- close(s);
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
- af = AF_INET6;
- else
- close(s);
- }
- }
- if (af != AF_UNSPEC) {
- if ((s = socket(af, SOCK_DGRAM, 0)) < 0)
- return 0;
- close(s);
- }
- pai->ai_family = af;
- return 1;
+ /* XXX What is a good default value? */
+ return match_prefix (ss, default_precedence, 0);
}
-#ifdef INET6
-/* convert a string to a scope identifier. XXX: IPv6 specific */
+
static int
-ip6_str2scopeid(scope, sin6)
- char *scope;
- struct sockaddr_in6 *sin6;
+rfc3484_sort (const void *p1, const void *p2)
{
- int scopeid;
- struct in6_addr *a6 = &sin6->sin6_addr;
- char *ep;
-
- /* empty scopeid portion is invalid */
- if (*scope == '\0')
- return -1;
-
- if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {
- /*
- * We currently assume a one-to-one mapping between links
- * and interfaces, so we simply use interface indices for
- * like-local scopes.
- */
- scopeid = if_nametoindex(scope);
- if (scopeid == 0)
- goto trynumeric;
- return(scopeid);
- }
+ const struct sort_result *a1 = (const struct sort_result *) p1;
+ const struct sort_result *a2 = (const struct sort_result *) p2;
- /* still unclear about literal, allow numeric only - placeholder */
- if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6))
- goto trynumeric;
- if (IN6_IS_ADDR_MC_ORGLOCAL(a6))
- goto trynumeric;
- else
- goto trynumeric; /* global */
+ /* 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;
- /* try to convert to a numeric id as a last resort */
- trynumeric:
- scopeid = (int)strtoul(scope, &ep, 10);
- if (*ep == '\0')
- return scopeid;
- else
- return -1;
-}
-#endif
-#ifdef DEBUG
-static const char AskedForGot[] =
- "gethostby*.getanswer: asked for \"%s\", got \"%s\"";
-#endif
-static FILE *hostf = NULL;
-
-static struct addrinfo *
-getanswer(answer, anslen, qname, qtype, pai)
- const querybuf *answer;
- int anslen;
- const char *qname;
- int qtype;
- const struct addrinfo *pai;
-{
- struct addrinfo sentinel, *cur;
- struct addrinfo ai;
- const struct afd *afd;
- char *canonname;
- const HEADER *hp;
- const u_char *cp;
- int n;
- const u_char *eom;
- char *bp;
- int type, class, buflen, ancount, qdcount;
- int haveanswer, had_error;
- char tbuf[MAXDNAME];
- int (*name_ok)(const char *);
- char hostbuf[8*1024];
-
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
-
- canonname = NULL;
- eom = answer->buf + anslen;
- switch (qtype) {
- case T_A:
- case T_AAAA:
- case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/
- name_ok = res_hnok;
- break;
- default:
- return (NULL); /* XXX should be abort(); */
- }
- /*
- * find first satisfactory answer
- */
- hp = &answer->hdr;
- ancount = ntohs(hp->ancount);
- qdcount = ntohs(hp->qdcount);
- bp = hostbuf;
- buflen = sizeof hostbuf;
- cp = answer->buf + HFIXEDSZ;
- if (qdcount != 1) {
- h_errno = NO_RECOVERY;
- return (NULL);
- }
- n = dn_expand(answer->buf, eom, cp, bp, buflen);
- if ((n < 0) || !(*name_ok)(bp)) {
- h_errno = NO_RECOVERY;
- return (NULL);
- }
- cp += n + QFIXEDSZ;
- if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
- /* res_send() has already verified that the query name is the
- * same as the one we sent; this just gets the expanded name
- * (i.e., with the succeeding search-domain tacked on).
- */
- n = strlen(bp) + 1; /* for the \0 */
- if (n >= MAXHOSTNAMELEN) {
- h_errno = NO_RECOVERY;
- return (NULL);
- }
- canonname = bp;
- bp += n;
- buflen -= n;
- /* The qname can be abbreviated, but h_name is now absolute. */
- qname = canonname;
- }
- haveanswer = 0;
- had_error = 0;
- while (ancount-- > 0 && cp < eom && !had_error) {
- n = dn_expand(answer->buf, eom, cp, bp, buflen);
- if ((n < 0) || !(*name_ok)(bp)) {
- had_error++;
- continue;
- }
- cp += n; /* name */
- type = _getshort(cp);
- cp += INT16SZ; /* type */
- class = _getshort(cp);
- cp += INT16SZ + INT32SZ; /* class, TTL */
- n = _getshort(cp);
- cp += INT16SZ; /* len */
- if (class != C_IN) {
- /* XXX - debug? syslog? */
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
- if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&
- type == T_CNAME) {
- n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
- if ((n < 0) || !(*name_ok)(tbuf)) {
- had_error++;
- continue;
- }
- cp += n;
- /* Get canonical name. */
- n = strlen(tbuf) + 1; /* for the \0 */
- if (n > buflen || n >= MAXHOSTNAMELEN) {
- had_error++;
- continue;
- }
- strcpy(bp, tbuf);
- canonname = bp;
- bp += n;
- buflen -= n;
- continue;
- }
- if (qtype == T_ANY) {
- if (!(type == T_A || type == T_AAAA)) {
- cp += n;
- continue;
- }
- } else if (type != qtype) {
-#ifdef DEBUG
- if (type != T_KEY && type != T_SIG)
- syslog(LOG_NOTICE|LOG_AUTH,
- "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
- qname, p_class(C_IN), p_type(qtype),
- p_type(type));
-#endif
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
- switch (type) {
- case T_A:
- case T_AAAA:
- if (strcasecmp(canonname, bp) != 0) {
-#ifdef DEBUG
- syslog(LOG_NOTICE|LOG_AUTH,
- AskedForGot, canonname, bp);
-#endif
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
- if (type == T_A && n != INADDRSZ) {
- cp += n;
- continue;
- }
- if (type == T_AAAA && n != IN6ADDRSZ) {
- cp += n;
- continue;
- }
-#ifdef FILTER_V4MAPPED
- if (type == T_AAAA) {
- struct in6_addr in6;
- memcpy(&in6, cp, sizeof(in6));
- if (IN6_IS_ADDR_V4MAPPED(&in6)) {
- cp += n;
- continue;
- }
- }
-#endif
- if (!haveanswer) {
- int nn;
+ /* Rule 2: Prefer matching scope. Only interesting if both
+ destination addresses are IPv6. */
+ int a1_dst_scope
+ = get_scope ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
- canonname = bp;
- nn = strlen(bp) + 1; /* for the \0 */
- bp += nn;
- buflen -= nn;
- }
+ int a2_dst_scope
+ = get_scope ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
- /* don't overwrite pai */
- ai = *pai;
- ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;
- afd = find_afd(ai.ai_family);
- if (afd == NULL) {
- cp += n;
- continue;
- }
- cur->ai_next = get_ai(&ai, afd, (const char *)cp);
- if (cur->ai_next == NULL)
- had_error++;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- cp += n;
- break;
- default:
- abort();
- }
- if (!had_error)
- haveanswer++;
- }
- if (haveanswer) {
- if (!canonname)
- (void)get_canonname(pai, sentinel.ai_next, qname);
- else
- (void)get_canonname(pai, sentinel.ai_next, canonname);
- h_errno = NETDB_SUCCESS;
- return sentinel.ai_next;
- }
+ if (a1->got_source_addr)
+ {
+ int a1_src_scope = get_scope (&a1->source_addr);
+ int a2_src_scope = get_scope (&a2->source_addr);
- h_errno = NO_RECOVERY;
- return NULL;
-}
+ 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;
+ }
-/*ARGSUSED*/
-static int
-_dns_getaddrinfo(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
-{
- struct addrinfo *ai;
- querybuf buf, buf2;
- const char *name;
- const struct addrinfo *pai;
- struct addrinfo sentinel, *cur;
- struct res_target q, q2;
-
- name = va_arg(ap, char *);
- pai = va_arg(ap, const struct addrinfo *);
-
- memset(&q, 0, sizeof(q2));
- memset(&q2, 0, sizeof(q2));
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
-
- switch (pai->ai_family) {
- case AF_UNSPEC:
- /* prefer IPv6 */
- q.qclass = C_IN;
- q.qtype = T_AAAA;
- q.answer = buf.buf;
- q.anslen = sizeof(buf);
- q.next = &q2;
- q2.qclass = C_IN;
- q2.qtype = T_A;
- q2.answer = buf2.buf;
- q2.anslen = sizeof(buf2);
- break;
- case AF_INET:
- q.qclass = C_IN;
- q.qtype = T_A;
- q.answer = buf.buf;
- q.anslen = sizeof(buf);
- break;
- case AF_INET6:
- q.qclass = C_IN;
- q.qtype = T_AAAA;
- q.answer = buf.buf;
- q.anslen = sizeof(buf);
- break;
- default:
- return NS_UNAVAIL;
- }
- if (res_searchN(name, &q) < 0)
- return NS_NOTFOUND;
- ai = getanswer(&buf, q.n, q.name, q.qtype, pai);
- if (ai) {
- cur->ai_next = ai;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
- if (q.next) {
- ai = getanswer(&buf2, q2.n, q2.name, q2.qtype, pai);
- if (ai)
- cur->ai_next = ai;
- }
- if (sentinel.ai_next == NULL)
- switch (h_errno) {
- case HOST_NOT_FOUND:
- return NS_NOTFOUND;
- case TRY_AGAIN:
- return NS_TRYAGAIN;
- default:
- return NS_UNAVAIL;
- }
- *((struct addrinfo **)rv) = sentinel.ai_next;
- return NS_SUCCESS;
-}
-static void
-_sethtent()
-{
- if (!hostf)
- hostf = fopen(_PATH_HOSTS, "r" );
- else
- rewind(hostf);
-}
+ /* Rule 3: Avoid deprecated addresses.
+ That's something only the kernel could decide. */
-static void
-_endhtent()
-{
- if (hostf) {
- (void) fclose(hostf);
- hostf = NULL;
- }
-}
+ /* Rule 4: Prefer home addresses.
+ Another thing only the kernel can decide. */
-static struct addrinfo *
-_gethtent(name, pai)
- const char *name;
- const struct addrinfo *pai;
-{
- char *p;
- char *cp, *tname, *cname;
- struct addrinfo hints, *res0, *res;
- int error;
- const char *addr;
- char hostbuf[8*1024];
-
- if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" )))
- return (NULL);
- again:
- if (!(p = fgets(hostbuf, sizeof hostbuf, hostf)))
- return (NULL);
- if (*p == '#')
- goto again;
- if (!(cp = strpbrk(p, "#\n")))
- goto again;
- *cp = '\0';
- if (!(cp = strpbrk(p, " \t")))
- goto again;
- *cp++ = '\0';
- addr = p;
- cname = NULL;
- /* if this is not something we're looking for, skip it. */
- while (cp && *cp) {
- if (*cp == ' ' || *cp == '\t') {
- cp++;
- continue;
- }
- tname = cp;
- if (cname == NULL)
- cname = cp;
- if ((cp = strpbrk(cp, " \t")) != NULL)
- *cp++ = '\0';
- if (strcasecmp(name, tname) == 0)
- goto found;
- }
- goto again;
-
-found:
- hints = *pai;
- hints.ai_flags = AI_NUMERICHOST;
- error = getaddrinfo(addr, NULL, &hints, &res0);
- if (error)
- goto again;
-#ifdef FILTER_V4MAPPED
- /* XXX should check all items in the chain */
- if (res0->ai_family == AF_INET6 &&
- IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) {
- freeaddrinfo(res0);
- goto again;
- }
-#endif
- for (res = res0; res; res = res->ai_next) {
- /* cover it up */
- res->ai_flags = pai->ai_flags;
-
- if (pai->ai_flags & AI_CANONNAME) {
- if (get_canonname(pai, res, cname) != 0) {
- freeaddrinfo(res0);
- goto again;
- }
- }
- }
- return res0;
-}
+ /* Rule 5: Prefer matching label. */
+ if (a1->got_source_addr)
+ {
+ int a1_dst_label
+ = get_label ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
+ int a1_src_label = get_label (&a1->source_addr);
-/*ARGSUSED*/
-static int
-_files_getaddrinfo(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
-{
- const char *name;
- const struct addrinfo *pai;
- struct addrinfo sentinel, *cur;
- struct addrinfo *p;
-
- name = va_arg(ap, char *);
- pai = va_arg(ap, struct addrinfo *);
-
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
-
- _sethtent();
- while ((p = _gethtent(name, pai)) != NULL) {
- cur->ai_next = p;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
- _endhtent();
+ int a2_dst_label
+ = get_label ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
+ int a2_src_label = get_label (&a2->source_addr);
- *((struct addrinfo **)rv) = sentinel.ai_next;
- if (sentinel.ai_next == NULL)
- return NS_NOTFOUND;
- return NS_SUCCESS;
-}
+ 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;
+ }
-#ifdef YP
-static char *__ypdomain;
-/*ARGSUSED*/
-static struct addrinfo *
-_yphostent(line, pai)
- char *line;
- const struct addrinfo *pai;
-{
- struct addrinfo sentinel, *cur;
- struct addrinfo hints, *res, *res0;
- int error;
- char *p = line;
- const char *addr, *canonname;
- char *nextline;
- char *cp;
-
- addr = canonname = NULL;
-
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
-
-nextline:
- /* terminate line */
- cp = strchr(p, '\n');
- if (cp) {
- *cp++ = '\0';
- nextline = cp;
- } else
- nextline = NULL;
-
- cp = strpbrk(p, " \t");
- if (cp == NULL) {
- if (canonname == NULL)
- return (NULL);
- else
- goto done;
- }
- *cp++ = '\0';
+ /* Rule 6: Prefer higher precedence. */
+ int a1_prec
+ = get_precedence ((struct sockaddr_storage *) a1->dest_addr->ai_addr);
+ int a2_prec
+ = get_precedence ((struct sockaddr_storage *) a2->dest_addr->ai_addr);
- addr = p;
+ if (a1_prec > a2_prec)
+ return -1;
+ if (a1_prec < a2_prec)
+ return 1;
- while (cp && *cp) {
- if (*cp == ' ' || *cp == '\t') {
- cp++;
- continue;
- }
- if (!canonname)
- canonname = cp;
- if ((cp = strpbrk(cp, " \t")) != NULL)
- *cp++ = '\0';
- }
- hints = *pai;
- hints.ai_flags = AI_NUMERICHOST;
- error = getaddrinfo(addr, NULL, &hints, &res0);
- if (error == 0) {
- for (res = res0; res; res = res->ai_next) {
- /* cover it up */
- res->ai_flags = pai->ai_flags;
+ /* Rule 7: Prefer native transport.
+ XXX How to recognize tunnels? */
- if (pai->ai_flags & AI_CANONNAME)
- (void)get_canonname(pai, res, canonname);
- }
- } else
- res0 = NULL;
- if (res0) {
- cur->ai_next = res0;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
- if (nextline) {
- p = nextline;
- goto nextline;
- }
+ /* Rule 8: Prefer smaller scope. */
+ if (a1_dst_scope < a2_dst_scope)
+ return -1;
+ if (a1_dst_scope > a2_dst_scope)
+ return 1;
-done:
- return sentinel.ai_next;
-}
-/*ARGSUSED*/
-static int
-_yp_getaddrinfo(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
-{
- struct addrinfo sentinel, *cur;
- struct addrinfo *ai = NULL;
- static char *__ypcurrent;
- int __ypcurrentlen, r;
- const char *name;
- const struct addrinfo *pai;
-
- name = va_arg(ap, char *);
- pai = va_arg(ap, const struct addrinfo *);
-
- memset(&sentinel, 0, sizeof(sentinel));
- cur = &sentinel;
-
- if (!__ypdomain) {
- if (_yp_check(&__ypdomain) == 0)
- return NS_UNAVAIL;
- }
- if (__ypcurrent)
- free(__ypcurrent);
- __ypcurrent = NULL;
-
- /* hosts.byname is only for IPv4 (Solaris8) */
- if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) {
- r = yp_match(__ypdomain, "hosts.byname", name,
- (int)strlen(name), &__ypcurrent, &__ypcurrentlen);
- if (r == 0) {
- struct addrinfo ai4;
-
- ai4 = *pai;
- ai4.ai_family = AF_INET;
- ai = _yphostent(__ypcurrent, &ai4);
- if (ai) {
- cur->ai_next = ai;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
- }
- }
+ /* 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;
- /* ipnodes.byname can hold both IPv4/v6 */
- r = yp_match(__ypdomain, "ipnodes.byname", name,
- (int)strlen(name), &__ypcurrent, &__ypcurrentlen);
- if (r == 0) {
- ai = _yphostent(__ypcurrent, pai);
- if (ai) {
- cur->ai_next = ai;
- while (cur && cur->ai_next)
- cur = cur->ai_next;
- }
- }
+ if (a1->dest_addr->ai_family == PF_INET)
+ {
+ assert (a1->source_addr.ss_family == PF_INET);
+ assert (a2->source_addr.ss_family == PF_INET);
- if (sentinel.ai_next == NULL) {
- h_errno = HOST_NOT_FOUND;
- return NS_NOTFOUND;
- }
- *((struct addrinfo **)rv) = sentinel.ai_next;
- return NS_SUCCESS;
-}
-#endif
+ struct sockaddr_in *in1_dst;
+ struct sockaddr_in *in1_src;
+ struct sockaddr_in *in2_dst;
+ struct sockaddr_in *in2_src;
-/* resolver logic */
-
-extern const char *__hostalias(const char *);
-extern int h_errno;
-
-/*
- * Formulate a normal query, send, and await answer.
- * Returned answer is placed in supplied buffer "answer".
- * Perform preliminary check of answer, returning success only
- * if no error is indicated and the answer count is nonzero.
- * Return the size of the response on success, -1 on error.
- * Error number is left in h_errno.
- *
- * Caller must parse answer and determine whether it answers the question.
- */
-static int
-res_queryN(name, target)
- const char *name; /* domain name */
- struct res_target *target;
-{
- u_char buf[MAXPACKET];
- HEADER *hp;
- int n;
- struct res_target *t;
- int rcode;
- int ancount;
-
- rcode = NOERROR;
- ancount = 0;
-
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
- h_errno = NETDB_INTERNAL;
- return (-1);
+ in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
+ in1_src = (struct sockaddr_in *) &a1->source_addr;
+ in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
+ in2_src = (struct sockaddr_in *) &a2->source_addr;
+
+ bit1 = ffs (in1_dst->sin_addr.s_addr ^ in1_src->sin_addr.s_addr);
+ bit2 = ffs (in2_dst->sin_addr.s_addr ^ in2_src->sin_addr.s_addr);
+ }
+ else if (a1->dest_addr->ai_family == PF_INET6)
+ {
+ assert (a1->source_addr.ss_family == PF_INET6);
+ assert (a2->source_addr.ss_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 = ffs (in1_dst->sin6_addr.s6_addr32[i]
+ ^ in1_src->sin6_addr.s6_addr32[i]);
+ bit2 = ffs (in2_dst->sin6_addr.s6_addr32[i]
+ ^ in2_src->sin6_addr.s6_addr32[i]);
+ }
}
- for (t = target; t; t = t->next) {
- int class, type;
- u_char *answer;
- int anslen;
-
- hp = (HEADER *)(void *)t->answer;
- hp->rcode = NOERROR; /* default */
-
- /* make it easier... */
- class = t->qclass;
- type = t->qtype;
- answer = t->answer;
- anslen = t->anslen;
-#ifdef DEBUG
- if (_res.options & RES_DEBUG)
- printf(";; res_query(%s, %d, %d)\n", name, class, type);
-#endif
+ if (bit1 > bit2)
+ return -1;
+ if (bit1 < bit2)
+ return 1;
+ }
- n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
- buf, sizeof(buf));
- if (n > 0 && (_res.options & RES_USE_EDNS0) != 0)
- n = res_opt(n, buf, sizeof(buf), anslen);
- if (n <= 0) {
-#ifdef DEBUG
- if (_res.options & RES_DEBUG)
- printf(";; res_query: mkquery failed\n");
-#endif
- h_errno = NO_RECOVERY;
- return (n);
- }
- n = res_send(buf, n, answer, anslen);
-#if 0
- if (n < 0) {
-#ifdef DEBUG
- if (_res.options & RES_DEBUG)
- printf(";; res_query: send error\n");
-#endif
- h_errno = TRY_AGAIN;
- return (n);
- }
-#endif
- if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
- rcode = hp->rcode; /* record most recent error */
-#ifdef DEBUG
- if (_res.options & RES_DEBUG)
- printf(";; rcode = %d, ancount=%d\n", hp->rcode,
- ntohs(hp->ancount));
-#endif
- continue;
- }
+ /* Rule 10: Otherwise, leave the order unchanged. */
+ return 0;
+}
- ancount += ntohs(hp->ancount);
- t->n = n;
+int
+getaddrinfo (const char *name, const char *service,
+ const struct addrinfo *hints, struct addrinfo **pai)
+{
+ int i = 0, j = 0, last_i = 0;
+ int nresults = 0;
+ struct addrinfo *p = NULL, **end;
+ struct gaih *g = gaih, *pg = 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;
+
+ if (hints->ai_flags & AI_ADDRCONFIG)
+ {
+ /* Determine whether we have IPv4 or IPv6 interfaces or both.
+ We cannot cache the results since new interfaces could be
+ added at any time. */
+ bool seen_ipv4;
+ bool seen_ipv6;
+ __check_pf (&seen_ipv4, &seen_ipv6);
+
+ /* 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)
+ {
+ 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. */
+ 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)
+ return EAI_NONAME;
+
+ gaih_service.num = -1;
}
- if (ancount == 0) {
- switch (rcode) {
- case NXDOMAIN:
- h_errno = HOST_NOT_FOUND;
- break;
- case SERVFAIL:
- h_errno = TRY_AGAIN;
- break;
- case NOERROR:
- h_errno = NO_DATA;
- break;
- case FORMERR:
- case NOTIMP:
- case REFUSED:
- default:
- h_errno = NO_RECOVERY;
- break;
+ pservice = &gaih_service;
+ }
+ else
+ pservice = NULL;
+
+ if (pai)
+ end = &p;
+ else
+ end = NULL;
+
+ while (g->gaih)
+ {
+ if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
+ {
+ j++;
+ if (pg == NULL || pg->gaih != g->gaih)
+ {
+ pg = g;
+ i = g->gaih (name, pservice, hints, end);
+ if (i != 0)
+ {
+ /* EAI_NODATA is a more specific result as it says that
+ we found a result but it is not usable. */
+ if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
+ last_i = i;
+
+ if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
+ {
+ ++g;
+ continue;
+ }
+
+ freeaddrinfo (p);
+
+ return -(i & GAIH_EAI);
}
- return (-1);
+ if (end)
+ while (*end)
+ {
+ end = &((*end)->ai_next);
+ ++nresults;
+ }
+ }
}
- return (ancount);
-}
-
-/*
- * Formulate a normal query, send, and retrieve answer in supplied buffer.
- * Return the size of the response on success, -1 on error.
- * If enabled, implement search rules until answer or unrecoverable failure
- * is detected. Error code, if any, is left in h_errno.
- */
-static int
-res_searchN(name, target)
- const char *name; /* domain name */
- struct res_target *target;
-{
- const char *cp, * const *domain;
- HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/
- u_int dots;
- int trailing_dot, ret, saved_herrno;
- int got_nodata = 0, got_servfail = 0, tried_as_is = 0;
-
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
- h_errno = NETDB_INTERNAL;
- return (-1);
+ ++g;
+ }
+
+ if (j == 0)
+ return EAI_FAMILY;
+
+ if (nresults > 1)
+ {
+ /* Sort results according to RFC 3484. */
+ struct sort_result results[nresults];
+ struct addrinfo *q;
+ struct addrinfo *last = NULL;
+ char *canonname = NULL;
+
+ for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
+ {
+ results[i].dest_addr = q;
+ results[i].got_source_addr = false;
+
+ /* 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;
+ }
+ else
+ {
+ /* 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. */
+ int fd = socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
+ 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;
+ }
+ 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;
+
+ if (fd != -1)
+ close_not_cancel_no_status (fd);
+ }
+
+ /* Remember the canonical name. */
+ if (q->ai_canonname != NULL)
+ {
+ assert (canonname == NULL);
+ canonname = q->ai_canonname;
+ q->ai_canonname = NULL;
+ }
}
- errno = 0;
- h_errno = HOST_NOT_FOUND; /* default, if we never query */
- dots = 0;
- for (cp = name; *cp; cp++)
- dots += (*cp == '.');
- trailing_dot = 0;
- if (cp > name && *--cp == '.')
- trailing_dot++;
-
- /*
- * if there aren't any dots, it could be a user-level alias
- */
- if (!dots && (cp = __hostalias(name)) != NULL)
- return (res_queryN(cp, target));
-
- /*
- * If there are dots in the name already, let's just give it a try
- * 'as is'. The threshold can be set with the "ndots" option.
- */
- saved_herrno = -1;
- if (dots >= _res.ndots) {
- ret = res_querydomainN(name, NULL, target);
- if (ret > 0)
- return (ret);
- saved_herrno = h_errno;
- tried_as_is++;
- }
+ /* We got all the source addresses we can get, now sort using
+ the information. */
+ qsort (results, nresults, sizeof (results[0]), rfc3484_sort);
- /*
- * We do at least one level of search if
- * - there is no dot and RES_DEFNAME is set, or
- * - there is at least one dot, there is no trailing dot,
- * and RES_DNSRCH is set.
- */
- if ((!dots && (_res.options & RES_DEFNAMES)) ||
- (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
- int done = 0;
-
- for (domain = (const char * const *)_res.dnsrch;
- *domain && !done;
- domain++) {
-
- ret = res_querydomainN(name, *domain, target);
- if (ret > 0)
- return (ret);
-
- /*
- * If no server present, give up.
- * If name isn't found in this domain,
- * keep trying higher domains in the search list
- * (if that's enabled).
- * On a NO_DATA error, keep trying, otherwise
- * a wildcard entry of another type could keep us
- * from finding this entry higher in the domain.
- * If we get some other error (negative answer or
- * server failure), then stop searching up,
- * but try the input name below in case it's
- * fully-qualified.
- */
- if (errno == ECONNREFUSED) {
- h_errno = TRY_AGAIN;
- return (-1);
- }
+ /* Queue the results up as they come out of sorting. */
+ q = p = results[0].dest_addr;
+ for (i = 1; i < nresults; ++i)
+ q = q->ai_next = results[i].dest_addr;
+ q->ai_next = NULL;
- switch (h_errno) {
- case NO_DATA:
- got_nodata++;
- /* FALLTHROUGH */
- case HOST_NOT_FOUND:
- /* keep trying */
- break;
- case TRY_AGAIN:
- if (hp->rcode == SERVFAIL) {
- /* try next search element, if any */
- got_servfail++;
- break;
- }
- /* FALLTHROUGH */
- default:
- /* anything else implies that we're done */
- done++;
- }
- /*
- * if we got here for some reason other than DNSRCH,
- * we only wanted one iteration of the loop, so stop.
- */
- if (!(_res.options & RES_DNSRCH))
- done++;
- }
- }
+ /* Fill in the canonical name into the new first entry. */
+ p->ai_canonname = canonname;
+ }
- /*
- * if we have not already tried the name "as is", do that now.
- * note that we do this regardless of how many dots were in the
- * name or whether it ends with a dot.
- */
- if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) {
- ret = res_querydomainN(name, NULL, target);
- if (ret > 0)
- return (ret);
- }
+ if (p)
+ {
+ *pai = p;
+ return 0;
+ }
+
+ if (pai == NULL && last_i == 0)
+ return 0;
- /*
- * if we got here, we didn't satisfy the search.
- * if we did an initial full query, return that query's h_errno
- * (note that we wouldn't be here if that query had succeeded).
- * else if we ever got a nodata, send that back as the reason.
- * else send back meaningless h_errno, that being the one from
- * the last DNSRCH we did.
- */
- if (saved_herrno != -1)
- h_errno = saved_herrno;
- else if (got_nodata)
- h_errno = NO_DATA;
- else if (got_servfail)
- h_errno = TRY_AGAIN;
- return (-1);
+ return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
}
+libc_hidden_def (getaddrinfo)
-/*
- * Perform a call on res_query on the concatenation of name and domain,
- * removing a trailing dot from name if domain is NULL.
- */
-static int
-res_querydomainN(name, domain, target)
- const char *name, *domain;
- struct res_target *target;
-{
- char nbuf[MAXDNAME];
- const char *longname = nbuf;
- size_t n, d;
+static_link_warning (getaddrinfo)
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
- h_errno = NETDB_INTERNAL;
- return (-1);
- }
-#ifdef DEBUG
- if (_res.options & RES_DEBUG)
- printf(";; res_querydomain(%s, %s)\n",
- name, domain?domain:"<Nil>");
-#endif
- if (domain == NULL) {
- /*
- * Check for trailing '.';
- * copy without '.' if present.
- */
- n = strlen(name);
- if (n >= MAXDNAME) {
- h_errno = NO_RECOVERY;
- return (-1);
- }
- if (n > 0 && name[--n] == '.') {
- strncpy(nbuf, name, n);
- nbuf[n] = '\0';
- } else
- longname = name;
- } else {
- n = strlen(name);
- d = strlen(domain);
- if (n + d + 1 >= MAXDNAME) {
- h_errno = NO_RECOVERY;
- return (-1);
- }
- sprintf(nbuf, "%s.%s", name, domain);
- }
- return (res_queryN(longname, target));
+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)