diff options
Diffstat (limited to 'lib/libc/net')
78 files changed, 25963 insertions, 0 deletions
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc new file mode 100644 index 0000000..9921f8d --- /dev/null +++ b/lib/libc/net/Makefile.inc @@ -0,0 +1,126 @@ +# from @(#)Makefile.inc 8.2 (Berkeley) 9/5/93 +# $FreeBSD$ + +# machine-independent net sources +.PATH: ${.CURDIR}/${MACHINE_ARCH}/net ${.CURDIR}/net + +SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c eui64.c \ + gai_strerror.c getaddrinfo.c \ + gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ + getifaddrs.c getifmaddrs.c getnameinfo.c \ + getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \ + getproto.c getprotoent.c getprotoname.c getservent.c \ + if_indextoname.c if_nameindex.c if_nametoindex.c \ + ip6opt.c linkaddr.c map_v4v6.c name6.c \ + nsdispatch.c nslexer.c nsparser.c nss_compat.c \ + rcmd.c rcmdsh.c recv.c rthdr.c send.c sockatmark.c vars.c + +.if ${MK_NS_CACHING} != "no" +SRCS+= nscache.c nscachedcli.c +.endif + +# for binary backward compatibility against FreeBSD 6.X and earlier +SRCS+= res_mkupdate.c res_update.c + +SYM_MAPS+=${.CURDIR}/net/Symbol.map + +CFLAGS+=-DINET6 -I${.OBJDIR} + +# name6.c refers res_private.h +CFLAGS+=-I${.CURDIR}/resolv + +YFLAGS+=-p_nsyy +LFLAGS+=-P_nsyy + +CLEANFILES+=nsparser.c nslexer.c nsparser.h + +nsparser.h: nsparser.c + mv y.tab.h ${.TARGET} + +nslexer.c: nslexer.l nsparser.h + ${LEX} ${LFLAGS} -o/dev/stdout ${.IMPSRC} | \ + sed -e '/YY_BUF_SIZE/s/16384/1024/' >${.TARGET} + +# machine-dependent net sources +.if exists(${.CURDIR}/${MACHINE_ARCH}/net/Makefile.inc) +.include "${.CURDIR}/${MACHINE_ARCH}/net/Makefile.inc" +.endif + +MAN+= addr2ascii.3 byteorder.3 ethers.3 eui64.3 \ + getaddrinfo.3 gai_strerror.3 gethostbyname.3 \ + getifaddrs.3 getifmaddrs.3 getipnodebyname.3 \ + getnameinfo.3 getnetent.3 getprotoent.3 getservent.3 \ + if_indextoname.3 \ + inet.3 inet_net.3 \ + inet6_opt_init.3 inet6_option_space.3 inet6_rth_space.3 \ + inet6_rthdr_space.3 linkaddr.3 \ + nsdispatch.3 rcmd.3 rcmdsh.3 resolver.3 sockatmark.3 + +MLINKS+=addr2ascii.3 ascii2addr.3 +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+=eui64.3 eui64_aton.3 eui64.3 eui64_hostton.3 \ + eui64.3 eui64_ntoa.3 eui64.3 eui64_ntohost.3 +MLINKS+=getaddrinfo.3 freeaddrinfo.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+=getifaddrs.3 freeifaddrs.3 +MLINKS+=getifmaddrs.3 freeifmaddrs.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 \ + getprotoent.3 getprotobynumber.3 getprotoent.3 setprotoent.3 +MLINKS+=getservent.3 endservent.3 getservent.3 getservbyname.3 \ + getservent.3 getservbyport.3 getservent.3 setservent.3 +MLINKS+=if_indextoname.3 if_nametoindex.3 if_indextoname.3 if_nameindex.3 \ + if_indextoname.3 if_freenameindex.3 +MLINKS+=inet.3 addr.3 inet.3 inet_addr.3 inet.3 inet_aton.3 \ + inet.3 inet_lnaof.3 inet.3 inet_makeaddr.3 inet.3 inet_netof.3 \ + inet.3 inet_network.3 inet.3 inet_ntoa.3 \ + inet.3 inet_ntop.3 inet.3 inet_pton.3 \ + inet.3 network.3 inet.3 ntoa.3 +MLINKS+=inet_net.3 inet_net_ntop.3 inet_net.3 inet_net_pton.3 +MLINKS+=inet6_opt_init.3 inet6_opt_append.3 \ + inet6_opt_init.3 inet6_opt_find.3 \ + inet6_opt_init.3 inet6_opt_finish.3 \ + inet6_opt_init.3 inet6_opt_get_val.3 \ + inet6_opt_init.3 inet6_opt_next.3 \ + inet6_opt_init.3 inet6_opt_set_val.3 \ + inet6_option_space.3 inet6_option_alloc.3 \ + inet6_option_space.3 inet6_option_append.3 \ + inet6_option_space.3 inet6_option_find.3 \ + inet6_option_space.3 inet6_option_init.3 \ + inet6_option_space.3 inet6_option_next.3 \ + inet6_rth_space.3 inet6_rth_add.3 \ + inet6_rth_space.3 inet6_rth_getaddr.3 \ + inet6_rth_space.3 inet6_rth_init.3 \ + inet6_rth_space.3 inet6_rth_reverse.3 \ + inet6_rth_space.3 inet6_rth_segments.3 \ + inet6_rthdr_space.3 inet6_rthdr_add.3 \ + inet6_rthdr_space.3 inet6_rthdr_getaddr.3 \ + inet6_rthdr_space.3 inet6_rthdr_getflags.3 \ + inet6_rthdr_space.3 inet6_rthdr_init.3 \ + inet6_rthdr_space.3 inet6_rthdr_lasthop.3 \ + inet6_rthdr_space.3 inet6_rthdr_reverse.3 \ + inet6_rthdr_space.3 inet6_rthdr_segments.3 +MLINKS+=linkaddr.3 link_addr.3 linkaddr.3 link_ntoa.3 +MLINKS+=rcmd.3 iruserok.3 rcmd.3 iruserok_sa.3 \ + rcmd.3 rcmd_af.3 \ + rcmd.3 rresvport.3 rcmd.3 rresvport_af.3 \ + rcmd.3 ruserok.3 +MLINKS+=resolver.3 dn_comp.3 resolver.3 dn_expand.3 resolver.3 res_init.3 \ + resolver.3 res_mkquery.3 resolver.3 res_query.3 \ + resolver.3 res_search.3 resolver.3 res_send.3 resolver.3 dn_skipname.3 \ + resolver.3 ns_get16.3 resolver.3 ns_get32.3 \ + resolver.3 ns_put16.3 resolver.3 ns_put32.3 + +.if ${MK_HESIOD} != "no" +SRCS+= hesiod.c +MAN+= hesiod.3 +.endif + diff --git a/lib/libc/net/Symbol.map b/lib/libc/net/Symbol.map new file mode 100644 index 0000000..849d392 --- /dev/null +++ b/lib/libc/net/Symbol.map @@ -0,0 +1,143 @@ +# $FreeBSD$ + +FBSD_1.0 { + addr2ascii; + ascii2addr; + __b64_ntop; + __b64_pton; + ether_line; + ether_aton; + ether_ntoa; + ether_ntohost; + ether_hostton; + eui64_aton; + eui64_ntoa; + eui64_ntohost; + eui64_hostton; + gai_strerror; + freeaddrinfo; + getaddrinfo; + endhostdnsent; + gethostent; + gethostent_r; + gethostbyname; + gethostbyname_r; + gethostbyname2; + gethostbyname2_r; + gethostbyaddr; + gethostbyaddr_r; + sethostent; + endhostent; + getifaddrs; + freeifaddrs; + getifmaddrs; + freeifmaddrs; + getnameinfo; + getnetent; + getnetent_r; + getnetbyname; + getnetbyname_r; + getnetbyaddr; + getnetbyaddr_r; + setnetent; + endnetent; + getprotobynumber; + getprotobynumber_r; + setprotoent; + endprotoent; + getprotoent; + getprotoent_r; + getprotobyname; + getprotobyname_r; + getservbyname; + getservbyname_r; + getservbyport; + getservbyport_r; + setservent; + endservent; + getservent; + getservent_r; + hesiod_init; + hesiod_end; + hesiod_to_bind; + hesiod_resolv; + hesiod_free_list; + hes_init; + hes_to_bind; + hes_resolv; + hes_error; + hes_free; + if_indextoname; + if_nameindex; + if_freenameindex; + if_nametoindex; + inet6_option_space; + inet6_option_init; + inet6_option_append; + inet6_option_alloc; + inet6_option_next; + inet6_option_find; + inet6_opt_init; + inet6_opt_append; + inet6_opt_finish; + inet6_opt_set_val; + inet6_opt_next; + inet6_opt_find; + inet6_opt_get_val; + link_addr; + link_ntoa; + getipnodebyname; + getipnodebyaddr; + freehostent; + __nsdefaultsrc; + _nsdbtaddsrc; + _nsdbtdump; + _nsdbtput; + nsdispatch; + rcmd; + rcmd_af; + rresvport; + rresvport_af; + ruserok; + iruserok; + iruserok_sa; + rcmdsh; + recv; + __res_freeupdrec; + __res_mkupdrec; + __res_nmkupdate; + __res_nupdate; + inet6_rthdr_space; + inet6_rthdr_init; + inet6_rthdr_add; + inet6_rthdr_lasthop; + inet6_rthdr_segments; + inet6_rthdr_getaddr; + inet6_rthdr_getflags; + inet6_rth_space; + inet6_rth_init; + inet6_rth_add; + inet6_rth_reverse; + inet6_rth_segments; + inet6_rth_getaddr; + send; + sockatmark; + in6addr_any; + in6addr_loopback; + in6addr_nodelocal_allnodes; + in6addr_linklocal_allnodes; +}; + +FBSDprivate { + _nsdispatch; + _nsyyerror; # generated from nslexer.l + _nsyylex; # generated from nslexer.l + _nsyyparse; # generated from nsparser.y + _nsyylineno; # generated from nsparser.y + __dns_getanswer; + __ivaliduser; + __ivaliduser_af; + __ivaliduser_sa; + __check_rhosts_file; + __rcmd_errstr; +}; diff --git a/lib/libc/net/addr2ascii.3 b/lib/libc/net/addr2ascii.3 new file mode 100644 index 0000000..675c39d --- /dev/null +++ b/lib/libc/net/addr2ascii.3 @@ -0,0 +1,235 @@ +.\" +.\" Copyright 1996 Massachusetts Institute of Technology +.\" +.\" Permission to use, copy, modify, and distribute this software and +.\" its documentation for any purpose and without fee is hereby +.\" granted, provided that both the above copyright notice and this +.\" permission notice appear in all copies, that both the above +.\" copyright notice and this permission notice appear in all +.\" supporting documentation, and that the name of M.I.T. not be used +.\" in advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. M.I.T. makes +.\" no representations about the suitability of this software for any +.\" purpose. It is provided "as is" without express or implied +.\" warranty. +.\" +.\" THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS +.\" ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, +.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT +.\" SHALL M.I.T. 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. +.\" +.\" $ANA: addr2ascii.3,v 1.1 1996/06/13 18:41:46 wollman Exp $ +.\" $FreeBSD$ +.\" +.Dd June 13, 1996 +.Dt ADDR2ASCII 3 +.Os +.Sh NAME +.Nm addr2ascii , +.Nm ascii2addr +.Nd Generic address formatting routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In arpa/inet.h +.Ft "char *" +.Fn addr2ascii "int af" "const void *addrp" "int len" "char *buf" +.Ft int +.Fn ascii2addr "int af" "const char *ascii" "void *result" +.Sh DESCRIPTION +The routines +.Fn addr2ascii +and +.Fn ascii2addr +are used to convert network addresses between binary form and a +printable form appropriate to the address family. +Both functions take +an +.Fa af +argument, specifying the address family to be used in the conversion +process. +(Currently, only the +.Dv AF_INET +and +.Dv AF_LINK +address families are supported.) +.Pp +The +.Fn addr2ascii +function +is used to convert binary, network-format addresses into printable +form. +In addition to +.Fa af , +there are three other arguments. +The +.Fa addrp +argument is a pointer to the network address to be converted. +The +.Fa len +argument is the length of the address. +The +.Fa buf +argument is an optional pointer to a caller-allocated buffer to hold +the result; if a null pointer is passed, +.Fn addr2ascii +uses a statically-allocated buffer. +.Pp +The +.Fn ascii2addr +function performs the inverse operation to +.Fn addr2ascii . +In addition to +.Fa af , +it takes two arguments, +.Fa ascii +and +.Fa result . +The +.Fa ascii +argument is a pointer to the string which is to be converted into +binary. +The +.Fa result +argument is a pointer to an appropriate network address structure for +the specified family. +.Pp +The following gives the appropriate structure to use for binary +addresses in the specified family: +.Pp +.Bl -tag -width AF_INETxxxx -compact +.It Dv AF_INET +.Vt "struct in_addr" +(in +.In arpa/inet.h ) +.It Dv AF_LINK +.Vt "struct sockaddr_dl" +(in +.In net/if_dl.h ) +.\" .It Dv AF_INET6 +.\" .Vt "struct in6_addr" +.\" (in +.\" .In netinet6/in6.h ) +.El +.Pp +.Dv AF_INET +and +.Dv AF_LINK +constants are defined in +.In sys/socket.h . +.Sh RETURN VALUES +The +.Fn addr2ascii +function returns the address of the buffer it was passed, or a static +buffer if the a null pointer was passed; on failure, it returns a null +pointer. +The +.Fn ascii2addr +function returns the length of the binary address in bytes, or -1 on +failure. +.Sh EXAMPLES +The +.Xr inet 3 +functions +.Fn inet_ntoa +and +.Fn inet_aton +could be implemented thusly: +.Bd -literal -offset indent +#include <sys/socket.h> +#include <arpa/inet.h> + +char * +inet_ntoa(struct in_addr addr) +{ + return addr2ascii(AF_INET, &addr, sizeof addr, 0); +} + +int +inet_aton(const char *ascii, struct in_addr *addr) +{ + return (ascii2addr(AF_INET, ascii, addr) + == sizeof(*addr)); +} +.Ed +.Pp +In actuality, this cannot be done because +.Fn addr2ascii +and +.Fn ascii2addr +are implemented in terms of the +.Xr inet 3 +functions, rather than the other way around. +.Sh ERRORS +When a failure is returned, +.Va errno +is set to one of the following values: +.Bl -tag -width Er +.It Bq Er ENAMETOOLONG +The +.Fn addr2ascii +routine was passed a +.Fa len +argument which was inappropriate for the address family given by +.Fa af . +.It Bq Er EPROTONOSUPPORT +Either routine was passed an +.Fa af +argument other than +.Dv AF_INET +or +.Dv AF_LINK . +.It Bq Er EINVAL +The string passed to +.Fn ascii2addr +was improperly formatted for address family +.Fa af . +.El +.Sh SEE ALSO +.Xr inet 3 , +.Xr linkaddr 3 , +.Xr inet 4 +.Sh HISTORY +An interface close to this one was originally suggested by Craig +Partridge. +This particular interface originally appeared in the +.Tn INRIA +.Tn IPv6 +implementation. +.Sh AUTHORS +Code and documentation by +.An Garrett A. Wollman , +MIT Laboratory for Computer Science. +.Sh BUGS +The original implementations supported IPv6. +This support should +eventually be resurrected. +The +.Tn NRL +implementation also included support for the +.Dv AF_ISO +and +.Dv AF_NS +address families. +.Pp +The genericity of this interface is somewhat questionable. +A truly +generic interface would provide a means for determining the length of +the buffer to be used so that it could be dynamically allocated, and +would always require a +.Vt "struct sockaddr" +to hold the binary address. +Unfortunately, this is incompatible with existing +practice. +This limitation means that a routine for printing network +addresses from arbitrary address families must still have internal +knowledge of the maximum buffer length needed and the appropriate part +of the address to use as the binary address. diff --git a/lib/libc/net/addr2ascii.c b/lib/libc/net/addr2ascii.c new file mode 100644 index 0000000..4fad6a8 --- /dev/null +++ b/lib/libc/net/addr2ascii.c @@ -0,0 +1,95 @@ +/* + * Copyright 1996 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $ANA: addr2ascii.c,v 1.1 1996/06/13 18:41:46 wollman Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> + +#include <errno.h> +#include <string.h> + +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/*- + * Convert a network address from binary to printable numeric format. + * This API is copied from INRIA's IPv6 implementation, but it is a + * bit bogus in two ways: + * + * 1) There is no value in passing both an address family and + * an address length; either one should imply the other, + * or we should be passing sockaddrs instead. + * 2) There should by contrast be /added/ a length for the buffer + * that we pass in, so that programmers are spared the need to + * manually calculate (read: ``guess'') the maximum length. + * + * Flash: the API is also the same in the NRL implementation, and seems to + * be some sort of standard, so we appear to be stuck with both the bad + * naming and the poor choice of arguments. + */ +char * +addr2ascii(af, addrp, len, buf) + int af; + const void *addrp; + int len; /* should be size_t XXX */ + char *buf; /* XXX should pass length of buffer */ +{ + static char staticbuf[64]; /* 64 for AF_LINK > 16 for AF_INET */ + + if (!buf) + buf = staticbuf; + + switch(af) { + case AF_INET: + if (len != sizeof(struct in_addr)) { + errno = ENAMETOOLONG; + return 0; + } + strcpy(buf, inet_ntoa(*(const struct in_addr *)addrp)); + break; + + case AF_LINK: + if (len != sizeof(struct sockaddr_dl)) { + errno = ENAMETOOLONG; + return 0; + } + strcpy(buf, link_ntoa((const struct sockaddr_dl *)addrp)); + break; + + default: + errno = EPROTONOSUPPORT; + return 0; + } + return buf; +} diff --git a/lib/libc/net/ascii2addr.c b/lib/libc/net/ascii2addr.c new file mode 100644 index 0000000..5167a51 --- /dev/null +++ b/lib/libc/net/ascii2addr.c @@ -0,0 +1,75 @@ +/* + * Copyright 1996 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * $ANA: ascii2addr.c,v 1.2 1996/06/13 18:46:02 wollman Exp $ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> + +#include <errno.h> +#include <string.h> + +#include <net/if_dl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +int +ascii2addr(af, ascii, result) + int af; + const char *ascii; + void *result; +{ + struct in_addr *ina; + char strbuf[4*sizeof("123")]; /* long enough for V4 only */ + + switch(af) { + case AF_INET: + ina = result; + strbuf[0] = '\0'; + strncat(strbuf, ascii, (sizeof strbuf)-1); + if (inet_aton(strbuf, ina)) + return sizeof(struct in_addr); + errno = EINVAL; + break; + + case AF_LINK: + link_addr(ascii, result); + /* oops... no way to detect failure */ + return sizeof(struct sockaddr_dl); + + default: + errno = EPROTONOSUPPORT; + break; + } + + return -1; +} diff --git a/lib/libc/net/base64.c b/lib/libc/net/base64.c new file mode 100644 index 0000000..4335030 --- /dev/null +++ b/lib/libc/net/base64.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(src, target, targsize) + char const *src; + u_char *target; + size_t targsize; +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace((unsigned char)ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/lib/libc/net/byteorder.3 b/lib/libc/net/byteorder.3 new file mode 100644 index 0000000..71ccc4d --- /dev/null +++ b/lib/libc/net/byteorder.3 @@ -0,0 +1,90 @@ +.\" Copyright (c) 1983, 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. +.\" +.\" @(#)byteorder.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd March 20, 2005 +.Dt BYTEORDER 3 +.Os +.Sh NAME +.Nm htonl , +.Nm htons , +.Nm ntohl , +.Nm ntohs +.Nd convert values between host and network byte order +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In arpa/inet.h +.Pp +or +.Pp +.In netinet/in.h +.Ft uint32_t +.Fn htonl "uint32_t hostlong" +.Ft uint16_t +.Fn htons "uint16_t hostshort" +.Ft uint32_t +.Fn ntohl "uint32_t netlong" +.Ft uint16_t +.Fn ntohs "uint16_t netshort" +.Sh DESCRIPTION +These routines convert 16 and 32 bit quantities between network +byte order and host byte order. +On machines which have a byte order which is the same as the network +order, routines are defined as null macros. +.Pp +These routines are most often used in conjunction with Internet +addresses and ports as returned by +.Xr gethostbyname 3 +and +.Xr getservent 3 . +.Sh SEE ALSO +.Xr gethostbyname 3 , +.Xr getservent 3 , +.Xr byteorder 9 +.Sh STANDARDS +The +.Nm byteorder +functions conform to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Nm byteorder +functions appeared in +.Bx 4.2 . +.Sh BUGS +On the +.Tn VAX +bytes are handled backwards from most everyone else in +the world. +This is not expected to be fixed in the near future. diff --git a/lib/libc/net/ether_addr.c b/lib/libc/net/ether_addr.c new file mode 100644 index 0000000..e8bc8ba --- /dev/null +++ b/lib/libc/net/ether_addr.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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. + * + * ethernet address conversion and lookup routines + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Center for Telecommunications Research + * Columbia University, New York City + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <paths.h> +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <net/ethernet.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif + +#ifndef _PATH_ETHERS +#define _PATH_ETHERS "/etc/ethers" +#endif + +/* + * Parse a string of text containing an ethernet address and hostname + * and separate it into its component parts. + */ +int +ether_line(l, e, hostname) + const char *l; + struct ether_addr *e; + char *hostname; +{ + int i, o[6]; + + i = sscanf(l, "%x:%x:%x:%x:%x:%x %s", &o[0], &o[1], &o[2], + &o[3], &o[4], &o[5], + hostname); + if (i != 7) + return (i); + + for (i=0; i<6; i++) + e->octet[i] = o[i]; + return (0); +} + +/* + * Convert an ASCII representation of an ethernet address to + * binary form. + */ +struct +ether_addr *ether_aton(a) + const char *a; +{ + int i; + static struct ether_addr o; + unsigned int o0, o1, o2, o3, o4, o5; + + i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); + + if (i != 6) + return (NULL); + + o.octet[0]=o0; + o.octet[1]=o1; + o.octet[2]=o2; + o.octet[3]=o3; + o.octet[4]=o4; + o.octet[5]=o5; + + return ((struct ether_addr *)&o); +} + +/* + * Convert a binary representation of an ethernet address to + * an ASCII string. + */ +char +*ether_ntoa(n) + const struct ether_addr *n; +{ + int i; + static char a[18]; + + i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x", + n->octet[0], n->octet[1], n->octet[2], + n->octet[3], n->octet[4], n->octet[5]); + if (i < 17) + return (NULL); + return ((char *)&a); +} + +/* + * Map an ethernet address to a hostname. Use either /etc/ethers or + * NIS/YP. + */ +int +ether_ntohost(hostname, e) + char *hostname; + const struct ether_addr *e; +{ + FILE *fp; + char buf[BUFSIZ + 2]; + struct ether_addr local_ether; + char local_host[MAXHOSTNAMELEN]; +#ifdef YP + char *result; + int resultlen; + char *ether_a; + char *yp_domain; +#endif + if ((fp = fopen(_PATH_ETHERS, "r")) == NULL) + return (1); + + while (fgets(buf,BUFSIZ,fp)) { + if (buf[0] == '#') + continue; +#ifdef YP + if (buf[0] == '+') { + if (yp_get_default_domain(&yp_domain)) + continue; + ether_a = ether_ntoa(e); + if (yp_match(yp_domain, "ethers.byaddr", ether_a, + strlen(ether_a), &result, &resultlen)) { + continue; + } + strncpy(buf, result, resultlen); + buf[resultlen] = '\0'; + free(result); + } +#endif + if (!ether_line(buf, &local_ether, local_host)) { + if (!bcmp((char *)&local_ether.octet[0], + (char *)&e->octet[0], 6)) { + /* We have a match */ + strcpy(hostname, local_host); + fclose(fp); + return(0); + } + } + } + fclose(fp); + return (1); +} + +/* + * Map a hostname to an ethernet address using /etc/ethers or + * NIS/YP. + */ +int +ether_hostton(hostname, e) + const char *hostname; + struct ether_addr *e; +{ + FILE *fp; + char buf[BUFSIZ + 2]; + struct ether_addr local_ether; + char local_host[MAXHOSTNAMELEN]; +#ifdef YP + char *result; + int resultlen; + char *yp_domain; +#endif + if ((fp = fopen(_PATH_ETHERS, "r")) == NULL) + return (1); + + while (fgets(buf,BUFSIZ,fp)) { + if (buf[0] == '#') + continue; +#ifdef YP + if (buf[0] == '+') { + if (yp_get_default_domain(&yp_domain)) + continue; + if (yp_match(yp_domain, "ethers.byname", hostname, + strlen(hostname), &result, &resultlen)) { + continue; + } + strncpy(buf, result, resultlen); + buf[resultlen] = '\0'; + free(result); + } +#endif + if (!ether_line(buf, &local_ether, local_host)) { + if (!strcmp(hostname, local_host)) { + /* We have a match */ + bcopy((char *)&local_ether.octet[0], + (char *)&e->octet[0], 6); + fclose(fp); + return(0); + } + } + } + fclose(fp); + return (1); +} diff --git a/lib/libc/net/ethers.3 b/lib/libc/net/ethers.3 new file mode 100644 index 0000000..a132447 --- /dev/null +++ b/lib/libc/net/ethers.3 @@ -0,0 +1,201 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. 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 Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 12, 1995 +.Dt ETHERS 3 +.Os +.Sh NAME +.Nm ethers , +.Nm ether_line , +.Nm ether_aton , +.Nm ether_ntoa , +.Nm ether_ntohost , +.Nm ether_hostton +.Nd Ethernet address conversion and lookup routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In net/ethernet.h +.Ft int +.Fn ether_line "const char *l" "struct ether_addr *e" "char *hostname" +.Ft struct ether_addr * +.Fn ether_aton "const char *a" +.Ft char * +.Fn ether_ntoa "const struct ether_addr *n" +.Ft int +.Fn ether_ntohost "char *hostname" "const struct ether_addr *e" +.Ft int +.Fn ether_hostton "const char *hostname" "struct ether_addr *e" +.Sh DESCRIPTION +These functions operate on ethernet addresses using an +.Vt ether_addr +structure, which is defined in the header file +.In netinet/if_ether.h : +.Bd -literal -offset indent +/* + * The number of bytes in an ethernet (MAC) address. + */ +#define ETHER_ADDR_LEN 6 + +/* + * Structure of a 48-bit Ethernet address. + */ +struct ether_addr { + u_char octet[ETHER_ADDR_LEN]; +}; +.Ed +.Pp +The function +.Fn ether_line +scans +.Fa l , +an +.Tn ASCII +string in +.Xr ethers 5 +format and sets +.Fa e +to the ethernet address specified in the string and +.Fa h +to the hostname. +This function is used to parse lines from +.Pa /etc/ethers +into their component parts. +.Pp +The +.Fn ether_aton +function converts an +.Tn ASCII +representation of an ethernet address into an +.Vt ether_addr +structure. +Likewise, +.Fn ether_ntoa +converts an ethernet address specified as an +.Vt ether_addr +structure into an +.Tn ASCII +string. +.Pp +The +.Fn ether_ntohost +and +.Fn ether_hostton +functions map ethernet addresses to their corresponding hostnames +as specified in the +.Pa /etc/ethers +database. +The +.Fn ether_ntohost +function +converts from ethernet address to hostname, and +.Fn ether_hostton +converts from hostname to ethernet address. +.Sh RETURN VALUES +The +.Fn ether_line +function +returns zero on success and non-zero if it was unable to parse +any part of the supplied line +.Fa l . +It returns the extracted ethernet address in the supplied +.Vt ether_addr +structure +.Fa e +and the hostname in the supplied string +.Fa h . +.Pp +On success, +.Fn ether_ntoa +returns a pointer to a string containing an +.Tn ASCII +representation of an ethernet address. +If it is unable to convert +the supplied +.Vt ether_addr +structure, it returns a +.Dv NULL +pointer. +Likewise, +.Fn ether_aton +returns a pointer to an +.Vt ether_addr +structure on success and a +.Dv NULL +pointer on failure. +.Pp +The +.Fn ether_ntohost +and +.Fn ether_hostton +functions both return zero on success or non-zero if they were +unable to find a match in the +.Pa /etc/ethers +database. +.Sh NOTES +The user must insure that the hostname strings passed to the +.Fn ether_line , +.Fn ether_ntohost +and +.Fn ether_hostton +functions are large enough to contain the returned hostnames. +.Sh NIS INTERACTION +If the +.Pa /etc/ethers +contains a line with a single + in it, the +.Fn ether_ntohost +and +.Fn ether_hostton +functions will attempt to consult the NIS +.Pa ethers.byname +and +.Pa ethers.byaddr +maps in addition to the data in the +.Pa /etc/ethers +file. +.Sh SEE ALSO +.Xr ethers 5 , +.Xr yp 8 +.Sh HISTORY +This particular implementation of the +.Nm +library functions were written for and first appeared in +.Fx 2.1 . +.Sh BUGS +The +.Fn ether_aton +and +.Fn ether_ntoa +functions returns values that are stored in static memory areas +which may be overwritten the next time they are called. diff --git a/lib/libc/net/eui64.3 b/lib/libc/net/eui64.3 new file mode 100644 index 0000000..3000c74 --- /dev/null +++ b/lib/libc/net/eui64.3 @@ -0,0 +1,230 @@ +.\" Copyright 2004 The Aerospace Corporation. 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. The name of The Aerospace Corporation may not be used to endorse or +.\" promote products derived from this software. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "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 AEROSPACE CORPORATION 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. +.\" +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. 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 Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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. +.\" +.\" $FreeBSD$ +.\" +.Dd March 4, 2004 +.Dt EUI64 3 +.Os +.Sh NAME +.Nm eui64 , +.\" .Nm eui64_line , +.Nm eui64_aton , +.Nm eui64_ntoa , +.Nm eui64_ntohost , +.Nm eui64_hostton +.Nd IEEE EUI-64 conversion and lookup routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/eui64.h +.\" .Ft int +.\" .Fn eui64_line "const char *l" "struct eui64 *e" "char *hostname" "size_t len" +.Ft int +.Fn eui64_aton "const char *a" "struct eui64 *e" +.Ft int +.Fn eui64_ntoa "const struct eui64 *id" "char *a" "size_t len" +.Ft int +.Fn eui64_ntohost "char *hostname" "size_t len" "const struct eui64 *id" +.Ft int +.Fn eui64_hostton "const char *hostname" "struct eui64 *id" +.Sh DESCRIPTION +These functions operate on IEEE EUI-64s using an +.Vt eui64 +structure, which is defined in the header file +.In sys/eui64.h : +.Bd -literal -offset indent +/* + * The number of bytes in an EUI-64. + */ +#define EUI64_LEN 8 + +/* + * Structure of an IEEE EUI-64. + */ +struct eui64 { + u_char octet[EUI64_LEN]; +}; +.Ed +.\" .Pp +.\" The function +.\" .Fn eui64_line +.\" scans +.\" .Fa l , +.\" an +.\" .Tn ASCII +.\" string in +.\" .Xr eui64 5 +.\" format and sets +.\" .Fa e +.\" to the EUI-64 specified in the string and +.\" .Fa h +.\" to the hostname. +.\" This function is used to parse lines from +.\" .Pa /etc/eui64 +.\" into their component parts. +.Pp +The +.Fn eui64_aton +function converts an +.Tn ASCII +representation of an EUI-64 into an +.Vt eui64 +structure. +Likewise, +.Fn eui64_ntoa +converts an EUI-64 specified as an +.Vt eui64 +structure into an +.Tn ASCII +string. +.Pp +The +.Fn eui64_ntohost +and +.Fn eui64_hostton +functions map EUI-64s to their corresponding hostnames +as specified in the +.Pa /etc/eui64 +database. +The +.Fn eui64_ntohost +function +converts from EUI-64 to hostname, and +.Fn eui64_hostton +converts from hostname to EUI-64. +.Sh RETURN VALUES +.\" The +.\" .Fn eui64_line +.\" function +.\" returns zero on success and non-zero if it was unable to parse +.\" any part of the supplied line +.\" .Fa l . +.\" It returns the extracted EUI-64 in the supplied +.\" .Vt eui64 +.\" structure +.\" .Fa e +.\" and the hostname in the supplied string +.\" .Fa h . +.\" .Pp +On success, +.Fn eui64_ntoa +returns a pointer to a string containing an +.Tn ASCII +representation of an EUI-64. +If it is unable to convert +the supplied +.Vt eui64 +structure, it returns a +.Dv NULL +pointer. +Likewise, +.Fn eui64_aton +returns a pointer to an +.Vt eui64 +structure on success and a +.Dv NULL +pointer on failure. +.Pp +The +.Fn eui64_ntohost +and +.Fn eui64_hostton +functions both return zero on success or non-zero if they were +unable to find a match in the +.Pa /etc/eui64 +database. +.Sh NOTES +The user must insure that the hostname strings passed to the +.\" .Fn eui64_line , +.Fn eui64_ntohost +and +.Fn eui64_hostton +functions are large enough to contain the returned hostnames. +.Sh NIS INTERACTION +If the +.Pa /etc/eui64 +contains a line with a single +.Ql + +in it, the +.Fn eui64_ntohost +and +.Fn eui64_hostton +functions will attempt to consult the NIS +.Pa eui64.byname +and +.Pa eui64.byid +maps in addition to the data in the +.Pa /etc/eui64 +file. +.Sh SEE ALSO +.Xr firewire 4 , +.Xr eui64 5 , +.Xr yp 8 +.Sh HISTORY +These functions first appears in +.Fx 5.3 . +They are derived from the +.Xr ethers 3 +family of functions. +.Sh BUGS +The +.Fn eui64_aton +and +.Fn eui64_ntoa +functions returns values that are stored in static memory areas +which may be overwritten the next time they are called. diff --git a/lib/libc/net/eui64.c b/lib/libc/net/eui64.c new file mode 100644 index 0000000..5085167 --- /dev/null +++ b/lib/libc/net/eui64.c @@ -0,0 +1,311 @@ +/* + * Copyright 2004 The Aerospace Corporation. 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. The name of The Aerospace Corporation may not be used to endorse or + * promote products derived from this software. + * + * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "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 AEROSPACE CORPORATION 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. + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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. + * + * EUI-64 conversion and lookup routines + * + * + * Converted from ether_addr.c rev + * FreeBSD: src/lib/libc/net/eui64.c,v 1.15 2002/04/08 07:51:10 ru Exp + * by Brooks Davis + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Center for Telecommunications Research + * Columbia University, New York City + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <paths.h> +#include <sys/types.h> +#include <sys/eui64.h> +#include <string.h> +#include <stdlib.h> +#include <sys/param.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif + +#ifndef _PATH_EUI64 +#define _PATH_EUI64 "/etc/eui64" +#endif + +static int eui64_line(const char *l, struct eui64 *e, char *hostname, + size_t len); + +/* + * Parse a string of text containing an EUI-64 and hostname + * and separate it into its component parts. + */ +static int +eui64_line(const char *l, struct eui64 *e, char *hostname, size_t len) +{ + char *line, *linehead, *cur; + + linehead = strdup(l); + if (linehead == NULL) + return (-1); + line = linehead; + + /* Find and parse the EUI64 */ + while ((cur = strsep(&line, " \t\r\n")) != NULL) { + if (*cur != '\0') { + if (eui64_aton(cur, e) == 0) + break; + else + goto bad; + } + } + + /* Find the hostname */ + while ((cur = strsep(&line, " \t\r\n")) != NULL) { + if (*cur != '\0') { + if (strlcpy(hostname, cur, len) <= len) + break; + else + goto bad; + } + } + + /* Make sure what remains is either whitespace or a comment */ + while ((cur = strsep(&line, " \t\r\n")) != NULL) { + if (*cur == '#') + break; + if (*cur != '\0') + goto bad; + } + + return (0); + +bad: + free(linehead); + return (-1); +} + +/* + * Convert an ASCII representation of an EUI-64 to binary form. + */ +int +eui64_aton(const char *a, struct eui64 *e) +{ + int i; + unsigned int o0, o1, o2, o3, o4, o5, o6, o7; + + /* canonical form */ + i = sscanf(a, "%x-%x-%x-%x-%x-%x-%x-%x", + &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); + if (i == EUI64_LEN) + goto good; + /* ethernet form */ + i = sscanf(a, "%x:%x:%x:%x:%x:%x:%x:%x", + &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); + if (i == EUI64_LEN) + goto good; + /* classic fwcontrol/dconschat form */ + i = sscanf(a, "0x%2x%2x%2x%2x%2x%2x%2x%2x", + &o0, &o1, &o2, &o3, &o4, &o5, &o6, &o7); + if (i == EUI64_LEN) + goto good; + /* MAC format (-) */ + i = sscanf(a, "%x-%x-%x-%x-%x-%x", + &o0, &o1, &o2, &o5, &o6, &o7); + if (i == 6) { + o3 = 0xff; + o4 = 0xfe; + goto good; + } + /* MAC format (:) */ + i = sscanf(a, "%x:%x:%x:%x:%x:%x", + &o0, &o1, &o2, &o5, &o6, &o7); + if (i == 6) { + o3 = 0xff; + o4 = 0xfe; + goto good; + } + + return (-1); + +good: + e->octet[0]=o0; + e->octet[1]=o1; + e->octet[2]=o2; + e->octet[3]=o3; + e->octet[4]=o4; + e->octet[5]=o5; + e->octet[6]=o6; + e->octet[7]=o7; + + return (0); +} + +/* + * Convert a binary representation of an EUI-64 to an ASCII string. + */ +int +eui64_ntoa(const struct eui64 *id, char *a, size_t len) +{ + int i; + + i = snprintf(a, len, "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x", + id->octet[0], id->octet[1], id->octet[2], id->octet[3], + id->octet[4], id->octet[5], id->octet[6], id->octet[7]); + if (i < 23 || i >= len) + return (-1); + return (0); +} + +/* + * Map an EUI-64 to a hostname. Use either /etc/eui64 or NIS/YP. + */ +int +eui64_ntohost(char *hostname, size_t len, const struct eui64 *id) +{ + FILE *fp; + char buf[BUFSIZ + 2]; + struct eui64 local_eui64; + char local_host[MAXHOSTNAMELEN]; +#ifdef YP + char *result; + int resultlen; + char eui64_a[24]; + char *yp_domain; +#endif + if ((fp = fopen(_PATH_EUI64, "r")) == NULL) + return (1); + + while (fgets(buf,BUFSIZ,fp)) { + if (buf[0] == '#') + continue; +#ifdef YP + if (buf[0] == '+') { + if (yp_get_default_domain(&yp_domain)) + continue; + eui64_ntoa(id, eui64_a, sizeof(eui64_a)); + if (yp_match(yp_domain, "eui64.byid", eui64_a, + strlen(eui64_a), &result, &resultlen)) { + continue; + } + strncpy(buf, result, resultlen); + buf[resultlen] = '\0'; + free(result); + } +#endif + if (eui64_line(buf, &local_eui64, local_host, + sizeof(local_host)) == 0) { + if (bcmp(&local_eui64.octet[0], + &id->octet[0], EUI64_LEN) == 0) { + /* We have a match */ + strcpy(hostname, local_host); + fclose(fp); + return(0); + } + } + } + fclose(fp); + return (1); +} + +/* + * Map a hostname to an EUI-64 using /etc/eui64 or NIS/YP. + */ +int +eui64_hostton(const char *hostname, struct eui64 *id) +{ + FILE *fp; + char buf[BUFSIZ + 2]; + struct eui64 local_eui64; + char local_host[MAXHOSTNAMELEN]; +#ifdef YP + char *result; + int resultlen; + char *yp_domain; +#endif + if ((fp = fopen(_PATH_EUI64, "r")) == NULL) + return (1); + + while (fgets(buf,BUFSIZ,fp)) { + if (buf[0] == '#') + continue; +#ifdef YP + if (buf[0] == '+') { + if (yp_get_default_domain(&yp_domain)) + continue; + if (yp_match(yp_domain, "eui64.byname", hostname, + strlen(hostname), &result, &resultlen)) { + continue; + } + strncpy(buf, result, resultlen); + buf[resultlen] = '\0'; + free(result); + } +#endif + if (eui64_line(buf, &local_eui64, local_host, + sizeof(local_host)) == 0) { + if (strcmp(hostname, local_host) == 0) { + /* We have a match */ + bcopy(&local_eui64, id, sizeof(struct eui64)); + fclose(fp); + return(0); + } + } + } + fclose(fp); + return (1); +} diff --git a/lib/libc/net/gai_strerror.3 b/lib/libc/net/gai_strerror.3 new file mode 100644 index 0000000..7c55030 --- /dev/null +++ b/lib/libc/net/gai_strerror.3 @@ -0,0 +1,90 @@ +.\" $KAME: gai_strerror.3,v 1.1 2005/01/05 03:04:47 itojun Exp $ +.\" $OpenBSD: gai_strerror.3,v 1.4 2004/12/20 23:04:53 millert Exp $ +.\" +.\" Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2000, 2001 Internet Software Consortium. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +.\" PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 20, 2004 +.Dt GAI_STRERROR 3 +.Os +.Sh NAME +.Nm gai_strerror +.Nd get error message string from EAI_xxx error code +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netdb.h +.Ft "const char *" +.Fn gai_strerror "int ecode" +.Sh DESCRIPTION +The +.Fn gai_strerror +function returns an error message string corresponding to the error code +returned by +.Xr getaddrinfo 3 +or +.Xr getnameinfo 3 . +.Pp +The following error codes and their meaning are defined in +.In netdb.h : +.Pp +.Bl -tag -width ".Dv EAI_BADFLAGS" -offset indent -compact +.It Dv EAI_AGAIN +temporary failure in name resolution +.It Dv EAI_BADFLAGS +invalid value for +.Fa ai_flags +.It Dv EAI_BADHINTS +invalid value for +.Fa hints +.It Dv EAI_FAIL +non-recoverable failure in name resolution +.It Dv EAI_FAMILY +.Fa ai_family +not supported +.It Dv EAI_MEMORY +memory allocation failure +.It Dv EAI_NONAME +.Fa hostname +or +.Fa servname +not provided, or not known +.It Dv EAI_PROTOCOL +resolved protocol is unknown +.It Dv EAI_SERVICE +.Fa servname +not supported for +.Fa ai_socktype +.It Dv EAI_SOCKTYPE +.Fa ai_socktype +not supported +.It Dv EAI_SYSTEM +system error returned in +.Va errno +.El +.Sh RETURN VALUES +The +.Fn gai_strerror +function +returns a pointer to the error message string corresponding to +.Fa ecode . +If +.Fa ecode +is out of range, an implementation-specific error message string is returned. +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr getnameinfo 3 diff --git a/lib/libc/net/gai_strerror.c b/lib/libc/net/gai_strerror.c new file mode 100644 index 0000000..bd351ab --- /dev/null +++ b/lib/libc/net/gai_strerror.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <netdb.h> + +/* Entries EAI_ADDRFAMILY (1) and EAI_NODATA (7) are obsoleted, but left */ +/* for backward compatibility with userland code prior to 2553bis-02 */ +static const char *ai_errlist[] = { + "Success", /* 0 */ + "Address family for hostname not supported", /* 1 */ + "Temporary failure in name resolution", /* EAI_AGAIN */ + "Invalid value for ai_flags", /* EAI_BADFLAGS */ + "Non-recoverable failure in name resolution", /* EAI_FAIL */ + "ai_family not supported", /* EAI_FAMILY */ + "Memory allocation failure", /* EAI_MEMORY */ + "No address associated with hostname", /* 7 */ + "hostname nor servname provided, or not known", /* EAI_NONAME */ + "servname not supported for ai_socktype", /* EAI_SERVICE */ + "ai_socktype not supported", /* EAI_SOCKTYPE */ + "System error returned in errno", /* EAI_SYSTEM */ + "Invalid value for hints", /* EAI_BADHINTS */ + "Resolved protocol is unknown" /* EAI_PROTOCOL */ +}; + +const char * +gai_strerror(int ecode) +{ + if (ecode >= 0 && ecode < EAI_MAX) + return ai_errlist[ecode]; + return "Unknown error"; +} diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3 new file mode 100644 index 0000000..9c5a8b6 --- /dev/null +++ b/lib/libc/net/getaddrinfo.3 @@ -0,0 +1,434 @@ +.\" $KAME: getaddrinfo.3,v 1.36 2005/01/05 03:23:05 itojun Exp $ +.\" $OpenBSD: getaddrinfo.3,v 1.35 2004/12/21 03:40:31 jaredy Exp $ +.\" +.\" Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2000, 2001 Internet Software Consortium. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +.\" PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 20, 2004 +.Dt GETADDRINFO 3 +.Os +.Sh NAME +.Nm getaddrinfo , +.Nm freeaddrinfo +.Nd socket address structure to host and service name +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <sys/socket.h> +.Fd #include <netdb.h> +.Ft int +.Fo getaddrinfo +.Fa "const char *hostname" "const char *servname" +.Fa "const struct addrinfo *hints" "struct addrinfo **res" +.Fc +.Ft void +.Fn freeaddrinfo "struct addrinfo *ai" +.Sh DESCRIPTION +The +.Fn getaddrinfo +function is used to get a list of +.Tn IP +addresses and port numbers for host +.Fa hostname +and service +.Fa servname . +It is a replacement for and provides more flexibility than the +.Xr gethostbyname 3 +and +.Xr getservbyname 3 +functions. +.Pp +The +.Fa hostname +and +.Fa servname +arguments are either pointers to NUL-terminated strings or the null pointer. +An acceptable value for +.Fa hostname +is either a valid host name or a numeric host address string consisting +of a dotted decimal IPv4 address or an IPv6 address. +The +.Fa servname +is either a decimal port number or a service name listed in +.Xr services 5 . +At least one of +.Fa hostname +and +.Fa servname +must be non-null. +.Pp +.Fa hints +is an optional pointer to a +.Li struct addrinfo , +as defined by +.Aq Pa netdb.h : +.Bd -literal +struct addrinfo { + int ai_flags; /* input flags */ + int ai_family; /* protocol family for socket */ + int ai_socktype; /* socket type */ + int ai_protocol; /* protocol for socket */ + socklen_t ai_addrlen; /* length of socket-address */ + struct sockaddr *ai_addr; /* socket-address for socket */ + char *ai_canonname; /* canonical name for service location */ + struct addrinfo *ai_next; /* pointer to next in list */ +}; +.Ed +.Pp +This structure can be used to provide hints concerning the type of socket +that the caller supports or wishes to use. +The caller can supply the following structure elements in +.Fa hints : +.Bl -tag -width "ai_socktypeXX" +.It Fa ai_family +The protocol family that should be used. +When +.Fa ai_family +is set to +.Dv PF_UNSPEC , +it means the caller will accept any protocol family supported by the +operating system. +.It Fa ai_socktype +Denotes the type of socket that is wanted: +.Dv SOCK_STREAM , +.Dv SOCK_DGRAM , +or +.Dv SOCK_RAW . +When +.Fa ai_socktype +is zero the caller will accept any socket type. +.It Fa ai_protocol +Indicates which transport protocol is desired, +.Dv IPPROTO_UDP +or +.Dv IPPROTO_TCP . +If +.Fa ai_protocol +is zero the caller will accept any protocol. +.It Fa ai_flags +.Fa ai_flags +is formed by +.Tn OR Ns 'ing +the following values: +.Bl -tag -width "AI_CANONNAMEXX" +.It Dv AI_CANONNAME +If the +.Dv AI_CANONNAME +bit is set, a successful call to +.Fn getaddrinfo +will return a NUL-terminated string containing the canonical name +of the specified hostname in the +.Fa ai_canonname +element of the first +.Li addrinfo +structure returned. +.It Dv AI_NUMERICHOST +If the +.Dv AI_NUMERICHOST +bit is set, it indicates that +.Fa hostname +should be treated as a numeric string defining an IPv4 or IPv6 address +and no name resolution should be attempted. +.It Dv AI_PASSIVE +If the +.Dv AI_PASSIVE +bit is set it indicates that the returned socket address structure +is intended for use in a call to +.Xr bind 2 . +In this case, if the +.Fa hostname +argument is the 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, the returned socket address structure will be ready +for use in a call to +.Xr connect 2 +for a connection-oriented protocol or +.Xr connect 2 , +.Xr sendto 2 , +or +.Xr sendmsg 2 +if a connectionless protocol was chosen. +The +.Tn IP +address portion of the socket address structure will be set to the +loopback address if +.Fa hostname +is the null pointer and +.Dv AI_PASSIVE +is not set. +.El +.El +.Pp +All other elements of the +.Li addrinfo +structure passed via +.Fa hints +must be zero or the null pointer. +.Pp +If +.Fa hints +is the null pointer, +.Fn getaddrinfo +behaves as if the caller provided a +.Li struct addrinfo +with +.Fa ai_family +set to +.Dv PF_UNSPEC +and all other elements set to zero or +.Dv NULL . +.Pp +After a successful call to +.Fn getaddrinfo , +.Fa *res +is a pointer to a linked list of one or more +.Li addrinfo +structures. +The list can be traversed by following the +.Fa ai_next +pointer in each +.Li addrinfo +structure until a null pointer is encountered. +The three members +.Fa ai_family, +.Fa ai_socktype, +and +.Fa ai_protocol +in each returned +.Li addrinfo +structure are suitable for a call to +.Xr socket 2 . +For each +.Li addrinfo +structure in the list, the +.Fa ai_addr +member points to a filled-in socket address structure of length +.Fa ai_addrlen . +.Pp +This implementation of +.Fn getaddrinfo +allows numeric IPv6 address notation with scope identifier, +as documented in chapter 11 of draft-ietf-ipv6-scoping-arch-02.txt. +By appending the percent character and scope identifier to addresses, +one can fill the +.Li sin6_scope_id +field for addresses. +This would make management of scoped addresses easier +and allows cut-and-paste input of scoped addresses. +.Pp +At this moment the code supports only link-local addresses with the format. +The scope identifier is hardcoded to the name of the hardware interface +associated +with the link +.Po +such as +.Li ne0 +.Pc . +An example is +.Dq Li fe80::1%ne0 , +which means +.Do +.Li fe80::1 +on the link associated with the +.Li ne0 +interface +.Dc . +.Pp +The current implementation assumes a one-to-one relationship between +the interface and link, which is not necessarily true from the specification. +.Pp +All of the information returned by +.Fn getaddrinfo +is dynamically allocated: the +.Li addrinfo +structures themselves as well as the socket address structures and +the canonical host name strings included in the +.Li addrinfo +structures. +.Pp +Memory allocated for the dynamically allocated structures created by +a successful call to +.Fn getaddrinfo +is released by the +.Fn freeaddrinfo +function. +The +.Fa ai +pointer should be a +.Li addrinfo +structure created by a call to +.Fn getaddrinfo . +.Sh RETURN VALUES +.Fn getaddrinfo +returns zero on success or one of the error codes listed in +.Xr gai_strerror 3 +if an error occurs. +.Sh EXAMPLES +The following code tries to connect to +.Dq Li www.kame.net +service +.Dq Li http +via a stream socket. +It loops through all the addresses available, regardless of address family. +If the destination resolves to an IPv4 address, it will use an +.Dv AF_INET +socket. +Similarly, if it resolves to IPv6, an +.Dv AF_INET6 +socket is used. +Observe that there is no hardcoded reference to a particular address family. +The code works even if +.Fn getaddrinfo +returns addresses that are not IPv4/v6. +.Bd -literal -offset indent +struct addrinfo hints, *res, *res0; +int error; +int s; +const char *cause = NULL; + +memset(&hints, 0, sizeof(hints)); +hints.ai_family = PF_UNSPEC; +hints.ai_socktype = SOCK_STREAM; +error = getaddrinfo("www.kame.net", "http", &hints, &res0); +if (error) { + errx(1, "%s", gai_strerror(error)); + /*NOTREACHED*/ +} +s = -1; +for (res = res0; res; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (s < 0) { + cause = "socket"; + continue; + } + + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + cause = "connect"; + close(s); + s = -1; + continue; + } + + break; /* okay we got one */ +} +if (s < 0) { + err(1, "%s", cause); + /*NOTREACHED*/ +} +freeaddrinfo(res0); +.Ed +.Pp +The following example tries to open a wildcard listening socket onto service +.Dq Li http , +for all the address families available. +.Bd -literal -offset indent +struct addrinfo hints, *res, *res0; +int error; +int s[MAXSOCK]; +int nsock; +const char *cause = NULL; + +memset(&hints, 0, sizeof(hints)); +hints.ai_family = PF_UNSPEC; +hints.ai_socktype = SOCK_STREAM; +hints.ai_flags = AI_PASSIVE; +error = getaddrinfo(NULL, "http", &hints, &res0); +if (error) { + errx(1, "%s", gai_strerror(error)); + /*NOTREACHED*/ +} +nsock = 0; +for (res = res0; res && nsock < MAXSOCK; res = res->ai_next) { + s[nsock] = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (s[nsock] < 0) { + cause = "socket"; + continue; + } + + if (bind(s[nsock], res->ai_addr, res->ai_addrlen) < 0) { + cause = "bind"; + close(s[nsock]); + continue; + } + (void) listen(s[nsock], 5); + + nsock++; +} +if (nsock == 0) { + err(1, "%s", cause); + /*NOTREACHED*/ +} +freeaddrinfo(res0); +.Ed +.Sh SEE ALSO +.Xr bind 2 , +.Xr connect 2 , +.Xr send 2 , +.Xr socket 2 , +.Xr gai_strerror 3 , +.Xr gethostbyname 3 , +.Xr getnameinfo 3 , +.Xr getservbyname 3 , +.Xr resolver 3 , +.Xr hosts 5 , +.Xr resolv.conf 5 , +.Xr services 5 , +.Xr hostname 7 , +.Xr named 8 +.Rs +.%A R. Gilligan +.%A S. Thomson +.%A J. Bound +.%A J. McCann +.%A W. Stevens +.%T Basic Socket Interface Extensions for IPv6 +.%R RFC 3493 +.%D February 2003 +.Re +.Rs +.%A S. Deering +.%A B. Haberman +.%A T. Jinmei +.%A E. Nordmark +.%A B. Zill +.%T "IPv6 Scoped Address Architecture" +.%R internet draft +.%N draft-ietf-ipv6-scoping-arch-02.txt +.%O work in progress material +.Re +.Rs +.%A Craig Metz +.%T Protocol Independence Using the Sockets API +.%B "Proceedings of the freenix track: 2000 USENIX annual technical conference" +.%D June 2000 +.Re +.Sh STANDARDS +The +.Fn getaddrinfo +function is defined by the +.St -p1003.1g-2000 +draft specification and documented in +.Dv "RFC 3493" , +.Dq Basic Socket Interface Extensions for IPv6 . diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c new file mode 100644 index 0000000..b422c4b --- /dev/null +++ b/lib/libc/net/getaddrinfo.c @@ -0,0 +1,2772 @@ +/* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. + * + * Issues to be discussed: + * - Thread safe-ness must be checked. + * - Return values. There are nonstandard return values defined and used + * in the source code. This is because RFC2553 is silent about which error + * code must be returned for which situation. + * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is + * invalid. current code - SEGV on freeaddrinfo(NULL) + * + * Note: + * - The code filters out AFs that are not supported by the kernel, + * when globbing NULL hostname (to loopback, or wildcard). Is it the right + * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG + * in ai_flags? + * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. + * (1) what should we do against numeric hostname (2) what should we do + * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? + * non-loopback address configured? global address configured? + * + * OS specific notes for netbsd/openbsd/freebsd4/bsdi4: + * - To avoid search order issue, we have a big amount of code duplicate + * from gethnamaddr.c and some other places. The issues that there's no + * lower layer function to lookup "IPv4 or IPv6" record. Calling + * gethostbyname2 from getaddrinfo will end up in wrong search order, as + * presented above. + * + * OS specific notes for freebsd4: + * - FreeBSD supported $GAI. The code does not. + * - FreeBSD allowed classful IPv4 numeric (127.1), the code does not. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <sys/queue.h> +#ifdef INET6 +#include <net/if_var.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <netinet6/in6_var.h> /* XXX */ +#endif +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#include <netdb.h> +#include <resolv.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#include "res_config.h" + +#ifdef DEBUG +#include <syslog.h> +#endif + +#include <stdarg.h> +#include <nsswitch.h> +#include "un-namespace.h" +#include "libc_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif + +#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 in_loopback[] = { 127, 0, 0, 1 }; +#ifdef INET6 +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; +#endif + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; +#ifdef INET6 + struct in6_addrpolicy pc_policy; +#endif +}; +TAILQ_HEAD(policyhead, policyqueue); + +static const struct afd { + int a_af; + int a_addrlen; + socklen_t a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { +#ifdef INET6 +#define N_INET6 0 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, +#define N_INET 1 +#else +#define N_INET 0 +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, +}; + +struct explore { + int e_af; + int e_socktype; + int e_protocol; + const char *e_protostr; + int e_wild; +#define WILD_AF(ex) ((ex)->e_wild & 0x01) +#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) +#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) +}; + +static const struct explore explore[] = { +#if 0 + { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, +#endif +#ifdef INET6 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, +#endif + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, + { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, +}; + +#ifdef INET6 +#define PTON_MAX 16 +#else +#define PTON_MAX 4 +#endif + +#define AIO_SRCFLAG_DEPRECATED 0x1 + +struct ai_order { + union { + struct sockaddr_storage aiou_ss; + struct sockaddr aiou_sa; + } aio_src_un; +#define aio_srcsa aio_src_un.aiou_sa + u_int32_t aio_srcflag; + int aio_srcscope; + int aio_dstscope; + struct policyqueue *aio_srcpolicy; + struct policyqueue *aio_dstpolicy; + struct addrinfo *aio_ai; + int aio_matchlen; +}; + +static const ns_src default_dns_files[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, + { 0 } +}; + +struct res_target { + struct res_target *next; + const char *name; /* domain name */ + int qclass, qtype; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ + int n; /* result length */ +}; + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +static int str2number(const char *); +static int explore_null(const struct addrinfo *, + const char *, struct addrinfo **); +static int explore_numeric(const struct addrinfo *, const char *, + const char *, struct addrinfo **, const char *); +static int explore_numeric_scope(const struct addrinfo *, const char *, + const char *, struct addrinfo **); +static int get_canonname(const struct addrinfo *, + struct addrinfo *, const char *); +static struct addrinfo *get_ai(const struct addrinfo *, + const struct afd *, const char *); +static int get_portmatch(const struct addrinfo *, const char *); +static int get_port(struct addrinfo *, const char *, int); +static const struct afd *find_afd(int); +static int addrconfig(struct addrinfo *); +static void set_source(struct ai_order *, struct policyhead *); +static int comp_dst(const void *, const void *); +#ifdef INET6 +static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); +#endif +static int gai_addr2scopetype(struct sockaddr *); + +static int explore_fqdn(const struct addrinfo *, const char *, + const char *, struct addrinfo **); + +static int reorder(struct addrinfo *); +static int get_addrselectpolicy(struct policyhead *); +static void free_addrselectpolicy(struct policyhead *); +static struct policyqueue *match_addrselectpolicy(struct sockaddr *, + struct policyhead *); +static int matchlen(struct sockaddr *, struct sockaddr *); + +static struct addrinfo *getanswer(const querybuf *, int, const char *, int, + const struct addrinfo *, res_state); +#if defined(RESOLVSORT) +static int addr4sort(struct addrinfo *, res_state); +#endif +static int _dns_getaddrinfo(void *, void *, va_list); +static void _sethtent(FILE **); +static void _endhtent(FILE **); +static struct addrinfo *_gethtent(FILE **, const char *, + const struct addrinfo *); +static int _files_getaddrinfo(void *, void *, va_list); +#ifdef YP +static struct addrinfo *_yphostent(char *, const struct addrinfo *); +static int _yp_getaddrinfo(void *, void *, va_list); +#endif +#ifdef NS_CACHING +static int addrinfo_id_func(char *, size_t *, va_list, void *); +static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); +static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int res_queryN(const char *, struct res_target *, res_state); +static int res_searchN(const char *, struct res_target *, res_state); +static int res_querydomainN(const char *, const char *, + struct res_target *, res_state); + +/* XXX macros that make external reference is BAD. */ + +#define GET_AI(ai, afd, addr) \ +do { \ + /* external reference: pai, error, and label free */ \ + (ai) = get_ai(pai, (afd), (addr)); \ + if ((ai) == NULL) { \ + error = EAI_MEMORY; \ + goto free; \ + } \ +} while (/*CONSTCOND*/0) + +#define GET_PORT(ai, serv) \ +do { \ + /* external reference: error and label free */ \ + error = get_port((ai), (serv), 0); \ + if (error != 0) \ + goto free; \ +} while (/*CONSTCOND*/0) + +#define GET_CANONNAME(ai, str) \ +do { \ + /* external reference: pai, error and label free */ \ + error = get_canonname(pai, (ai), (str)); \ + if (error != 0) \ + goto free; \ +} while (/*CONSTCOND*/0) + +#define ERR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + goto bad; \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) + +#define MATCH_FAMILY(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) +#define MATCH(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) + +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + ai = next; + } while (ai); +} + +static int +str2number(const char *p) +{ + char *ep; + unsigned long v; + + if (*p == '\0') + return -1; + ep = NULL; + errno = 0; + v = strtoul(p, &ep, 10); + if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) + return v; + else + return -1; +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai; + struct addrinfo ai0; + struct addrinfo *pai; + const struct explore *ex; + int numeric = 0; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = ANY; + pai->ai_protocol = ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef INET6 + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + + /* + * if both socktype/protocol are specified, check if they + * are meaningful combination. + */ + if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { + for (ex = explore; ex->e_af >= 0; ex++) { + if (pai->ai_family != ex->e_af) + continue; + if (ex->e_socktype == ANY) + continue; + if (ex->e_protocol == ANY) + continue; + if (pai->ai_socktype == ex->e_socktype && + pai->ai_protocol != ex->e_protocol) { + ERR(EAI_BADHINTS); + } + } + } + } + + /* + * post-2553: AI_ALL and AI_V4MAPPED are effective only against + * AF_INET6 query. They need to be ignored if specified in other + * occassions. + */ + switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { + case AI_V4MAPPED: + case AI_ALL | AI_V4MAPPED: + if (pai->ai_family != AF_INET6) + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; + case AI_ALL: +#if 1 + /* illegal */ + ERR(EAI_BADFLAGS); +#else + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); +#endif + break; + } + + /* + * check for special cases. (1) numeric servname is disallowed if + * socktype/protocol are left unspecified. (2) servname is disallowed + * for raw and other inet{,6} sockets. + */ + if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) +#ifdef PF_INET6 + || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) +#endif + ) { + ai0 = *pai; /* backup *pai */ + + if (pai->ai_family == PF_UNSPEC) { +#ifdef PF_INET6 + pai->ai_family = PF_INET6; +#else + pai->ai_family = PF_INET; +#endif + } + error = get_portmatch(pai, servname); + if (error) + ERR(error); + + *pai = ai0; + } + + ai0 = *pai; + + /* NULL hostname, or numeric hostname */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + /* PF_UNSPEC entries are prepared for DNS queries only */ + if (ex->e_af == PF_UNSPEC) + continue; + + 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, 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) { + numeric = 1; + goto good; + } + + if (hostname == NULL) + ERR(EAI_NONAME); /* used to be EAI_NODATA */ + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) + ERR(EAI_FAIL); + + /* + * hostname as alphabetical name. + * we would like to prefer AF_INET6 than AF_INET, so we'll make a + * outer loop by AFs. + */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + /* require exact match for family field */ + if (pai->ai_family != ex->e_af) + continue; + + if (!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_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 inhibit errors if we have the result */ + if (sentinel.ai_next) + error = 0; + +good: + /* + * ensure we return either: + * - error == 0, non-NULL *res + * - error != 0, NULL *res + */ + if (error == 0) { + if (sentinel.ai_next) { + /* + * If the returned entry is for an active connection, + * and the given name is not numeric, reorder the + * list, so that the application would try the list + * in the most efficient order. + */ + if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { + if (!numeric) + (void)reorder(&sentinel); + } + *res = sentinel.ai_next; + return SUCCESS; + } else + error = EAI_FAIL; + } +free: +bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + *res = NULL; + return error; +} + +static int +reorder(struct addrinfo *sentinel) +{ + struct addrinfo *ai, **aip; + struct ai_order *aio; + int i, n; + struct policyhead policyhead; + + /* count the number of addrinfo elements for sorting. */ + for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) + ; + + /* + * If the number is small enough, we can skip the reordering process. + */ + if (n <= 1) + return(n); + + /* allocate a temporary array for sort and initialization of it. */ + if ((aio = malloc(sizeof(*aio) * n)) == NULL) + return(n); /* give up reordering */ + memset(aio, 0, sizeof(*aio) * n); + + /* retrieve address selection policy from the kernel */ + TAILQ_INIT(&policyhead); + if (!get_addrselectpolicy(&policyhead)) { + /* no policy is installed into kernel, we don't sort. */ + free(aio); + return (n); + } + + for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { + aio[i].aio_ai = ai; + aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); + aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, + &policyhead); + set_source(&aio[i], &policyhead); + } + + /* perform sorting. */ + qsort(aio, n, sizeof(*aio), comp_dst); + + /* reorder the addrinfo chain. */ + for (i = 0, aip = &sentinel->ai_next; i < n; i++) { + *aip = aio[i].aio_ai; + aip = &aio[i].aio_ai->ai_next; + } + *aip = NULL; + + /* cleanup and return */ + free(aio); + free_addrselectpolicy(&policyhead); + return(n); +} + +static int +get_addrselectpolicy(struct policyhead *head) +{ +#ifdef INET6 + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) + return (0); + if ((buf = malloc(l)) == NULL) + return (0); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { + free(buf); + return (0); + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + struct policyqueue *new; + + if ((new = malloc(sizeof(*new))) == NULL) { + free_addrselectpolicy(head); /* make the list empty */ + break; + } + new->pc_policy = *pol; + TAILQ_INSERT_TAIL(head, new, pc_entry); + } + + free(buf); + return (1); +#else + return (0); +#endif +} + +static void +free_addrselectpolicy(struct policyhead *head) +{ + struct policyqueue *ent, *nent; + + for (ent = TAILQ_FIRST(head); ent; ent = nent) { + nent = TAILQ_NEXT(ent, pc_entry); + TAILQ_REMOVE(head, ent, pc_entry); + free(ent); + } +} + +static struct policyqueue * +match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) +{ +#ifdef INET6 + struct policyqueue *ent, *bestent = NULL; + struct in6_addrpolicy *pol; + int matchlen, bestmatchlen = -1; + u_char *mp, *ep, *k, *p, m; + struct sockaddr_in6 key; + + switch(addr->sa_family) { + case AF_INET6: + key = *(struct sockaddr_in6 *)addr; + break; + case AF_INET: + /* convert the address into IPv4-mapped IPv6 address. */ + memset(&key, 0, sizeof(key)); + key.sin6_family = AF_INET6; + key.sin6_len = sizeof(key); + key.sin6_addr.s6_addr[10] = 0xff; + key.sin6_addr.s6_addr[11] = 0xff; + memcpy(&key.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)addr)->sin_addr, 4); + break; + default: + return(NULL); + } + + for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { + pol = &ent->pc_policy; + matchlen = 0; + + mp = (u_char *)&pol->addrmask.sin6_addr; + ep = mp + 16; /* XXX: scope field? */ + k = (u_char *)&key.sin6_addr; + p = (u_char *)&pol->addr.sin6_addr; + for (; mp < ep && *mp; mp++, k++, p++) { + m = *mp; + if ((*k & m) != *p) + goto next; /* not match */ + if (m == 0xff) /* short cut for a typical case */ + matchlen += 8; + else { + while (m >= 0x80) { + matchlen++; + m <<= 1; + } + } + } + + /* matched. check if this is better than the current best. */ + if (matchlen > bestmatchlen) { + bestent = ent; + bestmatchlen = matchlen; + } + + next: + continue; + } + + return(bestent); +#else + return(NULL); +#endif + +} + +static void +set_source(struct ai_order *aio, struct policyhead *ph) +{ + struct addrinfo ai = *aio->aio_ai; + struct sockaddr_storage ss; + socklen_t srclen; + int s; + + /* set unspec ("no source is available"), just in case */ + aio->aio_srcsa.sa_family = AF_UNSPEC; + aio->aio_srcscope = -1; + + switch(ai.ai_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + break; + default: /* ignore unsupported AFs explicitly */ + return; + } + + /* XXX: make a dummy addrinfo to call connect() */ + ai.ai_socktype = SOCK_DGRAM; + ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ + ai.ai_next = NULL; + memset(&ss, 0, sizeof(ss)); + memcpy(&ss, ai.ai_addr, ai.ai_addrlen); + ai.ai_addr = (struct sockaddr *)&ss; + get_port(&ai, "1", 0); + + /* open a socket to get the source address for the given dst */ + if ((s = _socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol)) < 0) + return; /* give up */ + if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) + goto cleanup; + srclen = ai.ai_addrlen; + if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { + aio->aio_srcsa.sa_family = AF_UNSPEC; + goto cleanup; + } + aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); + aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); + aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); +#ifdef INET6 + if (ai.ai_family == AF_INET6) { + struct in6_ifreq ifr6; + u_int32_t flags6; + + /* XXX: interface name should not be hardcoded */ + strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); + if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { + flags6 = ifr6.ifr_ifru.ifru_flags6; + if ((flags6 & IN6_IFF_DEPRECATED)) + aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; + } + } +#endif + + cleanup: + _close(s); + return; +} + +static int +matchlen(struct sockaddr *src, struct sockaddr *dst) +{ + int match = 0; + u_char *s, *d; + u_char *lim, r; + int addrlen; + + switch (src->sa_family) { +#ifdef INET6 + case AF_INET6: + s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; + d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; + addrlen = sizeof(struct in6_addr); + lim = s + addrlen; + break; +#endif + case AF_INET: + s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; + d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; + addrlen = sizeof(struct in_addr); + lim = s + addrlen; + break; + default: + return(0); + } + + while (s < lim) + if ((r = (*d++ ^ *s++)) != 0) { + while (r < addrlen * 8) { + match++; + r <<= 1; + } + break; + } else + match += 8; + return(match); +} + +static int +comp_dst(const void *arg1, const void *arg2) +{ + const struct ai_order *dst1 = arg1, *dst2 = arg2; + + /* + * Rule 1: Avoid unusable destinations. + * XXX: we currently do not consider if an appropriate route exists. + */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family == AF_UNSPEC) { + return(-1); + } + if (dst1->aio_srcsa.sa_family == AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + return(1); + } + + /* Rule 2: Prefer matching scope. */ + if (dst1->aio_dstscope == dst1->aio_srcscope && + dst2->aio_dstscope != dst2->aio_srcscope) { + return(-1); + } + if (dst1->aio_dstscope != dst1->aio_srcscope && + dst2->aio_dstscope == dst2->aio_srcscope) { + return(1); + } + + /* Rule 3: Avoid deprecated addresses. */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(-1); + } + if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(1); + } + } + + /* Rule 4: Prefer home addresses. */ + /* XXX: not implemented yet */ + + /* Rule 5: Prefer matching label. */ +#ifdef INET6 + if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && + dst1->aio_srcpolicy->pc_policy.label == + dst1->aio_dstpolicy->pc_policy.label && + (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || + dst2->aio_srcpolicy->pc_policy.label != + dst2->aio_dstpolicy->pc_policy.label)) { + return(-1); + } + if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && + dst2->aio_srcpolicy->pc_policy.label == + dst2->aio_dstpolicy->pc_policy.label && + (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || + dst1->aio_srcpolicy->pc_policy.label != + dst1->aio_dstpolicy->pc_policy.label)) { + return(1); + } +#endif + + /* Rule 6: Prefer higher precedence. */ +#ifdef INET6 + if (dst1->aio_dstpolicy && + (dst2->aio_dstpolicy == NULL || + dst1->aio_dstpolicy->pc_policy.preced > + dst2->aio_dstpolicy->pc_policy.preced)) { + return(-1); + } + if (dst2->aio_dstpolicy && + (dst1->aio_dstpolicy == NULL || + dst2->aio_dstpolicy->pc_policy.preced > + dst1->aio_dstpolicy->pc_policy.preced)) { + return(1); + } +#endif + + /* Rule 7: Prefer native transport. */ + /* XXX: not implemented yet */ + + /* Rule 8: Prefer smaller scope. */ + if (dst1->aio_dstscope >= 0 && + dst1->aio_dstscope < dst2->aio_dstscope) { + return(-1); + } + if (dst2->aio_dstscope >= 0 && + dst2->aio_dstscope < dst1->aio_dstscope) { + return(1); + } + + /* + * Rule 9: Use longest matching prefix. + * We compare the match length in a same AF only. + */ + if (dst1->aio_ai->ai_addr->sa_family == + dst2->aio_ai->ai_addr->sa_family) { + if (dst1->aio_matchlen > dst2->aio_matchlen) { + return(-1); + } + if (dst1->aio_matchlen < dst2->aio_matchlen) { + return(1); + } + } + + /* Rule 10: Otherwise, leave the order unchanged. */ + return(-1); +} + +/* + * Copy from scope.c. + * XXX: we should standardize the functions and link them as standard + * library. + */ +static int +gai_addr2scopetype(struct sockaddr *sa) +{ +#ifdef INET6 + struct sockaddr_in6 *sa6; +#endif + struct sockaddr_in *sa4; + + switch(sa->sa_family) { +#ifdef INET6 + case AF_INET6: + sa6 = (struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { + /* just use the scope field of the multicast address */ + return(sa6->sin6_addr.s6_addr[2] & 0x0f); + } + /* + * Unicast addresses: map scope type to corresponding scope + * value defined for multcast addresses. + * XXX: hardcoded scope type values are bad... + */ + if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) + return(1); /* node local scope */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) + return(2); /* link-local scope */ + if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) + return(5); /* site-local scope */ + return(14); /* global scope */ + break; +#endif + case AF_INET: + /* + * IPv4 pseudo scoping according to RFC 3484. + */ + sa4 = (struct sockaddr_in *)sa; + /* IPv4 autoconfiguration addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 169 && + ((u_char *)&sa4->sin_addr)[1] == 254) + return(2); + /* Private addresses have site-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 10 || + (((u_char *)&sa4->sin_addr)[0] == 172 && + (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || + (((u_char *)&sa4->sin_addr)[0] == 192 && + ((u_char *)&sa4->sin_addr)[1] == 168)) + return(14); /* XXX: It should be 5 unless NAT */ + /* Loopback addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 127) + return(2); + return(14); + break; + default: + errno = EAFNOSUPPORT; /* is this a good error? */ + return(-1); + } +} + +/* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(const struct addrinfo *pai, const char *servname, + struct addrinfo **res) +{ + int s; + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = _socket(pai->ai_family, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + _close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + GET_PORT(cur->ai_next, servname); + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + GET_PORT(cur->ai_next, servname); + } + cur = cur->ai_next; + + *res = sentinel.ai_next; + return 0; + +free: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * numeric hostname + */ +static int +explore_numeric(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res, const char *canonname) +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + switch (afd->a_af) { +#if 1 /*X/Open spec*/ + case AF_INET: + if (inet_aton(hostname, (struct in_addr *)pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname); + if ((pai->ai_flags & AI_CANONNAME)) { + /* + * Set the numeric address itself as + * the canonical name, based on a + * clarification in rfc3493. + */ + GET_CANONNAME(cur->ai_next, canonname); + } + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + break; +#endif + default: + if (inet_pton(afd->a_af, hostname, pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname); + if ((pai->ai_flags & AI_CANONNAME)) { + /* + * Set the numeric address itself as + * the canonical name, based on a + * clarification in rfc3493. + */ + GET_CANONNAME(cur->ai_next, canonname); + } + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /* XXX */ + } + break; + } + + *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(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res) +{ +#if !defined(SCOPE_DELIMITER) || !defined(INET6) + return explore_numeric(pai, hostname, servname, res, hostname); +#else + const struct afd *afd; + struct addrinfo *cur; + int error; + char *cp, *hostname2 = NULL, *scope, *addr; + struct sockaddr_in6 *sin6; + + /* + * 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, hostname); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res, hostname); + + /* + * Handle special case of <scoped_address><delimiter><scope id> + */ + hostname2 = strdup(hostname); + if (hostname2 == NULL) + return EAI_MEMORY; + /* terminate at the delimiter */ + hostname2[cp - hostname] = '\0'; + addr = hostname2; + scope = cp + 1; + + error = explore_numeric(pai, addr, servname, res, hostname); + if (error == 0) { + u_int32_t scopeid; + + for (cur = *res; cur; cur = cur->ai_next) { + if (cur->ai_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; + if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { + free(hostname2); + return(EAI_NONAME); /* XXX: is return OK? */ + } + sin6->sin6_scope_id = scopeid; + } + } + + free(hostname2); + + return error; +#endif +} + +static int +get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) +{ + if ((pai->ai_flags & AI_CANONNAME) != 0) { + ai->ai_canonname = strdup(str); + if (ai->ai_canonname == NULL) + return EAI_MEMORY; + } + return 0; +} + +static struct addrinfo * +get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) +{ + char *p; + struct addrinfo *ai; +#ifdef FAITH + struct in6_addr faith_prefix; + char *fp_str; + int translate = 0; +#endif + +#ifdef FAITH + /* + * Transfrom an IPv4 addr into a special IPv6 addr format for + * IPv6->IPv4 translation gateway. (only TCP is supported now) + * + * +-----------------------------------+------------+ + * | faith prefix part (12 bytes) | embedded | + * | | IPv4 addr part (4 bytes) + * +-----------------------------------+------------+ + * + * faith prefix part is specified as ascii IPv6 addr format + * in environmental variable GAI. + * For FAITH to work correctly, routing to faith prefix must be + * setup toward a machine where a FAITH daemon operates. + * Also, the machine must enable some mechanizm + * (e.g. faith interface hack) to divert those packet with + * faith prefixed destination addr to user-land FAITH daemon. + */ + fp_str = getenv("GAI"); + if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && + afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { + u_int32_t v4a; + u_int8_t v4a_top; + + memcpy(&v4a, addr, sizeof v4a); + v4a_top = v4a >> IN_CLASSA_NSHIFT; + if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && + v4a_top != 0 && v4a != IN_LOOPBACKNET) { + afd = &afdl[N_INET6]; + memcpy(&faith_prefix.s6_addr[12], addr, + sizeof(struct in_addr)); + translate = 1; + } + } +#endif + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + + (afd->a_socklen)); + if (ai == NULL) + return NULL; + + memcpy(ai, pai, sizeof(struct addrinfo)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memset(ai->ai_addr, 0, (size_t)afd->a_socklen); + ai->ai_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 *)(void *)(ai->ai_addr); +#ifdef FAITH + if (translate == 1) + memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen); + else +#endif + memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); + return ai; +} + +static int +get_portmatch(const struct addrinfo *ai, const char *servname) +{ + + /* get_port does not touch first argument when matchonly == 1. */ + /* LINTED const cast */ + return get_port((struct addrinfo *)ai, servname, 1); +} + +static int +get_port(struct addrinfo *ai, const char *servname, int matchonly) +{ + const char *proto; + struct servent *sp; + int port; + int allownumeric; + + if (servname == NULL) + return 0; + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + break; + default: + return 0; + } + + 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; + } + + port = str2number(servname); + if (port >= 0) { + if (!allownumeric) + return EAI_SERVICE; + if (port < 0 || port > 65535) + return EAI_SERVICE; + port = htons(port); + } else { + if (ai->ai_flags & AI_NUMERICSERV) + return EAI_NONAME; + switch (ai->ai_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 *)(void *) + ai->ai_addr)->sin_port = port; + break; +#ifdef INET6 + case AF_INET6: + ((struct sockaddr_in6 *)(void *) + ai->ai_addr)->sin6_port = port; + break; +#endif + } + } + + return 0; +} + +static const struct afd * +find_afd(int af) +{ + const struct afd *afd; + + if (af == PF_UNSPEC) + return NULL; + for (afd = afdl; afd->a_af; afd++) { + if (afd->a_af == af) + return afd; + } + return NULL; +} + +/* + * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend + * will take care of it. + * the semantics of AI_ADDRCONFIG is not defined well. we are not sure + * if the code is right or not. + * + * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with + * _dns_getaddrinfo. + */ +static int +addrconfig(struct addrinfo *pai) +{ + int s, af; + + /* + * TODO: + * Note that implementation dependent test for address + * configuration should be done everytime called + * (or apropriate interval), + * because addresses will be dynamically assigned or deleted. + */ + af = pai->ai_family; + if (af == AF_UNSPEC) { + if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + af = AF_INET; + else { + _close(s); + if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0) + af = AF_INET6; + else + _close(s); + } + } + if (af != AF_UNSPEC) { + if ((s = _socket(af, SOCK_DGRAM, 0)) < 0) + return 0; + _close(s); + } + pai->ai_family = af; + return 1; +} + +#ifdef INET6 +/* convert a string to a scope identifier. XXX: IPv6 specific */ +static int +ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) +{ + u_long lscopeid; + struct in6_addr *a6; + char *ep; + + a6 = &sin6->sin6_addr; + + /* empty scopeid portion is invalid */ + if (*scope == '\0') + return -1; + + if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { + /* + * We currently assume a one-to-one mapping between links + * and interfaces, so we simply use interface indices for + * like-local scopes. + */ + *scopeid = if_nametoindex(scope); + if (*scopeid == 0) + goto trynumeric; + return 0; + } + + /* still unclear about literal, allow numeric only - placeholder */ + if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) + goto trynumeric; + if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) + goto trynumeric; + else + goto trynumeric; /* global */ + + /* try to convert to a numeric id as a last resort */ + trynumeric: + errno = 0; + lscopeid = strtoul(scope, &ep, 10); + *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); + if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) + return 0; + else + return -1; +} +#endif + + +#ifdef NS_CACHING +static int +addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 0; /* identifies the getaddrinfo for the cache */ + char *hostname; + struct addrinfo *hints; + + char *p; + int ai_flags, ai_family, ai_socktype, ai_protocol; + size_t desired_size, size; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + hostname = va_arg(ap, char *); + hints = va_arg(ap, struct addrinfo *); + + desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; + if (hostname != NULL) { + size = strlen(hostname); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + *buffer_size = desired_size; + return (NS_RETURN); + } + + if (hints == NULL) + ai_flags = ai_family = ai_socktype = ai_protocol = 0; + else { + ai_flags = hints->ai_flags; + ai_family = hints->ai_family; + ai_socktype = hints->ai_socktype; + ai_protocol = hints->ai_protocol; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_flags, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_family, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_socktype, sizeof(int)); + p += sizeof(int); + + memcpy(p, &ai_protocol, sizeof(int)); + p += sizeof(int); + + if (hostname != NULL) + memcpy(p, hostname, size); + + *buffer_size = desired_size; + return (NS_SUCCESS); +} + +static int +addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo *ai, *cai; + char *p; + size_t desired_size, size, ai_size; + + ai = *((struct addrinfo **)retval); + + desired_size = sizeof(size_t); + ai_size = 0; + for (cai = ai; cai != NULL; cai = cai->ai_next) { + desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; + if (cai->ai_canonname != NULL) + desired_size += sizeof(size_t) + + strlen(cai->ai_canonname); + ++ai_size; + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + errno = ERANGE; + *buffer_size = desired_size; + return (NS_RETURN); + } + + memset(buffer, 0, desired_size); + p = buffer; + + memcpy(p, &ai_size, sizeof(size_t)); + p += sizeof(size_t); + for (cai = ai; cai != NULL; cai = cai->ai_next) { + memcpy(p, cai, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + + memcpy(p, cai->ai_addr, cai->ai_addrlen); + p += cai->ai_addrlen; + + if (cai->ai_canonname != NULL) { + size = strlen(cai->ai_canonname); + memcpy(p, &size, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, cai->ai_canonname, size); + p += size; + } + } + + return (NS_SUCCESS); +} + +static int +addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct addrinfo new_ai, *result, *sentinel, *lasts; + + char *p; + size_t ai_size, ai_i, size; + + p = buffer; + memcpy(&ai_size, p, sizeof(size_t)); + p += sizeof(size_t); + + result = NULL; + lasts = NULL; + for (ai_i = 0; ai_i < ai_size; ++ai_i) { + memcpy(&new_ai, p, sizeof(struct addrinfo)); + p += sizeof(struct addrinfo); + size = new_ai.ai_addrlen + sizeof(struct addrinfo) + + _ALIGNBYTES; + + sentinel = (struct addrinfo *)malloc(size); + memset(sentinel, 0, size); + + memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); + sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + + sizeof(struct addrinfo)); + + memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); + p += new_ai.ai_addrlen; + + if (new_ai.ai_canonname != NULL) { + memcpy(&size, p, sizeof(size_t)); + p += sizeof(size_t); + + sentinel->ai_canonname = (char *)malloc(size + 1); + memset(sentinel->ai_canonname, 0, size + 1); + + memcpy(sentinel->ai_canonname, p, size); + p += size; + } + + if (result == NULL) { + result = sentinel; + lasts = sentinel; + } else { + lasts->ai_next = sentinel; + lasts = sentinel; + } + } + + *((struct addrinfo **)retval) = result; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + +/* + * FQDN hostname, DNS lookup + */ +static int +explore_fqdn(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res) +{ + struct addrinfo *result; + struct addrinfo *cur; + int error = 0; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, + addrinfo_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_files_getaddrinfo, NULL) + { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ + NS_NIS_CB(_yp_getaddrinfo, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + + result = NULL; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", + default_dns_files, hostname, pai)) { + case NS_TRYAGAIN: + error = EAI_AGAIN; + goto free; + case NS_UNAVAIL: + error = EAI_FAIL; + goto free; + case NS_NOTFOUND: + error = EAI_NONAME; + goto free; + case NS_SUCCESS: + error = 0; + for (cur = result; cur; cur = cur->ai_next) { + GET_PORT(cur, servname); + /* canonname should be filled already */ + } + break; + } + + *res = result; + + return 0; + +free: + if (result) + freeaddrinfo(result); + return error; +} + +#ifdef DEBUG +static const char AskedForGot[] = + "gethostby*.getanswer: asked for \"%s\", got \"%s\""; +#endif + +static struct addrinfo * +getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, + const struct addrinfo *pai, res_state res) +{ + struct addrinfo sentinel, *cur; + struct addrinfo ai; + const struct afd *afd; + char *canonname; + const HEADER *hp; + const u_char *cp; + int n; + const u_char *eom; + char *bp, *ep; + int type, class, ancount, qdcount; + int haveanswer, had_error; + char tbuf[MAXDNAME]; + int (*name_ok)(const char *); + char hostbuf[8*1024]; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + canonname = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ + name_ok = res_hnok; + break; + default: + return (NULL); /* XXX should be abort(); */ + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hostbuf; + ep = hostbuf + sizeof hostbuf; + cp = answer->buf + HFIXEDSZ; + if (qdcount != 1) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (NULL); + } + cp += n + QFIXEDSZ; + if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (NULL); + } + canonname = bp; + bp += n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = canonname; + } + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + had_error++; + continue; + } + cp += n; /* name */ + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + if (class != C_IN) { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && + type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if ((n < 0) || !(*name_ok)(tbuf)) { + had_error++; + continue; + } + cp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strlcpy(bp, tbuf, ep - bp); + canonname = bp; + bp += n; + continue; + } + if (qtype == T_ANY) { + if (!(type == T_A || type == T_AAAA)) { + cp += n; + continue; + } + } else if (type != qtype) { +#ifdef DEBUG + if (type != T_KEY && type != T_SIG) + syslog(LOG_NOTICE|LOG_AUTH, + "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", + qname, p_class(C_IN), p_type(qtype), + p_type(type)); +#endif + cp += n; + continue; /* XXX - had_error++ ? */ + } + switch (type) { + case T_A: + case T_AAAA: + if (strcasecmp(canonname, bp) != 0) { +#ifdef DEBUG + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, canonname, bp); +#endif + cp += n; + continue; /* XXX - had_error++ ? */ + } + if (type == T_A && n != INADDRSZ) { + cp += n; + continue; + } + if (type == T_AAAA && n != IN6ADDRSZ) { + cp += n; + continue; + } +#ifdef FILTER_V4MAPPED + if (type == T_AAAA) { + struct in6_addr in6; + memcpy(&in6, cp, sizeof(in6)); + if (IN6_IS_ADDR_V4MAPPED(&in6)) { + cp += n; + continue; + } + } +#endif + if (!haveanswer) { + int nn; + + canonname = bp; + nn = strlen(bp) + 1; /* for the \0 */ + bp += nn; + } + + /* don't overwrite pai */ + ai = *pai; + ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; + afd = find_afd(ai.ai_family); + if (afd == NULL) { + cp += n; + continue; + } + cur->ai_next = get_ai(&ai, afd, (const char *)cp); + if (cur->ai_next == NULL) + had_error++; + while (cur && cur->ai_next) + cur = cur->ai_next; + cp += n; + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { +#if defined(RESOLVSORT) + /* + * We support only IPv4 address for backward + * compatibility against gethostbyname(3). + */ + if (res->nsort && qtype == T_A) { + if (addr4sort(&sentinel, res) < 0) { + freeaddrinfo(sentinel.ai_next); + RES_SET_H_ERRNO(res, NO_RECOVERY); + return NULL; + } + } +#endif /*RESOLVSORT*/ + if (!canonname) + (void)get_canonname(pai, sentinel.ai_next, qname); + else + (void)get_canonname(pai, sentinel.ai_next, canonname); + RES_SET_H_ERRNO(res, NETDB_SUCCESS); + return sentinel.ai_next; + } + + RES_SET_H_ERRNO(res, NO_RECOVERY); + return NULL; +} + +#ifdef RESOLVSORT +struct addr_ptr { + struct addrinfo *ai; + int aval; +}; + +static int +addr4sort(struct addrinfo *sentinel, res_state res) +{ + struct addrinfo *ai; + struct addr_ptr *addrs, addr; + struct sockaddr_in *sin; + int naddrs, i, j; + int needsort = 0; + + if (!sentinel) + return -1; + naddrs = 0; + for (ai = sentinel->ai_next; ai; ai = ai->ai_next) + naddrs++; + if (naddrs < 2) + return 0; /* We don't need sorting. */ + if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) + return -1; + i = 0; + for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { + sin = (struct sockaddr_in *)ai->ai_addr; + for (j = 0; (unsigned)j < res->nsort; j++) { + if (res->sort_list[j].addr.s_addr == + (sin->sin_addr.s_addr & res->sort_list[j].mask)) + break; + } + addrs[i].ai = ai; + addrs[i].aval = j; + if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) + needsort = i; + i++; + } + if (!needsort) { + free(addrs); + return 0; + } + + while (needsort < naddrs) { + for (j = needsort - 1; j >= 0; j--) { + if (addrs[j].aval > addrs[j+1].aval) { + addr = addrs[j]; + addrs[j] = addrs[j + 1]; + addrs[j + 1] = addr; + } else + break; + } + needsort++; + } + + ai = sentinel; + for (i = 0; i < naddrs; ++i) { + ai->ai_next = addrs[i].ai; + ai = ai->ai_next; + } + ai->ai_next = NULL; + free(addrs); + return 0; +} +#endif /*RESOLVSORT*/ + +/*ARGSUSED*/ +static int +_dns_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo *ai; + querybuf *buf, *buf2; + const char *hostname; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + struct res_target q, q2; + res_state res; + + hostname = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&q, 0, sizeof(q)); + memset(&q2, 0, sizeof(q2)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + buf = malloc(sizeof(*buf)); + if (!buf) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return NS_NOTFOUND; + } + buf2 = malloc(sizeof(*buf2)); + if (!buf2) { + free(buf); + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return NS_NOTFOUND; + } + + switch (pai->ai_family) { + case AF_UNSPEC: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + q.next = &q2; + q2.name = hostname; + q2.qclass = C_IN; + q2.qtype = T_AAAA; + q2.answer = buf2->buf; + q2.anslen = sizeof(buf2->buf); + break; + case AF_INET: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + case AF_INET6: + q.name = hostname; + q.qclass = C_IN; + q.qtype = T_AAAA; + q.answer = buf->buf; + q.anslen = sizeof(buf->buf); + break; + default: + free(buf); + free(buf2); + return NS_UNAVAIL; + } + + res = __res_state(); + if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + free(buf); + free(buf2); + return NS_NOTFOUND; + } + + if (res_searchN(hostname, &q, res) < 0) { + free(buf); + free(buf2); + return NS_NOTFOUND; + } + /* prefer IPv6 */ + if (q.next) { + ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + } + ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); + if (ai) + cur->ai_next = ai; + free(buf); + free(buf2); + if (sentinel.ai_next == NULL) + switch (res->res_h_errno) { + case HOST_NOT_FOUND: + return NS_NOTFOUND; + case TRY_AGAIN: + return NS_TRYAGAIN; + default: + return NS_UNAVAIL; + } + *((struct addrinfo **)rv) = sentinel.ai_next; + return NS_SUCCESS; +} + +static void +_sethtent(FILE **hostf) +{ + if (!*hostf) + *hostf = fopen(_PATH_HOSTS, "r"); + else + rewind(*hostf); +} + +static void +_endhtent(FILE **hostf) +{ + if (*hostf) { + (void) fclose(*hostf); + *hostf = NULL; + } +} + +static struct addrinfo * +_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) +{ + char *p; + char *cp, *tname, *cname; + struct addrinfo hints, *res0, *res; + int error; + const char *addr; + char hostbuf[8*1024]; + + if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "r"))) + return (NULL); +again: + if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) + return (NULL); + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + if (!(cp = strpbrk(p, " \t"))) + goto again; + *cp++ = '\0'; + addr = p; + cname = NULL; + /* if this is not something we're looking for, skip it. */ + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + tname = cp; + if (cname == NULL) + cname = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + if (strcasecmp(name, tname) == 0) + goto found; + } + goto again; + +found: + /* we should not glob socktype/protocol here */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = pai->ai_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(addr, "0", &hints, &res0); + if (error) + goto again; +#ifdef FILTER_V4MAPPED + /* XXX should check all items in the chain */ + if (res0->ai_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { + freeaddrinfo(res0); + goto again; + } +#endif + for (res = res0; res; res = res->ai_next) { + /* cover it up */ + res->ai_flags = pai->ai_flags; + res->ai_socktype = pai->ai_socktype; + res->ai_protocol = pai->ai_protocol; + + if (pai->ai_flags & AI_CANONNAME) { + if (get_canonname(pai, res, cname) != 0) { + freeaddrinfo(res0); + goto again; + } + } + } + return res0; +} + +/*ARGSUSED*/ +static int +_files_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + const char *name; + const struct addrinfo *pai; + struct addrinfo sentinel, *cur; + struct addrinfo *p; + FILE *hostf = NULL; + + name = va_arg(ap, char *); + pai = va_arg(ap, struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + _sethtent(&hostf); + while ((p = _gethtent(&hostf, name, pai)) != NULL) { + cur->ai_next = p; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + _endhtent(&hostf); + + *((struct addrinfo **)rv) = sentinel.ai_next; + if (sentinel.ai_next == NULL) + return NS_NOTFOUND; + return NS_SUCCESS; +} + +#ifdef YP +/*ARGSUSED*/ +static struct addrinfo * +_yphostent(char *line, const struct addrinfo *pai) +{ + struct addrinfo sentinel, *cur; + struct addrinfo hints, *res, *res0; + int error; + char *p = line; + const char *addr, *canonname; + char *nextline; + char *cp; + + addr = canonname = NULL; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + +nextline: + /* terminate line */ + cp = strchr(p, '\n'); + if (cp) { + *cp++ = '\0'; + nextline = cp; + } else + nextline = NULL; + + cp = strpbrk(p, " \t"); + if (cp == NULL) { + if (canonname == NULL) + return (NULL); + else + goto done; + } + *cp++ = '\0'; + + addr = p; + + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (!canonname) + canonname = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + } + + hints = *pai; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(addr, NULL, &hints, &res0); + if (error == 0) { + for (res = res0; res; res = res->ai_next) { + /* cover it up */ + res->ai_flags = pai->ai_flags; + + if (pai->ai_flags & AI_CANONNAME) + (void)get_canonname(pai, res, canonname); + } + } else + res0 = NULL; + if (res0) { + cur->ai_next = res0; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + if (nextline) { + p = nextline; + goto nextline; + } + +done: + return sentinel.ai_next; +} + +/*ARGSUSED*/ +static int +_yp_getaddrinfo(void *rv, void *cb_data, va_list ap) +{ + struct addrinfo sentinel, *cur; + struct addrinfo *ai = NULL; + char *ypbuf; + int ypbuflen, r; + const char *name; + const struct addrinfo *pai; + char *ypdomain; + + if (_yp_check(&ypdomain) == 0) + return NS_UNAVAIL; + + name = va_arg(ap, char *); + pai = va_arg(ap, const struct addrinfo *); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + /* hosts.byname is only for IPv4 (Solaris8) */ + if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { + r = yp_match(ypdomain, "hosts.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + struct addrinfo ai4; + + ai4 = *pai; + ai4.ai_family = AF_INET; + ai = _yphostent(ypbuf, &ai4); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + free(ypbuf); + } + } + + /* ipnodes.byname can hold both IPv4/v6 */ + r = yp_match(ypdomain, "ipnodes.byname", name, + (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + ai = _yphostent(ypbuf, pai); + if (ai) + cur->ai_next = ai; + free(ypbuf); + } + + if (sentinel.ai_next == NULL) { + RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); + return NS_NOTFOUND; + } + *((struct addrinfo **)rv) = sentinel.ai_next; + return NS_SUCCESS; +} +#endif + +/* resolver logic */ + +/* + * Formulate a normal query, send, and await answer. + * Returned answer is placed in supplied buffer "answer". + * Perform preliminary check of answer, returning success only + * if no error is indicated and the answer count is nonzero. + * Return the size of the response on success, -1 on error. + * Error number is left in h_errno. + * + * Caller must parse answer and determine whether it answers the question. + */ +static int +res_queryN(const char *name, struct res_target *target, res_state res) +{ + u_char *buf; + HEADER *hp; + int n; + u_int oflags; + struct res_target *t; + int rcode; + int ancount; + + rcode = NOERROR; + ancount = 0; + + buf = malloc(MAXPACKET); + if (!buf) { + RES_SET_H_ERRNO(res, NETDB_INTERNAL); + return -1; + } + + for (t = target; t; t = t->next) { + int class, type; + u_char *answer; + int anslen; + + hp = (HEADER *)(void *)t->answer; + + /* make it easier... */ + class = t->qclass; + type = t->qtype; + answer = t->answer; + anslen = t->anslen; + + oflags = res->_flags; + +again: + hp->rcode = NOERROR; /* default */ + +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query(%s, %d, %d)\n", name, class, type); +#endif + + n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, + buf, MAXPACKET); + if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && + (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) + n = res_nopt(res, n, buf, MAXPACKET, anslen); + if (n <= 0) { +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query: mkquery failed\n"); +#endif + free(buf); + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (n); + } + n = res_nsend(res, buf, n, answer, anslen); + if (n < 0) { + /* + * if the query choked with EDNS0, retry + * without EDNS0 + */ + if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) + != 0U && + ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { + res->_flags |= RES_F_EDNS0ERR; + if (res->options & RES_DEBUG) + printf(";; res_nquery: retry without EDNS0\n"); + goto again; + } + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + continue; + } + + if (n > anslen) + hp->rcode = FORMERR; /* XXX not very informative */ + if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; rcode = %u, ancount=%u\n", hp->rcode, + ntohs(hp->ancount)); +#endif + continue; + } + + ancount += ntohs(hp->ancount); + + t->n = n; + } + + free(buf); + + if (ancount == 0) { + switch (rcode) { + case NXDOMAIN: + RES_SET_H_ERRNO(res, HOST_NOT_FOUND); + break; + case SERVFAIL: + RES_SET_H_ERRNO(res, TRY_AGAIN); + break; + case NOERROR: + RES_SET_H_ERRNO(res, NO_DATA); + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + RES_SET_H_ERRNO(res, NO_RECOVERY); + break; + } + return (-1); + } + return (ancount); +} + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error code, if any, is left in h_errno. + */ +static int +res_searchN(const char *name, struct res_target *target, res_state res) +{ + const char *cp, * const *domain; + HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ + u_int dots; + int trailing_dot, ret, saved_herrno; + int got_nodata = 0, got_servfail = 0, root_on_list = 0; + int tried_as_is = 0; + int searched = 0; + char abuf[MAXDNAME]; + + errno = 0; + RES_SET_H_ERRNO(res, HOST_NOT_FOUND); /* default, if we never query */ + dots = 0; + for (cp = name; *cp; cp++) + dots += (*cp == '.'); + trailing_dot = 0; + if (cp > name && *--cp == '.') + trailing_dot++; + + /* + * if there aren't any dots, it could be a user-level alias + */ + if (!dots && + (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) + return (res_queryN(cp, target, res)); + + /* + * If there are enough dots in the name, let's just give it a + * try 'as is'. The threshold can be set with the "ndots" option. + * Also, query 'as is', if there is a trailing dot in the name. + */ + saved_herrno = -1; + if (dots >= res->ndots || trailing_dot) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0 || trailing_dot) + return (ret); + if (errno == ECONNREFUSED) { + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); + } + switch (res->res_h_errno) { + case NO_DATA: + case HOST_NOT_FOUND: + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) + break; + /* FALLTHROUGH */ + default: + return (-1); + } + saved_herrno = res->res_h_errno; + tried_as_is++; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (res->options & RES_DEFNAMES)) || + (dots && !trailing_dot && (res->options & RES_DNSRCH))) { + int done = 0; + + for (domain = (const char * const *)res->dnsrch; + *domain && !done; + domain++) { + searched = 1; + + if (domain[0][0] == '\0' || + (domain[0][0] == '.' && domain[0][1] == '\0')) + root_on_list++; + + if (root_on_list && tried_as_is) + continue; + + ret = res_querydomainN(name, *domain, target, res); + if (ret > 0) + return (ret); + + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's + * fully-qualified. + */ + if (errno == ECONNREFUSED) { + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); + } + + switch (res->res_h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + got_servfail++; + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + break; + } + /* FALLTHROUGH */ + default: + /* anything else implies that we're done */ + done++; + } + /* + * if we got here for some reason other than DNSRCH, + * we only wanted one iteration of the loop, so stop. + */ + if (!(res->options & RES_DNSRCH)) + done++; + } + } + + switch (res->res_h_errno) { + case NO_DATA: + case HOST_NOT_FOUND: + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) + break; + /* FALLTHROUGH */ + default: + goto giveup; + } + + /* + * If the query has not already been tried as is then try it + * unless RES_NOTLDQUERY is set and there were no dots. + */ + if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && + !(tried_as_is || root_on_list)) { + ret = res_querydomainN(name, NULL, target, res); + if (ret > 0) + return (ret); + } + + /* + * if we got here, we didn't satisfy the search. + * if we did an initial full query, return that query's h_errno + * (note that we wouldn't be here if that query had succeeded). + * else if we ever got a nodata, send that back as the reason. + * else send back meaningless h_errno, that being the one from + * the last DNSRCH we did. + */ +giveup: + if (saved_herrno != -1) + RES_SET_H_ERRNO(res, saved_herrno); + else if (got_nodata) + RES_SET_H_ERRNO(res, NO_DATA); + else if (got_servfail) + RES_SET_H_ERRNO(res, TRY_AGAIN); + return (-1); +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +static int +res_querydomainN(const char *name, const char *domain, + struct res_target *target, res_state res) +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; + +#ifdef DEBUG + if (res->options & RES_DEBUG) + printf(";; res_querydomain(%s, %s)\n", + name, domain?domain:"<Nil>"); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n >= MAXDNAME) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (-1); + } + if (n > 0 && name[--n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + longname = name; + } else { + n = strlen(name); + d = strlen(domain); + if (n + d + 1 >= MAXDNAME) { + RES_SET_H_ERRNO(res, NO_RECOVERY); + return (-1); + } + snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); + } + return (res_queryN(longname, target, res)); +} diff --git a/lib/libc/net/gethostbydns.c b/lib/libc/net/gethostbydns.c new file mode 100644 index 0000000..b19dc79 --- /dev/null +++ b/lib/libc/net/gethostbydns.c @@ -0,0 +1,762 @@ +/* + * ++Copyright++ 1985, 1988, 1993 + * - + * Copyright (c) 1985, 1988, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; +static char fromrcsid[] = "From: Id: gethnamaddr.c,v 8.23 1998/04/07 04:59:46 vixie Exp $"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> +#include <resolv.h> +#include <ctype.h> +#include <errno.h> +#include <syslog.h> +#include <stdarg.h> +#include <nsswitch.h> + +#include "netdb_private.h" +#include "res_config.h" + +#define SPRINTF(x) ((size_t)sprintf x) + +static const char AskedForGot[] = + "gethostby*.gethostanswer: asked for \"%s\", got \"%s\""; + +#ifdef RESOLVSORT +static void addrsort(char **, int, res_state); +#endif + +#ifdef DEBUG +static void dprintf(char *, int, res_state) __printflike(1, 0); +#endif + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +typedef union { + int32_t al; + char ac; +} align; + +int _dns_ttl_; + +#ifdef DEBUG +static void +dprintf(msg, num, res) + char *msg; + int num; + res_state res; +{ + if (res->options & RES_DEBUG) { + int save = errno; + + printf(msg, num); + errno = save; + } +} +#else +# define dprintf(msg, num, res) /*nada*/ +#endif + +#define BOUNDED_INCR(x) \ + do { \ + cp += x; \ + if (cp > eom) { \ + RES_SET_H_ERRNO(statp, NO_RECOVERY); \ + return (-1); \ + } \ + } while (0) + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + RES_SET_H_ERRNO(statp, NO_RECOVERY); \ + return (-1); \ + } \ + } while (0) + +static int +gethostanswer(const querybuf *answer, int anslen, const char *qname, int qtype, + struct hostent *he, struct hostent_data *hed, res_state statp) +{ + const HEADER *hp; + const u_char *cp; + int n; + const u_char *eom, *erdata; + char *bp, *ep, **ap, **hap; + int type, class, ancount, qdcount; + int haveanswer, had_error; + int toobig = 0; + char tbuf[MAXDNAME]; + const char *tname; + int (*name_ok)(const char *); + + tname = qname; + he->h_name = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + name_ok = res_hnok; + break; + case T_PTR: + name_ok = res_dnok; + break; + default: + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); /* XXX should be abort(); */ + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hed->hostbuf; + ep = hed->hostbuf + sizeof hed->hostbuf; + cp = answer->buf; + BOUNDED_INCR(HFIXEDSZ); + if (qdcount != 1) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + BOUNDED_INCR(n + QFIXEDSZ); + if (qtype == T_A || qtype == T_AAAA) { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + he->h_name = bp; + bp += n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = he->h_name; + } + ap = hed->host_aliases; + *ap = NULL; + he->h_aliases = hed->host_aliases; + hap = hed->h_addr_ptrs; + *hap = NULL; + he->h_addr_list = hed->h_addr_ptrs; + haveanswer = 0; + had_error = 0; + _dns_ttl_ = -1; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + had_error++; + continue; + } + cp += n; /* name */ + BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ; /* class */ + if (qtype == T_A && type == T_A) + _dns_ttl_ = _getlong(cp); + cp += INT32SZ; /* TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + BOUNDS_CHECK(cp, n); + erdata = cp + n; + if (class != C_IN) { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { + if (ap >= &hed->host_aliases[_MAXALIASES-1]) + continue; + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if ((n < 0) || !(*name_ok)(tbuf)) { + had_error++; + continue; + } + cp += n; + if (cp != erdata) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + /* Store alias. */ + *ap++ = bp; + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + bp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strcpy(bp, tbuf); + he->h_name = bp; + bp += n; + continue; + } + if (qtype == T_PTR && type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if (n < 0 || !res_dnok(tbuf)) { + had_error++; + continue; + } + cp += n; + if (cp != erdata) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strcpy(bp, tbuf); + tname = bp; + bp += n; + continue; + } + if (type != qtype) { + if (type != T_SIG) + syslog(LOG_NOTICE|LOG_AUTH, + "gethostby*.gethostanswer: asked for \"%s %s %s\", got type \"%s\"", + qname, p_class(C_IN), p_type(qtype), + p_type(type)); + cp += n; + continue; /* XXX - had_error++ ? */ + } + switch (type) { + case T_PTR: + if (strcasecmp(tname, bp) != 0) { + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, qname, bp); + cp += n; + continue; /* XXX - had_error++ ? */ + } + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !res_hnok(bp)) { + had_error++; + break; + } +#if MULTI_PTRS_ARE_ALIASES + cp += n; + if (cp != erdata) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + if (!haveanswer) + he->h_name = bp; + else if (ap < &hed->host_aliases[_MAXALIASES-1]) + *ap++ = bp; + else + n = -1; + if (n != -1) { + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + had_error++; + break; + } + bp += n; + } + break; +#else + he->h_name = bp; + if (statp->options & RES_USE_INET6) { + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + had_error++; + break; + } + bp += n; + _map_v4v6_hostent(he, &bp, ep); + } + RES_SET_H_ERRNO(statp, NETDB_SUCCESS); + return (0); +#endif + case T_A: + case T_AAAA: + if (strcasecmp(he->h_name, bp) != 0) { + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, he->h_name, bp); + cp += n; + continue; /* XXX - had_error++ ? */ + } + if (n != he->h_length) { + cp += n; + continue; + } + if (!haveanswer) { + int nn; + + he->h_name = bp; + nn = strlen(bp) + 1; /* for the \0 */ + bp += nn; + } + + bp += sizeof(align) - ((u_long)bp % sizeof(align)); + + if (bp + n >= ep) { + dprintf("size (%d) too big\n", n, statp); + had_error++; + continue; + } + if (hap >= &hed->h_addr_ptrs[_MAXADDRS-1]) { + if (!toobig++) + dprintf("Too many addresses (%d)\n", + _MAXADDRS, statp); + cp += n; + continue; + } + memcpy(*hap++ = bp, cp, n); + bp += n; + cp += n; + if (cp != erdata) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + break; + default: + dprintf("Impossible condition (type=%d)\n", type, + statp); + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + /* BIND has abort() here, too risky on bad data */ + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + *ap = NULL; + *hap = NULL; +# if defined(RESOLVSORT) + /* + * Note: we sort even if host can take only one address + * in its return structures - should give it the "best" + * address in that case, not some random one + */ + if (statp->nsort && haveanswer > 1 && qtype == T_A) + addrsort(hed->h_addr_ptrs, haveanswer, statp); +# endif /*RESOLVSORT*/ + if (!he->h_name) { + n = strlen(qname) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) + goto no_recovery; + strcpy(bp, qname); + he->h_name = bp; + bp += n; + } + if (statp->options & RES_USE_INET6) + _map_v4v6_hostent(he, &bp, ep); + RES_SET_H_ERRNO(statp, NETDB_SUCCESS); + return (0); + } + no_recovery: + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); +} + +/* XXX: for async DNS resolver in ypserv */ +struct hostent * +__dns_getanswer(const char *answer, int anslen, const char *qname, int qtype) +{ + struct hostent *he; + struct hostent_data *hed; + int error; + res_state statp; + + statp = __res_state(); + if ((he = __hostent_init()) == NULL || + (hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (NULL); + } + switch (qtype) { + case T_AAAA: + he->h_addrtype = AF_INET6; + he->h_length = NS_IN6ADDRSZ; + break; + case T_A: + default: + he->h_addrtype = AF_INET; + he->h_length = NS_INADDRSZ; + break; + } + + error = gethostanswer((const querybuf *)answer, anslen, qname, qtype, + he, hed, statp); + return (error == 0) ? he : NULL; +} + +int +_dns_gethostbyname(void *rval, void *cb_data, va_list ap) +{ + const char *name; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct hostent *hptr, he; + struct hostent_data *hed; + querybuf *buf; + int n, type, error; + res_state statp; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + he.h_addrtype = af; + switch (af) { + case AF_INET: + he.h_length = NS_INADDRSZ; + type = T_A; + break; + case AF_INET6: + he.h_length = NS_IN6ADDRSZ; + type = T_AAAA; + break; + default: + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + errno = EAFNOSUPPORT; + return (NS_UNAVAIL); + } + + if ((buf = malloc(sizeof(*buf))) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + n = res_nsearch(statp, name, C_IN, type, buf->buf, sizeof(buf->buf)); + if (n < 0) { + free(buf); + dprintf("res_nsearch failed (%d)\n", n, statp); + *h_errnop = statp->res_h_errno; + return (0); + } else if (n > sizeof(buf->buf)) { + free(buf); + dprintf("static buffer is too small (%d)\n", n, statp); + *h_errnop = statp->res_h_errno; + return (0); + } + error = gethostanswer(buf, n, name, type, &he, hed, statp); + free(buf); + if (error != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +} + +int +_dns_gethostbyaddr(void *rval, void *cb_data, va_list ap) +{ + const void *addr; + socklen_t len; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + const u_char *uaddr; + struct hostent *hptr, he; + struct hostent_data *hed; + int n; + querybuf *buf; + char qbuf[MAXDNAME+1], *qp; + res_state statp; +#ifdef SUNSECURITY + struct hostdata rhd; + struct hostent *rhe; + char **haddr; + u_long old_options; + char hname2[MAXDNAME+1], numaddr[46]; + int ret_h_error; +#endif /*SUNSECURITY*/ + + addr = va_arg(ap, const void *); + len = va_arg(ap, socklen_t); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + uaddr = (const u_char *)addr; + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + switch (af) { + case AF_INET: + (void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", + (uaddr[3] & 0xff), + (uaddr[2] & 0xff), + (uaddr[1] & 0xff), + (uaddr[0] & 0xff)); + break; + case AF_INET6: + qp = qbuf; + for (n = NS_IN6ADDRSZ - 1; n >= 0; n--) { + qp += SPRINTF((qp, "%x.%x.", + uaddr[n] & 0xf, + (uaddr[n] >> 4) & 0xf)); + } + strlcat(qbuf, "ip6.arpa", sizeof(qbuf)); + break; + default: + abort(); + } + if ((buf = malloc(sizeof(*buf))) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return NS_NOTFOUND; + } + n = res_nquery(statp, qbuf, C_IN, T_PTR, (u_char *)buf->buf, + sizeof buf->buf); + if (n < 0) { + free(buf); + dprintf("res_nquery failed (%d)\n", n, statp); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + if (n > sizeof buf->buf) { + free(buf); + dprintf("static buffer is too small (%d)\n", n, statp); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + if (gethostanswer(buf, n, qbuf, T_PTR, &he, hed, statp) != 0) { + free(buf); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); /* h_errno was set by gethostanswer() */ + } + free(buf); +#ifdef SUNSECURITY + if (af == AF_INET) { + /* + * turn off search as the name should be absolute, + * 'localhost' should be matched by defnames + */ + strncpy(hname2, he.h_name, MAXDNAME); + hname2[MAXDNAME] = '\0'; + old_options = statp->options; + statp->options &= ~RES_DNSRCH; + statp->options |= RES_DEFNAMES; + memset(&rhd, 0, sizeof rhd); + rhe = gethostbyname_r(hname2, &rhd.host, &rhd.data, + sizeof(rhd.data), &ret_h_error); + if (rhe == NULL) { + if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL) + strlcpy(numaddr, "UNKNOWN", sizeof(numaddr)); + syslog(LOG_NOTICE|LOG_AUTH, + "gethostbyaddr: No A record for %s (verifying [%s])", + hname2, numaddr); + statp->options = old_options; + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + statp->options = old_options; + for (haddr = rhe->h_addr_list; *haddr; haddr++) + if (!memcmp(*haddr, addr, NS_INADDRSZ)) + break; + if (!*haddr) { + if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL) + strlcpy(numaddr, "UNKNOWN", sizeof(numaddr)); + syslog(LOG_NOTICE|LOG_AUTH, + "gethostbyaddr: A record of %s != PTR record [%s]", + hname2, numaddr); + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + } +#endif /*SUNSECURITY*/ + he.h_addrtype = af; + he.h_length = len; + memcpy(hed->host_addr, uaddr, len); + hed->h_addr_ptrs[0] = (char *)hed->host_addr; + hed->h_addr_ptrs[1] = NULL; + if (af == AF_INET && (statp->options & RES_USE_INET6)) { + _map_v4v6_address((char*)hed->host_addr, (char*)hed->host_addr); + he.h_addrtype = AF_INET6; + he.h_length = NS_IN6ADDRSZ; + } + RES_SET_H_ERRNO(statp, NETDB_SUCCESS); + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +} + +#ifdef RESOLVSORT +static void +addrsort(char **ap, int num, res_state res) +{ + int i, j; + char **p; + short aval[_MAXADDRS]; + int needsort = 0; + + p = ap; + for (i = 0; i < num; i++, p++) { + for (j = 0 ; (unsigned)j < res->nsort; j++) + if (res->sort_list[j].addr.s_addr == + (((struct in_addr *)(*p))->s_addr & res->sort_list[j].mask)) + break; + aval[i] = j; + if (needsort == 0 && i > 0 && j < aval[i-1]) + needsort = i; + } + if (!needsort) + return; + + while (needsort < num) { + for (j = needsort - 1; j >= 0; j--) { + if (aval[j] > aval[j+1]) { + char *hp; + + i = aval[j]; + aval[j] = aval[j+1]; + aval[j+1] = i; + + hp = ap[j]; + ap[j] = ap[j+1]; + ap[j+1] = hp; + + } else + break; + } + needsort++; + } +} +#endif + +void +_sethostdnsent(int stayopen) +{ + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) + return; + if (stayopen) + statp->options |= RES_STAYOPEN | RES_USEVC; +} + +void +_endhostdnsent() +{ + res_state statp; + + statp = __res_state(); + statp->options &= ~(RES_STAYOPEN | RES_USEVC); + res_nclose(statp); +} diff --git a/lib/libc/net/gethostbyht.c b/lib/libc/net/gethostbyht.c new file mode 100644 index 0000000..cb5009d --- /dev/null +++ b/lib/libc/net/gethostbyht.c @@ -0,0 +1,335 @@ +/*- + * Copyright (c) 1985, 1988, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include <arpa/nameser.h> /* XXX */ +#include <resolv.h> /* XXX */ +#include "netdb_private.h" + +void +_sethosthtent(int f, struct hostent_data *hed) +{ + if (!hed->hostf) + hed->hostf = fopen(_PATH_HOSTS, "r"); + else + rewind(hed->hostf); + hed->stayopen = f; +} + +void +_endhosthtent(struct hostent_data *hed) +{ + if (hed->hostf && !hed->stayopen) { + (void) fclose(hed->hostf); + hed->hostf = NULL; + } +} + +static int +gethostent_p(struct hostent *he, struct hostent_data *hed, int mapped, + res_state statp) +{ + char *p, *bp, *ep; + char *cp, **q; + int af, len; + char hostbuf[BUFSIZ + 1]; + + if (!hed->hostf && !(hed->hostf = fopen(_PATH_HOSTS, "r"))) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + again: + if (!(p = fgets(hostbuf, sizeof hostbuf, hed->hostf))) { + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + return (-1); + } + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + if (!(cp = strpbrk(p, " \t"))) + goto again; + *cp++ = '\0'; + if (inet_pton(AF_INET6, p, hed->host_addr) > 0) { + af = AF_INET6; + len = IN6ADDRSZ; + } else if (inet_pton(AF_INET, p, hed->host_addr) > 0) { + if (mapped) { + _map_v4v6_address((char *)hed->host_addr, + (char *)hed->host_addr); + af = AF_INET6; + len = IN6ADDRSZ; + } else { + af = AF_INET; + len = INADDRSZ; + } + } else { + goto again; + } + hed->h_addr_ptrs[0] = (char *)hed->host_addr; + hed->h_addr_ptrs[1] = NULL; + he->h_addr_list = hed->h_addr_ptrs; + he->h_length = len; + he->h_addrtype = af; + while (*cp == ' ' || *cp == '\t') + cp++; + bp = hed->hostbuf; + ep = hed->hostbuf + sizeof hed->hostbuf; + he->h_name = bp; + q = he->h_aliases = hed->host_aliases; + if ((p = strpbrk(cp, " \t")) != NULL) + *p++ = '\0'; + len = strlen(cp) + 1; + if (ep - bp < len) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + strlcpy(bp, cp, ep - bp); + bp += len; + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q >= &hed->host_aliases[_MAXALIASES - 1]) + break; + if ((p = strpbrk(cp, " \t")) != NULL) + *p++ = '\0'; + len = strlen(cp) + 1; + if (ep - bp < len) + break; + strlcpy(bp, cp, ep - bp); + *q++ = bp; + bp += len; + cp = p; + } + *q = NULL; + RES_SET_H_ERRNO(statp, NETDB_SUCCESS); + return (0); +} + +int +gethostent_r(struct hostent *hptr, char *buffer, size_t buflen, + struct hostent **result, int *h_errnop) +{ + struct hostent_data *hed; + struct hostent he; + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + if (gethostent_p(&he, hed, statp->options & RES_USE_INET6, statp) != 0) + return (-1); + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) + return (-1); + *result = hptr; + return (0); +} + +struct hostent * +gethostent(void) +{ + struct hostdata *hd; + struct hostent *rval; + int ret_h_errno; + + if ((hd = __hostdata_init()) == NULL) + return (NULL); + if (gethostent_r(&hd->host, hd->data, sizeof(hd->data), &rval, + &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +int +_ht_gethostbyname(void *rval, void *cb_data, va_list ap) +{ + const char *name; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct hostent *hptr, he; + struct hostent_data *hed; + char **cp; + res_state statp; + int error; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + _sethosthtent(0, hed); + while ((error = gethostent_p(&he, hed, 0, statp)) == 0) { + if (he.h_addrtype != af) + continue; + if (he.h_addrtype == AF_INET && + statp->options & RES_USE_INET6) { + _map_v4v6_address(he.h_addr, he.h_addr); + he.h_length = IN6ADDRSZ; + he.h_addrtype = AF_INET6; + } + if (strcasecmp(he.h_name, name) == 0) + break; + for (cp = he.h_aliases; *cp != 0; cp++) + if (strcasecmp(*cp, name) == 0) + goto found; + } +found: + _endhosthtent(hed); + + if (error != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +} + +int +_ht_gethostbyaddr(void *rval, void *cb_data, va_list ap) +{ + const void *addr; + socklen_t len; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct hostent *hptr, he; + struct hostent_data *hed; + res_state statp; + int error; + + addr = va_arg(ap, const void *); + len = va_arg(ap, socklen_t); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + _sethosthtent(0, hed); + while ((error = gethostent_p(&he, hed, 0, statp)) == 0) + if (he.h_addrtype == af && !bcmp(he.h_addr, addr, len)) { + if (he.h_addrtype == AF_INET && + statp->options & RES_USE_INET6) { + _map_v4v6_address(he.h_addr, he.h_addr); + he.h_length = IN6ADDRSZ; + he.h_addrtype = AF_INET6; + } + break; + } + _endhosthtent(hed); + + if (error != 0) + return (NS_NOTFOUND); + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +} diff --git a/lib/libc/net/gethostbyname.3 b/lib/libc/net/gethostbyname.3 new file mode 100644 index 0000000..7c83d02 --- /dev/null +++ b/lib/libc/net/gethostbyname.3 @@ -0,0 +1,379 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd May 25, 1995 +.Dt GETHOSTBYNAME 3 +.Os +.Sh NAME +.Nm gethostbyname , +.Nm gethostbyname2 , +.Nm gethostbyaddr , +.Nm gethostent , +.Nm sethostent , +.Nm endhostent , +.Nm herror , +.Nm hstrerror +.Nd get network host entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In netdb.h +.Vt extern int h_errno ; +.Ft struct hostent * +.Fn gethostbyname "const char *name" +.Ft struct hostent * +.Fn gethostbyname2 "const char *name" "int af" +.Ft struct hostent * +.Fn gethostbyaddr "const void *addr" "socklen_t len" "int type" +.Ft struct hostent * +.Fn gethostent void +.Ft void +.Fn sethostent "int stayopen" +.Ft void +.Fn endhostent void +.Ft void +.Fn herror "const char *string" +.Ft const char * +.Fn hstrerror "int err" +.Sh DESCRIPTION +.Bf -symbolic +The +.Xr getaddrinfo 3 +and +.Xr getnameinfo 3 +functions are preferred over the +.Fn gethostbyname , +.Fn gethostbyname2 , +and +.Fn gethostbyaddr +functions. +.Ef +.Pp +The +.Fn gethostbyname , +.Fn gethostbyname2 +and +.Fn gethostbyaddr +functions +each return a pointer to an object with the +following structure describing an internet host +referenced by name or by address, respectively. +.Pp +The +.Fa name +argument passed to +.Fn gethostbyname +or +.Fn gethostbyname2 +should point to a +.Dv NUL Ns -terminated +hostname. +The +.Fa addr +argument passed to +.Fn gethostbyaddr +should point to an address which is +.Fa len +bytes long, +in binary form +(i.e., not an IP address in human readable +.Tn ASCII +form). +The +.Fa type +argument specifies the address family +(e.g.\& +.Dv AF_INET , AF_INET6 , +etc.) of this address. +.Pp +The structure returned contains either the information obtained from the name +server, +.Xr named 8 , +broken-out fields from a line in +.Pa /etc/hosts , +or database entries supplied by the +.Xr yp 8 +system. +The order of the lookups is controlled by the +.Sq hosts +entry in +.Xr nsswitch.conf 5 . +.Bd -literal +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses from name server */ +}; +#define h_addr h_addr_list[0] /* address, for backward compatibility */ +.Ed +.Pp +The members of this structure are: +.Bl -tag -width h_addr_list +.It Va h_name +Official name of the host. +.It Va h_aliases +A +.Dv NULL Ns -terminated +array of alternate names for the host. +.It Va h_addrtype +The type of address being returned; usually +.Dv AF_INET . +.It Va h_length +The length, in bytes, of the address. +.It Va h_addr_list +A +.Dv NULL Ns -terminated +array of network addresses for the host. +Host addresses are returned in network byte order. +.It Va h_addr +The first address in +.Va h_addr_list ; +this is for backward compatibility. +.El +.Pp +When using the nameserver, +.Fn gethostbyname +and +.Fn gethostbyname2 +will search for the named host in the current domain and its parents +unless the name ends in a dot. +If the name contains no dot, and if the environment variable +.Dq Ev HOSTALIASES +contains the name of an alias file, the alias file will first be searched +for an alias matching the input name. +See +.Xr hostname 7 +for the domain search procedure and the alias file format. +.Pp +The +.Fn gethostbyname2 +function is an evolution of +.Fn gethostbyname +which is intended to allow lookups in address families other than +.Dv AF_INET , +for example +.Dv AF_INET6 . +.Pp +The +.Fn sethostent +function +may be used to request the use of a connected +.Tn TCP +socket for queries. +If the +.Fa stayopen +flag is non-zero, +this sets the option to send all queries to the name server using +.Tn TCP +and to retain the connection after each call to +.Fn gethostbyname , +.Fn gethostbyname2 +or +.Fn gethostbyaddr . +Otherwise, queries are performed using +.Tn UDP +datagrams. +.Pp +The +.Fn endhostent +function +closes the +.Tn TCP +connection. +.Pp +The +.Fn herror +function writes a message to the diagnostic output consisting of the +string argument +.Fa string , +the constant string +.Qq Li ":\ " , +and a message corresponding to the value of +.Va h_errno . +.Pp +The +.Fn hstrerror +function returns a string which is the message text corresponding to the +value of the +.Fa err +argument. +.Sh FILES +.Bl -tag -width /etc/nsswitch.conf -compact +.It Pa /etc/hosts +.It Pa /etc/nsswitch.conf +.It Pa /etc/resolv.conf +.El +.Sh EXAMPLES +Print out the hostname associated with a specific IP address: +.Bd -literal -offset indent +const char *ipstr = "127.0.0.1"; +struct in_addr ip; +struct hostent *hp; + +if (!inet_aton(ipstr, &ip)) + errx(1, "can't parse IP address %s", ipstr); + +if ((hp = gethostbyaddr((const void *)&ip, + sizeof ip, AF_INET)) == NULL) + errx(1, "no name associated with %s", ipstr); + +printf("name associated with %s is %s\en", ipstr, hp->h_name); +.Ed +.Sh DIAGNOSTICS +Error return status from +.Fn gethostbyname , +.Fn gethostbyname2 +and +.Fn gethostbyaddr +is indicated by return of a +.Dv NULL +pointer. +The external integer +.Va h_errno +may then be checked to see whether this is a temporary failure +or an invalid or unknown host. +The routine +.Fn herror +can be used to print an error message describing the failure. +If its argument +.Fa string +is +.Pf non- Dv NULL , +it is printed, followed by a colon and a space. +The error message is printed with a trailing newline. +.Pp +The variable +.Va h_errno +can have the following values: +.Bl -tag -width HOST_NOT_FOUND +.It Dv HOST_NOT_FOUND +No such host is known. +.It Dv TRY_AGAIN +This is usually a temporary error +and means that the local server did not receive +a response from an authoritative server. +A retry at some later time may succeed. +.It Dv NO_RECOVERY +Some unexpected server failure was encountered. +This is a non-recoverable error. +.It Dv NO_DATA +The requested name is valid but does not have an IP address; +this is not a temporary error. +This means that the name is known to the name server but there is no address +associated with this name. +Another type of request to the name server using this domain name +will result in an answer; +for example, a mail-forwarder may be registered for this domain. +.El +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr getnameinfo 3 , +.Xr inet_aton 3 , +.Xr resolver 3 , +.Xr hosts 5 , +.Xr hostname 7 , +.Xr named 8 +.Sh CAVEAT +The +.Fn gethostent +function +is defined, and +.Fn sethostent +and +.Fn endhostent +are redefined, +when +.Lb libc +is built to use only the routines to lookup in +.Pa /etc/hosts +and not the name server. +.Pp +The +.Fn gethostent +function +reads the next line of +.Pa /etc/hosts , +opening the file if necessary. +.Pp +The +.Fn sethostent +function +opens and/or rewinds the file +.Pa /etc/hosts . +If the +.Fa stayopen +argument is non-zero, +the file will not be closed after each call to +.Fn gethostbyname , +.Fn gethostbyname2 +or +.Fn gethostbyaddr . +.Pp +The +.Fn endhostent +function +closes the file. +.Sh HISTORY +The +.Fn herror +function appeared in +.Bx 4.3 . +The +.Fn endhostent , +.Fn gethostbyaddr , +.Fn gethostbyname , +.Fn gethostent , +and +.Fn sethostent +functions appeared in +.Bx 4.2 . +The +.Fn gethostbyname2 +function first appeared in +.Tn BIND +version 4.9.4. +.Sh BUGS +These functions use a thread-specific data storage; +if the data is needed for future use, it should be +copied before any subsequent calls overwrite it. +.Pp +Though these functions are thread-safe, +still it is recommended to use the +.Xr getaddrinfo 3 +family of functions, instead. +.Pp +Only the Internet +address format is currently understood. diff --git a/lib/libc/net/gethostbynis.c b/lib/libc/net/gethostbynis.c new file mode 100644 index 0000000..7921ced --- /dev/null +++ b/lib/libc/net/gethostbynis.c @@ -0,0 +1,348 @@ +/*- + * Copyright (c) 1994, Garrett Wollman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include <resolv.h> /* XXX */ +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif +#include "netdb_private.h" + +#ifdef YP +static int +_gethostbynis(const char *name, char *map, int af, struct hostent *he, + struct hostent_data *hed) +{ + char *p, *bp, *ep; + char *cp, **q; + char *result; + int resultlen, size, addrok = 0; + char ypbuf[YPMAXRECORD + 2]; + res_state statp; + + statp = __res_state(); + switch(af) { + case AF_INET: + size = NS_INADDRSZ; + break; + case AF_INET6: + size = NS_IN6ADDRSZ; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + + if (hed->yp_domain == (char *)NULL) + if (yp_get_default_domain (&hed->yp_domain)) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + + if (yp_match(hed->yp_domain, map, name, strlen(name), &result, + &resultlen)) { + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + return (-1); + } + + /* avoid potential memory leak */ + bcopy((char *)result, (char *)&ypbuf, resultlen); + ypbuf[resultlen] = '\0'; + free(result); + result = (char *)&ypbuf; + + if ((cp = index(result, '\n'))) + *cp = '\0'; + + cp = strpbrk(result, " \t"); + *cp++ = '\0'; + he->h_addr_list = hed->h_addr_ptrs; + he->h_addr = (char *)hed->host_addr; + switch (af) { + case AF_INET: + addrok = inet_aton(result, (struct in_addr *)hed->host_addr); + if (addrok != 1) + break; + if (statp->options & RES_USE_INET6) { + _map_v4v6_address((char *)hed->host_addr, + (char *)hed->host_addr); + af = AF_INET6; + size = NS_IN6ADDRSZ; + } + break; + case AF_INET6: + addrok = inet_pton(af, result, hed->host_addr); + break; + } + if (addrok != 1) { + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + return (-1); + } + he->h_addr_list[1] = NULL; + he->h_length = size; + he->h_addrtype = af; + while (*cp == ' ' || *cp == '\t') + cp++; + bp = hed->hostbuf; + ep = hed->hostbuf + sizeof hed->hostbuf; + he->h_name = bp; + q = he->h_aliases = hed->host_aliases; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + size = strlen(cp) + 1; + if (ep - bp < size) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + strlcpy(bp, cp, ep - bp); + bp += size; + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q >= &hed->host_aliases[_MAXALIASES - 1]) + break; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + size = strlen(cp) + 1; + if (ep - bp < size) + break; + strlcpy(bp, cp, ep - bp); + *q++ = bp; + bp += size; + cp = p; + } + *q = NULL; + return (0); +} + +static int +_gethostbynisname_r(const char *name, int af, struct hostent *he, + struct hostent_data *hed) +{ + char *map; + + switch (af) { + case AF_INET: + map = "hosts.byname"; + break; + default: + map = "ipnodes.byname"; + break; + } + return (_gethostbynis(name, map, af, he, hed)); +} + +static int +_gethostbynisaddr_r(const void *addr, socklen_t len, int af, + struct hostent *he, struct hostent_data *hed) +{ + char *map; + char numaddr[46]; + + switch (af) { + case AF_INET: + map = "hosts.byaddr"; + break; + default: + map = "ipnodes.byaddr"; + break; + } + if (inet_ntop(af, addr, numaddr, sizeof(numaddr)) == NULL) + return (-1); + return (_gethostbynis(numaddr, map, af, he, hed)); +} +#endif /* YP */ + +/* XXX _gethostbynisname/_gethostbynisaddr only used by getipnodeby*() */ +struct hostent * +_gethostbynisname(const char *name, int af) +{ +#ifdef YP + struct hostent *he; + struct hostent_data *hed; + u_long oresopt; + int error; + res_state statp; + + statp = __res_state(); + if ((he = __hostent_init()) == NULL || + (hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (NULL); + } + + oresopt = statp->options; + statp->options &= ~RES_USE_INET6; + error = _gethostbynisname_r(name, af, he, hed); + statp->options = oresopt; + return (error == 0) ? he : NULL; +#else + return (NULL); +#endif +} + +struct hostent * +_gethostbynisaddr(const void *addr, socklen_t len, int af) +{ +#ifdef YP + struct hostent *he; + struct hostent_data *hed; + u_long oresopt; + int error; + res_state statp; + + statp = __res_state(); + if ((he = __hostent_init()) == NULL || + (hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (NULL); + } + + oresopt = statp->options; + statp->options &= ~RES_USE_INET6; + error = _gethostbynisaddr_r(addr, len, af, he, hed); + statp->options = oresopt; + return (error == 0) ? he : NULL; +#else + return (NULL); +#endif +} + +int +_nis_gethostbyname(void *rval, void *cb_data, va_list ap) +{ +#ifdef YP + const char *name; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct hostent *hptr, he; + struct hostent_data *hed; + res_state statp; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + if (_gethostbynisname_r(name, af, &he, hed) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +#else + *((struct hostent **)rval) = NULL; + return (NS_UNAVAIL); +#endif +} + +int +_nis_gethostbyaddr(void *rval, void *cb_data, va_list ap) +{ +#ifdef YP + const void *addr; + socklen_t len; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct hostent *hptr, he; + struct hostent_data *hed; + res_state statp; + + addr = va_arg(ap, const void *); + len = va_arg(ap, socklen_t); + af = va_arg(ap, int); + hptr = va_arg(ap, struct hostent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + *((struct hostent **)rval) = NULL; + + statp = __res_state(); + if ((hed = __hostent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + if (_gethostbynisaddr_r(addr, len, af, &he, hed) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_hostent(&he, hptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct hostent **)rval) = hptr; + return (NS_SUCCESS); +#else + *((struct hostent **)rval) = NULL; + return (NS_UNAVAIL); +#endif +} diff --git a/lib/libc/net/gethostnamadr.c b/lib/libc/net/gethostnamadr.c new file mode 100644 index 0000000..7ff3500 --- /dev/null +++ b/lib/libc/net/gethostnamadr.c @@ -0,0 +1,725 @@ +/*- + * Copyright (c) 1994, Garrett Wollman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include "reentrant.h" +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include <arpa/nameser.h> /* XXX hack for _res */ +#include <resolv.h> /* XXX hack for _res */ +#include "un-namespace.h" +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif + +extern int _ht_gethostbyname(void *, void *, va_list); +extern int _dns_gethostbyname(void *, void *, va_list); +extern int _nis_gethostbyname(void *, void *, va_list); +extern int _ht_gethostbyaddr(void *, void *, va_list); +extern int _dns_gethostbyaddr(void *, void *, va_list); +extern int _nis_gethostbyaddr(void *, void *, va_list); + +static int gethostbyname_internal(const char *, int, struct hostent *, char *, + size_t, struct hostent **, int *, res_state); + +/* Host lookup order if nsswitch.conf is broken or nonexistant */ +static const ns_src default_src[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, + { 0 } +}; +#ifdef NS_CACHING +static int host_id_func(char *, size_t *, va_list, void *); +static int host_marshal_func(char *, size_t *, void *, va_list, void *); +static int host_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +NETDB_THREAD_ALLOC(hostent) +NETDB_THREAD_ALLOC(hostent_data) +NETDB_THREAD_ALLOC(hostdata) + +static void +hostent_free(void *ptr) +{ + free(ptr); +} + +static void +hostent_data_free(void *ptr) +{ + struct hostent_data *hed = ptr; + + if (hed == NULL) + return; + hed->stayopen = 0; + _endhosthtent(hed); + free(hed); +} + +static void +hostdata_free(void *ptr) +{ + free(ptr); +} + +int +__copy_hostent(struct hostent *he, struct hostent *hptr, char *buf, + size_t buflen) +{ + char *cp; + char **ptr; + int i, n; + int nptr, len; + + /* Find out the amount of space required to store the answer. */ + nptr = 2; /* NULL ptrs */ + len = (char *)ALIGN(buf) - buf; + for (i = 0; he->h_addr_list[i]; i++, nptr++) { + len += he->h_length; + } + for (i = 0; he->h_aliases[i]; i++, nptr++) { + len += strlen(he->h_aliases[i]) + 1; + } + len += strlen(he->h_name) + 1; + len += nptr * sizeof(char*); + + if (len > buflen) { + errno = ERANGE; + return (-1); + } + + /* copy address size and type */ + hptr->h_addrtype = he->h_addrtype; + n = hptr->h_length = he->h_length; + + ptr = (char **)ALIGN(buf); + cp = (char *)ALIGN(buf) + nptr * sizeof(char *); + + /* copy address list */ + hptr->h_addr_list = ptr; + for (i = 0; he->h_addr_list[i]; i++ , ptr++) { + memcpy(cp, he->h_addr_list[i], n); + hptr->h_addr_list[i] = cp; + cp += n; + } + hptr->h_addr_list[i] = NULL; + ptr++; + + /* copy official name */ + n = strlen(he->h_name) + 1; + strcpy(cp, he->h_name); + hptr->h_name = cp; + cp += n; + + /* copy aliases */ + hptr->h_aliases = ptr; + for (i = 0 ; he->h_aliases[i]; i++) { + n = strlen(he->h_aliases[i]) + 1; + strcpy(cp, he->h_aliases[i]); + hptr->h_aliases[i] = cp; + cp += n; + } + hptr->h_aliases[i] = NULL; + + return (0); +} + +#ifdef NS_CACHING +static int +host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 1; + char *str; + int len, type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + char *p; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + + size = strlen(str); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(int); + + memcpy(p, &type, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(int)); + p += sizeof(int); + + memcpy(p, str, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + ht = va_arg(ap, struct hostent *); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *str; + int len, type; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + size_t orig_buf_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + str = va_arg(ap, char *); + type = va_arg(ap, int); + break; + case nss_lt_id: + str = va_arg(ap, char *); + len = va_arg(ap, int); + type = va_arg(ap, int); + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ht = va_arg(ap, struct hostent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + if (orig_buf_size < + buffer_size - sizeof(struct hostent) - sizeof(char *)) { + errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct hostent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + +static int +fakeaddr(const char *name, int af, struct hostent *hp, char *buf, + size_t buflen, res_state statp) +{ + struct hostent_data *hed; + struct hostent he; + + if ((hed = __hostent_data_init()) == NULL) { + errno = ENOMEM; + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + + if ((af != AF_INET || + inet_aton(name, (struct in_addr *)hed->host_addr) != 1) && + inet_pton(af, name, hed->host_addr) != 1) { + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + return (-1); + } + strncpy(hed->hostbuf, name, MAXDNAME); + hed->hostbuf[MAXDNAME] = '\0'; + if (af == AF_INET && (statp->options & RES_USE_INET6) != 0U) { + _map_v4v6_address((char *)hed->host_addr, + (char *)hed->host_addr); + af = AF_INET6; + } + he.h_addrtype = af; + switch(af) { + case AF_INET: + he.h_length = NS_INADDRSZ; + break; + case AF_INET6: + he.h_length = NS_IN6ADDRSZ; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + he.h_name = hed->hostbuf; + he.h_aliases = hed->host_aliases; + hed->host_aliases[0] = NULL; + hed->h_addr_ptrs[0] = (char *)hed->host_addr; + hed->h_addr_ptrs[1] = NULL; + he.h_addr_list = hed->h_addr_ptrs; + RES_SET_H_ERRNO(statp, NETDB_SUCCESS); + return (__copy_hostent(&he, hp, buf, buflen)); +} + +int +gethostbyname_r(const char *name, struct hostent *he, char *buffer, + size_t buflen, struct hostent **result, int *h_errnop) +{ + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + if (statp->options & RES_USE_INET6) { + if (fakeaddr(name, AF_INET, he, buffer, buflen, statp) == 0) { + *result = he; + return (0); + } + if (gethostbyname_internal(name, AF_INET6, he, buffer, buflen, + result, h_errnop, statp) == 0) + return (0); + } + return (gethostbyname_internal(name, AF_INET, he, buffer, buflen, + result, h_errnop, statp)); +} + +int +gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer, + size_t buflen, struct hostent **result, int *h_errnop) +{ + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + return (-1); + } + return (gethostbyname_internal(name, af, he, buffer, buflen, result, + h_errnop, statp)); +} + +int +gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf, + size_t buflen, struct hostent **result, int *h_errnop, res_state statp) +{ + const char *cp; + int rval, ret_errno; + char abuf[MAXDNAME]; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_ht_gethostbyname, NULL) + { NSSRC_DNS, _dns_gethostbyname, NULL }, + NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + + switch (af) { + case AF_INET: + case AF_INET6: + break; + default: + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + errno = EAFNOSUPPORT; + return (-1); + } + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_query() since we are not the only + * function that looks up host names. + */ + if (!strchr(name, '.') && + (cp = res_hostalias(statp, name, abuf, sizeof abuf))) + name = cp; + + if (fakeaddr(name, af, hp, buf, buflen, statp) == 0) { + *result = hp; + return (0); + } + + rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, + "gethostbyname2_r", default_src, name, af, hp, buf, buflen, + &ret_errno, h_errnop); + + return ((rval == NS_SUCCESS) ? 0 : -1); +} + +int +gethostbyaddr_r(const void *addr, +#if __LONG_BIT == 64 + int len, +#else + socklen_t len, +#endif + int af, struct hostent *hp, char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + const u_char *uaddr = (const u_char *)addr; + const struct in6_addr *addr6; + socklen_t size; + int rval, ret_errno; + res_state statp; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + host_id_func, host_marshal_func, host_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_ht_gethostbyaddr, NULL) + { NSSRC_DNS, _dns_gethostbyaddr, NULL }, + NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + + if (af == AF_INET6 && len == NS_IN6ADDRSZ) { + addr6 = (const struct in6_addr *)addr; + if (IN6_IS_ADDR_LINKLOCAL(addr6)) { + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + *h_errnop = statp->res_h_errno; + return (-1); + } + if (IN6_IS_ADDR_V4MAPPED(addr6) || + IN6_IS_ADDR_V4COMPAT(addr6)) { + /* Unmap. */ + uaddr += NS_IN6ADDRSZ - NS_INADDRSZ; + af = AF_INET; + len = NS_INADDRSZ; + } + } + switch (af) { + case AF_INET: + size = NS_INADDRSZ; + break; + case AF_INET6: + size = NS_IN6ADDRSZ; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + if (size != len) { + errno = EINVAL; + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + + rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS, + "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen, + &ret_errno, h_errnop); + + return ((rval == NS_SUCCESS) ? 0 : -1); +} + +struct hostent * +gethostbyname(const char *name) +{ + struct hostdata *hd; + struct hostent *rval; + int ret_h_errno; + + if ((hd = __hostdata_init()) == NULL) + return (NULL); + if (gethostbyname_r(name, &hd->host, hd->data, sizeof(hd->data), &rval, + &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +struct hostent * +gethostbyname2(const char *name, int af) +{ + struct hostdata *hd; + struct hostent *rval; + int ret_h_errno; + + if ((hd = __hostdata_init()) == NULL) + return (NULL); + if (gethostbyname2_r(name, af, &hd->host, hd->data, sizeof(hd->data), + &rval, &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +struct hostent * +#if __LONG_BIT == 64 +gethostbyaddr(const void *addr, int len, int af) +#else +gethostbyaddr(const void *addr, socklen_t len, int af) +#endif +{ + struct hostdata *hd; + struct hostent *rval; + int ret_h_errno; + + if ((hd = __hostdata_init()) == NULL) + return (NULL); + if (gethostbyaddr_r(addr, len, af, &hd->host, hd->data, + sizeof(hd->data), &rval, &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +void +sethostent(int stayopen) +{ + struct hostent_data *hed; + + if ((hed = __hostent_data_init()) == NULL) + return; + _sethosthtent(stayopen, hed); + _sethostdnsent(stayopen); +} + +void +endhostent(void) +{ + struct hostent_data *hed; + + if ((hed = __hostent_data_init()) == NULL) + return; + _endhosthtent(hed); + _endhostdnsent(); +} diff --git a/lib/libc/net/getifaddrs.3 b/lib/libc/net/getifaddrs.3 new file mode 100644 index 0000000..a12cd00 --- /dev/null +++ b/lib/libc/net/getifaddrs.3 @@ -0,0 +1,167 @@ +.\" $KAME: getifaddrs.3,v 1.4 2000/05/17 14:13:14 itojun Exp $ +.\" BSDI getifaddrs.3,v 2.5 2000/02/23 14:51:59 dab Exp +.\" +.\" Copyright (c) 1995, 1999 +.\" Berkeley Software Design, Inc. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 12, 1995 +.Dt GETIFADDRS 3 +.Os +.Sh NAME +.Nm getifaddrs +.Nd get interface addresses +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In ifaddrs.h +.Ft int +.Fn getifaddrs "struct ifaddrs **ifap" +.Ft void +.Fn freeifaddrs "struct ifaddrs *ifp" +.Sh DESCRIPTION +The +.Fn getifaddrs +function stores a reference to a linked list of the network interfaces +on the local machine in the memory referenced by +.Fa ifap . +The list consists of +.Nm ifaddrs +structures, as defined in the include file +.In ifaddrs.h . +The +.Nm ifaddrs +structure contains at least the following entries: +.Bd -literal + struct ifaddrs *ifa_next; /* Pointer to next struct */ + char *ifa_name; /* Interface name */ + u_int ifa_flags; /* Interface flags */ + struct sockaddr *ifa_addr; /* Interface address */ + struct sockaddr *ifa_netmask; /* Interface netmask */ + struct sockaddr *ifa_broadaddr; /* Interface broadcast address */ + struct sockaddr *ifa_dstaddr; /* P2P interface destination */ + void *ifa_data; /* Address specific data */ +.Ed +.Pp +The +.Li ifa_next +field contains a pointer to the next structure on the list. +This field is +.Dv NULL +in last structure on the list. +.Pp +The +.Li ifa_name +field contains the interface name. +.Pp +The +.Li ifa_flags +field contains the interface flags, as set by +.Xr ifconfig 8 +utility. +.Pp +The +.Li ifa_addr +field references either the address of the interface or the link level +address of the interface, if one exists, otherwise it is NULL. +(The +.Li sa_family +field of the +.Li ifa_addr +field should be consulted to determine the format of the +.Li ifa_addr +address.) +.Pp +The +.Li ifa_netmask +field references the netmask associated with +.Li ifa_addr , +if one is set, otherwise it is NULL. +.Pp +The +.Li ifa_broadaddr +field, +which should only be referenced for non-P2P interfaces, +references the broadcast address associated with +.Li ifa_addr , +if one exists, otherwise it is NULL. +.Pp +The +.Li ifa_dstaddr +field references the destination address on a P2P interface, +if one exists, otherwise it is NULL. +.Pp +The +.Li ifa_data +field references address family specific data. +For +.Dv AF_LINK +addresses it contains a pointer to the +.Fa struct if_data +(as defined in include file +.In net/if.h ) +which contains various interface attributes and statistics. +For all other address families, it contains a pointer to the +.Fa struct ifa_data +(as defined in include file +.In net/if.h ) +which contains per-address interface statistics. +.Pp +The data returned by +.Fn getifaddrs +is dynamically allocated and should be freed using +.Fn freeifaddrs +when no longer needed. +.Sh RETURN VALUES +.Rv -std getifaddrs +.Sh ERRORS +The +.Fn getifaddrs +may fail and set +.Va errno +for any of the errors specified for the library routines +.Xr ioctl 2 , +.Xr socket 2 , +.Xr malloc 3 +or +.Xr sysctl 3 . +.Sh SEE ALSO +.Xr ioctl 2 , +.Xr socket 2 , +.Xr sysctl 3 , +.Xr networking 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +implementation first appeared in BSDi +.Bsx . +.Sh BUGS +If both +.In net/if.h +and +.In ifaddrs.h +are being included, +.In net/if.h +.Em must +be included before +.In ifaddrs.h . diff --git a/lib/libc/net/getifaddrs.c b/lib/libc/net/getifaddrs.c new file mode 100644 index 0000000..41ef3f4 --- /dev/null +++ b/lib/libc/net/getifaddrs.c @@ -0,0 +1,418 @@ +/* $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $ */ + +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp + */ +/* + * NOTE: SIOCGIFCONF case is not LP64 friendly. it also does not perform + * try-and-error for region size. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#ifdef NET_RT_IFLIST +#include <sys/param.h> +#include <net/route.h> +#include <sys/sysctl.h> +#include <net/if_dl.h> +#endif + +#include <errno.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> +#include "un-namespace.h" + +#if !defined(AF_LINK) +#define SA_LEN(sa) sizeof(struct sockaddr) +#endif + +#if !defined(SA_LEN) +#define SA_LEN(sa) (sa)->sa_len +#endif + +#define SALIGN (sizeof(long) - 1) +#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) + +#ifndef ALIGNBYTES +/* + * On systems with a routing socket, ALIGNBYTES should match the value + * that the kernel uses when building the messages. + */ +#define ALIGNBYTES XXX +#endif +#ifndef ALIGN +#define ALIGN(p) (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES) +#endif + +#if _BSDI_VERSION >= 199701 +#define HAVE_IFM_DATA +#endif + +#if _BSDI_VERSION >= 199802 +/* ifam_data is very specific to recent versions of bsdi */ +#define HAVE_IFAM_DATA +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +#define HAVE_IFM_DATA +#endif + +#define MAX_SYSCTL_TRY 5 + +int +getifaddrs(struct ifaddrs **pif) +{ + int icnt = 1; + int dcnt = 0; + int ncnt = 0; +#ifdef NET_RT_IFLIST + int ntry = 0; + int mib[6]; + size_t needed; + char *buf; + char *next; + struct ifaddrs *cif = 0; + char *p, *p0; + struct rt_msghdr *rtm; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + struct sockaddr_dl *dl; + struct sockaddr *sa; + struct ifaddrs *ifa, *ift; + u_short idx = 0; +#else /* NET_RT_IFLIST */ + char buf[1024]; + int m, sock; + struct ifconf ifc; + struct ifreq *ifr; + struct ifreq *lifr; +#endif /* NET_RT_IFLIST */ + int i; + size_t len, alen; + char *data; + char *names; + +#ifdef NET_RT_IFLIST + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + do { + /* + * We'll try to get addresses several times in case that + * the number of addresses is unexpectedly increased during + * the two sysctl calls. This should rarely happen, but we'll + * try to do our best for applications that assume success of + * this library (which should usually be the case). + * Portability note: since FreeBSD does not add margin of + * memory at the first sysctl, the possibility of failure on + * the second sysctl call is a bit higher. + */ + + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + return (-1); + if ((buf = malloc(needed)) == NULL) + return (-1); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + free(buf); + return (-1); + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)(void *)rtm; + if (ifm->ifm_addrs & RTA_IFP) { + idx = ifm->ifm_index; + ++icnt; + dl = (struct sockaddr_dl *)(void *)(ifm + 1); + dcnt += SA_RLEN((struct sockaddr *)(void*)dl) + + ALIGNBYTES; +#ifdef HAVE_IFM_DATA + dcnt += sizeof(ifm->ifm_data); +#endif /* HAVE_IFM_DATA */ + ncnt += dl->sdl_nlen + 1; + } else + idx = 0; + break; + + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)(void *)rtm; + if (idx && ifam->ifam_index != idx) + abort(); /* this cannot happen */ + +#define RTA_MASKS (RTA_NETMASK | RTA_IFA | RTA_BRD) + if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) + break; + p = (char *)(void *)(ifam + 1); + ++icnt; +#ifdef HAVE_IFAM_DATA + dcnt += sizeof(ifam->ifam_data) + ALIGNBYTES; +#endif /* HAVE_IFAM_DATA */ + /* Scan to look for length of address */ + alen = 0; + for (p0 = p, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + if (i == RTAX_IFA) { + alen = len; + break; + } + p += len; + } + for (p = p0, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + if (i == RTAX_NETMASK && SA_LEN(sa) == 0) + dcnt += alen; + else + dcnt += len; + p += len; + } + break; + } + } +#else /* NET_RT_IFLIST */ + ifc.ifc_buf = buf; + ifc.ifc_len = sizeof(buf); + + if ((sock = _socket(AF_INET, SOCK_STREAM, 0)) < 0) + return (-1); + i = _ioctl(sock, SIOCGIFCONF, (char *)&ifc); + _close(sock); + if (i < 0) + return (-1); + + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + + sa = &ifr->ifr_addr; + ++icnt; + dcnt += SA_RLEN(sa); + ncnt += sizeof(ifr->ifr_name) + 1; + + if (SA_LEN(sa) < sizeof(*sa)) + ifr = (struct ifreq *)(((char *)sa) + sizeof(*sa)); + else + ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); + } +#endif /* NET_RT_IFLIST */ + + if (icnt + dcnt + ncnt == 1) { + *pif = NULL; + free(buf); + return (0); + } + data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt); + if (data == NULL) { + free(buf); + return(-1); + } + + ifa = (struct ifaddrs *)(void *)data; + data += sizeof(struct ifaddrs) * icnt; + names = data + dcnt; + + memset(ifa, 0, sizeof(struct ifaddrs) * icnt); + ift = ifa; + +#ifdef NET_RT_IFLIST + idx = 0; + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)(void *)rtm; + if (ifm->ifm_addrs & RTA_IFP) { + idx = ifm->ifm_index; + dl = (struct sockaddr_dl *)(void *)(ifm + 1); + + cif = ift; + ift->ifa_name = names; + ift->ifa_flags = (int)ifm->ifm_flags; + memcpy(names, dl->sdl_data, + (size_t)dl->sdl_nlen); + names[dl->sdl_nlen] = 0; + names += dl->sdl_nlen + 1; + + ift->ifa_addr = (struct sockaddr *)(void *)data; + memcpy(data, dl, + (size_t)SA_LEN((struct sockaddr *) + (void *)dl)); + data += SA_RLEN((struct sockaddr *)(void *)dl); + +#ifdef HAVE_IFM_DATA + /* ifm_data needs to be aligned */ + ift->ifa_data = data = (void *)ALIGN(data); + memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data)); + data += sizeof(ifm->ifm_data); +#else /* HAVE_IFM_DATA */ + ift->ifa_data = NULL; +#endif /* HAVE_IFM_DATA */ + + ift = (ift->ifa_next = ift + 1); + } else + idx = 0; + break; + + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)(void *)rtm; + if (idx && ifam->ifam_index != idx) + abort(); /* this cannot happen */ + + if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0) + break; + ift->ifa_name = cif->ifa_name; + ift->ifa_flags = cif->ifa_flags; + ift->ifa_data = NULL; + p = (char *)(void *)(ifam + 1); + /* Scan to look for length of address */ + alen = 0; + for (p0 = p, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + if (i == RTAX_IFA) { + alen = len; + break; + } + p += len; + } + for (p = p0, i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifam->ifam_addrs & (1 << i)) + == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + switch (i) { + case RTAX_IFA: + ift->ifa_addr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_NETMASK: + ift->ifa_netmask = + (struct sockaddr *)(void *)data; + if (SA_LEN(sa) == 0) { + memset(data, 0, alen); + data += alen; + break; + } + memcpy(data, p, len); + data += len; + break; + + case RTAX_BRD: + ift->ifa_broadaddr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + } + p += len; + } + +#ifdef HAVE_IFAM_DATA + /* ifam_data needs to be aligned */ + ift->ifa_data = data = (void *)ALIGN(data); + memcpy(data, &ifam->ifam_data, sizeof(ifam->ifam_data)); + data += sizeof(ifam->ifam_data); +#endif /* HAVE_IFAM_DATA */ + + ift = (ift->ifa_next = ift + 1); + break; + } + } + + free(buf); +#else /* NET_RT_IFLIST */ + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + + ift->ifa_name = names; + names[sizeof(ifr->ifr_name)] = 0; + strncpy(names, ifr->ifr_name, sizeof(ifr->ifr_name)); + while (*names++) + ; + + ift->ifa_addr = (struct sockaddr *)data; + sa = &ifr->ifr_addr; + memcpy(data, sa, SA_LEN(sa)); + data += SA_RLEN(sa); + + ifr = (struct ifreq *)(((char *)sa) + SA_LEN(sa)); + ift = (ift->ifa_next = ift + 1); + } +#endif /* NET_RT_IFLIST */ + if (--ift >= ifa) { + ift->ifa_next = NULL; + *pif = ifa; + } else { + *pif = NULL; + free(ifa); + } + return (0); +} + +void +freeifaddrs(struct ifaddrs *ifp) +{ + + free(ifp); +} diff --git a/lib/libc/net/getifmaddrs.3 b/lib/libc/net/getifmaddrs.3 new file mode 100644 index 0000000..2d2a936 --- /dev/null +++ b/lib/libc/net/getifmaddrs.3 @@ -0,0 +1,116 @@ +.\" Copyright (c) 2003 Bruce M. Simpson. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bruce M. Simpson ``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 Bruce M. Simpson 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$ +.\" +.Dd November 14, 2003 +.Dt GETIFMADDRS 3 +.Os +.Sh NAME +.Nm getifmaddrs +.Nd get multicast group memberships +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In ifaddrs.h +.Ft int +.Fn getifmaddrs "struct ifmaddrs **ifmap" +.Ft void +.Fn freeifmaddrs "struct ifmaddrs *ifmp" +.Sh DESCRIPTION +The +.Fn getifmaddrs +function stores a reference to a linked list of the multicast memberships +on the local machine in the memory referenced by +.Fa ifmap . +The list consists of +.Vt ifmaddrs +structures, as defined in the include file +.In ifaddrs.h . +The +.Vt ifmaddrs +structure contains at least the following entries: +.Bd -literal + struct ifmaddrs *ifma_next; /* Pointer to next struct */ + struct sockaddr *ifma_name; /* Interface name (AF_LINK) */ + struct sockaddr *ifma_addr; /* Multicast address */ + struct sockaddr *ifma_lladdr; /* Link-layer translation, if any */ +.Ed +.Pp +The +.Va ifma_next +field contains a pointer to the next structure on the list. +This field is +.Dv NULL +in last structure on the list. +.Pp +The +.Va ifma_name +field references an +.Dv AF_LINK +address structure, containing the name of the +interface where the membership exists. +.Pp +The +.Va ifma_addr +references the address that this membership is for. +.Pp +The +.Va ifma_lladdr +field references a link-layer translation for the protocol-level address in +.Va ifma_addr , +if one is set, otherwise it is +.Dv NULL . +.Pp +The data returned by +.Fn getifmaddrs +is dynamically allocated and should be freed using +.Fn freeifmaddrs +when no longer needed. +.Sh RETURN VALUES +.Rv -std getifmaddrs +.Sh ERRORS +The +.Fn getifmaddrs +may fail and set +.Va errno +for any of the errors specified for the library routines +.Xr malloc 3 +or +.Xr sysctl 3 . +.Sh SEE ALSO +.Xr sysctl 3 , +.Xr networking 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Fn getifmaddrs +function first appeared in +.Fx 5.2 . +.Sh BUGS +If both +.In net/if.h +and +.In ifaddrs.h +are being included, +.In net/if.h +.Em must +be included before +.In ifaddrs.h . diff --git a/lib/libc/net/getifmaddrs.c b/lib/libc/net/getifmaddrs.c new file mode 100644 index 0000000..adbc4a5 --- /dev/null +++ b/lib/libc/net/getifmaddrs.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2003 Bruce M. Simpson. + * 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 Bruce M. Simpson. + * 4. Neither the name of Bruce M. Simpson nor the names of other + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRUCE M. SIMPSON AND AFFILIATES + * ``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 BRUCE M. SIMPSON OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <errno.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> +#include "un-namespace.h" + +#define SALIGN (sizeof(long) - 1) +#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ + (SALIGN + 1)) +#define MAX_SYSCTL_TRY 5 +#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) + +int +getifmaddrs(struct ifmaddrs **pif) +{ + int icnt = 1; + int dcnt = 0; + int ntry = 0; + size_t len; + size_t needed; + int mib[6]; + int i; + char *buf; + char *data; + char *next; + char *p; + struct ifma_msghdr *ifmam; + struct ifmaddrs *ifa, *ift; + struct rt_msghdr *rtm; + struct sockaddr *sa; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFMALIST; + mib[5] = 0; /* no flags */ + do { + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + return (-1); + if ((buf = malloc(needed)) == NULL) + return (-1); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + free(buf); + return (-1); + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_NEWMADDR: + ifmam = (struct ifma_msghdr *)(void *)rtm; + if ((ifmam->ifmam_addrs & RTA_IFA) == 0) + break; + icnt++; + p = (char *)(ifmam + 1); + for (i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifmam->ifmam_addrs & + (1 << i)) == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + dcnt += len; + p += len; + } + break; + } + } + + data = malloc(sizeof(struct ifmaddrs) * icnt + dcnt); + if (data == NULL) { + free(buf); + return (-1); + } + + ifa = (struct ifmaddrs *)(void *)data; + data += sizeof(struct ifmaddrs) * icnt; + + memset(ifa, 0, sizeof(struct ifmaddrs) * icnt); + ift = ifa; + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + + switch (rtm->rtm_type) { + case RTM_NEWMADDR: + ifmam = (struct ifma_msghdr *)(void *)rtm; + if ((ifmam->ifmam_addrs & RTA_IFA) == 0) + break; + + p = (char *)(ifmam + 1); + for (i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifmam->ifmam_addrs & + (1 << i)) == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + switch (i) { + case RTAX_GATEWAY: + ift->ifma_lladdr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_IFP: + ift->ifma_name = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_IFA: + ift->ifma_addr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + default: + data += len; + break; + } + p += len; + } + ift->ifma_next = ift + 1; + ift = ift->ifma_next; + break; + } + } + + free(buf); + + if (ift > ifa) { + ift--; + ift->ifma_next = NULL; + *pif = ifa; + } else { + *pif = NULL; + free(ifa); + } + return (0); +} + +void +freeifmaddrs(struct ifmaddrs *ifmp) +{ + + free(ifmp); +} diff --git a/lib/libc/net/getipnodebyname.3 b/lib/libc/net/getipnodebyname.3 new file mode 100644 index 0000000..dbef578 --- /dev/null +++ b/lib/libc/net/getipnodebyname.3 @@ -0,0 +1,478 @@ +.\" $KAME: getipnodebyname.3,v 1.6 2000/08/09 21:16:17 itojun Exp $ +.\" +.\" 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 +.\" $FreeBSD$ +.\" +.Dd August 6, 2004 +.Dt GETIPNODEBYNAME 3 +.Os +.\" +.Sh NAME +.Nm getipnodebyname , +.Nm getipnodebyaddr , +.Nm freehostent +.Nd nodename-to-address and address-to-nodename translation +.\" +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netdb.h +.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 +The +.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, +.Fa af , +and +.Fa flags , +for specifying address family and operation mode. +The additional arguments allow programmer to get address for a nodename, +for specific address family +(such as +.Dv AF_INET +or +.Dv AF_INET6 ) . +The functions also require an additional pointer argument, +.Fa 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 +.Fa name +argument can be either a node name or a numeric address +string +(i.e., a dotted-decimal IPv4 address or an IPv6 hex address). +The +.Fa af +argument specifies the address family, either +.Dv AF_INET +or +.Dv AF_INET6 . +The +.Fa 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 +(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 +.Fa flags +argument. +.Pp +A +.Fa flags +of +.Li 0 +implies a strict interpretation of the +.Fa af +argument: +.Bl -bullet +.It +If +.Fa flags +is 0 and +.Fa 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 +.Fa flags +is 0 and if +.Fa 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 +.Fa flags +argument, to modify the behavior of the function. +.Bl -bullet +.It +If the +.Dv AI_V4MAPPED +flag is specified along with an +.Fa 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 +.Li ( h_length +will be 16). +The +.Dv AI_V4MAPPED +flag is ignored unless +.Fa 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 +.Fa 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 +.Fa name +argument to be either a node name or a literal address string +(i.e., a dotted-decimal IPv4 address or an IPv6 hex address). +This saves applications from having to call +.Xr inet_pton 3 +to handle literal address strings. +When the +.Fa name +argument is a literal address string, +the +.Fa flags +argument is always ignored. +.Pp +There are four scenarios based on the type of literal address string +and the value of the +.Fa af +argument. +The two simple cases are when +.Fa name +is a dotted-decimal IPv4 address and +.Fa af +equals +.Dv AF_INET , +or when +.Fa name +is an IPv6 hex address and +.Fa af +equals +.Dv AF_INET6 . +The members of the +returned hostent structure are: +.Li h_name +points to a copy of the +.Fa name +argument, +.Li h_aliases +is a +.Dv NULL +pointer, +.Li h_addrtype +is a copy of the +.Fa af +argument, +.Li h_length +is either 4 +(for +.Dv AF_INET ) +or 16 +(for +.Dv AF_INET6 ) , +.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 +.Fa name +is a dotted-decimal IPv4 address and +.Fa 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 +.Fa name +is an IPv6 hex address and +.Fa af +equals +.Dv AF_INET . +The function's return value is a +.Dv NULL +pointer and the value pointed to by +.Fa error_num +equals +.Dv HOST_NOT_FOUND . +.Pp +The +.Fn getipnodebyaddr +function +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 +The +.Fn getipnodebyname +and +.Fn getipnodebyaddr +functions +dynamically allocate the structure to be returned to the caller. +The +.Fn freehostent +function +reclaims memory region allocated and returned by +.Fn getipnodebyname +or +.Fn getipnodebyaddr . +.\" +.Sh FILES +.Bl -tag -width /etc/nsswitch.conf -compact +.It Pa /etc/hosts +.It Pa /etc/nsswitch.conf +.It Pa /etc/resolv.conf +.El +.\" +.Sh DIAGNOSTICS +The +.Fn getipnodebyname +and +.Fn getipnodebyaddr +functions +returns +.Dv NULL +on errors. +The integer values pointed to by +.Fa 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 getaddrinfo 3 , +.Xr gethostbyaddr 3 , +.Xr gethostbyname 3 , +.Xr getnameinfo 3 , +.Xr hosts 5 , +.Xr nsswitch.conf 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 STANDARDS +The +.Fn getipnodebyname +and +.Fn getipnodebyaddr +functions +are documented in +.Dq Basic Socket Interface Extensions for IPv6 +(RFC2553). +.\" +.Sh HISTORY +The implementation first appeared in KAME advanced networking kit. +.\" +.Sh BUGS +The +.Fn getipnodebyname +and +.Fn getipnodebyaddr +functions +do not handle scoped IPv6 address properly. +If you use these functions, +your program will not be able to handle scoped IPv6 addresses. +For IPv6 address manipulation, +.Fn getaddrinfo 3 +and +.Fn getnameinfo 3 +are recommended. +.Pp +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..61479cf --- /dev/null +++ b/lib/libc/net/getnameinfo.3 @@ -0,0 +1,270 @@ +.\" $KAME: getnameinfo.3,v 1.37 2005/01/05 03:23:05 itojun Exp $ +.\" $OpenBSD: getnameinfo.3,v 1.36 2004/12/21 09:48:20 jmc Exp $ +.\" +.\" Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") +.\" Copyright (C) 2000, 2001 Internet Software Consortium. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +.\" PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 20, 2004 +.Dt GETNAMEINFO 3 +.Os +.Sh NAME +.Nm getnameinfo +.Nd socket address structure to hostname and service name +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <sys/socket.h> +.Fd #include <netdb.h> +.Ft int +.Fo getnameinfo +.Fa "const struct sockaddr *sa" "socklen_t salen" "char *host" +.Fa "size_t hostlen" "char *serv" "size_t servlen" "int flags" +.Fc +.Sh DESCRIPTION +The +.Fn getnameinfo +function is used to convert a +.Li sockaddr +structure to a pair of host name and service strings. +It is a replacement for and provides more flexibility than the +.Xr gethostbyaddr 3 +and +.Xr getservbyport 3 +functions and is the converse of the +.Xr getaddrinfo 3 +function. +.Pp +The +.Li sockaddr +structure +.Fa sa +should point to either a +.Li sockaddr_in +or +.Li sockaddr_in6 +structure (for IPv4 or IPv6 respectively) that is +.Fa salen +bytes long. +.Pp +The host and service names associated with +.Fa sa +are stored in +.Fa host +and +.Fa serv +which have length parameters +.Fa hostlen +and +.Fa servlen . +The maximum value for +.Fa hostlen +is +.Dv NI_MAXHOST +and +the maximum value for +.Fa servlen +is +.Dv NI_MAXSERV , +as defined by +.Aq Pa netdb.h . +If a length parameter is zero, no string will be stored. +Otherwise, enough space must be provided to store the +host name or service string plus a byte for the NUL terminator. +.Pp +The +.Fa flags +argument is formed by +.Tn OR Ns 'ing +the following values: +.Bl -tag -width "NI_NUMERICHOSTXX" +.It Dv NI_NOFQDN +A fully qualified domain name is not required for local hosts. +The local part of the fully qualified domain name is returned instead. +.It Dv NI_NUMERICHOST +Return the address in numeric form, as if calling +.Xr inet_ntop 3 , +instead of a host name. +.It Dv NI_NAMEREQD +A name is required. +If the host name cannot be found in DNS and this flag is set, +a non-zero error code is returned. +If the host name is not found and the flag is not set, the +address is returned in numeric form. +.It NI_NUMERICSERV +The service name is returned as a digit string representing the port number. +.It NI_DGRAM +Specifies that the service being looked up is a datagram +service, and causes +.Xr getservbyport 3 +to be called with a second argument of +.Dq udp +instead of its default of +.Dq tcp . +This is required for the few ports (512\-514) that have different services +for +.Tn UDP +and +.Tn TCP . +.El +.Pp +This implementation allows numeric IPv6 address notation with scope identifier, +as documented in chapter 11 of draft-ietf-ipv6-scoping-arch-02.txt. +IPv6 link-local address will appear as a string like +.Dq Li fe80::1%ne0 . +Refer to +.Xr getaddrinfo 3 +for more information. +.Sh RETURN VALUES +.Fn getnameinfo +returns zero on success or one of the error codes listed in +.Xr gai_strerror 3 +if an error occurs. +.Sh EXAMPLES +The following code tries to get a numeric host name, and service name, +for a given socket address. +Observe that there is no hardcoded reference to a particular address family. +.Bd -literal -offset indent +struct sockaddr *sa; /* input */ +char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + +if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf, + sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { + errx(1, "could not get numeric hostname"); + /*NOTREACHED*/ +} +printf("host=%s, serv=%s\en", hbuf, sbuf); +.Ed +.Pp +The following version checks if the socket address has a reverse address mapping: +.Bd -literal -offset indent +struct sockaddr *sa; /* input */ +char hbuf[NI_MAXHOST]; + +if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, + NI_NAMEREQD)) { + errx(1, "could not resolve hostname"); + /*NOTREACHED*/ +} +printf("host=%s\en", hbuf); +.Ed +.Sh SEE ALSO +.Xr gai_strerror 3 , +.Xr getaddrinfo 3 , +.Xr gethostbyaddr 3 , +.Xr getservbyport 3 , +.Xr inet_ntop 3 , +.Xr resolver 3 , +.Xr hosts 5 , +.Xr resolv.conf 5 , +.Xr services 5 , +.Xr hostname 7 , +.Xr named 8 +.Rs +.%A R. Gilligan +.%A S. Thomson +.%A J. Bound +.%A W. Stevens +.%T Basic Socket Interface Extensions for IPv6 +.%R RFC 2553 +.%D March 1999 +.Re +.Rs +.%A S. Deering +.%A B. Haberman +.%A T. Jinmei +.%A E. Nordmark +.%A B. Zill +.%T "IPv6 Scoped Address Architecture" +.%R internet draft +.%N draft-ietf-ipv6-scoping-arch-02.txt +.%O work in progress material +.Re +.Rs +.%A Craig Metz +.%T Protocol Independence Using the Sockets API +.%B "Proceedings of the freenix track: 2000 USENIX annual technical conference" +.%D June 2000 +.Re +.Sh STANDARDS +The +.Fn getnameinfo +function is defined by the +.St -p1003.1g-2000 +draft specification and documented in +.Tn "RFC 2553" , +.Dq Basic Socket Interface Extensions for IPv6 . +.Sh CAVEATS +.Fn getnameinfo +can return both numeric and FQDN forms of the address specified in +.Fa sa . +There is no return value that indicates whether the string returned in +.Fa host +is a result of binary to numeric-text translation (like +.Xr inet_ntop 3 ) , +or is the result of a DNS reverse lookup. +Because of this, malicious parties could set up a PTR record as follows: +.Bd -literal -offset indent +1.0.0.127.in-addr.arpa. IN PTR 10.1.1.1 +.Ed +.Pp +and trick the caller of +.Fn getnameinfo +into believing that +.Fa sa +is +.Li 10.1.1.1 +when it is actually +.Li 127.0.0.1 . +.Pp +To prevent such attacks, the use of +.Dv NI_NAMEREQD +is recommended when the result of +.Fn getnameinfo +is used +for access control purposes: +.Bd -literal -offset indent +struct sockaddr *sa; +socklen_t salen; +char addr[NI_MAXHOST]; +struct addrinfo hints, *res; +int error; + +error = getnameinfo(sa, salen, addr, sizeof(addr), + NULL, 0, NI_NAMEREQD); +if (error == 0) { + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(addr, "0", &hints, &res) == 0) { + /* malicious PTR record */ + freeaddrinfo(res); + printf("bogus PTR record\en"); + return -1; + } + /* addr is FQDN as a result of PTR lookup */ +} else { + /* addr is numeric string */ + error = getnameinfo(sa, salen, addr, sizeof(addr), + NULL, 0, NI_NUMERICHOST); +} +.Ed +.\".Pp +.\".Ox +.\"intentionally uses a different +.\".Dv NI_MAXHOST +.\"value from what +.\".Tn "RFC 2553" +.\"suggests, to avoid buffer length handling mistakes. diff --git a/lib/libc/net/getnameinfo.c b/lib/libc/net/getnameinfo.c new file mode 100644 index 0000000..b14cc27 --- /dev/null +++ b/lib/libc/net/getnameinfo.c @@ -0,0 +1,345 @@ +/* $KAME: getnameinfo.c,v 1.61 2002/06/27 09:25:47 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + * - RFC2553 says that we should raise error on short buffer. X/Open says + * we need to truncate the result. We obey RFC2553 (and X/Open should be + * modified). ipngwg rough consensus seems to follow RFC2553. + * - What is "local" in NI_FQDN? + * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. + * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if + * sin6_scope_id is filled - standardization status? + * XXX breaks backward compat for code that expects no scopeid. + * beware on merge. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <resolv.h> +#include <string.h> +#include <stddef.h> +#include <errno.h> + +static const struct afd { + int a_af; + size_t a_addrlen; + socklen_t 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; +}; + +#ifdef INET6 +static int ip6_parsenumeric(const struct sockaddr *, const char *, char *, + size_t, int); +static int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int); +#endif + +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, char *serv, size_t servlen, + int flags) +{ + const struct afd *afd; + struct servent *sp; + struct hostent *hp; + u_short port; + int family, i; + const char *addr; + u_int32_t v4a; + int h_error; + char numserv[512]; + char numaddr[512]; + + if (sa == NULL) + return EAI_FAIL; + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return EAI_FAMILY; + + found: + if (salen != afd->a_socklen) + return EAI_FAIL; + + /* network byte order */ + port = ((const struct sockinet *)sa)->si_port; + addr = (const char *)sa + afd->a_off; + + if (serv == NULL || servlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that serv == NULL OR + * servlen == 0 means that the caller does not want the result. + */ + } else { + if (flags & NI_NUMERICSERV) + sp = NULL; + else { + sp = getservbyport(port, + (flags & NI_DGRAM) ? "udp" : "tcp"); + } + if (sp) { + if (strlen(sp->s_name) + 1 > servlen) + return EAI_MEMORY; + strlcpy(serv, sp->s_name, servlen); + } else { + snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); + if (strlen(numserv) + 1 > servlen) + return EAI_MEMORY; + strlcpy(serv, numserv, servlen); + } + } + + switch (sa->sa_family) { + case AF_INET: + v4a = (u_int32_t) + ntohl(((const 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) + flags |= NI_NUMERICHOST; + break; +#ifdef INET6 + case AF_INET6: + { + const struct sockaddr_in6 *sin6; + sin6 = (const struct sockaddr_in6 *)sa; + switch (sin6->sin6_addr.s6_addr[0]) { + case 0x00: + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + ; + else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + ; + else + flags |= NI_NUMERICHOST; + break; + default: + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + flags |= NI_NUMERICHOST; + } + else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + break; + } + } + break; +#endif + } + if (host == NULL || hostlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that host == NULL or + * hostlen == 0 means that the caller does not want the result. + */ + } else if (flags & NI_NUMERICHOST) { + size_t numaddrlen; + + /* NUMERICHOST and NAMEREQD conflicts with each other */ + if (flags & NI_NAMEREQD) + return EAI_NONAME; + + switch(afd->a_af) { +#ifdef INET6 + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, flags)) != 0) + return(error); + break; + } +#endif + default: + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return EAI_SYSTEM; + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return EAI_MEMORY; + strlcpy(host, numaddr, hostlen); + break; + } + } else { + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); + + if (hp) { +#if 0 + /* + * commented out, since "for local host" is not + * implemented here - see RFC2553 p30 + */ + if (flags & NI_NOFQDN) { + char *p; + p = strchr(hp->h_name, '.'); + if (p) + *p = '\0'; + } +#endif + if (strlen(hp->h_name) + 1 > hostlen) { + freehostent(hp); + return EAI_MEMORY; + } + strlcpy(host, hp->h_name, hostlen); + freehostent(hp); + } else { + if (flags & NI_NAMEREQD) + return EAI_NONAME; + switch(afd->a_af) { +#ifdef INET6 + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, + flags)) != 0) + return(error); + break; + } +#endif + default: + if (inet_ntop(afd->a_af, addr, host, + hostlen) == NULL) + return EAI_SYSTEM; + break; + } + } + } + return(0); +} + +#ifdef INET6 +static int +ip6_parsenumeric(const struct sockaddr *sa, const char *addr, + char *host, size_t hostlen, int flags) +{ + size_t numaddrlen; + char numaddr[512]; + + if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) + return EAI_SYSTEM; + + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return EAI_MEMORY; + strlcpy(host, numaddr, hostlen); + + if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { + char zonebuf[MAXHOSTNAMELEN]; + int zonelen; + + zonelen = ip6_sa2str( + (const struct sockaddr_in6 *)(const void *)sa, + zonebuf, sizeof(zonebuf), flags); + if (zonelen < 0) + return EAI_MEMORY; + if (zonelen + 1 + numaddrlen + 1 > hostlen) + return EAI_MEMORY; + + /* construct <numeric-addr><delim><zoneid> */ + memcpy(host + numaddrlen + 1, zonebuf, + (size_t)zonelen); + host[numaddrlen] = SCOPE_DELIMITER; + host[numaddrlen + 1 + zonelen] = '\0'; + } + + return 0; +} + +/* ARGSUSED */ +static int +ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, size_t bufsiz, int flags) +{ + unsigned int ifindex; + const struct in6_addr *a6; + int n; + + ifindex = (unsigned int)sa6->sin6_scope_id; + a6 = &sa6->sin6_addr; + +#ifdef NI_NUMERICSCOPE + if ((flags & NI_NUMERICSCOPE) != 0) { + n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); + if (n < 0 || n >= bufsiz) + return -1; + else + return n; + } +#endif + + /* if_indextoname() does not take buffer size. not a good api... */ + if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || + IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) { + char *p = if_indextoname(ifindex, buf); + if (p) { + return(strlen(p)); + } + } + + /* last resort */ + n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); + if (n < 0 || (size_t)n >= bufsiz) + return -1; + else + return n; +} +#endif /* INET6 */ diff --git a/lib/libc/net/getnetbydns.c b/lib/libc/net/getnetbydns.c new file mode 100644 index 0000000..23c9c45 --- /dev/null +++ b/lib/libc/net/getnetbydns.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 1985, 1988, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ +/* Portions Copyright (c) 1993 Carlos Leandro and Rui Salgueiro + * Dep. Matematica Universidade de Coimbra, Portugal, Europe + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <netdb.h> +#include <resolv.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <stdarg.h> +#include <nsswitch.h> + +#include "netdb_private.h" +#include "res_config.h" + +#define BYADDR 0 +#define BYNAME 1 + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +typedef union { + long al; + char ac; +} align; + +/* + * Reverse the order of first four dotted entries of in. + * Out must contain space for at least strlen(in) characters. + * The result does not include any leading 0s of in. + */ +static void +ipreverse(char *in, char *out) +{ + char *pos[4]; + int len[4]; + char *p, *start; + int i = 0; + int leading = 1; + + /* Fill-in element positions and lengths: pos[], len[]. */ + start = p = in; + for (;;) { + if (*p == '.' || *p == '\0') { + /* Leading 0? */ + if (leading && p - start == 1 && *start == '0') + len[i] = 0; + else { + len[i] = p - start; + leading = 0; + } + pos[i] = start; + start = p + 1; + i++; + } + if (i == 4) + break; + if (*p == 0) { + for (; i < 4; i++) { + pos[i] = p; + len[i] = 0; + } + break; + } + p++; + } + + /* Copy the entries in reverse order */ + p = out; + leading = 1; + for (i = 3; i >= 0; i--) { + memcpy(p, pos[i], len[i]); + if (len[i]) + leading = 0; + p += len[i]; + /* Need a . separator? */ + if (!leading && i > 0 && len[i - 1]) + *p++ = '.'; + } + *p = '\0'; +} + +static int +getnetanswer(querybuf *answer, int anslen, int net_i, struct netent *ne, + struct netent_data *ned, res_state statp) +{ + + HEADER *hp; + u_char *cp; + int n; + u_char *eom; + int type, class, ancount, qdcount, haveanswer; + char aux[MAXHOSTNAMELEN]; + char ans[MAXHOSTNAMELEN]; + char *in, *bp, *ep, **ap; + + /* + * find first satisfactory answer + * + * answer --> +------------+ ( MESSAGE ) + * | Header | + * +------------+ + * | Question | the question for the name server + * +------------+ + * | Answer | RRs answering the question + * +------------+ + * | Authority | RRs pointing toward an authority + * | Additional | RRs holding additional information + * +------------+ + */ + eom = answer->buf + anslen; + hp = &answer->hdr; + ancount = ntohs(hp->ancount); /* #/records in the answer section */ + qdcount = ntohs(hp->qdcount); /* #/entries in the question section */ + bp = ned->netbuf; + ep = ned->netbuf + sizeof(ned->netbuf); + cp = answer->buf + HFIXEDSZ; + if (!qdcount) { + if (hp->aa) + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + else + RES_SET_H_ERRNO(statp, TRY_AGAIN); + return (-1); + } + while (qdcount-- > 0) + cp += __dn_skipname(cp, eom) + QFIXEDSZ; + ap = ned->net_aliases; + *ap = NULL; + ne->n_aliases = ned->net_aliases; + haveanswer = 0; + while (--ancount >= 0 && cp < eom) { + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !res_dnok(bp)) + break; + cp += n; + ans[0] = '\0'; + (void)strncpy(&ans[0], bp, sizeof(ans) - 1); + ans[sizeof(ans) - 1] = '\0'; + GETSHORT(type, cp); + GETSHORT(class, cp); + cp += INT32SZ; /* TTL */ + GETSHORT(n, cp); + if (class == C_IN && type == T_PTR) { + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !res_hnok(bp)) { + cp += n; + return (-1); + } + cp += n; + *ap++ = bp; + n = strlen(bp) + 1; + bp += n; + ne->n_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC; + haveanswer++; + } + } + if (haveanswer) { + *ap = NULL; + switch (net_i) { + case BYADDR: + ne->n_name = *ne->n_aliases; + ne->n_net = 0L; + break; + case BYNAME: + in = *ne->n_aliases; + n = strlen(ans) + 1; + if (ep - bp < n) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + errno = ENOBUFS; + return (-1); + } + strlcpy(bp, ans, ep - bp); + ne->n_name = bp; + if (strlen(in) + 1 > sizeof(aux)) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + errno = ENOBUFS; + return (-1); + } + ipreverse(in, aux); + ne->n_net = inet_network(aux); + break; + } + ne->n_aliases++; + return (0); + } + RES_SET_H_ERRNO(statp, TRY_AGAIN); + return (-1); +} + +int +_dns_getnetbyaddr(void *rval, void *cb_data, va_list ap) +{ + uint32_t net; + int net_type; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + unsigned int netbr[4]; + int nn, anslen, error; + querybuf *buf; + char qbuf[MAXDNAME]; + uint32_t net2; + res_state statp; + + net = va_arg(ap, uint32_t); + net_type = va_arg(ap, int); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + *((struct netent **)rval) = NULL; + + if (net_type != AF_INET) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + for (nn = 4, net2 = net; net2; net2 >>= 8) + netbr[--nn] = net2 & 0xff; + switch (nn) { + case 3: /* Class A */ + sprintf(qbuf, "0.0.0.%u.in-addr.arpa", netbr[3]); + break; + case 2: /* Class B */ + sprintf(qbuf, "0.0.%u.%u.in-addr.arpa", netbr[3], netbr[2]); + break; + case 1: /* Class C */ + sprintf(qbuf, "0.%u.%u.%u.in-addr.arpa", netbr[3], netbr[2], + netbr[1]); + break; + case 0: /* Class D - E */ + sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", netbr[3], netbr[2], + netbr[1], netbr[0]); + break; + } + if ((buf = malloc(sizeof(*buf))) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + anslen = res_nquery(statp, qbuf, C_IN, T_PTR, (u_char *)buf, + sizeof(*buf)); + if (anslen < 0) { + free(buf); +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf("res_nsearch failed\n"); +#endif + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } else if (anslen > sizeof(*buf)) { + free(buf); +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf("res_nsearch static buffer too small\n"); +#endif + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + error = getnetanswer(buf, anslen, BYADDR, &ne, ned, statp); + free(buf); + if (error == 0) { + /* Strip trailing zeros */ + while ((net & 0xff) == 0 && net != 0) + net >>= 8; + ne.n_net = net; + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); + } + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); +} + +int +_dns_getnetbyname(void *rval, void *cb_data, va_list ap) +{ + const char *net; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + int anslen, error; + querybuf *buf; + char qbuf[MAXDNAME]; + res_state statp; + + net = va_arg(ap, const char *); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + if ((buf = malloc(sizeof(*buf))) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + + *((struct netent **)rval) = NULL; + + strncpy(qbuf, net, sizeof(qbuf) - 1); + qbuf[sizeof(qbuf) - 1] = '\0'; + anslen = res_nsearch(statp, qbuf, C_IN, T_PTR, (u_char *)buf, + sizeof(*buf)); + if (anslen < 0) { + free(buf); +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf("res_nsearch failed\n"); +#endif + return (NS_UNAVAIL); + } else if (anslen > sizeof(*buf)) { + free(buf); +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf("res_search static buffer too small\n"); +#endif + return (NS_UNAVAIL); + } + error = getnetanswer(buf, anslen, BYNAME, &ne, ned, statp); + free(buf); + if (error != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); +} + +void +_setnetdnsent(int stayopen) +{ + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) + return; + if (stayopen) + statp->options |= RES_STAYOPEN | RES_USEVC; +} + +void +_endnetdnsent() +{ + res_state statp; + + statp = __res_state(); + statp->options &= ~(RES_STAYOPEN | RES_USEVC); + res_nclose(statp); +} diff --git a/lib/libc/net/getnetbyht.c b/lib/libc/net/getnetbyht.c new file mode 100644 index 0000000..580345a --- /dev/null +++ b/lib/libc/net/getnetbyht.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1983, 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. + */ + +/* Portions Copyright (c) 1993 Carlos Leandro and Rui Salgueiro + * Dep. Matematica Universidade de Coimbra, Portugal, Europe + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * from getnetent.c 1.1 (Coimbra) 93/06/02 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getnetent.c 8.1 (Berkeley) 6/4/93"; +static char orig_rcsid[] = "From: Id: getnetent.c,v 8.4 1997/06/01 20:34:37 vixie Exp"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include "netdb_private.h" + +void +_setnethtent(int f, struct netent_data *ned) +{ + + if (ned->netf == NULL) + ned->netf = fopen(_PATH_NETWORKS, "r"); + else + rewind(ned->netf); + ned->stayopen |= f; +} + +void +_endnethtent(struct netent_data *ned) +{ + + if (ned->netf) { + fclose(ned->netf); + ned->netf = NULL; + } + ned->stayopen = 0; +} + +static int +getnetent_p(struct netent *ne, struct netent_data *ned) +{ + char *p, *bp, *ep; + char *cp, **q; + int len; + char line[BUFSIZ + 1]; + + if (ned->netf == NULL && + (ned->netf = fopen(_PATH_NETWORKS, "r")) == NULL) + return (-1); +again: + p = fgets(line, sizeof line, ned->netf); + if (p == NULL) + return (-1); + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + bp = ned->netbuf; + ep = ned->netbuf + sizeof ned->netbuf; + ne->n_name = bp; + cp = strpbrk(p, " \t"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + len = strlen(p) + 1; + if (ep - bp < len) { + RES_SET_H_ERRNO(__res_state(), NO_RECOVERY); + return (-1); + } + strlcpy(bp, p, ep - bp); + bp += len; + while (*cp == ' ' || *cp == '\t') + cp++; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + ne->n_net = inet_network(cp); + ne->n_addrtype = AF_INET; + q = ne->n_aliases = ned->net_aliases; + if (p != NULL) { + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q >= &ned->net_aliases[_MAXALIASES - 1]) + break; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + len = strlen(cp) + 1; + if (ep - bp < len) + break; + strlcpy(bp, cp, ep - bp); + *q++ = bp; + bp += len; + cp = p; + } + } + *q = NULL; + return (0); +} + +int +getnetent_r(struct netent *nptr, char *buffer, size_t buflen, + struct netent **result, int *h_errnop) +{ + struct netent_data *ned; + struct netent ne; + res_state statp; + + statp = __res_state(); + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (-1); + } + if (getnetent_p(&ne, ned) != 0) + return (-1); + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) + return (-1); + *result = nptr; + return (0); +} + +struct netent * +getnetent(void) +{ + struct netdata *nd; + struct netent *rval; + int ret_h_errno; + + if ((nd = __netdata_init()) == NULL) + return (NULL); + if (getnetent_r(&nd->net, nd->data, sizeof(nd->data), &rval, + &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +int +_ht_getnetbyname(void *rval, void *cb_data, va_list ap) +{ + const char *name; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + char **cp; + res_state statp; + int error; + + name = va_arg(ap, const char *); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + _setnethtent(ned->stayopen, ned); + while ((error = getnetent_p(&ne, ned)) == 0) { + if (strcasecmp(ne.n_name, name) == 0) + break; + for (cp = ne.n_aliases; *cp != 0; cp++) + if (strcasecmp(*cp, name) == 0) + goto found; + } +found: + if (!ned->stayopen) + _endnethtent(ned); + if (error != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); +} + +int +_ht_getnetbyaddr(void *rval, void *cb_data, va_list ap) +{ + uint32_t net; + int type; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + res_state statp; + int error; + + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + _setnethtent(ned->stayopen, ned); + while ((error = getnetent_p(&ne, ned)) == 0) + if (ne.n_addrtype == type && ne.n_net == net) + break; + if (!ned->stayopen) + _endnethtent(ned); + if (error != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); +} diff --git a/lib/libc/net/getnetbynis.c b/lib/libc/net/getnetbynis.c new file mode 100644 index 0000000..d00c7f2 --- /dev/null +++ b/lib/libc/net/getnetbynis.c @@ -0,0 +1,255 @@ +/*- + * Copyright (c) 1994, Garrett Wollman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include <arpa/nameser.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif +#include "netdb_private.h" + +#ifdef YP +static int +_getnetbynis(const char *name, char *map, int af, struct netent *ne, + struct netent_data *ned) +{ + char *p, *bp, *ep; + char *cp, **q; + char *result; + int resultlen, len; + char ypbuf[YPMAXRECORD + 2]; + + switch(af) { + case AF_INET: + break; + default: + case AF_INET6: + errno = EAFNOSUPPORT; + return (-1); + } + + if (ned->yp_domain == (char *)NULL) + if (yp_get_default_domain (&ned->yp_domain)) + return (-1); + + if (yp_match(ned->yp_domain, map, name, strlen(name), &result, + &resultlen)) + return (-1); + + bcopy((char *)result, (char *)&ypbuf, resultlen); + ypbuf[resultlen] = '\0'; + free(result); + result = (char *)&ypbuf; + + if ((cp = index(result, '\n'))) + *cp = '\0'; + + cp = strpbrk(result, " \t"); + *cp++ = '\0'; + bp = ned->netbuf; + ep = ned->netbuf + sizeof ned->netbuf; + len = strlen(result) + 1; + if (ep - bp < len) { + RES_SET_H_ERRNO(__res_state(), NO_RECOVERY); + return (-1); + } + strlcpy(bp, result, ep - bp); + ne->n_name = bp; + bp += len; + + while (*cp == ' ' || *cp == '\t') + cp++; + + ne->n_net = inet_network(cp); + ne->n_addrtype = AF_INET; + + q = ne->n_aliases = ned->net_aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q > &ned->net_aliases[_MAXALIASES - 1]) + break; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + len = strlen(cp) + 1; + if (ep - bp < len) + break; + strlcpy(bp, cp, ep - bp); + *q++ = bp; + bp += len; + cp = p; + } + *q = NULL; + return (0); +} +#endif /* YP */ + +int +_nis_getnetbyname(void *rval, void *cb_data, va_list ap) +{ +#ifdef YP + const char *name; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + res_state statp; + + name = va_arg(ap, const char *); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + if (_getnetbynis(name, "networks.byname", AF_INET, &ne, ned) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); +#else + return (NS_UNAVAIL); +#endif + +} + +int +_nis_getnetbyaddr(void *rval, void *cb_data, va_list ap) +{ +#ifdef YP + uint32_t addr; + int af; + char *buffer; + size_t buflen; + int *errnop, *h_errnop; + struct netent *nptr, ne; + struct netent_data *ned; + char *str, *cp; + uint32_t net2; + int nn; + unsigned int netbr[4]; + char buf[MAXDNAME]; + res_state statp; + + addr = va_arg(ap, uint32_t); + af = va_arg(ap, int); + nptr = va_arg(ap, struct netent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + h_errnop = va_arg(ap, int *); + + statp = __res_state(); + if ((ned = __netent_data_init()) == NULL) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + return (NS_UNAVAIL); + } + + if (af != AF_INET) { + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + *h_errnop = statp->res_h_errno; + errno = EAFNOSUPPORT; + return (NS_UNAVAIL); + } + + for (nn = 4, net2 = addr; net2; net2 >>= 8) { + netbr[--nn] = net2 & 0xff; + } + + switch (nn) { + case 3: /* Class A */ + sprintf(buf, "%u", netbr[3]); + break; + case 2: /* Class B */ + sprintf(buf, "%u.%u", netbr[2], netbr[3]); + break; + case 1: /* Class C */ + sprintf(buf, "%u.%u.%u", netbr[1], netbr[2], netbr[3]); + break; + case 0: /* Class D - E */ + sprintf(buf, "%u.%u.%u.%u", netbr[0], netbr[1], + netbr[2], netbr[3]); + break; + } + + str = (char *)&buf; + cp = str + (strlen(str) - 2); + + while(!strcmp(cp, ".0")) { + *cp = '\0'; + cp = str + (strlen(str) - 2); + } + + if (_getnetbynis(str, "networks.byaddr", af, &ne, ned) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + if (__copy_netent(&ne, nptr, buffer, buflen) != 0) { + *h_errnop = statp->res_h_errno; + return (NS_NOTFOUND); + } + *((struct netent **)rval) = nptr; + return (NS_SUCCESS); +#else + return (NS_UNAVAIL); +#endif /* YP */ +} diff --git a/lib/libc/net/getnetent.3 b/lib/libc/net/getnetent.3 new file mode 100644 index 0000000..3c329a6 --- /dev/null +++ b/lib/libc/net/getnetent.3 @@ -0,0 +1,176 @@ +.\" Copyright (c) 1983, 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. +.\" +.\" @(#)getnetent.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd June 4, 1993 +.Dt GETNETENT 3 +.Os +.Sh NAME +.Nm getnetent , +.Nm getnetbyaddr , +.Nm getnetbyname , +.Nm setnetent , +.Nm endnetent +.Nd get network entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In netdb.h +.Ft struct netent * +.Fn getnetent void +.Ft struct netent * +.Fn getnetbyname "const char *name" +.Ft struct netent * +.Fn getnetbyaddr "uint32_t net" "int type" +.Ft void +.Fn setnetent "int stayopen" +.Ft void +.Fn endnetent void +.Sh DESCRIPTION +The +.Fn getnetent , +.Fn getnetbyname , +and +.Fn getnetbyaddr +functions +each return a pointer to an object with the +following structure describing an internet network. +This structure contains either the information obtained +from the nameserver, +.Xr named 8 , +broken-out fields of a line in the network data base +.Pa /etc/networks , +or entries supplied by the +.Xr yp 8 +system. +The order of the lookups is controlled by the +`networks' entry in +.Xr nsswitch.conf 5 . +.Pp +.Bd -literal -offset indent +struct netent { + char *n_name; /* official name of net */ + char **n_aliases; /* alias list */ + int n_addrtype; /* net number type */ + uint32_t n_net; /* net number */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width n_addrtype +.It Fa n_name +The official name of the network. +.It Fa n_aliases +A zero terminated list of alternate names for the network. +.It Fa n_addrtype +The type of the network number returned; currently only AF_INET. +.It Fa n_net +The network number. +Network numbers are returned in machine byte +order. +.El +.Pp +The +.Fn getnetent +function +reads the next line of the file, opening the file if necessary. +.Pp +The +.Fn setnetent +function +opens and rewinds the file. +If the +.Fa stayopen +flag is non-zero, +the net data base will not be closed after each call to +.Fn getnetbyname +or +.Fn getnetbyaddr . +.Pp +The +.Fn endnetent +function +closes the file. +.Pp +The +.Fn getnetbyname +function +and +.Fn getnetbyaddr +sequentially search from the beginning +of the file until a matching +net name or +net address and type is found, +or until +.Dv EOF +is encountered. +The +.Fa type +argument +must be +.Dv AF_INET . +Network numbers are supplied in host order. +.Sh FILES +.Bl -tag -width /etc/nsswitch.conf -compact +.It Pa /etc/networks +.It Pa /etc/nsswitch.conf +.It Pa /etc/resolv.conf +.El +.Sh DIAGNOSTICS +Null pointer +(0) returned on +.Dv EOF +or error. +.Sh SEE ALSO +.Xr networks 5 +.Pp +.%T RFC 1101 +.Sh HISTORY +The +.Fn getnetent , +.Fn getnetbyaddr , +.Fn getnetbyname , +.Fn setnetent , +and +.Fn endnetent +functions appeared in +.Bx 4.2 . +.Sh BUGS +The data space used by +these functions is thread-specific; if future use requires the data, it should be +copied before any subsequent calls to these functions overwrite it. +Only Internet network +numbers are currently understood. +Expecting network numbers to fit +in no more than 32 bits is probably +naive. diff --git a/lib/libc/net/getnetnamadr.c b/lib/libc/net/getnetnamadr.c new file mode 100644 index 0000000..ec7e94b --- /dev/null +++ b/lib/libc/net/getnetnamadr.c @@ -0,0 +1,450 @@ +/*- + * Copyright (c) 1994, Garrett Wollman + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include "reentrant.h" +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include "un-namespace.h" +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif + +extern int _ht_getnetbyname(void *, void *, va_list); +extern int _dns_getnetbyname(void *, void *, va_list); +extern int _nis_getnetbyname(void *, void *, va_list); +extern int _ht_getnetbyaddr(void *, void *, va_list); +extern int _dns_getnetbyaddr(void *, void *, va_list); +extern int _nis_getnetbyaddr(void *, void *, va_list); + +/* Network lookup order if nsswitch.conf is broken or nonexistant */ +static const ns_src default_src[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, + { 0 } +}; + +NETDB_THREAD_ALLOC(netent_data) +NETDB_THREAD_ALLOC(netdata) + +#ifdef NS_CACHING +static int +net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + + sizeof(uint32_t) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &net, + sizeof(uint32_t)); + memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t), + &type, sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +static int +net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + + struct netent new_ne; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *); + if (ne->n_name != NULL) + desired_size += strlen(ne->n_name) + 1; + + if (ne->n_aliases != NULL) { + aliases_size = 0; + for (alias = ne->n_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ne, ne, sizeof(struct netent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct netent) + sizeof(char *); + memcpy(buffer + sizeof(struct netent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ne.n_name != NULL) { + size = strlen(new_ne.n_name); + memcpy(p, new_ne.n_name, size); + new_ne.n_name = p; + p += size + 1; + } + + if (new_ne.n_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size); + new_ne.n_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_ne.n_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_ne, sizeof(struct netent)); + return (NS_SUCCESS); +} + +static int +net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + uint32_t net; + int type; + struct netent *ne; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + net = va_arg(ap, uint32_t); + type = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + ne = va_arg(ap, struct netent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct netent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(ne, buffer, sizeof(struct netent)); + memcpy(&p, buffer + sizeof(struct netent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct netent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *); + if (ne->n_aliases != NULL) { + NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **); + + for (alias = ne->n_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct netent **)retval) = ne; + + return (NS_SUCCESS); +} +#endif /* NS_CACHING */ + +static void +netent_data_free(void *ptr) +{ + struct netent_data *ned = ptr; + + if (ned == NULL) + return; + ned->stayopen = 0; + _endnethtent(ned); + free(ned); +} + +static void +netdata_free(void *ptr) +{ + free(ptr); +} + +int +__copy_netent(struct netent *ne, struct netent *nptr, char *buf, size_t buflen) +{ + char *cp; + int i, n; + int numptr, len; + + /* Find out the amount of space required to store the answer. */ + numptr = 1; /* NULL ptr */ + len = (char *)ALIGN(buf) - buf; + for (i = 0; ne->n_aliases[i]; i++, numptr++) { + len += strlen(ne->n_aliases[i]) + 1; + } + len += strlen(ne->n_name) + 1; + len += numptr * sizeof(char*); + + if (len > (int)buflen) { + errno = ERANGE; + return (-1); + } + + /* copy net value and type */ + nptr->n_addrtype = ne->n_addrtype; + nptr->n_net = ne->n_net; + + cp = (char *)ALIGN(buf) + numptr * sizeof(char *); + + /* copy official name */ + n = strlen(ne->n_name) + 1; + strcpy(cp, ne->n_name); + nptr->n_name = cp; + cp += n; + + /* copy aliases */ + nptr->n_aliases = (char **)ALIGN(buf); + for (i = 0 ; ne->n_aliases[i]; i++) { + n = strlen(ne->n_aliases[i]) + 1; + strcpy(cp, ne->n_aliases[i]); + nptr->n_aliases[i] = cp; + cp += n; + } + nptr->n_aliases[i] = NULL; + + return (0); +} + +int +getnetbyname_r(const char *name, struct netent *ne, char *buffer, + size_t buflen, struct netent **result, int *h_errorp) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_name, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_ht_getnetbyname, NULL) + { NSSRC_DNS, _dns_getnetbyname, NULL }, + NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + int rval, ret_errno; + + rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, + "getnetbyname_r", default_src, name, ne, buffer, buflen, + &ret_errno, h_errorp); + + return ((rval == NS_SUCCESS) ? 0 : -1); +} + +int +getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer, + size_t buflen, struct netent **result, int *h_errorp) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + networks, (void *)nss_lt_id, + net_id_func, net_marshal_func, net_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_ht_getnetbyaddr, NULL) + { NSSRC_DNS, _dns_getnetbyaddr, NULL }, + NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */ +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + int rval, ret_errno; + + rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS, + "getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen, + &ret_errno, h_errorp); + + return ((rval == NS_SUCCESS) ? 0 : -1); +} + +struct netent * +getnetbyname(const char *name) +{ + struct netdata *nd; + struct netent *rval; + int ret_h_errno; + + if ((nd = __netdata_init()) == NULL) + return (NULL); + if (getnetbyname_r(name, &nd->net, nd->data, sizeof(nd->data), &rval, + &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +struct netent * +getnetbyaddr(uint32_t addr, int af) +{ + struct netdata *nd; + struct netent *rval; + int ret_h_errno; + + if ((nd = __netdata_init()) == NULL) + return (NULL); + if (getnetbyaddr_r(addr, af, &nd->net, nd->data, sizeof(nd->data), + &rval, &ret_h_errno) != 0) + return (NULL); + return (rval); +} + +void +setnetent(int stayopen) +{ + struct netent_data *ned; + + if ((ned = __netent_data_init()) == NULL) + return; + _setnethtent(stayopen, ned); + _setnetdnsent(stayopen); +} + +void +endnetent(void) +{ + struct netent_data *ned; + + if ((ned = __netent_data_init()) == NULL) + return; + _endnethtent(ned); + _endnetdnsent(); +} diff --git a/lib/libc/net/getproto.c b/lib/libc/net/getproto.c new file mode 100644 index 0000000..b2a3fe7 --- /dev/null +++ b/lib/libc/net/getproto.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1983, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getproto.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <netdb.h> +#include <nsswitch.h> +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobynumber(void *retval, void *mdata, va_list ap) +{ + struct protoent pe; + struct protoent_data *ped; + int error; + + int number; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + number = va_arg(ap, int); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } + + __setprotoent_p(ped->stayopen, ped); + while ((error = __getprotoent_p(&pe, ped)) == 0) + if (pe.p_proto == number) + break; + if (!ped->stayopen) + __endprotoent_p(ped); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + +int +getprotobynumber_r(int proto, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_id, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobynumber, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobynumber_r", + defaultsrc, proto, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +struct protoent * +getprotobynumber(int proto) +{ + struct protodata *pd; + struct protoent *rval; + + if ((pd = __protodata_init()) == NULL) + return (NULL); + if (getprotobynumber_r(proto, &pd->proto, pd->data, sizeof(pd->data), + &rval) != 0) + return (NULL); + return (rval); +} diff --git a/lib/libc/net/getprotoent.3 b/lib/libc/net/getprotoent.3 new file mode 100644 index 0000000..e15ad01 --- /dev/null +++ b/lib/libc/net/getprotoent.3 @@ -0,0 +1,150 @@ +.\" Copyright (c) 1983, 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. +.\" +.\" @(#)getprotoent.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd June 4, 1993 +.Dt GETPROTOENT 3 +.Os +.Sh NAME +.Nm getprotoent , +.Nm getprotobynumber , +.Nm getprotobyname , +.Nm setprotoent , +.Nm endprotoent +.Nd get protocol entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In netdb.h +.Ft struct protoent * +.Fn getprotoent void +.Ft struct protoent * +.Fn getprotobyname "const char *name" +.Ft struct protoent * +.Fn getprotobynumber "int proto" +.Ft void +.Fn setprotoent "int stayopen" +.Ft void +.Fn endprotoent void +.Sh DESCRIPTION +The +.Fn getprotoent , +.Fn getprotobyname , +and +.Fn getprotobynumber +functions +each return a pointer to an object with the +following structure +containing the broken-out +fields of a line in the network protocol data base, +.Pa /etc/protocols . +.Pp +.Bd -literal -offset indent +struct protoent { + char *p_name; /* official name of protocol */ + char **p_aliases; /* alias list */ + int p_proto; /* protocol number */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width p_aliases +.It Fa p_name +The official name of the protocol. +.It Fa p_aliases +A zero terminated list of alternate names for the protocol. +.It Fa p_proto +The protocol number. +.El +.Pp +The +.Fn getprotoent +function +reads the next line of the file, opening the file if necessary. +.Pp +The +.Fn setprotoent +function +opens and rewinds the file. +If the +.Fa stayopen +flag is non-zero, +the net data base will not be closed after each call to +.Fn getprotobyname +or +.Fn getprotobynumber . +.Pp +The +.Fn endprotoent +function +closes the file. +.Pp +The +.Fn getprotobyname +function +and +.Fn getprotobynumber +sequentially search from the beginning +of the file until a matching +protocol name or +protocol number is found, +or until +.Dv EOF +is encountered. +.Sh RETURN VALUES +Null pointer +(0) returned on +.Dv EOF +or error. +.Sh FILES +.Bl -tag -width /etc/protocols -compact +.It Pa /etc/protocols +.El +.Sh SEE ALSO +.Xr protocols 5 +.Sh HISTORY +The +.Fn getprotoent , +.Fn getprotobynumber , +.Fn getprotobyname , +.Fn setprotoent , +and +.Fn endprotoent +functions appeared in +.Bx 4.2 . +.Sh BUGS +These functions use a thread-specific data space; +if the data is needed for future use, it should be +copied before any subsequent calls overwrite it. +Only the Internet +protocols are currently understood. diff --git a/lib/libc/net/getprotoent.c b/lib/libc/net/getprotoent.c new file mode 100644 index 0000000..3d3a57e --- /dev/null +++ b/lib/libc/net/getprotoent.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 1983, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getprotoent.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <nsswitch.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "namespace.h" +#include "reentrant.h" +#include "un-namespace.h" +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +NETDB_THREAD_ALLOC(protoent_data) +NETDB_THREAD_ALLOC(protodata) + +static void +protoent_data_clear(struct protoent_data *ped) +{ + if (ped->fp) { + fclose(ped->fp); + ped->fp = NULL; + } +} + +static void +protoent_data_free(void *ptr) +{ + struct protoent_data *ped = ptr; + + protoent_data_clear(ped); + free(ped); +} + +static void +protodata_free(void *ptr) +{ + free(ptr); +} + +#ifdef NS_CACHING +int +__proto_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + char *name; + int proto; + + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + proto = va_arg(ap, int); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &proto, + sizeof(int)); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + + +int +__proto_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + + struct protoent new_proto; + size_t desired_size, size, aliases_size; + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *); + if (proto->p_name != NULL) + desired_size += strlen(proto->p_name) + 1; + + if (proto->p_aliases != NULL) { + aliases_size = 0; + for (alias = proto->p_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + (aliases_size + 1) * + sizeof(char *); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_proto, proto, sizeof(struct protoent)); + + *buffer_size = desired_size; + memset(buffer, 0, desired_size); + p = buffer + sizeof(struct protoent) + sizeof(char *); + memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_proto.p_name != NULL) { + size = strlen(new_proto.p_name); + memcpy(p, new_proto.p_name, size); + new_proto.p_name = p; + p += size + 1; + } + + if (new_proto.p_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size); + new_proto.p_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_proto.p_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_proto, sizeof(struct protoent)); + return (NS_SUCCESS); +} + +int +__proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + char *name; + int num; + struct protoent *proto; + char *orig_buf; + size_t orig_buf_size; + int *ret_errno; + + char *p; + char **alias; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + break; + case nss_lt_id: + num = va_arg(ap, int); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + proto = va_arg(ap, struct protoent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct protoent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(proto, buffer, sizeof(struct protoent)); + memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) + + _ALIGN(p) - (size_t)p, + buffer_size - sizeof(struct protoent) - sizeof(char *) - + _ALIGN(p) + (size_t)p); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *); + if (proto->p_aliases != NULL) { + NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **); + + for (alias = proto->p_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct protoent **)retval) = proto; + + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(protocols); +#endif /* NS_CACHING */ + +int +__copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf, + size_t buflen) +{ + char *cp; + int i, n; + int numptr, len; + + /* Find out the amount of space required to store the answer. */ + numptr = 1; /* NULL ptr */ + len = (char *)ALIGN(buf) - buf; + for (i = 0; pe->p_aliases[i]; i++, numptr++) { + len += strlen(pe->p_aliases[i]) + 1; + } + len += strlen(pe->p_name) + 1; + len += numptr * sizeof(char*); + + if (len > (int)buflen) { + errno = ERANGE; + return (-1); + } + + /* copy protocol value*/ + pptr->p_proto = pe->p_proto; + + cp = (char *)ALIGN(buf) + numptr * sizeof(char *); + + /* copy official name */ + n = strlen(pe->p_name) + 1; + strcpy(cp, pe->p_name); + pptr->p_name = cp; + cp += n; + + /* copy aliases */ + pptr->p_aliases = (char **)ALIGN(buf); + for (i = 0 ; pe->p_aliases[i]; i++) { + n = strlen(pe->p_aliases[i]) + 1; + strcpy(cp, pe->p_aliases[i]); + pptr->p_aliases[i] = cp; + cp += n; + } + pptr->p_aliases[i] = NULL; + + return (0); +} + +void +__setprotoent_p(int f, struct protoent_data *ped) +{ + if (ped->fp == NULL) + ped->fp = fopen(_PATH_PROTOCOLS, "r"); + else + rewind(ped->fp); + ped->stayopen |= f; +} + +void +__endprotoent_p(struct protoent_data *ped) +{ + if (ped->fp) { + fclose(ped->fp); + ped->fp = NULL; + } + ped->stayopen = 0; +} + +int +__getprotoent_p(struct protoent *pe, struct protoent_data *ped) +{ + char *p; + char *cp, **q, *endp; + long l; + + if (ped->fp == NULL && (ped->fp = fopen(_PATH_PROTOCOLS, "r")) == NULL) + return (-1); +again: + if ((p = fgets(ped->line, sizeof ped->line, ped->fp)) == NULL) + return (-1); + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + pe->p_name = p; + cp = strpbrk(p, " \t"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + while (*cp == ' ' || *cp == '\t') + cp++; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + l = strtol(cp, &endp, 10); + if (endp == cp || *endp != '\0' || l < 0 || l > USHRT_MAX) + goto again; + pe->p_proto = l; + q = pe->p_aliases = ped->aliases; + if (p != NULL) { + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &ped->aliases[_MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + return (0); +} + +static int +files_getprotoent_r(void *retval, void *mdata, va_list ap) +{ + struct protoent pe; + struct protoent_data *ped; + + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + if ((ped = __protoent_data_init()) == NULL) + return (-1); + + if (__getprotoent_p(&pe, ped) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = errno; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + +static int +files_setprotoent(void *retval, void *mdata, va_list ap) +{ + struct protoent_data *ped; + int f; + + f = va_arg(ap, int); + if ((ped = __protoent_data_init()) == NULL) + return (NS_UNAVAIL); + + __setprotoent_p(f, ped); + return (NS_UNAVAIL); +} + +static int +files_endprotoent(void *retval, void *mdata, va_list ap) +{ + struct protoent_data *ped; + + if ((ped = __protoent_data_init()) == NULL) + return (NS_UNAVAIL); + + __endprotoent_p(ped); + return (NS_UNAVAIL); +} + +int +getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen, + struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r", + defaultsrc, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +void +setprotoent(int stayopen) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc, + stayopen); +} + +void +endprotoent(void) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_all, + NULL, NULL); +#endif + + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_endprotoent, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc); +} + +struct protoent * +getprotoent(void) +{ + struct protodata *pd; + struct protoent *rval; + + if ((pd = __protodata_init()) == NULL) + return (NULL); + if (getprotoent_r(&pd->proto, pd->data, sizeof(pd->data), &rval) != 0) + return (NULL); + return (rval); +} diff --git a/lib/libc/net/getprotoname.c b/lib/libc/net/getprotoname.c new file mode 100644 index 0000000..4ef50e3 --- /dev/null +++ b/lib/libc/net/getprotoname.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 1983, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getprotoname.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <netdb.h> +#include <nsswitch.h> +#include <string.h> +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; + +#ifdef NS_CACHING +extern int __proto_id_func(char *, size_t *, va_list, void *); +extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *); +extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +files_getprotobyname(void *retval, void *mdata, va_list ap) +{ + struct protoent pe; + struct protoent_data *ped; + char **cp; + int error; + + char *name; + struct protoent *pptr; + char *buffer; + size_t buflen; + int *errnop; + + name = va_arg(ap, char *); + pptr = va_arg(ap, struct protoent *); + buffer = va_arg(ap, char *); + buflen = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + + if ((ped = __protoent_data_init()) == NULL) { + *errnop = -1; + return (NS_NOTFOUND); + } + + __setprotoent_p(ped->stayopen, ped); + while ((error = __getprotoent_p(&pe, ped)) == 0) { + if (strcmp(pe.p_name, name) == 0) + break; + for (cp = pe.p_aliases; *cp != 0; cp++) + if (strcmp(*cp, name) == 0) + goto found; + } +found: + if (!ped->stayopen) + __endprotoent_p(ped); + if (error != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) { + *errnop = -1; + return (NS_NOTFOUND); + } + + *((struct protoent **)retval) = pptr; + return (NS_SUCCESS); +} + + +int +getprotobyname_r(const char *name, struct protoent *pptr, char *buffer, + size_t buflen, struct protoent **result) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + protocols, (void *)nss_lt_name, + __proto_id_func, __proto_marshal_func, __proto_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_getprotobyname, NULL }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobyname_r", + defaultsrc, name, pptr, buffer, buflen, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +struct protoent * +getprotobyname(const char *name) +{ + struct protodata *pd; + struct protoent *rval; + + if ((pd = __protodata_init()) == NULL) + return (NULL); + if (getprotobyname_r(name, &pd->proto, pd->data, sizeof(pd->data), + &rval) != 0) + return (NULL); + return (rval); +} diff --git a/lib/libc/net/getservent.3 b/lib/libc/net/getservent.3 new file mode 100644 index 0000000..8c43c8f --- /dev/null +++ b/lib/libc/net/getservent.3 @@ -0,0 +1,159 @@ +.\" Copyright (c) 1983, 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: @(#)getservent.3 8.3 (Berkeley) 1/12/94 +.\" $FreeBSD$ +.\" +.Dd July 9, 1995 +.Dt GETSERVENT 3 +.Os +.Sh NAME +.Nm getservent , +.Nm getservbyport , +.Nm getservbyname , +.Nm setservent , +.Nm endservent +.Nd get service entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In netdb.h +.Ft struct servent * +.Fn getservent +.Ft struct servent * +.Fn getservbyname "const char *name" "const char *proto" +.Ft struct servent * +.Fn getservbyport "int port" "const char *proto" +.Ft void +.Fn setservent "int stayopen" +.Ft void +.Fn endservent void +.Sh DESCRIPTION +The +.Fn getservent , +.Fn getservbyname , +and +.Fn getservbyport +functions +each return a pointer to an object with the +following structure +containing the broken-out +fields of a line in the network services data base, +.Pa /etc/services . +.Bd -literal -offset indent +struct servent { + char *s_name; /* official name of service */ + char **s_aliases; /* alias list */ + int s_port; /* port service resides at */ + char *s_proto; /* protocol to use */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width s_aliases +.It Fa s_name +The official name of the service. +.It Fa s_aliases +A zero terminated list of alternate names for the service. +.It Fa s_port +The port number at which the service resides. +Port numbers are returned in network byte order. +.It Fa s_proto +The name of the protocol to use when contacting the +service. +.El +.Pp +The +.Fn getservent +function +reads the next line of the file, opening the file if necessary. +.Pp +The +.Fn setservent +function +opens and rewinds the file. +If the +.Fa stayopen +flag is non-zero, +the net data base will not be closed after each call to +.Fn getservbyname +or +.Fn getservbyport . +.Pp +The +.Fn endservent +function +closes the file. +.Pp +The +.Fn getservbyname +and +.Fn getservbyport +functions +sequentially search from the beginning +of the file until a matching +protocol name or +port number (which must be specified in +network byte order) is found, +or until +.Dv EOF +is encountered. +If a protocol name is also supplied (non- +.Dv NULL ) , +searches must also match the protocol. +.Sh FILES +.Bl -tag -width /etc/services -compact +.It Pa /etc/services +.El +.Sh DIAGNOSTICS +Null pointer +(0) returned on +.Dv EOF +or error. +.Sh SEE ALSO +.Xr getprotoent 3 , +.Xr services 5 +.Sh HISTORY +The +.Fn getservent , +.Fn getservbyport , +.Fn getservbyname , +.Fn setservent , +and +.Fn endservent +functions appeared in +.Bx 4.2 . +.Sh BUGS +These functions use a thread-specific data storage; +if the data is needed for future use, it should be +copied before any subsequent calls overwrite it. +Expecting port numbers to fit in a 32 bit +quantity is probably naive. diff --git a/lib/libc/net/getservent.c b/lib/libc/net/getservent.c new file mode 100644 index 0000000..b616e25 --- /dev/null +++ b/lib/libc/net/getservent.c @@ -0,0 +1,1213 @@ +/* + * Copyright (c) 1983, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getservent.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <nsswitch.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif +#include "namespace.h" +#include "reentrant.h" +#include "un-namespace.h" +#include "netdb_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif +#include "nss_tls.h" + +enum constants +{ + SETSERVENT = 1, + ENDSERVENT = 2, + SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ +}; + +struct servent_mdata +{ + enum nss_lookup_type how; + int compat_mode; +}; + +static const ns_src defaultsrc[] = { + { NSSRC_COMPAT, NS_SUCCESS }, + { NULL, 0 } +}; + +static int servent_unpack(char *, struct servent *, char **, size_t, int *); + +/* files backend declarations */ +struct files_state +{ + FILE *fp; + int stayopen; + + int compat_mode_active; +}; +static void files_endstate(void *); +NSS_TLS_HANDLING(files); + +static int files_servent(void *, void *, va_list); +static int files_setservent(void *, void *, va_list); + +#ifdef YP +/* nis backend declarations */ +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); + +struct nis_state +{ + int yp_stepping; + char yp_domain[MAXHOSTNAMELEN]; + char *yp_key; + int yp_keylen; +}; +static void nis_endstate(void *); +NSS_TLS_HANDLING(nis); + +static int nis_servent(void *, void *, va_list); +static int nis_setservent(void *, void *, va_list); +#endif + +/* compat backend declarations */ +static int compat_setservent(void *, void *, va_list); + +/* get** wrappers for get**_r functions declarations */ +struct servent_state { + struct servent serv; + char *buffer; + size_t bufsize; +}; +static void servent_endstate(void *); +NSS_TLS_HANDLING(servent); + +struct key { + const char *proto; + union { + const char *name; + int port; + }; +}; + +static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, + struct servent **); +static int wrap_getservent_r(struct key, struct servent *, char *, size_t, + struct servent **); +static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, + size_t, struct servent **), struct key); + +#ifdef NS_CACHING +static int serv_id_func(char *, size_t *, va_list, void *); +static int serv_marshal_func(char *, size_t *, void *, va_list, void *); +static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +static int +servent_unpack(char *p, struct servent *serv, char **aliases, + size_t aliases_size, int *errnop) +{ + char *cp, **q, *endp; + long l; + + if (*p == '#') + return -1; + + memset(serv, 0, sizeof(struct servent)); + + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + serv->s_name = p; + + p = strpbrk(p, " \t"); + if (p == NULL) + return -1; + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + cp = strpbrk(p, ",/"); + if (cp == NULL) + return -1; + + *cp++ = '\0'; + l = strtol(p, &endp, 10); + if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) + return -1; + serv->s_port = htons((in_port_t)l); + serv->s_proto = cp; + + q = serv->s_aliases = aliases; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &aliases[aliases_size - 1]) { + *q++ = cp; + } else { + *q = NULL; + *errnop = ERANGE; + return -1; + } + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; + + return 0; +} + +/* files backend implementation */ +static void +files_endstate(void *p) +{ + FILE * f; + + if (p == NULL) + return; + + f = ((struct files_state *)p)->fp; + if (f != NULL) + fclose(f); + + free(p); +} + +/* + * compat structures. compat and files sources functionalities are almost + * equal, so they all are managed by files_servent function + */ +static int +files_servent(void *retval, void *mdata, va_list ap) +{ + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_servent, + (void *)((struct servent_mdata *)mdata)->how }, +#endif + { NULL, NULL, NULL } + }; + + struct files_state *st; + int rv; + int stayopen; + + struct servent_mdata *serv_mdata; + char *name; + char *proto; + int port; + + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; + + char **aliases; + int aliases_size; + size_t linesize; + char *line; + char **cp; + + name = NULL; + proto = NULL; + serv_mdata = (struct servent_mdata *)mdata; + switch (serv_mdata->how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap,int *); + + *errnop = files_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->fp == NULL) + st->compat_mode_active = 0; + + if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) { + *errnop = errno; + return (NS_UNAVAIL); + } + + if (serv_mdata->how == nss_lt_all) + stayopen = 1; + else { + rewind(st->fp); + stayopen = st->stayopen; + } + + rv = NS_NOTFOUND; + do { + if (!st->compat_mode_active) { + if ((line = fgetln(st->fp, &linesize)) == NULL) { + *errnop = errno; + rv = NS_RETURN; + break; + } + + if (*line=='+') { + if (serv_mdata->compat_mode != 0) + st->compat_mode_active = 1; + } else { + if (bufsize <= linesize + _ALIGNBYTES + + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + aliases = (char **)_ALIGN(&buffer[linesize+1]); + aliases_size = (buffer + bufsize - + (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + memcpy(buffer, line, linesize); + buffer[linesize] = '\0'; + } + } + + if (st->compat_mode_active != 0) { + switch (serv_mdata->how) { + case nss_lt_name: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyname_r", + compat_src, name, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_id: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservbyport_r", + compat_src, port, proto, serv, buffer, + bufsize, errnop); + break; + case nss_lt_all: + rv = nsdispatch(retval, compat_dtab, + NSDB_SERVICES_COMPAT, "getservent_r", + compat_src, serv, buffer, bufsize, errnop); + break; + } + + if (!(rv & NS_TERMINATE) || + serv_mdata->how != nss_lt_all) + st->compat_mode_active = 0; + + continue; + } + + rv = servent_unpack(buffer, serv, aliases, aliases_size, + errnop); + if (rv !=0 ) { + if (*errnop == 0) { + rv = NS_NOTFOUND; + continue; + } + else { + rv = NS_RETURN; + break; + } + } + + rv = NS_NOTFOUND; + switch (serv_mdata->how) { + case nss_lt_name: + if (strcmp(name, serv->s_name) == 0) + goto gotname; + for (cp = serv->s_aliases; *cp; cp++) + if (strcmp(name, *cp) == 0) + goto gotname; + + continue; + gotname: + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_id: + if (port != serv->s_port) + continue; + + if (proto == 0 || strcmp(serv->s_proto, proto) == 0) + rv = NS_SUCCESS; + break; + case nss_lt_all: + rv = NS_SUCCESS; + break; + } + + } while (!(rv & NS_TERMINATE)); + + if (!stayopen && st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + + if ((rv == NS_SUCCESS) && (retval != NULL)) + *(struct servent **)retval=serv; + + return (rv); +} + +static int +files_setservent(void *retval, void *mdata, va_list ap) +{ + struct files_state *st; + int rv; + int f; + + rv = files_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + if (st->fp == NULL) + st->fp = fopen(_PATH_SERVICES, "r"); + else + rewind(st->fp); + st->stayopen |= f; + break; + case ENDSERVENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + st->stayopen = 0; + break; + default: + break; + }; + + st->compat_mode_active = 0; + return (NS_UNAVAIL); +} + +/* nis backend implementation */ +#ifdef YP +static void +nis_endstate(void *p) +{ + if (p == NULL) + return; + + free(((struct nis_state *)p)->yp_key); + free(p); +} + +static int +nis_servent(void *retval, void *mdata, va_list ap) +{ + char *resultbuf, *lastkey; + int resultbuflen; + char buf[YPMAXRECORD + 2]; + + struct nis_state *st; + int rv; + + enum nss_lookup_type how; + char *name; + char *proto; + int port; + + struct servent *serv; + char *buffer; + size_t bufsize; + int *errnop; + + char **aliases; + int aliases_size; + + name = NULL; + proto = NULL; + how = (enum nss_lookup_type)mdata; + switch (how) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + return NS_NOTFOUND; + }; + + serv = va_arg(ap, struct servent *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + + *errnop = nis_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + + if (st->yp_domain[0] == '\0') { + if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { + *errnop = errno; + return (NS_UNAVAIL); + } + } + + do { + switch (how) { + case nss_lt_name: + snprintf(buf, sizeof(buf), "%s/%s", name, proto); + if (yp_match(st->yp_domain, "services.byname", buf, + strlen(buf), &resultbuf, &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + break; + case nss_lt_id: + snprintf(buf, sizeof(buf), "%d/%s", ntohs(port), + proto); + + /* + * We have to be a little flexible + * here. Ideally you're supposed to have both + * a services.byname and a services.byport + * map, but some systems have only + * services.byname. FreeBSD cheats a little by + * putting the services.byport information in + * the same map as services.byname so that + * either case will work. We allow for both + * possibilities here: if there is no + * services.byport map, we try services.byname + * instead. + */ + rv = yp_match(st->yp_domain, "services.byport", buf, + strlen(buf), &resultbuf, &resultbuflen); + if (rv) { + if (rv == YPERR_MAP) { + if (yp_match(st->yp_domain, + "services.byname", buf, + strlen(buf), &resultbuf, + &resultbuflen)) { + rv = NS_NOTFOUND; + goto fin; + } + } else { + rv = NS_NOTFOUND; + goto fin; + } + } + + break; + case nss_lt_all: + if (!st->yp_stepping) { + free(st->yp_key); + rv = yp_first(st->yp_domain, "services.byname", + &st->yp_key, &st->yp_keylen, &resultbuf, + &resultbuflen); + if (rv) { + rv = NS_NOTFOUND; + goto fin; + } + st->yp_stepping = 1; + } else { + lastkey = st->yp_key; + rv = yp_next(st->yp_domain, "services.byname", + st->yp_key, st->yp_keylen, &st->yp_key, + &st->yp_keylen, &resultbuf, &resultbuflen); + free(lastkey); + if (rv) { + st->yp_stepping = 0; + rv = NS_NOTFOUND; + goto fin; + } + } + break; + }; + + /* we need a room for additional \n symbol */ + if (bufsize <= + resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]); + aliases_size = + (buffer + bufsize - (char *)aliases) / sizeof(char *); + if (aliases_size < 1) { + *errnop = ERANGE; + rv = NS_RETURN; + break; + } + + /* + * servent_unpack expects lines terminated with \n -- + * make it happy + */ + memcpy(buffer, resultbuf, resultbuflen); + buffer[resultbuflen] = '\n'; + buffer[resultbuflen + 1] = '\0'; + + if (servent_unpack(buffer, serv, aliases, aliases_size, + errnop) != 0) { + if (*errnop == 0) + rv = NS_NOTFOUND; + else + rv = NS_RETURN; + } else + rv = NS_SUCCESS; + free(resultbuf); + + } while (!(rv & NS_TERMINATE) && how == nss_lt_all); + +fin: + if (rv == NS_SUCCESS && retval != NULL) + *(struct servent **)retval = serv; + + return (rv); +} + +static int +nis_setservent(void *result, void *mdata, va_list ap) +{ + struct nis_state *st; + int rv; + + rv = nis_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + + switch ((enum constants)mdata) { + case SETSERVENT: + case ENDSERVENT: + free(st->yp_key); + st->yp_key = NULL; + st->yp_stepping = 0; + break; + default: + break; + }; + + return (NS_UNAVAIL); +} +#endif + +/* compat backend implementation */ +static int +compat_setservent(void *retval, void *mdata, va_list ap) +{ + static const ns_src compat_src[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, +#endif + { NULL, 0 } + }; + ns_dtab compat_dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_setservent, mdata }, +#endif + { NULL, NULL, NULL } + }; + int f; + + (void)files_setservent(retval, mdata, ap); + + switch ((enum constants)mdata) { + case SETSERVENT: + f = va_arg(ap,int); + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "setservent", compat_src, f); + break; + case ENDSERVENT: + (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, + "endservent", compat_src); + break; + default: + break; + } + + return (NS_UNAVAIL); +} + +#ifdef NS_CACHING +static int +serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) +{ + char *name; + char *proto; + int port; + + size_t desired_size, size, size2; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + + size = strlen(name); + desired_size = sizeof(enum nss_lookup_type) + size + 1; + if (proto != NULL) { + size2 = strlen(proto); + desired_size += size2 + 1; + } else + size2 = 0; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, + proto, size2 + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + + desired_size = sizeof(enum nss_lookup_type) + sizeof(int); + if (proto != NULL) { + size = strlen(proto); + desired_size += size + 1; + } else + size = 0; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); + memcpy(buffer + sizeof(enum nss_lookup_type), &port, + sizeof(int)); + + if (proto != NULL) + memcpy(buffer + sizeof(enum nss_lookup_type) + + sizeof(int), proto, size + 1); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +int +serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + size_t orig_buf_size; + + struct servent new_serv; + size_t desired_size; + char **alias; + char *p; + size_t size; + size_t aliases_size; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + + desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); + if (serv->s_name != NULL) + desired_size += strlen(serv->s_name) + 1; + if (serv->s_proto != NULL) + desired_size += strlen(serv->s_proto) + 1; + + aliases_size = 0; + if (serv->s_aliases != NULL) { + for (alias = serv->s_aliases; *alias; ++alias) { + desired_size += strlen(*alias) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + sizeof(char *) * (aliases_size + 1); + } + + if (*buffer_size < desired_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_serv, serv, sizeof(struct servent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct servent) + sizeof(char *); + memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_serv.s_name != NULL) { + size = strlen(new_serv.s_name); + memcpy(p, new_serv.s_name, size); + new_serv.s_name = p; + p += size + 1; + } + + if (new_serv.s_proto != NULL) { + size = strlen(new_serv.s_proto); + memcpy(p, new_serv.s_proto, size); + new_serv.s_proto = p; + p += size + 1; + } + + if (new_serv.s_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); + new_serv.s_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (alias = new_serv.s_aliases; *alias; ++alias) { + size = strlen(*alias); + memcpy(p, *alias, size); + *alias = p; + p += size + 1; + } + } + + memcpy(buffer, &new_serv, sizeof(struct servent)); + return (NS_SUCCESS); +} + +int +serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, + void *cache_mdata) +{ + char *name; + char *proto; + int port; + struct servent *serv; + char *orig_buf; + char *p; + char **alias; + size_t orig_buf_size; + int *ret_errno; + + switch ((enum nss_lookup_type)cache_mdata) { + case nss_lt_name: + name = va_arg(ap, char *); + proto = va_arg(ap, char *); + break; + case nss_lt_id: + port = va_arg(ap, int); + proto = va_arg(ap, char *); + break; + case nss_lt_all: + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + + serv = va_arg(ap, struct servent *); + orig_buf = va_arg(ap, char *); + orig_buf_size = va_arg(ap, size_t); + ret_errno = va_arg(ap, int *); + + if (orig_buf_size < + buffer_size - sizeof(struct servent) - sizeof(char *)) { + *ret_errno = ERANGE; + return (NS_RETURN); + } + + memcpy(serv, buffer, sizeof(struct servent)); + memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); + + orig_buf = (char *)_ALIGN(orig_buf); + memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + + (_ALIGN(p) - (size_t)p), + buffer_size - sizeof(struct servent) - sizeof(char *) - + (_ALIGN(p) - (size_t)p)); + p = (char *)_ALIGN(p); + + NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); + NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); + if (serv->s_aliases != NULL) { + NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); + + for (alias = serv->s_aliases; *alias; ++alias) + NS_APPLY_OFFSET(*alias, orig_buf, p, char *); + } + + if (retval != NULL) + *((struct servent **)retval) = serv; + return (NS_SUCCESS); +} + +NSS_MP_CACHE_HANDLING(services); +#endif /* NS_CACHING */ + +/* get**_r functions implementation */ +int +getservbyname_r(const char *name, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) +{ + static const struct servent_mdata mdata = { nss_lt_name, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_name, + serv_id_func, serv_marshal_func, serv_unmarshal_func); +#endif /* NS_CACHING */ + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, +#ifdef YP + { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", + defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +int +getservbyport_r(int port, const char *proto, struct servent *serv, + char *buffer, size_t bufsize, struct servent **result) +{ + static const struct servent_mdata mdata = { nss_lt_id, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_id, + serv_id_func, serv_marshal_func, serv_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, +#ifdef YP + { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", + defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +int +getservent_r(struct servent *serv, char *buffer, size_t bufsize, + struct servent **result) +{ + static const struct servent_mdata mdata = { nss_lt_all, 0 }; + static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + serv_marshal_func, serv_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_servent, (void *)&mdata }, +#ifdef YP + { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, +#endif + { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", + defaultsrc, serv, buffer, bufsize, &ret_errno); + + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); +} + +void +setservent(int stayopen) +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, + stayopen); +} + +void +endservent() +{ +#ifdef NS_CACHING + static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( + services, (void *)nss_lt_all, + NULL, NULL); +#endif + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, +#ifdef YP + { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, +#endif + { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { NULL, NULL, NULL } + }; + + (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); +} + +/* get** wrappers for get**_r functions implementation */ +static void +servent_endstate(void *p) +{ + if (p == NULL) + return; + + free(((struct servent_state *)p)->buffer); + free(p); +} + +static int +wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, + res)); +} + +static int +wrap_getservent_r(struct key key, struct servent *serv, char *buffer, + size_t bufsize, struct servent **res) +{ + return (getservent_r(serv, buffer, bufsize, res)); +} + +static struct servent * +getserv(int (*fn)(struct key, struct servent *, char *, size_t, + struct servent **), struct key key) +{ + int rv; + struct servent *res; + struct servent_state * st; + + rv = servent_getstate(&st); + if (rv != 0) { + errno = rv; + return NULL; + } + + if (st->buffer == NULL) { + st->buffer = malloc(SERVENT_STORAGE_INITIAL); + if (st->buffer == NULL) + return (NULL); + st->bufsize = SERVENT_STORAGE_INITIAL; + } + do { + rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); + if (res == NULL && rv == ERANGE) { + free(st->buffer); + if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { + st->buffer = NULL; + errno = ERANGE; + return (NULL); + } + st->bufsize <<= 1; + st->buffer = malloc(st->bufsize); + if (st->buffer == NULL) + return (NULL); + } + } while (res == NULL && rv == ERANGE); + if (rv != 0) + errno = rv; + + return (res); +} + +struct servent * +getservbyname(const char *name, const char *proto) +{ + struct key key; + + key.name = name; + key.proto = proto; + + return (getserv(wrap_getservbyname_r, key)); +} + +struct servent * +getservbyport(int port, const char *proto) +{ + struct key key; + + key.port = port; + key.proto = proto; + + return (getserv(wrap_getservbyport_r, key)); +} + +struct servent * +getservent() +{ + struct key key; + + key.proto = NULL; + key.port = 0; + + return (getserv(wrap_getservent_r, key)); +} diff --git a/lib/libc/net/hesiod.3 b/lib/libc/net/hesiod.3 new file mode 100644 index 0000000..bae4e44 --- /dev/null +++ b/lib/libc/net/hesiod.3 @@ -0,0 +1,176 @@ +.\" $NetBSD: hesiod.3,v 1.1 1999/01/25 03:43:04 lukem Exp $ +.\" +.\" from: #Id: hesiod.3,v 1.9.2.1 1997/01/03 21:02:23 ghudson Exp # +.\" +.\" Copyright 1988, 1996 by the Massachusetts Institute of Technology. +.\" +.\" Permission to use, copy, modify, and distribute this +.\" software and its documentation for any purpose and without +.\" fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright +.\" notice and this permission notice appear in supporting +.\" documentation, and that the name of M.I.T. not be used in +.\" advertising or publicity pertaining to distribution of the +.\" software without specific, written prior permission. +.\" M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" +.\" without express or implied warranty. +.\" +.\" $FreeBSD$ +.\" +.Dd November 30, 1996 +.Dt HESIOD 3 +.Os +.Sh NAME +.Nm hesiod , +.Nm hesiod_init , +.Nm hesiod_resolve , +.Nm hesiod_free_list , +.Nm hesiod_to_bind , +.Nm hesiod_end +.Nd Hesiod name server interface library +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In hesiod.h +.Ft int +.Fn hesiod_init "void **context" +.Ft char ** +.Fn hesiod_resolve "void *context" "const char *name" "const char *type" +.Ft void +.Fn hesiod_free_list "void *context" "char **list" +.Ft char * +.Fn hesiod_to_bind "void *context" "const char *name" "const char *type" +.Ft void +.Fn hesiod_end "void *context" +.Sh DESCRIPTION +This family of functions allows you to perform lookups of Hesiod +information, which is stored as text records in the Domain Name +Service. +To perform lookups, you must first initialize a +.Fa context , +an opaque object which stores information used internally by the +library between calls. +The +.Fn hesiod_init +function +initializes a context, storing a pointer to the context in the +location pointed to by the +.Fa context +argument. +The +.Fn hesiod_end +function +frees the resources used by a context. +.Pp +The +.Fn hesiod_resolve +function +is the primary interface to the library. +If successful, it returns a +list of one or more strings giving the records matching +.Fa name +and +.Fa type . +The last element of the list is followed by a +.Dv NULL +pointer. +It is the +caller's responsibility to call +.Fn hesiod_free_list +to free the resources used by the returned list. +.Pp +The +.Fn hesiod_to_bind +function +converts +.Fa name +and +.Fa type +into the DNS name used by +.Fn hesiod_resolve . +It is the caller's responsibility to free the returned string using +.Fn free . +.Sh RETURN VALUES +.Rv -std hesiod_init +On failure, +.Fn hesiod_resolve +and +.Fn hesiod_to_bind +return +.Dv NULL +and set the global variable +.Va errno +to indicate the error. +.Sh ENVIRONMENT +.Bl -tag -width HESIOD_CONFIG +.It Ev HES_DOMAIN +If the environment variable +.Ev HES_DOMAIN +is set, it will override the domain in the Hesiod configuration file. +.It Ev HESIOD_CONFIG +If the environment variable +.Ev HESIOD_CONFIG +is set, it specifies the location of the Hesiod configuration file. +.El +.Sh ERRORS +Hesiod calls may fail because of: +.Bl -tag -width Er +.It Bq Er ENOMEM +Insufficient memory was available to carry out the requested +operation. +.It Bq Er ENOEXEC +The +.Fn hesiod_init +function +failed because the Hesiod configuration file was invalid. +.It Bq Er ECONNREFUSED +The +.Fn hesiod_resolve +function +failed because no name server could be contacted to answer the query. +.It Bq Er EMSGSIZE +The +.Fn hesiod_resolve +or +.Fn hesiod_to_bind +function +failed because the query or response was too big to fit into the +packet buffers. +.It Bq Er ENOENT +The +.Fn hesiod_resolve +function +failed because the name server had no text records matching +.Fa name +and +.Fa type , +or +.Fn hesiod_to_bind +failed because the +.Fa name +argument had a domain extension which could not be resolved with type +.Dq rhs\-extension +in the local Hesiod domain. +.El +.Sh SEE ALSO +.Xr hesiod.conf 5 , +.Xr named 8 +.Rs +.%T "Hesiod - Project Athena Technical Plan -- Name Service" +.Re +.Sh AUTHORS +.An Steve Dyer , +IBM/Project Athena +.An Greg Hudson , +MIT Team Athena +.Pp +Copyright 1987, 1988, 1995, 1996 by the Massachusetts Institute of Technology. +.Sh BUGS +The strings corresponding to the +.Va errno +values set by the Hesiod functions are not particularly indicative of +what went wrong, especially for +.Er ENOEXEC +and +.Er ENOENT . diff --git a/lib/libc/net/hesiod.c b/lib/libc/net/hesiod.c new file mode 100644 index 0000000..c22bdba --- /dev/null +++ b/lib/libc/net/hesiod.c @@ -0,0 +1,583 @@ +/* $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $ */ + +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Copyright 1996 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +/* This file is part of the hesiod library. It implements the core + * portion of the hesiod resolver. + * + * This file is loosely based on an interim version of hesiod.c from + * the BIND IRS library, which was in turn based on an earlier version + * of this file. Extensive changes have been made on each step of the + * path. + * + * This implementation is not truly thread-safe at the moment because + * it uses res_send() and accesses _res. + */ + +#include <sys/cdefs.h> + +#if 0 +static char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $"; +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <hesiod.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct hesiod_p { + char *lhs; /* normally ".ns" */ + char *rhs; /* AKA the default hesiod domain */ + int classes[2]; /* The class search order. */ +}; + +#define MAX_HESRESP 1024 + +static int read_config_file(struct hesiod_p *, const char *); +static char **get_txt_records(int, const char *); +static int init_context(void); +static void translate_errors(void); + + +/* + * hesiod_init -- + * initialize a hesiod_p. + */ +int +hesiod_init(context) + void **context; +{ + struct hesiod_p *ctx; + const char *p, *configname; + + ctx = malloc(sizeof(struct hesiod_p)); + if (ctx) { + *context = ctx; + if (!issetugid()) + configname = getenv("HESIOD_CONFIG"); + else + configname = NULL; + if (!configname) + configname = _PATH_HESIOD_CONF; + if (read_config_file(ctx, configname) >= 0) { + /* + * The default rhs can be overridden by an + * environment variable. + */ + if (!issetugid()) + p = getenv("HES_DOMAIN"); + else + p = NULL; + if (p) { + if (ctx->rhs) + free(ctx->rhs); + ctx->rhs = malloc(strlen(p) + 2); + if (ctx->rhs) { + *ctx->rhs = '.'; + strcpy(ctx->rhs + 1, + (*p == '.') ? p + 1 : p); + return 0; + } else + errno = ENOMEM; + } else + return 0; + } + } else + errno = ENOMEM; + + if (ctx->lhs) + free(ctx->lhs); + if (ctx->rhs) + free(ctx->rhs); + if (ctx) + free(ctx); + return -1; +} + +/* + * hesiod_end -- + * Deallocates the hesiod_p. + */ +void +hesiod_end(context) + void *context; +{ + struct hesiod_p *ctx = (struct hesiod_p *) context; + + free(ctx->rhs); + if (ctx->lhs) + free(ctx->lhs); + free(ctx); +} + +/* + * hesiod_to_bind -- + * takes a hesiod (name, type) and returns a DNS + * name which is to be resolved. + */ +char * +hesiod_to_bind(void *context, const char *name, const char *type) +{ + struct hesiod_p *ctx = (struct hesiod_p *) context; + char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL; + const char *rhs; + int len; + + if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) { + errno = EMSGSIZE; + return NULL; + } + + /* + * Find the right right hand side to use, possibly + * truncating bindname. + */ + p = strchr(bindname, '@'); + if (p) { + *p++ = 0; + if (strchr(p, '.')) + rhs = name + (p - bindname); + else { + rhs_list = hesiod_resolve(context, p, "rhs-extension"); + if (rhs_list) + rhs = *rhs_list; + else { + errno = ENOENT; + return NULL; + } + } + } else + rhs = ctx->rhs; + + /* See if we have enough room. */ + len = strlen(bindname) + 1 + strlen(type); + if (ctx->lhs) + len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); + len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); + if (len > sizeof(bindname) - 1) { + if (rhs_list) + hesiod_free_list(context, rhs_list); + errno = EMSGSIZE; + return NULL; + } + /* Put together the rest of the domain. */ + strcat(bindname, "."); + strcat(bindname, type); + /* Only append lhs if it isn't empty. */ + if (ctx->lhs && ctx->lhs[0] != '\0' ) { + if (ctx->lhs[0] != '.') + strcat(bindname, "."); + strcat(bindname, ctx->lhs); + } + if (rhs[0] != '.') + strcat(bindname, "."); + strcat(bindname, rhs); + + /* rhs_list is no longer needed, since we're done with rhs. */ + if (rhs_list) + hesiod_free_list(context, rhs_list); + + /* Make a copy of the result and return it to the caller. */ + ret = strdup(bindname); + if (!ret) + errno = ENOMEM; + return ret; +} + +/* + * hesiod_resolve -- + * Given a hesiod name and type, return an array of strings returned + * by the resolver. + */ +char ** +hesiod_resolve(context, name, type) + void *context; + const char *name; + const char *type; +{ + struct hesiod_p *ctx = (struct hesiod_p *) context; + char *bindname, **retvec; + + bindname = hesiod_to_bind(context, name, type); + if (!bindname) + return NULL; + + retvec = get_txt_records(ctx->classes[0], bindname); + if (retvec == NULL && errno == ENOENT && ctx->classes[1]) + retvec = get_txt_records(ctx->classes[1], bindname); + + free(bindname); + return retvec; +} + +/*ARGSUSED*/ +void +hesiod_free_list(context, list) + void *context; + char **list; +{ + char **p; + + if (list == NULL) + return; + for (p = list; *p; p++) + free(*p); + free(list); +} + + +/* read_config_file -- + * Parse the /etc/hesiod.conf file. Returns 0 on success, + * -1 on failure. On failure, it might leave values in ctx->lhs + * or ctx->rhs which need to be freed by the caller. + */ +static int +read_config_file(ctx, filename) + struct hesiod_p *ctx; + const char *filename; +{ + char *key, *data, *p, **which; + char buf[MAXDNAME + 7]; + int n; + FILE *fp; + + /* Set default query classes. */ + ctx->classes[0] = C_IN; + ctx->classes[1] = C_HS; + + /* Try to open the configuration file. */ + fp = fopen(filename, "r"); + if (!fp) { + /* Use compiled in default domain names. */ + ctx->lhs = strdup(DEF_LHS); + ctx->rhs = strdup(DEF_RHS); + if (ctx->lhs && ctx->rhs) + return 0; + else { + errno = ENOMEM; + return -1; + } + } + ctx->lhs = NULL; + ctx->rhs = NULL; + while (fgets(buf, sizeof(buf), fp) != NULL) { + p = buf; + if (*p == '#' || *p == '\n' || *p == '\r') + continue; + while (*p == ' ' || *p == '\t') + p++; + key = p; + while (*p != ' ' && *p != '\t' && *p != '=') + p++; + *p++ = 0; + + while (isspace(*p) || *p == '=') + p++; + data = p; + while (!isspace(*p)) + p++; + *p = 0; + + if (strcasecmp(key, "lhs") == 0 || + strcasecmp(key, "rhs") == 0) { + which = (strcasecmp(key, "lhs") == 0) + ? &ctx->lhs : &ctx->rhs; + *which = strdup(data); + if (!*which) { + errno = ENOMEM; + return -1; + } + } else { + if (strcasecmp(key, "classes") == 0) { + n = 0; + while (*data && n < 2) { + p = data; + while (*p && *p != ',') + p++; + if (*p) + *p++ = 0; + if (strcasecmp(data, "IN") == 0) + ctx->classes[n++] = C_IN; + else + if (strcasecmp(data, "HS") == 0) + ctx->classes[n++] = + C_HS; + data = p; + } + while (n < 2) + ctx->classes[n++] = 0; + } + } + } + fclose(fp); + + if (!ctx->rhs || ctx->classes[0] == 0 || + ctx->classes[0] == ctx->classes[1]) { + errno = ENOEXEC; + return -1; + } + return 0; +} + +/* + * get_txt_records -- + * Given a DNS class and a DNS name, do a lookup for TXT records, and + * return a list of them. + */ +static char ** +get_txt_records(qclass, name) + int qclass; + const char *name; +{ + HEADER *hp; + unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; + char *dst, **list; + int ancount, qdcount, i, j, n, skip, type, class, len; + + /* Make sure the resolver is initialized. */ + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return NULL; + + /* Construct the query. */ + n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0, + NULL, qbuf, PACKETSZ); + if (n < 0) + return NULL; + + /* Send the query. */ + n = res_send(qbuf, n, abuf, MAX_HESRESP); + if (n < 0 || n > MAX_HESRESP) { + errno = ECONNREFUSED; /* XXX */ + return NULL; + } + /* Parse the header of the result. */ + hp = (HEADER *) (void *) abuf; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + p = abuf + sizeof(HEADER); + eom = abuf + n; + + /* + * Skip questions, trying to get to the answer section + * which follows. + */ + for (i = 0; i < qdcount; i++) { + skip = dn_skipname(p, eom); + if (skip < 0 || p + skip + QFIXEDSZ > eom) { + errno = EMSGSIZE; + return NULL; + } + p += skip + QFIXEDSZ; + } + + /* Allocate space for the text record answers. */ + list = malloc((ancount + 1) * sizeof(char *)); + if (!list) { + errno = ENOMEM; + return NULL; + } + /* Parse the answers. */ + j = 0; + for (i = 0; i < ancount; i++) { + /* Parse the header of this answer. */ + skip = dn_skipname(p, eom); + if (skip < 0 || p + skip + 10 > eom) + break; + type = p[skip + 0] << 8 | p[skip + 1]; + class = p[skip + 2] << 8 | p[skip + 3]; + len = p[skip + 8] << 8 | p[skip + 9]; + p += skip + 10; + if (p + len > eom) { + errno = EMSGSIZE; + break; + } + /* Skip entries of the wrong class and type. */ + if (class != qclass || type != T_TXT) { + p += len; + continue; + } + /* Allocate space for this answer. */ + list[j] = malloc((size_t)len); + if (!list[j]) { + errno = ENOMEM; + break; + } + dst = list[j++]; + + /* Copy answer data into the allocated area. */ + eor = p + len; + while (p < eor) { + n = (unsigned char) *p++; + if (p + n > eor) { + errno = EMSGSIZE; + break; + } + memcpy(dst, p, (size_t)n); + p += n; + dst += n; + } + if (p < eor) { + errno = EMSGSIZE; + break; + } + *dst = 0; + } + + /* + * If we didn't terminate the loop normally, something + * went wrong. + */ + if (i < ancount) { + for (i = 0; i < j; i++) + free(list[i]); + free(list); + return NULL; + } + if (j == 0) { + errno = ENOENT; + free(list); + return NULL; + } + list[j] = NULL; + return list; +} + + /* + * COMPATIBILITY FUNCTIONS + */ + +static int inited = 0; +static void *context; +static int errval = HES_ER_UNINIT; + +int +hes_init() +{ + init_context(); + return errval; +} + +char * +hes_to_bind(name, type) + const char *name; + const char *type; +{ + static char *bindname; + if (init_context() < 0) + return NULL; + if (bindname) + free(bindname); + bindname = hesiod_to_bind(context, name, type); + if (!bindname) + translate_errors(); + return bindname; +} + +char ** +hes_resolve(name, type) + const char *name; + const char *type; +{ + static char **list; + + if (init_context() < 0) + return NULL; + + /* + * In the old Hesiod interface, the caller was responsible for + * freeing the returned strings but not the vector of strings itself. + */ + if (list) + free(list); + + list = hesiod_resolve(context, name, type); + if (!list) + translate_errors(); + return list; +} + +int +hes_error() +{ + return errval; +} + +void +hes_free(hp) + char **hp; +{ + hesiod_free_list(context, hp); +} + +static int +init_context() +{ + if (!inited) { + inited = 1; + if (hesiod_init(&context) < 0) { + errval = HES_ER_CONFIG; + return -1; + } + errval = HES_ER_OK; + } + return 0; +} + +static void +translate_errors() +{ + switch (errno) { + case ENOENT: + errval = HES_ER_NOTFOUND; + break; + case ECONNREFUSED: + case EMSGSIZE: + errval = HES_ER_NET; + break; + case ENOMEM: + default: + /* Not a good match, but the best we can do. */ + errval = HES_ER_CONFIG; + break; + } +} diff --git a/lib/libc/net/if_indextoname.3 b/lib/libc/net/if_indextoname.3 new file mode 100644 index 0000000..715c33c --- /dev/null +++ b/lib/libc/net/if_indextoname.3 @@ -0,0 +1,152 @@ +.\" $KAME: if_indextoname.3,v 1.10 2000/11/24 08:13:51 itojun Exp $ +.\" BSDI Id: if_indextoname.3,v 2.2 2000/04/17 22:38:05 dab Exp +.\" +.\" Copyright (c) 1997, 2000 +.\" Berkeley Software Design, Inc. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 23, 2005 +.Dt IF_NAMETOINDEX 3 +.Os +.Sh NAME +.Nm if_nametoindex , +.Nm if_indextoname , +.Nm if_nameindex , +.Nm if_freenameindex +.Nd provide mappings between interface names and indexes +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In net/if.h +.Ft "unsigned int" +.Fn if_nametoindex "const char *ifname" +.Ft "char *" +.Fn if_indextoname "unsigned int ifindex" "char *ifname" +.Ft "struct if_nameindex *" +.Fn if_nameindex "void" +.Ft void +.Fn if_freenameindex "struct if_nameindex *ptr" +.Sh DESCRIPTION +The +.Fn if_nametoindex +function maps the interface name specified in +.Fa ifname +to its corresponding index. +If the specified interface does not exist, it returns 0. +.Pp +The +.Fn if_indextoname +function maps the interface index specified in +.Fa ifindex +to it corresponding name, which is copied into the +buffer pointed to by +.Fa ifname , +which must be of at least +.Dv IFNAMSIZ +bytes. +This pointer is also the return value of the function. +If there is no interface corresponding to the specified +index, +.Dv NULL +is returned. +.Pp +The +.Fn if_nameindex +function returns an array of +.Vt if_nameindex +structures, one structure per interface, as +defined in the include file +.In net/if.h . +The +.Vt if_nameindex +structure contains at least the following entries: +.Bd -literal + unsigned int if_index; /* 1, 2, ... */ + char *if_name; /* null terminated name: "le0", ... */ +.Ed +.Pp +The end of the array of structures is indicated by a structure with an +.Va if_index +of 0 and an +.Va if_name +of +.Dv NULL . +A +.Dv NULL +pointer is returned upon an error. +.Pp +The +.Fn if_freenameindex +function frees the dynamic memory that was +allocated by +.Fn if_nameindex . +.Sh RETURN VALUES +Upon successful completion, +.Fn if_nametoindex +returns the index number of the interface. +If the interface is not found, a value of 0 is returned and +.Va errno +is set to +.Er ENXIO . +A value of 0 is also returned if an error +occurs while retrieving the list of interfaces via +.Xr getifaddrs 3 . +.Pp +Upon successful completion, +.Fn if_indextoname +returns +.Fa ifname . +If the interface is not found, a +.Dv NULL +pointer is returned and +.Va errno +is set to +.Er ENXIO . +A +.Dv NULL +pointer is also returned if an error +occurs while retrieving the list of interfaces via +.Xr getifaddrs 3 . +.Pp +The +.Fn if_nameindex +returns a +.Dv NULL +pointer if an error +occurs while retrieving the list of interfaces via +.Xr getifaddrs 3 , +or if sufficient memory cannot be allocated. +.Sh SEE ALSO +.Xr getifaddrs 3 , +.Xr networking 4 +.Sh STANDARDS +The +.Fn if_nametoindex , +.Fn if_indextoname , +.Fn if_nameindex , +and +.Fn if_freenameindex +functions conform to +.%T "RFC 2553" . +.Sh HISTORY +The implementation first appeared in BSDi +.Bsx . diff --git a/lib/libc/net/if_indextoname.c b/lib/libc/net/if_indextoname.c new file mode 100644 index 0000000..4dadce3 --- /dev/null +++ b/lib/libc/net/if_indextoname.c @@ -0,0 +1,88 @@ +/* $KAME: if_indextoname.c,v 1.7 2000/11/08 03:09:30 itojun Exp $ */ + +/*- + * Copyright (c) 1997, 2000 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI Id: if_indextoname.c,v 2.3 2000/04/17 22:38:05 dab Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if_dl.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* + * From RFC 2533: + * + * The second function maps an interface index into its corresponding + * name. + * + * #include <net/if.h> + * + * char *if_indextoname(unsigned int ifindex, char *ifname); + * + * The ifname argument must point to a buffer of at least IF_NAMESIZE + * bytes into which the interface name corresponding to the specified + * index is returned. (IF_NAMESIZE is also defined in <net/if.h> and + * its value includes a terminating null byte at the end of the + * interface name.) This pointer is also the return value of the + * function. If there is no interface corresponding to the specified + * index, NULL is returned, and errno is set to ENXIO, if there was a + * system error (such as running out of memory), if_indextoname returns + * NULL and errno would be set to the proper value (e.g., ENOMEM). + */ + +char * +if_indextoname(unsigned int ifindex, char *ifname) +{ + struct ifaddrs *ifaddrs, *ifa; + int error = 0; + + if (getifaddrs(&ifaddrs) < 0) + return(NULL); /* getifaddrs properly set errno */ + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK && + ifindex == ((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index) + break; + } + + if (ifa == NULL) { + error = ENXIO; + ifname = NULL; + } + else + strncpy(ifname, ifa->ifa_name, IFNAMSIZ); + + freeifaddrs(ifaddrs); + + errno = error; + return(ifname); +} diff --git a/lib/libc/net/if_nameindex.c b/lib/libc/net/if_nameindex.c new file mode 100644 index 0000000..7a12d34 --- /dev/null +++ b/lib/libc/net/if_nameindex.c @@ -0,0 +1,147 @@ +/* $KAME: if_nameindex.c,v 1.8 2000/11/24 08:20:01 itojun Exp $ */ + +/*- + * Copyright (c) 1997, 2000 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI Id: if_nameindex.c,v 2.3 2000/04/17 22:38:05 dab Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if_dl.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> + +/* + * From RFC 2553: + * + * 4.3 Return All Interface Names and Indexes + * + * The if_nameindex structure holds the information about a single + * interface and is defined as a result of including the <net/if.h> + * header. + * + * struct if_nameindex { + * unsigned int if_index; + * char *if_name; + * }; + * + * The final function returns an array of if_nameindex structures, one + * structure per interface. + * + * struct if_nameindex *if_nameindex(void); + * + * The end of the array of structures is indicated by a structure with + * an if_index of 0 and an if_name of NULL. The function returns a NULL + * pointer upon an error, and would set errno to the appropriate value. + * + * The memory used for this array of structures along with the interface + * names pointed to by the if_name members is obtained dynamically. + * This memory is freed by the next function. + * + * 4.4. Free Memory + * + * The following function frees the dynamic memory that was allocated by + * if_nameindex(). + * + * #include <net/if.h> + * + * void if_freenameindex(struct if_nameindex *ptr); + * + * The argument to this function must be a pointer that was returned by + * if_nameindex(). + */ + +struct if_nameindex * +if_nameindex(void) +{ + struct ifaddrs *ifaddrs, *ifa; + unsigned int ni; + int nbytes; + struct if_nameindex *ifni, *ifni2; + char *cp; + + if (getifaddrs(&ifaddrs) < 0) + return(NULL); + + /* + * First, find out how many interfaces there are, and how + * much space we need for the string names. + */ + ni = 0; + nbytes = 0; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK) { + nbytes += strlen(ifa->ifa_name) + 1; + ni++; + } + } + + /* + * Next, allocate a chunk of memory, use the first part + * for the array of structures, and the last part for + * the strings. + */ + cp = malloc((ni + 1) * sizeof(struct if_nameindex) + nbytes); + ifni = (struct if_nameindex *)cp; + if (ifni == NULL) + goto out; + cp += (ni + 1) * sizeof(struct if_nameindex); + + /* + * Now just loop through the list of interfaces again, + * filling in the if_nameindex array and making copies + * of all the strings. + */ + ifni2 = ifni; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK) { + ifni2->if_index = + ((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index; + ifni2->if_name = cp; + strcpy(cp, ifa->ifa_name); + ifni2++; + cp += strlen(cp) + 1; + } + } + /* + * Finally, don't forget to terminate the array. + */ + ifni2->if_index = 0; + ifni2->if_name = NULL; +out: + freeifaddrs(ifaddrs); + return(ifni); +} + +void +if_freenameindex(struct if_nameindex *ptr) +{ + free(ptr); +} diff --git a/lib/libc/net/if_nametoindex.c b/lib/libc/net/if_nametoindex.c new file mode 100644 index 0000000..d0ca521 --- /dev/null +++ b/lib/libc/net/if_nametoindex.c @@ -0,0 +1,99 @@ +/* $KAME: if_nametoindex.c,v 1.6 2000/11/24 08:18:54 itojun Exp $ */ + +/*- + * Copyright (c) 1997, 2000 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI Id: if_nametoindex.c,v 2.3 2000/04/17 22:38:05 dab Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <ifaddrs.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include "un-namespace.h" + +/* + * From RFC 2553: + * + * 4.1 Name-to-Index + * + * + * The first function maps an interface name into its corresponding + * index. + * + * #include <net/if.h> + * + * unsigned int if_nametoindex(const char *ifname); + * + * If the specified interface name does not exist, the return value is + * 0, and errno is set to ENXIO. If there was a system error (such as + * running out of memory), the return value is 0 and errno is set to the + * proper value (e.g., ENOMEM). + */ + +unsigned int +if_nametoindex(const char *ifname) +{ + int s; + struct ifreq ifr; + struct ifaddrs *ifaddrs, *ifa; + unsigned int ni; + + s = _socket(AF_INET, SOCK_DGRAM, 0); + if (s != -1) { + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (_ioctl(s, SIOCGIFINDEX, &ifr) != -1) { + _close(s); + return (ifr.ifr_index); + } + _close(s); + } + + if (getifaddrs(&ifaddrs) < 0) + return(0); + + ni = 0; + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && + ifa->ifa_addr->sa_family == AF_LINK && + strcmp(ifa->ifa_name, ifname) == 0) { + ni = ((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index; + break; + } + } + + freeifaddrs(ifaddrs); + if (!ni) + errno = ENXIO; + return(ni); +} diff --git a/lib/libc/net/inet.3 b/lib/libc/net/inet.3 new file mode 100644 index 0000000..28877f1 --- /dev/null +++ b/lib/libc/net/inet.3 @@ -0,0 +1,303 @@ +.\" Copyright (c) 1983, 1990, 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: @(#)inet.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd June 14, 2004 +.Dt INET 3 +.Os +.Sh NAME +.Nm inet_aton , +.Nm inet_addr , +.Nm inet_network , +.Nm inet_ntoa , +.Nm inet_ntop , +.Nm inet_pton , +.Nm inet_makeaddr , +.Nm inet_lnaof , +.Nm inet_netof +.Nd Internet address manipulation routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netinet/in.h +.In arpa/inet.h +.Ft int +.Fn inet_aton "const char *cp" "struct in_addr *pin" +.Ft in_addr_t +.Fn inet_addr "const char *cp" +.Ft in_addr_t +.Fn inet_network "const char *cp" +.Ft char * +.Fn inet_ntoa "struct in_addr in" +.Ft const char * +.Fo inet_ntop +.Fa "int af" +.Fa "const void * restrict src" +.Fa "char * restrict dst" +.Fa "socklen_t size" +.Fc +.Ft int +.Fn inet_pton "int af" "const char * restrict src" "void * restrict dst" +.Ft struct in_addr +.Fn inet_makeaddr "in_addr_t net" "in_addr_t lna" +.Ft in_addr_t +.Fn inet_lnaof "struct in_addr in" +.Ft in_addr_t +.Fn inet_netof "struct in_addr in" +.Sh DESCRIPTION +The routines +.Fn inet_aton , +.Fn inet_addr +and +.Fn inet_network +interpret character strings representing +numbers expressed in the Internet standard +.Ql .\& +notation. +.Pp +The +.Fn inet_pton +function converts a presentation format address (that is, printable form +as held in a character string) to network format (usually a +.Ft struct in_addr +or some other internal binary representation, in network byte order). +It returns 1 if the address was valid for the specified address family, or +0 if the address was not parseable in the specified address family, or -1 +if some system error occurred (in which case +.Va errno +will have been set). +This function is presently valid for +.Dv AF_INET +and +.Dv AF_INET6 . +.Pp +The +.Fn inet_aton +routine interprets the specified character string as an Internet address, +placing the address into the structure provided. +It returns 1 if the string was successfully interpreted, +or 0 if the string is invalid. +The +.Fn inet_addr +and +.Fn inet_network +functions return numbers suitable for use +as Internet addresses and Internet network +numbers, respectively. +.Pp +The function +.Fn inet_ntop +converts an address +.Fa *src +from network format +(usually a +.Ft struct in_addr +or some other binary form, in network byte order) to presentation format +(suitable for external display purposes). +The +.Fa size +argument specifies the size, in bytes, of the buffer +.Fa *dst . +It returns NULL if a system error occurs (in which case, +.Va errno +will have been set), or it returns a pointer to the destination string. +This function is presently valid for +.Dv AF_INET +and +.Dv AF_INET6 . +.Pp +The routine +.Fn inet_ntoa +takes an Internet address and returns an +.Tn ASCII +string representing the address in +.Ql .\& +notation. +The routine +.Fn inet_makeaddr +takes an Internet network number and a local +network address and constructs an Internet address +from it. +The routines +.Fn inet_netof +and +.Fn inet_lnaof +break apart Internet host addresses, returning +the network number and local network address part, +respectively. +.Pp +All Internet addresses are returned in network +order (bytes ordered from left to right). +All network numbers and local address parts are +returned as machine byte order integer values. +.Sh INTERNET ADDRESSES +Values specified using the +.Ql .\& +notation take one +of the following forms: +.Bd -literal -offset indent +a.b.c.d +a.b.c +a.b +a +.Ed +.Pp +When four parts are specified, each is interpreted +as a byte of data and assigned, from left to right, +to the four bytes of an Internet address. +Note +that when an Internet address is viewed as a 32-bit +integer quantity on the +.Tn VAX +the bytes referred to +above appear as +.Dq Li d.c.b.a . +That is, +.Tn VAX +bytes are +ordered from right to left. +.Pp +When a three part address is specified, the last +part is interpreted as a 16-bit quantity and placed +in the right-most two bytes of the network address. +This makes the three part address format convenient +for specifying Class B network addresses as +.Dq Li 128.net.host . +.Pp +When a two part address is supplied, the last part +is interpreted as a 24-bit quantity and placed in +the right most three bytes of the network address. +This makes the two part address format convenient +for specifying Class A network addresses as +.Dq Li net.host . +.Pp +When only one part is given, the value is stored +directly in the network address without any byte +rearrangement. +.Pp +All numbers supplied as +.Dq parts +in a +.Ql .\& +notation +may be decimal, octal, or hexadecimal, as specified +in the C language (i.e., a leading 0x or 0X implies +hexadecimal; otherwise, a leading 0 implies octal; +otherwise, the number is interpreted as decimal). +.Pp +The +.Fn inet_aton +and +.Fn inet_ntoa +functions are semi-deprecated in favor of the +.Xr addr2ascii 3 +family. +However, since those functions are not yet widely implemented, +portable programs cannot rely on their presence and will continue +to use the +.Xr inet 3 +functions for some time. +.Sh DIAGNOSTICS +The constant +.Dv INADDR_NONE +is returned by +.Fn inet_addr +and +.Fn inet_network +for malformed requests. +.Sh ERRORS +The +.Fn inet_ntop +call fails if: +.Bl -tag -width Er +.It Bq Er ENOSPC +.Fa size +was not large enough to store the presentation form of the address. +.It Bq Er EAFNOSUPPORT +.Fa *src +was not an +.Dv AF_INET +or +.Dv AF_INET6 +family address. +.El +.Sh SEE ALSO +.Xr addr2ascii 3 , +.Xr byteorder 3 , +.Xr gethostbyname 3 , +.Xr getnetent 3 , +.Xr inet_net 3 , +.Xr hosts 5 , +.Xr networks 5 +.Rs +.%R RFC +.%N 2373 +.%D July 1998 +.%T "IP Version 6 Addressing Architecture" +.Re +.Sh STANDARDS +The +.Fn inet_ntop +and +.Fn inet_pton +functions conform to +.St -xns5.2 . +Note that +.Fn inet_pton +does not accept 1-, 2-, or 3-part dotted addresses; all four parts +must be specified and are interpreted only as decimal values. +This is a narrower input set than that accepted by +.Fn inet_aton . +.Sh HISTORY +These +functions appeared in +.Bx 4.2 . +.Sh BUGS +The value +.Dv INADDR_NONE +(0xffffffff) is a valid broadcast address, but +.Fn inet_addr +cannot return that value without indicating failure. +The newer +.Fn inet_aton +function does not share this problem. +The problem of host byte ordering versus network byte ordering is +confusing. +The string returned by +.Fn inet_ntoa +resides in a static memory area. +.Pp +Inet_addr should return a +.Fa struct in_addr . diff --git a/lib/libc/net/inet6_opt_init.3 b/lib/libc/net/inet6_opt_init.3 new file mode 100644 index 0000000..6713468 --- /dev/null +++ b/lib/libc/net/inet6_opt_init.3 @@ -0,0 +1,337 @@ +.\" $KAME: inet6_opt_init.3,v 1.7 2004/12/27 05:08:23 itojun Exp $ +.\" +.\" Copyright (C) 2004 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$ +.\" +.Dd December 23, 2004 +.Dt INET6_OPT_INIT 3 +.Os +.\" +.Sh NAME +.Nm inet6_opt_init , +.Nm inet6_opt_append , +.Nm inet6_opt_finish , +.Nm inet6_opt_set_val , +.Nm inet6_opt_next , +.Nm inet6_opt_find , +.Nm inet6_opt_get_val +.Nd IPv6 Hop-by-Hop and Destination Options manipulation +.\" +.Sh SYNOPSIS +.In netinet/in.h +.Ft "int" +.Fn inet6_opt_init "void *extbuf" "socklen_t extlen" +.Ft "int" +.Fn inet6_opt_append "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t len" "u_int8_t align" "void **databufp" +.Ft "int" +.Fn inet6_opt_finish "void *extbuf" "socklen_t extlen" "int offset" +.Ft "int" +.Fn inet6_opt_set_val "void *databuf" "int offset" "void *val" "socklen_t vallen" +.Ft "int" +.Fn inet6_opt_next "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t *typep" "socklen_t *lenp" "void **databufp" +.Ft "int" +.Fn inet6_opt_find "void *extbuf" "socklen_t extlen" "int offset" "u_int8_t type" "socklen_t *lenp" "void **databufp" +.Ft "int" +.Fn inet6_opt_get_val "void *databuf" "int offset" "void *val" "socklen_t vallen" +.\" +.Sh DESCRIPTION +Building and parsing the Hop-by-Hop and Destination options is +complicated. +The advanced sockets API defines a set of functions to +help applications create and manipulate Hop-by-Hop and Destination +options. +This man page describes the functions specified in +IETF Draft RFC3542. +These functions use the +formatting rules specified in Appendix B in RFC2460, i.e., that the +largest field is placed last in the option. +The function prototypes +for these functions are all contained in the +.In netinet/in.h +header file. +.\" +.Ss inet6_opt_init +The +.Fn inet6_opt_init +function +returns the number of bytes needed for an empty +extension header, one without any options. +If the +.Fa extbuf +argument points to a valid section of memory +then the +.Fn inet6_opt_init +function also initializes the extension header's length field. +When attempting to initialize an extension buffer passed in the +.Fa extbuf +argument, +.Fa extlen +must be a positive multiple of 8 or else the function fails and +returns \-1 to the caller. +.\" +.Ss inet6_opt_append +The +.Fn inet6_opt_append +function can perform two different jobs. +When a valid +.Fa extbuf +argument is supplied it appends an option to the extension buffer and +returns the updated total length as well as a pointer to the newly +created option in +.Fa databufp . +If the value +of +.Fa extbuf +is +.Dv NULL +then the +.Fn inet6_opt_append +function only reports what the total length would +be if the option were actually appended. +The +.Fa len +and +.Fa align +arguments specify the length of the option and the required data +alignment which must be used when appending the option. +The +.Fa offset +argument should be the length returned by the +.Fn inet6_opt_init +function or a previous call to +.Fn inet6_opt_append . +.Pp +The +.Fa type +argument is the 8-bit option type. +.Pp +After +.Fn inet6_opt_append +has been called, the application can use the buffer pointed to by +.Fa databufp +directly, or use +.Fn inet6_opt_set_val +to specify the data to be contained in the option. +.Pp +Option types of +.Li 0 +and +.Li 1 +are reserved for the +.Li Pad1 +and +.Li PadN +options. +All other values from 2 through 255 may be used by applications. +.Pp +The length of the option data is contained in an 8-bit value and so +may contain any value from 0 through 255. +.Pp +The +.Fa align +parameter must have a value of 1, 2, 4, or 8 and cannot exceed the +value of +.Fa len . +The alignment values represent no alignment, 16 bit, 32 bit and 64 bit +alignments, respectively. +.\" +.Ss inet6_opt_finish +The +.Fn inet6_opt_finish +function +calculates the final padding necessary to make the extension header a +multiple of 8 bytes, as required by the IPv6 extension header +specification, and returns the extension header's updated total +length. +The +.Fa offset +argument should be the length returned by +.Fn inet6_opt_init +or +.Fn inet6_opt_append . +When +.Fa extbuf +is not +.Dv NULL +the function also sets up the appropriate padding bytes by inserting a +Pad1 or PadN option of the proper length. +.Pp +If the extension header is too small to contain the proper padding +then an error of \-1 is returned to the caller. +.\" +.Ss inet6_opt_set_val +The +.Fn inet6_opt_set_val +function inserts data items of various sizes into the data portion of +the option. +The +.Fa databuf +argument is a pointer to memory that was returned by the +.Fn inet6_opt_append +call and the +.Fa offset +argument specifies where the option should be placed in the +data buffer. +The +.Fa val +argument points to an area of memory containing the data to be +inserted into the extension header, and the +.Fa vallen +argument indicates how much data to copy. +.Pp +The caller should ensure that each field is aligned on its natural +boundaries as described in Appendix B of RFC2460. +.Pp +The function returns the offset for the next field which is calculated as +.Fa offset ++ +.Fa vallen +and is used when composing options with multiple fields. +.\" +.Ss inet6_opt_next +The +.Fn inet6_opt_next +function parses received extension headers. +The +.Fa extbuf +and +.Fa extlen +arguments specify the location and length of the extension header +being parsed. +The +.Fa offset +argument should either be zero, for the first option, or the length value +returned by a previous call to +.Fn inet6_opt_next +or +.Fn inet6_opt_find . +The return value specifies the position where to continue scanning the +extension buffer. +The option is returned in the arguments +.Fa typep , lenp , +and +.Fa databufp , +which +point to the 8-bit option type, the 8-bit option length and the option +data, respectively. +This function does not return any PAD1 or PADN options. +When an error occurs or there are no more options, the return +value is \-1. +.\" +.Ss inet6_opt_find +The +.Fn inet6_opt_find +function searches the extension buffer for a particular option type, +passed in through the +.Fa type +argument. +If the option is found then the +.Fa lenp +and +.Fa databufp +arguments are updated to point to the option's length and data, +respectively. +The +.Fa extbuf +and +.Fa extlen +arguments +must point to a valid extension buffer and give its length. +The +.Fa offset +argument can be used to search from a location anywhere in the +extension header. +.Ss inet6_opt_get_val +The +.Fn inet6_opt_get_val +function extracts data items of various sizes in the data portion of +the option. +The +.Fa databuf +is a pointer returned by the +.Fn inet6_opt_next +or +.Fn inet6_opt_find +functions. +The +.Fa val +argument points where the data will be extracted. +The +.Fa offset +argument specifies from where in the data portion of the option the +value should be extracted; the first byte of option data is specified +by an offset of zero. +.Pp +It is expected that each field is aligned on its natural boundaries as +described in Appendix B of RFC2460. +.Pp +The function returns the offset for the next field +by calculating +.Fa offset ++ +.Fa vallen +which can be used when extracting option content with multiple fields. +Robust receivers must verify alignment before calling this function. +.\" +.Sh RETURN VALUES +All the functions return +\-1 +on an error. +.\" +.Sh EXAMPLES +RFC3542 gives comprehensive examples in Section 23. +.Pp +KAME also provides examples in the +.Pa advapitest +directory of its kit. +.\" +.Sh SEE ALSO +.Rs +.%A W. Stevens +.%A M. Thomas +.%A E. Nordmark +.%A T. Jinmei +.%T "Advanced Sockets API for IPv6" +.%N RFC3542 +.%D October 2002 +.Re +.Rs +.%A S. Deering +.%A R. Hinden +.%T "Internet Protocol, Version 6 (IPv6) Specification" +.%N RFC2460 +.%D December 1998 +.Re +.Sh STANDARDS +The functions are documented in +.Dq Advanced Sockets API for IPv6 +.Pq RFC3542 . +.\" +.Sh HISTORY +The implementation first appeared in KAME advanced networking kit. diff --git a/lib/libc/net/inet6_option_space.3 b/lib/libc/net/inet6_option_space.3 new file mode 100644 index 0000000..e17855a --- /dev/null +++ b/lib/libc/net/inet6_option_space.3 @@ -0,0 +1,54 @@ +.\" $KAME: inet6_option_space.3,v 1.11 2005/01/05 03:00:44 itojun Exp $ +.\" +.\" Copyright (C) 2004 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$ +.\" +.Dd January 24, 2005 +.Dt INET6_OPTION_SPACE 3 +.Os +.\" +.Sh NAME +.Nm inet6_option_space , +.Nm inet6_option_init , +.Nm inet6_option_append , +.Nm inet6_option_alloc , +.Nm inet6_option_next , +.Nm inet6_option_find +.Nd IPv6 Hop-by-Hop and Destination Option Manipulation +.\" +.Sh DESCRIPTION +The functions that were documented in this manual page are now +deprecated in favor of those described in +.Xr inet6_opt_init 3 . +Please refer to that manual page for information on how to manipulate +IPv6 Hop-by-Hop and Destination options. +.Sh SEE ALSO +.Xr inet6_opt_init 3 +.\" +.\" diff --git a/lib/libc/net/inet6_rth_space.3 b/lib/libc/net/inet6_rth_space.3 new file mode 100644 index 0000000..2750170 --- /dev/null +++ b/lib/libc/net/inet6_rth_space.3 @@ -0,0 +1,224 @@ +.\" $KAME: inet6_rth_space.3,v 1.7 2005/01/05 03:00:44 itojun Exp $ +.\" +.\" Copyright (C) 2004 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$ +.\" +.Dd December 24, 2004 +.Dt INET6_RTH_SPACE 3 +.Os +.\" +.Sh NAME +.Nm inet6_rth_space , +.Nm inet6_rth_init , +.Nm inet6_rth_add , +.Nm inet6_rth_reverse , +.Nm inet6_rth_segments , +.Nm inet6_rth_getaddr +.Nd IPv6 Routing Header Options manipulation +.\" +.Sh SYNOPSIS +.In netinet/in.h +.Ft socklen_t +.Fn inet6_rth_space "int" "int" +.Ft "void *" +.Fn inet6_rth_init "void *" "socklen_t" "int" "int" +.Ft int +.Fn inet6_rth_add "void *" "const struct in6_addr *" +.Ft int +.Fn inet6_rth_reverse "const void *" "void *" +.Ft int +.Fn inet6_rth_segments "const void *" +.Ft "struct in6_addr *" +.Fn inet6_rth_getaddr "const void *" "int" +.\" +.Sh DESCRIPTION +The IPv6 Advanced API, RFC 3542, defines the functions that an +application calls to build and examine IPv6 Routing headers. +Routing headers are used to perform source routing in IPv6 networks. +The RFC uses the word +.Dq segments +to describe addresses and that is the term used here as well. +All of the functions are defined in the +.In netinet/in.h +header file. +The functions described in this manual page all operate +on routing header structures which are defined in +.In netinet/ip6.h +but which should not need to be modified outside the use of this API. +The size and shape of the route header structures may change, so using +the APIs is a more portable, long term, solution. +.Pp +The functions in the API are split into two groups, those that build a +routing header and those that parse a received routing header. +We will describe the builder functions followed by the parser functions. +.Ss inet6_rth_space +The +.Fn inet6_rth_space +function returns the number of bytes required to hold a Routing Header +of the type, specified in the +.Fa type +argument and containing the number of addresses specified in the +.Fa segments +argument. +When the type is +.Dv IPV6_RTHDR_TYPE_0 +the number of segments must be from 0 through 127. +Routing headers of type +.Dv IPV6_RTHDR_TYPE_2 +contain only one segment, and are only used with Mobile IPv6. +The return value from this function is the number of bytes required to +store the routing header. +If the value 0 is returned then either the +route header type was not recognized or another error occurred. +.Ss inet6_rth_init +The +.Fn inet6_rth_init +function initializes the pre-allocated buffer pointed to by +.Fa bp +to contain a routing header of the specified type The +.Fa bp_len +argument is used to verify that the buffer is large enough. +The caller must allocate the buffer pointed to by bp. +The necessary buffer size should be determined by calling +.Fn inet6_rth_space +described in the previous sections. +.Pp +The +.Fn inet6_rth_init +function returns a pointer to +.Fa bp +on success and +.Dv NULL +when there is an error. +.Ss inet6_rth_add +The +.Fn inet6_rth_add +function adds the IPv6 address pointed to by +.Fa addr +to the end of the routing header being constructed. +.Pp +A successful addition results in the function returning 0, otherwise +\-1 is returned. +.Ss inet6_rth_reverse +The +.Fn inet6_rth_reverse +function takes a routing header, pointed to by the +argument +.Fa in , +and writes a new routing header into the argument pointed to by +.Fa out . +The routing header at that sends datagrams along the reverse of that +route. +Both arguments are allowed to point to the same buffer meaning +that the reversal can occur in place. +.Pp +The return value of the function is 0 on success, or \-1 when +there is an error. +.\" +.Pp +The next set of functions operate on a routing header that the +application wants to parse. +In the usual case such a routing header +is received from the network, although these functions can also be +used with routing headers that the application itself created. +.Ss inet6_rth_segments +The +.Fn inet6_rth_segments +function returns the number of segments contained in the +routing header pointed to by +.Fa bp . +The return value is the number of segments contained in the routing +header, or \-1 if an error occurred. +It is not an error for 0 to be +returned as a routing header may contain 0 segments. +.\" +.Ss inet6_rth_getaddr +The +.Fn inet6_rth_getaddr +function is used to retrieve a single address from a routing header. +The +.Fa index +is the location in the routing header from which the application wants +to retrieve an address. +The +.Fa index +parameter must have a value between 0 and one less than the number of +segments present in the routing header. +The +.Fn inet6_rth_segments +function, described in the last section, should be used to determine +the total number of segments in the routing header. +The +.Fn inet6_rth_getaddr +function returns a pointer to an IPv6 address on success or +.Dv NULL +when an error has occurred. +.\" +.Sh EXAMPLES +RFC 3542 gives extensive examples in Section 21, Appendix B. +.Pp +KAME also provides examples in the advapitest directory of its kit. +.\" +.Sh DIAGNOSTICS +The +.Fn inet6_rth_space +and +.Fn inet6_rth_getaddr +functions return 0 on errors. +.Pp +The +.Fn inet6_rthdr_init +function returns +.Dv NULL +on error. +The +.Fn inet6_rth_add +and +.Fn inet6_rth_reverse +functions return 0 on success, or \-1 upon an error. +.\" +.Sh SEE ALSO +.Rs +.%A W. Stevens +.%A M. Thomas +.%A E. Nordmark +.%A T. Jinmei +.%T "Advanced Sockets API for IPv6" +.%N RFC 3542 +.%D May 2003 +.Re +.Rs +.%A S. Deering +.%A R. Hinden +.%T "Internet Protocol, Version 6 (IPv6) Specification" +.%N RFC2460 +.%D December 1998 +.Re +.Sh HISTORY +The implementation first appeared in KAME advanced networking kit. diff --git a/lib/libc/net/inet6_rthdr_space.3 b/lib/libc/net/inet6_rthdr_space.3 new file mode 100644 index 0000000..bfb2f23 --- /dev/null +++ b/lib/libc/net/inet6_rthdr_space.3 @@ -0,0 +1,57 @@ +.\" $KAME: inet6_rthdr_space.3,v 1.11 2005/01/05 03:00:44 itojun Exp $ +.\" +.\" Copyright (C) 2004 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$ +.\" +.Dd January 24, 2005 +.Dt INET6_RTHDR_SPACE 3 +.Os +.\" +.Sh NAME +.Nm inet6_rthdr_space , +.Nm inet6_rthdr_init , +.Nm inet6_rthdr_add , +.Nm inet6_rthdr_lasthop , +.Nm inet6_rthdr_reverse , +.Nm inet6_rthdr_segments , +.Nm inet6_rthdr_getaddr , +.Nm inet6_rthdr_getflags +.Nd IPv6 Routing Header Options Manipulation +.\" +.Sh DESCRIPTION +The RFC 2292 IPv6 Advanced API has been deprecated in favor of the +newer, RFC 3542 APIs documented in +.Xr inet6_rth_space 3 . +On platforms that support it, currently only +.Fx , +please use the newer API to manipulate routing header +options. +.\" +.Sh SEE ALSO +.Xr inet6_rth_space 3 diff --git a/lib/libc/net/inet_net.3 b/lib/libc/net/inet_net.3 new file mode 100644 index 0000000..a9d0781 --- /dev/null +++ b/lib/libc/net/inet_net.3 @@ -0,0 +1,167 @@ +.\" $NetBSD: inet_net.3,v 1.4 1999/03/22 19:44:52 garbled Exp $ +.\" +.\" Copyright (c) 1997 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd February 26, 2006 +.Dt INET_NET 3 +.Os +.Sh NAME +.Nm inet_net_ntop , +.Nm inet_net_pton +.Nd Internet network number manipulation routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netinet/in.h +.In arpa/inet.h +.Ft char * +.Fn inet_net_ntop "int af" "const void *src" "int bits" "char *dst" "size_t size" +.Ft int +.Fn inet_net_pton "int af" "const char *src" "void *dst" "size_t size" +.Sh DESCRIPTION +The +.Fn inet_net_ntop +function converts an Internet network number from network format (usually a +.Vt "struct in_addr" +or some other binary form, in network byte order) to CIDR presentation format +(suitable for external display purposes). +The +.Fa bits +argument +is the number of bits in +.Fa src +that are the network number. +It returns +.Dv NULL +if a system error occurs (in which case, +.Va errno +will have been set), or it returns a pointer to the destination string. +.Pp +The +.Fn inet_net_pton +function converts a presentation format Internet network number (that is, +printable form as held in a character string) to network format (usually a +.Vt "struct in_addr" +or some other internal binary representation, in network byte order). +It returns the number of bits (either computed based on the class, or +specified with /CIDR), or \-1 if a failure occurred +(in which case +.Va errno +will have been set. +It will be set to +.Er ENOENT +if the Internet network number was not valid). +.Pp +The currently supported values for +.Fa af +are +.Dv AF_INET +and +.Dv AF_INET6 . +The +.Fa size +argument +is the size of the result buffer +.Fa dst . +.Pp +.Sh NETWORK NUMBERS (IP VERSION 4) +Internet network numbers may be specified in one of the following forms: +.Bd -literal -offset indent +a.b.c.d/bits +a.b.c.d +a.b.c +a.b +a +.Ed +.Pp +When four parts are specified, each is interpreted +as a byte of data and assigned, from left to right, +to the four bytes of an Internet network number. +Note +that when an Internet network number is viewed as a 32-bit +integer quantity on a system that uses little-endian +byte order (such as the +.Tn Intel 386 , 486 , +and +.Tn Pentium +processors) the bytes referred to above appear as +.Dq Li d.c.b.a . +That is, little-endian bytes are ordered from right to left. +.Pp +When a three part number is specified, the last +part is interpreted as a 16-bit quantity and placed +in the rightmost two bytes of the Internet network number. +This makes the three part number format convenient +for specifying Class B network numbers as +.Dq Li 128.net.host . +.Pp +When a two part number is supplied, the last part +is interpreted as a 24-bit quantity and placed in +the rightmost three bytes of the Internet network number. +This makes the two part number format convenient +for specifying Class A network numbers as +.Dq Li net.host . +.Pp +When only one part is given, the value is stored +directly in the Internet network number without any byte +rearrangement. +.Pp +All numbers supplied as +.Dq parts +in a +.Ql \&. +notation +may be decimal, octal, or hexadecimal, as specified +in the C language (i.e., a leading 0x or 0X implies +hexadecimal; otherwise, a leading 0 implies octal; +otherwise, the number is interpreted as decimal). +.\" +.\" .Sh NETWORK NUMBERS (IP VERSION 6) +.\" XXX - document this! +.\" +.Sh SEE ALSO +.Xr byteorder 3 , +.Xr inet 3 , +.Xr networks 5 +.Sh HISTORY +The +.Fn inet_net_ntop +and +.Fn inet_net_pton +functions appeared in BIND 4.9.4. diff --git a/lib/libc/net/ip6opt.c b/lib/libc/net/ip6opt.c new file mode 100644 index 0000000..7b65d06 --- /dev/null +++ b/lib/libc/net/ip6opt.c @@ -0,0 +1,610 @@ +/* $KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> + +#include <string.h> +#include <stdio.h> + +static int ip6optlen(u_int8_t *opt, u_int8_t *lim); +static void inet6_insert_padopt(u_char *p, int len); + +/* + * This function returns the number of bytes required to hold an option + * when it is stored as ancillary data, including the cmsghdr structure + * at the beginning, and any padding at the end (to make its size a + * multiple of 8 bytes). The argument is the size of the structure + * defining the option, which must include any pad bytes at the + * beginning (the value y in the alignment term "xn + y"), the type + * byte, the length byte, and the option data. + */ +int +inet6_option_space(nbytes) + int nbytes; +{ + nbytes += 2; /* we need space for nxt-hdr and length fields */ + return(CMSG_SPACE((nbytes + 7) & ~7)); +} + +/* + * This function is called once per ancillary data object that will + * contain either Hop-by-Hop or Destination options. It returns 0 on + * success or -1 on an error. + */ +int +inet6_option_init(bp, cmsgp, type) + void *bp; + struct cmsghdr **cmsgp; + int type; +{ + struct cmsghdr *ch = (struct cmsghdr *)bp; + + /* argument validation */ + if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) + return(-1); + + ch->cmsg_level = IPPROTO_IPV6; + ch->cmsg_type = type; + ch->cmsg_len = CMSG_LEN(0); + + *cmsgp = ch; + return(0); +} + +/* + * This function appends a Hop-by-Hop option or a Destination option + * into an ancillary data object that has been initialized by + * inet6_option_init(). This function returns 0 if it succeeds or -1 on + * an error. + * multx is the value x in the alignment term "xn + y" described + * earlier. It must have a value of 1, 2, 4, or 8. + * plusy is the value y in the alignment term "xn + y" described + * earlier. It must have a value between 0 and 7, inclusive. + */ +int +inet6_option_append(cmsg, typep, multx, plusy) + struct cmsghdr *cmsg; + const u_int8_t *typep; + int multx; + int plusy; +{ + int padlen, optlen, off; + u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; + struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); + + /* argument validation */ + if (multx != 1 && multx != 2 && multx != 4 && multx != 8) + return(-1); + if (plusy < 0 || plusy > 7) + return(-1); + + /* + * If this is the first option, allocate space for the + * first 2 bytes(for next header and length fields) of + * the option header. + */ + if (bp == (u_char *)eh) { + bp += 2; + cmsg->cmsg_len += 2; + } + + /* calculate pad length before the option. */ + off = bp - (u_char *)eh; + padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - + (off % multx); + padlen += plusy; + padlen %= multx; /* keep the pad as short as possible */ + /* insert padding */ + inet6_insert_padopt(bp, padlen); + cmsg->cmsg_len += padlen; + bp += padlen; + + /* copy the option */ + if (typep[0] == IP6OPT_PAD1) + optlen = 1; + else + optlen = typep[1] + 2; + memcpy(bp, typep, optlen); + bp += optlen; + cmsg->cmsg_len += optlen; + + /* calculate pad length after the option and insert the padding */ + off = bp - (u_char *)eh; + padlen = ((off + 7) & ~7) - off; + inet6_insert_padopt(bp, padlen); + bp += padlen; + cmsg->cmsg_len += padlen; + + /* update the length field of the ip6 option header */ + eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; + + return(0); +} + +/* + * This function appends a Hop-by-Hop option or a Destination option + * into an ancillary data object that has been initialized by + * inet6_option_init(). This function returns a pointer to the 8-bit + * option type field that starts the option on success, or NULL on an + * error. + * The difference between this function and inet6_option_append() is + * that the latter copies the contents of a previously built option into + * the ancillary data object while the current function returns a + * pointer to the space in the data object where the option's TLV must + * then be built by the caller. + * + */ +u_int8_t * +inet6_option_alloc(cmsg, datalen, multx, plusy) + struct cmsghdr *cmsg; + int datalen; + int multx; + int plusy; +{ + int padlen, off; + u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; + u_int8_t *retval; + struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg); + + /* argument validation */ + if (multx != 1 && multx != 2 && multx != 4 && multx != 8) + return(NULL); + if (plusy < 0 || plusy > 7) + return(NULL); + + /* + * If this is the first option, allocate space for the + * first 2 bytes(for next header and length fields) of + * the option header. + */ + if (bp == (u_char *)eh) { + bp += 2; + cmsg->cmsg_len += 2; + } + + /* calculate pad length before the option. */ + off = bp - (u_char *)eh; + padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) - + (off % multx); + padlen += plusy; + padlen %= multx; /* keep the pad as short as possible */ + /* insert padding */ + inet6_insert_padopt(bp, padlen); + cmsg->cmsg_len += padlen; + bp += padlen; + + /* keep space to store specified length of data */ + retval = bp; + bp += datalen; + cmsg->cmsg_len += datalen; + + /* calculate pad length after the option and insert the padding */ + off = bp - (u_char *)eh; + padlen = ((off + 7) & ~7) - off; + inet6_insert_padopt(bp, padlen); + bp += padlen; + cmsg->cmsg_len += padlen; + + /* update the length field of the ip6 option header */ + eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1; + + return(retval); +} + +/* + * This function processes the next Hop-by-Hop option or Destination + * option in an ancillary data object. If another option remains to be + * processed, the return value of the function is 0 and *tptrp points to + * the 8-bit option type field (which is followed by the 8-bit option + * data length, followed by the option data). If no more options remain + * to be processed, the return value is -1 and *tptrp is NULL. If an + * error occurs, the return value is -1 and *tptrp is not NULL. + * (RFC 2292, 6.3.5) + */ +int +inet6_option_next(cmsg, tptrp) + const struct cmsghdr *cmsg; + u_int8_t **tptrp; +{ + struct ip6_ext *ip6e; + int hdrlen, optlen; + u_int8_t *lim; + + if (cmsg->cmsg_level != IPPROTO_IPV6 || + (cmsg->cmsg_type != IPV6_HOPOPTS && + cmsg->cmsg_type != IPV6_DSTOPTS)) + return(-1); + + /* message length validation */ + if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) + return(-1); + ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); + hdrlen = (ip6e->ip6e_len + 1) << 3; + if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) + return(-1); + + /* + * If the caller does not specify the starting point, + * simply return the 1st option. + * Otherwise, search the option list for the next option. + */ + lim = (u_int8_t *)ip6e + hdrlen; + if (*tptrp == NULL) + *tptrp = (u_int8_t *)(ip6e + 1); + else { + if ((optlen = ip6optlen(*tptrp, lim)) == 0) + return(-1); + + *tptrp = *tptrp + optlen; + } + if (*tptrp >= lim) { /* there is no option */ + *tptrp = NULL; + return(-1); + } + /* + * Finally, checks if the next option is safely stored in the + * cmsg data. + */ + if (ip6optlen(*tptrp, lim) == 0) + return(-1); + else + return(0); +} + +/* + * This function is similar to the inet6_option_next() function, + * except this function lets the caller specify the option type to be + * searched for, instead of always returning the next option in the + * ancillary data object. + * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think + * it's a typo. The variable should be type of u_int8_t **. + */ +int +inet6_option_find(cmsg, tptrp, type) + const struct cmsghdr *cmsg; + u_int8_t **tptrp; + int type; +{ + struct ip6_ext *ip6e; + int hdrlen, optlen; + u_int8_t *optp, *lim; + + if (cmsg->cmsg_level != IPPROTO_IPV6 || + (cmsg->cmsg_type != IPV6_HOPOPTS && + cmsg->cmsg_type != IPV6_DSTOPTS)) + return(-1); + + /* message length validation */ + if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext))) + return(-1); + ip6e = (struct ip6_ext *)CMSG_DATA(cmsg); + hdrlen = (ip6e->ip6e_len + 1) << 3; + if (cmsg->cmsg_len < CMSG_SPACE(hdrlen)) + return(-1); + + /* + * If the caller does not specify the starting point, + * search from the beginning of the option list. + * Otherwise, search from *the next option* of the specified point. + */ + lim = (u_int8_t *)ip6e + hdrlen; + if (*tptrp == NULL) + *tptrp = (u_int8_t *)(ip6e + 1); + else { + if ((optlen = ip6optlen(*tptrp, lim)) == 0) + return(-1); + + *tptrp = *tptrp + optlen; + } + for (optp = *tptrp; optp < lim; optp += optlen) { + if (*optp == type) { + *tptrp = optp; + return(0); + } + if ((optlen = ip6optlen(optp, lim)) == 0) + return(-1); + } + + /* search failed */ + *tptrp = NULL; + return(-1); +} + +/* + * Calculate the length of a given IPv6 option. Also checks + * if the option is safely stored in user's buffer according to the + * calculated length and the limitation of the buffer. + */ +static int +ip6optlen(opt, lim) + u_int8_t *opt, *lim; +{ + int optlen; + + if (*opt == IP6OPT_PAD1) + optlen = 1; + else { + /* is there enough space to store type and len? */ + if (opt + 2 > lim) + return(0); + optlen = *(opt + 1) + 2; + } + if (opt + optlen <= lim) + return(optlen); + + return(0); +} + +static void +inet6_insert_padopt(u_char *p, int len) +{ + switch(len) { + case 0: + return; + case 1: + p[0] = IP6OPT_PAD1; + return; + default: + p[0] = IP6OPT_PADN; + p[1] = len - 2; + memset(&p[2], 0, len - 2); + return; + } +} + +/* + * The following functions are defined in RFC3542, which is a successor + * of RFC2292. + */ + +int +inet6_opt_init(void *extbuf, socklen_t extlen) +{ + struct ip6_ext *ext = (struct ip6_ext *)extbuf; + + if (extlen < 0 || (extlen % 8)) + return(-1); + + if (ext) { + if (extlen == 0) + return(-1); + ext->ip6e_len = (extlen >> 3) - 1; + } + + return(2); /* sizeof the next and the length fields */ +} + +int +inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type, + socklen_t len, u_int8_t align, void **databufp) +{ + int currentlen = offset, padlen = 0; + + /* + * The option type must have a value from 2 to 255, inclusive. + * (0 and 1 are reserved for the Pad1 and PadN options, respectively.) + */ + if (type < 2) + return(-1); + + /* + * The option data length must have a value between 0 and 255, + * inclusive, and is the length of the option data that follows. + */ + if (len < 0 || len > 255) + return(-1); + + /* + * The align parameter must have a value of 1, 2, 4, or 8. + * The align value can not exceed the value of len. + */ + if (align != 1 && align != 2 && align != 4 && align != 8) + return(-1); + if (align > len) + return(-1); + + /* Calculate the padding length. */ + currentlen += 2 + len; /* 2 means "type + len" */ + if (currentlen % align) + padlen = align - (currentlen % align); + + /* The option must fit in the extension header buffer. */ + currentlen += padlen; + if (extlen && /* XXX: right? */ + currentlen > extlen) + return(-1); + + if (extbuf) { + u_int8_t *optp = (u_int8_t *)extbuf + offset; + + if (padlen == 1) { + /* insert a Pad1 option */ + *optp = IP6OPT_PAD1; + optp++; + } + else if (padlen > 0) { + /* insert a PadN option for alignment */ + *optp++ = IP6OPT_PADN; + *optp++ = padlen - 2; + memset(optp, 0, padlen - 2); + optp += (padlen - 2); + } + + *optp++ = type; + *optp++ = len; + + *databufp = optp; + } + + return(currentlen); +} + +int +inet6_opt_finish(void *extbuf, socklen_t extlen, int offset) +{ + int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;; + + if (extbuf) { + u_int8_t *padp; + int padlen = updatelen - offset; + + if (updatelen > extlen) + return(-1); + + padp = (u_int8_t *)extbuf + offset; + if (padlen == 1) + *padp = IP6OPT_PAD1; + else if (padlen > 0) { + *padp++ = IP6OPT_PADN; + *padp++ = (padlen - 2); + memset(padp, 0, padlen - 2); + } + } + + return(updatelen); +} + +int +inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + + memcpy((u_int8_t *)databuf + offset, val, vallen); + return(offset + vallen); +} + +int +inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep, + socklen_t *lenp, void **databufp) +{ + u_int8_t *optp, *lim; + int optlen; + + /* Validate extlen. XXX: is the variable really necessary?? */ + if (extlen == 0 || (extlen % 8)) + return(-1); + lim = (u_int8_t *)extbuf + extlen; + + /* + * If this is the first time this function called for this options + * header, simply return the 1st option. + * Otherwise, search the option list for the next option. + */ + if (offset == 0) { + optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); + } + else + optp = (u_int8_t *)extbuf + offset; + + /* Find the next option skipping any padding options. */ + while(optp < lim) { + switch(*optp) { + case IP6OPT_PAD1: + optp++; + break; + case IP6OPT_PADN: + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + optp += optlen; + break; + default: /* found */ + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + *typep = *optp; + *lenp = optlen - 2; + *databufp = optp + 2; + return(optp + optlen - (u_int8_t *)extbuf); + } + } + + optend: + *databufp = NULL; /* for safety */ + return(-1); +} + +int +inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type, + socklen_t *lenp, void **databufp) +{ + u_int8_t *optp, *lim; + int optlen; + + /* Validate extlen. XXX: is the variable really necessary?? */ + if (extlen == 0 || (extlen % 8)) + return(-1); + lim = (u_int8_t *)extbuf + extlen; + + /* + * If this is the first time this function called for this options + * header, simply return the 1st option. + * Otherwise, search the option list for the next option. + */ + if (offset == 0) { + optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1); + } + else + optp = (u_int8_t *)extbuf + offset; + + /* Find the specified option */ + while(optp < lim) { + if ((optlen = ip6optlen(optp, lim)) == 0) + goto optend; + + if (*optp == type) { /* found */ + *lenp = optlen - 2; + *databufp = optp + 2; + return(optp + optlen - (u_int8_t *)extbuf); + } + + optp += optlen; + } + + optend: + *databufp = NULL; /* for safety */ + return(-1); +} + +int +inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen) +{ + + /* we can't assume alignment here */ + memcpy(val, (u_int8_t *)databuf + offset, vallen); + + return(offset + vallen); +} diff --git a/lib/libc/net/linkaddr.3 b/lib/libc/net/linkaddr.3 new file mode 100644 index 0000000..1f1c21f --- /dev/null +++ b/lib/libc/net/linkaddr.3 @@ -0,0 +1,144 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at BSDI. +.\" +.\" 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: @(#)linkaddr.3 8.1 (Berkeley) 7/28/93 +.\" $FreeBSD$ +.\" +.Dd June 17, 1996 +.Dt LINK_ADDR 3 +.Os +.Sh NAME +.Nm link_addr , +.Nm link_ntoa +.Nd elementary address specification routines for link level access +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In net/if_dl.h +.Ft void +.Fn link_addr "const char *addr" "struct sockaddr_dl *sdl" +.Ft char * +.Fn link_ntoa "const struct sockaddr_dl *sdl" +.Sh DESCRIPTION +The routine +.Fn link_addr +interprets character strings representing +link-level addresses, returning binary information suitable +for use in system calls. +The routine +.Fn link_ntoa +takes +a link-level +address and returns an +.Tn ASCII +string representing some of the information present, +including the link level address itself, and the interface name +or number, if present. +This facility is experimental and is +still subject to change. +.Pp +For +.Fn link_addr , +the string +.Fa addr +may contain +an optional network interface identifier of the form +.Dq "name unit-number" , +suitable for the first argument to +.Xr ifconfig 8 , +followed in all cases by a colon and +an interface address in the form of +groups of hexadecimal digits +separated by periods. +Each group represents a byte of address; +address bytes are filled left to right from +low order bytes through high order bytes. +.Pp +.\" A regular expression may make this format clearer: +.\" .Bd -literal -offset indent +.\" ([a-z]+[0-9]+:)?[0-9a-f]+(\e.[0-9a-f]+)* +.\" .Ed +.\" .Pp +Thus +.Li le0:8.0.9.13.d.30 +represents an ethernet address +to be transmitted on the first Lance ethernet interface. +.Pp +The direct use of these functions is deprecated in favor of the +.Xr addr2ascii 3 +interface; however, portable programs cannot rely on the latter as it is +not yet widely implemented. +.Sh RETURN VALUES +The +.Fn link_ntoa +function +always returns a null terminated string. +The +.Fn link_addr +function +has no return value. +(See +.Sx BUGS . ) +.Sh SEE ALSO +.Xr addr2ascii 3 +.\" .Xr iso 4 +.Sh HISTORY +The +.Fn link_addr +and +.Fn link_ntoa +functions appeared in +.Bx 4.3 Reno . +.Sh BUGS +The returned values for link_ntoa +reside in a static memory area. +.Pp +The function +.Fn link_addr +should diagnose improperly formed input, and there should be an unambiguous +way to recognize this. +.Pp +If the +.Va sdl_len +field of the link socket address +.Fa sdl +is 0, +.Fn link_ntoa +will not insert a colon before the interface address bytes. +If this translated address is given to +.Fn link_addr +without inserting an initial colon, +the latter will not interpret it correctly. diff --git a/lib/libc/net/linkaddr.c b/lib/libc/net/linkaddr.c new file mode 100644 index 0000000..c23559a --- /dev/null +++ b/lib/libc/net/linkaddr.c @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1990, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)linkaddr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if_dl.h> +#include <string.h> + +/* States*/ +#define NAMING 0 +#define GOTONE 1 +#define GOTTWO 2 +#define RESET 3 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) +#define LETTER (4*3) + +void +link_addr(addr, sdl) + const char *addr; + struct sockaddr_dl *sdl; +{ + char *cp = sdl->sdl_data; + char *cplim = sdl->sdl_len + (char *)sdl; + int byte = 0, state = NAMING, new; + + bzero((char *)&sdl->sdl_family, sdl->sdl_len - 1); + sdl->sdl_family = AF_LINK; + do { + state &= ~LETTER; + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) { + state |= END; + } else if (state == NAMING && + (((*addr >= 'A') && (*addr <= 'Z')) || + ((*addr >= 'a') && (*addr <= 'z')))) + state |= LETTER; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case NAMING | DIGIT: + case NAMING | LETTER: + *cp++ = addr[-1]; + continue; + case NAMING | DELIM: + state = RESET; + sdl->sdl_nlen = cp - sdl->sdl_data; + continue; + case GOTTWO | DIGIT: + *cp++ = byte; + /* FALLTHROUGH */ + case RESET | DIGIT: + state = GOTONE; + byte = new; + continue; + case GOTONE | DIGIT: + state = GOTTWO; + byte = new + (byte << 4); + continue; + default: /* | DELIM */ + state = RESET; + *cp++ = byte; + byte = 0; + continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; + /* FALLTHROUGH */ + case RESET | END: + break; + } + break; + } while (cp < cplim); + sdl->sdl_alen = cp - LLADDR(sdl); + new = cp - (char *)sdl; + if (new > sizeof(*sdl)) + sdl->sdl_len = new; + return; +} + +static char hexlist[] = "0123456789abcdef"; + +char * +link_ntoa(sdl) + const struct sockaddr_dl *sdl; +{ + static char obuf[64]; + char *out = obuf; + int i; + u_char *in = (u_char *)LLADDR(sdl); + u_char *inlim = in + sdl->sdl_alen; + int firsttime = 1; + + if (sdl->sdl_nlen) { + bcopy(sdl->sdl_data, obuf, sdl->sdl_nlen); + out += sdl->sdl_nlen; + if (sdl->sdl_alen) + *out++ = ':'; + } + while (in < inlim) { + if (firsttime) + firsttime = 0; + else + *out++ = '.'; + i = *in++; + if (i > 0xf) { + out[1] = hexlist[i & 0xf]; + i >>= 4; + out[0] = hexlist[i]; + out += 2; + } else + *out++ = hexlist[i]; + } + *out = 0; + return (obuf); +} diff --git a/lib/libc/net/map_v4v6.c b/lib/libc/net/map_v4v6.c new file mode 100644 index 0000000..dbbddc0 --- /dev/null +++ b/lib/libc/net/map_v4v6.c @@ -0,0 +1,123 @@ +/* + * ++Copyright++ 1985, 1988, 1993 + * - + * Copyright (c) 1985, 1988, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)gethostnamadr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <resolv.h> +#include <ctype.h> +#include <syslog.h> + +typedef union { + int32_t al; + char ac; +} align; + +void +_map_v4v6_address(const char *src, char *dst) +{ + u_char *p = (u_char *)dst; + char tmp[NS_INADDRSZ]; + int i; + + /* Stash a temporary copy so our caller can update in place. */ + memcpy(tmp, src, NS_INADDRSZ); + /* Mark this ipv6 addr as a mapped ipv4. */ + for (i = 0; i < 10; i++) + *p++ = 0x00; + *p++ = 0xff; + *p++ = 0xff; + /* Retrieve the saved copy and we're done. */ + memcpy((void*)p, tmp, NS_INADDRSZ); +} + +void +_map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) { + char **ap; + + if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) + return; + hp->h_addrtype = AF_INET6; + hp->h_length = IN6ADDRSZ; + for (ap = hp->h_addr_list; *ap; ap++) { + int i = (u_long)*bpp % sizeof(align); + + if (i != 0) + i = sizeof(align) - i; + + if ((ep - *bpp) < (i + IN6ADDRSZ)) { + /* Out of memory. Truncate address list here. */ + *ap = NULL; + return; + } + *bpp += i; + _map_v4v6_address(*ap, *bpp); + *ap = *bpp; + *bpp += IN6ADDRSZ; + } +} diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c new file mode 100644 index 0000000..dc6521a --- /dev/null +++ b/lib/libc/net/name6.c @@ -0,0 +1,2354 @@ +/* $KAME: name6.c,v 1.25 2000/06/26 16:44:40 itojun Exp $ */ + +/* + * 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. + */ +/* + * ++Copyright++ 1985, 1988, 1993 + * - + * Copyright (c) 1985, 1988, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* + * Atsushi Onoe <onoe@sm.sony.co.jp> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#ifdef ICMPNL +#include "reentrant.h" +#endif +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/queue.h> +#include <netinet/in.h> +#ifdef INET6 +#include <net/if.h> +#include <net/if_var.h> +#include <sys/sysctl.h> +#include <sys/ioctl.h> +#include <netinet6/in6_var.h> /* XXX */ +#endif + +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <nsswitch.h> +#include <unistd.h> +#include "un-namespace.h" +#include "netdb_private.h" +#include "res_config.h" +#include "res_private.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif + +#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 +}; + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; +#ifdef INET6 + struct in6_addrpolicy pc_policy; +#endif +}; +TAILQ_HEAD(policyhead, policyqueue); + +#define AIO_SRCFLAG_DEPRECATED 0x1 + +struct hp_order { + union { + struct sockaddr_storage aiou_ss; + struct sockaddr aiou_sa; + } aio_src_un; +#define aio_srcsa aio_src_un.aiou_sa + u_int32_t aio_srcflag; + int aio_srcscope; + int aio_dstscope; + struct policyqueue *aio_srcpolicy; + struct policyqueue *aio_dstpolicy; + union { + struct sockaddr_storage aiou_ss; + struct sockaddr aiou_sa; + } aio_un; +#define aio_sa aio_un.aiou_sa + int aio_matchlen; + char *aio_h_addr; +}; + +static struct hostent *_hpcopy(struct hostent *, int *); +static struct hostent *_hpaddr(int, const char *, void *, int *); +static struct hostent *_hpmerge(struct hostent *, struct hostent *, int *); +#ifdef INET6 +static struct hostent *_hpmapv6(struct hostent *, int *); +#endif +static struct hostent *_hpsort(struct hostent *, res_state); +static struct hostent *_ghbyname(const char *, int, int, int *); +static char *_hgetword(char **); +static int _mapped_addr_enabled(void); + +static struct hostent *_hpreorder(struct hostent *); +static int get_addrselectpolicy(struct policyhead *); +static void free_addrselectpolicy(struct policyhead *); +static struct policyqueue *match_addrselectpolicy(struct sockaddr *, + struct policyhead *); +static void set_source(struct hp_order *, struct policyhead *); +static int matchlen(struct sockaddr *, struct sockaddr *); +static int comp_dst(const void *, const void *); +static int gai_addr2scopetype(struct sockaddr *); + +static FILE *_files_open(int *); +static int _files_ghbyname(void *, void *, va_list); +static int _files_ghbyaddr(void *, void *, va_list); +#ifdef YP +static int _nis_ghbyname(void *, void *, va_list); +static int _nis_ghbyaddr(void *, void *, va_list); +#endif +static int _dns_ghbyname(void *, void *, va_list); +static int _dns_ghbyaddr(void *, void *, va_list); +static void _dns_shent(int) __unused; +static void _dns_ehent(void) __unused; +#ifdef ICMPNL +static int _icmp_ghbyaddr(void *, void *, va_list); +#endif /* ICMPNL */ +#ifdef NS_CACHING +static int ipnode_id_func(char *, size_t *, va_list, void *); +static int ipnode_marshal_func(char *, size_t *, void *, va_list, void *); +static int ipnode_unmarshal_func(char *, size_t, void *, va_list, void *); +#endif + +#ifdef ICMPNL +static mutex_t _getipnodeby_thread_lock = MUTEX_INITIALIZER; +#define THREAD_LOCK() mutex_lock(&_getipnodeby_thread_lock); +#define THREAD_UNLOCK() mutex_unlock(&_getipnodeby_thread_lock); +#endif + +/* Host lookup order if nsswitch.conf is broken or nonexistant */ +static const ns_src default_src[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NSSRC_DNS, NS_SUCCESS }, +#ifdef ICMPNL +#define NSSRC_ICMP "icmp" + { NSSRC_ICMP, NS_SUCCESS }, +#endif + { 0 } +}; + +/* + * Check if kernel supports mapped address. + * implementation dependent + */ +#ifdef __KAME__ +#include <sys/sysctl.h> +#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; +} + +#ifdef NS_CACHING +static int +ipnode_id_func(char *buffer, size_t *buffer_size, va_list ap, + void *cache_mdata) +{ + res_state statp; + u_long res_options; + + const int op_id = 2; + char *name; + int af; + size_t len; + void *src; + + char *p; + size_t desired_size, size; + enum nss_lookup_type lookup_type; + int res = NS_UNAVAIL; + + statp = __res_state(); + res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | + RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); + + lookup_type = (enum nss_lookup_type)cache_mdata; + switch (lookup_type) { + case nss_lt_name: + name = va_arg(ap, char *); + af = va_arg(ap, int); + + size = strlen(name); + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + size + 1; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, name, size + 1); + + res = NS_SUCCESS; + break; + case nss_lt_id: + src = va_arg(ap, void *); + len = va_arg(ap, size_t); + af = va_arg(ap, int); + + desired_size = sizeof(res_options) + sizeof(int) + + sizeof(enum nss_lookup_type) + sizeof(int) + + sizeof(size_t) + len; + + if (desired_size > *buffer_size) { + res = NS_RETURN; + goto fin; + } + + p = buffer; + memcpy(p, &res_options, sizeof(res_options)); + p += sizeof(res_options); + + memcpy(p, &op_id, sizeof(int)); + p += sizeof(int); + + memcpy(p, &lookup_type, sizeof(enum nss_lookup_type)); + p += sizeof(enum nss_lookup_type); + + memcpy(p, &af, sizeof(int)); + p += sizeof(int); + + memcpy(p, &len, sizeof(size_t)); + p += sizeof(size_t); + + memcpy(p, src, len); + + res = NS_SUCCESS; + break; + default: + /* should be unreachable */ + return (NS_UNAVAIL); + } + +fin: + *buffer_size = desired_size; + return (res); +} + +static int +ipnode_marshal_func(char *buffer, size_t *buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent *ht; + + struct hostent new_ht; + size_t desired_size, aliases_size, addr_size, size; + char *p, **iter; + + ht = *((struct hostent **)retval); + + desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *); + if (ht->h_name != NULL) + desired_size += strlen(ht->h_name) + 1; + + if (ht->h_aliases != NULL) { + aliases_size = 0; + for (iter = ht->h_aliases; *iter; ++iter) { + desired_size += strlen(*iter) + 1; + ++aliases_size; + } + + desired_size += _ALIGNBYTES + + (aliases_size + 1) * sizeof(char *); + } + + if (ht->h_addr_list != NULL) { + addr_size = 0; + for (iter = ht->h_addr_list; *iter; ++iter) + ++addr_size; + + desired_size += addr_size * _ALIGN(ht->h_length); + desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *); + } + + if (desired_size > *buffer_size) { + /* this assignment is here for future use */ + *buffer_size = desired_size; + return (NS_RETURN); + } + + memcpy(&new_ht, ht, sizeof(struct hostent)); + memset(buffer, 0, desired_size); + + *buffer_size = desired_size; + p = buffer + sizeof(struct hostent) + sizeof(char *); + memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *)); + p = (char *)_ALIGN(p); + + if (new_ht.h_name != NULL) { + size = strlen(new_ht.h_name); + memcpy(p, new_ht.h_name, size); + new_ht.h_name = p; + p += size + 1; + } + + if (new_ht.h_aliases != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size); + new_ht.h_aliases = (char **)p; + p += sizeof(char *) * (aliases_size + 1); + + for (iter = new_ht.h_aliases; *iter; ++iter) { + size = strlen(*iter); + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + + if (new_ht.h_addr_list != NULL) { + p = (char *)_ALIGN(p); + memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size); + new_ht.h_addr_list = (char **)p; + p += sizeof(char *) * (addr_size + 1); + + size = _ALIGN(new_ht.h_length); + for (iter = new_ht.h_addr_list; *iter; ++iter) { + memcpy(p, *iter, size); + *iter = p; + p += size + 1; + } + } + memcpy(buffer, &new_ht, sizeof(struct hostent)); + return (NS_SUCCESS); +} + +static int +ipnode_unmarshal_func(char *buffer, size_t buffer_size, void *retval, + va_list ap, void *cache_mdata) +{ + struct hostent new_ht; + struct hostent *ht; + + char *p; + char **iter; + char *orig_buf; + int err; + + ht = &new_ht; + + memcpy(ht, buffer, sizeof(struct hostent)); + memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *)); + + orig_buf = buffer + sizeof(struct hostent) + sizeof(char *) + + _ALIGN(p) - (size_t)p; + p = (char *)_ALIGN(p); + + + NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *); + if (ht->h_aliases != NULL) { + NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **); + + for (iter = ht->h_aliases; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + if (ht->h_addr_list != NULL) { + NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **); + + for (iter = ht->h_addr_list; *iter; ++iter) + NS_APPLY_OFFSET(*iter, orig_buf, p, char *); + } + + ht = _hpcopy(ht, &err); + if (ht == NULL) + return (NS_UNAVAIL); + + *((struct hostent **)retval) = ht; + return (NS_SUCCESS); +} +#endif + +/* + * Functions defined in RFC2553 + * getipnodebyname, getipnodebyaddr, freehostent + */ + +static struct hostent * +_ghbyname(const char *name, int af, int flags, int *errp) +{ + struct hostent *hp; + int rval; + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_name, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_files_ghbyname, NULL) + { NSSRC_DNS, _dns_ghbyname, NULL }, + NS_NIS_CB(_nis_ghbyname, NULL) +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + + 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); + } + + rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyname", default_src, + name, af, errp); + return (rval == NS_SUCCESS) ? hp : NULL; +} + +struct hostent * +getipnodebyname(const char *name, int af, int flags, int *errp) +{ + struct hostent *hp; + union inx_addr addrbuf; + res_state statp; + + switch (af) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + break; + default: + *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_aton(name, (struct in_addr *)&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); + } + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0) { + if (res_ninit(statp) < 0) { + *errp = NETDB_INTERNAL; + return NULL; + } + } + + *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 _hpreorder(_hpsort(hp, statp)); +} + +struct hostent * +getipnodebyaddr(const void *src, size_t len, int af, int *errp) +{ + struct hostent *hp; + int rval; +#ifdef INET6 + struct in6_addr addrbuf; +#else + struct in_addr addrbuf; +#endif + +#ifdef NS_CACHING + static const nss_cache_info cache_info = + NS_COMMON_CACHE_INFO_INITIALIZER( + hosts, (void *)nss_lt_id, + ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func); +#endif + static const ns_dtab dtab[] = { + NS_FILES_CB(_files_ghbyaddr, NULL) + { NSSRC_DNS, _dns_ghbyaddr, NULL }, + NS_NIS_CB(_nis_ghbyaddr, NULL) +#ifdef ICMPNL + { NSSRC_ICMP, _icmp_ghbyaddr, NULL }, +#endif +#ifdef NS_CACHING + NS_CACHE_CB(&cache_info) +#endif + { 0 } + }; + + *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_UNSPECIFIED((struct in6_addr *)src)) + return NULL; + 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; + } + + rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyaddr", default_src, + src, len, af, errp); + return (rval == NS_SUCCESS) ? hp : NULL; +} + +void +freehostent(struct hostent *ptr) +{ + free(ptr); +} + +/* + * 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) + 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) { + 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, res_state statp) +{ + int i, j, n; + u_char *ap, *sp, *mp, **pp; + char t; + char order[MAXADDRS]; + int nsort = statp->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 (statp->_u._ext.ext->sort_list[j].af != + hp->h_addrtype) + continue; + sp = (u_char *)&statp->_u._ext.ext->sort_list[j].addr; + mp = (u_char *)&statp->_u._ext.ext->sort_list[j].mask; +#else + sp = (u_char *)&statp->sort_list[j].addr; + mp = (u_char *)&statp->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; +} + +/* + * _hpreorder: sort address by default address selection + */ +static struct hostent * +_hpreorder(struct hostent *hp) +{ + struct hp_order *aio; + int i, n; + char *ap; + struct sockaddr *sa; + struct policyhead policyhead; + + if (hp == NULL) + return hp; + + switch (hp->h_addrtype) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + break; + default: + free_addrselectpolicy(&policyhead); + return hp; + } + + /* count the number of addrinfo elements for sorting. */ + for (n = 0; hp->h_addr_list[n] != NULL; n++) + ; + + /* + * If the number is small enough, we can skip the reordering process. + */ + if (n <= 1) + return hp; + + /* allocate a temporary array for sort and initialization of it. */ + if ((aio = malloc(sizeof(*aio) * n)) == NULL) + return hp; /* give up reordering */ + memset(aio, 0, sizeof(*aio) * n); + + /* retrieve address selection policy from the kernel */ + TAILQ_INIT(&policyhead); + if (!get_addrselectpolicy(&policyhead)) { + /* no policy is installed into kernel, we don't sort. */ + free(aio); + return hp; + } + + for (i = 0; i < n; i++) { + ap = hp->h_addr_list[i]; + aio[i].aio_h_addr = ap; + sa = &aio[i].aio_sa; + switch (hp->h_addrtype) { + case AF_INET: + sa->sa_family = AF_INET; + sa->sa_len = sizeof(struct sockaddr_in); + memcpy(&((struct sockaddr_in *)sa)->sin_addr, ap, + sizeof(struct in_addr)); + break; +#ifdef INET6 + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { + sa->sa_family = AF_INET; + sa->sa_len = sizeof(struct sockaddr_in); + memcpy(&((struct sockaddr_in *)sa)->sin_addr, + &ap[12], sizeof(struct in_addr)); + } else { + sa->sa_family = AF_INET6; + sa->sa_len = sizeof(struct sockaddr_in6); + memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, + ap, sizeof(struct in6_addr)); + } + break; +#endif + } + aio[i].aio_dstscope = gai_addr2scopetype(sa); + aio[i].aio_dstpolicy = match_addrselectpolicy(sa, &policyhead); + set_source(&aio[i], &policyhead); + } + + /* perform sorting. */ + qsort(aio, n, sizeof(*aio), comp_dst); + + /* reorder the h_addr_list. */ + for (i = 0; i < n; i++) + hp->h_addr_list[i] = aio[i].aio_h_addr; + + /* cleanup and return */ + free(aio); + free_addrselectpolicy(&policyhead); + return hp; +} + +static int +get_addrselectpolicy(struct policyhead *head) +{ +#ifdef INET6 + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) + return (0); + if ((buf = malloc(l)) == NULL) + return (0); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { + free(buf); + return (0); + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + struct policyqueue *new; + + if ((new = malloc(sizeof(*new))) == NULL) { + free_addrselectpolicy(head); /* make the list empty */ + break; + } + new->pc_policy = *pol; + TAILQ_INSERT_TAIL(head, new, pc_entry); + } + + free(buf); + return (1); +#else + return (0); +#endif +} + +static void +free_addrselectpolicy(struct policyhead *head) +{ + struct policyqueue *ent, *nent; + + for (ent = TAILQ_FIRST(head); ent; ent = nent) { + nent = TAILQ_NEXT(ent, pc_entry); + TAILQ_REMOVE(head, ent, pc_entry); + free(ent); + } +} + +static struct policyqueue * +match_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) +{ +#ifdef INET6 + struct policyqueue *ent, *bestent = NULL; + struct in6_addrpolicy *pol; + int matchlen, bestmatchlen = -1; + u_char *mp, *ep, *k, *p, m; + struct sockaddr_in6 key; + + switch(addr->sa_family) { + case AF_INET6: + key = *(struct sockaddr_in6 *)addr; + break; + case AF_INET: + /* convert the address into IPv4-mapped IPv6 address. */ + memset(&key, 0, sizeof(key)); + key.sin6_family = AF_INET6; + key.sin6_len = sizeof(key); + key.sin6_addr.s6_addr[10] = 0xff; + key.sin6_addr.s6_addr[11] = 0xff; + memcpy(&key.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)addr)->sin_addr, 4); + break; + default: + return(NULL); + } + + for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { + pol = &ent->pc_policy; + matchlen = 0; + + mp = (u_char *)&pol->addrmask.sin6_addr; + ep = mp + 16; /* XXX: scope field? */ + k = (u_char *)&key.sin6_addr; + p = (u_char *)&pol->addr.sin6_addr; + for (; mp < ep && *mp; mp++, k++, p++) { + m = *mp; + if ((*k & m) != *p) + goto next; /* not match */ + if (m == 0xff) /* short cut for a typical case */ + matchlen += 8; + else { + while (m >= 0x80) { + matchlen++; + m <<= 1; + } + } + } + + /* matched. check if this is better than the current best. */ + if (matchlen > bestmatchlen) { + bestent = ent; + bestmatchlen = matchlen; + } + + next: + continue; + } + + return(bestent); +#else + return(NULL); +#endif + +} + +static void +set_source(struct hp_order *aio, struct policyhead *ph) +{ + struct sockaddr_storage ss = aio->aio_un.aiou_ss; + socklen_t srclen; + int s; + + /* set unspec ("no source is available"), just in case */ + aio->aio_srcsa.sa_family = AF_UNSPEC; + aio->aio_srcscope = -1; + + switch(ss.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_port = htons(1); + break; +#ifdef INET6 + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_port = htons(1); + break; +#endif + default: /* ignore unsupported AFs explicitly */ + return; + } + + /* open a socket to get the source address for the given dst */ + if ((s = _socket(ss.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) + return; /* give up */ + if (_connect(s, (struct sockaddr *)&ss, ss.ss_len) < 0) + goto cleanup; + srclen = ss.ss_len; + if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { + aio->aio_srcsa.sa_family = AF_UNSPEC; + goto cleanup; + } + aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); + aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); + aio->aio_matchlen = matchlen(&aio->aio_srcsa, (struct sockaddr *)&ss); +#ifdef INET6 + if (ss.ss_family == AF_INET6) { + struct in6_ifreq ifr6; + u_int32_t flags6; + + /* XXX: interface name should not be hardcoded */ + strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_addr, &ss, ss.ss_len); + if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { + flags6 = ifr6.ifr_ifru.ifru_flags6; + if ((flags6 & IN6_IFF_DEPRECATED)) + aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; + } + } +#endif + + cleanup: + _close(s); + return; +} + +static int +matchlen(struct sockaddr *src, struct sockaddr *dst) +{ + int match = 0; + u_char *s, *d; + u_char *lim, r; + int addrlen; + + switch (src->sa_family) { +#ifdef INET6 + case AF_INET6: + s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; + d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; + addrlen = sizeof(struct in6_addr); + lim = s + addrlen; + break; +#endif + case AF_INET: + s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; + d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; + addrlen = sizeof(struct in_addr); + lim = s + addrlen; + break; + default: + return(0); + } + + while (s < lim) + if ((r = (*d++ ^ *s++)) != 0) { + while (r < addrlen * 8) { + match++; + r <<= 1; + } + break; + } else + match += 8; + return(match); +} + +static int +comp_dst(const void *arg1, const void *arg2) +{ + const struct hp_order *dst1 = arg1, *dst2 = arg2; + + /* + * Rule 1: Avoid unusable destinations. + * XXX: we currently do not consider if an appropriate route exists. + */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family == AF_UNSPEC) { + return(-1); + } + if (dst1->aio_srcsa.sa_family == AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + return(1); + } + + /* Rule 2: Prefer matching scope. */ + if (dst1->aio_dstscope == dst1->aio_srcscope && + dst2->aio_dstscope != dst2->aio_srcscope) { + return(-1); + } + if (dst1->aio_dstscope != dst1->aio_srcscope && + dst2->aio_dstscope == dst2->aio_srcscope) { + return(1); + } + + /* Rule 3: Avoid deprecated addresses. */ + if (dst1->aio_srcsa.sa_family != AF_UNSPEC && + dst2->aio_srcsa.sa_family != AF_UNSPEC) { + if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(-1); + } + if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && + !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { + return(1); + } + } + + /* Rule 4: Prefer home addresses. */ + /* XXX: not implemented yet */ + + /* Rule 5: Prefer matching label. */ +#ifdef INET6 + if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && + dst1->aio_srcpolicy->pc_policy.label == + dst1->aio_dstpolicy->pc_policy.label && + (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || + dst2->aio_srcpolicy->pc_policy.label != + dst2->aio_dstpolicy->pc_policy.label)) { + return(-1); + } + if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && + dst2->aio_srcpolicy->pc_policy.label == + dst2->aio_dstpolicy->pc_policy.label && + (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || + dst1->aio_srcpolicy->pc_policy.label != + dst1->aio_dstpolicy->pc_policy.label)) { + return(1); + } +#endif + + /* Rule 6: Prefer higher precedence. */ +#ifdef INET6 + if (dst1->aio_dstpolicy && + (dst2->aio_dstpolicy == NULL || + dst1->aio_dstpolicy->pc_policy.preced > + dst2->aio_dstpolicy->pc_policy.preced)) { + return(-1); + } + if (dst2->aio_dstpolicy && + (dst1->aio_dstpolicy == NULL || + dst2->aio_dstpolicy->pc_policy.preced > + dst1->aio_dstpolicy->pc_policy.preced)) { + return(1); + } +#endif + + /* Rule 7: Prefer native transport. */ + /* XXX: not implemented yet */ + + /* Rule 8: Prefer smaller scope. */ + if (dst1->aio_dstscope >= 0 && + dst1->aio_dstscope < dst2->aio_dstscope) { + return(-1); + } + if (dst2->aio_dstscope >= 0 && + dst2->aio_dstscope < dst1->aio_dstscope) { + return(1); + } + + /* + * Rule 9: Use longest matching prefix. + * We compare the match length in a same AF only. + */ + if (dst1->aio_sa.sa_family == dst2->aio_sa.sa_family) { + if (dst1->aio_matchlen > dst2->aio_matchlen) { + return(-1); + } + if (dst1->aio_matchlen < dst2->aio_matchlen) { + return(1); + } + } + + /* Rule 10: Otherwise, leave the order unchanged. */ + return(-1); +} + +/* + * Copy from scope.c. + * XXX: we should standardize the functions and link them as standard + * library. + */ +static int +gai_addr2scopetype(struct sockaddr *sa) +{ +#ifdef INET6 + struct sockaddr_in6 *sa6; +#endif + struct sockaddr_in *sa4; + + switch(sa->sa_family) { +#ifdef INET6 + case AF_INET6: + sa6 = (struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { + /* just use the scope field of the multicast address */ + return(sa6->sin6_addr.s6_addr[2] & 0x0f); + } + /* + * Unicast addresses: map scope type to corresponding scope + * value defined for multcast addresses. + * XXX: hardcoded scope type values are bad... + */ + if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) + return(1); /* node local scope */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) + return(2); /* link-local scope */ + if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) + return(5); /* site-local scope */ + return(14); /* global scope */ + break; +#endif + case AF_INET: + /* + * IPv4 pseudo scoping according to RFC 3484. + */ + sa4 = (struct sockaddr_in *)sa; + /* IPv4 autoconfiguration addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 169 && + ((u_char *)&sa4->sin_addr)[1] == 254) + return(2); + /* Private addresses have site-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 10 || + (((u_char *)&sa4->sin_addr)[0] == 172 && + (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || + (((u_char *)&sa4->sin_addr)[0] == 192 && + ((u_char *)&sa4->sin_addr)[1] == 168)) + return(14); /* XXX: It should be 5 unless NAT */ + /* Loopback addresses have link-local scope. */ + if (((u_char *)&sa4->sin_addr)[0] == 127) + return(2); + return(14); + break; + default: + errno = EAFNOSUPPORT; /* is this a good error? */ + return(-1); + } +} + +/* + * 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 int +_files_ghbyname(void *rval, void *cb_data, va_list ap) +{ + 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]; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + errp = va_arg(ap, int *); + + *(struct hostent **)rval = NULL; + + if ((fp = _files_open(errp)) == NULL) + return NS_UNAVAIL; + 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; + switch (af) { + case AF_INET: + if (inet_aton(addrstr, (struct in_addr *)&addrbuf) + != 1) { + *errp = NO_DATA; /* name found */ + continue; + } + break; +#ifdef INET6 + case AF_INET6: + if (inet_pton(af, addrstr, &addrbuf) != 1) { + *errp = NO_DATA; /* name found */ + continue; + } + break; +#endif + } + 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); + *(struct hostent **)rval = rethp; + return (rethp != NULL) ? NS_SUCCESS : NS_NOTFOUND; +} + +static int +_files_ghbyaddr(void *rval, void *cb_data, va_list ap) +{ + 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]; + + addr = va_arg(ap, const void *); + addrlen = va_arg(ap, int); + af = va_arg(ap, int); + errp = va_arg(ap, int *); + + *(struct hostent**)rval = NULL; + + if ((fp = _files_open(errp)) == NULL) + return NS_UNAVAIL; + hp = NULL; + while (fgets(buf, sizeof(buf), fp)) { + line = buf; + if ((p = _hgetword(&line)) == NULL + || (af == AF_INET + ? inet_aton(p, (struct in_addr *)&addrbuf) + : 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); + *(struct hostent **)rval = hp; + return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; +} + +#ifdef YP +/* + * NIS + * + * XXX actually a hack. + */ +static int +_nis_ghbyname(void *rval, void *cb_data, va_list ap) +{ + const char *name; + int af; + int *errp; + struct hostent *hp = NULL; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + errp = va_arg(ap, int *); + + hp = _gethostbynisname(name, af); + if (hp != NULL) + hp = _hpcopy(hp, errp); + + *(struct hostent **)rval = hp; + return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; +} + +static int +_nis_ghbyaddr(void *rval, void *cb_data, va_list ap) +{ + const void *addr; + int addrlen; + int af; + int *errp; + struct hostent *hp = NULL; + + addr = va_arg(ap, const void *); + addrlen = va_arg(ap, int); + af = va_arg(ap, int); + + hp = _gethostbynisaddr(addr, addrlen, af); + if (hp != NULL) + hp = _hpcopy(hp, errp); + *(struct hostent **)rval = hp; + return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; +} +#endif + +#define MAXPACKET (64*1024) + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +static struct hostent *getanswer(const querybuf *, int, const char *, int, + struct hostent *, int *); + +/* + * we don't need to take care about sorting, nor IPv4 mapped address here. + */ +static struct hostent * +getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, + struct hostent *template, int *errp) +{ + const HEADER *hp; + const u_char *cp; + int n; + const u_char *eom, *erdata; + char *bp, *ep, **ap, **hap; + int type, class, ancount, qdcount; + int haveanswer, had_error; + char tbuf[MAXDNAME]; + const char *tname; + int (*name_ok)(const char *); + static char *h_addr_ptrs[MAXADDRS + 1]; + static char *host_aliases[MAXALIASES]; + static char hostbuf[8*1024]; + +#define BOUNDED_INCR(x) \ + do { \ + cp += x; \ + if (cp > eom) { \ + *errp = NO_RECOVERY; \ + return (NULL); \ + } \ + } while (0) + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + *errp = NO_RECOVERY; \ + return (NULL); \ + } \ + } while (0) + +/* XXX do {} while (0) cannot be put here */ +#define DNS_ASSERT(x) \ + { \ + if (!(x)) { \ + cp += n; \ + continue; \ + } \ + } + +/* XXX do {} while (0) cannot be put here */ +#define DNS_FATAL(x) \ + { \ + if (!(x)) { \ + had_error++; \ + continue; \ + } \ + } + + tname = qname; + template->h_name = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + name_ok = res_hnok; + break; + case T_PTR: + name_ok = res_dnok; + break; + default: + return (NULL); /* XXX should be abort(); */ + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hostbuf; + ep = hostbuf + sizeof hostbuf; + cp = answer->buf; + BOUNDED_INCR(HFIXEDSZ); + if (qdcount != 1) { + *errp = NO_RECOVERY; + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + if ((n < 0) || !(*name_ok)(bp)) { + *errp = NO_RECOVERY; + return (NULL); + } + BOUNDED_INCR(n + QFIXEDSZ); + if (qtype == T_A || qtype == T_AAAA) { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + *errp = NO_RECOVERY; + return (NULL); + } + template->h_name = bp; + bp += n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = template->h_name; + } + ap = host_aliases; + *ap = NULL; + template->h_aliases = host_aliases; + hap = h_addr_ptrs; + *hap = NULL; + template->h_addr_list = h_addr_ptrs; + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + DNS_FATAL(n >= 0); + DNS_FATAL((*name_ok)(bp)); + cp += n; /* name */ + BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + BOUNDS_CHECK(cp, n); + erdata = cp + n; + DNS_ASSERT(class == C_IN); + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { + if (ap >= &host_aliases[MAXALIASES-1]) + continue; + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + DNS_FATAL(n >= 0); + DNS_FATAL((*name_ok)(tbuf)); + cp += n; + if (cp != erdata) { + *errp = NO_RECOVERY; + return (NULL); + } + /* Store alias. */ + *ap++ = bp; + n = strlen(bp) + 1; /* for the \0 */ + DNS_FATAL(n < MAXHOSTNAMELEN); + bp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + DNS_FATAL(n <= ep - bp); + DNS_FATAL(n < MAXHOSTNAMELEN); + strcpy(bp, tbuf); + template->h_name = bp; + bp += n; + continue; + } + if (qtype == T_PTR && type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if (n < 0 || !res_dnok(tbuf)) { + had_error++; + continue; + } + cp += n; + if (cp != erdata) { + *errp = NO_RECOVERY; + return (NULL); + } + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strcpy(bp, tbuf); + tname = bp; + bp += n; + continue; + } + DNS_ASSERT(type == qtype); + switch (type) { + case T_PTR: + DNS_ASSERT(strcasecmp(tname, bp) == 0); + n = dn_expand(answer->buf, eom, cp, bp, ep - bp); + DNS_FATAL(n >= 0); + DNS_FATAL(res_hnok(bp)); +#if MULTI_PTRS_ARE_ALIASES + cp += n; + if (cp != erdata) { + *errp = NO_RECOVERY; + return (NULL); + } + if (!haveanswer) + template->h_name = bp; + else if (ap < &host_aliases[MAXALIASES-1]) + *ap++ = bp; + else + n = -1; + if (n != -1) { + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + had_error++; + break; + } + bp += n; + } + break; +#else + template->h_name = bp; + *errp = NETDB_SUCCESS; + return (template); +#endif + case T_A: + case T_AAAA: + DNS_ASSERT(strcasecmp(template->h_name, bp) == 0); + DNS_ASSERT(n == template->h_length); + if (!haveanswer) { + int nn; + + template->h_name = bp; + nn = strlen(bp) + 1; /* for the \0 */ + bp += nn; + } + bp = (char *)ALIGN(bp); + + DNS_FATAL(bp + n < ep); + DNS_ASSERT(hap < &h_addr_ptrs[MAXADDRS-1]); +#ifdef FILTER_V4MAPPED + if (type == T_AAAA) { + struct in6_addr in6; + memcpy(&in6, cp, sizeof(in6)); + DNS_ASSERT(IN6_IS_ADDR_V4MAPPED(&in6) == 0); + } +#endif + bcopy(cp, *hap++ = bp, n); + bp += n; + cp += n; + if (cp != erdata) { + *errp = NO_RECOVERY; + return (NULL); + } + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + *ap = NULL; + *hap = NULL; + if (!template->h_name) { + n = strlen(qname) + 1; /* for the \0 */ + if (n > ep - bp || n >= MAXHOSTNAMELEN) + goto no_recovery; + strcpy(bp, qname); + template->h_name = bp; + bp += n; + } + *errp = NETDB_SUCCESS; + return (template); + } + no_recovery: + *errp = NO_RECOVERY; + return (NULL); + +#undef BOUNDED_INCR +#undef BOUNDS_CHECK +#undef DNS_ASSERT +#undef DNS_FATAL +} + +static int +_dns_ghbyname(void *rval, void *cb_data, va_list ap) +{ + const char *name; + int af; + int *errp; + int n; + struct hostent *hp; + int qtype; + struct hostent hbuf; + querybuf *buf; + res_state statp; + + name = va_arg(ap, const char *); + af = va_arg(ap, int); + errp = va_arg(ap, int *); + + statp = __res_state(); + + memset(&hbuf, 0, sizeof(hbuf)); + hbuf.h_addrtype = af; + hbuf.h_length = ADDRLEN(af); + + switch (af) { +#ifdef INET6 + case AF_INET6: + qtype = T_AAAA; + break; +#endif + case AF_INET: + qtype = T_A; + break; + default: + *errp = NO_RECOVERY; + return NS_NOTFOUND; + } + buf = malloc(sizeof(*buf)); + if (buf == NULL) { + *errp = NETDB_INTERNAL; + return NS_UNAVAIL; + } + n = res_nsearch(statp, name, C_IN, qtype, buf->buf, sizeof(buf->buf)); + if (n < 0) { + free(buf); + *errp = statp->res_h_errno; + return NS_UNAVAIL; + } + hp = getanswer(buf, n, name, qtype, &hbuf, errp); + free(buf); + if (!hp) { + *errp = NO_RECOVERY; + return NS_NOTFOUND; + } + *(struct hostent **)rval = _hpcopy(&hbuf, errp); + if (*(struct hostent **)rval != NULL) + return NS_SUCCESS; + else if (*errp == TRY_AGAIN) + return NS_TRYAGAIN; + else + return NS_NOTFOUND; +} + +static int +_dns_ghbyaddr(void *rval, void *cb_data, va_list ap) +{ + const void *addr; + int addrlen; + int af; + int *errp; + int n; + int err; + struct hostent *hp; + u_char c, *cp; + char *bp; + struct hostent hbuf; +#ifdef INET6 + static const char hex[] = "0123456789abcdef"; +#endif + querybuf *buf; + char qbuf[MAXDNAME+1]; + char *hlist[2]; + char *tld6[] = { "ip6.arpa", NULL }; + char *tld4[] = { "in-addr.arpa", NULL }; + char **tld; + res_state statp; + + addr = va_arg(ap, const void *); + addrlen = va_arg(ap, int); + af = va_arg(ap, int); + errp = va_arg(ap, int *); + + *(struct hostent **)rval = NULL; + +#ifdef INET6 + /* XXX */ + if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr)) + return NS_NOTFOUND; +#endif + + switch (af) { +#ifdef INET6 + case AF_INET6: + tld = tld6; + break; +#endif + case AF_INET: + tld = tld4; + break; + default: + return NS_NOTFOUND; + } + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0) { + if (res_ninit(statp) < 0) { + *errp = NETDB_INTERNAL; + return NS_UNAVAIL; + } + } + memset(&hbuf, 0, sizeof(hbuf)); + hbuf.h_name = NULL; + hbuf.h_addrtype = af; + hbuf.h_length = addrlen; + + buf = malloc(sizeof(*buf)); + if (buf == NULL) { + *errp = NETDB_INTERNAL; + return NS_UNAVAIL; + } + err = NS_SUCCESS; + for (/* nothing */; *tld; tld++) { + /* + * XXX assumes that MAXDNAME is big enough - error checks + * has been made by callers + */ + n = 0; + bp = qbuf; + 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, *tld); + break; +#endif + case AF_INET: + 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, *tld); + break; + } + + n = res_nquery(statp, qbuf, C_IN, T_PTR, buf->buf, + sizeof buf->buf); + if (n < 0) { + *errp = statp->res_h_errno; + err = NS_UNAVAIL; + continue; + } else if (n > sizeof(buf->buf)) { +#if 0 + errno = ERANGE; /* XXX is it OK to set errno here? */ +#endif + *errp = NETDB_INTERNAL; + err = NS_UNAVAIL; + continue; + } + hp = getanswer(buf, n, qbuf, T_PTR, &hbuf, errp); + if (!hp) { + err = NS_NOTFOUND; + continue; + } + free(buf); + hbuf.h_addrtype = af; + hbuf.h_length = addrlen; + hbuf.h_addr_list = hlist; + hlist[0] = (char *)addr; + hlist[1] = NULL; + *(struct hostent **)rval = _hpcopy(&hbuf, errp); + return NS_SUCCESS; + } + free(buf); + return err; +} + +static void +_dns_shent(int stayopen) +{ + res_state statp; + + statp = __res_state(); + if ((statp->options & RES_INIT) == 0) { + if (res_ninit(statp) < 0) + return; + } + if (stayopen) + statp->options |= RES_STAYOPEN | RES_USEVC; +} + +static void +_dns_ehent(void) +{ + res_state statp; + + statp = __res_state(); + statp->options &= ~(RES_STAYOPEN | RES_USEVC); + res_nclose(statp); +} + +#ifdef ICMPNL + +/* + * experimental: + * draft-ietf-ipngwg-icmp-namelookups-02.txt + * ifindex is assumed to be encoded in addr. + */ +#include <sys/uio.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> + +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 struct _icmp_host_cache *hc_head; + + THREAD_LOCK(); + for (hc = hc_head; hc; hc = hc->hc_next) { + if (hc->hc_ifindex == ifindex + && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) { + THREAD_UNLOCK(); + return hc->hc_name; /* XXX: never freed */ + } + } + THREAD_UNLOCK(); + + 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)getpid(); + 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); + THREAD_LOCK(); + hc->hc_next = hc_head; + hc_head = hc; + THREAD_UNLOCK(); + 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/netdb_private.h b/lib/libc/net/netdb_private.h new file mode 100644 index 0000000..b48dd7b --- /dev/null +++ b/lib/libc/net/netdb_private.h @@ -0,0 +1,145 @@ +/*- + * Copyright (C) 2005 The FreeBSD 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE 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. + * + * $FreeBSD$ + */ + +#ifndef _NETDB_PRIVATE_H_ +#define _NETDB_PRIVATE_H_ + +#include <stdio.h> /* XXX: for FILE */ + +#define NETDB_THREAD_ALLOC(name) \ +static struct name name; \ +static thread_key_t name##_key; \ +static once_t name##_init_once = ONCE_INITIALIZER; \ +static int name##_thr_keycreated = 0; \ +\ +static void name##_free(void *); \ +\ +static void \ +name##_keycreate(void) \ +{ \ + name##_thr_keycreated = \ + (thr_keycreate(&name##_key, name##_free) == 0); \ +} \ +\ +struct name * \ +__##name##_init(void) \ +{ \ + struct name *he; \ + \ + if (thr_main() != 0) \ + return (&name); \ + if (thr_once(&name##_init_once, name##_keycreate) != 0 || \ + !name##_thr_keycreated) \ + return (NULL); \ + if ((he = thr_getspecific(name##_key)) != NULL) \ + return (he); \ + if ((he = calloc(1, sizeof(*he))) == NULL) \ + return (NULL); \ + if (thr_setspecific(name##_key, he) == 0) \ + return (he); \ + free(he); \ + return (NULL); \ +} + +#define _MAXALIASES 35 +#define _MAXLINELEN 1024 +#define _MAXADDRS 35 +#define _HOSTBUFSIZE (8 * 1024) +#define _NETBUFSIZE 1025 + +struct hostent_data { + uint32_t host_addr[4]; /* IPv4 or IPv6 */ + char *h_addr_ptrs[_MAXADDRS + 1]; + char *host_aliases[_MAXALIASES]; + char hostbuf[_HOSTBUFSIZE]; + FILE *hostf; + int stayopen; +#ifdef YP + char *yp_domain; +#endif +}; + +struct netent_data { + char *net_aliases[_MAXALIASES]; + char netbuf[_NETBUFSIZE]; + FILE *netf; + int stayopen; +#ifdef YP + char *yp_domain; +#endif +}; + +struct protoent_data { + FILE *fp; + char *aliases[_MAXALIASES]; + int stayopen; + char line[_MAXLINELEN + 1]; +}; + +struct hostdata { + struct hostent host; + char data[sizeof(struct hostent_data)]; +}; + +struct netdata { + struct netent net; + char data[sizeof(struct netent_data)]; +}; + +struct protodata { + struct protoent proto; + char data[sizeof(struct protoent_data)]; +}; + +struct hostdata *__hostdata_init(void); +struct hostent *__hostent_init(void); +struct hostent_data *__hostent_data_init(void); +struct netdata *__netdata_init(void); +struct netent_data *__netent_data_init(void); +struct protodata *__protodata_init(void); +struct protoent_data *__protoent_data_init(void); +int __copy_hostent(struct hostent *, struct hostent *, char *, size_t); +int __copy_netent(struct netent *, struct netent *, char *, size_t); +int __copy_protoent(struct protoent *, struct protoent *, char *, size_t); + +void __endprotoent_p(struct protoent_data *); +int __getprotoent_p(struct protoent *, struct protoent_data *); +void __setprotoent_p(int, struct protoent_data *); +void _endhostdnsent(void); +void _endhosthtent(struct hostent_data *); +void _endnetdnsent(void); +void _endnethtent(struct netent_data *); +struct hostent *_gethostbynisaddr(const void *, socklen_t, int); +struct hostent *_gethostbynisname(const char *, int); +void _map_v4v6_address(const char *, char *); +void _map_v4v6_hostent(struct hostent *, char **, char *); +void _sethostdnsent(int); +void _sethosthtent(int, struct hostent_data *); +void _setnetdnsent(int); +void _setnethtent(int, struct netent_data *); + +#endif /* _NETDB_PRIVATE_H_ */ diff --git a/lib/libc/net/nscache.c b/lib/libc/net/nscache.c new file mode 100644 index 0000000..98a4367 --- /dev/null +++ b/lib/libc/net/nscache.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <nsswitch.h> +#include <stdlib.h> +#include <string.h> +#include "un-namespace.h" +#include "nscachedcli.h" +#include "nscache.h" + +#define NSS_CACHE_KEY_INITIAL_SIZE (256) +#define NSS_CACHE_KEY_SIZE_LIMIT (NSS_CACHE_KEY_INITIAL_SIZE << 4) + +#define NSS_CACHE_BUFFER_INITIAL_SIZE (1024) +#define NSS_CACHE_BUFFER_SIZE_LIMIT (NSS_CACHE_BUFFER_INITIAL_SIZE << 8) + +#define CACHED_SOCKET_PATH "/var/run/cached" + +int +__nss_cache_handler(void *retval, void *mdata, va_list ap) +{ + return (NS_UNAVAIL); +} + +int +__nss_common_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size, size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE); + memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE); + cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE; + va_copy(ap_new, ap); + + do { + size = cache_data->key_size; + res = cache_info->id_func(cache_data->key, &size, ap_new, + cache_info->mdata); + va_end(ap_new); + if (res == NS_RETURN) { + if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT) + break; + + cache_data->key_size <<= 1; + cache_data->key = realloc(cache_data->key, + cache_data->key_size); + memset(cache_data->key, 0, cache_data->key_size); + va_copy(ap_new, ap); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + cache_data->key_size = size; + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + res = -1; + break; + } + res = __cached_read(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, + &buffer_size); + __close_cached_connection(connection); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + if (buffer_size == 0) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (NS_RETURN); + } + + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + return (res); + } else + res = 0; + } + + if (res == 0) { + free(cache_data->key); + cache_data->key = NULL; + cache_data->key_size = 0; + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_common_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_connection connection; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + __close_cached_connection(connection); + free(cache_data->key); + free(buffer); + return (res); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, buffer, buffer_size); + __close_cached_connection(connection); + + free(cache_data->key); + free(buffer); + + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_common_cache_write_negative(void *mdata) +{ + struct cached_connection_params params; + cached_connection connection; + int res; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_data->key == NULL) + return (NS_UNAVAIL); + + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + connection = __open_cached_connection(¶ms); + if (connection == NULL) { + free(cache_data->key); + return (NS_UNAVAIL); + } + + res = __cached_write(connection, cache_info->entry_name, + cache_data->key, cache_data->key_size, NULL, 0); + __close_cached_connection(connection); + + free(cache_data->key); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_read(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_read_session rs; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + rs = cache_info->get_mp_rs_func(); + if (rs == INVALID_CACHED_MP_READ_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + rs = __open_cached_mp_read_session(¶ms, + cache_info->entry_name); + if (rs == INVALID_CACHED_MP_READ_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_rs_func(rs); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + res = __cached_mp_read(rs, buffer, &buffer_size); + if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) { + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == -2); + + if (res == 0) { + va_copy(ap_new, ap); + res = cache_info->unmarshal_func(buffer, buffer_size, retval, + ap_new, cache_info->mdata); + va_end(ap_new); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } else + res = 0; + } else { + free(buffer); + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + return (res == -1 ? NS_RETURN : NS_UNAVAIL); + } + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_NOTFOUND); +} + +int +__nss_mp_cache_write(void *retval, void *mdata, va_list ap) +{ + struct cached_connection_params params; + cached_mp_write_session ws; + + char *buffer; + size_t buffer_size; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + va_list ap_new; + int res; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) { + memset(¶ms, 0, sizeof(struct cached_connection_params)); + params.socket_path = CACHED_SOCKET_PATH; + + ws = __open_cached_mp_write_session(¶ms, + cache_info->entry_name); + if (ws == INVALID_CACHED_MP_WRITE_SESSION) + return (NS_UNAVAIL); + + cache_info->set_mp_ws_func(ws); + } + + buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE; + buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE); + memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE); + + do { + size_t size; + + size = buffer_size; + va_copy(ap_new, ap); + res = cache_info->marshal_func(buffer, &size, retval, ap_new, + cache_info->mdata); + va_end(ap_new); + + if (res == NS_RETURN) { + if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT) + break; + + buffer_size <<= 1; + buffer = (char *)realloc(buffer, buffer_size); + memset(buffer, 0, buffer_size); + } + } while (res == NS_RETURN); + + if (res != NS_SUCCESS) { + free(buffer); + return (res); + } + + res = __cached_mp_write(ws, buffer, buffer_size); + + free(buffer); + return (res == 0 ? NS_SUCCESS : NS_UNAVAIL); +} + +int +__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __close_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + return (NS_UNAVAIL); +} + +int +__nss_mp_cache_end(void *retval, void *mdata, va_list ap) +{ + cached_mp_write_session ws; + cached_mp_read_session rs; + + nss_cache_info const *cache_info; + nss_cache_data *cache_data; + + cache_data = (nss_cache_data *)mdata; + cache_info = cache_data->info; + + ws = cache_info->get_mp_ws_func(); + if (ws != INVALID_CACHED_MP_WRITE_SESSION) { + __abandon_cached_mp_write_session(ws); + ws = INVALID_CACHED_MP_WRITE_SESSION; + cache_info->set_mp_ws_func(ws); + } + + rs = cache_info->get_mp_rs_func(); + if (rs != INVALID_CACHED_MP_READ_SESSION) { + __close_cached_mp_read_session(rs); + rs = INVALID_CACHED_MP_READ_SESSION; + cache_info->set_mp_rs_func(rs); + } + + return (NS_UNAVAIL); +} diff --git a/lib/libc/net/nscachedcli.c b/lib/libc/net/nscachedcli.c new file mode 100644 index 0000000..d95a0e8 --- /dev/null +++ b/lib/libc/net/nscachedcli.c @@ -0,0 +1,576 @@ +/*- + * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/event.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "un-namespace.h" +#include "nscachedcli.h" + +#define NS_DEFAULT_CACHED_IO_TIMEOUT 4 + +static int safe_write(struct cached_connection_ *, const void *, size_t); +static int safe_read(struct cached_connection_ *, void *, size_t); +static int send_credentials(struct cached_connection_ *, int); + +/* + * safe_write writes data to the specified connection and tries to do it in + * the very safe manner. We ensure, that we can write to the socket with + * kevent. If the data_size can't be sent in one piece, then it would be + * splitted. + */ +static int +safe_write(struct cached_connection_ *connection, const void *data, + size_t data_size) +{ + struct kevent eventlist; + int nevents; + size_t result; + ssize_t s_result; + struct timespec timeout; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, + 1, &timeout); + if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) { + s_result = _write(connection->sockfd, data + result, + eventlist.data < data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * safe_read reads data from connection and tries to do it in the very safe + * and stable way. It uses kevent to ensure, that the data are availabe for + * reading. If the amount of data to be read is too large, then they would + * be splitted. + */ +static int +safe_read(struct cached_connection_ *connection, void *data, size_t data_size) +{ + struct kevent eventlist; + size_t result; + ssize_t s_result; + struct timespec timeout; + int nevents; + + if (data_size == 0) + return (0); + + timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT; + timeout.tv_nsec = 0; + result = 0; + do { + nevents = kevent(connection->read_queue, NULL, 0, &eventlist, + 1, &timeout); + if (nevents == 1 && eventlist.filter == EVFILT_READ) { + s_result = _read(connection->sockfd, data + result, + eventlist.data <= data_size - result ? + eventlist.data : data_size - result); + if (s_result == -1) + return (-1); + else + result += s_result; + + if (eventlist.flags & EV_EOF) + return (result < data_size ? -1 : 0); + } else + return (-1); + } while (result < data_size); + + return (0); +} + +/* + * Sends the credentials information to the connection along with the + * communication element type. + */ +static int +send_credentials(struct cached_connection_ *connection, int type) +{ + struct kevent eventlist; + int nevents; + ssize_t result; + int res; + + struct msghdr cred_hdr; + struct iovec iov; + + struct { + struct cmsghdr hdr; + char cred[CMSG_SPACE(sizeof(struct cmsgcred))]; + } cmsg; + + memset(&cmsg, 0, sizeof(cmsg)); + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_CREDS; + + memset(&cred_hdr, 0, sizeof(struct msghdr)); + cred_hdr.msg_iov = &iov; + cred_hdr.msg_iovlen = 1; + cred_hdr.msg_control = (caddr_t)&cmsg; + cred_hdr.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); + + iov.iov_base = &type; + iov.iov_len = sizeof(int); + + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + NOTE_LOWAT, sizeof(int), NULL); + res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + + nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, + NULL); + if (nevents == 1 && eventlist.filter == EVFILT_WRITE) { + result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? + -1 : 0; + EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD, + 0, 0, NULL); + kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL); + return (result); + } else + return (-1); +} + +/* + * Opens the connection with the specified params. Initializes all kqueues. + */ +struct cached_connection_ * +__open_cached_connection(struct cached_connection_params const *params) +{ + struct cached_connection_ *retval; + struct kevent eventlist; + struct sockaddr_un client_address; + int client_address_len, client_socket; + int res; + + assert(params != NULL); + + client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0); + client_address.sun_family = PF_LOCAL; + strncpy(client_address.sun_path, params->socket_path, + sizeof(client_address.sun_path)); + client_address_len = sizeof(client_address.sun_family) + + strlen(client_address.sun_path) + 1; + + res = _connect(client_socket, (struct sockaddr *)&client_address, + client_address_len); + if (res == -1) { + _close(client_socket); + return (NULL); + } + _fcntl(client_socket, F_SETFL, O_NONBLOCK); + + retval = malloc(sizeof(struct cached_connection_)); + assert(retval != NULL); + memset(retval, 0, sizeof(struct cached_connection_)); + + retval->sockfd = client_socket; + + retval->write_queue = kqueue(); + assert(retval->write_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); + + retval->read_queue = kqueue(); + assert(retval->read_queue != -1); + + EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); + res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); + + return (retval); +} + +void +__close_cached_connection(struct cached_connection_ *connection) +{ + assert(connection != NULL); + + _close(connection->sockfd); + _close(connection->read_queue); + _close(connection->write_queue); + free(connection); +} + +/* + * This function is very close to the cache_write function of the caching + * library, which is used in the caching daemon. It caches the data with the + * specified key in the cache entry with entry_name. + */ +int +__cached_write(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, const char *data, size_t data_size) +{ + size_t name_size; + int error_code; + int result; + + error_code = -1; + result = 0; + result = send_credentials(connection, CET_WRITE_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_write(connection, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * This function is very close to the cache_read function of the caching + * library, which is used in the caching daemon. It reads cached data with the + * specified key from the cache entry with entry_name. + */ +int +__cached_read(struct cached_connection_ *connection, const char *entry_name, + const char *key, size_t key_size, char *data, size_t *data_size) +{ + size_t name_size, result_size; + int error_code, rec_error_code; + int result; + + assert(connection != NULL); + result = 0; + error_code = -1; + + result = send_credentials(connection, CET_READ_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, &key_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_write(connection, key, key_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(connection, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(connection, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +/* + * Initializes the mp_write_session. For such a session the new connection + * would be opened. The data should be written to the session with + * __cached_mp_write function. The __close_cached_mp_write_session function + * should be used to submit session and __abandon_cached_mp_write_session - to + * abandon it. When the session is submitted, the whole se + */ +struct cached_connection_ * +__open_cached_mp_write_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +/* + * Adds new portion of data to the opened write session + */ +int +__cached_mp_write(struct cached_connection_ *ws, const char *data, + size_t data_size) +{ + int request, result; + int error_code; + + error_code = -1; + + request = CET_MP_WRITE_SESSION_WRITE_REQUEST; + result = safe_write(ws, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_write(ws, &data_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(ws, data, data_size); + if (result != 0) + goto fin; + + result = safe_read(ws, &error_code, sizeof(int)); + if (result != 0) + error_code = -1; + +fin: + return (error_code); +} + +/* + * Abandons all operations with the write session. All data, that were written + * to the session before, are discarded. + */ +int +__abandon_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (result); +} + +/* + * Gracefully closes the write session. The data, that were previously written + * to the session, are committed. + */ +int +__close_cached_mp_write_session(struct cached_connection_ *ws) +{ + int notification; + int result; + + notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION; + result = safe_write(ws, ¬ification, sizeof(int)); + __close_cached_connection(ws); + return (0); +} + +struct cached_connection_ * +__open_cached_mp_read_session(struct cached_connection_params const *params, + const char *entry_name) +{ + struct cached_connection_ *connection, *retval; + size_t name_size; + int error_code; + int result; + + retval = NULL; + connection = __open_cached_connection(params); + if (connection == NULL) + return (NULL); + connection->mp_flag = 1; + + result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST); + if (result != 0) + goto fin; + + name_size = strlen(entry_name); + result = safe_write(connection, &name_size, sizeof(size_t)); + if (result != 0) + goto fin; + + result = safe_write(connection, entry_name, name_size); + if (result != 0) + goto fin; + + result = safe_read(connection, &error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (error_code != 0) + result = error_code; + +fin: + if (result != 0) + __close_cached_connection(connection); + else + retval = connection; + return (retval); +} + +int +__cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size) +{ + size_t result_size; + int error_code, rec_error_code; + int request, result; + + error_code = -1; + request = CET_MP_READ_SESSION_READ_REQUEST; + result = safe_write(rs, &request, sizeof(int)); + if (result != 0) + goto fin; + + result = safe_read(rs, &rec_error_code, sizeof(int)); + if (result != 0) + goto fin; + + if (rec_error_code != 0) { + error_code = rec_error_code; + goto fin; + } + + result = safe_read(rs, &result_size, sizeof(size_t)); + if (result != 0) + goto fin; + + if (result_size > *data_size) { + *data_size = result_size; + error_code = -2; + goto fin; + } + + result = safe_read(rs, data, result_size); + if (result != 0) + goto fin; + + *data_size = result_size; + error_code = 0; + +fin: + return (error_code); +} + +int +__close_cached_mp_read_session(struct cached_connection_ *rs) +{ + + __close_cached_connection(rs); + return (0); +} diff --git a/lib/libc/net/nsdispatch.3 b/lib/libc/net/nsdispatch.3 new file mode 100644 index 0000000..06af7af --- /dev/null +++ b/lib/libc/net/nsdispatch.3 @@ -0,0 +1,250 @@ +.\" $NetBSD: nsdispatch.3,v 1.8 1999/03/22 19:44:53 garbled Exp $ +.\" +.\" Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 16, 2003 +.Dt NSDISPATCH 3 +.Os +.Sh NAME +.Nm nsdispatch +.Nd name-service switch dispatcher routine +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In stdarg.h +.In nsswitch.h +.Ft int +.Fo nsdispatch +.Fa "void *retval" +.Fa "const ns_dtab dtab[]" +.Fa "const char *database" +.Fa "const char *method_name" +.Fa "const ns_src defaults[]" +.Fa "..." +.Fc +.Sh DESCRIPTION +The +.Fn nsdispatch +function invokes the methods specified in +.Va dtab +in the order given by +.Xr nsswitch.conf 5 +for the database +.Va database +until a successful entry is found. +.Pp +.Va retval +is passed to each method to modify as necessary, to pass back results to +the caller of +.Fn nsdispatch . +.Pp +Each method has the function signature described by the typedef: +.Pp +.Ft typedef int +.Fn \*(lp*nss_method\*(rp "void *retval" "void *mdata" "va_list *ap" ; +.Pp +.Va dtab +is an array of +.Va ns_dtab +structures, which have the following format: +.Bd -literal -offset indent +typedef struct _ns_dtab { + const char *src; + nss_method method; + void *mdata; +} ns_dtab; +.Ed +.Pp +.Bd -ragged -offset indent +The +.Fa dtab +array should consist of one entry for each source type that is +implemented, with +.Va src +as the name of the source, +.Va method +as a function which handles that source, and +.Va mdata +as a handle on arbitrary data to be passed to the method. +The last entry in +.Va dtab +should contain +.Dv NULL +values for +.Va src , +.Va method , +and +.Va mdata . +.Ed +.Pp +Additionally, methods may be implemented in NSS modules, in +which case they are selected using the +.Fa database +and +.Fa method_name +arguments along with the configured source. +(The methods supplied via +.Fa dtab +take priority over those implemented in NSS modules in the event +of a conflict.) +.Pp +.Va defaults +contains a list of default sources to try if +.Xr nsswitch.conf 5 +is missing or corrupted, or if there is no relevant entry for +.Va database . +It is an array of +.Va ns_src +structures, which have the following format: +.Bd -literal -offset indent +typedef struct _ns_src { + const char *src; + u_int32_t flags; +} ns_src; +.Ed +.Pp +.Bd -ragged -offset indent +The +.Fa defaults +array should consist of one entry for each source to be configured by +default indicated by +.Va src , +and +.Va flags +set to the criterion desired +(usually +.Dv NS_SUCCESS ; +refer to +.Sx Method return values +for more information). +The last entry in +.Va defaults +should have +.Va src +set to +.Dv NULL +and +.Va flags +set to 0. +.Pp +For convenience, a global variable defined as: +.Dl extern const ns_src __nsdefaultsrc[]; +exists which contains a single default entry for the source +.Sq files +that may be used by callers which do not require complicated default +rules. +.Ed +.Pp +.Sq Va ... +are optional extra arguments, which are passed to the appropriate method +as a variable argument list of the type +.Vt va_list . +.Ss Valid source types +While there is support for arbitrary sources, the following +#defines for commonly implemented sources are available: +.Bl -column NSSRC_COMPAT compat -offset indent +.It Sy "#define value" +.It Dv NSSRC_FILES Ta """files"" +.It Dv NSSRC_DNS Ta """dns"" +.It Dv NSSRC_NIS Ta """nis"" +.It Dv NSSRC_COMPAT Ta """compat"" +.El +.Pp +Refer to +.Xr nsswitch.conf 5 +for a complete description of what each source type is. +.Pp +.Ss Method return values +The +.Vt nss_method +functions must return one of the following values depending upon status +of the lookup: +.Bl -column "Return value" "Status code" +.It Sy "Return value Status code" +.It Dv NS_SUCCESS Ta success +.It Dv NS_NOTFOUND Ta notfound +.It Dv NS_UNAVAIL Ta unavail +.It Dv NS_TRYAGAIN Ta tryagain +.It Dv NS_RETURN Ta -none- +.El +.Pp +Refer to +.Xr nsswitch.conf 5 +for a complete description of each status code. +.Pp +The +.Fn nsdispatch +function returns the value of the method that caused the dispatcher to +terminate, or +.Dv NS_NOTFOUND +otherwise. +.Sh SEE ALSO +.Xr hesiod 3 , +.Xr stdarg 3 , +.Xr nsswitch.conf 5 , +.Xr yp 8 +.Sh HISTORY +The +.Fn nsdispatch +function first appeared in +.Fx 5.0 . +It was imported from the +.Nx +Project, +where it appeared first in +.Nx 1.4 . +Support for NSS modules first appeared in +.Fx 5.1 . +.Sh AUTHORS +Luke Mewburn +.Aq lukem@netbsd.org +wrote this freely-distributable name-service switch implementation, +using ideas from the +.Tn ULTRIX +svc.conf(5) +and +.Tn Solaris +nsswitch.conf(4) +manual pages. +The +.Fx +Project +added the support for threads and NSS modules, and normalized the uses +of +.Fn nsdispatch +within the standard C library. diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c new file mode 100644 index 0000000..0e3c419 --- /dev/null +++ b/lib/libc/net/nsdispatch.c @@ -0,0 +1,715 @@ +/* $NetBSD: nsdispatch.c,v 1.9 1999/01/25 00:16:17 lukem Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * Jacques A. Vidrine, Safeport Network Services, and Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/param.h> +#include <sys/stat.h> + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#define _NS_PRIVATE +#include <nsswitch.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "un-namespace.h" +#ifdef NS_CACHING +#include "nscache.h" +#endif + +enum _nss_constants { + /* Number of elements allocated when we grow a vector */ + ELEMSPERCHUNK = 8 +}; + +/* + * Global NSS data structures are mostly read-only, but we update + * them when we read or re-read the nsswitch.conf. + */ +static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* + * Runtime determination of whether we are dynamically linked or not. + */ +extern int _DYNAMIC __attribute__ ((weak)); +#define is_dynamic() (&_DYNAMIC != NULL) + +/* + * default sourcelist: `files' + */ +const ns_src __nsdefaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { 0 }, +}; + +/* Database, source mappings. */ +static unsigned int _nsmapsize; +static ns_dbt *_nsmap = NULL; + +/* NSS modules. */ +static unsigned int _nsmodsize; +static ns_mod *_nsmod; + +/* Placeholder for builtin modules' dlopen `handle'. */ +static int __nss_builtin_handle; +static void *nss_builtin_handle = &__nss_builtin_handle; + +#ifdef NS_CACHING +/* + * Cache lookup cycle prevention function - if !NULL then no cache lookups + * will be made + */ +static void *nss_cache_cycle_prevention_func = NULL; +#endif + +/* + * Attempt to spew relatively uniform messages to syslog. + */ +#define nss_log(level, fmt, ...) \ + syslog((level), "NSSWITCH(%s): " fmt, __func__, __VA_ARGS__) +#define nss_log_simple(level, s) \ + syslog((level), "NSSWITCH(%s): " s, __func__) + +/* + * Dynamically growable arrays are used for lists of databases, sources, + * and modules. The following `vector' interface is used to isolate the + * common operations. + */ +typedef int (*vector_comparison)(const void *, const void *); +typedef void (*vector_free_elem)(void *); +static void vector_sort(void *, unsigned int, size_t, + vector_comparison); +static void vector_free(void *, unsigned int *, size_t, + vector_free_elem); +static void *vector_ref(unsigned int, void *, unsigned int, size_t); +static void *vector_search(const void *, void *, unsigned int, size_t, + vector_comparison); +static void *vector_append(const void *, void *, unsigned int *, size_t); + + +/* + * Internal interfaces. + */ +static int string_compare(const void *, const void *); +static int mtab_compare(const void *, const void *); +static int nss_configure(void); +static void ns_dbt_free(ns_dbt *); +static void ns_mod_free(ns_mod *); +static void ns_src_free(ns_src **, int); +static void nss_load_builtin_modules(void); +static void nss_load_module(const char *, nss_module_register_fn); +static void nss_atexit(void); +/* nsparser */ +extern FILE *_nsyyin; + + +/* + * The vector operations + */ +static void +vector_sort(void *vec, unsigned int count, size_t esize, + vector_comparison comparison) +{ + qsort(vec, count, esize, comparison); +} + + +static void * +vector_search(const void *key, void *vec, unsigned int count, size_t esize, + vector_comparison comparison) +{ + return (bsearch(key, vec, count, esize, comparison)); +} + + +static void * +vector_append(const void *elem, void *vec, unsigned int *count, size_t esize) +{ + void *p; + + if ((*count % ELEMSPERCHUNK) == 0) { + p = realloc(vec, (*count + ELEMSPERCHUNK) * esize); + if (p == NULL) { + nss_log_simple(LOG_ERR, "memory allocation failure"); + return (vec); + } + vec = p; + } + memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); + (*count)++; + return (vec); +} + + +static void * +vector_ref(unsigned int i, void *vec, unsigned int count, size_t esize) +{ + if (i < count) + return (void *)((uintptr_t)vec + (i * esize)); + else + return (NULL); +} + + +#define VECTOR_FREE(v, c, s, f) \ + do { vector_free(v, c, s, f); v = NULL; } while (0) +static void +vector_free(void *vec, unsigned int *count, size_t esize, + vector_free_elem free_elem) +{ + unsigned int i; + void *elem; + + for (i = 0; i < *count; i++) { + elem = vector_ref(i, vec, *count, esize); + if (elem != NULL) + free_elem(elem); + } + free(vec); + *count = 0; +} + +/* + * Comparison functions for vector_search. + */ +static int +string_compare(const void *a, const void *b) +{ + return (strcasecmp(*(const char * const *)a, *(const char * const *)b)); +} + + +static int +mtab_compare(const void *a, const void *b) +{ + int cmp; + + cmp = strcmp(((const ns_mtab *)a)->name, ((const ns_mtab *)b)->name); + if (cmp != 0) + return (cmp); + else + return (strcmp(((const ns_mtab *)a)->database, + ((const ns_mtab *)b)->database)); +} + +/* + * NSS nsmap management. + */ +void +_nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) +{ + const ns_mod *modp; + + dbt->srclist = vector_append(src, dbt->srclist, &dbt->srclistsize, + sizeof(*src)); + modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod), + string_compare); + if (modp == NULL) + nss_load_module(src->name, NULL); +} + + +#ifdef _NSS_DEBUG +void +_nsdbtdump(const ns_dbt *dbt) +{ + int i; + + printf("%s (%d source%s):", dbt->name, dbt->srclistsize, + dbt->srclistsize == 1 ? "" : "s"); + for (i = 0; i < (int)dbt->srclistsize; i++) { + printf(" %s", dbt->srclist[i].name); + if (!(dbt->srclist[i].flags & + (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && + (dbt->srclist[i].flags & NS_SUCCESS)) + continue; + printf(" ["); + if (!(dbt->srclist[i].flags & NS_SUCCESS)) + printf(" SUCCESS=continue"); + if (dbt->srclist[i].flags & NS_UNAVAIL) + printf(" UNAVAIL=return"); + if (dbt->srclist[i].flags & NS_NOTFOUND) + printf(" NOTFOUND=return"); + if (dbt->srclist[i].flags & NS_TRYAGAIN) + printf(" TRYAGAIN=return"); + printf(" ]"); + } + printf("\n"); +} +#endif + + +/* + * The first time nsdispatch is called (during a process's lifetime, + * or after nsswitch.conf has been updated), nss_configure will + * prepare global data needed by NSS. + */ +static int +nss_configure(void) +{ + static pthread_mutex_t conf_lock = PTHREAD_MUTEX_INITIALIZER; + static time_t confmod; + struct stat statbuf; + int result, isthreaded; + const char *path; +#ifdef NS_CACHING + void *handle; +#endif + + result = 0; + isthreaded = __isthreaded; +#if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT) + /* NOTE WELL: THIS IS A SECURITY HOLE. This must only be built + * for debugging purposes and MUST NEVER be used in production. + */ + path = getenv("NSSWITCH_CONF"); + if (path == NULL) +#endif + path = _PATH_NS_CONF; + if (stat(path, &statbuf) != 0) + return (0); + if (statbuf.st_mtime <= confmod) + return (0); + if (isthreaded) { + result = _pthread_mutex_trylock(&conf_lock); + if (result != 0) + return (0); + (void)_pthread_rwlock_unlock(&nss_lock); + result = _pthread_rwlock_wrlock(&nss_lock); + if (result != 0) + goto fin2; + } + _nsyyin = fopen(path, "r"); + if (_nsyyin == NULL) + goto fin; + VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), + (vector_free_elem)ns_dbt_free); + VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), + (vector_free_elem)ns_mod_free); + nss_load_builtin_modules(); + _nsyyparse(); + (void)fclose(_nsyyin); + vector_sort(_nsmap, _nsmapsize, sizeof(*_nsmap), string_compare); + if (confmod == 0) + (void)atexit(nss_atexit); + confmod = statbuf.st_mtime; + +#ifdef NS_CACHING + handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); + if (handle != NULL) { + nss_cache_cycle_prevention_func = dlsym(handle, + "_nss_cache_cycle_prevention_function"); + dlclose(handle); + } +#endif +fin: + if (isthreaded) { + (void)_pthread_rwlock_unlock(&nss_lock); + if (result == 0) + result = _pthread_rwlock_rdlock(&nss_lock); + } +fin2: + if (isthreaded) + (void)_pthread_mutex_unlock(&conf_lock); + return (result); +} + + +void +_nsdbtput(const ns_dbt *dbt) +{ + unsigned int i; + ns_dbt *p; + + for (i = 0; i < _nsmapsize; i++) { + p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); + if (string_compare(&dbt->name, &p->name) == 0) { + /* overwrite existing entry */ + if (p->srclist != NULL) + ns_src_free(&p->srclist, p->srclistsize); + memmove(p, dbt, sizeof(*dbt)); + return; + } + } + _nsmap = vector_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); +} + + +static void +ns_dbt_free(ns_dbt *dbt) +{ + ns_src_free(&dbt->srclist, dbt->srclistsize); +} + + +static void +ns_src_free(ns_src **src, int srclistsize) +{ + int i; + + for (i = 0; i < srclistsize; i++) + if ((*src)[i].name != NULL) + /* This one was allocated by nslexer. You'll just + * have to trust me. + */ + free((void *)((*src)[i].name)); + free(*src); + *src = NULL; +} + + + +/* + * NSS module management. + */ +/* The built-in NSS modules are all loaded at once. */ +#define NSS_BACKEND(name, reg) \ +ns_mtab *reg(unsigned int *, nss_module_unregister_fn *); +#include "nss_backends.h" +#undef NSS_BACKEND + +static void +nss_load_builtin_modules(void) +{ +#define NSS_BACKEND(name, reg) nss_load_module(#name, reg); +#include "nss_backends.h" +#undef NSS_BACKEND +} + + +/* Load a built-in or dynamically linked module. If the `reg_fn' + * argument is non-NULL, assume a built-in module and use reg_fn to + * register it. Otherwise, search for a dynamic NSS module. + */ +static void +nss_load_module(const char *source, nss_module_register_fn reg_fn) +{ + char buf[PATH_MAX]; + ns_mod mod; + nss_module_register_fn fn; + + memset(&mod, 0, sizeof(mod)); + mod.name = strdup(source); + if (mod.name == NULL) { + nss_log_simple(LOG_ERR, "memory allocation failure"); + return; + } + if (reg_fn != NULL) { + /* The placeholder is required, as a NULL handle + * represents an invalid module. + */ + mod.handle = nss_builtin_handle; + fn = reg_fn; + } else if (!is_dynamic()) + goto fin; + else { + if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, + NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) + goto fin; + mod.handle = dlopen(buf, RTLD_LOCAL|RTLD_LAZY); + if (mod.handle == NULL) { +#ifdef _NSS_DEBUG + /* This gets pretty annoying since the built-in + * sources aren't modules yet. + */ + nss_log(LOG_DEBUG, "%s, %s", mod.name, dlerror()); +#endif + goto fin; + } + fn = (nss_module_register_fn)dlfunc(mod.handle, + "nss_module_register"); + if (fn == NULL) { + (void)dlclose(mod.handle); + mod.handle = NULL; + nss_log(LOG_ERR, "%s, %s", mod.name, dlerror()); + goto fin; + } + } + mod.mtab = fn(mod.name, &mod.mtabsize, &mod.unregister); + if (mod.mtab == NULL || mod.mtabsize == 0) { + if (mod.handle != nss_builtin_handle) + (void)dlclose(mod.handle); + mod.handle = NULL; + nss_log(LOG_ERR, "%s, registration failed", mod.name); + goto fin; + } + if (mod.mtabsize > 1) + qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), + mtab_compare); +fin: + _nsmod = vector_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); + vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare); +} + + + +static void +ns_mod_free(ns_mod *mod) +{ + + free(mod->name); + if (mod->handle == NULL) + return; + if (mod->unregister != NULL) + mod->unregister(mod->mtab, mod->mtabsize); + if (mod->handle != nss_builtin_handle) + (void)dlclose(mod->handle); +} + + + +/* + * Cleanup + */ +static void +nss_atexit(void) +{ + int isthreaded; + + isthreaded = __isthreaded; + if (isthreaded) + (void)_pthread_rwlock_wrlock(&nss_lock); + VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), + (vector_free_elem)ns_dbt_free); + VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), + (vector_free_elem)ns_mod_free); + if (isthreaded) + (void)_pthread_rwlock_unlock(&nss_lock); +} + + + +/* + * Finally, the actual implementation. + */ +static nss_method +nss_method_lookup(const char *source, const char *database, + const char *method, const ns_dtab disp_tab[], void **mdata) +{ + ns_mod *mod; + ns_mtab *match, key; + int i; + + if (disp_tab != NULL) + for (i = 0; disp_tab[i].src != NULL; i++) + if (strcasecmp(source, disp_tab[i].src) == 0) { + *mdata = disp_tab[i].mdata; + return (disp_tab[i].method); + } + mod = vector_search(&source, _nsmod, _nsmodsize, sizeof(*_nsmod), + string_compare); + if (mod != NULL && mod->handle != NULL) { + key.database = database; + key.name = method; + match = bsearch(&key, mod->mtab, mod->mtabsize, + sizeof(mod->mtab[0]), mtab_compare); + if (match != NULL) { + *mdata = match->mdata; + return (match->method); + } + } + if (is_dynamic()) + nss_log(LOG_DEBUG, "%s, %s, %s, not found", source, database, + method); + *mdata = NULL; + return (NULL); +} + + +__weak_reference(_nsdispatch, nsdispatch); + +int +_nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, + const char *method_name, const ns_src defaults[], ...) +{ + va_list ap; + const ns_dbt *dbt; + const ns_src *srclist; + nss_method method; + void *mdata; + int isthreaded, serrno, i, result, srclistsize; + +#ifdef NS_CACHING + nss_cache_data cache_data; + nss_cache_data *cache_data_p; + int cache_flag; +#endif + + isthreaded = __isthreaded; + serrno = errno; + if (isthreaded) { + result = _pthread_rwlock_rdlock(&nss_lock); + if (result != 0) { + result = NS_UNAVAIL; + goto fin; + } + } + result = nss_configure(); + if (result != 0) { + result = NS_UNAVAIL; + goto fin; + } + dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), + string_compare); + if (dbt != NULL) { + srclist = dbt->srclist; + srclistsize = dbt->srclistsize; + } else { + srclist = defaults; + srclistsize = 0; + while (srclist[srclistsize].name != NULL) + srclistsize++; + } + +#ifdef NS_CACHING + cache_data_p = NULL; + cache_flag = 0; +#endif + for (i = 0; i < srclistsize; i++) { + result = NS_NOTFOUND; + method = nss_method_lookup(srclist[i].name, database, + method_name, disp_tab, &mdata); + + if (method != NULL) { +#ifdef NS_CACHING + if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && + nss_cache_cycle_prevention_func == NULL) { +#ifdef NS_STRICT_LIBC_EID_CHECKING + if (issetugid() != 0) + continue; +#endif + cache_flag = 1; + + memset(&cache_data, 0, sizeof(nss_cache_data)); + cache_data.info = (nss_cache_info const *)mdata; + cache_data_p = &cache_data; + + va_start(ap, defaults); + if (cache_data.info->id_func != NULL) + result = __nss_common_cache_read(retval, + cache_data_p, ap); + else if (cache_data.info->marshal_func != NULL) + result = __nss_mp_cache_read(retval, + cache_data_p, ap); + else + result = __nss_mp_cache_end(retval, + cache_data_p, ap); + va_end(ap); + } else { + cache_flag = 0; + va_start(ap, defaults); + result = method(retval, mdata, ap); + va_end(ap); + } +#else /* NS_CACHING */ + va_start(ap, defaults); + result = method(retval, mdata, ap); + va_end(ap); +#endif /* NS_CACHING */ + + if (result & (srclist[i].flags)) + break; + } + } + +#ifdef NS_CACHING + if (cache_data_p != NULL && + (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { + va_start(ap, defaults); + if (result == NS_SUCCESS) { + if (cache_data.info->id_func != NULL) + __nss_common_cache_write(retval, cache_data_p, + ap); + else if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write(retval, cache_data_p, ap); + } else if (result == NS_NOTFOUND) { + if (cache_data.info->id_func == NULL) { + if (cache_data.info->marshal_func != NULL) + __nss_mp_cache_write_submit(retval, + cache_data_p, ap); + } else + __nss_common_cache_write_negative(cache_data_p); + } + va_end(ap); + } +#endif /* NS_CACHING */ + + if (isthreaded) + (void)_pthread_rwlock_unlock(&nss_lock); +fin: + errno = serrno; + return (result); +} diff --git a/lib/libc/net/nslexer.l b/lib/libc/net/nslexer.l new file mode 100644 index 0000000..34c79d9 --- /dev/null +++ b/lib/libc/net/nslexer.l @@ -0,0 +1,119 @@ +%{ +/* $NetBSD: nslexer.l,v 1.3 1999/01/25 00:16:17 lukem Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = + "$FreeBSD$"; +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include <ctype.h> +#define _NS_PRIVATE +#include <nsswitch.h> +#include <string.h> +#include <syslog.h> +#include "un-namespace.h" + +#include "nsparser.h" + +#define YY_NO_UNPUT + +%} + +%option yylineno + +BLANK [ \t] +CR \n +STRING [a-zA-Z][a-zA-Z0-9_]* + +%% + +{BLANK}+ ; /* skip whitespace */ + +#.* ; /* skip comments */ + +\\{CR} ; /* allow continuation */ + +{CR} return NL; + +[sS][uU][cC][cC][eE][sS][sS] return SUCCESS; +[uU][nN][aA][vV][aA][iI][lL] return UNAVAIL; +[nN][oO][tT][fF][oO][uU][nN][dD] return NOTFOUND; +[tT][rR][yY][aA][gG][aA][iI][nN] return TRYAGAIN; + +[rR][eE][tT][uU][rR][nN] return RETURN; +[cC][oO][nN][tT][iI][nN][uU][eE] return CONTINUE; + +{STRING} { + char *p; + int i; + + if ((p = strdup(yytext)) == NULL) { + syslog(LOG_ERR, + "NSSWITCH(nslexer): memory allocation failure"); + return ERRORTOKEN; + } + for (i = 0; i < strlen(p); i++) { + if (isupper((unsigned char)p[i])) + p[i] = tolower((unsigned char)p[i]); + } + _nsyylval.str = p; + return STRING; + } + +. return yytext[0]; + +%% + +#undef _nsyywrap +int +_nsyywrap() +{ + return 1; +} /* _nsyywrap */ + +void +_nsyyerror(msg) + const char *msg; +{ + + syslog(LOG_ERR, "NSSWITCH(nslexer): %s line %d: %s at '%s'", + _PATH_NS_CONF, yylineno, msg, yytext); +} /* _nsyyerror */ diff --git a/lib/libc/net/nsparser.y b/lib/libc/net/nsparser.y new file mode 100644 index 0000000..1f0fff1 --- /dev/null +++ b/lib/libc/net/nsparser.y @@ -0,0 +1,179 @@ +%{ +/* $NetBSD: nsparser.y,v 1.3 1999/01/25 00:16:18 lukem Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#define _NS_PRIVATE +#include <nsswitch.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include "un-namespace.h" + +static void _nsaddsrctomap(const char *); + +static ns_dbt curdbt; +static ns_src cursrc; +%} + +%union { + char *str; + int mapval; +} + +%token NL +%token SUCCESS UNAVAIL NOTFOUND TRYAGAIN +%token RETURN CONTINUE +%token ERRORTOKEN +%token <str> STRING + +%type <mapval> Status Action + +%% + +File + : /* empty */ + | Lines + ; + +Lines + : Entry + | Lines Entry + ; + +Entry + : NL + | Database ':' NL + | Database ':' Srclist NL + { + _nsdbtput(&curdbt); + } + | error NL + { + yyerrok; + } + ; + +Database + : STRING + { + curdbt.name = yylval.str; + curdbt.srclist = NULL; + curdbt.srclistsize = 0; + } + ; + +Srclist + : Item + | Srclist Item + ; + +Item + : STRING + { + cursrc.flags = NS_TERMINATE; + _nsaddsrctomap($1); + } + | STRING '[' { cursrc.flags = NS_SUCCESS; } Criteria ']' + { + _nsaddsrctomap($1); + } + ; + +Criteria + : Criterion + | Criteria Criterion + ; + +Criterion + : Status '=' Action + { + if ($3) /* if action == RETURN set RETURN bit */ + cursrc.flags |= $1; + else /* else unset it */ + cursrc.flags &= ~$1; + } + ; + +Status + : SUCCESS { $$ = NS_SUCCESS; } + | UNAVAIL { $$ = NS_UNAVAIL; } + | NOTFOUND { $$ = NS_NOTFOUND; } + | TRYAGAIN { $$ = NS_TRYAGAIN; } + ; + +Action + : RETURN { $$ = NS_ACTION_RETURN; } + | CONTINUE { $$ = NS_ACTION_CONTINUE; } + ; + +%% + +static void +_nsaddsrctomap(elem) + const char *elem; +{ + int i, lineno; + extern int _nsyylineno; + extern char * _nsyytext; + + lineno = _nsyylineno - (*_nsyytext == '\n' ? 1 : 0); + if (curdbt.srclistsize > 0) { + if (((strcasecmp(elem, NSSRC_COMPAT) == 0) && + (strcasecmp(curdbt.srclist[0].name, NSSRC_CACHE) != 0)) || + (strcasecmp(curdbt.srclist[0].name, NSSRC_COMPAT) == 0)) { + syslog(LOG_ERR, + "NSSWITCH(nsparser): %s line %d: 'compat' used with sources, other than 'cache'", + _PATH_NS_CONF, lineno); + return; + } + } + for (i = 0; i < curdbt.srclistsize; i++) { + if (strcasecmp(curdbt.srclist[i].name, elem) == 0) { + syslog(LOG_ERR, + "NSSWITCH(nsparser): %s line %d: duplicate source '%s'", + _PATH_NS_CONF, lineno, elem); + return; + } + } + cursrc.name = elem; + _nsdbtaddsrc(&curdbt, &cursrc); +} diff --git a/lib/libc/net/nss_backends.h b/lib/libc/net/nss_backends.h new file mode 100644 index 0000000..9bea37b --- /dev/null +++ b/lib/libc/net/nss_backends.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by + * Jacques A. Vidrine, Safeport Network Services, and Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Eventually, the implementations of existing built-in NSS functions + * may be moved into NSS modules and live here. + */ +#if 0 +NSS_BACKEND( files, _files_nss_module_register ) +NSS_BACKEND( dns, _dns_nss_module_register ) +NSS_BACKEND( nis, _nis_nss_module_register ) +NSS_BACKEND( compat, _compat_nss_module_register ) +#endif diff --git a/lib/libc/net/nss_compat.c b/lib/libc/net/nss_compat.c new file mode 100644 index 0000000..09a2d4f --- /dev/null +++ b/lib/libc/net/nss_compat.c @@ -0,0 +1,278 @@ +/*- + * Copyright (c) 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by + * Jacques A. Vidrine, Safeport Network Services, and Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Compatibility shims for the GNU C Library-style nsswitch interface. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/param.h> +#include <errno.h> +#include <nss.h> +#include <pthread.h> +#include <pthread_np.h> +#include "un-namespace.h" +#include "libc_private.h" + + +struct group; +struct passwd; + +static int terminator; + +#define DECLARE_TERMINATOR(x) \ +static pthread_key_t _term_key_##x; \ +static void \ +_term_create_##x(void) \ +{ \ + (void)_pthread_key_create(&_term_key_##x, NULL); \ +} \ +static void *_term_main_##x; \ +static pthread_once_t _term_once_##x = PTHREAD_ONCE_INIT + +#define SET_TERMINATOR(x, y) \ +do { \ + if (!__isthreaded || _pthread_main_np()) \ + _term_main_##x = (y); \ + else { \ + (void)_pthread_once(&_term_once_##x, _term_create_##x); \ + (void)_pthread_setspecific(_term_key_##x, y); \ + } \ +} while (0) + +#define CHECK_TERMINATOR(x) \ +(!__isthreaded || _pthread_main_np() ? \ + (_term_main_##x) : \ + ((void)_pthread_once(&_term_once_##x, _term_create_##x), \ + _pthread_getspecific(_term_key_##x))) + + + +DECLARE_TERMINATOR(group); + + +int +__nss_compat_getgrnam_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(const char *, struct group *, char *, size_t, int *); + const char *name; + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + name = va_arg(ap, const char *); + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(name, grp, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + return (status); +} + + +int +__nss_compat_getgrgid_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(gid_t, struct group *, char *, size_t, int *); + gid_t gid; + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + gid = va_arg(ap, gid_t); + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(gid, grp, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + return (status); +} + + +int +__nss_compat_getgrent_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(struct group *, char *, size_t, int *); + struct group *grp; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + if (CHECK_TERMINATOR(group)) + return (NS_NOTFOUND); + fn = mdata; + grp = va_arg(ap, struct group *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(grp, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct group **)retval = grp; + else if (status != NS_RETURN) + SET_TERMINATOR(group, &terminator); + return (status); +} + + +int +__nss_compat_setgrent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(group, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + +int +__nss_compat_endgrent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(group, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + + +DECLARE_TERMINATOR(passwd); + + +int +__nss_compat_getpwnam_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(const char *, struct passwd *, char *, size_t, int *); + const char *name; + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + name = va_arg(ap, const char *); + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(name, pwd, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + return (status); +} + + +int +__nss_compat_getpwuid_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(uid_t, struct passwd *, char *, size_t, int *); + uid_t uid; + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + fn = mdata; + uid = va_arg(ap, uid_t); + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(uid, pwd, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + return (status); +} + + +int +__nss_compat_getpwent_r(void *retval, void *mdata, va_list ap) +{ + int (*fn)(struct passwd *, char *, size_t, int *); + struct passwd *pwd; + char *buffer; + int *errnop; + size_t bufsize; + enum nss_status status; + + if (CHECK_TERMINATOR(passwd)) + return (NS_NOTFOUND); + fn = mdata; + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + status = fn(pwd, buffer, bufsize, errnop); + status = __nss_compat_result(status, *errnop); + if (status == NS_SUCCESS) + *(struct passwd **)retval = pwd; + else if (status != NS_RETURN) + SET_TERMINATOR(passwd, &terminator); + return (status); +} + + +int +__nss_compat_setpwent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(passwd, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} + + +int +__nss_compat_endpwent(void *retval, void *mdata, va_list ap) +{ + + SET_TERMINATOR(passwd, NULL); + ((int (*)(void))mdata)(); + return (NS_UNAVAIL); +} diff --git a/lib/libc/net/rcmd.3 b/lib/libc/net/rcmd.3 new file mode 100644 index 0000000..03403b8 --- /dev/null +++ b/lib/libc/net/rcmd.3 @@ -0,0 +1,302 @@ +.\" Copyright (c) 1983, 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: @(#)rcmd.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd March 3, 2000 +.Dt RCMD 3 +.Os +.Sh NAME +.Nm rcmd , +.Nm rresvport , +.Nm iruserok , +.Nm ruserok , +.Nm rcmd_af , +.Nm rresvport_af , +.Nm iruserok_sa +.Nd routines for returning a stream to a remote command +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn rcmd "char **ahost" "int inport" "const char *locuser" "const char *remuser" "const char *cmd" "int *fd2p" +.Ft int +.Fn rresvport "int *port" +.Ft int +.Fn iruserok "u_long raddr" "int superuser" "const char *ruser" "const char *luser" +.Ft int +.Fn ruserok "const char *rhost" "int superuser" "const char *ruser" "const char *luser" +.Ft int +.Fn rcmd_af "char **ahost" "int inport" "const char *locuser" "const char *remuser" "const char *cmd" "int *fd2p" "int af" +.Ft int +.Fn rresvport_af "int *port" "int af" +.Ft int +.Fn iruserok_sa "const void *addr" "int addrlen" "int superuser" "const char *ruser" "const char *luser" +.Sh DESCRIPTION +The +.Fn rcmd +function +is used by the super-user to execute a command on +a remote machine using an authentication scheme based +on reserved port numbers. +The +.Fn rresvport +function +returns a descriptor to a socket +with an address in the privileged port space. +The +.Fn ruserok +function +is used by servers +to authenticate clients requesting service with +.Fn rcmd . +All three functions are present in the same file and are used +by the +.Xr rshd 8 +server (among others). +.Pp +The +.Fn rcmd +function +looks up the host +.Fa *ahost +using +.Xr gethostbyname 3 , +returning -1 if the host does not exist. +Otherwise +.Fa *ahost +is set to the standard name of the host +and a connection is established to a server +residing at the well-known Internet port +.Fa inport . +.Pp +If the connection succeeds, +a socket in the Internet domain of type +.Dv SOCK_STREAM +is returned to the caller, and given to the remote +command as +.Dv stdin +and +.Dv stdout . +If +.Fa fd2p +is non-zero, then an auxiliary channel to a control +process will be set up, and a descriptor for it will be placed +in +.Fa *fd2p . +The control process will return diagnostic +output from the command (unit 2) on this channel, and will also +accept bytes on this channel as being +.Ux +signal numbers, to be +forwarded to the process group of the command. +If +.Fa fd2p +is 0, then the +.Dv stderr +(unit 2 of the remote +command) will be made the same as the +.Dv stdout +and no +provision is made for sending arbitrary signals to the remote process, +although you may be able to get its attention by using out-of-band data. +.Pp +The protocol is described in detail in +.Xr rshd 8 . +.Pp +The +.Fn rresvport +function is used to obtain a socket to which an address with a Privileged +Internet port is bound. +This socket is suitable for use by +.Fn rcmd +and several other functions. +Privileged Internet ports are those in the range 0 to 1023. +Only the super-user is allowed to bind an address of this sort +to a socket. +.Pp +The +.Fn iruserok +and +.Fn ruserok +functions take a remote host's IP address or name, as returned by the +.Xr gethostbyname 3 +routines, two user names and a flag indicating whether the local user's +name is that of the super-user. +Then, if the user is +.Em NOT +the super-user, it checks the +.Pa /etc/hosts.equiv +file. +If that lookup is not done, or is unsuccessful, the +.Pa .rhosts +in the local user's home directory is checked to see if the request for +service is allowed. +.Pp +If this file does not exist, is not a regular file, is owned by anyone +other than the user or the super-user, or is writable by anyone other +than the owner, the check automatically fails. +Zero is returned if the machine name is listed in the +.Dq Pa hosts.equiv +file, or the host and remote user name are found in the +.Dq Pa .rhosts +file; otherwise +.Fn iruserok +and +.Fn ruserok +return -1. +If the local domain (as obtained from +.Xr gethostname 3 ) +is the same as the remote domain, only the machine name need be specified. +.Pp +The +.Fn iruserok +function is strongly preferred for security reasons. +It requires trusting the local DNS at most, while the +.Fn ruserok +function requires trusting the entire DNS, which can be spoofed. +.Pp +The functions with an +.Dq Li _af +or +.Dq Li _sa +suffix, i.e., +.Fn rcmd_af , +.Fn rresvport_af +and +.Fn iruserok_sa , +work the same as the corresponding functions without a +suffix, except that they are capable of handling both IPv6 and IPv4 ports. +.Pp +The +.Dq Li _af +suffix means that the function has an additional +.Fa af +argument which is used to specify the address family, +(see below). +The +.Fa af +argument extension is implemented for functions +that have no binary address argument. +Instead, the +.Fa af +argument specifies which address family is desired. +.Pp +The +.Dq Li _sa +suffix means that the function has general socket address and +length arguments. +As the socket address is a protocol independent data structure, +IPv4 and IPv6 socket address can be passed as desired. +The +.Fa sa +argument extension is implemented for functions +that pass a protocol dependent binary address argument. +The argument needs to be replaced with a more general address structure +to support multiple address families in a general way. +.Pp +The functions with neither an +.Dq Li _af +suffix nor an +.Dq Li _sa +suffix work for IPv4 only, except for +.Fn ruserok +which can handle both IPv6 and IPv4. +To switch the address family, the +.Fa af +argument must be filled with +.Dv AF_INET , +or +.Dv AF_INET6 . +For +.Fn rcmd_af , +.Dv PF_UNSPEC +is also allowed. +.Sh DIAGNOSTICS +The +.Fn rcmd +function +returns a valid socket descriptor on success. +It returns -1 on error and prints a diagnostic message +on the standard error. +.Pp +The +.Fn rresvport +function +returns a valid, bound socket descriptor on success. +It returns -1 on error with the global value +.Va errno +set according to the reason for failure. +The error code +.Er EAGAIN +is overloaded to mean ``All network ports in use.'' +.Sh SEE ALSO +.Xr rlogin 1 , +.Xr rsh 1 , +.Xr intro 2 , +.Xr rlogind 8 , +.Xr rshd 8 +.Pp +.Rs +.%A W. Stevens +.%A M. Thomas +.%T "Advanced Socket API for IPv6" +.%O RFC2292 +.Re +.Rs +.%A W. Stevens +.%A M. Thomas +.%A E. Nordmark +.%T "Advanced Socket API for IPv6" +.%O RFC3542 +.Re +.Sh HISTORY +Most of these +functions appeared in +.Bx 4.2 . +The +.Fn rresvport_af +function +appeared in RFC2292, and was implemented by the WIDE project +for the Hydrangea IPv6 protocol stack kit. +The +.Fn rcmd_af +function +appeared in draft-ietf-ipngwg-rfc2292bis-01.txt, +and was implemented in the WIDE/KAME IPv6 protocol stack kit. +The +.Fn iruserok_sa +function +appeared in discussion on the IETF ipngwg mailing list, +and was implemented in +.Fx 4.0 . diff --git a/lib/libc/net/rcmd.c b/lib/libc/net/rcmd.c new file mode 100644 index 0000000..422e903 --- /dev/null +++ b/lib/libc/net/rcmd.c @@ -0,0 +1,765 @@ +/* + * Copyright (c) 1983, 1993, 1994 + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <signal.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdlib.h> +#include <unistd.h> +#include <pwd.h> +#include <errno.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <rpc/rpc.h> +#ifdef YP +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif +#include <arpa/nameser.h> +#include "un-namespace.h" + +extern int innetgr( const char *, const char *, const char *, const char * ); + +#define max(a, b) ((a > b) ? a : b) + +int __ivaliduser(FILE *, u_int32_t, const char *, const char *); +int __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int); +int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *, + const char *); +static int __icheckhost(const struct sockaddr *, socklen_t, const char *); + +char paddr[NI_MAXHOST]; + +int +rcmd(ahost, rport, locuser, remuser, cmd, fd2p) + char **ahost; + u_short rport; + const char *locuser, *remuser, *cmd; + int *fd2p; +{ + return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); +} + +int +rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) + char **ahost; + u_short rport; + const char *locuser, *remuser, *cmd; + int *fd2p; + int af; +{ + struct addrinfo hints, *res, *ai; + struct sockaddr_storage from; + fd_set reads; + sigset_t oldmask, newmask; + pid_t pid; + int s, aport, lport, timo, error; + char c, *p; + int refused, nres; + char num[8]; + static char canonnamebuf[MAXDNAME]; /* is it proper here? */ + + /* call rcmdsh() with specified remote shell if appropriate. */ + if (!issetugid() && (p = getenv("RSH"))) { + struct servent *sp = getservbyname("shell", "tcp"); + + if (sp && sp->s_port == rport) + return (rcmdsh(ahost, rport, locuser, remuser, + cmd, p)); + } + + /* use rsh(1) if non-root and remote port is shell. */ + if (geteuid()) { + struct servent *sp = getservbyname("shell", "tcp"); + + if (sp && sp->s_port == rport) + return (rcmdsh(ahost, rport, locuser, remuser, + cmd, NULL)); + } + + pid = getpid(); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); + error = getaddrinfo(*ahost, num, &hints, &res); + if (error) { + fprintf(stderr, "rcmd: getaddrinfo: %s\n", + gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "rcmd: getaddrinfo: %s\n", + strerror(errno)); + return (-1); + } + + if (res->ai_canonname + && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { + strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); + *ahost = canonnamebuf; + } + nres = 0; + for (ai = res; ai; ai = ai->ai_next) + nres++; + ai = res; + refused = 0; + sigemptyset(&newmask); + sigaddset(&newmask, SIGURG); + _sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask); + for (timo = 1, lport = IPPORT_RESERVED - 1;;) { + s = rresvport_af(&lport, ai->ai_family); + if (s < 0) { + if (errno != EAGAIN && ai->ai_next) { + ai = ai->ai_next; + continue; + } + if (errno == EAGAIN) + (void)fprintf(stderr, + "rcmd: socket: All ports in use\n"); + else + (void)fprintf(stderr, "rcmd: socket: %s\n", + strerror(errno)); + freeaddrinfo(res); + _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, + NULL); + return (-1); + } + _fcntl(s, F_SETOWN, pid); + if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) + break; + (void)_close(s); + if (errno == EADDRINUSE) { + lport--; + continue; + } + if (errno == ECONNREFUSED) + refused = 1; + if (ai->ai_next == NULL && (!refused || timo > 16)) { + (void)fprintf(stderr, "%s: %s\n", + *ahost, strerror(errno)); + freeaddrinfo(res); + _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, + NULL); + return (-1); + } + if (nres > 1) { + int oerrno = errno; + + getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, + sizeof(paddr), NULL, 0, NI_NUMERICHOST); + (void)fprintf(stderr, "connect to address %s: ", + paddr); + errno = oerrno; + perror(0); + } + if ((ai = ai->ai_next) == NULL) { + /* refused && timo <= 16 */ + struct timespec time_to_sleep, time_remaining; + + time_to_sleep.tv_sec = timo; + time_to_sleep.tv_nsec = 0; + (void)_nanosleep(&time_to_sleep, &time_remaining); + timo *= 2; + ai = res; + refused = 0; + } + if (nres > 1) { + getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, + sizeof(paddr), NULL, 0, NI_NUMERICHOST); + fprintf(stderr, "Trying %s...\n", paddr); + } + } + lport--; + if (fd2p == 0) { + _write(s, "", 1); + lport = 0; + } else { + int s2 = rresvport_af(&lport, ai->ai_family), s3; + socklen_t len = ai->ai_addrlen; + int nfds; + + if (s2 < 0) + goto bad; + _listen(s2, 1); + (void)snprintf(num, sizeof(num), "%d", lport); + if (_write(s, num, strlen(num)+1) != strlen(num)+1) { + (void)fprintf(stderr, + "rcmd: write (setting up stderr): %s\n", + strerror(errno)); + (void)_close(s2); + goto bad; + } + nfds = max(s, s2)+1; + if(nfds > FD_SETSIZE) { + fprintf(stderr, "rcmd: too many files\n"); + (void)_close(s2); + goto bad; + } +again: + FD_ZERO(&reads); + FD_SET(s, &reads); + FD_SET(s2, &reads); + errno = 0; + if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ + if (errno != 0) + (void)fprintf(stderr, + "rcmd: select (setting up stderr): %s\n", + strerror(errno)); + else + (void)fprintf(stderr, + "select: protocol failure in circuit setup\n"); + (void)_close(s2); + goto bad; + } + s3 = _accept(s2, (struct sockaddr *)&from, &len); + switch (from.ss_family) { + case AF_INET: + aport = ntohs(((struct sockaddr_in *)&from)->sin_port); + break; +#ifdef INET6 + case AF_INET6: + aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); + break; +#endif + default: + aport = 0; /* error */ + break; + } + /* + * XXX careful for ftp bounce attacks. If discovered, shut them + * down and check for the real auxiliary channel to connect. + */ + if (aport == 20) { + _close(s3); + goto again; + } + (void)_close(s2); + if (s3 < 0) { + (void)fprintf(stderr, + "rcmd: accept: %s\n", strerror(errno)); + lport = 0; + goto bad; + } + *fd2p = s3; + if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { + (void)fprintf(stderr, + "socket: protocol failure in circuit setup.\n"); + goto bad2; + } + } + (void)_write(s, locuser, strlen(locuser)+1); + (void)_write(s, remuser, strlen(remuser)+1); + (void)_write(s, cmd, strlen(cmd)+1); + if (_read(s, &c, 1) != 1) { + (void)fprintf(stderr, + "rcmd: %s: %s\n", *ahost, strerror(errno)); + goto bad2; + } + if (c != 0) { + while (_read(s, &c, 1) == 1) { + (void)_write(STDERR_FILENO, &c, 1); + if (c == '\n') + break; + } + goto bad2; + } + _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); + freeaddrinfo(res); + return (s); +bad2: + if (lport) + (void)_close(*fd2p); +bad: + (void)_close(s); + _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); + freeaddrinfo(res); + return (-1); +} + +int +rresvport(port) + int *port; +{ + return rresvport_af(port, AF_INET); +} + +int +rresvport_af(alport, family) + int *alport, family; +{ + int s; + struct sockaddr_storage ss; + u_short *sport; + + memset(&ss, 0, sizeof(ss)); + ss.ss_family = family; + switch (family) { + case AF_INET: + ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); + sport = &((struct sockaddr_in *)&ss)->sin_port; + ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; + break; +#ifdef INET6 + case AF_INET6: + ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); + sport = &((struct sockaddr_in6 *)&ss)->sin6_port; + ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; + break; +#endif + default: + errno = EAFNOSUPPORT; + return -1; + } + + s = _socket(ss.ss_family, SOCK_STREAM, 0); + if (s < 0) + return (-1); +#if 0 /* compat_exact_traditional_rresvport_semantics */ + sin.sin_port = htons((u_short)*alport); + if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) + return (s); + if (errno != EADDRINUSE) { + (void)_close(s); + return (-1); + } +#endif + *sport = 0; + if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { + (void)_close(s); + return (-1); + } + *alport = (int)ntohs(*sport); + return (s); +} + +int __check_rhosts_file = 1; +char *__rcmd_errstr; + +int +ruserok(rhost, superuser, ruser, luser) + const char *rhost, *ruser, *luser; + int superuser; +{ + struct addrinfo hints, *res, *r; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + error = getaddrinfo(rhost, "0", &hints, &res); + if (error) + return (-1); + + for (r = res; r; r = r->ai_next) { + if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, + luser) == 0) { + freeaddrinfo(res); + return (0); + } + } + freeaddrinfo(res); + return (-1); +} + +/* + * New .rhosts strategy: We are passed an ip address. We spin through + * hosts.equiv and .rhosts looking for a match. When the .rhosts only + * has ip addresses, we don't have to trust a nameserver. When it + * contains hostnames, we spin through the list of addresses the nameserver + * gives us and look for a match. + * + * Returns 0 if ok, -1 if not ok. + */ +int +iruserok(raddr, superuser, ruser, luser) + unsigned long raddr; + int superuser; + const char *ruser, *luser; +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); + return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, + ruser, luser); +} + +/* + * AF independent extension of iruserok. + * + * Returns 0 if ok, -1 if not ok. + */ +int +iruserok_sa(ra, rlen, superuser, ruser, luser) + const void *ra; + int rlen; + int superuser; + const char *ruser, *luser; +{ + char *cp; + struct stat sbuf; + struct passwd *pwd; + FILE *hostf; + uid_t uid; + int first; + char pbuf[MAXPATHLEN]; + const struct sockaddr *raddr; + struct sockaddr_storage ss; + + /* avoid alignment issue */ + if (rlen > sizeof(ss)) + return(-1); + memcpy(&ss, ra, rlen); + raddr = (struct sockaddr *)&ss; + + first = 1; + hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); +again: + if (hostf) { + if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { + (void)fclose(hostf); + return (0); + } + (void)fclose(hostf); + } + if (first == 1 && (__check_rhosts_file || superuser)) { + first = 0; + if ((pwd = getpwnam(luser)) == NULL) + return (-1); + (void)strcpy(pbuf, pwd->pw_dir); + (void)strcat(pbuf, "/.rhosts"); + + /* + * Change effective uid while opening .rhosts. If root and + * reading an NFS mounted file system, can't read files that + * are protected read/write owner only. + */ + uid = geteuid(); + (void)seteuid(pwd->pw_uid); + hostf = fopen(pbuf, "r"); + (void)seteuid(uid); + + if (hostf == NULL) + return (-1); + /* + * If not a regular file, or is owned by someone other than + * user or root or if writeable by anyone but the owner, quit. + */ + cp = NULL; + if (lstat(pbuf, &sbuf) < 0) + cp = ".rhosts lstat failed"; + else if (!S_ISREG(sbuf.st_mode)) + cp = ".rhosts not regular file"; + else if (_fstat(fileno(hostf), &sbuf) < 0) + cp = ".rhosts fstat failed"; + else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) + cp = "bad .rhosts owner"; + else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) + cp = ".rhosts writeable by other than owner"; + /* If there were any problems, quit. */ + if (cp) { + __rcmd_errstr = cp; + (void)fclose(hostf); + return (-1); + } + goto again; + } + return (-1); +} + +/* + * XXX + * Don't make static, used by lpd(8). + * + * Returns 0 if ok, -1 if not ok. + */ +int +__ivaliduser(hostf, raddr, luser, ruser) + FILE *hostf; + u_int32_t raddr; + const char *luser, *ruser; +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); + return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, + luser, ruser); +} + +/* + * Returns 0 if ok, -1 if not ok. + * + * XXX obsolete API. + */ +int +__ivaliduser_af(hostf, raddr, luser, ruser, af, len) + FILE *hostf; + const void *raddr; + const char *luser, *ruser; + int af, len; +{ + struct sockaddr *sa = NULL; + struct sockaddr_in *sin = NULL; +#ifdef INET6 + struct sockaddr_in6 *sin6 = NULL; +#endif + struct sockaddr_storage ss; + + memset(&ss, 0, sizeof(ss)); + switch (af) { + case AF_INET: + if (len != sizeof(sin->sin_addr)) + return -1; + sin = (struct sockaddr_in *)&ss; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); + break; +#ifdef INET6 + case AF_INET6: + if (len != sizeof(sin6->sin6_addr)) + return -1; + /* you will lose scope info */ + sin6 = (struct sockaddr_in6 *)&ss; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); + break; +#endif + default: + return -1; + } + + sa = (struct sockaddr *)&ss; + return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); +} + +int +__ivaliduser_sa(hostf, raddr, salen, luser, ruser) + FILE *hostf; + const struct sockaddr *raddr; + socklen_t salen; + const char *luser, *ruser; +{ + char *user, *p; + int ch; + char buf[MAXHOSTNAMELEN + 128]; /* host + login */ + char hname[MAXHOSTNAMELEN]; + /* Presumed guilty until proven innocent. */ + int userok = 0, hostok = 0; +#ifdef YP + char *ypdomain; + + if (yp_get_default_domain(&ypdomain)) + ypdomain = NULL; +#else +#define ypdomain NULL +#endif + /* We need to get the damn hostname back for netgroup matching. */ + if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, + NI_NAMEREQD) != 0) + hname[0] = '\0'; + + while (fgets(buf, sizeof(buf), hostf)) { + p = buf; + /* Skip lines that are too long. */ + if (strchr(p, '\n') == NULL) { + while ((ch = getc(hostf)) != '\n' && ch != EOF); + continue; + } + if (*p == '\n' || *p == '#') { + /* comment... */ + continue; + } + while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { + *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; + p++; + } + if (*p == ' ' || *p == '\t') { + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + user = p; + while (*p != '\n' && *p != ' ' && + *p != '\t' && *p != '\0') + p++; + } else + user = p; + *p = '\0'; + /* + * Do +/- and +@/-@ checking. This looks really nasty, + * but it matches SunOS's behavior so far as I can tell. + */ + switch(buf[0]) { + case '+': + if (!buf[1]) { /* '+' matches all hosts */ + hostok = 1; + break; + } + if (buf[1] == '@') /* match a host by netgroup */ + hostok = hname[0] != '\0' && + innetgr(&buf[2], hname, NULL, ypdomain); + else /* match a host by addr */ + hostok = __icheckhost(raddr, salen, + (char *)&buf[1]); + break; + case '-': /* reject '-' hosts and all their users */ + if (buf[1] == '@') { + if (hname[0] == '\0' || + innetgr(&buf[2], hname, NULL, ypdomain)) + return(-1); + } else { + if (__icheckhost(raddr, salen, + (char *)&buf[1])) + return(-1); + } + break; + default: /* if no '+' or '-', do a simple match */ + hostok = __icheckhost(raddr, salen, buf); + break; + } + switch(*user) { + case '+': + if (!*(user+1)) { /* '+' matches all users */ + userok = 1; + break; + } + if (*(user+1) == '@') /* match a user by netgroup */ + userok = innetgr(user+2, NULL, ruser, ypdomain); + else /* match a user by direct specification */ + userok = !(strcmp(ruser, user+1)); + break; + case '-': /* if we matched a hostname, */ + if (hostok) { /* check for user field rejections */ + if (!*(user+1)) + return(-1); + if (*(user+1) == '@') { + if (innetgr(user+2, NULL, + ruser, ypdomain)) + return(-1); + } else { + if (!strcmp(ruser, user+1)) + return(-1); + } + } + break; + default: /* no rejections: try to match the user */ + if (hostok) + userok = !(strcmp(ruser,*user ? user : luser)); + break; + } + if (hostok && userok) + return(0); + } + return (-1); +} + +/* + * Returns "true" if match, 0 if no match. + */ +static int +__icheckhost(raddr, salen, lhost) + const struct sockaddr *raddr; + socklen_t salen; + const char *lhost; +{ + struct sockaddr_in sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res, *r; + int error; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + + if (raddr->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)raddr; + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], + sizeof(sin.sin_addr)); + raddr = (struct sockaddr *)&sin; + salen = sin.sin_len; + } + } + + h1[0] = '\0'; + if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, + NI_NUMERICHOST) != 0) + return (0); + + /* Resolve laddr into sockaddr */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = raddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ + res = NULL; + error = getaddrinfo(lhost, "0", &hints, &res); + if (error) + return (0); + + for (r = res; r ; r = r->ai_next) { + h2[0] = '\0'; + if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), + NULL, 0, NI_NUMERICHOST) != 0) + continue; + if (strcmp(h1, h2) == 0) { + freeaddrinfo(res); + return (1); + } + } + + /* No match. */ + freeaddrinfo(res); + return (0); +} diff --git a/lib/libc/net/rcmdsh.3 b/lib/libc/net/rcmdsh.3 new file mode 100644 index 0000000..03e7dab --- /dev/null +++ b/lib/libc/net/rcmdsh.3 @@ -0,0 +1,120 @@ +.\" $OpenBSD: rcmdsh.3,v 1.6 1999/07/05 04:41:00 aaron Exp $ +.\" +.\" Copyright (c) 1983, 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 1996 +.Dt RCMDSH 3 +.Os +.Sh NAME +.Nm rcmdsh +.Nd return a stream to a remote command without superuser +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fo rcmdsh +.Fa "char **ahost" +.Fa "int inport" +.Fa "const char *locuser" +.Fa "const char *remuser" +.Fa "const char *cmd" +.Fa "const char *rshprog" +.Fc +.Sh DESCRIPTION +The +.Fn rcmdsh +function +is used by normal users to execute a command on +a remote machine using an authentication scheme based +on reserved port numbers using +.Xr rshd 8 +or the value of +.Fa rshprog +(if +.No non- Ns Dv NULL ) . +.Pp +The +.Fn rcmdsh +function +looks up the host +.Fa *ahost +using +.Xr gethostbyname 3 , +returning \-1 if the host does not exist. +Otherwise +.Fa *ahost +is set to the standard name of the host +and a connection is established to a server +residing at the well-known Internet port +.Dq Li shell/tcp +(or whatever port is used by +.Fa rshprog ) . +The +.Fa inport +argument +is ignored; it is only included to provide an interface similar to +.Xr rcmd 3 . +.Pp +If the connection succeeds, +a socket in the +.Ux +domain of type +.Dv SOCK_STREAM +is returned to the caller, and given to the remote +command as +.Dv stdin , stdout , +and +.Dv stderr . +.Sh RETURN VALUES +The +.Fn rcmdsh +function +returns a valid socket descriptor on success. +Otherwise, \-1 is returned +and a diagnostic message is printed on the standard error. +.Sh SEE ALSO +.Xr rsh 1 , +.Xr socketpair 2 , +.Xr rcmd 3 , +.Xr rshd 8 +.Sh HISTORY +The +.Fn rcmdsh +function first appeared in +.Ox 2.0 , +and made its way into +.Fx 4.6 . +.Sh BUGS +If +.Xr rsh 1 +encounters an error, a file descriptor is still returned instead of \-1. diff --git a/lib/libc/net/rcmdsh.c b/lib/libc/net/rcmdsh.c new file mode 100644 index 0000000..bc4e87a --- /dev/null +++ b/lib/libc/net/rcmdsh.c @@ -0,0 +1,170 @@ +/* $OpenBSD: rcmdsh.c,v 1.5 1998/04/25 16:23:58 millert Exp $ */ + +/* + * Copyright (c) 2001, MagniComp + * 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 MagniComp 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE 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. + */ + +/* + * This is an rcmd() replacement originally by + * Chris Siebenmann <cks@utcc.utoronto.ca>. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifndef _PATH_RSH +#define _PATH_RSH "/usr/bin/rsh" +#endif + +/* + * This is a replacement rcmd() function that uses the rsh(1) + * program in place of a direct rcmd(3) function call so as to + * avoid having to be root. Note that rport is ignored. + */ +int +rcmdsh(ahost, rport, locuser, remuser, cmd, rshprog) + char **ahost; + int rport; + const char *locuser, *remuser, *cmd, *rshprog; +{ + struct addrinfo hints, *res; + int cpid, sp[2], error; + char *p; + struct passwd *pw; + char num[8]; + static char hbuf[NI_MAXHOST]; + + /* What rsh/shell to use. */ + if (rshprog == NULL) + rshprog = _PATH_RSH; + + /* locuser must exist on this host. */ + if ((pw = getpwnam(locuser)) == NULL) { + (void)fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser); + return (-1); + } + + /* Validate remote hostname. */ + if (strcmp(*ahost, "localhost") != 0) { + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + (void)snprintf(num, sizeof(num), "%u", + (unsigned int)ntohs(rport)); + error = getaddrinfo(*ahost, num, &hints, &res); + if (error) { + fprintf(stderr, "rcmdsh: getaddrinfo: %s\n", + gai_strerror(error)); + return (-1); + } + if (res->ai_canonname) { + strncpy(hbuf, res->ai_canonname, sizeof(hbuf) - 1); + hbuf[sizeof(hbuf) - 1] = '\0'; + *ahost = hbuf; + } + freeaddrinfo(res); + } + + /* Get a socketpair we'll use for stdin and stdout. */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { + perror("rcmdsh: socketpair"); + return (-1); + } + + cpid = fork(); + if (cpid == -1) { + perror("rcmdsh: fork failed"); + return (-1); + } else if (cpid == 0) { + /* + * Child. We use sp[1] to be stdin/stdout, and close sp[0]. + */ + (void)close(sp[0]); + if (dup2(sp[1], 0) == -1 || dup2(0, 1) == -1) { + perror("rcmdsh: dup2 failed"); + _exit(255); + } + /* Fork again to lose parent. */ + cpid = fork(); + if (cpid == -1) { + perror("rcmdsh: fork to lose parent failed"); + _exit(255); + } + if (cpid > 0) + _exit(0); + + /* In grandchild here. Become local user for rshprog. */ + if (setuid(pw->pw_uid) == -1) { + (void)fprintf(stderr, "rcmdsh: setuid(%u): %s\n", + pw->pw_uid, strerror(errno)); + _exit(255); + } + + /* + * If remote host is "localhost" and local and remote users + * are the same, avoid running remote shell for efficiency. + */ + if (strcmp(*ahost, "localhost") == 0 && + strcmp(locuser, remuser) == 0) { + if (pw->pw_shell[0] == '\0') + rshprog = _PATH_BSHELL; + else + rshprog = pw->pw_shell; + p = strrchr(rshprog, '/'); + execlp(rshprog, p ? p + 1 : rshprog, "-c", cmd, + (char *)NULL); + } else { + p = strrchr(rshprog, '/'); + execlp(rshprog, p ? p + 1 : rshprog, *ahost, "-l", + remuser, cmd, (char *)NULL); + } + (void)fprintf(stderr, "rcmdsh: execlp %s failed: %s\n", + rshprog, strerror(errno)); + _exit(255); + } else { + /* Parent. close sp[1], return sp[0]. */ + (void)close(sp[1]); + /* Reap child. */ + (void)wait(NULL); + return (sp[0]); + } + /* NOTREACHED */ +} diff --git a/lib/libc/net/recv.c b/lib/libc/net/recv.c new file mode 100644 index 0000000..267e171 --- /dev/null +++ b/lib/libc/net/recv.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1988, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)recv.c 8.2 (Berkeley) 2/21/94"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/socket.h> + +#include <stddef.h> +#include "un-namespace.h" + +ssize_t +recv(s, buf, len, flags) + int s, flags; + size_t len; + void *buf; +{ + return (_recvfrom(s, buf, len, flags, NULL, 0)); +} diff --git a/lib/libc/net/res_config.h b/lib/libc/net/res_config.h new file mode 100644 index 0000000..05909bc --- /dev/null +++ b/lib/libc/net/res_config.h @@ -0,0 +1,6 @@ +/* $FreeBSD$ */ + +#define DEBUG 1 /* enable debugging code (needed for dig) */ +#define RESOLVSORT /* allow sorting of addresses in gethostbyname */ +#undef SUNSECURITY /* verify gethostbyaddr() calls - WE DONT NEED IT */ +#define MULTI_PTRS_ARE_ALIASES 1 /* fold multiple PTR records into aliases */ diff --git a/lib/libc/net/res_mkupdate.c b/lib/libc/net/res_mkupdate.c new file mode 100644 index 0000000..ebcaba2 --- /dev/null +++ b/lib/libc/net/res_mkupdate.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Based on the Dynamic DNS reference implementation by Viraj Bais + * <viraj_bais@ccm.fm.intel.com> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <limits.h> +#include <netdb.h> +#include <resolv.h> +#include <res_update.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "res_config.h" + +static int getnum_str(u_char **, u_char *); +static int getword_str(char *, int, u_char **, u_char *); + +#define ShrinkBuffer(x) if ((buflen -= x) < 0) return (-2); + +/* + * Form update packets. + * Returns the size of the resulting packet if no error + * On error, + * returns -1 if error in reading a word/number in rdata + * portion for update packets + * -2 if length of buffer passed is insufficient + * -3 if zone section is not the first section in + * the linked list, or section order has a problem + * -4 on a number overflow + * -5 unknown operation or no records + */ +int +res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) { + ns_updrec *rrecp_start = rrecp_in; + HEADER *hp; + u_char *cp, *sp2, *startp, *endp; + int n, i, soanum, multiline; + ns_updrec *rrecp; + struct in_addr ina; + char buf2[MAXDNAME]; + int section, numrrs = 0, counts[ns_s_max]; + u_int16_t rtype, rclass; + u_int32_t n1, rttl; + u_char *dnptrs[20], **dpp, **lastdnptr; + + /* + * Initialize header fields. + */ + if ((buf == NULL) || (buflen < HFIXEDSZ)) + return (-1); + memset(buf, 0, HFIXEDSZ); + hp = (HEADER *) buf; + hp->id = htons(++statp->id); + hp->opcode = ns_o_update; + hp->rcode = NOERROR; + cp = buf + HFIXEDSZ; + buflen -= HFIXEDSZ; + dpp = dnptrs; + *dpp++ = buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + + if (rrecp_start == NULL) + return (-5); + else if (rrecp_start->r_section != S_ZONE) + return (-3); + + memset(counts, 0, sizeof counts); + for (rrecp = rrecp_start; rrecp; rrecp = rrecp->r_grpnext) { + numrrs++; + section = rrecp->r_section; + if (section < 0 || section >= ns_s_max) + return (-1); + counts[section]++; + for (i = section + 1; i < ns_s_max; i++) + if (counts[i]) + return (-3); + rtype = rrecp->r_type; + rclass = rrecp->r_class; + rttl = rrecp->r_ttl; + /* overload class and type */ + if (section == S_PREREQ) { + rttl = 0; + switch (rrecp->r_opcode) { + case YXDOMAIN: + rclass = C_ANY; + rtype = T_ANY; + rrecp->r_size = 0; + break; + case NXDOMAIN: + rclass = C_NONE; + rtype = T_ANY; + rrecp->r_size = 0; + break; + case NXRRSET: + rclass = C_NONE; + rrecp->r_size = 0; + break; + case YXRRSET: + if (rrecp->r_size == 0) + rclass = C_ANY; + break; + default: + fprintf(stderr, + "res_nmkupdate: incorrect opcode: %d\n", + rrecp->r_opcode); + fflush(stderr); + return (-1); + } + } else if (section == S_UPDATE) { + switch (rrecp->r_opcode) { + case DELETE: + rclass = rrecp->r_size == 0 ? C_ANY : C_NONE; + break; + case ADD: + break; + default: + fprintf(stderr, + "res_nmkupdate: incorrect opcode: %d\n", + rrecp->r_opcode); + fflush(stderr); + return (-1); + } + } + + /* + * XXX appending default domain to owner name is omitted, + * fqdn must be provided + */ + if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs, + lastdnptr)) < 0) + return (-1); + cp += n; + ShrinkBuffer(n + 2*INT16SZ); + PUTSHORT(rtype, cp); + PUTSHORT(rclass, cp); + if (section == S_ZONE) { + if (numrrs != 1 || rrecp->r_type != T_SOA) + return (-3); + continue; + } + ShrinkBuffer(INT32SZ + INT16SZ); + PUTLONG(rttl, cp); + sp2 = cp; /* save pointer to length byte */ + cp += INT16SZ; + if (rrecp->r_size == 0) { + if (section == S_UPDATE && rclass != C_ANY) + return (-1); + else { + PUTSHORT(0, sp2); + continue; + } + } + startp = rrecp->r_data; + endp = startp + rrecp->r_size - 1; + /* XXX this should be done centrally. */ + switch (rrecp->r_type) { + case T_A: + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + if (!inet_aton(buf2, &ina)) + return (-1); + n1 = ntohl(ina.s_addr); + ShrinkBuffer(INT32SZ); + PUTLONG(n1, cp); + break; + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + case T_MINFO: + case T_SOA: + case T_RP: + for (i = 0; i < 2; i++) { + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, + dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + } + if (rrecp->r_type == T_SOA) { + ShrinkBuffer(5 * INT32SZ); + while (isspace(*startp) || !*startp) + startp++; + if (*startp == '(') { + multiline = 1; + startp++; + } else + multiline = 0; + /* serial, refresh, retry, expire, minimum */ + for (i = 0; i < 5; i++) { + soanum = getnum_str(&startp, endp); + if (soanum < 0) + return (-1); + PUTLONG(soanum, cp); + } + if (multiline) { + while (isspace(*startp) || !*startp) + startp++; + if (*startp != ')') + return (-1); + } + } + break; + case T_MX: + case T_AFSDB: + case T_RT: + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + PUTSHORT(n, cp); + ShrinkBuffer(INT16SZ); + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + case T_PX: + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + PUTSHORT(n, cp); + ShrinkBuffer(INT16SZ); + for (i = 0; i < 2; i++) { + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, + lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + } + break; + case T_WKS: + case T_HINFO: + case T_TXT: + case T_X25: + case T_ISDN: + case T_NSAP: + case T_LOC: + /* XXX - more fine tuning needed here */ + ShrinkBuffer(rrecp->r_size); + memcpy(cp, rrecp->r_data, rrecp->r_size); + cp += rrecp->r_size; + break; + default: + return (-1); + } /*switch*/ + n = (u_int16_t)((cp - sp2) - INT16SZ); + PUTSHORT(n, sp2); + } /*for*/ + + hp->qdcount = htons(counts[0]); + hp->ancount = htons(counts[1]); + hp->nscount = htons(counts[2]); + hp->arcount = htons(counts[3]); + return (cp - buf); +} + +/* + * Get a whitespace delimited word from a string (not file) + * into buf. modify the start pointer to point after the + * word in the string. + */ +static int +getword_str(char *buf, int size, u_char **startpp, u_char *endp) { + char *cp; + int c; + + for (cp = buf; *startpp <= endp; ) { + c = **startpp; + if (isspace(c) || c == '\0') { + if (cp != buf) /* trailing whitespace */ + break; + else { /* leading whitespace */ + (*startpp)++; + continue; + } + } + (*startpp)++; + if (cp >= buf+size-1) + break; + *cp++ = (u_char)c; + } + *cp = '\0'; + return (cp != buf); +} + +/* + * Get a whitespace delimited number from a string (not file) into buf + * update the start pointer to point after the number in the string. + */ +static int +getnum_str(u_char **startpp, u_char *endp) { + int c, n; + int seendigit = 0; + int m = 0; + + for (n = 0; *startpp <= endp; ) { + c = **startpp; + if (isspace(c) || c == '\0') { + if (seendigit) /* trailing whitespace */ + break; + else { /* leading whitespace */ + (*startpp)++; + continue; + } + } + if (c == ';') { + while ((*startpp <= endp) && + ((c = **startpp) != '\n')) + (*startpp)++; + if (seendigit) + break; + continue; + } + if (!isdigit(c)) { + if (c == ')' && seendigit) { + (*startpp)--; + break; + } + return (-1); + } + (*startpp)++; + n = n * 10 + (c - '0'); + seendigit = 1; + } + return (n + m); +} + +/* + * Allocate a resource record buffer & save rr info. + */ +ns_updrec * +res_mkupdrec(int section, const char *dname, + u_int class, u_int type, u_long ttl) { + ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec)); + + if (!rrecp || !(rrecp->r_dname = strdup(dname))) + return (NULL); + rrecp->r_class = class; + rrecp->r_type = type; + rrecp->r_ttl = ttl; + rrecp->r_section = section; + return (rrecp); +} + +/* + * Free a resource record buffer created by res_mkupdrec. + */ +void +res_freeupdrec(ns_updrec *rrecp) { + /* Note: freeing r_dp is the caller's responsibility. */ + if (rrecp->r_dname != NULL) + free(rrecp->r_dname); + free(rrecp); +} diff --git a/lib/libc/net/res_update.c b/lib/libc/net/res_update.c new file mode 100644 index 0000000..46f1efa --- /dev/null +++ b/lib/libc/net/res_update.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Based on the Dynamic DNS reference implementation by Viraj Bais + * <viraj_bais@ccm.fm.intel.com> + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <limits.h> +#include <netdb.h> +#include <resolv.h> +#include <res_update.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * Separate a linked list of records into groups so that all records + * in a group will belong to a single zone on the nameserver. + * Create a dynamic update packet for each zone and send it to the + * nameservers for that zone, and await answer. + * Abort if error occurs in updating any zone. + * Return the number of zones updated on success, < 0 on error. + * + * On error, caller must deal with the unsynchronized zones + * eg. an A record might have been successfully added to the forward + * zone but the corresponding PTR record would be missing if error + * was encountered while updating the reverse zone. + */ + +#define NSMAX 16 + +struct ns1 { + char nsname[MAXDNAME]; + struct in_addr nsaddr1; +}; + +struct zonegrp { + char z_origin[MAXDNAME]; + int16_t z_class; + char z_soardata[MAXDNAME + 5 * INT32SZ]; + struct ns1 z_ns[NSMAX]; + int z_nscount; + ns_updrec * z_rr; + struct zonegrp *z_next; +}; + + +int +res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) { + ns_updrec *rrecp, *tmprrecp; + u_char buf[PACKETSZ], answer[PACKETSZ], packet[2*PACKETSZ]; + char name[MAXDNAME], zname[MAXDNAME], primary[MAXDNAME], + mailaddr[MAXDNAME]; + u_char soardata[2*MAXCDNAME+5*INT32SZ]; + char *dname, *svdname, *cp1, *target; + u_char *cp, *eom; + HEADER *hp = (HEADER *) answer; + struct zonegrp *zptr = NULL, *tmpzptr, *prevzptr, *zgrp_start = NULL; + int i, j, k = 0, n, ancount, nscount, arcount, rcode, rdatasize, + newgroup, done, myzone, seen_before, numzones = 0; + u_int16_t dlen, class, qclass, type, qtype; + u_int32_t ttl; + + if (key != NULL) { + /* TSIG is not supported. */ + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + + for (rrecp = rrecp_in; rrecp; rrecp = rrecp->r_next) { + dname = rrecp->r_dname; + n = strlen(dname); + if (dname[n-1] == '.') + dname[n-1] = '\0'; + qtype = T_SOA; + qclass = rrecp->r_class; + done = 0; + seen_before = 0; + + while (!done && dname) { + if (qtype == T_SOA) { + for (tmpzptr = zgrp_start; + tmpzptr && !seen_before; + tmpzptr = tmpzptr->z_next) { + if (strcasecmp(dname, + tmpzptr->z_origin) == 0 && + tmpzptr->z_class == qclass) + seen_before++; + for (tmprrecp = tmpzptr->z_rr; + tmprrecp && !seen_before; + tmprrecp = tmprrecp->r_grpnext) + if (strcasecmp(dname, tmprrecp->r_dname) == 0 + && tmprrecp->r_class == qclass) { + seen_before++; + break; + } + if (seen_before) { + /* + * Append to the end of + * current group. + */ + for (tmprrecp = tmpzptr->z_rr; + tmprrecp->r_grpnext; + tmprrecp = tmprrecp->r_grpnext) + (void)NULL; + tmprrecp->r_grpnext = rrecp; + rrecp->r_grpnext = NULL; + done = 1; + break; + } + } + } else if (qtype == T_A) { + for (tmpzptr = zgrp_start; + tmpzptr && !done; + tmpzptr = tmpzptr->z_next) + for (i = 0; i < tmpzptr->z_nscount; i++) + if (tmpzptr->z_class == qclass && + strcasecmp(tmpzptr->z_ns[i].nsname, + dname) == 0 && + tmpzptr->z_ns[i].nsaddr1.s_addr != 0) { + zptr->z_ns[k].nsaddr1.s_addr = + tmpzptr->z_ns[i].nsaddr1.s_addr; + done = 1; + break; + } + } + if (done) + break; + n = res_nmkquery(statp, QUERY, dname, qclass, qtype, NULL, + 0, NULL, buf, sizeof buf); + if (n <= 0) { + fprintf(stderr, "res_nupdate: mkquery failed\n"); + return (n); + } + n = res_nsend(statp, buf, n, answer, sizeof answer); + if (n < 0) { + fprintf(stderr, "res_nupdate: send error for %s\n", + rrecp->r_dname); + return (n); + } else if (n > sizeof(answer)) { + fprintf(stderr, "res_nupdate: buffer too small\n"); + return (-1); + } + if (n < HFIXEDSZ) + return (-1); + ancount = ntohs(hp->ancount); + nscount = ntohs(hp->nscount); + arcount = ntohs(hp->arcount); + rcode = hp->rcode; + cp = answer + HFIXEDSZ; + eom = answer + n; + /* skip the question section */ + n = dn_skipname(cp, eom); + if (n < 0 || cp + n + 2 * INT16SZ > eom) + return (-1); + cp += n + 2 * INT16SZ; + + if (qtype == T_SOA) { + if (ancount == 0 && nscount == 0 && arcount == 0) { + /* + * if (rcode == NOERROR) then the dname exists but + * has no soa record associated with it. + * if (rcode == NXDOMAIN) then the dname does not + * exist and the server is replying out of NCACHE. + * in either case, proceed with the next try + */ + dname = strchr(dname, '.'); + if (dname != NULL) + dname++; + continue; + } else if ((rcode == NOERROR || rcode == NXDOMAIN) && + ancount == 0 && + nscount == 1 && arcount == 0) { + /* + * name/data does not exist, soa record supplied in the + * authority section + */ + /* authority section must contain the soa record */ + if ((n = dn_expand(answer, eom, cp, zname, + sizeof zname)) < 0) + return (n); + cp += n; + if (cp + 2 * INT16SZ > eom) + return (-1); + GETSHORT(type, cp); + GETSHORT(class, cp); + if (type != T_SOA || class != qclass) { + fprintf(stderr, "unknown answer\n"); + return (-1); + } + myzone = 0; + svdname = dname; + while (dname) + if (strcasecmp(dname, zname) == 0) { + myzone = 1; + break; + } else if ((dname = strchr(dname, '.')) != NULL) + dname++; + if (!myzone) { + dname = strchr(svdname, '.'); + if (dname != NULL) + dname++; + continue; + } + nscount = 0; + /* fallthrough */ + } else if (rcode == NOERROR && ancount == 1) { + /* + * found the zone name + * new servers will supply NS records for the zone + * in authority section and A records for those + * nameservers in the additional section + * older servers have to be explicitly queried for + * NS records for the zone + */ + /* answer section must contain the soa record */ + if ((n = dn_expand(answer, eom, cp, zname, + sizeof zname)) < 0) + return (n); + else + cp += n; + if (cp + 2 * INT16SZ > eom) + return (-1); + GETSHORT(type, cp); + GETSHORT(class, cp); + if (type == T_CNAME) { + dname = strchr(dname, '.'); + if (dname != NULL) + dname++; + continue; + } + if (strcasecmp(dname, zname) != 0 || + type != T_SOA || + class != rrecp->r_class) { + fprintf(stderr, "unknown answer\n"); + return (-1); + } + /* FALLTHROUGH */ + } else { + fprintf(stderr, + "unknown response: ans=%d, auth=%d, add=%d, rcode=%d\n", + ancount, nscount, arcount, hp->rcode); + return (-1); + } + if (cp + INT32SZ + INT16SZ > eom) + return (-1); + /* continue processing the soa record */ + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + if (cp + dlen > eom) + return (-1); + newgroup = 1; + zptr = zgrp_start; + prevzptr = NULL; + while (zptr) { + if (strcasecmp(zname, zptr->z_origin) == 0 && + type == T_SOA && class == qclass) { + newgroup = 0; + break; + } + prevzptr = zptr; + zptr = zptr->z_next; + } + if (!newgroup) { + for (tmprrecp = zptr->z_rr; + tmprrecp->r_grpnext; + tmprrecp = tmprrecp->r_grpnext) + ; + tmprrecp->r_grpnext = rrecp; + rrecp->r_grpnext = NULL; + done = 1; + cp += dlen; + break; + } else { + if ((n = dn_expand(answer, eom, cp, primary, + sizeof primary)) < 0) + return (n); + cp += n; + /* + * We don't have to bounds check here because the + * next use of 'cp' is in dn_expand(). + */ + cp1 = (char *)soardata; + strcpy(cp1, primary); + cp1 += strlen(cp1) + 1; + if ((n = dn_expand(answer, eom, cp, mailaddr, + sizeof mailaddr)) < 0) + return (n); + cp += n; + strcpy(cp1, mailaddr); + cp1 += strlen(cp1) + 1; + if (cp + 5*INT32SZ > eom) + return (-1); + memcpy(cp1, cp, 5*INT32SZ); + cp += 5*INT32SZ; + cp1 += 5*INT32SZ; + rdatasize = (u_char *)cp1 - soardata; + zptr = calloc(1, sizeof(struct zonegrp)); + if (zptr == NULL) + return (-1); + if (zgrp_start == NULL) + zgrp_start = zptr; + else + prevzptr->z_next = zptr; + zptr->z_rr = rrecp; + rrecp->r_grpnext = NULL; + strcpy(zptr->z_origin, zname); + zptr->z_class = class; + memcpy(zptr->z_soardata, soardata, rdatasize); + /* fallthrough to process NS and A records */ + } + } else if (qtype == T_NS) { + if (rcode == NOERROR && ancount > 0) { + strcpy(zname, dname); + for (zptr = zgrp_start; zptr; zptr = zptr->z_next) { + if (strcasecmp(zname, zptr->z_origin) == 0) + break; + } + if (zptr == NULL) + /* should not happen */ + return (-1); + if (nscount > 0) { + /* + * answer and authority sections contain + * the same information, skip answer section + */ + for (j = 0; j < ancount; j++) { + n = dn_skipname(cp, eom); + if (n < 0) + return (-1); + n += 2*INT16SZ + INT32SZ; + if (cp + n + INT16SZ > eom) + return (-1); + cp += n; + GETSHORT(dlen, cp); + cp += dlen; + } + } else + nscount = ancount; + /* fallthrough to process NS and A records */ + } else { + fprintf(stderr, "cannot determine nameservers for %s:\ +ans=%d, auth=%d, add=%d, rcode=%d\n", + dname, ancount, nscount, arcount, hp->rcode); + return (-1); + } + } else if (qtype == T_A) { + if (rcode == NOERROR && ancount > 0) { + arcount = ancount; + ancount = nscount = 0; + /* fallthrough to process A records */ + } else { + fprintf(stderr, "cannot determine address for %s:\ +ans=%d, auth=%d, add=%d, rcode=%d\n", + dname, ancount, nscount, arcount, hp->rcode); + return (-1); + } + } + /* process NS records for the zone */ + j = 0; + for (i = 0; i < nscount; i++) { + if ((n = dn_expand(answer, eom, cp, name, + sizeof name)) < 0) + return (n); + cp += n; + if (cp + 3 * INT16SZ + INT32SZ > eom) + return (-1); + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + if (cp + dlen > eom) + return (-1); + if (strcasecmp(name, zname) == 0 && + type == T_NS && class == qclass) { + if ((n = dn_expand(answer, eom, cp, + name, sizeof name)) < 0) + return (n); + target = zptr->z_ns[j++].nsname; + strcpy(target, name); + } + cp += dlen; + } + if (zptr->z_nscount == 0) + zptr->z_nscount = j; + /* get addresses for the nameservers */ + for (i = 0; i < arcount; i++) { + if ((n = dn_expand(answer, eom, cp, name, + sizeof name)) < 0) + return (n); + cp += n; + if (cp + 3 * INT16SZ + INT32SZ > eom) + return (-1); + GETSHORT(type, cp); + GETSHORT(class, cp); + GETLONG(ttl, cp); + GETSHORT(dlen, cp); + if (cp + dlen > eom) + return (-1); + if (type == T_A && dlen == INT32SZ && class == qclass) { + for (j = 0; j < zptr->z_nscount; j++) + if (strcasecmp(name, zptr->z_ns[j].nsname) == 0) { + memcpy(&zptr->z_ns[j].nsaddr1.s_addr, cp, + INT32SZ); + break; + } + } + cp += dlen; + } + if (zptr->z_nscount == 0) { + dname = zname; + qtype = T_NS; + continue; + } + done = 1; + for (k = 0; k < zptr->z_nscount; k++) + if (zptr->z_ns[k].nsaddr1.s_addr == 0) { + done = 0; + dname = zptr->z_ns[k].nsname; + qtype = T_A; + } + + } /* while */ + } + + statp->options |= RES_DEBUG; + for (zptr = zgrp_start; zptr; zptr = zptr->z_next) { + + /* append zone section */ + rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin, + zptr->z_class, ns_t_soa, 0); + if (rrecp == NULL) { + fprintf(stderr, "saverrec error\n"); + fflush(stderr); + return (-1); + } + rrecp->r_grpnext = zptr->z_rr; + zptr->z_rr = rrecp; + + n = res_nmkupdate(statp, zptr->z_rr, packet, sizeof packet); + if (n < 0) { + fprintf(stderr, "res_nmkupdate error\n"); + fflush(stderr); + return (-1); + } else + fprintf(stdout, "res_nmkupdate: packet size = %d\n", n); + + /* + * Override the list of NS records from res_ninit() with + * the authoritative nameservers for the zone being updated. + * Sort primary to be the first in the list of nameservers. + */ + for (i = 0; i < zptr->z_nscount; i++) { + if (strcasecmp(zptr->z_ns[i].nsname, + zptr->z_soardata) == 0) { + struct in_addr tmpaddr; + + if (i != 0) { + strcpy(zptr->z_ns[i].nsname, + zptr->z_ns[0].nsname); + strcpy(zptr->z_ns[0].nsname, + zptr->z_soardata); + tmpaddr = zptr->z_ns[i].nsaddr1; + zptr->z_ns[i].nsaddr1 = + zptr->z_ns[0].nsaddr1; + zptr->z_ns[0].nsaddr1 = tmpaddr; + } + break; + } + } + for (i = 0; i < MAXNS; i++) { + statp->nsaddr_list[i].sin_addr = zptr->z_ns[i].nsaddr1; + statp->nsaddr_list[i].sin_family = AF_INET; + statp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT); + } + statp->nscount = (zptr->z_nscount < MAXNS) ? + zptr->z_nscount : MAXNS; + n = res_nsend(statp, packet, n, answer, sizeof(answer)); + if (n < 0) { + fprintf(stderr, "res_nsend: send error, n=%d\n", n); + break; + } else if (n > sizeof(answer)) { + fprintf(stderr, "res_nsend: buffer too small\n"); + break; + } + numzones++; + } + + /* free malloc'ed memory */ + while(zgrp_start) { + zptr = zgrp_start; + zgrp_start = zgrp_start->z_next; + res_freeupdrec(zptr->z_rr); /* Zone section we allocated. */ + free((char *)zptr); + } + + return (numzones); +} diff --git a/lib/libc/net/resolver.3 b/lib/libc/net/resolver.3 new file mode 100644 index 0000000..16f7006 --- /dev/null +++ b/lib/libc/net/resolver.3 @@ -0,0 +1,438 @@ +.\" Copyright (c) 1985, 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. +.\" +.\" @(#)resolver.3 8.1 (Berkeley) 6/4/93 +.\" $FreeBSD$ +.\" +.Dd June 4, 1993 +.Dt RESOLVER 3 +.Os +.Sh NAME +.Nm res_query , +.Nm res_search , +.Nm res_mkquery , +.Nm res_send , +.Nm res_init , +.Nm dn_comp , +.Nm dn_expand , +.Nm dn_skipname , +.Nm ns_get16 , +.Nm ns_get32 , +.Nm ns_put16 , +.Nm ns_put32 +.Nd resolver routines +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In netinet/in.h +.In arpa/nameser.h +.In resolv.h +.Ft int +.Fo res_query +.Fa "const char *dname" +.Fa "int class" +.Fa "int type" +.Fa "u_char *answer" +.Fa "int anslen" +.Fc +.Ft int +.Fo res_search +.Fa "const char *dname" +.Fa "int class" +.Fa "int type" +.Fa "u_char *answer" +.Fa "int anslen" +.Fc +.Ft int +.Fo res_mkquery +.Fa "int op" +.Fa "const char *dname" +.Fa "int class" +.Fa "int type" +.Fa "const u_char *data" +.Fa "int datalen" +.Fa "const u_char *newrr_in" +.Fa "u_char *buf" +.Fa "int buflen" +.Fc +.Ft int +.Fo res_send +.Fa "const u_char *msg" +.Fa "int msglen" +.Fa "u_char *answer" +.Fa "int anslen" +.Fc +.Ft int +.Fn res_init +.Ft int +.Fo dn_comp +.Fa "const char *exp_dn" +.Fa "u_char *comp_dn" +.Fa "int length" +.Fa "u_char **dnptrs" +.Fa "u_char **lastdnptr" +.Fc +.Ft int +.Fo dn_expand +.Fa "const u_char *msg" +.Fa "const u_char *eomorig" +.Fa "const u_char *comp_dn" +.Fa "char *exp_dn" +.Fa "int length" +.Fc +.Ft int +.Fn dn_skipname "const u_char *comp_dn" "const u_char *eom" +.Ft u_int +.Fn ns_get16 "const u_char *src" +.Ft u_long +.Fn ns_get32 "const u_char *src" +.Ft void +.Fn ns_put16 "u_int src" "u_char *dst" +.Ft void +.Fn ns_put32 "u_long src" "u_char *dst" +.Sh DESCRIPTION +These routines are used for making, sending and interpreting +query and reply messages with Internet domain name servers. +.Pp +Global configuration and state information that is used by the +resolver routines is kept in the structure +.Va _res . +Most of the values have reasonable defaults and can be ignored. +Options +stored in +.Va _res.options +are defined in +.In resolv.h +and are as follows. +Options are stored as a simple bit mask containing the bitwise ``or'' +of the options enabled. +.Bl -tag -width RES_USE_INET6 +.It Dv RES_INIT +True if the initial name server address and default domain name are +initialized (i.e., +.Fn res_init +has been called). +.It Dv RES_DEBUG +Print debugging messages. +.It Dv RES_AAONLY +Accept authoritative answers only. +With this option, +.Fn res_send +should continue until it finds an authoritative answer or finds an error. +Currently this is not implemented. +.It Dv RES_USEVC +Use +.Tn TCP +connections for queries instead of +.Tn UDP +datagrams. +.It Dv RES_STAYOPEN +Used with +.Dv RES_USEVC +to keep the +.Tn TCP +connection open between +queries. +This is useful only in programs that regularly do many queries. +.Tn UDP +should be the normal mode used. +.It Dv RES_IGNTC +Unused currently (ignore truncation errors, i.e., do not retry with +.Tn TCP ) . +.It Dv RES_RECURSE +Set the recursion-desired bit in queries. +This is the default. +.Pf ( Fn res_send +does not do iterative queries and expects the name server +to handle recursion.) +.It Dv RES_DEFNAMES +If set, +.Fn res_search +will append the default domain name to single-component names +(those that do not contain a dot). +This option is enabled by default. +.It Dv RES_DNSRCH +If this option is set, +.Fn res_search +will search for host names in the current domain and in parent domains; see +.Xr hostname 7 . +This is used by the standard host lookup routine +.Xr gethostbyname 3 . +This option is enabled by default. +.It Dv RES_NOALIASES +This option turns off the user level aliasing feature controlled by the +.Dq Ev HOSTALIASES +environment variable. +Network daemons should set this option. +.It Dv RES_USE_INET6 +Enables support for IPv6-only applications. +This causes IPv4 addresses to be returned as an IPv4 mapped address. +For example, +.Li 10.1.1.1 +will be returned as +.Li ::ffff:10.1.1.1 . +The option is meaningful with certain kernel configuration only. +.It Dv RES_USE_EDNS0 +Enables support for OPT pseudo-RR for EDNS0 extension. +With the option, resolver code will attach OPT pseudo-RR into DNS queries, +to inform of our receive buffer size. +The option will allow DNS servers to take advantage of non-default receive +buffer size, and to send larger replies. +DNS query packets with EDNS0 extension is not compatible with +non-EDNS0 DNS servers. +.El +.Pp +The +.Fn res_init +routine +reads the configuration file (if any; see +.Xr resolver 5 ) +to get the default domain name, +search list and +the Internet address of the local name server(s). +If no server is configured, the host running +the resolver is tried. +The current domain name is defined by the hostname +if not specified in the configuration file; +it can be overridden by the environment variable +.Ev LOCALDOMAIN . +This environment variable may contain several blank-separated +tokens if you wish to override the +.Em "search list" +on a per-process basis. +This is similar to the +.Ic search +command in the configuration file. +Another environment variable +.Dq Ev RES_OPTIONS +can be set to +override certain internal resolver options which are otherwise +set by changing fields in the +.Va _res +structure or are inherited from the configuration file's +.Ic options +command. +The syntax of the +.Dq Ev RES_OPTIONS +environment variable is explained in +.Xr resolver 5 . +Initialization normally occurs on the first call +to one of the following routines. +.Pp +The +.Fn res_query +function provides an interface to the server query mechanism. +It constructs a query, sends it to the local server, +awaits a response, and makes preliminary checks on the reply. +The query requests information of the specified +.Fa type +and +.Fa class +for the specified fully-qualified domain name +.Fa dname . +The reply message is left in the +.Fa answer +buffer with length +.Fa anslen +supplied by the caller. +.Pp +The +.Fn res_search +routine makes a query and awaits a response like +.Fn res_query , +but in addition, it implements the default and search rules +controlled by the +.Dv RES_DEFNAMES +and +.Dv RES_DNSRCH +options. +It returns the first successful reply. +.Pp +The remaining routines are lower-level routines used by +.Fn res_query . +The +.Fn res_mkquery +function +constructs a standard query message and places it in +.Fa buf . +It returns the size of the query, or \-1 if the query is +larger than +.Fa buflen . +The query type +.Fa op +is usually +.Dv QUERY , +but can be any of the query types defined in +.In arpa/nameser.h . +The domain name for the query is given by +.Fa dname . +The +.Fa newrr_in +argument +is currently unused but is intended for making update messages. +.Pp +The +.Fn res_send +routine +sends a pre-formatted query and returns an answer. +It will call +.Fn res_init +if +.Dv RES_INIT +is not set, send the query to the local name server, and +handle timeouts and retries. +The length of the reply message is returned, or +\-1 if there were errors. +.Pp +The +.Fn dn_comp +function +compresses the domain name +.Fa exp_dn +and stores it in +.Fa comp_dn . +The size of the compressed name is returned or \-1 if there were errors. +The size of the array pointed to by +.Fa comp_dn +is given by +.Fa length . +The compression uses +an array of pointers +.Fa dnptrs +to previously-compressed names in the current message. +The first pointer points to +the beginning of the message and the list ends with +.Dv NULL . +The limit to the array is specified by +.Fa lastdnptr . +A side effect of +.Fn dn_comp +is to update the list of pointers for +labels inserted into the message +as the name is compressed. +If +.Fa dnptr +is +.Dv NULL , +names are not compressed. +If +.Fa lastdnptr +is +.Dv NULL , +the list of labels is not updated. +.Pp +The +.Fn dn_expand +entry +expands the compressed domain name +.Fa comp_dn +to a full domain name +The compressed name is contained in a query or reply message; +.Fa msg +is a pointer to the beginning of the message. +The uncompressed name is placed in the buffer indicated by +.Fa exp_dn +which is of size +.Fa length . +The size of compressed name is returned or \-1 if there was an error. +.Pp +The +.Fn dn_skipname +function skips over a compressed domain name, which starts at a location +pointed to by +.Fa comp_dn . +The compressed name is contained in a query or reply message; +.Fa eom +is a pointer to the end of the message. +The size of compressed name is returned or \-1 if there was +an error. +.Pp +The +.Fn ns_get16 +function gets a 16-bit quantity from a buffer pointed to by +.Fa src . +.Pp +The +.Fn ns_get32 +function gets a 32-bit quantity from a buffer pointed to by +.Fa src . +.Pp +The +.Fn ns_put16 +function puts a 16-bit quantity +.Fa src +to a buffer pointed to by +.Fa dst . +.Pp +The +.Fn ns_put32 +function puts a 32-bit quantity +.Fa src +to a buffer pointed to by +.Fa dst . +.Sh IMPLEMENTATION NOTES +This implementation of the resolver is thread-safe, but it will not +function properly if the programmer attempts to declare his or her own +.Va _res +structure in an attempt to replace the per-thread version referred to +by that macro. +.Sh RETURN VALUES +The +.Fn res_init +function will return 0 on success, or \-1 in a threaded program if +per-thread storage could not be allocated. +.Sh FILES +.Bl -tag -width /etc/resolv.conf +.It Pa /etc/resolv.conf +The configuration file, +see +.Xr resolver 5 . +.El +.Sh SEE ALSO +.Xr gethostbyname 3 , +.Xr resolver 5 , +.Xr hostname 7 , +.Xr named 8 +.Pp +.%T RFC1032 , +.%T RFC1033 , +.%T RFC1034 , +.%T RFC1035 , +.%T RFC974 +.Rs +.%T "Name Server Operations Guide for BIND" +.Re +.Sh HISTORY +The +.Nm +function appeared in +.Bx 4.3 . diff --git a/lib/libc/net/rthdr.c b/lib/libc/net/rthdr.c new file mode 100644 index 0000000..5fbb4bb --- /dev/null +++ b/lib/libc/net/rthdr.c @@ -0,0 +1,442 @@ +/* $KAME: rthdr.c,v 1.19 2003/06/06 10:48:51 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> + +#include <string.h> +#include <stdio.h> + +/* + * RFC2292 API + */ + +size_t +inet6_rthdr_space(type, seg) + int type, seg; +{ + switch (type) { + case IPV6_RTHDR_TYPE_0: + if (seg < 1 || seg > 23) + return (0); +#ifdef COMPAT_RFC2292 + return (CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1) + + sizeof(struct ip6_rthdr0))); +#else + return (CMSG_SPACE(sizeof(struct in6_addr) * seg + + sizeof(struct ip6_rthdr0))); +#endif + default: + return (0); + } +} + +struct cmsghdr * +inet6_rthdr_init(bp, type) + void *bp; + int type; +{ + struct cmsghdr *ch = (struct cmsghdr *)bp; + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(ch); + + ch->cmsg_level = IPPROTO_IPV6; + ch->cmsg_type = IPV6_RTHDR; + + switch (type) { + case IPV6_RTHDR_TYPE_0: +#ifdef COMPAT_RFC2292 + ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - + sizeof(struct in6_addr)); +#else + ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0)); +#endif + + bzero(rthdr, sizeof(struct ip6_rthdr0)); + rthdr->ip6r_type = IPV6_RTHDR_TYPE_0; + return (ch); + default: + return (NULL); + } +} + +/* ARGSUSED */ +int +inet6_rthdr_add(cmsg, addr, flags) + struct cmsghdr *cmsg; + const struct in6_addr *addr; + u_int flags; +{ + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); + + switch (rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + { + struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; + if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) + return (-1); + if (rt0->ip6r0_segleft == 23) + return (-1); + +#ifdef COMPAT_RFC1883 /* XXX */ + if (flags == IPV6_RTHDR_STRICT) { + int c, b; + c = rt0->ip6r0_segleft / 8; + b = rt0->ip6r0_segleft % 8; + rt0->ip6r0_slmap[c] |= (1 << (7 - b)); + } +#else + if (flags != IPV6_RTHDR_LOOSE) + return (-1); +#endif + rt0->ip6r0_segleft++; + bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3), + sizeof(struct in6_addr)); + rt0->ip6r0_len += sizeof(struct in6_addr) >> 3; + cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3); + break; + } + default: + return (-1); + } + + return (0); +} + +/* ARGSUSED */ +int +inet6_rthdr_lasthop(cmsg, flags) + struct cmsghdr *cmsg; + unsigned int flags; +{ + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); + + switch (rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + { + struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; +#ifdef COMPAT_RFC1883 /* XXX */ + if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) + return (-1); +#endif /* COMPAT_RFC1883 */ + if (rt0->ip6r0_segleft > 23) + return (-1); +#ifdef COMPAT_RFC1883 /* XXX */ + if (flags == IPV6_RTHDR_STRICT) { + int c, b; + c = rt0->ip6r0_segleft / 8; + b = rt0->ip6r0_segleft % 8; + rt0->ip6r0_slmap[c] |= (1 << (7 - b)); + } +#else + if (flags != IPV6_RTHDR_LOOSE) + return (-1); +#endif /* COMPAT_RFC1883 */ + break; + } + default: + return (-1); + } + + return (0); +} + +#if 0 +int +inet6_rthdr_reverse(in, out) + const struct cmsghdr *in; + struct cmsghdr *out; +{ + + return (-1); +} +#endif + +int +inet6_rthdr_segments(cmsg) + const struct cmsghdr *cmsg; +{ + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); + + switch (rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + { + struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; + + if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) + return (-1); + + return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); + } + + default: + return (-1); + } +} + +struct in6_addr * +inet6_rthdr_getaddr(cmsg, idx) + struct cmsghdr *cmsg; + int idx; +{ + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); + + switch (rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + { + struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; + int naddr; + + if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) + return NULL; + naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); + if (idx <= 0 || naddr < idx) + return NULL; +#ifdef COMPAT_RFC2292 + return (((struct in6_addr *)(rt0 + 1)) + idx - 1); +#else + return (((struct in6_addr *)(rt0 + 1)) + idx); +#endif + } + + default: + return NULL; + } +} + +int +inet6_rthdr_getflags(cmsg, idx) + const struct cmsghdr *cmsg; + int idx; +{ + struct ip6_rthdr *rthdr; + + rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg); + + switch (rthdr->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + { + struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr; + int naddr; + + if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) + return (-1); + naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr); + if (idx < 0 || naddr < idx) + return (-1); +#ifdef COMPAT_RFC1883 /* XXX */ + if (rt0->ip6r0_slmap[idx / 8] & (0x80 >> (idx % 8))) + return IPV6_RTHDR_STRICT; + else + return IPV6_RTHDR_LOOSE; +#else + return IPV6_RTHDR_LOOSE; +#endif /* COMPAT_RFC1883 */ + } + + default: + return (-1); + } +} + +/* + * RFC3542 API + */ + +socklen_t +inet6_rth_space(int type, int segments) +{ + switch (type) { + case IPV6_RTHDR_TYPE_0: + return (((segments * 2) + 1) << 3); + default: + return (0); /* type not suppported */ + } +} + +void * +inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments) +{ + struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; + struct ip6_rthdr0 *rth0; + + switch (type) { + case IPV6_RTHDR_TYPE_0: + /* length validation */ + if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments)) + return (NULL); + + memset(bp, 0, bp_len); + rth0 = (struct ip6_rthdr0 *)rth; + rth0->ip6r0_len = segments * 2; + rth0->ip6r0_type = IPV6_RTHDR_TYPE_0; + rth0->ip6r0_segleft = 0; + rth0->ip6r0_reserved = 0; + break; + default: + return (NULL); /* type not supported */ + } + + return (bp); +} + +int +inet6_rth_add(void *bp, const struct in6_addr *addr) +{ + struct ip6_rthdr *rth = (struct ip6_rthdr *)bp; + struct ip6_rthdr0 *rth0; + struct in6_addr *nextaddr; + + switch (rth->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rth0 = (struct ip6_rthdr0 *)rth; + nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft; + *nextaddr = *addr; + rth0->ip6r0_segleft++; + break; + default: + return (-1); /* type not supported */ + } + + return (0); +} + +int +inet6_rth_reverse(const void *in, void *out) +{ + struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in; + struct ip6_rthdr0 *rth0_in, *rth0_out; + int i, segments; + + switch (rth_in->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rth0_in = (struct ip6_rthdr0 *)in; + rth0_out = (struct ip6_rthdr0 *)out; + + /* parameter validation XXX too paranoid? */ + if (rth0_in->ip6r0_len % 2) + return (-1); + segments = rth0_in->ip6r0_len / 2; + + /* we can't use memcpy here, since in and out may overlap */ + memmove((void *)rth0_out, (void *)rth0_in, + ((rth0_in->ip6r0_len) + 1) << 3); + rth0_out->ip6r0_segleft = segments; + + /* reverse the addresses */ + for (i = 0; i < segments / 2; i++) { + struct in6_addr addr_tmp, *addr1, *addr2; + + addr1 = (struct in6_addr *)(rth0_out + 1) + i; + addr2 = (struct in6_addr *)(rth0_out + 1) + + (segments - i - 1); + addr_tmp = *addr1; + *addr1 = *addr2; + *addr2 = addr_tmp; + } + + break; + default: + return (-1); /* type not supported */ + } + + return (0); +} + +int +inet6_rth_segments(const void *bp) +{ + struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; + struct ip6_rthdr0 *rh0; + int addrs; + + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rh0 = (struct ip6_rthdr0 *)bp; + + /* + * Validation for a type-0 routing header. + * Is this too strict? + */ + if ((rh0->ip6r0_len % 2) != 0 || + (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) + return (-1); + + return (addrs); + default: + return (-1); /* unknown type */ + } +} + +struct in6_addr * +inet6_rth_getaddr(const void *bp, int idx) +{ + struct ip6_rthdr *rh = (struct ip6_rthdr *)bp; + struct ip6_rthdr0 *rh0; + int addrs; + + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rh0 = (struct ip6_rthdr0 *)bp; + + /* + * Validation for a type-0 routing header. + * Is this too strict? + */ + if ((rh0->ip6r0_len % 2) != 0 || + (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft) + return (NULL); + + if (idx < 0 || addrs <= idx) + return (NULL); + + return (((struct in6_addr *)(rh0 + 1)) + idx); + default: + return (NULL); /* unknown type */ + break; + } +} diff --git a/lib/libc/net/send.c b/lib/libc/net/send.c new file mode 100644 index 0000000..f10d21b --- /dev/null +++ b/lib/libc/net/send.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1988, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)send.c 8.2 (Berkeley) 2/21/94"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/types.h> +#include <sys/socket.h> + +#include <stddef.h> +#include "un-namespace.h" + +ssize_t +send(s, msg, len, flags) + int s, flags; + size_t len; + const void *msg; +{ + return (_sendto(s, msg, len, flags, NULL, 0)); +} diff --git a/lib/libc/net/sockatmark.3 b/lib/libc/net/sockatmark.3 new file mode 100644 index 0000000..61a1a9f --- /dev/null +++ b/lib/libc/net/sockatmark.3 @@ -0,0 +1,123 @@ +.\" Copyright (c) 2002 William C. Fenner. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 13, 2002 +.Dt SOCKATMARK 3 +.Os +.Sh NAME +.Nm sockatmark +.Nd determine whether the read pointer is at the OOB mark +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/socket.h +.Ft int +.Fn sockatmark "int s" +.Sh DESCRIPTION +To find out if the read pointer is currently pointing at +the mark in the data stream, the +.Fn sockatmark +function is provided. +If +.Fn sockatmark +returns 1, the next read will return data +after the mark. +Otherwise (assuming out of band data has arrived), +the next read will provide data sent by the client prior +to transmission of the out of band signal. +The routine used +in the remote login process to flush output on receipt of an +interrupt or quit signal is shown below. +It reads the normal data up to the mark (to discard it), +then reads the out-of-band byte. +.Bd -literal -offset indent +#include <sys/socket.h> +\&... +oob() +{ + int out = FWRITE, mark; + char waste[BUFSIZ]; + + /* flush local terminal output */ + ioctl(1, TIOCFLUSH, (char *)&out); + for (;;) { + if ((mark = sockatmark(rem)) < 0) { + perror("sockatmark"); + break; + } + if (mark) + break; + (void) read(rem, waste, sizeof (waste)); + } + if (recv(rem, &mark, 1, MSG_OOB) < 0) { + perror("recv"); + ... + } + ... +} +.Ed +.Sh RETURN VALUES +Upon successful completion, the +.Fn sockatmark +function returns the value 1 if the read pointer is pointing at +the OOB mark, 0 if it is not. +Otherwise the value \-1 is returned +and the global variable +.Va errno +is set to +indicate the error. +.Sh ERRORS +The +.Fn sockatmark +call fails if: +.Bl -tag -width Er +.It Bq Er EBADF +The +.Fa s +argument +is not a valid descriptor. +.It Bq Er ENOTTY +The +.Fa s +argument +is a descriptor for a file, not a socket. +.El +.Sh SEE ALSO +.Xr recv 2 , +.Xr send 2 +.Sh HISTORY +The +.Fn sockatmark +function was introduced by +.St -p1003.1-2001 , +to standardize the historical +.Dv SIOCATMARK +.Xr ioctl 2 . +The +.Er ENOTTY +error instead of the usual +.Er ENOTSOCK +is to match the historical behavior of +.Dv SIOCATMARK . diff --git a/lib/libc/net/sockatmark.c b/lib/libc/net/sockatmark.c new file mode 100644 index 0000000..e416de8 --- /dev/null +++ b/lib/libc/net/sockatmark.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002 William C. Fenner. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + * $FreeBSD$ + */ +#include <sys/ioctl.h> + +int sockatmark(int s) +{ + int atmark; + + if (ioctl(s, SIOCATMARK, &atmark) == -1) + return -1; + return atmark; +} diff --git a/lib/libc/net/vars.c b/lib/libc/net/vars.c new file mode 100644 index 0000000..42ee205 --- /dev/null +++ b/lib/libc/net/vars.c @@ -0,0 +1,45 @@ +/* $KAME: vars.c,v 1.2 2001/08/20 02:32:41 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <netinet/in.h> + +/* + * Definitions of some costant IPv6 addresses. + */ +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; +const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; + |