📄 getaddrinfo.c
字号:
/* $KAME: getaddrinfo.c,v 1.14 2001/01/06 09:41:15 jinmei Exp $ *//* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* * Issues to be discussed: * - Thread safe-ness must be checked. * - Return values. There are nonstandard return values defined and used * in the source code. This is because RFC2553 is silent about which error * code must be returned for which situation. * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 * says to use inet_aton() to convert IPv4 numeric to binary (allows * classful form as a result). * current code - disallow classful form for IPv4 (due to use of inet_pton). * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is * invalid. * current code - SEGV on freeaddrinfo(NULL) * Note: * - We use getipnodebyname() just for thread-safeness. There's no intent * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to * getipnodebyname(). * - The code filters out AFs that are not supported by the kernel, * when globbing NULL hostname (to loopback, or wildcard). Is it the right * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG * in ai_flags? * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. * (1) what should we do against numeric hostname (2) what should we do * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? * non-loopback address configured? global address configured? * - To avoid search order issue, we have a big amount of code duplicate * from gethnamaddr.c and some other places. The issues that there's no * lower layer function to lookup "IPv4 or IPv6" record. Calling * gethostbyname2 from getaddrinfo will end up in wrong search order, as * follows: * - The code makes use of following calls when asked to resolver with * ai_family = PF_UNSPEC: * getipnodebyname(host, AF_INET6); * getipnodebyname(host, AF_INET); * This will result in the following queries if the node is configure to * prefer /etc/hosts than DNS: * lookup /etc/hosts for IPv6 address * lookup DNS for IPv6 address * lookup /etc/hosts for IPv4 address * lookup DNS for IPv4 address * which may not meet people's requirement. * The right thing to happen is to have underlying layer which does * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. * This would result in a bit of code duplicate with _dns_ghbyname() and * friends. */#include "port_before.h"#include <sys/types.h>#include <sys/param.h>#include <sys/socket.h>#include <net/if.h>#include <netinet/in.h>#include <arpa/inet.h>#include <arpa/nameser.h>#include <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 <stdarg.h>#include <irs.h>#include <isc/assertions.h>#include "port_after.h"#include "irs_data.h"#define SUCCESS 0#define ANY 0#define YES 1#define NO 0static const char in_addrany[] = { 0, 0, 0, 0 };static const char in6_addrany[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};static const char in_loopback[] = { 127, 0, 0, 1 };static const char in6_loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};static const struct afd { int a_af; int a_addrlen; int a_socklen; int a_off; const char *a_addrany; const char *a_loopback; int a_scoped;} afdl [] = { {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1}, {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0}, {0, 0, 0, 0, NULL, NULL, 0},};struct explore { int e_af; int e_socktype; int e_protocol; const char *e_protostr; int e_wild;#define WILD_AF(ex) ((ex)->e_wild & 0x01)#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)};static const struct explore explore[] = {#if 0 { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },#endif { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, { -1, 0, 0, NULL, 0 },};#define PTON_MAX 16static int str_isnumber __P((const char *));static int explore_fqdn __P((const struct addrinfo *, const char *, const char *, struct addrinfo **));static int explore_copy __P((const struct addrinfo *, const struct addrinfo *, struct addrinfo **));static int explore_null __P((const struct addrinfo *, const char *, struct addrinfo **));static int explore_numeric __P((const struct addrinfo *, const char *, const char *, struct addrinfo **));static int explore_numeric_scope __P((const struct addrinfo *, const char *, const char *, struct addrinfo **));static int get_canonname __P((const struct addrinfo *, struct addrinfo *, const char *));static struct addrinfo *get_ai __P((const struct addrinfo *, const struct afd *, const char *));static struct addrinfo *copy_ai __P((const struct addrinfo *));static int get_portmatch __P((const struct addrinfo *, const char *));static int get_port __P((const struct addrinfo *, const char *, int));static const struct afd *find_afd __P((int));static int addrconfig __P((int));static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *, u_int32_t *scopeidp));static struct net_data *init __P((void));struct addrinfo *hostent2addrinfo __P((struct hostent *, const struct addrinfo *));struct addrinfo *addr2addrinfo __P((const struct addrinfo *, const char *));#if 0static const char *ai_errlist[] = { "Success", "Address family for hostname not supported", /* EAI_ADDRFAMILY */ "Temporary failure in name resolution", /* EAI_AGAIN */ "Invalid value for ai_flags", /* EAI_BADFLAGS */ "Non-recoverable failure in name resolution", /* EAI_FAIL */ "ai_family not supported", /* EAI_FAMILY */ "Memory allocation failure", /* EAI_MEMORY */ "No address associated with hostname", /* EAI_NODATA */ "hostname nor servname provided, or not known", /* EAI_NONAME */ "servname not supported for ai_socktype", /* EAI_SERVICE */ "ai_socktype not supported", /* EAI_SOCKTYPE */ "System error returned in errno", /* EAI_SYSTEM */ "Invalid value for hints", /* EAI_BADHINTS */ "Resolved protocol is unknown", /* EAI_PROTOCOL */ "Unknown error", /* EAI_MAX */};#endif/* 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)))#if 0 /* bind8 has its own version */char *gai_strerror(ecode) int ecode;{ if (ecode < 0 || ecode > EAI_MAX) ecode = EAI_MAX; return ai_errlist[ecode];}#endifvoidfreeaddrinfo(ai) struct addrinfo *ai;{ struct addrinfo *next; do { next = ai->ai_next; if (ai->ai_canonname) free(ai->ai_canonname); /* no need to free(ai->ai_addr) */ free(ai); ai = next; } while (ai);}static intstr_isnumber(p) const char *p;{ char *ep; if (*p == '\0') return NO; ep = NULL; errno = 0; (void)strtoul(p, &ep, 10); if (errno == 0 && ep && *ep == '\0') return YES; else return NO;}intgetaddrinfo(hostname, servname, hints, res) const char *hostname, *servname; const struct addrinfo *hints; struct addrinfo **res;{ struct addrinfo sentinel; struct addrinfo *cur; int error = 0; struct addrinfo ai, ai0, *afai = NULL; struct addrinfo *pai; const struct explore *ex; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; pai->ai_flags = 0; pai->ai_family = PF_UNSPEC; pai->ai_socktype = ANY; pai->ai_protocol = ANY; pai->ai_addrlen = 0; pai->ai_canonname = NULL; pai->ai_addr = NULL; pai->ai_next = NULL; if (hostname == NULL && servname == NULL) return EAI_NONAME; if (hints) { /* error check for hints */ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) ERR(EAI_BADHINTS); /* xxx */ if (hints->ai_flags & ~AI_MASK) ERR(EAI_BADFLAGS); switch (hints->ai_family) { case PF_UNSPEC: case PF_INET: case PF_INET6: break; default: ERR(EAI_FAMILY); } memcpy(pai, hints, sizeof(*pai)); /* * if both socktype/protocol are specified, check if they * are meaningful combination. */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { for (ex = explore; ex->e_af >= 0; ex++) { if (pai->ai_family != ex->e_af) continue; if (ex->e_socktype == ANY) continue; if (ex->e_protocol == ANY) continue; if (pai->ai_socktype == ex->e_socktype && pai->ai_protocol != ex->e_protocol) { ERR(EAI_BADHINTS); } } } } /* * post-2553: AI_ALL and AI_V4MAPPED are effective only against * AF_INET6 query. They needs to be ignored if specified in other * occassions. */ switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { case AI_V4MAPPED: case AI_ALL | AI_V4MAPPED: if (pai->ai_family != AF_INET6) pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; case AI_ALL:#if 1 /* illegal */ ERR(EAI_BADFLAGS);#else pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break;#endif } /* * check for special cases. (1) numeric servname is disallowed if * socktype/protocol are left unspecified. (2) servname is disallowed * for raw and other inet{,6} sockets. */ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)#ifdef PF_INET6 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)#endif ) { ai0 = *pai; /* backup *pai */ if (pai->ai_family == PF_UNSPEC) {#ifdef PF_INET6 pai->ai_family = PF_INET6;#else pai->ai_family = PF_INET;#endif } error = get_portmatch(pai, servname); if (error) ERR(error); *pai = ai0; } ai0 = *pai; /* NULL hostname, or numeric hostname */ 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 (hostname == NULL) { /* * filter out AFs that are not supported by the kernel * XXX errno? */ if (!addrconfig(pai->ai_family)) continue; error = explore_null(pai, servname, &cur->ai_next); } else error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); if (error) goto free; while (cur && cur->ai_next) cur = cur->ai_next; } /* * XXX * If numreic representation of AF1 can be interpreted as FQDN * representation of AF2, we need to think again about the code below. */ if (sentinel.ai_next) goto good; if (pai->ai_flags & AI_NUMERICHOST) ERR(EAI_NONAME); if (hostname == NULL) ERR(EAI_NONAME); /* * hostname as alphabetical name. * We'll make sure that * - if returning addrinfo list is empty, return non-zero error * value (already known one or EAI_NONAME). * - otherwise, * + if we haven't had any errors, return 0 (i.e. success). * + if we've had an error, free the list and return the error. * without any assumption on the behavior of explore_fqdn(). */ /* first, try to query DNS for all possible address families. */ *pai = ai0; error = explore_fqdn(pai, hostname, servname, &afai); if (error) { if (afai != NULL) freeaddrinfo(afai); goto free; } if (afai == NULL) { error = EAI_NONAME; /* we've had no errors. */ goto free; } /* * we would like to prefer AF_INET6 than AF_INET, so we'll make an * outer loop by AFs. */ for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; 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; }#ifdef AI_ADDRCONFIG /* * If AI_ADDRCONFIG is specified, check if we are * expected to return the address family or not. */ if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(pai->ai_family)) continue;#endif 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 ((error = explore_copy(pai, afai, &cur->ai_next)) != 0) { freeaddrinfo(afai); goto free; } while (cur && cur->ai_next) cur = cur->ai_next; } freeaddrinfo(afai); /* afai must not be NULL at this point. */ /* we must not have got any errors. */ if (error != 0) /* just for diagnosis */ abort(); if (sentinel.ai_next) {good: *res = sentinel.ai_next; return(SUCCESS); } else { /* * All the process succeeded, but we've had an empty list. * This can happen if the given hints do not match our * candidates. */ error = EAI_NONAME; }free:bad: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); *res = NULL; return(error);}/* * FQDN hostname, DNS lookup */static intexplore_fqdn(pai, hostname, servname, res) const struct addrinfo *pai; const char *hostname; const char *servname; struct addrinfo **res;{ struct addrinfo *result; struct addrinfo *cur; struct net_data *net_data = init(); struct irs_ho *ho; int error = 0; char tmp[NS_MAXDNAME]; const char *cp; INSIST(res != NULL && *res == NULL); /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return(0); if (!net_data || !(ho = net_data->ho)) return(0);#if 0 /* XXX (notyet) */ if (net_data->ho_stayopen && net_data->ho_last && net_data->ho_last->h_addrtype == af) { if (ns_samename(name, net_data->ho_last->h_name) == 1) return (net_data->ho_last); for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) if (ns_samename(name, *hap) == 1) return (net_data->ho_last); }#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -