diff options
-rw-r--r-- | include/machine/rtems-bsd-thread.h | 34 | ||||
-rw-r--r-- | libc/gai_strerror.c | 125 | ||||
-rw-r--r-- | libc/getaddrinfo.c | 3086 | ||||
-rw-r--r-- | libc/nsswitch.h | 246 | ||||
-rw-r--r-- | libc/vars.c | 47 | ||||
-rw-r--r-- | net/if_nametoindex.c | 111 | ||||
-rw-r--r-- | netsources.py | 7 | ||||
-rw-r--r-- | resolv.h | 3 | ||||
-rw-r--r-- | rtems/rtems-kernel-program.c | 86 | ||||
m--------- | rtems_waf | 0 | ||||
-rw-r--r-- | testsuites/resolve/getaddrinfo_test.c | 573 | ||||
-rw-r--r-- | testsuites/resolve/init.c | 176 | ||||
-rw-r--r-- | testsuites/resolve/resolve.doc | 11 | ||||
-rw-r--r-- | testsuites/resolve/resolve.scn | 166 | ||||
-rw-r--r-- | testsuites/resolve/wscript | 49 | ||||
-rw-r--r-- | testsuites/wscript | 2 |
16 files changed, 4719 insertions, 3 deletions
diff --git a/include/machine/rtems-bsd-thread.h b/include/machine/rtems-bsd-thread.h new file mode 100644 index 0000000..ed3e8b3 --- /dev/null +++ b/include/machine/rtems-bsd-thread.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore <kinsey.moore@oarcorp.com> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +#ifndef _RTEMSLWIP_RTEMS_BSD_THREAD_H +#define _RTEMSLWIP_RTEMS_BSD_THREAD_H +#include <sys/param.h> +#include <rtems.h> +#include <rtems/score/thread.h> + +#define BSD_TASK_NAME rtems_build_name('_', 'B', 'S', 'D') +#endif diff --git a/libc/gai_strerror.c b/libc/gai_strerror.c new file mode 100644 index 0000000..070e237 --- /dev/null +++ b/libc/gai_strerror.c @@ -0,0 +1,125 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifndef __rtems__ +#include "namespace.h" +#endif /* __rtems */ +#include <netdb.h> +#if defined(NLS) +#include <nl_types.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "reentrant.h" +#endif +#ifndef __rtems__ +#include "un-namespace.h" +#endif /* __rtems */ + +/* Entries EAI_ADDRFAMILY (1) and EAI_NODATA (7) are obsoleted, but left */ +/* for backward compatibility with userland code prior to 2553bis-02 */ +static const char *ai_errlist[] = { + "Success", /* 0 */ + "Address family for hostname not supported", /* 1 */ + "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", /* 7 */ + "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 */ + "Argument buffer overflow" /* EAI_OVERFLOW */ +}; + +#if defined(NLS) +static char gai_buf[NL_TEXTMAX]; +static once_t gai_init_once = ONCE_INITIALIZER; +static thread_key_t gai_key; +static int gai_keycreated = 0; + +static void +gai_keycreate(void) +{ + gai_keycreated = (thr_keycreate(&gai_key, free) == 0); +} +#endif + +const char * +gai_strerror(int ecode) +{ +#if defined(NLS) + nl_catd catd; + char *buf; + + if (thr_main() != 0) + buf = gai_buf; + else { + if (thr_once(&gai_init_once, gai_keycreate) != 0 || + !gai_keycreated) + goto thr_err; + if ((buf = thr_getspecific(gai_key)) == NULL) { + if ((buf = malloc(sizeof(gai_buf))) == NULL) + goto thr_err; + if (thr_setspecific(gai_key, buf) != 0) { + free(buf); + goto thr_err; + } + } + } + + catd = catopen("libc", NL_CAT_LOCALE); + if (ecode > 0 && ecode < EAI_MAX) + strlcpy(buf, catgets(catd, 3, ecode, ai_errlist[ecode]), + sizeof(gai_buf)); + else if (ecode == 0) + strlcpy(buf, catgets(catd, 3, NL_MSGMAX - 1, "Success"), + sizeof(gai_buf)); + else + strlcpy(buf, catgets(catd, 3, NL_MSGMAX, "Unknown error"), + sizeof(gai_buf)); + catclose(catd); + return buf; + +thr_err: +#endif + if (ecode >= 0 && ecode < EAI_MAX) + return ai_errlist[ecode]; + return "Unknown error"; +} diff --git a/libc/getaddrinfo.c b/libc/getaddrinfo.c new file mode 100644 index 0000000..707f13c --- /dev/null +++ b/libc/getaddrinfo.c @@ -0,0 +1,3086 @@ +/* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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. + */ + +/* + * Issues to be discussed: + * - 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 accepts NULL to be compatible with other OSes. + * + * Note: + * - 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? + * + * OS specific notes for freebsd4: + * - FreeBSD supported $GAI. The code does not. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifndef __rtems__ +#include "namespace.h" +#endif /* __rtems__ */ +#include <sys/param.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <net/if_types.h> +#include <ifaddrs.h> +#include <sys/queue.h> +#ifdef INET6 +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <netinet6/in6_var.h> +#include <netinet6/nd6.h> +#endif +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <rpc/rpc.h> +#ifndef __rtems__ +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif /* __rtems__ */ +#include <netdb.h> +#include <resolv.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#include "res_config.h" + +#ifdef DEBUG +#include <syslog.h> +#endif + +#include <stdarg.h> +#ifndef __rtems__ +#include <nsswitch.h> +#include "un-namespace.h" +#include "netdb_private.h" +#include "libc_private.h" +#else /* __rtems__ */ +#include "nsswitch.h" +#endif /* __rtems__ */ +#ifdef NS_CACHING +#include "nscache.h" +#endif + +#ifdef __rtems__ +#define _socket socket +#define _connect connect +#define _getsockname getsockname +#define _close close +#define RES_SET_H_ERRNO(r, x) r->res_h_errno = x +static int res_ninit(res_state statp) { return res_init(); } +#define res_nmkquery(a,b,c,d,e,f,g,h,i,j) res_mkquery(b,c,d,e,f,g,h,i,j) +#define res_nsend(a,b,c,d,e) res_send(b,c,d,e) +#undef DEBUG +static struct __res_state __res; +static res_state __res_state() { + memset(&__res, 0, sizeof(__res)); + return &__res; +} +#endif /* __rtems__ */ + +#define ANY 0 +#define YES 1 +#define NO 0 + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in_loopback[] = { 127, 0, 0, 1 }; +#ifdef INET6 +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; +#endif + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; +#ifdef INET6 + struct in6_addrpolicy pc_policy; +#endif +}; +TAILQ_HEAD(policyhead, policyqueue); + +static const struct afd { + int a_af; + int a_addrlen; + socklen_t 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 +#define N_LOCAL 2 +#else +#define N_INET 0 +#define N_LOCAL 1 +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, +#define sizeofmember(type, member) (sizeof(((type *)0)->member)) + {PF_LOCAL, sizeofmember(struct sockaddr_un, sun_path), + sizeof(struct sockaddr_un), + offsetof(struct sockaddr_un, sun_path), + NULL, NULL, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, +}; + +struct explore { + int e_af; + int e_socktype; + int e_protocol; + int e_wild; +#define AF_ANY 0x01 +#define SOCKTYPE_ANY 0x02 +#define PROTOCOL_ANY 0x04 +#define WILD_AF(ex) ((ex)->e_wild & AF_ANY) +#define WILD_SOCKTYPE(ex) ((ex)->e_wild & SOCKTYPE_ANY) +#define WILD_PROTOCOL(ex) ((ex)->e_wild & PROTOCOL_ANY) +}; + +static const struct explore explore[] = { +#ifdef INET6 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET6, SOCK_STREAM, IPPROTO_SCTP, + AF_ANY | SOCKTYPE_ANY }, + { PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE, + AF_ANY | SOCKTYPE_ANY }, + { PF_INET6, SOCK_RAW, ANY, + AF_ANY | PROTOCOL_ANY }, +#endif + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET, SOCK_STREAM, IPPROTO_SCTP, + AF_ANY | SOCKTYPE_ANY }, + { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE, + AF_ANY | SOCKTYPE_ANY }, + { PF_INET, SOCK_RAW, ANY, + AF_ANY | PROTOCOL_ANY }, + { PF_LOCAL, SOCK_DGRAM, ANY, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_LOCAL, SOCK_STREAM, ANY, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { PF_LOCAL, SOCK_SEQPACKET, ANY, + AF_ANY | SOCKTYPE_ANY | PROTOCOL_ANY }, + { -1, 0, 0, 0 }, +}; + +#ifdef INET6 +#define PTON_MAX 16 +#else +#define PTON_MAX 4 +#endif + +#define AIO_SRCFLAG_DEPRECATED 0x1 + +struct ai_order { + union { + struct sockaddr_storage aiou_ss; + struct sockaddr aiou_sa; + } aio_src_un; +#define aio_srcsa aio_src_un.aiou_sa + u_int32_t aio_srcflag; + int aio_srcscope; + int aio_dstscope; + struct policyqueue *aio_srcpolicy; + struct policyqueue *aio_dstpolicy; + struct addrinfo *aio_ai; + int aio_matchlen; + int aio_initial_sequence; +}; + +static const ns_src default_dns_files[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, + { 0 } +}; + +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 */ +}; + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +static int str2number(const char *, int *); +static int explore_copy(const struct addrinfo *, const struct addrinfo *, + 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 **, const char *); +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 struct addrinfo *copy_ai(const struct addrinfo *); +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 is_ifdisabled(char *); +#endif +static void set_source(struct ai_order *, struct policyhead *); +static int comp_dst(const void *, const void *); +#ifdef INET6 +static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); +#endif +static int gai_addr2scopetype(struct sockaddr *); + +static int explore_fqdn(const struct addrinfo *, const char *, + const char *, struct addrinfo **); + +static int reorder(struct addrinfo *); +static int get_addrselectpolicy(struct policyhead *); +static void free_addrselectpolicy(struct policyhead *); +static struct policyqueue *match_addrselectpolicy(struct sockaddr *, + struct policyhead *); +static int matchlen(struct sockaddr *, struct sockaddr *); + +static struct addrinfo *getanswer(const querybuf *, int, const char *, int, + const struct addrinfo *, res_state); +#if defined(RESOLVSORT) +static int addr4sort(struct addrinfo *, res_state); +#endif +static int _dns_getaddrinfo(void *, void *, va_list); +static void _sethtent(FILE **); +static void _endhtent(FILE **); +static struct addrinfo *_gethtent(FILE **, 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); +#endif +#ifdef NS_CACHING +static int addrinfo_id_func(char *, size_t *, va_list, void *); +static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); +static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int res_queryN(const char *, struct res_target *, res_state); +static int res_searchN(const char *, struct res_target *, res_state); +static int res_querydomainN(const char *, const char *, + struct res_target *, res_state); + +/* 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))) + +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + ai = next; + } +} + +static int +str2number(const char *p, int *portp) +{ + char *ep; + unsigned long v; + + if (*p == '\0') + return -1; + ep = NULL; + errno = 0; + v = strtoul(p, &ep, 10); + if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) { + *portp = v; + return 0; + } else + return -1; +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai, ai0, *afai; + struct addrinfo *pai; + const struct afd *afd; + const struct explore *ex; + struct addrinfo *afailist[nitems(afdl)]; + struct addrinfo *afai_unspec; + int found; + int numeric = 0; + + /* ensure we return NULL on errors */ + *res = NULL; + + memset(&ai, 0, sizeof(ai)); + + memset(afailist, 0, sizeof(afailist)); + afai_unspec = NULL; + + 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_LOCAL: + 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 (!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; + + /* matched */ + break; + } + + if (ex->e_af < 0) + ERR(EAI_BADHINTS); + } + } + + /* + * RFC 3493: AI_ALL and AI_V4MAPPED are effective only against + * AF_INET6 query. They need to be ignored if specified in other + * occasions. + */ + switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { + case AI_V4MAPPED: + case AI_ALL | AI_V4MAPPED: +#ifdef INET6 + if (pai->ai_family != AF_INET6) + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; +#endif + case AI_ALL: + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; + } + + /* + * 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) + goto bad; + + *pai = ai0; + } + + ai0 = *pai; + + /* + * NULL hostname, or numeric hostname. + * If numeric representation of AF1 can be interpreted as FQDN + * representation of AF2, we need to think again about the code below. + */ + found = 0; + for (afd = afdl; afd->a_af; afd++) { + *pai = ai0; + + if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) + continue; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = afd->a_af; + + if (hostname == NULL) { + error = explore_null(pai, servname, + &afailist[afd - afdl]); + + /* + * Errors from explore_null should be unexpected and + * be caught to avoid returning an incomplete result. + */ + if (error != 0) + goto bad; + } else { + error = explore_numeric_scope(pai, hostname, servname, + &afailist[afd - afdl]); + + /* + * explore_numeric_scope returns an error for address + * families that do not match that of hostname. + * Thus we should not catch the error at this moment. + */ + } + + if (!error && afailist[afd - afdl]) + found++; + } + if (found) { + numeric = 1; + goto globcopy; + } + + if (hostname == NULL) + ERR(EAI_NONAME); /* used to be EAI_NODATA */ + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) + ERR(EAI_FAIL); + + /* + * hostname as alphabetical name. + */ + *pai = ai0; + error = explore_fqdn(pai, hostname, servname, &afai_unspec); + +globcopy: + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + 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; + + 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 the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + continue; + + if (afai_unspec) + afai = afai_unspec; + else { + if ((afd = find_afd(pai->ai_family)) == NULL) + continue; + /* XXX assumes that afd points inside afdl[] */ + afai = afailist[afd - afdl]; + } + if (!afai) + continue; + + error = explore_copy(pai, afai, &cur->ai_next); + if (error != 0) + goto bad; + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + /* + * ensure we return either: + * - error == 0, non-NULL *res + * - error != 0, NULL *res + */ + if (error == 0) { + if (sentinel.ai_next) { + /* + * If the returned entry is for an active connection, + * and the given name is not numeric, reorder the + * list, so that the application would try the list + * in the most efficient order. Since the head entry + * of the original list may contain ai_canonname and + * that entry may be moved elsewhere in the new list, + * we keep the pointer and will restore it in the new + * head entry. (Note that RFC3493 requires the head + * entry store it when requested by the caller). + */ + if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { + if (!numeric) { + char *canonname; + + canonname = + sentinel.ai_next->ai_canonname; + sentinel.ai_next->ai_canonname = NULL; + (void)reorder(&sentinel); + if (sentinel.ai_next->ai_canonname == + NULL) { + sentinel.ai_next->ai_canonname + = canonname; + } else if (canonname != NULL) + free(canonname); + } + } + *res = sentinel.ai_next; + } else + error = EAI_FAIL; + } + +bad: + if (afai_unspec) + freeaddrinfo(afai_unspec); + for (afd = afdl; afd->a_af; afd++) { + if (afailist[afd - afdl]) + freeaddrinfo(afailist[afd - afdl]); + } + if (!*res) + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + + return (error); +} + +static int +reorder(struct addrinfo *sentinel) +{ + struct addrinfo *ai, **aip; + struct ai_order *aio; + int i, n; + struct policyhead policyhead; + + /* count the number of addrinfo elements for sorting. */ + for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) + ; + + /* + * If the number is small enough, we can skip the reordering process. + */ + if (n <= 1) + return(n); + + /* allocate a temporary array for sort and initialization of it. */ + if ((aio = calloc(n, sizeof(*aio))) == NULL) + return(n); /* give up reordering */ + + /* retrieve address selection policy from the kernel */ + TAILQ_INIT(&policyhead); + if (!get_addrselectpolicy(&policyhead)) { + /* no policy is installed into kernel, we don't sort. */ + free(aio); + return (n); + } + + for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { + aio[i].aio_ai = ai; + aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); + aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, + &policyhead); + set_source(&aio[i], &policyhead); + aio[i].aio_initial_sequence = i; + } + + /* perform sorting. */ + qsort(aio, n, sizeof(*aio), comp_dst); + + /* reorder the addrinfo chain. */ + for (i = 0, aip = &sentinel->ai_next; i < n; i++) { + *aip = aio[i].aio_ai; + aip = &aio[i].aio_ai->ai_next; + } + *aip = NULL; + + /* cleanup and return */ + free(aio); + free_addrselectpolicy(&policyhead); + return(n); +} + +static int +get_addrselectpolicy(struct policyhead *head) +{ +#ifdef INET6 + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) + return (0); + if (l == 0) + return (0); + if ((buf = malloc(l)) == NULL) + return (0); + if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { + free(buf); + return (0); + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + struct policyqueue *new; + + if ((new = malloc(sizeof(*new))) == NULL) { + free_addrselectpolicy(head); /* make the list empty */ + break; + } + new->pc_policy = *pol; + TAILQ_INSERT_TAIL(head, new, pc_entry); + } + + free(buf); + return (1); +#else + return (0); +#endif +} + +static void +free_addrselectpolicy(struct policyhead *head) +{ + struct policyqueue *ent, *nent; + + for (ent = TAILQ_FIRST(head); ent; ent = nent) { + nent = TAILQ_NEXT(ent, pc_entry); + TAILQ_REMOVE(head, ent, pc_entry); + free(ent); + } +} + +static struct policyqueue * +match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) +{ +#ifdef INET6 + struct policyqueue *ent, *bestent = NULL; + struct in6_addrpolicy *pol; + int matchlen, bestmatchlen = -1; + u_char *mp, *ep, *k, *p, m; + struct sockaddr_in6 key; + + switch(addr->sa_family) { + case AF_INET6: + key = *(struct sockaddr_in6 *)addr; + break; + case AF_INET: + /* convert the address into IPv4-mapped IPv6 address. */ + memset(&key, 0, sizeof(key)); + key.sin6_family = AF_INET6; + key.sin6_len = sizeof(key); + _map_v4v6_address( + (char *)&((struct sockaddr_in *)addr)->sin_addr, + (char *)&key.sin6_addr); + break; + default: + return(NULL); + } + + for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { + pol = &ent->pc_policy; + matchlen = 0; + + mp = (u_char *)&pol->addrmask.sin6_addr; + ep = mp + 16; /* XXX: scope field? */ + k = (u_char *)&key.sin6_addr; + p = (u_char *)&pol->addr.sin6_addr; + for (; mp < ep && *mp; mp++, k++, p++) { + m = *mp; + if ((*k & m) != *p) + goto next; /* not match */ + if (m == 0xff) /* short cut for a typical case */ + matchlen += 8; + else { + while (m >= 0x80) { + matchlen++; + m <<= 1; + } + } + } + + /* matched. check if this is better than the current best. */ + if (matchlen > bestmatchlen) { + bestent = ent; + bestmatchlen = matchlen; + } + + next: + continue; + } + + return(bestent); +#else + return(NULL); +#endif + +} + +static void +set_source(struct ai_order *aio, struct policyhead *ph) +{ + struct addrinfo ai = *aio->aio_ai; + struct sockaddr_storage ss; + socklen_t srclen; + int s; + + /* set unspec ("no source is available"), just in case */ + aio->aio_srcsa.sa_family = AF_UNSPEC; + aio->aio_srcscope = -1; + + switch(ai.ai_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + break; + default: /* ignore unsupported AFs explicitly */ + return; + } + + /* XXX: make a dummy addrinfo to call connect() */ + ai.ai_socktype = SOCK_DGRAM; + ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ + ai.ai_next = NULL; + memset(&ss, 0, sizeof(ss)); + memcpy(&ss, ai.ai_addr, ai.ai_addrlen); + ai.ai_addr = (struct sockaddr *)&ss; + get_port(&ai, "1", 0); + + /* open a socket to get the source address for the given dst */ + if ((s = _socket(ai.ai_family, ai.ai_socktype | SOCK_CLOEXEC, + ai.ai_protocol)) < 0) + return; /* give up */ +#ifdef INET6 + if (ai.ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai.ai_addr; + int off = 0; + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + (void)_setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&off, sizeof(off)); + } +#endif + if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) + goto cleanup; + srclen = ai.ai_addrlen; + if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { + aio->aio_srcsa.sa_family = AF_UNSPEC; + goto cleanup; + } + aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); + aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); + aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); +#ifdef INET6 + if (ai.ai_family == AF_INET6) { + struct in6_ifreq ifr6; + u_int32_t flags6; + + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); + if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { + flags6 = ifr6.ifr_ifru.ifru_flags6; + if ((flags6 & IN6_IFF_DEPRECATED)) + aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; + } + } +#endif + + cleanup: + _close(s); + return; +} + +static int +matchlen(struct sockaddr *src, struct sockaddr *dst) +{ + int match = 0; + u_char *s, *d; + u_char *lim, r; + int addrlen; + + switch (src->sa_family) { +#ifdef INET6 + case AF_INET6: + s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; + d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; + addrlen = sizeof(struct in6_addr); + lim = s + addrlen; + break; +#endif + case AF_INET: + s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; + d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; + addrlen = sizeof(struct in_addr); + lim = s + addrlen; + break; + default: + return(0); + } + + while (s < lim) + if ((r = (*d++ ^ *s++)) != 0) { + while ((r & 0x80) == 0) { + match++; + r <<= 1; + } + break; + } else + match += 8; + return(match); +} + +static int +comp_dst(const void *arg1, const void *arg2) +{ + const struct ai_order *dst1 = arg1, *dst2 = arg2; + + /* + * Rule 1: Avoid unusable destinations. + * XXX: we currently do not consider if an appropriate route exists. + */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family == AF_UNSPEC) { + return(-1); + } + if (dst1->aio_srcsa.sa_family == AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + return(1); + } + + /* Rule 2: Prefer matching scope. */ + if (dst1->aio_dstscope == dst1->aio_srcscope && + dst2->aio_dstscope != dst2->aio_srcscope) { + return(-1); + } + if (dst1->aio_dstscope != dst1->aio_srcscope && + dst2->aio_dstscope == dst2->aio_srcscope) { + return(1); + } + + /* Rule 3: Avoid deprecated addresses. */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(-1); + } + if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(1); + } + } + + /* Rule 4: Prefer home addresses. */ + /* XXX: not implemented yet */ + + /* Rule 5: Prefer matching label. */ +#ifdef INET6 + if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && + dst1->aio_srcpolicy->pc_policy.label == + dst1->aio_dstpolicy->pc_policy.label && + (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || + dst2->aio_srcpolicy->pc_policy.label != + dst2->aio_dstpolicy->pc_policy.label)) { + return(-1); + } + if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && + dst2->aio_srcpolicy->pc_policy.label == + dst2->aio_dstpolicy->pc_policy.label && + (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || + dst1->aio_srcpolicy->pc_policy.label != + dst1->aio_dstpolicy->pc_policy.label)) { + return(1); + } +#endif + + /* Rule 6: Prefer higher precedence. */ +#ifdef INET6 + if (dst1->aio_dstpolicy && + (dst2->aio_dstpolicy == NULL || + dst1->aio_dstpolicy->pc_policy.preced > + dst2->aio_dstpolicy->pc_policy.preced)) { + return(-1); + } + if (dst2->aio_dstpolicy && + (dst1->aio_dstpolicy == NULL || + dst2->aio_dstpolicy->pc_policy.preced > + dst1->aio_dstpolicy->pc_policy.preced)) { + return(1); + } +#endif + + /* Rule 7: Prefer native transport. */ + /* XXX: not implemented yet */ + + /* Rule 8: Prefer smaller scope. */ + if (dst1->aio_dstscope >= 0 && + dst1->aio_dstscope < dst2->aio_dstscope) { + return(-1); + } + if (dst2->aio_dstscope >= 0 && + dst2->aio_dstscope < dst1->aio_dstscope) { + return(1); + } + + /* + * Rule 9: Use longest matching prefix. + * We compare the match length in a same AF only. + */ + if (dst1->aio_ai->ai_addr->sa_family == + dst2->aio_ai->ai_addr->sa_family && + dst1->aio_ai->ai_addr->sa_family != AF_INET) { + if (dst1->aio_matchlen > dst2->aio_matchlen) { + return(-1); + } + if (dst1->aio_matchlen < dst2->aio_matchlen) { + return(1); + } + } + + /* Rule 10: Otherwise, leave the order unchanged. */ + + /* + * Note that qsort is unstable; so, we can't return zero and + * expect the order to be unchanged. + * That also means we can't depend on the current position of + * dst2 being after dst1. We must enforce the initial order + * with an explicit compare on the original position. + * The qsort specification requires that "When the same objects + * (consisting of width bytes, irrespective of their current + * positions in the array) are passed more than once to the + * comparison function, the results shall be consistent with one + * another." + * In other words, If A < B, then we must also return B > A. + */ + if (dst2->aio_initial_sequence < dst1->aio_initial_sequence) + return(1); + + return(-1); +} + +/* + * Copy from scope.c. + * XXX: we should standardize the functions and link them as standard + * library. + */ +static int +gai_addr2scopetype(struct sockaddr *sa) +{ +#ifdef INET6 + struct sockaddr_in6 *sa6; +#endif + struct sockaddr_in *sa4; + + switch(sa->sa_family) { +#ifdef INET6 + case AF_INET6: + sa6 = (struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { + /* just use the scope field of the multicast address */ + return(sa6->sin6_addr.s6_addr[2] & 0x0f); + } + /* + * Unicast addresses: map scope type to corresponding scope + * value defined for multcast addresses. + * XXX: hardcoded scope type values are bad... + */ + if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) + return(1); /* node local scope */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) + return(2); /* link-local scope */ + if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) + return(5); /* site-local scope */ + return(14); /* global scope */ + break; +#endif + case AF_INET: + /* + * IPv4 pseudo scoping according to RFC 3484. + */ + sa4 = (struct sockaddr_in *)sa; + /* IPv4 autoconfiguration addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 169 && + ((u_char *)&sa4->sin_addr)[1] == 254) + return(2); + /* Private addresses have site-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 10 || + (((u_char *)&sa4->sin_addr)[0] == 172 && + (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || + (((u_char *)&sa4->sin_addr)[0] == 192 && + ((u_char *)&sa4->sin_addr)[1] == 168)) + return(14); /* XXX: It should be 5 unless NAT */ + /* Loopback addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 127) + return(2); + return(14); + break; + default: + errno = EAFNOSUPPORT; /* is this a good error? */ + return(-1); + } +} + +static int +explore_copy(const struct addrinfo *pai, const struct addrinfo *src0, + struct addrinfo **res) +{ + int error; + struct addrinfo sentinel, *cur; + const struct addrinfo *src; + + error = 0; + sentinel.ai_next = NULL; + cur = &sentinel; + + for (src = src0; src != NULL; src = src->ai_next) { + if (src->ai_family != pai->ai_family) + continue; + + cur->ai_next = copy_ai(src); + if (!cur->ai_next) { + error = EAI_MEMORY; + goto fail; + } + + cur->ai_next->ai_socktype = pai->ai_socktype; + cur->ai_next->ai_protocol = pai->ai_protocol; + cur = cur->ai_next; + } + + *res = sentinel.ai_next; + return 0; + +fail: + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(const struct addrinfo *pai, const char *servname, + struct addrinfo **res) +{ + int s; + const struct afd *afd; + struct addrinfo *ai; + int error; + + *res = NULL; + ai = NULL; + + if (pai->ai_family == PF_LOCAL) + return (0); + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = _socket(pai->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + _close(s); + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(ai, afd, afd->a_addrany); + GET_PORT(ai, servname); + } else { + GET_AI(ai, afd, afd->a_loopback); + GET_PORT(ai, servname); + } + + *res = ai; + return 0; + +free: + if (ai != NULL) + freeaddrinfo(ai); + return error; +} + +/* + * numeric hostname + */ +static int +explore_numeric(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res, const char *canonname) +{ + const struct afd *afd; + struct addrinfo *ai, ai0; + int error; + char pton[PTON_MAX], path[PATH_MAX], *p; + +#ifdef CTASSERT + CTASSERT(sizeofmember(struct sockaddr_un, sun_path) <= PATH_MAX); +#endif + *res = NULL; + ai = NULL; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + switch (afd->a_af) { + case AF_LOCAL: + if (hostname[0] != '/') + ERR(EAI_NONAME); + if (strlen(hostname) > afd->a_addrlen) + ERR(EAI_MEMORY); + /* NUL-termination does not need to be guaranteed. */ + strncpy(path, hostname, afd->a_addrlen); + p = &path[0]; + break; + case AF_INET: + /* + * RFC3493 requires getaddrinfo() to accept AF_INET formats + * that are accepted by inet_addr() and its family. The + * accepted forms includes the "classful" one, which inet_pton + * does not accept. So we need to separate the case for + * AF_INET. + */ + if (inet_aton(hostname, (struct in_addr *)pton) != 1 || + hostname[strspn(hostname, "0123456789.xabcdefXABCDEF")] != '\0') + return 0; + p = pton; + break; + default: + if (inet_pton(afd->a_af, hostname, pton) != 1) { + if (pai->ai_family != AF_INET6 || + (pai->ai_flags & AI_V4MAPPED) != AI_V4MAPPED) + return 0; + if (inet_aton(hostname, (struct in_addr *)pton) != 1) + return 0; + afd = &afdl[N_INET]; + ai0 = *pai; + ai0.ai_family = AF_INET; + pai = &ai0; + } + p = pton; + break; + } + + if (pai->ai_family == afd->a_af) { + GET_AI(ai, afd, p); + GET_PORT(ai, servname); + if ((pai->ai_family == AF_INET || + pai->ai_family == AF_INET6) && + (pai->ai_flags & AI_CANONNAME)) { + /* + * Set the numeric address itself as the canonical + * name, based on a clarification in RFC3493. + */ + GET_CANONNAME(ai, canonname); + } + } else { + /* + * XXX: This should not happen since we already matched the AF + * by find_afd. + */ + ERR(EAI_FAMILY); + } + + *res = ai; + return 0; + +free: +bad: + if (ai != NULL) + freeaddrinfo(ai); + return error; +} + +/* + * numeric hostname with scope + */ +static int +explore_numeric_scope(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, hostname); +#else + const struct afd *afd; + struct addrinfo *cur; + int error; + char *cp, *hostname2 = NULL, *scope, *addr; + struct sockaddr_in6 *sin6; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (!afd->a_scoped) + return explore_numeric(pai, hostname, servname, res, hostname); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res, hostname); + + /* + * 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, hostname); + if (error == 0) { + u_int32_t 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 (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { + free(hostname2); + freeaddrinfo(*res); + *res = NULL; + return(EAI_NONAME); /* XXX: is return OK? */ + } + sin6->sin6_scope_id = scopeid; + } + } + + free(hostname2); + + if (error && *res) { + freeaddrinfo(*res); + *res = NULL; + } + return error; +#endif +} + +static int +get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) +{ + if ((pai->ai_flags & AI_CANONNAME) != 0) { + ai->ai_canonname = strdup(str); + if (ai->ai_canonname == NULL) + return EAI_MEMORY; + } + return 0; +} + +static struct addrinfo * +get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) +{ + char *p; + struct addrinfo *ai; +#ifdef INET6 + struct in6_addr mapaddr; + + if (afd->a_af == AF_INET && (pai->ai_flags & AI_V4MAPPED) != 0) { + afd = &afdl[N_INET6]; + _map_v4v6_address(addr, (char *)&mapaddr); + addr = (char *)&mapaddr; + } +#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_addr->sa_len = afd->a_socklen; + ai->ai_addrlen = afd->a_socklen; + if (ai->ai_family == PF_LOCAL) { + size_t n = strnlen(addr, afd->a_addrlen); + + ai->ai_addrlen -= afd->a_addrlen - n; + ai->ai_addr->sa_len -= afd->a_addrlen - n; + } + ai->ai_addr->sa_family = ai->ai_family = afd->a_af; + p = (char *)(void *)(ai->ai_addr); + memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); + return ai; +} + +/* XXX need to malloc() the same way we do from other functions! */ +static struct addrinfo * +copy_ai(const struct addrinfo *pai) +{ + struct addrinfo *ai; + size_t l; + + l = sizeof(*ai) + pai->ai_addrlen; + if ((ai = calloc(1, l)) == NULL) + return NULL; + memcpy(ai, pai, sizeof(*ai)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); + + if (pai->ai_canonname) { + l = strlen(pai->ai_canonname) + 1; + if ((ai->ai_canonname = malloc(l)) == NULL) { + free(ai); + return NULL; + } + strlcpy(ai->ai_canonname, pai->ai_canonname, l); + } else { + /* just to make sure */ + ai->ai_canonname = NULL; + } + + ai->ai_next = NULL; + + return ai; +} + +static int +get_portmatch(const struct addrinfo *ai, const char *servname) +{ + + /* 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(struct addrinfo *ai, const char *servname, int matchonly) +{ + const char *proto; + struct servent *sp; + int port, error; + int allownumeric; + + if (servname == NULL) + return 0; + switch (ai->ai_family) { + case AF_LOCAL: + /* AF_LOCAL ignores servname silently. */ + return (0); + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + break; + default: + return 0; + } + + switch (ai->ai_socktype) { + case SOCK_RAW: + return EAI_SERVICE; + case SOCK_DGRAM: + case SOCK_STREAM: + case SOCK_SEQPACKET: + allownumeric = 1; + break; + case ANY: + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + allownumeric = 1; + break; + default: + allownumeric = 0; + break; + } + break; + default: + return EAI_SOCKTYPE; + } + + error = str2number(servname, &port); + if (error == 0) { + if (!allownumeric) + return EAI_SERVICE; + if (port < 0 || port > 65535) + return EAI_SERVICE; + port = htons(port); + } else { + if (ai->ai_flags & AI_NUMERICSERV) + return EAI_NONAME; + + switch (ai->ai_protocol) { + case IPPROTO_UDP: + proto = "udp"; + break; + case IPPROTO_TCP: + proto = "tcp"; + break; + case IPPROTO_SCTP: + proto = "sctp"; + break; + case IPPROTO_UDPLITE: + proto = "udplite"; + break; + default: + proto = NULL; + break; + } + + if ((sp = getservbyname(servname, proto)) == NULL) + return EAI_SERVICE; + port = sp->s_port; + } + + 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 + } + } + + return 0; +} + +static const struct afd * +find_afd(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; +} + +/* + * RFC 3493: AI_ADDRCONFIG check. Determines which address families are + * configured on the local system and correlates with pai->ai_family value. + * If an address family is not configured on the system, it will not be + * queried for. For this purpose, loopback addresses are not considered + * configured addresses. + * + * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with + * _dns_getaddrinfo. + */ +static int +addrconfig(struct addrinfo *pai) +{ + struct ifaddrs *ifaddrs, *ifa; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + int seen_inet = 0, seen_inet6 = 0; + + if (getifaddrs(&ifaddrs) != 0) + return (0); + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || (ifa->ifa_flags & IFF_UP) == 0) + continue; + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + if (seen_inet) + continue; + sin = (struct sockaddr_in *)(ifa->ifa_addr); + if (htonl(sin->sin_addr.s_addr) == INADDR_LOOPBACK) + continue; + seen_inet = 1; + break; +#ifdef INET6 + case AF_INET6: + if (seen_inet6) + continue; + sin6 = (struct sockaddr_in6 *)(ifa->ifa_addr); + if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + continue; + if ((ifa->ifa_flags & IFT_LOOP) != 0 && + IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + if (is_ifdisabled(ifa->ifa_name)) + continue; + seen_inet6 = 1; + break; +#endif + } + } + freeifaddrs(ifaddrs); + + switch(pai->ai_family) { + case AF_INET6: + return (seen_inet6); + case AF_INET: + return (seen_inet); + case AF_UNSPEC: + if (seen_inet == seen_inet6) + return (seen_inet); + pai->ai_family = seen_inet ? AF_INET : AF_INET6; + return (1); + } + return (1); +} + +#ifdef INET6 +static int +is_ifdisabled(char *name) +{ + struct in6_ndireq nd; + int fd; + + if ((fd = _socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) + return (-1); + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, name, sizeof(nd.ifname)); + if (_ioctl(fd, SIOCGIFINFO_IN6, &nd) < 0) { + _close(fd); + return (-1); + } + _close(fd); + return ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0); +} + +/* convert a string to a scope identifier. XXX: IPv6 specific */ +static int +ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) +{ + u_long lscopeid; + struct in6_addr *a6; + char *ep; + + a6 = &sin6->sin6_addr; + + /* empty scopeid portion is invalid */ + if (*scope == '\0') + return -1; + + if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || + IN6_IS_ADDR_MC_NODELOCAL(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 0; + } + + /* 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 */ + + /* try to convert to a numeric id as a last resort */ + trynumeric: + errno = 0; + lscopeid = strtoul(scope, &ep, 10); + *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); + if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) + return 0; + else + return -1; +} +#endif + + +#ifdef NS_CACHING +static int +addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 0; /* identifies the getaddrinfo for the cache */ + char *hostname; + struct addrinfo *hints; + + char *p; + int ai_flags, ai_family, ai_socktype, ai_protocol; + size_t desired_size, size; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + hostname = va_arg(ap, char *); + hints = va_arg(ap, struct addrinfo *); + + desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; + if (hostname != NULL) { + size = strlen(hostname); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + *buffer_size = desired_size; + return (NS_RETURN); + } + + if (hints == NULL) + ai_flags = ai_family = ai_socktype = ai_protocol = 0; + else { + ai_flags = hints->ai_flags; + ai_family = hints->ai_family; + ai_socktype = hints->ai_socktype; + ai_protocol = hints->ai_protocol; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_flags, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_family, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_socktype, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_protocol, sizeof(int)); + p += sizeof(int); + + if (hostname != NULL) + memcpy(p, hostname, size); + + *buffer_size = desired_size; + return (NS_SUCCESS); +} + +static int +addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo *ai, *cai; + char *p; + size_t desired_size, size, ai_size; + + ai = *((struct addrinfo **)retval); + + desired_size = sizeof(size_t); + ai_size = 0; + for (cai = ai; cai != NULL; cai = cai->ai_next) { + desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; + if (cai->ai_canonname != NULL) + desired_size += sizeof(size_t) + + strlen(cai->ai_canonname); + ++ai_size; + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + errno = ERANGE; + *buffer_size = desired_size; + return (NS_RETURN); + } + + memset(buffer, 0, desired_size); + p = buffer; + + memcpy(p, &ai_size, sizeof(size_t)); + p += sizeof(size_t); + for (cai = ai; cai != NULL; cai = cai->ai_next) { + memcpy(p, cai, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + + memcpy(p, cai->ai_addr, cai->ai_addrlen); + p += cai->ai_addrlen; + + if (cai->ai_canonname != NULL) { + size = strlen(cai->ai_canonname); + memcpy(p, &size, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, cai->ai_canonname, size); + p += size; + } + } + + return (NS_SUCCESS); +} + +static int +addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo new_ai, *result, *sentinel, *lasts; + + char *p; + size_t ai_size, ai_i, size; + + p = buffer; + memcpy(&ai_size, p, sizeof(size_t)); + p += sizeof(size_t); + + result = NULL; + lasts = NULL; + for (ai_i = 0; ai_i < ai_size; ++ai_i) { + memcpy(&new_ai, p, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + size = new_ai.ai_addrlen + sizeof(struct addrinfo) + + _ALIGNBYTES; + + sentinel = calloc(1, size); + + memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); + sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + + sizeof(struct addrinfo)); + + memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); + p += new_ai.ai_addrlen; + + if (new_ai.ai_canonname != NULL) { + memcpy(&size, p, sizeof(size_t)); + p += sizeof(size_t); + + sentinel->ai_canonname = calloc(1, size + 1); + + memcpy(sentinel->ai_canonname, p, size); + p += size; + } + + if (result == NULL) { + result = sentinel; + lasts = sentinel; + } else { + lasts->ai_next = sentinel; + lasts = sentinel; + } + } + + *((struct addrinfo **)retval) = result; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + +#ifdef __rtems__ +static int __dns_getaddrinfo(struct addrinfo **result, ...) { + va_list ap; + int r; + va_start(ap, result); + r = _dns_getaddrinfo(result, NULL, ap); + va_end(ap); + return r; +} +#endif /* __rtems__ */ + +/* + * FQDN hostname, DNS lookup + */ +static int +explore_fqdn(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res) +{ + struct addrinfo *result; + struct addrinfo *cur; + int error = 0; + +#ifndef __rtems__ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, + addrinfo_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_files_getaddrinfo, NULL) + { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ + NS_NIS_CB(_yp_getaddrinfo, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; +#endif /* __rtems__ */ + result = NULL; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + +#ifndef __rtems__ + switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", + default_dns_files, hostname, pai)) { +#else /* __rtems__ */ + switch (__dns_getaddrinfo(&result, hostname)) { +#endif /* __rtems__ */ + case NS_TRYAGAIN: + error = EAI_AGAIN; + goto free; + case NS_UNAVAIL: + error = EAI_FAIL; + goto free; + case NS_NOTFOUND: + error = EAI_NONAME; + goto free; + case NS_SUCCESS: + error = 0; + for (cur = result; cur; cur = cur->ai_next) { + GET_PORT(cur, servname); + /* canonname should be filled already */ + } + break; + } + + *res = result; + + return 0; + +free: + if (result) + freeaddrinfo(result); + return error; +} + +#ifdef DEBUG +static const char AskedForGot[] = + "gethostby*.getanswer: asked for \"%s\", got \"%s\""; +#endif + +static struct addrinfo * +getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, + const struct addrinfo *pai, res_state res) +{ + 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, *ep; + int type, class, 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; + ep = hostbuf + sizeof hostbuf; + cp = answer->buf + HFIXEDSZ; + if (qdcount != 1) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + RES_SET_H_ERRNO(res, 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) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (NULL); + } + canonname = bp; + bp += 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, ep - bp); + 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 > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strlcpy(bp, tbuf, ep - bp); + canonname = bp; + bp += 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 && + type != ns_t_dname) + 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; + + canonname = bp; + nn = strlen(bp) + 1; /* for the \0 */ + bp += nn; + } + + /* 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 defined(RESOLVSORT) + /* + * We support only IPv4 address for backward + * compatibility against gethostbyname(3). + */ + if (res->nsort && qtype == T_A) { + if (addr4sort(&sentinel, res) < 0) { + freeaddrinfo(sentinel.ai_next); + RES_SET_H_ERRNO(res, NO_RECOVERY); + return NULL; + } + } +#endif /*RESOLVSORT*/ + if (!canonname) + (void)get_canonname(pai, sentinel.ai_next, qname); + else + (void)get_canonname(pai, sentinel.ai_next, canonname); + RES_SET_H_ERRNO(res, NETDB_SUCCESS); + return sentinel.ai_next; + } + + /* + * We could have walked a CNAME chain, but the ultimate target + * may not have what we looked for. + */ + RES_SET_H_ERRNO(res, ntohs(hp->ancount) > 0 ? NO_DATA : NO_RECOVERY); + return NULL; +} + +#ifdef RESOLVSORT +struct addr_ptr { + struct addrinfo *ai; + int aval; +}; + +static int +addr4sort(struct addrinfo *sentinel, res_state res) +{ + struct addrinfo *ai; + struct addr_ptr *addrs, addr; + struct sockaddr_in *sin; + int naddrs, i, j; + int needsort = 0; + + if (!sentinel) + return -1; + naddrs = 0; + for (ai = sentinel->ai_next; ai; ai = ai->ai_next) + naddrs++; + if (naddrs < 2) + return 0; /* We don't need sorting. */ + if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) + return -1; + i = 0; + for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { + sin = (struct sockaddr_in *)ai->ai_addr; + for (j = 0; (unsigned)j < res->nsort; j++) { + if (res->sort_list[j].addr.s_addr == + (sin->sin_addr.s_addr & res->sort_list[j].mask)) + break; + } + addrs[i].ai = ai; + addrs[i].aval = j; + if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) + needsort = i; + i++; + } + if (!needsort) { + free(addrs); + return 0; + } + + while (needsort < naddrs) { + for (j = needsort - 1; j >= 0; j--) { + if (addrs[j].aval > addrs[j+1].aval) { + addr = addrs[j]; + addrs[j] = addrs[j + 1]; + addrs[j + 1] = addr; + } else + break; + } + needsort++; + } + + ai = sentinel; + for (i = 0; i < naddrs; ++i) { + ai->ai_next = addrs[i].ai; + ai = ai->ai_next; + } + ai->ai_next = NULL; + free(addrs); + return 0; +} +#endif /*RESOLVSORT*/ + +/*ARGSUSED*/ +static int +_dns_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo *ai, ai0; + querybuf *buf, *buf2; + const char *hostname; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + struct res_target q, q2; + res_state res; + + ai = NULL; + + hostname = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&q, 0, sizeof(q)); + memset(&q2, 0, sizeof(q2)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + res = __res_state(); + + buf = malloc(sizeof(*buf)); + if (!buf) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return NS_NOTFOUND; + } + buf2 = malloc(sizeof(*buf2)); + if (!buf2) { + free(buf); + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return NS_NOTFOUND; + } + + if (pai->ai_family == AF_INET6 && + (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) { + ai0 = *pai; + ai0.ai_family = AF_UNSPEC; + pai = &ai0; + } + + switch (pai->ai_family) { + case AF_UNSPEC: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + q.next = &q2; + q2.name = hostname; + q2.qclass = C_IN; + q2.qtype = T_AAAA; + q2.answer = buf2->buf; + q2.anslen = sizeof(buf2->buf); + break; + case AF_INET: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + case AF_INET6: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_AAAA; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + default: + free(buf); + free(buf2); + return NS_UNAVAIL; + } + + if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + free(buf); + free(buf2); + return NS_NOTFOUND; + } + + if (res_searchN(hostname, &q, res) < 0) { + free(buf); + free(buf2); + return NS_NOTFOUND; + } + /* prefer IPv6 */ + if (q.next) { + ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); + if (ai != NULL) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + } + if (ai == NULL || pai->ai_family != AF_UNSPEC || + (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) != AI_V4MAPPED) { + ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); + if (ai != NULL) + cur->ai_next = ai; + } + free(buf); + free(buf2); + if (sentinel.ai_next == NULL) + switch (res->res_h_errno) { + case HOST_NOT_FOUND: + case NO_DATA: + 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(FILE **hostf) +{ + if (!*hostf) + *hostf = fopen(_PATH_HOSTS, "re"); + else + rewind(*hostf); +} + +static void +_endhtent(FILE **hostf) +{ + if (*hostf) { + (void) fclose(*hostf); + *hostf = NULL; + } +} + +static struct addrinfo * +_gethtent(FILE **hostf, 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, "re"))) + return (NULL); +again: + if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) + return (NULL); + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *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: + /* we should not glob socktype/protocol here */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = pai->ai_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_NUMERICHOST; + if (pai->ai_family == AF_INET6 && + (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) + hints.ai_flags |= AI_V4MAPPED; + error = getaddrinfo(addr, "0", &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; + res->ai_socktype = pai->ai_socktype; + res->ai_protocol = pai->ai_protocol; + + if (pai->ai_flags & AI_CANONNAME) { + if (get_canonname(pai, res, cname) != 0) { + freeaddrinfo(res0); + goto again; + } + } + } + return res0; +} + +static struct addrinfo * +_getht(FILE **hostf, const char *name, const struct addrinfo *pai, + struct addrinfo *cur) +{ + struct addrinfo *p; + + while ((p = _gethtent(hostf, name, pai)) != NULL) { + cur->ai_next = p; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + return (cur); +} + +/*ARGSUSED*/ +static int +_files_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + const char *name; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + FILE *hostf = NULL; + + name = va_arg(ap, char *); + pai = va_arg(ap, struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + _sethtent(&hostf); + if (pai->ai_family == AF_INET6 && + (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) == AI_V4MAPPED) { + struct addrinfo ai0 = *pai; + + ai0.ai_flags &= ~AI_V4MAPPED; + cur = _getht(&hostf, name, &ai0, cur); + if (sentinel.ai_next == NULL) { + _sethtent(&hostf); + ai0.ai_flags |= AI_V4MAPPED; + cur = _getht(&hostf, name, &ai0, cur); + } + } else + cur = _getht(&hostf, name, pai, cur); + _endhtent(&hostf); + + *((struct addrinfo **)rv) = sentinel.ai_next; + if (sentinel.ai_next == NULL) + return NS_NOTFOUND; + return NS_SUCCESS; +} + +#ifdef YP +/*ARGSUSED*/ +static struct addrinfo * +_yphostent(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'; + + addr = p; + + 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; + if (pai->ai_family == AF_INET6 && + (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) + hints.ai_flags |= AI_V4MAPPED; + 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; + + 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; + } + +done: + return sentinel.ai_next; +} + +/*ARGSUSED*/ +static int +_yp_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo sentinel, *cur; + struct addrinfo *ai = NULL; + char *ypbuf; + int ypbuflen, r; + const char *name; + const struct addrinfo *pai; + char *ypdomain; + + if (_yp_check(&ypdomain) == 0) + return NS_UNAVAIL; + + name = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + /* ipnodes.byname can hold both IPv4/v6 */ + r = yp_match(ypdomain, "ipnodes.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + ai = _yphostent(ypbuf, pai); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + free(ypbuf); + } + + if (ai != NULL) { + struct sockaddr_in6 *sin6; + + switch (ai->ai_family) { + case AF_INET: + goto done; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ai->ai_addr; + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + goto done; + break; + } + } + + /* hosts.byname is only for IPv4 (Solaris8) */ + if (pai->ai_family == AF_UNSPEC || pai->ai_family == AF_INET || + ((pai->ai_family == AF_INET6 && + (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) && + (ai == NULL || (pai->ai_flags & AI_ALL) == AI_ALL))) { + r = yp_match(ypdomain, "hosts.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + struct addrinfo ai4; + + ai4 = *pai; + if (pai->ai_family == AF_UNSPEC) + ai4.ai_family = AF_INET; + ai = _yphostent(ypbuf, &ai4); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + free(ypbuf); + } + } + +done: + if (sentinel.ai_next == NULL) { + RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); + return NS_NOTFOUND; + } + *((struct addrinfo **)rv) = sentinel.ai_next; + return NS_SUCCESS; +} +#endif + +/* resolver logic */ + +/* + * 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(const char *name, struct res_target *target, res_state res) +{ + u_char *buf; + HEADER *hp; + int n; + u_int oflags; + struct res_target *t; + int rcode; + int ancount; + + rcode = NOERROR; + ancount = 0; + + buf = malloc(MAXPACKET); + if (!buf) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return -1; + } + + for (t = target; t; t = t->next) { + int class, type; + u_char *answer; + int anslen; + + hp = (HEADER *)(void *)t->answer; + + /* make it easier... */ + class = t->qclass; + type = t->qtype; + answer = t->answer; + anslen = t->anslen; + +#ifndef __rtems__ + oflags = res->_flags; +#else /* __rtems__ */ + oflags = 0; +#endif /* __rtems__ */ + +again: + hp->rcode = NOERROR; /* default */ + +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query(%s, %d, %d)\n", name, class, type); +#endif + + n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, + buf, MAXPACKET); +#ifndef __rtems__ + if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && + (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) + n = res_nopt(res, n, buf, MAXPACKET, anslen); +#endif /* __rtems__ */ + if (n <= 0) { +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query: mkquery failed\n"); +#endif + free(buf); + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (n); + } + n = res_nsend(res, buf, n, answer, anslen); + if (n < 0) { +#ifndef __rtems__ + /* + * if the query choked with EDNS0, retry + * without EDNS0 + */ + if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) + != 0U && + ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { + res->_flags |= RES_F_EDNS0ERR; + if (res->options & RES_DEBUG) + printf(";; res_nquery: retry without EDNS0\n"); + goto again; + } +#endif /* __rtems__ */ + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + continue; + } + + if (n > anslen) + hp->rcode = FORMERR; /* XXX not very informative */ + if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; rcode = %u, ancount=%u\n", hp->rcode, + ntohs(hp->ancount)); +#endif + continue; + } + + ancount += ntohs(hp->ancount); + + t->n = n; + } + + free(buf); + + if (ancount == 0) { + switch (rcode) { + case NXDOMAIN: + RES_SET_H_ERRNO(res, HOST_NOT_FOUND); + break; + case SERVFAIL: + RES_SET_H_ERRNO(res, TRY_AGAIN); + break; + case NOERROR: + RES_SET_H_ERRNO(res, NO_DATA); + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + RES_SET_H_ERRNO(res, NO_RECOVERY); + break; + } + return (-1); + } + 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(const char *name, struct res_target *target, res_state res) +{ + 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, root_on_list = 0; + int tried_as_is = 0; + int searched = 0; + char abuf[MAXDNAME]; + + errno = 0; + RES_SET_H_ERRNO(res, 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++; + +#ifndef __rtems__ + /* + * if there aren't any dots, it could be a user-level alias + */ + if (!dots && + (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) + return (res_queryN(cp, target, res)); +#endif /* __rtems__ */ + + /* + * If there are enough dots in the name, let's just give it a + * try 'as is'. The threshold can be set with the "ndots" option. + * Also, query 'as is', if there is a trailing dot in the name. + */ + saved_herrno = -1; + if (dots >= res->ndots || trailing_dot) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0 || trailing_dot) + return (ret); + if (errno == ECONNREFUSED) { + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); + } + switch (res->res_h_errno) { + case NO_DATA: + case HOST_NOT_FOUND: + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) + break; + /* FALLTHROUGH */ + default: + return (-1); + } + saved_herrno = res->res_h_errno; + tried_as_is++; + } + + /* + * 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++) { + searched = 1; + + if (domain[0][0] == '\0' || + (domain[0][0] == '.' && domain[0][1] == '\0')) + root_on_list++; + + if (root_on_list && tried_as_is) + continue; + + ret = res_querydomainN(name, *domain, target, res); + 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) { + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); + } + + switch (res->res_h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + got_servfail++; + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + 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++; + } + } + + switch (res->res_h_errno) { + case NO_DATA: + case HOST_NOT_FOUND: + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) + break; + /* FALLTHROUGH */ + default: + goto giveup; + } + + /* + * If the query has not already been tried as is then try it + * unless RES_NOTLDQUERY is set and there were no dots. + */ + if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && + !(tried_as_is || root_on_list)) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0) + return (ret); + } + + /* + * 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. + */ +giveup: + if (saved_herrno != -1) + RES_SET_H_ERRNO(res, saved_herrno); + else if (got_nodata) + RES_SET_H_ERRNO(res, NO_DATA); + else if (got_servfail) + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); +} + +/* + * 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(const char *name, const char *domain, + struct res_target *target, res_state res) +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; + +#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) { + RES_SET_H_ERRNO(res, 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) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (-1); + } + snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); + } + return (res_queryN(longname, target, res)); +} diff --git a/libc/nsswitch.h b/libc/nsswitch.h new file mode 100644 index 0000000..f713129 --- /dev/null +++ b/libc/nsswitch.h @@ -0,0 +1,246 @@ +/* $NetBSD: nsswitch.h,v 1.6 1999/01/26 01:04:07 lukem Exp $ */ +/* $FreeBSD$ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * + * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _NSSWITCH_H +#define _NSSWITCH_H 1 + +#include <sys/types.h> +#include <stdarg.h> + +#define NSS_MODULE_INTERFACE_VERSION 1 + +#ifndef _PATH_NS_CONF +#define _PATH_NS_CONF "/etc/nsswitch.conf" +#endif + +/* NSS source actions */ +#define NS_ACTION_CONTINUE 0 /* try the next source */ +#define NS_ACTION_RETURN 1 /* look no further */ + +#define NS_SUCCESS (1<<0) /* entry was found */ +#define NS_UNAVAIL (1<<1) /* source not responding, or corrupt */ +#define NS_NOTFOUND (1<<2) /* source responded 'no such entry' */ +#define NS_TRYAGAIN (1<<3) /* source busy, may respond to retry */ +#define NS_RETURN (1<<4) /* stop search, e.g. for ERANGE */ +#define NS_TERMINATE (NS_SUCCESS|NS_RETURN) /* flags that end search */ +#define NS_STATUSMASK 0x000000ff /* bitmask to get the status flags */ + +/* + * currently implemented sources + */ +#define NSSRC_FILES "files" /* local files */ +#define NSSRC_DB "db" /* database */ +#define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */ +#define NSSRC_NIS "nis" /* YP/NIS */ +#define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */ +#define NSSRC_CACHE "cache" /* nscd daemon */ +#define NSSRC_FALLBACK "__fallback" /* internal fallback source */ + +/* + * currently implemented databases + */ +#define NSDB_HOSTS "hosts" +#define NSDB_GROUP "group" +#define NSDB_GROUP_COMPAT "group_compat" +#define NSDB_NETGROUP "netgroup" +#define NSDB_NETWORKS "networks" +#define NSDB_PASSWD "passwd" +#define NSDB_PASSWD_COMPAT "passwd_compat" +#define NSDB_SHELLS "shells" +#define NSDB_SERVICES "services" +#define NSDB_SERVICES_COMPAT "services_compat" +#define NSDB_SSH_HOSTKEYS "ssh_hostkeys" +#define NSDB_PROTOCOLS "protocols" +#define NSDB_RPC "rpc" + +/* + * suggested databases to implement + */ +#define NSDB_ALIASES "aliases" +#define NSDB_AUTH "auth" +#define NSDB_AUTOMOUNT "automount" +#define NSDB_BOOTPARAMS "bootparams" +#define NSDB_ETHERS "ethers" +#define NSDB_EXPORTS "exports" +#define NSDB_NETMASKS "netmasks" +#define NSDB_PHONES "phones" +#define NSDB_PRINTCAP "printcap" +#define NSDB_REMOTE "remote" +#define NSDB_SENDMAILVARS "sendmailvars" +#define NSDB_TERMCAP "termcap" +#define NSDB_TTYS "ttys" + +/* + * ns_dtab `method' function signature. + */ +typedef int (*nss_method)(void *_retval, void *_mdata, va_list _ap); + +/* + * Macro for generating method prototypes. + */ +#define NSS_METHOD_PROTOTYPE(method) \ + int method(void *, void *, va_list) + +/* + * ns_dtab - `nsswitch dispatch table' + * Contains an entry for each source and the appropriate function to + * call. ns_dtabs are used in the nsdispatch() API in order to allow + * the application to override built-in actions. + */ +typedef struct _ns_dtab { + const char *src; /* Source this entry implements */ + nss_method method; /* Method to be called */ + void *mdata; /* Data passed to method */ +} ns_dtab; + +/* + * macros to help build an ns_dtab[] + */ +#define NS_FILES_CB(F,C) { NSSRC_FILES, F, C }, +#define NS_COMPAT_CB(F,C) { NSSRC_COMPAT, F, C }, +#define NS_FALLBACK_CB(F) { NSSRC_FALLBACK, F, NULL }, + +#ifdef HESIOD +# define NS_DNS_CB(F,C) { NSSRC_DNS, F, C }, +#else +# define NS_DNS_CB(F,C) +#endif + +#ifdef YP +# define NS_NIS_CB(F,C) { NSSRC_NIS, F, C }, +#else +# define NS_NIS_CB(F,C) +#endif + +/* + * ns_src - `nsswitch source' + * used by the nsparser routines to store a mapping between a source + * and its dispatch control flags for a given database. + */ +typedef struct _ns_src { + const char *name; + u_int32_t flags; +} ns_src; + + +/* + * default sourcelist (if nsswitch.conf is missing, corrupt, + * or the requested database doesn't have an entry. + */ +extern const ns_src __nsdefaultsrc[]; + +/* + * ns_mtab - NSS method table + * An NSS module provides a mapping from (database name, method name) + * tuples to the nss_method and associated data. + */ +typedef struct _ns_mtab { + const char *database; + const char *name; + nss_method method; + void *mdata; +} ns_mtab; + +/* + * NSS module de-registration, called at module unload. + */ +typedef void (*nss_module_unregister_fn)(ns_mtab *, unsigned int); + +/* + * NSS module registration, called at module load. + */ +typedef ns_mtab *(*nss_module_register_fn)(const char *, unsigned int *, + nss_module_unregister_fn *); + +/* + * Many NSS interfaces follow the getXXnam, getXXid, getXXent pattern. + * Developers are encouraged to use nss_lookup_type where approriate. + */ +enum nss_lookup_type { + nss_lt_name = 1, + nss_lt_id = 2, + nss_lt_all = 3 +}; + +#ifdef _NS_PRIVATE +/* + * private data structures for back-end nsswitch implementation + */ + +/* + * ns_dbt - `nsswitch database thang' + * for each database in /etc/nsswitch.conf there is a ns_dbt, with its + * name and a list of ns_src's containing the source information. + */ +typedef struct _ns_dbt { + const char *name; /* name of database */ + ns_src *srclist; /* list of sources */ + int srclistsize; /* size of srclist */ +} ns_dbt; + +/* + * ns_mod - NSS module + */ +typedef struct _ns_mod { + char *name; /* module name */ + void *handle; /* handle from dlopen */ + ns_mtab *mtab; /* method table */ + unsigned int mtabsize; /* count of entries in method table */ + nss_module_unregister_fn unregister; /* called to unload module */ +} ns_mod; + +#endif /* _NS_PRIVATE */ + + +#include <sys/cdefs.h> + +__BEGIN_DECLS +extern int nsdispatch(void *, const ns_dtab [], const char *, + const char *, const ns_src [], ...); + +#ifdef _NS_PRIVATE +extern void _nsdbtaddsrc(ns_dbt *, const ns_src *); +extern void _nsdbtput(const ns_dbt *); +extern void _nsyyerror(const char *); +extern int _nsyylex(void); +extern int _nsyyparse(void); +extern int _nsyylineno; +#ifdef _NSS_DEBUG +extern void _nsdbtdump(const ns_dbt *); +#endif +#endif /* _NS_PRIVATE */ + +__END_DECLS + +#endif /* !_NSSWITCH_H */ diff --git a/libc/vars.c b/libc/vars.c new file mode 100644 index 0000000..5bcfb9f --- /dev/null +++ b/libc/vars.c @@ -0,0 +1,47 @@ +/* $KAME: vars.c,v 1.2 2001/08/20 02:32:41 itojun Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <netinet/in.h> + +/* + * Definitions of some costant IPv6 addresses. + */ +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; +const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; + diff --git a/net/if_nametoindex.c b/net/if_nametoindex.c new file mode 100644 index 0000000..2966078 --- /dev/null +++ b/net/if_nametoindex.c @@ -0,0 +1,111 @@ +/* $KAME: if_nametoindex.c,v 1.6 2000/11/24 08:18:54 itojun Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-1-Clause + * + * Copyright (c) 1997, 2000 + * Berkeley Software Design, Inc. 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. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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. + * + * BSDI Id: if_nametoindex.c,v 2.3 2000/04/17 22:38:05 dab Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifndef __rtems__ +#include "namespace.h" +#endif /* __rtems__ */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#ifndef __rtems__ +#include "un-namespace.h" +#else /* __rtems__ */ +#define _socket socket +#define _ioctl ioctl +#define _close close +#define LLINDEX(s) ((s)->sdl_index) +#endif /* __rtems__ */ + +/* + * From RFC 2553: + * + * 4.1 Name-to-Index + * + * + * The first function maps an interface name into its corresponding + * index. + * + * #include <net/if.h> + * + * unsigned int if_nametoindex(const char *ifname); + * + * If the specified interface name does not exist, the return value is + * 0, and errno is set to ENXIO. If there was a system error (such as + * running out of memory), the return value is 0 and errno is set to the + * proper value (e.g., ENOMEM). + */ + +unsigned int +if_nametoindex(const char *ifname) +{ + int s; + struct ifreq ifr; + struct ifaddrs *ifaddrs, *ifa; + unsigned int ni; + + s = _socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (s != -1) { + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (_ioctl(s, SIOCGIFINDEX, &ifr) != -1) { + _close(s); + return (ifr.ifr_index); + } + _close(s); + } + + if (getifaddrs(&ifaddrs) < 0) + return(0); + + ni = 0; + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK && + strcmp(ifa->ifa_name, ifname) == 0) { + ni = LLINDEX((struct sockaddr_dl*)ifa->ifa_addr); + break; + } + } + + freeifaddrs(ifaddrs); + if (!ni) + errno = ENXIO; + return(ni); +} diff --git a/netsources.py b/netsources.py index 9f1b7ff..54dbbf1 100644 --- a/netsources.py +++ b/netsources.py @@ -30,8 +30,9 @@ class source: network = [ # rtems 'rtems/mkrootfs.c', - 'rtems/rtems_bootp.c', 'rtems/rtems-bsd-iface.c', + 'rtems/rtems-kernel-program.c', + 'rtems/rtems_bootp.c', 'rtems/rtems_bsdnet_malloc_starvation.c', 'rtems/rtems_dhcp.c', 'rtems/rtems_dhcp_failsafe.c', @@ -74,6 +75,7 @@ class source: 'net/if.c', 'net/if_ethersubr.c', 'net/if_loop.c', + 'net/if_nametoindex.c', 'net/if_ppp.c', 'net/ppp_tty.c', 'net/radix.c', @@ -166,6 +168,8 @@ class source: 'lib/syslog.c', # libc 'libc/base64.c', + 'libc/gai_strerror.c', + 'libc/getaddrinfo.c', 'libc/gethostbydns.c', 'libc/gethostbyht.c', 'libc/gethostbynis.c', @@ -214,6 +218,7 @@ class source: 'libc/res_stubs.c', 'libc/res_update.c', 'libc/send.c', + 'libc/vars.c', # libtest 'libtest/testbeginend.c', 'libtest/testbusy.c', @@ -1,7 +1,7 @@ /* * Copyright (c) 1983, 1987, 1989 * The Regents of the University of California. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -131,6 +131,7 @@ struct __res_state { struct in_addr addr; u_int32_t mask; } sort_list[MAXRESOLVSORT]; + int res_h_errno; /*%< last one set for this context */ char pad[72]; /* on an i386 this means 512b total */ }; diff --git a/rtems/rtems-kernel-program.c b/rtems/rtems-kernel-program.c new file mode 100644 index 0000000..6dd8028 --- /dev/null +++ b/rtems/rtems-kernel-program.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore <kinsey.moore@oarcorp.com> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +#include <machine/rtems-bsd-kernel-space.h> +#include <machine/rtems-bsd-thread.h> + +#include <sys/types.h> +#include <sys/kernel.h> + +#include <stdio.h> + +#include <rtems/extension.h> +#include <rtems/score/percpu.h> +#include <rtems/score/thread.h> +#include <rtems/sysinit.h> + +static size_t rtems_bsd_extension_index; + +static const rtems_extensions_table rtems_bsd_extensions = {}; + +/* + * This must only be run after the system has become multithreading such as in + * Init(). This must not be run as part of normal RTEMS initialization as part + * of a SYSINIT call. + */ +void rtems_bsd_compat_initialize(void) +{ + rtems_id ext_id; + rtems_status_code sc; + + sc = rtems_extension_create( + BSD_TASK_NAME, + &rtems_bsd_extensions, + &ext_id + ); + if (sc != RTEMS_SUCCESSFUL) { + printf("%s: cannot create extension\n", __func__); + rtems_task_suspend(RTEMS_SELF); + rtems_fatal_error_occurred(0xdeadbeef); + } + + rtems_bsd_extension_index = rtems_object_id_get_index(ext_id); +} + +static struct rtems_bsd_program_control ** +rtems_bsd_get_bsd_extension_ptr(void) +{ + Thread_Control *executing = _Thread_Get_executing(); + return ( struct rtems_bsd_program_control ** )&executing->extensions[rtems_bsd_extension_index]; +} + +struct rtems_bsd_program_control * +rtems_bsd_program_get_control_or_null(void) +{ + return *rtems_bsd_get_bsd_extension_ptr(); +} + +int +rtems_bsd_program_set_control(struct rtems_bsd_program_control *prog_ctrl) +{ + *rtems_bsd_get_bsd_extension_ptr() = prog_ctrl; + return 0; +} diff --git a/rtems_waf b/rtems_waf -Subproject 2c15b90de5c369aa78cd2252a50bba677e9b13f +Subproject 68654b4f995382765605dc16917baad4bdbf7f7 diff --git a/testsuites/resolve/getaddrinfo_test.c b/testsuites/resolve/getaddrinfo_test.c new file mode 100644 index 0000000..6674c3c --- /dev/null +++ b/testsuites/resolve/getaddrinfo_test.c @@ -0,0 +1,573 @@ +/*- + * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef __rtems__ +#include <stringlist.h> +#endif /* __rtems__ */ +#include <unistd.h> + +#ifndef __rtems__ +#include <atf-c.h> + +#include "freebsd_test_suite/macros.h" +#include "testutil.h" +#endif /* __rtems__ */ + +enum test_methods { + TEST_GETADDRINFO, + TEST_BUILD_SNAPSHOT +}; + +static struct addrinfo hints; +static enum test_methods method = TEST_GETADDRINFO; + +#ifndef __rtems__ +DECLARE_TEST_DATA(addrinfo) +DECLARE_TEST_FILE_SNAPSHOT(addrinfo) +DECLARE_2PASS_TEST(addrinfo) +#endif /* __rtems__ */ + +static void clone_addrinfo(struct addrinfo *, struct addrinfo const *); +static int compare_addrinfo(struct addrinfo *, struct addrinfo *, void *); +static void dump_addrinfo(struct addrinfo *); + +static void sdump_addrinfo(struct addrinfo *, char *, size_t); + +#ifndef __rtems__ +IMPLEMENT_TEST_DATA(addrinfo) +IMPLEMENT_TEST_FILE_SNAPSHOT(addrinfo) +IMPLEMENT_2PASS_TEST(addrinfo) +#else +#include <assert.h> +#define ATF_REQUIRE(e) assert(e) +#define ATF_REQUIRE_MSG(e, f, ...) assert(e) +#endif /* __rtems__ */ + +static void +clone_addrinfo(struct addrinfo *dest, struct addrinfo const *src) +{ + + ATF_REQUIRE(dest != NULL); + ATF_REQUIRE(src != NULL); + + memcpy(dest, src, sizeof(struct addrinfo)); + if (src->ai_canonname != NULL) + dest->ai_canonname = strdup(src->ai_canonname); + + if (src->ai_addr != NULL) { + dest->ai_addr = malloc(src->ai_addrlen); + ATF_REQUIRE(dest->ai_addr != NULL); + memcpy(dest->ai_addr, src->ai_addr, src->ai_addrlen); + } + + if (src->ai_next != NULL) { + dest->ai_next = malloc(sizeof(struct addrinfo)); + ATF_REQUIRE(dest->ai_next != NULL); + clone_addrinfo(dest->ai_next, src->ai_next); + } +} + +static int +compare_addrinfo_(struct addrinfo *ai1, struct addrinfo *ai2) +{ + + if ((ai1 == NULL) || (ai2 == NULL)) + return (-1); + + if (ai1->ai_flags != ai2->ai_flags || + ai1->ai_family != ai2->ai_family || + ai1->ai_socktype != ai2->ai_socktype || + ai1->ai_protocol != ai2->ai_protocol || + ai1->ai_addrlen != ai2->ai_addrlen || + ((ai1->ai_addr == NULL || ai2->ai_addr == NULL) && + ai1->ai_addr != ai2->ai_addr) || + ((ai1->ai_canonname == NULL || ai2->ai_canonname == NULL) && + ai1->ai_canonname != ai2->ai_canonname)) + return (-1); + + if (ai1->ai_canonname != NULL && + strcmp(ai1->ai_canonname, ai2->ai_canonname) != 0) + return (-1); + + if (ai1->ai_addr != NULL && + memcmp(ai1->ai_addr, ai2->ai_addr, ai1->ai_addrlen) != 0) + return (-1); + + if (ai1->ai_next == NULL && ai2->ai_next == NULL) + return (0); + else + return (compare_addrinfo_(ai1->ai_next, ai2->ai_next)); +} + +static int +compare_addrinfo(struct addrinfo *ai1, struct addrinfo *ai2, + void *mdata __unused) +{ + int rv; + + printf("testing equality of 2 addrinfo structures\n"); + + rv = compare_addrinfo_(ai1, ai2); + + if (rv == 0) + printf("equal\n"); + else { + dump_addrinfo(ai1); + dump_addrinfo(ai2); + printf("not equal\n"); + } + + return (rv); +} + +static void +free_addrinfo(struct addrinfo *ai) +{ + if (ai == NULL) + return; + + free(ai->ai_addr); + free(ai->ai_canonname); + free_addrinfo(ai->ai_next); +} + +void +sdump_addrinfo(struct addrinfo *ai, char *buffer, size_t buflen) +{ + int written, i; + + written = snprintf(buffer, buflen, "%d %d %d %d %d ", + ai->ai_flags, ai->ai_family, ai->ai_socktype, ai->ai_protocol, + ai->ai_addrlen); + buffer += written; + if (written > (int)buflen) + return; + buflen -= written; + + written = snprintf(buffer, buflen, "%s ", + ai->ai_canonname == NULL ? "(null)" : ai->ai_canonname); + buffer += written; + if (written > (int)buflen) + return; + buflen -= written; + + if (ai->ai_addr == NULL) { + written = snprintf(buffer, buflen, "(null)"); + buffer += written; + if (written > (int)buflen) + return; + buflen -= written; + } else { + for (i = 0; i < (int)ai->ai_addrlen; i++) { + written = snprintf(buffer, buflen, + i + 1 != (int)ai->ai_addrlen ? "%d." : "%d", + ((unsigned char *)ai->ai_addr)[i]); + buffer += written; + if (written > (int)buflen) + return; + buflen -= written; + + if (buflen == 0) + return; + } + } + + if (ai->ai_next != NULL) { + written = snprintf(buffer, buflen, ":"); + buffer += written; + if (written > (int)buflen) + return; + buflen -= written; + + sdump_addrinfo(ai->ai_next, buffer, buflen); + } +} + +void +dump_addrinfo(struct addrinfo *result) +{ + if (result != NULL) { + char buffer[2048]; + sdump_addrinfo(result, buffer, sizeof(buffer)); + printf("%s\n", buffer); + } else + printf("(null)\n"); +} + +static int +addrinfo_read_snapshot_addr(char *addr, unsigned char *result, size_t len) +{ + char *s, *ps, *ts; + + ps = addr; + while ((s = strsep(&ps, ".")) != NULL) { + if (len == 0) + return (-1); + + *result = (unsigned char)strtol(s, &ts, 10); + ++result; + if (*ts != '\0') + return (-1); + + --len; + } + if (len != 0) + return (-1); + else + return (0); +} + +static int +addrinfo_read_snapshot_ai(struct addrinfo *ai, char *line) +{ + char *s, *ps, *ts; + int i, rv, *pi; + + rv = 0; + i = 0; + ps = line; + memset(ai, 0, sizeof(struct addrinfo)); + while ((s = strsep(&ps, " ")) != NULL) { + switch (i) { + case 0: + case 1: + case 2: + case 3: + pi = &ai->ai_flags + i; + *pi = (int)strtol(s, &ts, 10); + if (*ts != '\0') + goto fin; + break; + case 4: + ai->ai_addrlen = (socklen_t)strtol(s, &ts, 10); + if (*ts != '\0') + goto fin; + break; + case 5: + if (strcmp(s, "(null)") != 0) { + ai->ai_canonname = strdup(s); + ATF_REQUIRE(ai->ai_canonname != NULL); + } + break; + case 6: + if (strcmp(s, "(null)") != 0) { + ai->ai_addr = calloc(1, ai->ai_addrlen); + ATF_REQUIRE(ai->ai_addr != NULL); + rv = addrinfo_read_snapshot_addr(s, + (unsigned char *)ai->ai_addr, + ai->ai_addrlen); + + if (rv != 0) + goto fin; + } + break; + default: + /* NOTE: should not be reachable */ + rv = -1; + goto fin; + } + + ++i; + } + +fin: + if (i != 7 || rv != 0) { + free_addrinfo(ai); + ai = NULL; + return (-1); + } + + return (0); +} + +static int +addrinfo_read_snapshot_func(struct addrinfo *ai, char *line) +{ + struct addrinfo *ai2; + char *s, *ps; + int rv; + + printf("1 line read from snapshot:\n%s\n", line); + + rv = 0; + ps = line; + + s = strsep(&ps, ":"); + if (s == NULL) + return (-1); + + rv = addrinfo_read_snapshot_ai(ai, s); + if (rv != 0) + return (-1); + + ai2 = ai; + while ((s = strsep(&ps, ":")) != NULL) { + ai2->ai_next = calloc(1, sizeof(struct addrinfo)); + ATF_REQUIRE(ai2->ai_next != NULL); + + rv = addrinfo_read_snapshot_ai(ai2->ai_next, s); + if (rv != 0) { + free_addrinfo(ai); + ai = NULL; + return (-1); + } + + ai2 = ai2->ai_next; + } + + return (0); +} + +static int +addrinfo_test_correctness(struct addrinfo *ai, void *mdata __unused) +{ + + printf("testing correctness with the following data:\n"); + dump_addrinfo(ai); + + if (ai == NULL) + goto errfin; + + if (!(ai->ai_family >= 0 && ai->ai_family < AF_MAX)) + goto errfin; + + if (ai->ai_socktype != 0 && ai->ai_socktype != SOCK_STREAM && + ai->ai_socktype != SOCK_DGRAM && ai->ai_socktype != SOCK_RAW) + goto errfin; + + if (ai->ai_protocol != 0 && ai->ai_protocol != IPPROTO_UDP && + ai->ai_protocol != IPPROTO_TCP) + goto errfin; + +#ifndef __rtems__ + if ((ai->ai_flags & ~(AI_CANONNAME | AI_NUMERICHOST | AI_PASSIVE)) != 0) + goto errfin; +#endif /* __rtems__ */ + + if (ai->ai_addrlen != ai->ai_addr->sa_len || + ai->ai_family != ai->ai_addr->sa_family) + goto errfin; + + printf("correct\n"); + + return (0); +errfin: + printf("incorrect\n"); + + return (-1); +} + +int +addrinfo_read_hostlist_func(struct addrinfo *ai, char *line) +{ + struct addrinfo *result; + int rv; + + printf("resolving %s: ", line); + rv = getaddrinfo(line, NULL, &hints, &result); + if (rv == 0) { + printf("found\n"); + + rv = addrinfo_test_correctness(result, NULL); + if (rv != 0) { + freeaddrinfo(result); + result = NULL; + return (rv); + } + + clone_addrinfo(ai, result); + freeaddrinfo(result); + result = NULL; + } else { + printf("not found\n"); + + memset(ai, 0, sizeof(struct addrinfo)); + } + return (0); +} + +#ifndef __rtems__ +void +run_tests(char *hostlist_file, const char *snapshot_file, int ai_family) +{ + struct addrinfo_test_data td, td_snap; + char *snapshot_file_copy; + int rv; + + if (snapshot_file == NULL) + snapshot_file_copy = NULL; + else { + snapshot_file_copy = strdup(snapshot_file); + ATF_REQUIRE(snapshot_file_copy != NULL); + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = ai_family; + hints.ai_flags = AI_CANONNAME; + + if (snapshot_file != NULL) + method = TEST_BUILD_SNAPSHOT; + + TEST_DATA_INIT(addrinfo, &td, clone_addrinfo, free_addrinfo); + TEST_DATA_INIT(addrinfo, &td_snap, clone_addrinfo, free_addrinfo); + + ATF_REQUIRE_MSG(access(hostlist_file, R_OK) == 0, + "can't access the hostlist file %s\n", hostlist_file); + + printf("building host lists from %s\n", hostlist_file); + + rv = TEST_SNAPSHOT_FILE_READ(addrinfo, hostlist_file, &td, + addrinfo_read_hostlist_func); + if (rv != 0) + goto fin; + + if (snapshot_file != NULL) { + if (access(snapshot_file, W_OK | R_OK) != 0) { + if (errno == ENOENT) + method = TEST_BUILD_SNAPSHOT; + else { + printf("can't access the snapshot " + "file %s\n", snapshot_file); + + rv = -1; + goto fin; + } + } else { + rv = TEST_SNAPSHOT_FILE_READ(addrinfo, snapshot_file, + &td_snap, addrinfo_read_snapshot_func); + if (rv != 0) { + printf("error reading snapshot file: %s\n", + strerror(errno)); + goto fin; + } + } + } + + switch (method) { + case TEST_GETADDRINFO: + if (snapshot_file != NULL) + ATF_CHECK(DO_2PASS_TEST(addrinfo, &td, &td_snap, + compare_addrinfo, NULL) == 0); + break; + case TEST_BUILD_SNAPSHOT: + if (snapshot_file != NULL) { + ATF_CHECK(TEST_SNAPSHOT_FILE_WRITE(addrinfo, + snapshot_file, &td, sdump_addrinfo) == 0); + } + break; + default: + break; + } + +fin: + TEST_DATA_DESTROY(addrinfo, &td_snap); + TEST_DATA_DESTROY(addrinfo, &td); + + free(snapshot_file_copy); +} + +#define HOSTLIST_FILE "mach" +#define RUN_TESTS(tc, snapshot_file, ai_family) do { \ + char *_hostlist_file; \ + ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \ + atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \ + run_tests(_hostlist_file, snapshot_file, ai_family); \ + free(_hostlist_file); \ +} while (0) + +ATF_TC_WITHOUT_HEAD(pf_unspec); +ATF_TC_BODY(pf_unspec, tc) +{ + + RUN_TESTS(tc, NULL, AF_UNSPEC); +} + +ATF_TC_WITHOUT_HEAD(pf_unspec_with_snapshot); +ATF_TC_BODY(pf_unspec_with_snapshot, tc) +{ + + RUN_TESTS(tc, "snapshot_ai", AF_UNSPEC); +} + +ATF_TC_WITHOUT_HEAD(pf_inet); +ATF_TC_BODY(pf_inet, tc) +{ + + ATF_REQUIRE_FEATURE("inet"); + RUN_TESTS(tc, NULL, AF_INET); +} + +ATF_TC_WITHOUT_HEAD(pf_inet_with_snapshot); +ATF_TC_BODY(pf_inet_with_snapshot, tc) +{ + + ATF_REQUIRE_FEATURE("inet"); + RUN_TESTS(tc, "snapshot_ai4", AF_INET); +} + +ATF_TC_WITHOUT_HEAD(pf_inet6); +ATF_TC_BODY(pf_inet6, tc) +{ + + ATF_REQUIRE_FEATURE("inet6"); + RUN_TESTS(tc, NULL, AF_INET6); +} + +ATF_TC_WITHOUT_HEAD(pf_inet6_with_snapshot); +ATF_TC_BODY(pf_inet6_with_snapshot, tc) +{ + + ATF_REQUIRE_FEATURE("inet6"); + RUN_TESTS(tc, "snapshot_ai6", AF_INET6); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, pf_unspec); + ATF_TP_ADD_TC(tp, pf_unspec_with_snapshot); + ATF_TP_ADD_TC(tp, pf_inet); + ATF_TP_ADD_TC(tp, pf_inet_with_snapshot); + ATF_TP_ADD_TC(tp, pf_inet6); + ATF_TP_ADD_TC(tp, pf_inet6_with_snapshot); + + return (atf_no_error()); +} +#endif /* __rtems__ */ diff --git a/testsuites/resolve/init.c b/testsuites/resolve/init.c new file mode 100644 index 0000000..beb0e9a --- /dev/null +++ b/testsuites/resolve/init.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Chris Johns <chris@contemporay.software>. All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/dhcp.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/telnetd.h> + +#include <tmacros.h> +#include <net-legacy-config.h> +#include <network-config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <resolv.h> + +#define START_SHELL 0 + +const char rtems_test_name[] = "RESOLVE"; + +#define MACH_SIZE 45 +static const char* mach[MACH_SIZE] = { + "localhost", + "anoncvs.cirr.com", + "anoncvs.netbsd.se", + "antioche.antioche.eu.org", + "centaurus.4web.cz", + "chur.math.ntnu.no", + "console.netbsd.org", + "cvs.netbsd.org", + "cvsup.netbsd.se", + "ftp.chg.ru", + "ftp.estpak.ee", + "ftp.fsn.hu", + "ftp.funet.fi", + "ftp.netbsd.org", + "ftp.nluug.nl", + "ftp.plig.org", + "ftp.uni-erlangen.de", + "ftp.xgate.co.kr", + "gd.tuwien.ac.at", + "gort.ludd.luth.se", + "irc.warped.net", + "knug.youn.co.kr", + "mail.jp.netbsd.org", + "mail.netbsd.org", + "melanoma.cs.rmit.edu.au", + "mirror.aarnet.edu.au", + "moon.vub.ac.be", + "net.bsd.cz", + "netbsd.3miasto.net", + "netbsd.4ka.mipt.ru", + "netbsd.csie.nctu.edu.tw", + "netbsd.enderunix.org", + "netbsd.ftp.fu-berlin.de", + "netbsd.pair.com", + "netbsdiso.interoute.net.uk", + "netbsdwww.cs.rmit.edu.au", + "netbsdwww.interoute.net.uk", + "ns.netbsd.org", + "skeleton.phys.spbu.ru", + "www.en.netbsd.de", + "www.netbsd.cl", + "www.netbsd.nl", + "www.netbsd.org", + "www.netbsd.ro", + "zeppo.rediris.es" +}; + +extern int addrinfo_read_hostlist_func(struct addrinfo *ai, const char *line); + +static int resolve_test(void) { + struct addrinfo addr; + size_t l; + int r = 0; + printf("getaddrinfo test start\n"); + for (l = 0; l < MACH_SIZE - 1; ++l) { + r = addrinfo_read_hostlist_func(&addr, mach[l]); + if (r != 0) { + break; + } + } + fflush(stdout); + printf("getaddrinfo test finish\n\n\n"); + sleep(2); + return r; +} + +struct rtems_bsdnet_config rtems_bsdnet_config; + +static rtems_task Init(rtems_task_argument argument) +{ + rtems_status_code sc; + int rv; + + TEST_BEGIN(); + + rtems_test_assert(rtems_net_legacy_config(&rtems_bsdnet_config)); + + rtems_bsdnet_config.domainname = "gemini.edu"; + rtems_bsdnet_config.name_server[0] = "10.1.5.8"; + + rv = rtems_bsdnet_initialize_network(); + rtems_test_assert(rv == 0); + + rv = resolve_test(); + rtems_test_assert(rv == 0); + +#if START_SHELL + sc = rtems_shell_init( + "SHLL", /* task name */ + RTEMS_MINIMUM_STACK_SIZE * 4, /* task stack size */ + 1, /* task priority */ + "/dev/console", /* device name */ + false, /* run forever */ + true, /* wait for shell to terminate */ + NULL /* login check function, + use NULL to disable a login check */ + ); + rtems_test_assert( sc == RTEMS_SUCCESSFUL ); +#endif + + TEST_END(); + sleep(2); + rtems_test_exit(0); +} + +#define CONFIGURE_INIT + +#define CONFIGURE_MICROSECONDS_PER_TICK 10000 + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK + +#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 32 + +#define CONFIGURE_MAXIMUM_TASKS 12 + +#define CONFIGURE_MAXIMUM_POSIX_KEYS 10 +#define CONFIGURE_MAXIMUM_SEMAPHORES 20 +#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 10 + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT + +#define CONFIGURE_UNLIMITED_OBJECTS +#define CONFIGURE_UNIFIED_WORK_AREAS + +#define RTEMS_NETWORKING 1 +#define CONFIGURE_SHELL_COMMANDS_INIT +#define CONFIGURE_SHELL_COMMANDS_ALL +#define CONFIGURE_SHELL_COMMANDS_ALL_NETWORKING + +#include <rtems/shellconfig.h> + +#include <rtems/confdefs.h> diff --git a/testsuites/resolve/resolve.doc b/testsuites/resolve/resolve.doc new file mode 100644 index 0000000..d58ef5b --- /dev/null +++ b/testsuites/resolve/resolve.doc @@ -0,0 +1,11 @@ +This file describes the directives and concepts tested by this test set. + +test set name: resolve + +directives: + + - getaddrinfo + +concepts: + ++ Runs a tests from FreeBSD for getaddrinfo the mach file diff --git a/testsuites/resolve/resolve.scn b/testsuites/resolve/resolve.scn new file mode 100644 index 0000000..2014d06 --- /dev/null +++ b/testsuites/resolve/resolve.scn @@ -0,0 +1,166 @@ +*** BEGIN OF TEST RESOLVE *** +*** TEST VERSION: 6.0.0.7e119562ae48d170604d3c82dcf2425a0a1ae3cd +*** TEST STATE: EXPECTED_PASS +*** TEST BUILD: RTEMS_POSIX_API +*** TEST TOOLS: 12.2.1 20230224 (RTEMS 6, RSB 77dc54ce841a04598c64073c217aa9e66df31d8f, Newlib 17ac400) +dec21140 : found device 'dc1', bus 0x00, dev 0x0e, func 0x00 +dec2114x : unit 1 base address 0xc1042100. +dec2114x : driver attached +dec2114x : driver tasks created +dec2114x : 00:01:af:03:6d:d0 name 'dc1', io 11100, mem c1042100, int 10 +dec2114x: Installing IRQ 10 +getaddrinfo test start +resolving localhost: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.127.0.0.1.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.127.0.0.1.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.127.0.0.1.0.0.0.0.0.0.0.0 +correct +resolving anoncvs.cirr.com: not found +resolving anoncvs.netbsd.se: not found +resolving antioche.antioche.eu.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.132.227.74.11.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.132.227.74.11.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.132.227.74.11.0.0.0.0.0.0.0.0 +correct +resolving centaurus.4web.cz: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.146.112.61.106.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.146.112.61.106.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.146.112.61.106.0.0.0.0.0.0.0.0 +correct +resolving chur.math.ntnu.no: not found +resolving console.netbsd.org: not found +resolving cvs.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.199.233.217.197.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.199.233.217.197.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.199.233.217.197.0.0.0.0.0.0.0.0 +correct +resolving cvsup.netbsd.se: not found +resolving ftp.chg.ru: not found +resolving ftp.estpak.ee: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.62.65.42.236.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.62.65.42.236.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.62.65.42.236.0.0.0.0.0.0.0.0 +correct +resolving ftp.fsn.hu: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.195.228.252.133.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.195.228.252.133.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.195.228.252.133.0.0.0.0.0.0.0.0 +correct +resolving ftp.funet.fi: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.193.166.3.2.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.193.166.3.2.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.193.166.3.2.0.0.0.0.0.0.0.0 +correct +resolving ftp.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.199.233.217.201.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.199.233.217.201.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.199.233.217.201.0.0.0.0.0.0.0.0 +correct +resolving ftp.nluug.nl: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.145.220.21.40.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.145.220.21.40.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.145.220.21.40.0.0.0.0.0.0.0.0 +correct +resolving ftp.plig.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.188.226.162.120.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.188.226.162.120.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.188.226.162.120.0.0.0.0.0.0.0.0 +correct +resolving ftp.uni-erlangen.de: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.131.188.12.211.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.131.188.12.211.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.131.188.12.211.0.0.0.0.0.0.0.0 +correct +resolving ftp.xgate.co.kr: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.203.254.197.172.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.203.254.197.172.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.203.254.197.172.0.0.0.0.0.0.0.0 +correct +resolving gd.tuwien.ac.at: not found +resolving gort.ludd.luth.se: not found +resolving irc.warped.net: not found +resolving knug.youn.co.kr: not found +resolving mail.jp.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.210.148.108.146.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.210.148.108.146.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.210.148.108.146.0.0.0.0.0.0.0.0 +correct +resolving mail.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0 +correct +resolving melanoma.cs.rmit.edu.au: not found +resolving mirror.aarnet.edu.au: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.202.158.214.106.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.202.158.214.106.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.202.158.214.106.0.0.0.0.0.0.0.0 +correct +resolving moon.vub.ac.be: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.134.184.49.5.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.134.184.49.5.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.134.184.49.5.0.0.0.0.0.0.0.0 +correct +resolving net.bsd.cz: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.94.124.105.4.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.94.124.105.4.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.94.124.105.4.0.0.0.0.0.0.0.0 +correct +resolving netbsd.3miasto.net: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.104.247.81.52.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.104.247.81.52.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.104.247.81.52.0.0.0.0.0.0.0.0 +correct +resolving netbsd.4ka.mipt.ru: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.93.175.29.188.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.93.175.29.188.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.93.175.29.188.0.0.0.0.0.0.0.0 +correct +resolving netbsd.csie.nctu.edu.tw: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.140.113.17.207.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.140.113.17.207.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.140.113.17.207.0.0.0.0.0.0.0.0 +correct +resolving netbsd.enderunix.org: not found +resolving netbsd.ftp.fu-berlin.de: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.130.133.3.143.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.130.133.3.143.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.130.133.3.143.0.0.0.0.0.0.0.0 +correct +resolving netbsd.pair.com: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.216.92.73.137.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.216.92.73.137.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.216.92.73.137.0.0.0.0.0.0.0.0 +correct +resolving netbsdiso.interoute.net.uk: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.212.23.56.19.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.212.23.56.19.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.212.23.56.19.0.0.0.0.0.0.0.0 +correct +resolving netbsdwww.cs.rmit.edu.au: not found +resolving netbsdwww.interoute.net.uk: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.212.23.56.18.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.212.23.56.18.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.212.23.56.18.0.0.0.0.0.0.0.0 +correct +resolving ns.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.199.233.217.200.0.0.0.0.0.0.0.0 +correct +resolving skeleton.phys.spbu.ru: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.185.148.210.80.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.185.148.210.80.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.185.148.210.80.0.0.0.0.0.0.0.0 +correct +resolving www.en.netbsd.de: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.195.22.142.107.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.195.22.142.107.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.195.22.142.107.0.0.0.0.0.0.0.0 +correct +resolving www.netbsd.cl: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.172.67.129.38.0.0.0.0.0.0.0.0:3162112 2 2 17 16 (null) 16.2.0.0.104.21.1.106.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.172.67.129.38.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.104.21.1.106.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.172.67.129.38.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.104.21.1.106.0.0.0.0.0.0.0.0 +correct +resolving www.netbsd.nl: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.91.195.241.232.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.91.195.241.232.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.91.195.241.232.0.0.0.0.0.0.0.0 +correct +resolving www.netbsd.org: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.151.101.65.6.0.0.0.0.0.0.0.0:3162112 2 2 17 16 (null) 16.2.0.0.151.101.1.6.0.0.0.0.0.0.0.0:3162112 2 2 17 16 (null) 16.2.0.0.151.101.193.6.0.0.0.0.0.0.0.0:3162112 2 2 17 16 (null) 16.2.0.0.151.101.129.6.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.151.101.65.6.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.151.101.1.6.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.151.101.193.6.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.151.101.129.6.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.151.101.65.6.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.1 +51.101.1.6.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.151.101.193.6.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.151.101.129.6.0.0.0.0.0.0.0.0 +correct +resolving www.netbsd.ro: found +testing correctness with the following data: +3162112 2 2 17 16 (null) 16.2.0.0.172.67.142.193.0.0.0.0.0.0.0.0:3162112 2 2 17 16 (null) 16.2.0.0.104.21.95.36.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.172.67.142.193.0.0.0.0.0.0.0.0:3162112 2 1 6 16 (null) 16.2.0.0.104.21.95.36.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.172.67.142.193.0.0.0.0.0.0.0.0:3162112 2 5 132 16 (null) 16.2.0.0.104.21.95.36.0.0.0.0.0.0.0.0 +correct +getaddrinfo test finish + + + +*** END OF TEST RESOLVE *** + + + + + +*** BEGIN OF TEST RESOLVE *** +*** TEST VERSION: 6.0.0.d15640249372de3106a66304569a44f6f32cca23 +*** TEST STATE: EXPECTED_PASS +*** TEST BUILD: RTEMS_POSIX_API +*** TEST TOOLS: 12.2.1 20230224 (RTEMS 6, RSB 77dc54ce841a04598c64073c217aa9e66df31d8f, Newlib 17ac400) +syslog: telnetd: started successfully on port 23 diff --git a/testsuites/resolve/wscript b/testsuites/resolve/wscript new file mode 100644 index 0000000..18ce5ea --- /dev/null +++ b/testsuites/resolve/wscript @@ -0,0 +1,49 @@ +# +# RTEMS Project (https://www.rtems.org/) +# +# Copyright (c) 2023 Chris Johns <chris@contemporary.software> +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +from rtems_waf import rtems + + +def init(ctx): + pass + + +def configure(conf): + pass + + +def build(bld): + legacy_config = bld.path.parent.find_node('support/net-legacy-config.c') + source = ['init.c', 'getaddrinfo_test.c', legacy_config] + bld.program(target='resolve.exe', + features='c cprogram', + cflags=bld.env.OPTIMIZATION + ['-g'], + includes=bld.env.IFLAGS, + lib=['networking'], + libpath=['.'], + source=source, + install_path=False) diff --git a/testsuites/wscript b/testsuites/wscript index 6bfc1eb..c0fd37d 100644 --- a/testsuites/wscript +++ b/testsuites/wscript @@ -29,7 +29,7 @@ from rtems_waf import rtems subdirs = [ - 'ftp01', 'loopback', 'networking01', 'pppd', 'syscall01', 'telnetd01', 'telnetd02' + 'ftp01', 'loopback', 'networking01', 'pppd', 'resolve', 'syscall01', 'telnetd01', 'telnetd02' ] |