diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-30 08:54:21 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 1999-12-30 08:54:21 +0000 |
commit | 72f05f19f83762fb1584770a5045578abf4e38b6 (patch) | |
tree | 16c5821a3b97501d3fee6b4ef1fe7b2a3cd11aea | |
parent | bc7101c62bc75984bad49655468923107b964b31 (diff) |
replace NRL get{addr,name}info with KAME get{addr,name}info.
removed functionality:
new code will not return AF_LOCAL addrinfo struct.
added funtionality:
SOCK_RAW is permitted as ai_socktype (no servname allowed).
draft-ietf-ipngwg-scopedaddr-format-00.txt
-rw-r--r-- | include/netdb.h | 10 | ||||
-rw-r--r-- | lib/libc/net/gai_strerror.c | 6 | ||||
-rw-r--r-- | lib/libc/net/getaddrinfo.c | 1434 | ||||
-rw-r--r-- | lib/libc/net/getnameinfo.c | 446 |
4 files changed, 1223 insertions, 673 deletions
diff --git a/include/netdb.h b/include/netdb.h index 6e64b9820f7..458cbdf1a09 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: netdb.h,v 1.7 1999/07/03 18:14:51 deraadt Exp $ */ +/* $OpenBSD: netdb.h,v 1.8 1999/12/30 08:54:20 itojun Exp $ */ /* * ++Copyright++ 1980, 1983, 1988, 1993 @@ -148,10 +148,16 @@ struct protoent { #define NI_NOFQDN 4 /* return a short name if in the local domain */ #define NI_NAMEREQD 8 /* fail if either host or service name is unknown */ #define NI_DGRAM 16 /* look up datagram service instead of stream */ +#define NI_WITHSCOPEID 32 /* KAME hack: attach scopeid to host portion */ #define NI_MAXHOST MAXHOSTNAMELEN /* max host name returned by getnameinfo */ #define NI_MAXSERV 32 /* max serv. name length returned by getnameinfo */ +/* + * Scope delimit character (KAME hack) + */ +#define SCOPE_DELIMITER '@' + #define EAI_BADFLAGS -1 /* invalid value for ai_flags */ #define EAI_NONAME -2 /* name or service is not known */ #define EAI_AGAIN -3 /* temporary failure in name resolution */ @@ -163,6 +169,8 @@ struct protoent { #define EAI_ADDRFAMILY -9 /* address family for name not supported */ #define EAI_MEMORY -10 /* memory allocation failure */ #define EAI_SYSTEM -11 /* system error (code indicated in errno) */ +#define EAI_BADHINTS -12 /* invalid value for hints */ +#define EAI_PROTOCOL -13 /* resolved protocol is unknown */ struct addrinfo { int ai_flags; /* input flags */ diff --git a/lib/libc/net/gai_strerror.c b/lib/libc/net/gai_strerror.c index c701c8f4224..708fb23d180 100644 --- a/lib/libc/net/gai_strerror.c +++ b/lib/libc/net/gai_strerror.c @@ -1,3 +1,5 @@ +/* $OpenBSD: gai_strerror.c,v 1.4 1999/12/30 08:54:20 itojun Exp $ */ + /* * %%% copyright-cmetz-97-bsd * Copyright (c) 1997-1999, Craig Metz, All rights reserved. @@ -65,6 +67,10 @@ gai_strerror(int errnum) return "memory allocation failure"; case EAI_SYSTEM: return "system error"; + case EAI_BADHINTS: + return "invalid value for hints"; + case EAI_PROTOCOL: + return "resolved protocol is unknown"; default: return "unknown/invalid error"; } diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index 5f3fa20c3a7..9c89de2863e 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -1,7 +1,9 @@ +/* $OpenBSD: getaddrinfo.c,v 1.5 1999/12/30 08:54:20 itojun Exp $ */ + /* - * %%% copyright-cmetz-96-bsd - * Copyright (c) 1996-1999, Craig Metz, All rights reserved. - * + * 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: @@ -10,18 +12,14 @@ * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Craig Metz and - * by other contributors. - * 4. Neither the name of the author nor the names of contributors + * 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * + * 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 REGENTS OR CONTRIBUTORS BE LIABLE + * 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) @@ -31,537 +29,1055 @@ * SUCH DAMAGE. */ -/* getaddrinfo() v1.38 */ - /* - I'd like to thank Matti Aarnio for finding some bugs in this code and - sending me patches, as well as everyone else who has contributed to this - code (by reporting bugs, suggesting improvements, and commented on its - behavior and proposed changes). -*/ + * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. + * + * Issues to be discussed: + * - Thread safe-ness must be checked. + * - Return values. There are nonstandard return values defined and used + * in the source code. This is because RFC2553 is silent about which error + * code must be returned for which situation. + * 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 resolving FQDNs and globbing NULL hostname. Is it the right + * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG + * in ai_flags? + */ + +#if 0 +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#else +#define HAVE_SOCKADDR_SA_LEN +#define INET6 +#endif #include <sys/types.h> -#include <stdlib.h> -#include <unistd.h> +#include <sys/param.h> +#if 0 +#include <sys/sysctl.h> +#endif #include <sys/socket.h> -#include <string.h> -#include <stdio.h> -#include <sys/utsname.h> -#include <sys/un.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> -#define GAIH_OKIFUNSPEC 0x0100 -#define GAIH_EAI ~(GAIH_OKIFUNSPEC) - -static struct addrinfo nullreq = { - 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL +#if 0 +#ifndef HAVE_PORTABLE_PROTOTYPE +#include "cdecl_ext.h" +#endif + +#ifndef HAVE_U_INT32_T +#include "bittypes.h" +#endif + +#ifndef HAVE_SOCKADDR_STORAGE +#include "sockstorage.h" +#endif + +#ifndef HAVE_ADDRINFO +#include "addrinfo.h" +#endif + +#if defined(__KAME__) && defined(INET6) +# define FAITH +#endif +#endif + +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 + +#ifdef FAITH +static int translate = NO; +static struct in6_addr faith_prefix = IN6ADDR_ANY_INIT; +#endif + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - -struct gaih_service { - char *name; - int num; +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 }; -struct gaih_servtuple { - struct gaih_servtuple *next; - int socktype; - int protocol; - int port; +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + u_int32_t si_scope_id; }; -static struct gaih_servtuple nullserv = { - NULL, 0, 0, 0 +static const struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { +#ifdef INET6 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, }; -struct gaih_addrtuple { - struct gaih_addrtuple *next; - int family; - char addr[16]; - char *cname; +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 struct gaih_typeproto { - int socktype; - int protocol; - char *name; -} gaih_inet_typeproto[] = { - { 0, 0, NULL }, - { SOCK_STREAM, IPPROTO_TCP, "tcp" }, - { SOCK_DGRAM, IPPROTO_UDP, "udp" }, - { 0, 0, NULL } +static const struct explore explore[] = { +#if 0 + { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, +#endif +#ifdef INET6 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, +#endif + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, }; -static int -netdb_lookup_addr(const char *name, int af, - const struct addrinfo *req, struct gaih_addrtuple **pat) +#ifdef INET6 +#define PTON_MAX 16 +#else +#define PTON_MAX 4 +#endif + + +static int str_isnumber __P((const char *)); +static int explore_fqdn __P((const struct addrinfo *, const char *, + const char *, struct addrinfo **)); +static int explore_null __P((const struct addrinfo *, const char *, + 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_name __P((const char *, const struct afd *, struct addrinfo **, + char *, const struct addrinfo *, const char *)); +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 int get_portmatch __P((const struct addrinfo *, const char *)); +static int get_port __P((struct addrinfo *, const char *, int)); +static const struct afd *find_afd __P((int)); + +#if 0 +static char *ai_errlist[] = { + "Success", + "Address family for hostname not supported", /* EAI_ADDRFAMILY */ + "Temporary failure in name resolution", /* EAI_AGAIN */ + "Invalid value for ai_flags", /* EAI_BADFLAGS */ + "Non-recoverable failure in name resolution", /* EAI_FAIL */ + "ai_family not supported", /* EAI_FAMILY */ + "Memory allocation failure", /* EAI_MEMORY */ + "No address associated with hostname", /* EAI_NODATA */ + "hostname nor servname provided, or not known", /* EAI_NONAME */ + "servname not supported for ai_socktype", /* EAI_SERVICE */ + "ai_socktype not supported", /* EAI_SOCKTYPE */ + "System error returned in errno", /* EAI_SYSTEM */ + "Invalid value for hints", /* EAI_BADHINTS */ + "Resolved protocol is unknown", /* EAI_PROTOCOL */ + "Unknown error", /* EAI_MAX */ +}; +#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 (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 (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 (0) + +#define ERR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + goto bad; \ +} while (0) + +#define MATCH_FAMILY(x, y, w) \ + ((x) == (y) || ((w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) +#define MATCH(x, y, w) \ + ((x) == (y) || ((w) && ((x) == ANY || (y) == ANY))) + +static int +str_isnumber(p) + const char *p; { - int rval = 0, herrno, i; - char *prevcname = NULL; - struct hostent *h; - - h = gethostbyname2(name, af); - herrno = h_errno; - - if (!h) { - switch (herrno) { - case NETDB_INTERNAL: - return -EAI_SYSTEM; - case HOST_NOT_FOUND: - return 1; - case TRY_AGAIN: - return -EAI_AGAIN; - case NO_RECOVERY: - return -EAI_FAIL; - case NO_DATA: - return 1; - default: - return -EAI_FAIL; - } + char *q = (char *)p; + while (*q) { + if (! isdigit(*q)) + return NO; + q++; } + return YES; +} - for (i = 0; h->h_addr_list[i]; i++) { - while (*pat) - pat = &((*pat)->next); - - if (!(*pat = malloc(sizeof(struct gaih_addrtuple)))) - return -EAI_MEMORY; - memset(*pat, 0, sizeof(struct gaih_addrtuple)); - - switch ((*pat)->family = af) { - case AF_INET: - memcpy((*pat)->addr, h->h_addr_list[i], - sizeof(struct in_addr)); - break; - case AF_INET6: - memcpy((*pat)->addr, h->h_addr_list[i], - sizeof(struct in6_addr)); +int +getaddrinfo(hostname, servname, hints, res) + const char *hostname, *servname; + const struct addrinfo *hints; + struct addrinfo **res; +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai; + struct addrinfo ai0; + struct addrinfo *pai; + const struct afd *afd; + const struct explore *ex; +#ifndef AI_MASK +#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST) +#endif + +#ifdef FAITH + static int firsttime = 1; + + if (firsttime) { + /* translator hack */ + char *q = getenv("GAI"); + if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1) + translate = YES; + firsttime = 0; + } +#endif + + sentinel.ai_next = NULL; + cur = &sentinel; + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = ANY; + pai->ai_protocol = ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef INET6 + case PF_INET6: +#endif break; default: - return -EAI_FAIL; + ERR(EAI_FAMILY); } - - if (req->ai_flags & AI_CANONNAME) { - if (prevcname && !strcmp(prevcname, h->h_name)) - (*pat)->cname = prevcname; - else - prevcname = (*pat)->cname = strdup(h->h_name); + 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); + } + } } - pat = &((*pat)->next); } - return rval; -} -static int -gaih_local(const char *name, const struct gaih_service *service, - const struct addrinfo *req, struct addrinfo **pai) -{ - struct utsname utsname; - struct addrinfo *ai; - struct sockaddr_un *sun; - int siz; - - if (name || (req->ai_flags & AI_CANONNAME)) - if (uname(&utsname) < 0) - return (-EAI_SYSTEM); - - if (name && strcmp(name, "localhost") && strcmp(name, "local") && - strcmp(name, "unix") && strcmp(name, utsname.nodename)) - return (GAIH_OKIFUNSPEC | -EAI_NONAME); - - siz = sizeof(struct addrinfo) + sizeof(struct sockaddr_un); - if (req->ai_flags & AI_CANONNAME) - siz += strlen(utsname.nodename) + 1; - - if (!(ai = malloc(siz))) - return -EAI_MEMORY; - - *pai = ai; - ai->ai_next = NULL; - ai->ai_flags = req->ai_flags; - ai->ai_family = AF_LOCAL; - ai->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM; - ai->ai_protocol = req->ai_protocol; - ai->ai_addrlen = sizeof(struct sockaddr_un); - ai->ai_addr = (void *)ai + sizeof(struct addrinfo); - - sun = (struct sockaddr_un *)ai->ai_addr; - sun->sun_len = sizeof(struct sockaddr_un); - sun->sun_family = AF_LOCAL; - memset(&sun->sun_path, 0, sizeof sun->sun_path); - - if (service) { - char *c; - - c = strchr(service->name, '/'); - if (c) { - if (strlen(service->name) >= sizeof(sun->sun_path)) - return (GAIH_OKIFUNSPEC | -EAI_SERVICE); - strlcpy(sun->sun_path, service->name, sizeof (sun->sun_path)); - } else { - if (strlen(P_tmpdir "/") + strlen(service->name) + 1 >= - sizeof(sun->sun_path)) - return(GAIH_OKIFUNSPEC | -EAI_SERVICE); - snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/%s", - P_tmpdir, service->name); - } - } else { - extern char *_mktemp __P((char *)); - char tmpn[MAXPATHLEN], *c; - - snprintf(tmpn, sizeof tmpn, "%stmp.XXXXXXXXXXXXX", P_tmpdir); - if (!(c = _mktemp(tmpn))) - return (GAIH_OKIFUNSPEC | -EAI_SYSTEM); - strlcpy(sun->sun_path, c, sizeof(sun->sun_path)); + /* + * 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) + || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)) { + ai0 = *pai; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = PF_INET6; + error = get_portmatch(pai, servname); + if (error) + ERR(error); + + *pai = ai0; } - ai->ai_canonname = NULL; - if (req->ai_flags & AI_CANONNAME) { - ai->ai_canonname = (void *)sun + sizeof(struct sockaddr_un); - strlcpy(ai->ai_canonname, utsname.nodename, - strlen(utsname.nodename)+1); - } - return 0; -} + ai0 = *pai; -static int -gaih_inet_serv(char *servicename, struct gaih_typeproto *tp, - struct gaih_servtuple **st) -{ - struct servent *s; + /* NULL hostname, or numeric hostname */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; - s = getservbyname(servicename, tp->name); - if (!s) - return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + 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 (!(*st = malloc(sizeof(struct gaih_servtuple)))) - return (-EAI_MEMORY); + 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; - (*st)->next = NULL; - (*st)->socktype = tp->socktype; - (*st)->protocol = tp->protocol; - (*st)->port = s->s_port; - return (0); -} + if (hostname == NULL) + error = explore_null(pai, hostname, servname, &cur->ai_next); + else + error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); -static int -gaih_inet(const char *name, const struct gaih_service *service, - const struct addrinfo*req, struct addrinfo **pai) -{ - struct gaih_typeproto *tp = gaih_inet_typeproto; - struct gaih_servtuple *st = &nullserv; - struct gaih_addrtuple *at = NULL; - char *prevcname = NULL; - struct gaih_servtuple *st2; - struct gaih_addrtuple *at2 = at; - struct addrinfo *ai = NULL; - int canonlen, addrlen, rval = 0; - - if (req->ai_protocol || req->ai_socktype) { - for (tp++; tp->name && - ((req->ai_socktype != tp->socktype) || !req->ai_socktype) && - ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++); - if (!tp->name) { - rval = GAIH_OKIFUNSPEC | -EAI_SERVICE; - if (req->ai_socktype) - rval = GAIH_OKIFUNSPEC | -EAI_SOCKTYPE; - goto ret; - } + if (error) + goto free; + + while (cur && cur->ai_next) + cur = cur->ai_next; } - if (service && (service->num < 0)) { - if (tp->name) { - rval = gaih_inet_serv(service->name, tp, &st); - if (rval) - goto ret; - } else { - struct gaih_servtuple **pst = &st; - for (tp++; tp->name; tp++) { - rval = gaih_inet_serv(service->name, tp, pst); - if (rval) { - if (rval & GAIH_OKIFUNSPEC) - continue; - goto ret; - } - pst = &((*pst)->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 would like to prefer AF_INET6 than AF_INET, so we'll make a + * outer loop by AFs. + */ + for (afd = afdl; afd->a_af; afd++) { + *pai = ai0; + + if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) + continue; + + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = afd->a_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 (st == &nullserv) { - rval = GAIH_OKIFUNSPEC | -EAI_SERVICE; - goto ret; + 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; + + error = explore_fqdn(pai, hostname, servname, + &cur->ai_next); + + while (cur && cur->ai_next) + cur = cur->ai_next; } - } else { - if (!(st = malloc(sizeof(struct gaih_servtuple)))) { - rval = -EAI_MEMORY; - goto ret; - } + } - st->next = NULL; - st->socktype = tp->socktype; - st->protocol = tp->protocol; - if (service) - st->port = htons(service->num); - else - st->port = 0; + /* XXX */ + if (sentinel.ai_next) + error = 0; + + if (error) + goto free; + if (error == 0) { + if (sentinel.ai_next) { + good: + *res = sentinel.ai_next; + return SUCCESS; + } else + error = EAI_FAIL; } + free: + bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + *res = NULL; + return error; +} - if (!name) { - if (!(at = malloc(sizeof(struct gaih_addrtuple)))) { - rval = -EAI_MEMORY; - goto ret; +/* + * FQDN hostname, DNS lookup + */ +static int +explore_fqdn(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + int s; + struct hostent *hp; + int h_error; + int af; + char **aplist = NULL, *apbuf = NULL; + char *ap; + struct addrinfo sentinel, *cur; + int i; +#ifndef USE_GETIPNODEBY + int naddrs; +#endif + const struct afd *afd; + int error; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = socket(pai->ai_family, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + + /* + * post-RFC2553: should look at (pai->ai_flags & AI_ADDRCONFIG) + * rather than hardcoding it. we may need to add AI_ADDRCONFIG + * handling code by ourselves in case we don't have getipnodebyname(). + */ +#ifdef USE_GETIPNODEBY + hp = getipnodebyname(hostname, pai->ai_family, AI_ADDRCONFIG, &h_error); +#else + hp = gethostbyname2(hostname, pai->ai_family); + h_error = h_errno; +#endif + + if (hp == NULL) { + switch (h_error) { + case HOST_NOT_FOUND: + case NO_DATA: + error = EAI_NODATA; + break; + case TRY_AGAIN: + error = EAI_AGAIN; + break; + case NO_RECOVERY: + case NETDB_INTERNAL: + default: + error = EAI_FAIL; + break; } + } else if ((hp->h_name == NULL) || (hp->h_name[0] == 0) + || (hp->h_addr_list[0] == NULL)) { +#ifdef USE_GETIPNODEBY + freehostent(hp); +#endif + hp = NULL; + error = EAI_FAIL; + } - memset(at, 0, sizeof(struct gaih_addrtuple)); - - if (req->ai_family) - at->family = req->ai_family; - else { - if (!(at->next = malloc(sizeof(struct gaih_addrtuple)))) { - rval = -EAI_MEMORY; - goto ret; - } + if (hp == NULL) + goto free; + +#ifdef USE_GETIPNODEBY + aplist = hp->h_addr_list; +#else + /* + * hp will be overwritten if we use gethostbyname2(). + * always deep copy for simplification. + */ + for (naddrs = 0; hp->h_addr_list[naddrs] != NULL; naddrs++) + ; + naddrs++; + aplist = (char **)malloc(sizeof(aplist[0]) * naddrs); + apbuf = (char *)malloc(hp->h_length * naddrs); + if (aplist == NULL || apbuf == NULL) { + error = EAI_MEMORY; + goto free; + } + memset(aplist, 0, sizeof(aplist[0]) * naddrs); + for (i = 0; i < naddrs; i++) { + if (hp->h_addr_list[i] == NULL) { + aplist[i] = NULL; + continue; + } + memcpy(&apbuf[i * hp->h_length], hp->h_addr_list[i], + hp->h_length); + aplist[i] = &apbuf[i * hp->h_length]; + } +#endif + + for (i = 0; aplist[i] != NULL; i++) { + af = hp->h_addrtype; + ap = aplist[i]; + if (af == AF_INET6 + && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { + af = AF_INET; + ap = ap + sizeof(struct in6_addr) + - sizeof(struct in_addr); + } - at->family = AF_INET6; + if (af != pai->ai_family) + continue; - memset(at->next, 0, sizeof(struct gaih_addrtuple)); - at->next->family = AF_INET; + if ((pai->ai_flags & AI_CANONNAME) == 0) { + GET_AI(cur->ai_next, afd, ap); + GET_PORT(cur->ai_next, servname); + } else { + /* + * if AI_CANONNAME and if reverse lookup + * fail, return ai anyway to pacify + * calling application. + * + * XXX getaddrinfo() is a name->address + * translation function, and it looks + * strange that we do addr->name + * translation here. + */ + get_name(ap, afd, &cur->ai_next, + ap, pai, servname); } - goto build; + while (cur && cur->ai_next) + cur = cur->ai_next; } - if (!req->ai_family || (req->ai_family == AF_INET)) { - struct in_addr in_addr; - if (inet_pton(AF_INET, name, &in_addr) > 0) { - if (!(at = malloc(sizeof(struct gaih_addrtuple)))) { - rval = -EAI_MEMORY; - goto ret; - } + *res = sentinel.ai_next; + return 0; - memset(at, 0, sizeof(struct gaih_addrtuple)); +free: +#ifdef USE_GETIPNODEBY + if (hp) + freehostent(hp); +#endif + if (aplist) + free(aplist); + if (apbuf) + free(apbuf); + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} - at->family = AF_INET; - memcpy(at->addr, &in_addr, sizeof(struct in_addr)); - goto build; - } +/* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + int s; + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = socket(pai->ai_family, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + GET_PORT(cur->ai_next, servname); + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + GET_PORT(cur->ai_next, servname); } + cur = cur->ai_next; - if (!req->ai_family || (req->ai_family == AF_INET6)) { - struct in6_addr in6_addr; - if (inet_pton(AF_INET6, name, &in6_addr) > 0) { - if (!(at = malloc(sizeof(struct gaih_addrtuple)))) { - rval = -EAI_MEMORY; - goto ret; - } + *res = sentinel.ai_next; + return 0; - memset(at, 0, sizeof(struct gaih_addrtuple)); +free: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} - at->family = AF_INET6; - memcpy(at->addr, &in6_addr, sizeof(struct in6_addr)); - goto build; +/* + * numeric hostname + */ +static int +explore_numeric(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + int flags; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + flags = pai->ai_flags; + + if (inet_pton(afd->a_af, hostname, pton) == 1) { + u_int32_t v4a; +#ifdef INET6 + u_char pfx; +#endif + + switch (afd->a_af) { + case AF_INET: + v4a = (u_int32_t)ntohl(((struct in_addr *)pton)->s_addr); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags &= ~AI_CANONNAME; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) + flags &= ~AI_CANONNAME; + break; +#ifdef INET6 + case AF_INET6: + pfx = ((struct in6_addr *)pton)->s6_addr[0]; + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) + flags &= ~AI_CANONNAME; + break; +#endif } + + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + if ((flags & AI_CANONNAME) == 0) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname); + } else { + /* + * if AI_CANONNAME and if reverse lookup + * fail, return ai anyway to pacify + * calling application. + * + * XXX getaddrinfo() is a name->address + * translation function, and it looks + * strange that we do addr->name + * translation here. + */ + get_name(pton, afd, &cur->ai_next, + pton, pai, servname); + } + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /*xxx*/ } - if (!(req->ai_flags & AI_NUMERICHOST)) { - if (!req->ai_family || (req->ai_family == AF_INET6)) - if ((rval = netdb_lookup_addr(name, AF_INET6, req, &at)) < 0) - goto ret; - if (!req->ai_family || (req->ai_family == AF_INET)) - if ((rval = netdb_lookup_addr(name, AF_INET, req, &at)) < 0) - goto ret; + *res = sentinel.ai_next; + return 0; - if (!rval) - goto build; - } +free: +bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} - if (!at) { - rval = GAIH_OKIFUNSPEC | -EAI_NONAME; - goto ret; +/* + * numeric hostname with scope + */ +static int +explore_numeric_scope(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ +#ifndef SCOPE_DELIMITER + return explore_numeric(pai, hostname, servname, res); +#else + const struct afd *afd; + struct addrinfo *cur; + int error; + char *cp, *hostname2 = NULL; + int scope; + struct sockaddr_in6 *sin6; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (!afd->a_scoped) + return explore_numeric(pai, hostname, servname, res); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res); + + /* + * Handle special case of <scoped_address><delimiter><scope id> + */ + hostname2 = strdup(hostname); + if (hostname2 == NULL) + return EAI_MEMORY; + /* terminate at the delimiter */ + hostname2[cp - hostname] = '\0'; + + cp++; + switch (pai->ai_family) { +#ifdef INET6 + case AF_INET6: + scope = if_nametoindex(cp); + if (scope == 0) { + free(hostname2); + return (EAI_NONAME); + } + break; +#endif } -build: - at2 = at; - while (at2) { - if (req->ai_flags & AI_CANONNAME) { - if (at2->cname) - canonlen = strlen(at2->cname) + 1; - else - if (name) - canonlen = strlen(name) + 1; - else - canonlen = 2; - } else - canonlen = 0; + error = explore_numeric(pai, hostname2, servname, res); + if (error == 0) { + for (cur = *res; cur; cur = cur->ai_next) { + if (cur->ai_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)cur->ai_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) + sin6->sin6_scope_id = scope; + } + } - if (at2->family == AF_INET6) - addrlen = sizeof(struct sockaddr_in6); - else - addrlen = sizeof(struct sockaddr_in); - - st2 = st; - while (st2) { - if (!(ai = malloc(sizeof(struct addrinfo) + - addrlen + canonlen))) { - rval = -EAI_MEMORY; - goto ret; - } + free(hostname2); - *pai = ai; - memset(ai, 0, sizeof(struct addrinfo) + addrlen + canonlen); - - ai->ai_flags = req->ai_flags; - ai->ai_family = at2->family; - ai->ai_socktype = st2->socktype; - ai->ai_protocol = st2->protocol; - ai->ai_addrlen = addrlen; - ai->ai_addr = (void *)ai + sizeof(struct addrinfo); - ai->ai_addr->sa_len = addrlen; - ai->ai_addr->sa_family = at2->family; - - switch (at2->family) { - case AF_INET6: - { - struct sockaddr_in6 *sin6 = (void *)ai->ai_addr; - - memcpy(&sin6->sin6_addr, at2->addr, - sizeof(sin6->sin6_addr)); - sin6->sin6_port = st2->port; - break; - } - default: - { - struct sockaddr_in *sin = (void *)ai->ai_addr; - - memcpy(&sin->sin_addr, at2->addr, - sizeof(sin->sin_addr)); - sin->sin_port = st2->port; - break; - } - } + return error; +#endif +} - if (canonlen) { - ai->ai_canonname = (void *) ai->ai_addr + addrlen; - - if (at2->cname) { - strcpy(ai->ai_canonname, at2->cname); - if (prevcname != at2->cname) { - if (prevcname) - free(prevcname); - prevcname = at2->cname; - } - } else - strcpy(ai->ai_canonname, name ? name : "*"); - } - pai = &(ai->ai_next); - st2 = st2->next; - } - at2 = at2->next; - } - rval = 0; - -ret: - if (st != &nullserv) { - struct gaih_servtuple *st2 = st; - while (st) { - st2 = st->next; - free(st); - st = st2; +static int +get_name(addr, afd, res, numaddr, pai, servname) + const char *addr; + const struct afd *afd; + struct addrinfo **res; + char *numaddr; + const struct addrinfo *pai; + const char *servname; +{ + struct hostent *hp = NULL; + struct addrinfo *cur = NULL; + int error = 0; + char *ap = NULL, *cn = NULL; +#ifdef USE_GETIPNODEBY + int h_error; + + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); +#else + hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); +#endif + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { +#ifdef USE_GETIPNODEBY + GET_AI(cur, afd, hp->h_addr_list[0]); + GET_PORT(cur, servname); + GET_CANONNAME(cur, hp->h_name); +#else + /* hp will be damaged if we use gethostbyaddr() */ + if ((ap = (char *)malloc(hp->h_length)) == NULL) { + error = EAI_MEMORY; + goto free; } - } - if (at) { - struct gaih_addrtuple *at2 = at; - while (at) { - at2 = at->next; - free(at); - at = at2; + memcpy(ap, hp->h_addr_list[0], hp->h_length); + if ((cn = strdup(hp->h_name)) == NULL) { + error = EAI_MEMORY; + goto free; } + + GET_AI(cur, afd, ap); + GET_PORT(cur, servname); + GET_CANONNAME(cur, cn); + free(ap); ap = NULL; + free(cn); cn = NULL; +#endif + } else { + GET_AI(cur, afd, numaddr); + GET_PORT(cur, servname); } - return rval; + +#ifdef USE_GETIPNODEBY + if (hp) + freehostent(hp); +#endif + *res = cur; + return SUCCESS; + free: + if (cur) + freeaddrinfo(cur); + if (ap) + free(ap); + if (cn) + free(cn); +#ifdef USE_GETIPNODEBY + if (hp) + freehostent(hp); +#endif + *res = NULL; + return error; } -static struct gaih { - int family; - char *name; - int (*gaih) __P((const char *name, - const struct gaih_service *service, - const struct addrinfo *req, struct addrinfo **pai)); -} gaih[] = { - { PF_INET6, "inet6", gaih_inet }, - { PF_INET, "inet", gaih_inet }, - { PF_LOCAL, "local", gaih_local }, - { -1, NULL, NULL} -}; +static int +get_canonname(pai, ai, str) + const struct addrinfo *pai; + struct addrinfo *ai; + const char *str; +{ + if ((pai->ai_flags & AI_CANONNAME) != 0) { + ai->ai_canonname = (char *)malloc(strlen(str) + 1); + if (ai->ai_canonname == NULL) + return EAI_MEMORY; + strcpy(ai->ai_canonname, str); + } + return 0; +} -int -getaddrinfo(const char *name, const char *service, - const struct addrinfo *req, struct addrinfo **pai) +static struct addrinfo * +get_ai(pai, afd, addr) + const struct addrinfo *pai; + const struct afd *afd; + const char *addr; { - int rval = EAI_SYSTEM; /* XXX */ - int j = 0; - struct addrinfo *p = NULL, **end; - struct gaih *g = gaih, *pg = NULL; - struct gaih_service gaih_service, *pservice; + char *p; + struct addrinfo *ai; - if (name && (name[0] == '*') && !name[1]) - name = NULL; + 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 *)(ai + 1); + memset(ai->ai_addr, 0, afd->a_socklen); +#ifdef HAVE_SOCKADDR_SA_LEN + ai->ai_addr->sa_len = afd->a_socklen; +#endif + ai->ai_addrlen = afd->a_socklen; + ai->ai_addr->sa_family = ai->ai_family = afd->a_af; + p = (char *)(ai->ai_addr); + memcpy(p + afd->a_off, addr, afd->a_addrlen); + return ai; +} - if (service && service[0] == '*' && service[1] == '\0') - service = NULL; +static int +get_portmatch(ai, servname) + const struct addrinfo *ai; + const char *servname; +{ - if (!req) - req = &nullreq; + /* get_port does not touch first argument. when matchonly == 1. */ + return get_port((struct addrinfo *)ai, servname, 1); +} + +static int +get_port(ai, servname, matchonly) + struct addrinfo *ai; + const char *servname; + int matchonly; +{ + const char *proto; + struct servent *sp; + int port; + int allownumeric; + + if (servname == NULL) + return 0; + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + return 0; + + switch (ai->ai_socktype) { + case SOCK_RAW: + return EAI_SERVICE; + case SOCK_DGRAM: + case SOCK_STREAM: + allownumeric = 1; + break; + case ANY: + allownumeric = 0; + break; + default: + return EAI_SOCKTYPE; + } - if (req->ai_flags & ~(AI_CANONNAME | AI_PASSIVE | AI_NUMERICHOST | AI_EXT)) - return (EAI_BADFLAGS); + if (str_isnumber(servname)) { + if (!allownumeric) + return EAI_SERVICE; + port = htons(atoi(servname)); + if (port < 0 || port > 65535) + return EAI_SERVICE; + } else { + switch (ai->ai_socktype) { + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + proto = NULL; + break; + } - if (service && *service) { - char *c; + if ((sp = getservbyname(servname, proto)) == NULL) + return EAI_SERVICE; + port = sp->s_port; + } - gaih_service.num = strtoul(service, &c, 10); - if (*c) - gaih_service.num = -1; - gaih_service.name = (void *)service; - pservice = &gaih_service; - } else - pservice = NULL; - - if (pai) - end = &p; - else - end = NULL; - - while (g->gaih) { - if ((req->ai_family == g->family) || !req->ai_family) { - j++; - if (!((pg && (pg->gaih == g->gaih)))) { - pg = g; - rval = g->gaih(name, pservice, req, end); - if (rval) { - if (!req->ai_family && (rval & GAIH_OKIFUNSPEC)) - continue; - - if (p) - freeaddrinfo(p); - - return -(rval & GAIH_EAI); - } - if (end) - while (*end) - end = &((*end)->ai_next); - } + if (!matchonly) { + switch (ai->ai_family) { + case AF_INET: + ((struct sockaddr_in *)ai->ai_addr)->sin_port = port; + break; +#ifdef INET6 + case AF_INET6: + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = port; + break; +#endif } - g++; } - if (!j) - return (EAI_FAMILY); + return 0; +} + +static const struct afd * +find_afd(af) + int af; +{ + const struct afd *afd; - if (p) { - *pai = p; - return (0); + if (af == PF_UNSPEC) + return NULL; + for (afd = afdl; afd->a_af; afd++) { + if (afd->a_af == af) + return afd; } - if (!pai && !rval) - return (0); - return EAI_NONAME; + return NULL; } diff --git a/lib/libc/net/getnameinfo.c b/lib/libc/net/getnameinfo.c index 62c4b9d9287..787beec9c6c 100644 --- a/lib/libc/net/getnameinfo.c +++ b/lib/libc/net/getnameinfo.c @@ -1,7 +1,9 @@ +/* $OpenBSD: getnameinfo.c,v 1.5 1999/12/30 08:54:20 itojun Exp $ */ + /* - * %%% copyright-cmetz-96-bsd - * Copyright (c) 1996-1999, Craig Metz, All rights reserved. - * + * 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: @@ -10,18 +12,14 @@ * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Craig Metz and - * by other contributors. - * 4. Neither the name of the author nor the names of contributors + * 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * + * 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 REGENTS OR CONTRIBUTORS BE LIABLE + * 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) @@ -29,236 +27,258 @@ * 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. - * */ -/* getnameinfo() v1.38 */ +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + * - Return values. There seems to be no standard for return value (RFC2553) + * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). + * - RFC2553 says that we should raise error on short buffer. X/Open says + * we need to truncate the result. We obey RFC2553 (and X/Open should be + * modified). + */ + +#if 0 +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#else +#define HAVE_SA_LEN +#define INET6 +#endif #include <sys/types.h> #include <sys/socket.h> +#include <net/if.h> #include <netinet/in.h> -#include <sys/un.h> -#include <sys/utsname.h> -#include <netdb.h> #include <arpa/inet.h> -#include <errno.h> -#include <string.h> +#include <arpa/nameser.h> +#include <netdb.h> #include <resolv.h> +#include <string.h> +#include <stddef.h> -#ifndef min -#define min(x,y) (((x) > (y)) ? (y) : (x)) -#endif /* min */ - -static int -netdb_lookup_name(int family, void *addr, int addrlen, char *name, - int namelen, int flags) -{ - struct hostent *hostent; - char *c, *c2; - int i; - - if (!(hostent = gethostbyaddr(addr, addrlen, family))) { - switch (h_errno) { - case NETDB_INTERNAL: - return(EAI_SYSTEM); - case HOST_NOT_FOUND: - return(1); - case TRY_AGAIN: - return(EAI_AGAIN); - case NO_RECOVERY: - return(EAI_FAIL); - case NO_DATA: - return(1); - default: - return(EAI_FAIL); - } - } - - endhostent(); - - c = hostent->h_name; - if ((flags & NI_NOFQDN) && (_res.options & RES_INIT) && _res.defdname[0] && - (c2 = strstr(c + 1, _res.defdname)) && (*(--c2) == '.')) { - *c2 = 0; - i = min(c2 - c, namelen); - strlcpy(name, c, i); - } else - strlcpy(name, c, namelen); - return 0; -} - -int -getnameinfo(const struct sockaddr *sa, size_t addrlen, char *host, - size_t hostlen, char *serv, size_t servlen, int flags) -{ - int rval; - int saved_errno; - - if (sa == NULL || addrlen != sa->sa_len) - return EAI_FAIL; - saved_errno = errno; +#if 0 +#ifndef HAVE_PORTABLE_PROTOTYPE +#include "cdecl_ext.h" +#endif - if (host && hostlen > 0) { - switch (sa->sa_family) { - case AF_INET6: - { - struct sockaddr_in6 *sin6 = (void *)sa; +#ifndef HAVE_ADDRINFO +#include "addrinfo.h" +#endif +#endif - if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { - if (flags & NI_NUMERICHOST) - goto inet6_noname; - strlcpy(host, "*", hostlen); - break; - } +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - struct sockaddr_in sin; +static struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; +} afdl [] = { +#ifdef INET6 + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, +#endif + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {0, 0, 0}, +}; - memset(&sin, 0, sizeof(struct sockaddr_in)); - sin.sin_len = sizeof(struct sockaddr_in); - sin.sin_family = AF_INET; - sin.sin_port = sin6->sin6_port; - sin.sin_addr.s_addr = - ((u_int32_t *)&sin6->sin6_addr)[3]; - if (!(rval = getnameinfo((struct sockaddr *)&sin, - sizeof(struct sockaddr_in), host, hostlen, - serv, servlen, flags | NI_NAMEREQD))) - goto ret; - if (rval != EAI_NONAME) - goto ret; - goto inet6_noname; - } +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; - if (flags & NI_NUMERICHOST) - goto inet6_noname; - if ((rval = netdb_lookup_name(AF_INET6, - &sin6->sin6_addr, sizeof(struct in6_addr), - host, hostlen, flags)) < 0) - goto ret; +#define ENI_NOSOCKET 0 +#define ENI_NOSERVNAME 1 +#define ENI_NOHOSTNAME 2 +#define ENI_MEMORY 3 +#define ENI_SYSTEM 4 +#define ENI_FAMILY 5 +#define ENI_SALEN 6 - if (!rval) - break; - inet6_noname: - if (flags & NI_NAMEREQD) { - rval = EAI_NONAME; - goto ret; - } - if (!inet_ntop(AF_INET6, &sin6->sin6_addr, host, hostlen)) { - rval = EAI_NONAME; - goto ret; - } - break; - } - case AF_INET: - { - struct sockaddr_in *sin = (void *)sa; +int +getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) + const struct sockaddr *sa; + size_t salen; + char *host; + size_t hostlen; + char *serv; + size_t servlen; + int flags; +{ + struct afd *afd; + struct servent *sp; + struct hostent *hp; + u_short port; + int family, i; + char *addr, *p; + u_int32_t v4a; + int h_error; + char numserv[512]; + char numaddr[512]; - if (flags & NI_NUMERICHOST) - goto inet_noname; + if (sa == NULL) + return ENI_NOSOCKET; - if (sin->sin_addr.s_addr == 0) { - strlcpy(host, "*", hostlen); - break; - } +#ifdef HAVE_SA_LEN /*XXX*/ + if (sa->sa_len != salen) + return ENI_SALEN; +#endif + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return ENI_FAMILY; + + found: + if (salen != afd->a_socklen) + return ENI_SALEN; + + port = ((struct sockinet *)sa)->si_port; /* network byte order */ + addr = (char *)sa + afd->a_off; - if ((rval = netdb_lookup_name(AF_INET, - &sin->sin_addr, sizeof(struct in_addr), - host, hostlen, flags)) < 0) - goto ret; + if (serv == NULL || servlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: RFC2553 says that serv == NULL OR servlen == 0 + * means that the caller does not want the result. + */ + } else { + if (flags & NI_NUMERICSERV) + sp = NULL; + else { + sp = getservbyport(port, + (flags & NI_DGRAM) ? "udp" : "tcp"); + } + if (sp) { + if (strlen(sp->s_name) > servlen) + return ENI_MEMORY; + strcpy(serv, sp->s_name); + } else { + snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); + if (strlen(numserv) > servlen) + return ENI_MEMORY; + strcpy(serv, numserv); + } + } - if (!rval) - break; - inet_noname: - if (flags & NI_NAMEREQD) { - rval = EAI_NONAME; - goto ret; - } - if (!inet_ntop(AF_INET, &sin->sin_addr, host, hostlen)) { - rval = EAI_NONAME; - goto ret; - } + switch (sa->sa_family) { + case AF_INET: + v4a = (u_int32_t) + ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags |= NI_NUMERICHOST; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) + flags |= NI_NUMERICHOST; + break; +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)sa; + switch (sin6->sin6_addr.s6_addr[0]) { + case 0x00: + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + ; + else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + ; + else + flags |= NI_NUMERICHOST; break; - } - case AF_LOCAL: - if (!(flags & NI_NUMERICHOST)) { - struct utsname utsname; - - if (!uname(&utsname)) { - strlcpy(host, utsname.nodename, hostlen); - break; - } - } - - if (flags & NI_NAMEREQD) { - rval = EAI_NONAME; - goto ret; + default: + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + flags |= NI_NUMERICHOST; } - - strlcpy(host, "localhost", hostlen); + else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; break; - default: - rval = EAI_FAMILY; - goto ret; } + } + break; +#endif } + if (host == NULL || hostlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: RFC2553 says that host == NULL OR hostlen == 0 + * means that the caller does not want the result. + */ + } else if (flags & NI_NUMERICHOST) { + /* NUMERICHOST and NAMEREQD conflicts with each other */ + if (flags & NI_NAMEREQD) + return ENI_NOHOSTNAME; + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_SYSTEM; + if (strlen(numaddr) > hostlen) + return ENI_MEMORY; + strcpy(host, numaddr); +#if defined(INET6) && defined(NI_WITHSCOPEID) + if (afd->a_af == AF_INET6 && + (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || + IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && + ((struct sockaddr_in6 *)sa)->sin6_scope_id) { +#ifndef ALWAYS_WITHSCOPE + if (flags & NI_WITHSCOPEID) +#endif /* !ALWAYS_WITHSCOPE */ + { + char *ep = strchr(host, '\0'); + unsigned int ifindex = + ((struct sockaddr_in6 *)sa)->sin6_scope_id; - if (serv && servlen > 0) { - switch (sa->sa_family) { - case AF_INET: - { - struct sockaddr_in *sin = (void *)sa; - struct servent *s; - - if ((flags & NI_NUMERICSERV) == 0) { - s = getservbyport(sin->sin_port, - (flags & NI_DGRAM) ? "udp" : "tcp"); - if (s) { - strlcpy(serv, s->s_name, servlen); - break; - } - if (sin->sin_port == 0) { - strlcpy(serv, "*", servlen); - break; - } + *ep = SCOPE_DELIMITER; + if ((if_indextoname(ifindex, ep + 1)) == NULL) + /* XXX what should we do? */ + strncpy(ep + 1, "???", 3); } - snprintf(serv, servlen, "%d", ntohs(sin->sin_port)); - break; - } - case AF_INET6: - { - struct sockaddr_in6 *sin6 = (void *)sa; - struct servent *s; - - if ((flags & NI_NUMERICSERV) == 0) { + } +#endif /* INET6 */ + } else { +#ifdef USE_GETIPNODEBY + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); +#else + hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); + h_error = h_errno; +#endif - s = getservbyport(sin6->sin6_port, - (flags & NI_DGRAM) ? "udp" : "tcp"); - if (s) { - strlcpy(serv, s->s_name, servlen); - break; - } - if (sin6->sin6_port == 0) { - strlcpy(serv, "*", servlen); - break; - } + if (hp) { + if (flags & NI_NOFQDN) { + p = strchr(hp->h_name, '.'); + if (p) *p = '\0'; } - snprintf(serv, servlen, "%d", ntohs(sin6->sin6_port)); - break; - } - case AF_LOCAL: - { - struct sockaddr_un *sun = (void *)sa; - - strlcpy(serv, sun->sun_path, servlen); - break; - } + if (strlen(hp->h_name) > hostlen) { +#ifdef USE_GETIPNODEBY + freehostent(hp); +#endif + return ENI_MEMORY; + } + strcpy(host, hp->h_name); +#ifdef USE_GETIPNODEBY + freehostent(hp); +#endif + } else { + if (flags & NI_NAMEREQD) + return ENI_NOHOSTNAME; + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_NOHOSTNAME; + if (strlen(numaddr) > hostlen) + return ENI_MEMORY; + strcpy(host, numaddr); } } - rval = 0; - -ret: - if (rval == 1) - rval = EAI_FAIL; - errno = saved_errno; - return (rval); + return SUCCESS; } |