From 8c2ccb59caf882ac518eda1f570ea731d4466216 Mon Sep 17 00:00:00 2001 From: shin Date: Tue, 28 Dec 1999 02:37:14 +0000 Subject: Getaddrinfo(), getnameinfo(), and etc support in libc/net. Several udp and raw apps IPv6 support. Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project --- lib/libc/net/Makefile.inc | 14 +- lib/libc/net/getaddrinfo.3 | 408 +++++++++++++ lib/libc/net/getaddrinfo.c | 1014 ++++++++++++++++++++++++++++++++ lib/libc/net/getipnodebyname.3 | 446 ++++++++++++++ lib/libc/net/getnameinfo.3 | 232 ++++++++ lib/libc/net/getnameinfo.c | 228 ++++++++ lib/libc/net/name6.c | 1260 ++++++++++++++++++++++++++++++++++++++++ lib/libc/net/res_init.c | 59 ++ 8 files changed, 3657 insertions(+), 4 deletions(-) create mode 100644 lib/libc/net/getaddrinfo.3 create mode 100644 lib/libc/net/getaddrinfo.c create mode 100644 lib/libc/net/getipnodebyname.3 create mode 100644 lib/libc/net/getnameinfo.3 create mode 100644 lib/libc/net/getnameinfo.c create mode 100644 lib/libc/net/name6.c (limited to 'lib/libc/net') diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 5b7dc85..87ac304 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -4,15 +4,16 @@ # machine-independent net sources .PATH: ${.CURDIR}/../libc/${MACHINE_ARCH}/net ${.CURDIR}/../libc/net -SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c \ +SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c getaddrinfo.c \ gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ + getnameinfo.c \ getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \ getproto.c getprotoent.c getprotoname.c getservbyname.c \ getservbyport.c getservent.c herror.c inet_addr.c ifname.c \ inet_lnaof.c \ inet_makeaddr.c inet_net_ntop.c inet_net_pton.c inet_neta.c \ inet_netof.c inet_network.c inet_ntoa.c inet_ntop.c \ - inet_pton.c ip6opt.c linkaddr.c map_v4v6.c ns_addr.c \ + inet_pton.c ip6opt.c linkaddr.c map_v4v6.c name6.c ns_addr.c \ ns_name.c ns_netint.c \ ns_ntoa.c ns_parse.c ns_print.c ns_ttl.c nsap_addr.c \ rcmd.c recv.c res_comp.c res_data.c res_debug.c \ @@ -20,12 +21,15 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c \ res_update.c rthdr.c send.c vars.c # not supported: iso_addr.c +CFLAGS+=-DINET6 + # machine-dependent net sources .include "${.CURDIR}/../libc/${MACHINE_ARCH}/net/Makefile.inc" .if ${LIB} == "c" -MAN3+= addr2ascii.3 byteorder.3 ethers.3 gethostbyname.3 \ - getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \ +MAN3+= addr2ascii.3 byteorder.3 ethers.3 getaddrinfo.3 gethostbyname.3 \ + getipnodebyname.3 \ + getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 if_indextoname.3 \ inet.3 linkaddr.3 rcmd.3 resolver.3 # not installed: iso_addr.3 ns.3 @@ -34,10 +38,12 @@ MLINKS+=byteorder.3 htonl.3 byteorder.3 htons.3 byteorder.3 ntohl.3 \ byteorder.3 ntohs.3 MLINKS+=ethers.3 ether_aton.3 ethers.3 ether_hostton.3 ethers.3 ether_line.3 \ ethers.3 ether_ntoa.3 ethers.3 ether_ntohost.3 +MLINKS+=getaddrinfo.3 freeaddrinfo.3 getaddrinfo.3 gai_strerror.3 MLINKS+=gethostbyname.3 endhostent.3 gethostbyname.3 gethostbyaddr.3 \ gethostbyname.3 gethostbyname2.3 gethostbyname.3 gethostent.3 \ gethostbyname.3 herror.3 gethostbyname.3 hstrerror.3 \ gethostbyname.3 sethostent.3 +MLINKS+=getipnodebyname.3 getipnodebyaddr.3 getipnodebyname.3 freehostent.3 MLINKS+=getnetent.3 endnetent.3 getnetent.3 getnetbyaddr.3 \ getnetent.3 getnetbyname.3 getnetent.3 setnetent.3 MLINKS+=getprotoent.3 endprotoent.3 getprotoent.3 getprotobyname.3 \ diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3 new file mode 100644 index 0000000..39bcdb7 --- /dev/null +++ b/lib/libc/net/getaddrinfo.3 @@ -0,0 +1,408 @@ +.\" Copyright (c) 1983, 1987, 1991, 1993 +.\" 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: +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 +.\" 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 +.\" 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: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95 +.\" $Id: getaddrinfo.3,v 1.3 1999/10/07 08:22:04 jinmei Exp $ +.\" $FreeBSD$ +.\" +.Dd May 25, 1995 +.Dt GETADDRINFO 3 +.Os KAME +.\" +.Sh NAME +.Nm getaddrinfo +.Nm freeaddrinfo , +.Nm gai_strerror +.Nd nodename-to-address translation in protocol-independent manner +.\" +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Ft int +.Fn getaddrinfo "const char *nodename" "const char *servname" \ +"const struct addrinfo *hints" "struct addrinfo **res" +.Ft void +.Fn freeaddrinfo "struct addrinfo *ai" +.Ft "char *" +.Fn gai_strerror "int ecode" +.\" +.Sh DESCRIPTION +The +.Fn getaddrinfo +function is defined for protocol-independent nodename-to-address translation. +It performs functionality of +.Xr gethostbyname 3 +and +.Xr getservbyname 3 , +in more sophisticated manner. +.Pp +The addrinfo structure is defined as a result of including the +.Li +header: +.Bd -literal -offset +struct addrinfo { * + int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for nodename */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +.Ed +.Pp +The +.Fa nodename +and +.Fa servname +arguments are pointers to null-terminated strings or +.Dv NULL . +One or both of these two arguments must be a +.Pf non Dv -NULL +pointer. +In the normal client scenario, both the +.Fa nodename +and +.Fa servname +are specified. +In the normal server scenario, only the +.Fa servname +is specified. +A +.Pf non Dv -NULL +.Fa nodename +string can be either a node name or a numeric host address string +.Po +i.e., a dotted-decimal IPv4 address or an IPv6 hex address +.Pc . +A +.Pf non Dv -NULL +.Fa servname +string can be either a service name or a decimal port number. +.Pp +The caller can optionally pass an +.Li addrinfo +structure, pointed to by the third argument, +to provide hints concerning the type of socket that the caller supports. +In this +.Fa hints +structure all members other than +.Fa ai_flags , +.Fa ai_family , +.Fa ai_socktype , +and +.Fa ai_protocol +must be zero or a +.Dv NULL +pointer. +A value of +.Dv PF_UNSPEC +for +.Fa ai_family +means the caller will accept any protocol family. +A value of 0 for +.Fa ai_socktype +means the caller will accept any socket type. +A value of 0 for +.Fa ai_protocol +means the caller will accept any protocol. +For example, if the caller handles only TCP and not UDP, then the +.Fa ai_socktype +member of the hints structure should be set to +.Dv SOCK_STREAM +when +.Fn getaddrinfo +is called. +If the caller handles only IPv4 and not IPv6, then the +.Fa ai_family +member of the +.Fa hints +structure should be set to +.Dv PF_INET +when +.Fn getaddrinfo +is called. +If the third argument to +.Fn getaddrinfo +is a +.Dv NULL +pointer, this is the same as if the caller had filled in an +.Li addrinfo +structure initialized to zero with +.Fa ai_family +set to PF_UNSPEC. +.Pp +Upon successful return a pointer to a linked list of one or more +.Li addrinfo +structures is returned through the final argument. +The caller can process each +.Li addrinfo +structure in this list by following the +.Fa ai_next +pointer, until a +.Dv NULL +pointer is encountered. +In each returned +.Li addrinfo +structure the three members +.Fa ai_family , +.Fa ai_socktype , +and +.Fa ai_protocol +are the corresponding arguments for a call to the +.Fn socket +function. +In each +.Li addrinfo +structure the +.Fa ai_addr +member points to a filled-in socket address structure whose length is +specified by the +.Fa ai_addrlen +member. +.Pp +If the +.Dv AI_PASSIVE +bit is set in the +.Fa ai_flags +member of the +.Fa hints +structure, then the caller plans to use the returned socket address +structure in a call to +.Fn bind . +In this case, if the +.Fa nodename +argument is a +.Dv NULL +pointer, then the IP address portion of the socket +address structure will be set to +.Dv INADDR_ANY +for an IPv4 address or +.Dv IN6ADDR_ANY_INIT +for an IPv6 address. +.Pp +If the +.Dv AI_PASSIVE +bit is not set in the +.Fa ai_flags +member of the +.Fa hints +structure, then the returned socket address structure will be ready for a +call to +.Fn connect +.Pq for a connection-oriented protocol +or either +.Fn connect , +.Fn sendto , or +.Fn sendmsg +.Pq for a connectionless protocol . +In this case, if the +.Fa nodename +argument is a +.Dv NULL +pointer, then the IP address portion of the +socket address structure will be set to the loopback address. +.Pp +If the +.Dv AI_CANONNAME +bit is set in the +.Fa ai_flags +member of the +.Fa hints +structure, then upon successful return the +.Fa ai_canonname +member of the first +.Li addrinfo +structure in the linked list will point to a null-terminated string +containing the canonical name of the specified +.Fa nodename . +.Pp +If the +.Dv AI_NUMERICHOST +bit is set in the +.Fa ai_flags +member of the +.Fa hints +structure, then a +.Pf non Dv -NULL +.Fa nodename +string must be a numeric host address string. +Otherwise an error of +.Dv EAI_NONAME +is returned. +This flag prevents any type of name resolution service (e.g., the DNS) +from being called. +.Pp +All of the information returned by +.Fn getaddrinfo +is dynamically allocated: +the +.Li addrinfo +structures, and the socket address structures and canonical node name +strings pointed to by the addrinfo structures. +To return this information to the system the function +.Fn freeaddrinfo +is called. +The +.Fa addrinfo +structure pointed to by the +.Fa ai argument +is freed, along with any dynamic storage pointed to by the structure. +This operation is repeated until a +.Dv NULL +.Fa ai_next +pointer is encountered. +.Pp +To aid applications in printing error messages based on the +.Dv EAI_xxx +codes returned by +.Fn getaddrinfo , +.Fn gai_strerror +is defined. +The argument is one of the +.Dv EAI_xxx +values defined earlier and the return value points to a string describing +the error. +If the argument is not one of the +.Dv EAI_xxx +values, the function still returns a pointer to a string whose contents +indicate an unknown error. +.\" +.Sh EXTENSION +The implementation allows experimental numeric IPv6 address notation with +scope identifier. +By appending atmark and scope identifier to addresses, you can fill +.Li sin6_scope_id +field for addresses. +This would make management of scoped address easier, +and allows cut-and-paste input of scoped address. +.Pp +At this moment the code supports only link-local addresses with the format. +Scope identifier is hardcoded to name of hardware interface associated +with the link. +.Po +such as +.Li ne0 +.Pc . +Example would be like +.Dq Li fe80::1@ne0 , +which means +.Do +.Li fe80::1 +on the link associated with +.Li ne0 +interface +.Dc . +.Pp +The implementation is still very experimental and non-standard. +The current implementation assumes one-by-one relationship between +interface and link, which is not necessarily true from the specification. +.\" +.Sh FILES +.Bl -tag -width /etc/resolv.conf -compact +.It Pa /etc/hosts +.It Pa /etc/host.conf +.It Pa /etc/resolv.conf +.El +.\" +.Sh DIAGNOSTICS +Error return status from +.Fn getaddrinfo +is zero on success and non-zero on errors. +Non-zero error codes are defined in +.Li , +and as follows: +.Pp +.Bl -tag -width EAI_ADDRFAMILY -compact +.It Dv EAI_ADDRFAMILY +address family for nodename not supported +.It Dv EAI_AGAIN +temporary failure in name resolution +.It Dv EAI_BADFLAGS +invalid value for ai_flags +.It Dv EAI_FAIL +non-recoverable failure in name resolution +.It Dv EAI_FAMILY +ai_family not supported +.It Dv EAI_MEMORY +memory allocation failure +.It Dv EAI_NODATA +no address associated with nodename +.It Dv EAI_NONAME +nodename nor servname provided, or not known +.It Dv EAI_SERVICE +servname not supported for ai_socktype +.It Dv EAI_SOCKTYPE +ai_socktype not supported +.It Dv EAI_SYSTEM +system error returned in errno +.El +.Pp +If called with proper argument, +.Fn gai_strerror +returns a pointer to a string describing the given error code. +If the argument is not one of the +.Dv EAI_xxx +values, the function still returns a pointer to a string whose contents +indicate an unknown error. +.\" +.Sh SEE ALSO +.Xr getnameinfo 3 , +.Xr gethostbyname 3 , +.Xr getservbyname 3 , +.Xr hosts 5 , +.Xr services 5 , +.Xr hostname 7 , +.Xr named 8 . +.Pp +.Rs +.%A R. Gilligan +.%A S. Thomson +.%A J. Bound +.%A W. Stevens +.%T Basic Socket Interface Extensions for IPv6 +.%R RFC2553 +.%D March 1999 +.Re +.\" +.Sh HISTORY +The implementation first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" +.Sh STANDARDS +The +.Fn getaddrinfo +function is defined IEEE POSIX 1003.1g draft specification, +and documented in ``Basic Socket Interface Extensions for IPv6'' +.Pq RFC2533 . +.\" +.Sh BUGS +The text was shamelessly copied from RFC2553. diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c new file mode 100644 index 0000000..79b07de --- /dev/null +++ b/lib/libc/net/getaddrinfo.c @@ -0,0 +1,1014 @@ +/* + * 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. + * + * $FreeBSD$ + */ + +/* + * "#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. + * - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__KAME__) && defined(INET6) +# define FAITH +#endif + +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in_loopback[] = { 127, 0, 0, 1 }; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; + +static const struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { +#ifdef INET6 +#define N_INET6 0 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, +#define N_INET 1 +#else +#define N_INET 0 +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, +}; + +struct explore { + int e_af; + int e_socktype; + int e_protocol; + const char *e_protostr; + int e_wild; +#define WILD_AF(ex) ((ex)->e_wild & 0x01) +#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) +#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) +}; + +static const struct explore explore[] = { +#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 }, +}; + +#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)); + +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 */ + "Argument res is NULL", /* EAI_RESNULL */ + "Unknown error", /* EAI_MAX */ +}; + +/* 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))) + +char * +gai_strerror(ecode) + int ecode; +{ + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return ai_errlist[ecode]; +} + +void +freeaddrinfo(ai) + struct addrinfo *ai; +{ + struct addrinfo *next; + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + } while ((ai = next) != NULL); +} + +static int +str_isnumber(p) + const char *p; +{ + char *q = (char *)p; + while (*q) { + if (! isdigit(*q)) + return NO; + q++; + } + return YES; +} + +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; + + 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 (res == NULL) + return EAI_RESNULL; /* xxx */ + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef INET6 + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + + /* + * if both socktype/protocol are specified, check if they + * are meaningful combination. + */ + if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { + int matched = 0; + + 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) + matched = 1; + else + continue; + if (matched == 0) + ERR(EAI_BADHINTS); + } + } + } + + /* backup original pai contents */ + ai0 = *pai; + + /* + * special cases check for inet and inet6 sockets. + * (1) servname is disallowed for raw sockets. + * (2) numeric servname is disallowed if socktype/protocol is left + * unspecified. + */ + if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) +#ifdef INET6 + || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) +#endif + ) { + *pai = ai0; + + if (pai->ai_family == PF_UNSPEC) +#ifdef INET6 + pai->ai_family = PF_INET6; +#else + pai->ai_family = PF_INET; +#endif + error = get_portmatch(pai, servname); + if (error) + ERR(error); + } + + /* 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 (hostname == NULL) + error = explore_null(pai, hostname, 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 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 (!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; + } + } + + /* XXX: if any addrinfo found, SUCCESS return even if (error != 0) */ + if (sentinel.ai_next) { + good: + *res = sentinel.ai_next; + return SUCCESS; + } + /* else, failed */ + free: + bad: + if (error == 0) + error = EAI_FAIL; + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + *res = NULL; + return error; +} + +/* + * 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 *ap; + struct addrinfo sentinel, *cur; + int i; + 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) + return 0; + close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + hp = getipnodebyname(hostname, pai->ai_family, AI_ADDRCONFIG, + &h_error); + 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)) { + freehostent(hp); + hp = NULL; + error = EAI_FAIL; + } + + if (hp == NULL) + goto free; + + for (i = 0; hp->h_addr_list[i] != NULL; i++) { + af = hp->h_addrtype; + ap = hp->h_addr_list[i]; + + if (af != pai->ai_family) + continue; + + 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); + } + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + *res = sentinel.ai_next; + return 0; + +free: + if (hp) + freehostent(hp); + if (sentinel.ai_next) + 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(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) + return 0; + close(s); + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + GET_AI(cur->ai_next, afd, + (pai->ai_flags & AI_PASSIVE) ? afd->a_addrany : afd->a_loopback + ); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + * or + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + /* if the servname does not match socktype/protocol, ignored */ + GET_PORT(cur->ai_next, servname); + + *res = sentinel.ai_next; + return 0; + +free: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * numeric hostname + */ +static int +explore_numeric(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + 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); + if (afd == NULL) + return 0; + flags = pai->ai_flags; + + if (inet_pton(afd->a_af, hostname, pton) == 1) { + u_int32_t v4a; +#ifdef INET6 + struct in6_addr * v6a; +#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: + v6a = (struct in6_addr *)pton; + if (IN6_IS_ADDR_MULTICAST(v6a)) + flags &= ~AI_CANONNAME; + if (IN6_IS_ADDR_UNSPECIFIED(v6a) || + IN6_IS_ADDR_LOOPBACK(v6a)) + flags &= ~AI_CANONNAME; + if (IN6_IS_ADDR_LINKLOCAL(v6a)) + flags &= ~AI_CANONNAME; + + /* should also do this for SITELOCAL ?? */ + + 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*/ + } + + *res = sentinel.ai_next; + return 0; + +free: +bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * 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; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + if (!afd->a_scoped) + return explore_numeric(pai, hostname, servname, res); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res); + + /* + * Handle special case of + */ + 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); + break; +#endif + } + + error = explore_numeric(pai, hostname2, servname, res); + if (error == 0) { + for (cur = *res; cur; cur = cur->ai_next) { +#ifdef INET6 + 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; +#endif + } + } + + free(hostname2); + + return error; +#endif +} + +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; + struct addrinfo *cur; + int error = 0; + int h_error; + + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + GET_AI(cur, afd, hp->h_addr_list[0]); + GET_PORT(cur, servname); + GET_CANONNAME(cur, hp->h_name); + } else { + GET_AI(cur, afd, numaddr); + GET_PORT(cur, servname); + } + + if (hp) + freehostent(hp); + *res = cur; + return SUCCESS; + free: + if (cur) + freeaddrinfo(cur); + if (hp) + freehostent(hp); + /* bad: */ + *res = NULL; + return error; +} + +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; +} + +static struct addrinfo * +get_ai(pai, afd, addr) + const struct addrinfo *pai; + const struct afd *afd; + const char *addr; +{ + char *p; + struct addrinfo *ai; +#ifdef FAITH + struct in6_addr faith_prefix; + char *fp_str; + int translate = 0; +#endif + +#ifdef FAITH + /* + * Transfrom an IPv4 addr into a special IPv6 addr format for + * IPv6->IPv4 translation gateway. (only TCP is supported now) + * + * +-----------------------------------+------------+ + * | faith prefix part (12 bytes) | embedded | + * | | IPv4 addr part (4 bytes) + * +-----------------------------------+------------+ + * + * faith prefix part is specified as ascii IPv6 addr format + * in environmental variable GAI. + * For FAITH to work correctly, routing to faith prefix must be + * setup toward a machine where a FAITH daemon operates. + * Also, the machine must enable some mechanizm + * (e.g. faith interface hack) to divert those packet with + * faith prefixed destination addr to user-land FAITH daemon. + */ + fp_str = getenv("GAI"); + if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && + afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { + u_int32_t v4a; + u_int8_t v4a_top; + + memcpy(&v4a, addr, sizeof v4a); + v4a_top = v4a >> IN_CLASSA_NSHIFT; + if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && + v4a_top != 0 && v4a != IN_LOOPBACKNET) { + afd = &afdl[N_INET6]; + memcpy(&faith_prefix.s6_addr[12], addr, + sizeof(struct in_addr)); + translate = 1; + } + } +#endif + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + + (afd->a_socklen)); + if (ai == NULL) + return NULL; + + memcpy(ai, pai, sizeof(struct addrinfo)); + ai->ai_addr = (struct sockaddr *)(ai + 1); + memset(ai->ai_addr, 0, afd->a_socklen); + ai->ai_addr->sa_len = afd->a_socklen; + ai->ai_addrlen = afd->a_socklen; + ai->ai_addr->sa_family = ai->ai_family = afd->a_af; + p = (char *)(ai->ai_addr); +#ifdef FAITH + if (translate == 1) + memcpy(p + afd->a_off, &faith_prefix, afd->a_addrlen); + else +#endif + memcpy(p + afd->a_off, addr, afd->a_addrlen); + + return ai; +} + +static int +get_portmatch(ai, servname) + const struct addrinfo *ai; + const char *servname; +{ + /* 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 +#ifdef INET6 + && ai->ai_family != AF_INET6 +#endif + ) + 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 (str_isnumber(servname)) { + if (!allownumeric) + return EAI_SERVICE; + port = htons(atoi(servname)); + if (port < 0 || port > 65535) + return EAI_SERVICE; + } else { + switch (ai->ai_socktype) { + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + proto = NULL; + break; + } + + if ((sp = getservbyname(servname, proto)) == NULL) + return EAI_SERVICE; + port = sp->s_port; + } + + 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 + } + } + + return 0; +} + +static const struct afd * +find_afd(af) + int af; +{ + const struct afd *afd; + + if (af == PF_UNSPEC) + return NULL; + for (afd = afdl; afd->a_af; afd++) { + if (afd->a_af == af) + return afd; + } + return NULL; +} diff --git a/lib/libc/net/getipnodebyname.3 b/lib/libc/net/getipnodebyname.3 new file mode 100644 index 0000000..7220af4 --- /dev/null +++ b/lib/libc/net/getipnodebyname.3 @@ -0,0 +1,446 @@ +.\" Copyright (c) 1983, 1987, 1991, 1993 +.\" 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: +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 +.\" 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 +.\" 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: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95 +.\" $Id: getipnodebyname.3,v 1.2 1999/09/13 16:04:51 itojun Exp $ +.\ $FreeBSD$ +.\" +.Dd May 25, 1995 +.Dt GETIPNODEBYNAME 3 +.Os KAME +.\" +.Sh NAME +.Nm getipnodebyname , +.Nm getipnodebyaddr , +.Nm freehostent +.Nd nodename-to-address and address-to-nodename translation +.\" +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Ft "struct hostent *" +.Fn getipnodebyname "const char *name" "int af" "int flags" "int *error_num" +.Ft "struct hostent *" +.Fn getipnodebyaddr "const void *src" "size_t len" "int af" "int *error_num" +.Ft void +.Fn freehostent "struct hostent *ptr" +.\" +.Sh DESCRIPTION +.Fn getipnodebyname +and +.Fn getipnodebyaddr +functions are very similar to +.Xr gethostbyname 3 , +.Xr gethostbyname2 3 +and +.Xr gethostbyaddr 3 . +The functions cover all the functionalities provided by the older ones, +and provide better interface to programmers. +The functions require additional arguments, +.Ar af , +and +.Ar flags , +for specifying address family and operation mode. +The additional arguments allow programmer to get address for a nodename, +for specific address family +.Po +such as +.Dv AF_INET +or +.Dv AF_INET6 +.Pc . +The functions also require an additional pointer argument, +.Ar error_num +to return the appropriate error code, +to support thread safe error code returns. +.Pp +The type and usage of the return value, +.Li "struct hostent" +is described in +.Xr gethostbyname 3 . +.Pp +For +.Fn getipnodebyname , +the +.Ar name +argument can be either a node name or a numeric address +string +.Po +i.e., a dotted-decimal IPv4 address or an IPv6 hex address +.Pc . +The +.Ar af +argument specifies the address family, either +.Dv AF_INET +or +.Dv AF_INET6 . +The +.Ar flags +argument specifies the types of addresses that are searched for, +and the types of addresses that are returned. +We note that a special flags value of +.Dv AI_DEFAULT +.Pq defined below +should handle most applications. +That is, porting simple applications to use IPv6 replaces the call +.Bd -literal -offset + hptr = gethostbyname(name); +.Ed +.Pp +with +.Bd -literal -offset + hptr = getipnodebyname(name, AF_INET6, AI_DEFAULT, &error_num); +.Ed +.Pp +Applications desiring finer control over the types of addresses +searched for and returned, can specify other combinations of the +.Ar flags +argument. +.Pp +A +.Ar flags +of +.Li 0 +implies a strict interpretation of the +.Ar af +argument: +.Bl -bullet +.It +If +.Ar flags +is 0 and +.Ar af +is +.Dv AF_INET , +then the caller wants only IPv4 addresses. +A query is made for +.Li A +records. +If successful, the IPv4 addresses are returned and the +.Li h_length +member of the +.Li hostent +structure will be 4, else the function returns a +.Dv NULL +pointer. +.It +If +.Ar flags +is 0 and if +.Ar af +is +.Li AF_INET6 , +then the caller wants only IPv6 addresses. +A query is made for +.Li AAAA +records. +If successful, the IPv6 addresses are returned and the +.Li h_length +member of the +.Li hostent +structure will be 16, else the function returns a +.Dv NULL +pointer. +.El +.Pp +Other constants can be logically-ORed into the +.Ar flags +argument, to modify the behavior of the function. +.Bl -bullet +.It +If the +.Dv AI_V4MAPPED +flag is specified along with an +.Ar af +of +.Dv AF_INET6 , +then the caller will accept IPv4-mapped IPv6 addresses. +That is, if no +.Li AAAA +records are found then a query is made for +.Li A +records and any found are returned as IPv4-mapped IPv6 addresses +.Po +.Li h_length +will be 16 +.Pc . +The +.Dv AI_V4MAPPED +flag is ignored unless +.Ar af +equals +.Dv AF_INET6 . +.It +The +.Dv AI_V4MAPPED_CFG +flag is exact same as the +.Dv AI_V4MAPPED +flag only if the kernel supports IPv4-mapped IPv6 address. +.It +If the +.Dv AI_ALL +flag is used in conjunction with the +.Dv AI_V4MAPPED +flag, and only used with the IPv6 address family. +When +.Dv AI_ALL +is logically or'd with +.Dv AI_V4MAPPED +flag then the caller wants all addresses: IPv6 and IPv4-mapped IPv6. +A query is first made for +.Li AAAA +records and if successful, the +IPv6 addresses are returned. Another query is then made for +.Li A +records and any found are returned as IPv4-mapped IPv6 addresses. +.Li h_length +will be 16. Only if both queries fail does the function +return a +.Dv NULL +pointer. This flag is ignored unless af equals +AF_INET6. If both +.Dv AI_ALL +and +.Dv AI_V4MAPPED +are specified, +.Dv AI_ALL +takes precedence. +.It +The +.Dv AI_ADDRCONFIG +flag specifies that a query for +.Li AAAA +records +should occur only if the node has at least one IPv6 source +address configured and a query for +.Li A +records should occur only if the node has at least one IPv4 source address +configured. +.Pp +For example, if the node has no IPv6 source addresses configured, +and +.Ar af +equals AF_INET6, and the node name being looked up has both +.Li AAAA +and +.Li A +records, then: +(a) if only +.Dv AI_ADDRCONFIG +is +specified, the function returns a +.Dv NULL +pointer; +(b) if +.Dv AI_ADDRCONFIG +| +.Dv AI_V4MAPPED +is specified, the +.Li A +records are returned as IPv4-mapped IPv6 addresses; +.El +.Pp +The special flags value of +.Dv AI_DEFAULT +is defined as +.Bd -literal -offset + #define AI_DEFAULT (AI_V4MAPPED_CFG | AI_ADDRCONFIG) +.Ed +.Pp +We noted that the +.Fn getipnodebyname +function must allow the +.Ar name +argument to be either a node name or a literal address string +.Po +i.e., a dotted-decimal IPv4 address or an IPv6 hex address +.Pc . +This saves applications from having to call +.Xr inet_pton 3 +to handle literal address strings. +When the +.Ar name +argument is a literal address string, +the +.Ar flags +argument is always ignored. +.Pp +There are four scenarios based on the type of literal address string +and the value of the +.Ar af +argument. +The two simple cases are when +.Ar name +is a dotted-decimal IPv4 address and +.Ar af +equals +.Dv AF_INET , +or when +.Ar name +is an IPv6 hex address and +.Ar af +equals +.Dv AF_INET6 . +The members of the +returned hostent structure are: +.Li h_name +points to a copy of the +.Ar name +argument, +.Li h_aliases +is a +.Dv NULL +pointer, +.Li h_addrtype +is a copy of the +.Ar af +argument, +.Li h_length +is either 4 +.Po +for +.Dv AF_INET +.Pc +or 16 +.Po +for +.Dv AF_INET6 +.Pc , +.Li h_addr_list[0] +is a pointer to the 4-byte or 16-byte binary address, +and +.Li h_addr_list[1] +is a +.Dv NULL +pointer. +.Pp +When +.Ar name +is a dotted-decimal IPv4 address and +.Ar af +equals +.Dv AF_INET6 , +and +.Dv AI_V4MAPPED +is specified, +an IPv4-mapped IPv6 address is returned: +.Li h_name +points to an IPv6 hex address containing the IPv4-mapped IPv6 address, +.Li h_aliases +is a +.Dv NULL +pointer, +.Li h_addrtype +is +.Dv AF_INET6 , +.Li h_length +is 16, +.Li h_addr_list[0] +is a pointer to the 16-byte binary address, and +.Li h_addr_list[1] +is a +.Dv NULL +pointer. +.Pp +It is an error when +.Ar name +is an IPv6 hex address and +.Ar af +equals +.Dv AF_INET . +The function's return value is a +.Dv NULL +pointer and the value pointed to by +.Ar error_num +equals +.Dv HOST_NOT_FOUND . +.Pp +.Fn getipnodebyaddr +takes almost the same argument as +.Xr gethostbyaddr 3 , +but adds a pointer to return an error number. +Additionally it takes care of IPv4-mapped IPv6 addresses, +and IPv4-compatible IPv6 addresses. +.Pp +.Fn getipnodebyname +and +.Fn getipnodebyaddr +dynamically allocate the structure to be returned to the caller. +.Fn freehostent +reclaims memory region allocated and returned by +.Fn getipnodebyname +or +.Fn getipnodebyaddr . +.\" +.Sh FILES +.Bl -tag -width /etc/resolv.conf -compact +.It Pa /etc/hosts +.It Pa /etc/host.conf +.It Pa /etc/resolv.conf +.El +.\" +.Sh DIAGNOSTICS +.Nm getipnodebyname +and +.Nm getipnodebyaddr +returns +.Dv NULL +on errors. +The integer values pointed to by +.Ar error_num +may then be checked to see whether this is a temporary failure +or an invalid or unknown host. +The meanings of each error code are described in +.Xr gethostbyname 3 . +.\" +.Sh SEE ALSO +.Xr gethostbyname 3 , +.Xr gethostbyaddr 3 , +.Xr hosts 5 , +.Xr services 5 , +.Xr hostname 7 , +.Xr named 8 +.Pp +R. Gilligan, S. Thomson, J. Bound, and W. Stevens, +``Basic Socket Interface Extensions for IPv6,'' RFC2553, March 1999. +.\" +.Sh HISTORY +The implementation first appeared in KAME advanced networking kit. +.\" +.Sh STANDARDS +.Fn getipnodebyname +and +.Fn getipnodebyaddr +are documented in ``Basic Socket Interface Extensions for IPv6'' +.Pq RFC2553 . +.\" +.Sh BUGS +The text was shamelessly copied from RFC2553. diff --git a/lib/libc/net/getnameinfo.3 b/lib/libc/net/getnameinfo.3 new file mode 100644 index 0000000..c110ac9 --- /dev/null +++ b/lib/libc/net/getnameinfo.3 @@ -0,0 +1,232 @@ +.\" Copyright (c) 1983, 1987, 1991, 1993 +.\" 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: +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 +.\" 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 +.\" 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: @(#)gethostbyname.3 8.4 (Berkeley) 5/25/95 +.\" $Id: getnameinfo.3,v 1.2 1999/10/07 04:46:27 itojun Exp $ +.\" $FreeBSD$ +.\" +.Dd May 25, 1995 +.Dt GETNAMEINFO 3 +.Os KAME +.\" +.Sh NAME +.Nm getnameinfo +.Nd address-to-nodename translation in protocol-independent manner +.\" +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Ft int +.Fn getnameinfo "const struct sockaddr *sa" "socklen_t salen" \ +"char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags" +.\" +.Sh DESCRIPTION +The +.Fn getnameinfo +function is defined for protocol-independent address-to-nodename translation. +Its functionality is a reverse conversion of +.Xr getaddrinfo 3 , +and implements similar functionality with +.Xr gethostbyaddr 3 and +.Xr getservbyport 3 +in more sophisticated manner. +.Pp +This function looks up an IP address and port number provided by the +caller in the DNS and system-specific database, and returns text +strings for both in buffers provided by the caller. +The function indicates successful completion by a zero return value; +a non-zero return value indicates failure. +.Pp +The first argument, +.Fa sa , +points to either a +.Fa sockaddr_in +structure (for IPv4) or a +.Fa sockaddr_in6 +structure (for IPv6) that holds the IP address and port number. +The +.Fa salen +argument gives the length of the +.Fa sockaddr_in +or +.Fa sockaddr_in6 +structure. +.Pp +The function returns the nodename associated with the IP address in +the buffer pointed to by the +.Fa host +argument. +The caller provides the size of this buffer via the +.Fa hostlen +argument. +The service name associated with the port number is returned in the buffer +pointed to by +.Fa serv , +and the +.Fa servlen +argument gives the length of this buffer. +The caller specifies not to return either string by providing a zero +value for the +.Fa hostlen +or +.Fa servlen +arguments. +Otherwise, the caller must provide buffers large enough to hold the +nodename and the service name, including the terminating null characters. +.Pp +Unfortunately most systems do not provide constants that specify the +maximum size of either a fully-qualified domain name or a service name. +Therefore to aid the application in allocating buffers for these two +returned strings the following constants are defined in +.Li : +.Bd -literal -offset +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 +.Ed +.Pp +The first value is actually defined as the constant +.Dv MAXDNAME +in recent versions of BIND's +.Li +header +.Po +older versions of BIND define this constant to be 256 +.Pc +and the second is a guess based on the services listed in the current +Assigned Numbers RFC. +.Pp +The final argument is a +.Fa flag +that changes the default actions of this function. +By default the fully-qualified domain name (FQDN) for the host is +looked up in the DNS and returned. +If the flag bit +.Dv NI_NOFQDN +is set, only the nodename portion of the FQDN is returned for local hosts. +.Pp +If the +.Fa flag +bit +.Dv NI_NUMERICHOST +is set, or if the host's name cannot be located in the DNS, +the numeric form of the host's address is returned instead of its name +.Po +e.g., by calling +.Fn inet_ntop +instead of +.Fn getnodebyaddr +.Pc . +If the +.Fa flag +bit +.Dv NI_NAMEREQD +is set, an error is returned if the host's name cannot be located in the DNS. +.Pp +If the flag bit +.Dv NI_NUMERICSERV +is set, the numeric form of the service address is returned +.Pq e.g., its port number +instead of its name. +The two +.Dv NI_NUMERICxxx +flags are required to support the +.Li "-n" +flag that many commands provide. +.Pp +A fifth flag bit, +.Dv NI_DGRAM , +specifies that the service is a datagram service, and causes +.Fn getservbyport +to be called with a second argument of "udp" instead of its default of "tcp". +This is required for the few ports (512-514) +that have different services for UDP and TCP. +.Pp +These +.Dv NI_xxx +flags are defined in +.Li . +.\" +.Sh EXTENSION +The implementation allows experimental numeric IPv6 address notation with +scope identifier. +IPv6 link-local address will appear as string like +.Dq Li fe80::1@ne0 , +if +.Dv NI_WITHSCOPEID +bit is enabled in +.Ar flags +argument. +Refer to +.Xr getaddrinfo 3 +for the notation. +.\" +.Sh FILES +.Bl -tag -width /etc/resolv.conf -compact +.It Pa /etc/hosts +.It Pa /etc/host.conf +.It Pa /etc/resolv.conf +.El +.\" +.Sh DIAGNOSTICS +The function indicates successful completion by a zero return value; +a non-zero return value indicates failure. +.\" +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr gethostbyaddr 3 , +.Xr getservbyport 3 , +.Xr hosts 5 , +.Xr services 5 , +.Xr hostname 7 , +.Xr named 8 +.Pp +.Rs +.%A R. Gilligan +.%A S. Thomson +.%A J. Bound +.%A W. Stevens +.%T Basic Socket Interface Extensions for IPv6 +.%R RFC2553 +.%D March 1999 +.Re +.\" +.Sh HISTORY +The implementation first appeared in WIDE Hydrangea IPv6 protocol stack kit. +.\" +.Sh STANDARDS +The +.Fn getaddrinfo +function is defined IEEE POSIX 1003.1g draft specification, +and documented in ``Basic Socket Interface Extensions for IPv6'' +.Pq RFC2533 . +.\" +.Sh BUGS +The text was shamelessly copied from RFC2553. diff --git a/lib/libc/net/getnameinfo.c b/lib/libc/net/getnameinfo.c new file mode 100644 index 0000000..76edc12 --- /dev/null +++ b/lib/libc/net/getnameinfo.c @@ -0,0 +1,228 @@ +/* + * 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. + * + * $FreeBSD$ + */ + +/* + * 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(). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 + +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}, +}; + +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; + +#define ENI_NOSOCKET 0 +#define ENI_NOSERVHOST 1 +#define ENI_NOHOSTNAME 2 +#define ENI_MEMORY 3 +#define ENI_SYSTEM 4 +#define ENI_FAMILY 5 +#define ENI_SALEN 6 + +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_long v4a; + int h_error; + char numserv[512]; + char numaddr[512]; + int noserv = 0; + + if (sa == NULL) + return ENI_NOSOCKET; + + if (sa->sa_len != salen) + return ENI_SALEN; + + 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 (serv == NULL || servlen == 0) { + noserv = 1; + } 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); + } + } + + switch (sa->sa_family) { + case AF_INET: + v4a = 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; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + } + break; +#endif + } + if (host == NULL || hostlen == 0) { + if (noserv == 1) + return ENI_NOSERVHOST; + } 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); +#ifdef INET6 + 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) { + if (flags & NI_WITHSCOPEID) { + char *ep = strchr(host, '\0'); + unsigned int ifindex = + ((struct sockaddr_in6 *)sa)->sin6_scope_id; + char ifname[IF_NAMESIZE * 2 /* for safety */]; + + if ((if_indextoname(ifindex, ifname)) == NULL) + return ENI_SYSTEM; + if (strlen(host) + 1 /* SCOPE_DELIMITER */ + + strlen(ifname) > hostlen) + return ENI_MEMORY; + *ep = SCOPE_DELIMITER; + strcpy(ep + 1, ifname); + } + } +#endif /* INET6 */ + } else { + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); + if (hp) { + if (flags & NI_NOFQDN) { + p = strchr(hp->h_name, '.'); + if (p) *p = '\0'; + } + if (strlen(hp->h_name) > hostlen) { + freehostent(hp); + return ENI_MEMORY; + } + strcpy(host, hp->h_name); + freehostent(hp); + } 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); + } + } + return SUCCESS; +} diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c new file mode 100644 index 0000000..6683451 --- /dev/null +++ b/lib/libc/net/name6.c @@ -0,0 +1,1260 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 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. + * + * $FreeBSD$ + */ +/* $Id: name6.c,v 1.9 1999/10/29 03:04:26 itojun Exp $ */ +/* + * Atsushi Onoe + */ + +/* + * TODO for thread safe + * use mutex for _hostconf, _hostconf_init. + * rewrite resolvers to be thread safe + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef _PATH_HOSTS +#define _PATH_HOSTS "/etc/hosts" +#endif + +#ifndef MAXALIASES +#define MAXALIASES 10 +#endif +#ifndef MAXADDRS +#define MAXADDRS 20 +#endif +#ifndef MAXDNAME +#define MAXDNAME 1025 +#endif + +#ifdef INET6 +#define ADDRLEN(af) ((af) == AF_INET6 ? sizeof(struct in6_addr) : \ + sizeof(struct in_addr)) +#else +#define ADDRLEN(af) sizeof(struct in_addr) +#endif + +#define MAPADDR(ab, ina) \ +do { \ + memcpy(&(ab)->map_inaddr, ina, sizeof(struct in_addr)); \ + memset((ab)->map_zero, 0, sizeof((ab)->map_zero)); \ + memset((ab)->map_one, 0xff, sizeof((ab)->map_one)); \ +} while (0) +#define MAPADDRENABLED(flags) \ + (((flags) & AI_V4MAPPED) || \ + (((flags) & AI_V4MAPPED_CFG) && _mapped_addr_enabled())) + +union inx_addr { + struct in_addr in_addr; +#ifdef INET6 + struct in6_addr in6_addr; +#endif + struct { + u_char mau_zero[10]; + u_char mau_one[2]; + struct in_addr mau_inaddr; + } map_addr_un; +#define map_zero map_addr_un.mau_zero +#define map_one map_addr_un.mau_one +#define map_inaddr map_addr_un.mau_inaddr +}; + +static struct hostent *_hpcopy(struct hostent *hp, int *errp); +static struct hostent *_hpaddr(int af, const char *name, void *addr, int *errp); +static struct hostent *_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp); +#ifdef INET6 +static struct hostent *_hpmapv6(struct hostent *hp, int *errp); +#endif +static struct hostent *_hpsort(struct hostent *hp); +static struct hostent *_ghbyname(const char *name, int af, int flags, int *errp); +static char *_hgetword(char **pp); +static int _mapped_addr_enabled(void); + +static FILE *_files_open(int *errp); +static struct hostent *_files_ghbyname(const char *name, int af, int *errp); +static struct hostent *_files_ghbyaddr(const void *addr, int addrlen, int af, int *errp); +static void _files_shent(int stayopen); +static void _files_ehent(void); +static struct hostent *_dns_ghbyname(const char *name, int af, int *errp); +static struct hostent *_dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp); +static void _dns_shent(int stayopen); +static void _dns_ehent(void); +#ifdef ICMPNL +static struct hostent *_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp); +#endif /* ICMPNL */ + +/* + * Select order host function. + */ +#define MAXHOSTCONF 4 + +#ifndef HOSTCONF +# define HOSTCONF "/etc/host.conf" +#endif /* !HOSTCONF */ + +struct _hostconf { + struct hostent *(*byname)(const char *name, int af, int *errp); + struct hostent *(*byaddr)(const void *addr, int addrlen, int af, int *errp); +}; + +/* default order */ +static struct _hostconf _hostconf[MAXHOSTCONF] = { + { _dns_ghbyname, _dns_ghbyaddr }, + { _files_ghbyname, _files_ghbyaddr }, +#ifdef ICMPNL + { NULL, _icmp_ghbyaddr }, +#endif /* ICMPNL */ +}; + +static int _hostconf_init_done; +static void _hostconf_init(void); + +/* + * Initialize hostconf structure. + */ + +static void +_hostconf_init(void) +{ + FILE *fp; + int n; + char *p, *line; + char buf[BUFSIZ]; + + _hostconf_init_done = 1; + n = 0; + p = HOSTCONF; + if ((fp = fopen(p, "r")) == NULL) + return; + while (n < MAXHOSTCONF && fgets(buf, sizeof(buf), fp)) { + line = buf; + if ((p = _hgetword(&line)) == NULL) + continue; + do { + if (strcmp(p, "hosts") == 0 + || strcmp(p, "local") == 0 + || strcmp(p, "file") == 0 + || strcmp(p, "files") == 0) { + _hostconf[n].byname = _files_ghbyname; + _hostconf[n].byaddr = _files_ghbyaddr; + n++; + } + else if (strcmp(p, "dns") == 0 + || strcmp(p, "bind") == 0) { + _hostconf[n].byname = _dns_ghbyname; + _hostconf[n].byaddr = _dns_ghbyaddr; + n++; + } +#ifdef ICMPNL + else if (strcmp(p, "icmp") == 0) { + _hostconf[n].byname = NULL; + _hostconf[n].byaddr = _icmp_ghbyaddr; + n++; + } +#endif /* ICMPNL */ + } while ((p = _hgetword(&line)) != NULL); + } + fclose(fp); + if (n < 0) { + /* no keyword found. do not change default configuration */ + return; + } + for (; n < MAXHOSTCONF; n++) { + _hostconf[n].byname = NULL; + _hostconf[n].byaddr = NULL; + } +} + +/* + * Check if kernel supports mapped address. + * implementation dependent + */ +#ifdef __KAME__ +#include +#endif /* __KAME__ */ + +static int +_mapped_addr_enabled(void) +{ + /* implementation dependent check */ +#if defined(__KAME__) && defined(IPV6CTL_MAPPED_ADDR) + int mib[4]; + size_t len; + int val; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_MAPPED_ADDR; + len = sizeof(val); + if (sysctl(mib, 4, &val, &len, 0, 0) == 0 && val != 0) + return 1; +#endif /* __KAME__ && IPV6CTL_MAPPED_ADDR */ + return 0; +} + +/* + * Functions defined in RFC2553 + * getipnodebyname, getipnodebyadr, freehostent + */ + +static struct hostent * +_ghbyname(const char *name, int af, int flags, int *errp) +{ + struct hostent *hp; + int i; + + if (flags & AI_ADDRCONFIG) { + int s; + + if ((s = socket(af, SOCK_DGRAM, 0)) < 0) + return NULL; + /* + * TODO: + * Note that implementation dependent test for address + * configuration should be done everytime called + * (or apropriate interval), + * because addresses will be dynamically assigned or deleted. + */ + close(s); + } + + for (i = 0; i < MAXHOSTCONF; i++) { + if (_hostconf[i].byname + && (hp = (*_hostconf[i].byname)(name, af, errp)) + != NULL) + return hp; + } + + return NULL; +} + +struct hostent * +getipnodebyname(const char *name, int af, int flags, int *errp) +{ + struct hostent *hp; + union inx_addr addrbuf; + + if (af != AF_INET +#ifdef INET6 + && af != AF_INET6 +#endif + ) + { + *errp = NO_RECOVERY; + return NULL; + } + +#ifdef INET6 + /* special case for literal address */ + if (inet_pton(AF_INET6, name, &addrbuf) == 1) { + if (af != AF_INET6) { + *errp = HOST_NOT_FOUND; + return NULL; + } + return _hpaddr(af, name, &addrbuf, errp); + } +#endif + if (inet_pton(AF_INET, name, &addrbuf) == 1) { + if (af != AF_INET) { + if (MAPADDRENABLED(flags)) { + MAPADDR(&addrbuf, &addrbuf.in_addr); + } else { + *errp = HOST_NOT_FOUND; + return NULL; + } + } + return _hpaddr(af, name, &addrbuf, errp); + } + + if (!_hostconf_init_done) + _hostconf_init(); + + *errp = HOST_NOT_FOUND; + hp = _ghbyname(name, af, flags, errp); + +#ifdef INET6 + if (af == AF_INET6 + && ((flags & AI_ALL) || hp == NULL) + && (MAPADDRENABLED(flags))) { + struct hostent *hp2 = _ghbyname(name, AF_INET, flags, errp); + if (hp == NULL) + hp = _hpmapv6(hp2, errp); + else { + if (hp2 && strcmp(hp->h_name, hp2->h_name) != 0) { + freehostent(hp2); + hp2 = NULL; + } + hp = _hpmerge(hp, hp2, errp); + } + } +#endif + return _hpsort(hp); +} + +struct hostent * +getipnodebyaddr(const void *src, size_t len, int af, int *errp) +{ + struct hostent *hp; + int i; +#ifdef INET6 + struct in6_addr addrbuf; +#else + struct in_addr addrbuf; +#endif + + *errp = HOST_NOT_FOUND; + + switch (af) { + case AF_INET: + if (len != sizeof(struct in_addr)) { + *errp = NO_RECOVERY; + return NULL; + } + if ((long)src & ~(sizeof(struct in_addr) - 1)) { + memcpy(&addrbuf, src, len); + src = &addrbuf; + } + if (((struct in_addr *)src)->s_addr == 0) + return NULL; + break; +#ifdef INET6 + case AF_INET6: + if (len != sizeof(struct in6_addr)) { + *errp = NO_RECOVERY; + return NULL; + } + if ((long)src & ~(sizeof(struct in6_addr) / 2 - 1)) { /*XXX*/ + memcpy(&addrbuf, src, len); + src = &addrbuf; + } + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src) + || IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) { + src = (char *)src + + (sizeof(struct in6_addr) - sizeof(struct in_addr)); + af = AF_INET; + len = sizeof(struct in_addr); + } + break; +#endif + default: + *errp = NO_RECOVERY; + return NULL; + } + + if (!_hostconf_init_done) + _hostconf_init(); + for (i = 0; i < MAXHOSTCONF; i++) { + if (_hostconf[i].byaddr + && (hp = (*_hostconf[i].byaddr)(src, len, af, errp)) != NULL) + return hp; + } + + return NULL; +} + +void +freehostent(struct hostent *ptr) +{ + free(ptr); +} + +#if 0 + +/* XXX: should be deprecated */ +struct hostent * +getnodebyname(const char *name, int af, int flags) +{ + return getipnodebyname(name, af, flags, &h_errno); +} + +#ifdef __warn_references +__warn_references(getnodebyname, + "warning: getnodebyname() deprecated, " + "should use getaddrinfo() or getipnodebyname()"); +#endif + +struct hostent * +getnodebyaddr(const void *src, size_t len, int af) +{ + return getipnodebyaddr(src, len, af, &h_errno); +} + +#ifdef __warn_references +__warn_references(getnodebyaddr, + "warning: getnodebyaddr() deprecated, " + "should use getnameinfo() or getipnodebyaddr()"); +#endif + +#endif + +/* + * Private utility functions + */ + +/* + * _hpcopy: allocate and copy hostent structure + */ +static struct hostent * +_hpcopy(struct hostent *hp, int *errp) +{ + struct hostent *nhp; + char *cp, **pp; + int size, addrsize; + int nalias = 0, naddr = 0; + int al_off; + int i; + + if (hp == NULL) + return hp; + + /* count size to be allocated */ + size = sizeof(struct hostent); + if (hp->h_name != NULL && *hp->h_name != '\0') + size += strlen(hp->h_name) + 1; + if ((pp = hp->h_aliases) != NULL) { + for (i = 0; *pp != NULL; i++, pp++) { + if (**pp != '\0') { + size += strlen(*pp) + 1; + nalias++; + } + } + } + /* adjust alignment */ + size = ALIGN(size); + al_off = size; + size += sizeof(char *) * (nalias + 1); + addrsize = ALIGN(hp->h_length); + if ((pp = hp->h_addr_list) != NULL) { + while (*pp++ != NULL) + naddr++; + } + size += addrsize * naddr; + size += sizeof(char *) * (naddr + 1); + + /* copy */ + if ((nhp = (struct hostent *)malloc(size)) == NULL) { + *errp = TRY_AGAIN; + return NULL; + } + cp = (char *)&nhp[1]; + if (hp->h_name != NULL && *hp->h_name != '\0') { + nhp->h_name = cp; + strcpy(cp, hp->h_name); + cp += strlen(cp) + 1; + } else + nhp->h_name = NULL; + nhp->h_aliases = (char **)((char *)nhp + al_off); + if ((pp = hp->h_aliases) != NULL) { + for (i = 0; *pp != NULL; pp++) { + if (**pp != '\0') { + nhp->h_aliases[i++] = cp; + strcpy(cp, *pp); + cp += strlen(cp) + 1; + } + } + } + nhp->h_aliases[nalias] = NULL; + cp = (char *)&nhp->h_aliases[nalias + 1]; + nhp->h_addrtype = hp->h_addrtype; + nhp->h_length = hp->h_length; + nhp->h_addr_list = (char **)cp; + if ((pp = hp->h_addr_list) != NULL) { + cp = (char *)&nhp->h_addr_list[naddr + 1]; + for (i = 0; *pp != NULL; pp++) { + nhp->h_addr_list[i++] = cp; + memcpy(cp, *pp, hp->h_length); + cp += addrsize; + } + } + nhp->h_addr_list[naddr] = NULL; + return nhp; +} + +/* + * _hpaddr: construct hostent structure with one address + */ +static struct hostent * +_hpaddr(int af, const char *name, void *addr, int *errp) +{ + struct hostent *hp, hpbuf; + char *addrs[2]; + + hp = &hpbuf; + hp->h_name = (char *)name; + hp->h_aliases = NULL; + hp->h_addrtype = af; + hp->h_length = ADDRLEN(af); + hp->h_addr_list = addrs; + addrs[0] = (char *)addr; + addrs[1] = NULL; + return _hpcopy(hp, errp); +} + +/* + * _hpmerge: merge 2 hostent structure, arguments will be freed + */ +static struct hostent * +_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp) +{ + int i, j; + int naddr, nalias; + char **pp; + struct hostent *hp, hpbuf; + char *aliases[MAXALIASES + 1], *addrs[MAXADDRS + 1]; + union inx_addr addrbuf[MAXADDRS]; + + if (hp1 == NULL) + return hp2; + if (hp2 == NULL) + return hp1; + +#define HP(i) (i == 1 ? hp1 : hp2) + hp = &hpbuf; + hp->h_name = (hp1->h_name != NULL ? hp1->h_name : hp2->h_name); + hp->h_aliases = aliases; + nalias = 0; + for (i = 1; i <= 2; i++) { + if ((pp = HP(i)->h_aliases) == NULL) + continue; + for (; nalias < MAXALIASES && *pp != NULL; pp++) { + /* check duplicates */ + for (j = 0; j < nalias; j++) + if (strcasecmp(*pp, aliases[j]) == 0) + break; + if (j == nalias) + aliases[nalias++] = *pp; + } + } + aliases[nalias] = NULL; +#ifdef INET6 + if (hp1->h_length != hp2->h_length) { + hp->h_addrtype = AF_INET6; + hp->h_length = sizeof(struct in6_addr); + } else { +#endif + hp->h_addrtype = hp1->h_addrtype; + hp->h_length = hp1->h_length; +#ifdef INET6 + } +#endif + hp->h_addr_list = addrs; + naddr = 0; + for (i = 1; i <= 2; i++) { + if ((pp = HP(i)->h_addr_list) == NULL) + continue; + if (HP(i)->h_length == hp->h_length) { + while (naddr < MAXADDRS && *pp != NULL) + addrs[naddr++] = *pp++; + } else { + /* copy IPv4 addr as mapped IPv6 addr */ + while (naddr < MAXADDRS && *pp != NULL) { + MAPADDR(&addrbuf[naddr], *pp++); + addrs[naddr] = (char *)&addrbuf[naddr]; + naddr++; + } + } + } + addrs[naddr] = NULL; + hp = _hpcopy(hp, errp); + freehostent(hp1); + freehostent(hp2); + return hp; +} + +/* + * _hpmapv6: convert IPv4 hostent into IPv4-mapped IPv6 addresses + */ +#ifdef INET6 +static struct hostent * +_hpmapv6(struct hostent *hp, int *errp) +{ + struct hostent *hp6; + + if (hp == NULL) + return NULL; + if (hp->h_addrtype == AF_INET6) + return hp; + + /* make dummy hostent to convert IPv6 address */ + if ((hp6 = (struct hostent *)malloc(sizeof(struct hostent))) == NULL) { + *errp = TRY_AGAIN; + return NULL; + } + hp6->h_name = NULL; + hp6->h_aliases = NULL; + hp6->h_addrtype = AF_INET6; + hp6->h_length = sizeof(struct in6_addr); + hp6->h_addr_list = NULL; + return _hpmerge(hp6, hp, errp); +} +#endif + +/* + * _hpsort: sort address by sortlist + */ +static struct hostent * +_hpsort(struct hostent *hp) +{ + int i, j, n; + u_char *ap, *sp, *mp, **pp; + char t; + char order[MAXADDRS]; + int nsort = _res.nsort; + + if (hp == NULL || hp->h_addr_list[1] == NULL || nsort == 0) + return hp; + for (i = 0; (ap = (u_char *)hp->h_addr_list[i]); i++) { + for (j = 0; j < nsort; j++) { +#ifdef INET6 + if (_res_ext.sort_list[j].af != hp->h_addrtype) + continue; + sp = (u_char *)&_res_ext.sort_list[j].addr; + mp = (u_char *)&_res_ext.sort_list[j].mask; +#else + sp = (u_char *)&_res.sort_list[j].addr; + mp = (u_char *)&_res.sort_list[j].mask; +#endif + for (n = 0; n < hp->h_length; n++) { + if ((ap[n] & mp[n]) != sp[n]) + break; + } + if (n == hp->h_length) + break; + } + order[i] = j; + } + n = i; + pp = (u_char **)hp->h_addr_list; + for (i = 0; i < n - 1; i++) { + for (j = i + 1; j < n; j++) { + if (order[i] > order[j]) { + ap = pp[i]; + pp[i] = pp[j]; + pp[j] = ap; + t = order[i]; + order[i] = order[j]; + order[j] = t; + } + } + } + return hp; +} + +static char * +_hgetword(char **pp) +{ + char c, *p, *ret; + const char *sp; + static const char sep[] = "# \t\n"; + + ret = NULL; + for (p = *pp; (c = *p) != '\0'; p++) { + for (sp = sep; *sp != '\0'; sp++) { + if (c == *sp) + break; + } + if (c == '#') + p[1] = '\0'; /* ignore rest of line */ + if (ret == NULL) { + if (*sp == '\0') + ret = p; + } else { + if (*sp != '\0') { + *p++ = '\0'; + break; + } + } + } + *pp = p; + if (ret == NULL || *ret == '\0') + return NULL; + return ret; +} + +/* + * FILES (/etc/hosts) + */ + +static FILE * +_files_open(int *errp) +{ + FILE *fp; + fp = fopen(_PATH_HOSTS, "r"); + if (fp == NULL) + *errp = NO_RECOVERY; + return fp; +} + +static struct hostent * +_files_ghbyname(const char *name, int af, int *errp) +{ + int match, nalias; + char *p, *line, *addrstr, *cname; + FILE *fp; + struct hostent *rethp, *hp, hpbuf; + char *aliases[MAXALIASES + 1], *addrs[2]; + union inx_addr addrbuf; + char buf[BUFSIZ]; + + if ((fp = _files_open(errp)) == NULL) + return NULL; + rethp = hp = NULL; + + while (fgets(buf, sizeof(buf), fp)) { + line = buf; + if ((addrstr = _hgetword(&line)) == NULL + || (cname = _hgetword(&line)) == NULL) + continue; + match = (strcasecmp(cname, name) == 0); + nalias = 0; + while ((p = _hgetword(&line)) != NULL) { + if (!match) + match = (strcasecmp(p, name) == 0); + if (nalias < MAXALIASES) + aliases[nalias++] = p; + } + if (!match) + continue; + if (inet_pton(af, addrstr, &addrbuf) != 1) { + *errp = NO_DATA; /* name found */ + continue; + } + hp = &hpbuf; + hp->h_name = cname; + hp->h_aliases = aliases; + aliases[nalias] = NULL; + hp->h_addrtype = af; + hp->h_length = ADDRLEN(af); + hp->h_addr_list = addrs; + addrs[0] = (char *)&addrbuf; + addrs[1] = NULL; + hp = _hpcopy(hp, errp); + rethp = _hpmerge(rethp, hp, errp); + } + fclose(fp); + return rethp; +} + +static struct hostent * +_files_ghbyaddr(const void *addr, int addrlen, int af, int *errp) +{ + int nalias; + char *p, *line; + FILE *fp; + struct hostent *hp, hpbuf; + char *aliases[MAXALIASES + 1], *addrs[2]; + union inx_addr addrbuf; + char buf[BUFSIZ]; + + if ((fp = _files_open(errp)) == NULL) + return NULL; + hp = NULL; + while (fgets(buf, sizeof(buf), fp)) { + line = buf; + if ((p = _hgetword(&line)) == NULL + || inet_pton(af, p, &addrbuf) != 1 + || memcmp(addr, &addrbuf, addrlen) != 0 + || (p = _hgetword(&line)) == NULL) + continue; + hp = &hpbuf; + hp->h_name = p; + hp->h_aliases = aliases; + nalias = 0; + while ((p = _hgetword(&line)) != NULL) { + if (nalias < MAXALIASES) + aliases[nalias++] = p; + } + aliases[nalias] = NULL; + hp->h_addrtype = af; + hp->h_length = addrlen; + hp->h_addr_list = addrs; + addrs[0] = (char *)&addrbuf; + addrs[1] = NULL; + hp = _hpcopy(hp, errp); + break; + } + fclose(fp); + return hp; +} + +#ifdef DEBUG +#define DNS_ASSERT(X) if (!(X)) { fprintf(stderr, "ASSFAIL: %s %d: %s\n", __FILE__, __LINE__, #X); goto badanswer; } +#else +#define DNS_ASSERT(X) if (!(X)) { goto badanswer; } +#endif + +static struct hostent * +_dns_ghbyname(const char *name, int af, int *errp) +{ + int n; + u_char answer[BUFSIZ]; + char tbuf[MAXDNAME+1]; + HEADER *hp; + u_char *cp, *eom; + int qtype; + int type, class, ancount, qdcount; + u_long ttl; + char hostbuf[BUFSIZ]; + char *bp; + char *alist[MAXALIASES]; + char *hlist[MAXADDRS]; + struct hostent hbuf; + int buflen; + int na, nh; + + if ((_res.options & RES_INIT) == 0) { + if (res_init() < 0) { + *errp = h_errno; + return NULL; + } + } + hbuf.h_aliases = alist; + hbuf.h_addrtype = af; + hbuf.h_length = ADDRLEN(af); + hbuf.h_addr_list = hlist; + na = nh = 0; + +#ifdef INET6 + qtype = (af == AF_INET6 ? T_AAAA : T_A); +#else + qtype = T_A; +#endif + n = res_search(name, C_IN, qtype, answer, sizeof(answer)); + if (n < 0) { + *errp = h_errno; + return NULL; + } + hp = (HEADER *)answer; + eom = answer + n; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + DNS_ASSERT(qdcount == 1); + cp = answer + sizeof(HEADER); + bp = hostbuf; + buflen = sizeof(hostbuf); + + n = dn_expand(answer, eom, cp, bp, buflen); + DNS_ASSERT(n >= 0); + cp += n + QFIXEDSZ; + hbuf.h_name = bp; + n = strlen(bp) + 1; + bp += n; + buflen -= n; + while (ancount-- > 0 && cp < eom) { + n = dn_expand(answer, eom, cp, bp, buflen); + DNS_ASSERT(n >= 0); + cp += n; /* name */ + type = _getshort(cp); + cp += 2; /* type */ + class = _getshort(cp); + cp += 2; /* class */ + ttl = _getlong(cp); + cp += 4; /* ttl */ + n = _getshort(cp); + cp += 2; /* len */ + DNS_ASSERT(class == C_IN); + switch (type) { + case T_CNAME: + if (na >= MAXALIASES-1) { + cp += n; + break; + } + n = dn_expand(answer, eom, cp, tbuf, sizeof(tbuf)); + DNS_ASSERT(n >= 0); + cp += n; + /* alias */ + alist[na++] = bp; + n = strlen(bp) + 1; + bp += n; + buflen -= n; + /* canon */ + n = strlen(tbuf) + 1; + DNS_ASSERT(n < buflen); + strcpy(bp, tbuf); + hbuf.h_name = bp; + bp += n; + buflen -= n; + break; + case T_A: +#ifdef INET6 + case T_AAAA: +#endif + DNS_ASSERT(type == qtype); + bp = (char *)ALIGN(bp); + DNS_ASSERT(n == hbuf.h_length); + DNS_ASSERT(n < buflen); + if (nh < MAXADDRS-1) { + hlist[nh++] = bp; + memcpy(bp, cp, n); + bp += n; + buflen -= n; + } + cp += n; + break; + default: + DNS_ASSERT(0); + cp += n; + break; + } + } + if (nh == 0) { + badanswer: + *errp = NO_RECOVERY; + return NULL; + } + alist[na] = NULL; + hlist[nh] = NULL; + return _hpcopy(&hbuf, errp); +} + +static struct hostent * +_dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp) +{ + int n; + u_char answer[BUFSIZ]; + HEADER *hp; + u_char c, *cp, *eom; + int type, class, ancount, qdcount; + u_long ttl; + char hostbuf[BUFSIZ]; + char *bp; + char *alist[MAXALIASES]; + char *hlist[2]; + struct hostent hbuf; + int buflen; + int na; +#ifdef INET6 + static const char hex[] = "0123456789abcdef"; +#endif + +#ifdef INET6 + /* XXX */ + if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr)) + return NULL; +#endif + + if ((_res.options & RES_INIT) == 0) { + if (res_init() < 0) { + *errp = h_errno; + return NULL; + } + } + hbuf.h_name = NULL; + hbuf.h_aliases = alist; + hbuf.h_addrtype = af; + hbuf.h_length = addrlen; + hbuf.h_addr_list = hlist; + hlist[0] = (char *)addr; + hlist[1] = NULL; + na = 0; + + n = 0; + bp = hostbuf; + cp = (u_char *)addr+addrlen-1; + switch (af) { +#ifdef INET6 + case AF_INET6: + for (; n < addrlen; n++, cp--) { + c = *cp; + *bp++ = hex[c & 0xf]; + *bp++ = '.'; + *bp++ = hex[c >> 4]; + *bp++ = '.'; + } + strcpy(bp, "ip6.int"); + break; +#endif + default: + for (; n < addrlen; n++, cp--) { + c = *cp; + if (c >= 100) + *bp++ = '0' + c / 100; + if (c >= 10) + *bp++ = '0' + (c % 100) / 10; + *bp++ = '0' + c % 10; + *bp++ = '.'; + } + strcpy(bp, "in-addr.arpa"); + break; + } + + n = res_query(hostbuf, C_IN, T_PTR, answer, sizeof(answer)); + if (n < 0) { + *errp = h_errno; + return NULL; + } + hp = (HEADER *)answer; + eom = answer + n; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + DNS_ASSERT(qdcount == 1); + cp = answer + sizeof(HEADER); + bp = hostbuf; + buflen = sizeof(hostbuf); + + n = dn_expand(answer, eom, cp, bp, buflen); + DNS_ASSERT(n >= 0); + cp += n + QFIXEDSZ; + while (ancount-- > 0 && cp < eom) { + n = dn_expand(answer, eom, cp, bp, buflen); + DNS_ASSERT(n >= 0); + cp += n; /* name */ + type = _getshort(cp); + cp += 2; /* type */ + class = _getshort(cp); + cp += 2; /* class */ + ttl = _getlong(cp); + cp += 4; /* ttl */ + n = _getshort(cp); + cp += 2; /* len */ + DNS_ASSERT(class == C_IN); + switch (type) { + case T_PTR: + n = dn_expand(answer, eom, cp, bp, buflen); + DNS_ASSERT(n >= 0); + cp += n; + if (na >= MAXALIASES-1) + break; + if (hbuf.h_name == NULL) + hbuf.h_name = bp; + else + alist[na++] = bp; + n = strlen(bp) + 1; + bp += n; + buflen -= n; + break; + case T_CNAME: + cp += n; + break; + default: + badanswer: + *errp = NO_RECOVERY; + return NULL; + } + } + if (hbuf.h_name == NULL) { + *errp = h_errno; + return NULL; + } + alist[na] = NULL; + return _hpcopy(&hbuf, errp); +} + +#ifdef ICMPNL + +/* + * experimental: + * draft-ietf-ipngwg-icmp-namelookups-02.txt + * ifindex is assumed to be encoded in addr. + */ +#include +#include +#include + +struct _icmp_host_cache { + struct _icmp_host_cache *hc_next; + int hc_ifindex; + struct in6_addr hc_addr; + char *hc_name; +}; + +static char * +_icmp_fqdn_query(const struct in6_addr *addr, int ifindex) +{ + int s; + struct icmp6_filter filter; + struct msghdr msg; + struct cmsghdr *cmsg; + struct in6_pktinfo *pkt; + char cbuf[256]; + char buf[1024]; + int cc; + struct icmp6_fqdn_query *fq; + struct icmp6_fqdn_reply *fr; + struct _icmp_host_cache *hc; + struct sockaddr_in6 sin6; + struct iovec iov; + fd_set s_fds, fds; + struct timeval tout; + int len; + char *name; + static int pid; + static struct _icmp_host_cache *hc_head; + + for (hc = hc_head; hc; hc = hc->hc_next) { + if (hc->hc_ifindex == ifindex + && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) + return hc->hc_name; + } + + if (pid == 0) + pid = getpid(); + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter); + + FD_ZERO(&s_fds); + tout.tv_sec = 0; + tout.tv_usec = 200000; /*XXX: 200ms*/ + + fq = (struct icmp6_fqdn_query *)buf; + fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY; + fq->icmp6_fqdn_code = 0; + fq->icmp6_fqdn_cksum = 0; + fq->icmp6_fqdn_id = (u_short)pid; + fq->icmp6_fqdn_unused = 0; + fq->icmp6_fqdn_cookie[0] = 0; + fq->icmp6_fqdn_cookie[1] = 0; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (caddr_t)&sin6; + msg.msg_namelen = sizeof(sin6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + iov.iov_base = (caddr_t)buf; + iov.iov_len = sizeof(struct icmp6_fqdn_query); + + if (ifindex) { + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + pkt = (struct in6_pktinfo *)&cmsg[1]; + memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr)); + pkt->ipi6_ifindex = ifindex; + cmsg = CMSG_NXTHDR(&msg, cmsg); + msg.msg_controllen = (char *)cmsg - cbuf; + } + + if ((s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) + return NULL; + (void)setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, + (char *)&filter, sizeof(filter)); + cc = sendmsg(s, &msg, 0); + if (cc < 0) { + close(s); + return NULL; + } + FD_SET(s, &s_fds); + for (;;) { + fds = s_fds; + if (select(s + 1, &fds, NULL, NULL, &tout) <= 0) { + close(s); + return NULL; + } + len = sizeof(sin6); + cc = recvfrom(s, buf, sizeof(buf), 0, + (struct sockaddr *)&sin6, &len); + if (cc <= 0) { + close(s); + return NULL; + } + if (cc < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + continue; + if (!IN6_ARE_ADDR_EQUAL(addr, &sin6.sin6_addr)) + continue; + fr = (struct icmp6_fqdn_reply *)(buf + sizeof(struct ip6_hdr)); + if (fr->icmp6_fqdn_type == ICMP6_FQDN_REPLY) + break; + } + close(s); + if (fr->icmp6_fqdn_cookie[1] != 0) { + /* rfc1788 type */ + name = buf + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4; + len = (buf + cc) - name; + } else { + len = fr->icmp6_fqdn_namelen; + name = fr->icmp6_fqdn_name; + } + if (len <= 0) + return NULL; + name[len] = 0; + + if ((hc = (struct _icmp_host_cache *)malloc(sizeof(*hc))) == NULL) + return NULL; + /* XXX: limit number of cached entries */ + hc->hc_ifindex = ifindex; + hc->hc_addr = *addr; + hc->hc_name = strdup(name); + hc->hc_next = hc_head; + hc_head = hc; + return hc->hc_name; +} + +static struct hostent * +_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp) +{ + char *hname; + int ifindex; + struct in6_addr addr6; + + if (af != AF_INET6) { + /* + * Note: rfc1788 defines Who Are You for IPv4, + * but no one implements it. + */ + return NULL; + } + + memcpy(&addr6, addr, addrlen); + ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3]; + addr6.s6_addr[2] = addr6.s6_addr[3] = 0; + + if (!IN6_IS_ADDR_LINKLOCAL(&addr6)) + return NULL; /*XXX*/ + + if ((hname = _icmp_fqdn_query(&addr6, ifindex)) == NULL) + return NULL; + return _hpaddr(af, hname, &addr6, errp); +} +#endif /* ICMPNL */ diff --git a/lib/libc/net/res_init.c b/lib/libc/net/res_init.c index 8f6e649..d1e6249 100644 --- a/lib/libc/net/res_init.c +++ b/lib/libc/net/res_init.c @@ -112,6 +112,9 @@ struct __res_state _res # endif ; +#ifdef INET6 +struct __res_state_ext _res_ext; +#endif /* INET6 */ /* * Set up default settings. If the configuration file exist, the values @@ -314,6 +317,9 @@ res_init() #ifdef RESOLVSORT if (MATCH(buf, "sortlist")) { struct in_addr a; +#ifdef INET6 + struct in6_addr a6; +#endif /* INET6 */ cp = buf + sizeof("sortlist") - 1; while (nsort < MAXRESOLVSORT) { @@ -347,8 +353,61 @@ res_init() _res.sort_list[nsort].mask = net_mask(_res.sort_list[nsort].addr); } +#ifdef INET6 + _res_ext.sort_list[nsort].af = AF_INET; + _res_ext.sort_list[nsort].addr.ina = + _res.sort_list[nsort].addr; + _res_ext.sort_list[nsort].mask.ina.s_addr = + _res.sort_list[nsort].mask; +#endif /* INET6 */ nsort++; } +#ifdef INET6 + else if (inet_pton(AF_INET6, net, &a6) == 1) { + int m, i; + u_char *u; + + _res_ext.sort_list[nsort].af = AF_INET6; + _res_ext.sort_list[nsort].addr.in6a = a6; + u = (u_char *)&_res_ext.sort_list[nsort].mask.in6a; + *cp++ = n; + net = cp; + while (*cp && *cp != ';' && + isascii(*cp) && !isspace(*cp)) + cp++; + m = n; + n = *cp; + *cp = 0; + switch (m) { + case '/': + m = atoi(net); + break; + case '&': + if (inet_pton(AF_INET6, net, u) == 1) { + m = -1; + break; + } + /*FALLTHROUGH*/ + default: + m = sizeof(struct in6_addr) * NBBY; + break; + } + if (m >= 0) { + for (i = 0; i < sizeof(struct in6_addr); i++) { + if (m <= 0) { + *u = 0; + } else { + m -= NBBY; + *u = (u_char)~0; + if (m < 0) + *u <<= -m; + } + u++; + } + } + nsort++; + } +#endif /* INET6 */ *cp = n; } continue; -- cgit v1.1