From 54d956c541aa6ea5a8e39d3db8bb3d4f3c9f4bb2 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Tue, 19 Mar 2002 11:28:17 +0000 Subject: Bart Visscher has added some missing IPV6 support, and added several additional reentrant networking functions such that iptables now runs with IPV6 support. --- libc/inet/Makefile | 7 +- libc/inet/gai_strerror.c | 62 ++++ libc/inet/getaddrinfo.c | 863 +++++++++++++++++++++++++++++++++++++++++++++ libc/inet/getservice.c | 98 +++-- libc/inet/if_nametoindex.c | 40 +++ libc/inet/resolv.c | 838 ++++++++++++++++++++++++++++--------------- 6 files changed, 1601 insertions(+), 307 deletions(-) create mode 100644 libc/inet/gai_strerror.c create mode 100644 libc/inet/getaddrinfo.c create mode 100644 libc/inet/if_nametoindex.c diff --git a/libc/inet/Makefile b/libc/inet/Makefile index 8f053f1f8..14c533be5 100644 --- a/libc/inet/Makefile +++ b/libc/inet/Makefile @@ -41,8 +41,9 @@ MOBJ2=encodeh.o decodeh.o encoded.o decoded.o lengthd.o encodeq.o \ formquery.o dnslookup.o resolveaddress.o resolvemailbox.o \ opennameservers.o closenameservers.o resolvename.o gethostbyname.o\ res_init.o res_query.o gethostbyaddr.o \ - get_hosts_byname.o get_hosts_byaddr.o read_etc_hosts.o \ - gethostbyname2.o getnameinfo.o gethostent.o sethostent.o endhostent.o + read_etc_hosts_r.o get_hosts_byname_r.o get_hosts_byaddr_r.o \ + gethostbyname2.o getnameinfo.o gethostent.o sethostent.o endhostent.o \ + gethostbyname_r.o gethostbyname2_r.o gethostbyaddr_r.o MSRC3=socketcalls.c MOBJ3= accept.o bind.o connect.o getpeername.o getsockname.o getsockopt.o \ @@ -50,7 +51,7 @@ MOBJ3= accept.o bind.o connect.o getpeername.o getsockname.o getsockopt.o \ setsockopt.o shutdown.o socket.o socketpair.o CSRC =getservice.c getproto.c hostid.c getnetent.c getnetbynm.c getnetbyad.c \ - inet_net.c ntop.c herror.c + inet_net.c ntop.c herror.c if_nametoindex.c gai_strerror.c getaddrinfo.c COBJS=$(patsubst %.c,%.o, $(CSRC)) diff --git a/libc/inet/gai_strerror.c b/libc/inet/gai_strerror.c new file mode 100644 index 000000000..56fb57684 --- /dev/null +++ b/libc/inet/gai_strerror.c @@ -0,0 +1,62 @@ +/* Copyright (C) 1997, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Philip Blundell , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define _GNU_SOURCE +#define __FORCE_GLIBC +#include +#include +#include + +#define N_(x) x +#define _(x) x +static struct + { + int code; + const char *msg; + } +values[] = + { + { EAI_ADDRFAMILY, N_("Address family for hostname not supported") }, + { EAI_AGAIN, N_("Temporary failure in name resolution") }, + { EAI_BADFLAGS, N_("Bad value for ai_flags") }, + { EAI_FAIL, N_("Non-recoverable failure in name resolution") }, + { EAI_FAMILY, N_("ai_family not supported") }, + { EAI_MEMORY, N_("Memory allocation failure") }, + { EAI_NODATA, N_("No address associated with hostname") }, + { EAI_NONAME, N_("Name or service not known") }, + { EAI_SERVICE, N_("Servname not supported for ai_socktype") }, + { EAI_SOCKTYPE, N_("ai_socktype not supported") }, + { EAI_SYSTEM, N_("System error") }, + { EAI_INPROGRESS, N_("Processing request in progress") }, + { EAI_CANCELED, N_("Request canceled") }, + { EAI_NOTCANCELED, N_("Request not canceled") }, + { EAI_ALLDONE, N_("All requests done") }, + { EAI_INTR, N_("Interrupted by a signal") } + }; + +const char * +gai_strerror (int code) +{ + size_t i; + for (i = 0; i < sizeof (values) / sizeof (values[0]); ++i) + if (values[i].code == code) + return _(values[i].msg); + + return _("Unknown error"); +} diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c new file mode 100644 index 000000000..6a068442a --- /dev/null +++ b/libc/inet/getaddrinfo.c @@ -0,0 +1,863 @@ +/* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */ + +/* The Inner Net License, Version 2.00 + + The author(s) grant permission for redistribution and use in source and +binary forms, with or without modification, of the software and documentation +provided that the following conditions are met: + +0. If you receive a version of the software that is specifically labelled + as not being for redistribution (check the version message and/or README), + you are not permitted to redistribute that version of the software in any + way or form. +1. All terms of the all other applicable copyrights and licenses must be + followed. +2. Redistributions of source code must retain the authors' copyright + notice(s), this list of conditions, and the following disclaimer. +3. Redistributions in binary form must reproduce the authors' copyright + notice(s), this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. +4. All advertising materials mentioning features or use of this software + must display the following acknowledgement with the name(s) of the + authors as specified in the copyright notice(s) substituted where + indicated: + + This product includes software developed by , The Inner + Net, and other contributors. + +5. Neither the name(s) of the author(s) 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 ITS AUTHORS 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 AUTHORS 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 these license terms cause you a real problem, contact the author. */ + +/* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */ + +#define _GNU_SOURCE +#define __FORCE_GLIBC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GAIH_OKIFUNSPEC 0x0100 +#define GAIH_EAI ~(GAIH_OKIFUNSPEC) + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +struct gaih_service +{ + const char *name; + int num; +}; + +struct gaih_servtuple +{ + struct gaih_servtuple *next; + int socktype; + int protocol; + int port; +}; + +static const struct gaih_servtuple nullserv; + +struct gaih_addrtuple +{ + struct gaih_addrtuple *next; + int family; + char addr[16]; + uint32_t scopeid; +}; + +struct gaih_typeproto +{ + int socktype; + int protocol; + char name[4]; + int protoflag; +}; + +/* Values for `protoflag'. */ +#define GAI_PROTO_NOSERVICE 1 +#define GAI_PROTO_PROTOANY 2 + +static const struct gaih_typeproto gaih_inet_typeproto[] = +{ + { 0, 0, "", 0 }, + { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 }, + { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 }, + { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE }, + { 0, 0, "", 0 } +}; + +struct gaih +{ + int family; + int (*gaih)(const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai); +}; + +#if PF_UNSPEC == 0 +static const struct addrinfo default_hints; +#else +static const struct addrinfo default_hints = +{ 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL }; +#endif + + +static int addrconfig (sa_family_t af) +{ + int s; + int ret; + int saved_errno = errno; + s = socket(af, SOCK_DGRAM, 0); + if (s < 0) + ret = (errno == EMFILE) ? 1 : 0; + else + { + close(s); + ret = 1; + } + __set_errno (saved_errno); + return ret; +} + +#if 0 +/* Using Unix sockets this way is a security risk. */ +static int +gaih_local (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai) +{ + struct utsname utsname; + + if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST)) + return GAIH_OKIFUNSPEC | -EAI_NONAME; + + if ((name != NULL) || (req->ai_flags & AI_CANONNAME)) + if (uname (&utsname) < 0) + return -EAI_SYSTEM; + + if (name != NULL) + { + if (strcmp(name, "localhost") && + strcmp(name, "local") && + strcmp(name, "unix") && + strcmp(name, utsname.nodename)) + return GAIH_OKIFUNSPEC | -EAI_NONAME; + } + + if (req->ai_protocol || req->ai_socktype) + { + const struct gaih_typeproto *tp = gaih_inet_typeproto + 1; + + while (tp->name[0] + && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0 + || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype) + || (req->ai_protocol != 0 + && !(tp->protoflag & GAI_PROTO_PROTOANY) + && req->ai_protocol != tp->protocol))) + ++tp; + + if (! tp->name[0]) + { + if (req->ai_socktype) + return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); + else + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + } + } + + *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un) + + ((req->ai_flags & AI_CANONNAME) + ? (strlen(utsname.nodename) + 1): 0)); + if (*pai == NULL) + return -EAI_MEMORY; + + (*pai)->ai_next = NULL; + (*pai)->ai_flags = req->ai_flags; + (*pai)->ai_family = AF_LOCAL; + (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM; + (*pai)->ai_protocol = req->ai_protocol; + (*pai)->ai_addrlen = sizeof (struct sockaddr_un); + (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo); + +#if SALEN + ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len = + sizeof (struct sockaddr_un); +#endif /* SALEN */ + + ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL; + memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX); + + if (service) + { + struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr; + + if (strchr (service->name, '/') != NULL) + { + if (strlen (service->name) >= sizeof (sunp->sun_path)) + return GAIH_OKIFUNSPEC | -EAI_SERVICE; + + strcpy (sunp->sun_path, service->name); + } + else + { + if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >= + sizeof (sunp->sun_path)) + return GAIH_OKIFUNSPEC | -EAI_SERVICE; + + __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name); + } + } + else + { + /* This is a dangerous use of the interface since there is a time + window between the test for the file and the actual creation + (done by the caller) in which a file with the same name could + be created. */ + char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path; + + if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0), + 0) != 0 + || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0) + return -EAI_SYSTEM; + } + + if (req->ai_flags & AI_CANONNAME) + (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo) + + sizeof (struct sockaddr_un), + utsname.nodename); + else + (*pai)->ai_canonname = NULL; + return 0; +} +#endif /* 0 */ + +static int +gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, + const struct addrinfo *req, struct gaih_servtuple *st) +{ + struct servent *s; + size_t tmpbuflen = 1024; + struct servent ts; + char *tmpbuf; + int r; + + do + { + tmpbuf = alloca (tmpbuflen); + + r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen, + &s); + if (r != 0 || s == NULL) + { + if (r == ERANGE) + tmpbuflen *= 2; + else + return GAIH_OKIFUNSPEC | -EAI_SERVICE; + } + } + while (r); + + st->next = NULL; + st->socktype = tp->socktype; + st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) + ? req->ai_protocol : tp->protocol); + st->port = s->s_port; + + return 0; +} + +#define gethosts(_family, _type) \ +{ \ + int i, herrno; \ + size_t tmpbuflen; \ + struct hostent th; \ + char *tmpbuf; \ + tmpbuflen = 512; \ + no_data = 0; \ + do { \ + tmpbuflen *= 2; \ + tmpbuf = alloca (tmpbuflen); \ + rc = gethostbyname2_r (name, _family, &th, tmpbuf, \ + tmpbuflen, &h, &herrno); \ + } while (rc == ERANGE && herrno == NETDB_INTERNAL); \ + if (rc != 0) \ + { \ + if (herrno == NETDB_INTERNAL) \ + { \ + __set_h_errno (herrno); \ + return -EAI_SYSTEM; \ + } \ + if (herrno == TRY_AGAIN) \ + no_data = EAI_AGAIN; \ + else \ + no_data = herrno == NO_DATA; \ + } \ + else if (h != NULL) \ + { \ + for (i = 0; h->h_addr_list[i]; i++) \ + { \ + if (*pat == NULL) { \ + *pat = alloca (sizeof(struct gaih_addrtuple)); \ + (*pat)->scopeid = 0; \ + } \ + (*pat)->next = NULL; \ + (*pat)->family = _family; \ + memcpy ((*pat)->addr, h->h_addr_list[i], \ + sizeof(_type)); \ + pat = &((*pat)->next); \ + } \ + } \ +} + +static int +gaih_inet (const char *name, const struct gaih_service *service, + const struct addrinfo *req, struct addrinfo **pai) +{ + const struct gaih_typeproto *tp = gaih_inet_typeproto; + struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; + struct gaih_addrtuple *at = NULL; + int rc; + int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) && + (req->ai_flags & AI_V4MAPPED); + + if (req->ai_protocol || req->ai_socktype) + { + ++tp; + + while (tp->name[0] + && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype) + || (req->ai_protocol != 0 + && !(tp->protoflag & GAI_PROTO_PROTOANY) + && req->ai_protocol != tp->protocol))) + ++tp; + + if (! tp->name[0]) + { + if (req->ai_socktype) + return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE); + else + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + } + } + + if (service != NULL) + { + if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + + if (service->num < 0) + { + if (tp->name[0]) + { + st = (struct gaih_servtuple *) + alloca (sizeof (struct gaih_servtuple)); + + if ((rc = gaih_inet_serv (service->name, tp, req, st))) + return rc; + } + else + { + struct gaih_servtuple **pst = &st; + for (tp++; tp->name[0]; tp++) + { + struct gaih_servtuple *newp; + + if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) + continue; + + if (req->ai_socktype != 0 + && req->ai_socktype != tp->socktype) + continue; + if (req->ai_protocol != 0 + && !(tp->protoflag & GAI_PROTO_PROTOANY) + && req->ai_protocol != tp->protocol) + continue; + + newp = (struct gaih_servtuple *) + alloca (sizeof (struct gaih_servtuple)); + + if ((rc = gaih_inet_serv (service->name, tp, req, newp))) + { + if (rc & GAIH_OKIFUNSPEC) + continue; + return rc; + } + + *pst = newp; + pst = &(newp->next); + } + if (st == (struct gaih_servtuple *) &nullserv) + return (GAIH_OKIFUNSPEC | -EAI_SERVICE); + } + } + else + { + st = alloca (sizeof (struct gaih_servtuple)); + st->next = NULL; + st->socktype = tp->socktype; + st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) + ? req->ai_protocol : tp->protocol); + st->port = htons (service->num); + } + } + else if (req->ai_socktype || req->ai_protocol) + { + st = alloca (sizeof (struct gaih_servtuple)); + st->next = NULL; + st->socktype = tp->socktype; + st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) + ? req->ai_protocol : tp->protocol); + st->port = 0; + } + else + { + /* + * Neither socket type nor protocol is set. Return all socket types + * we know about. + */ + struct gaih_servtuple **lastp = &st; + for (++tp; tp->name[0]; ++tp) + { + struct gaih_servtuple *newp; + + newp = alloca (sizeof (struct gaih_servtuple)); + newp->next = NULL; + newp->socktype = tp->socktype; + newp->protocol = tp->protocol; + newp->port = 0; + + *lastp = newp; + lastp = &newp->next; + } + } + + if (name != NULL) + { + at = alloca (sizeof (struct gaih_addrtuple)); + + at->family = AF_UNSPEC; + at->scopeid = 0; + at->next = NULL; + + if (inet_pton (AF_INET, name, at->addr) > 0) + { + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET || v4mapped) + at->family = AF_INET; + else + return -EAI_FAMILY; + } + +#if __UCLIBC_HAS_IPV6__ + if (at->family == AF_UNSPEC) + { + char *namebuf = strdupa (name); + char *scope_delim; + + scope_delim = strchr (namebuf, SCOPE_DELIMITER); + if (scope_delim != NULL) + *scope_delim = '\0'; + + if (inet_pton (AF_INET6, namebuf, at->addr) > 0) + { + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) + at->family = AF_INET6; + else + return -EAI_FAMILY; + + if (scope_delim != NULL) + { + int try_numericscope = 0; + if (IN6_IS_ADDR_LINKLOCAL (at->addr) + || IN6_IS_ADDR_MC_LINKLOCAL (at->addr)) + { + at->scopeid = if_nametoindex (scope_delim + 1); + if (at->scopeid == 0) + try_numericscope = 1; + } + else + try_numericscope = 1; + + if (try_numericscope != 0) + { + char *end; + assert (sizeof (uint32_t) <= sizeof (unsigned long)); + at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end, + 10); + if (*end != '\0') + return GAIH_OKIFUNSPEC | -EAI_NONAME; + } + } + } + } +#endif + + if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0) + { + struct hostent *h; + struct gaih_addrtuple **pat = &at; + int no_data = 0; + int no_inet6_data; + + /* + * If we are looking for both IPv4 and IPv6 address we don't want + * the lookup functions to automatically promote IPv4 addresses to + * IPv6 addresses. + */ + +#if __UCLIBC_HAS_IPV6__ + if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) + gethosts (AF_INET6, struct in6_addr); +#endif + no_inet6_data = no_data; + + if (req->ai_family == AF_INET || + (!v4mapped && req->ai_family == AF_UNSPEC) || + (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL)))) + gethosts (AF_INET, struct in_addr); + + if (no_data != 0 && no_inet6_data != 0) + { + /* If both requests timed out report this. */ + if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) + return -EAI_AGAIN; + + /* + * We made requests but they turned out no data. + * The name is known, though. + */ + return (GAIH_OKIFUNSPEC | -EAI_AGAIN); + } + } + + if (at->family == AF_UNSPEC) + return (GAIH_OKIFUNSPEC | -EAI_NONAME); + } + else + { + struct gaih_addrtuple *atr; + atr = at = alloca (sizeof (struct gaih_addrtuple)); + memset (at, '\0', sizeof (struct gaih_addrtuple)); + + if (req->ai_family == 0) + { + at->next = alloca (sizeof (struct gaih_addrtuple)); + memset (at->next, '\0', sizeof (struct gaih_addrtuple)); + } + +#if __UCLIBC_HAS_IPV6__ + if (req->ai_family == 0 || req->ai_family == AF_INET6) + { + at->family = AF_INET6; + if ((req->ai_flags & AI_PASSIVE) == 0) + memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); + atr = at->next; + } +#endif + + if (req->ai_family == 0 || req->ai_family == AF_INET) + { + atr->family = AF_INET; + if ((req->ai_flags & AI_PASSIVE) == 0) + *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK); + } + } + + if (pai == NULL) + return 0; + + { + const char *c = NULL; + struct gaih_servtuple *st2; + struct gaih_addrtuple *at2 = at; + size_t socklen, namelen; + sa_family_t family; + + /* + * buffer is the size of an unformatted IPv6 address in + * printable format. + */ + char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + + while (at2 != NULL) + { + if (req->ai_flags & AI_CANONNAME) + { + struct hostent *h = NULL; + + int herrno; + struct hostent th; + size_t tmpbuflen = 512; + char *tmpbuf; + + do + { + tmpbuflen *= 2; + tmpbuf = alloca (tmpbuflen); + + if (tmpbuf == NULL) + return -EAI_MEMORY; + + rc = gethostbyaddr_r (at2->addr, + ((at2->family == AF_INET6) + ? sizeof(struct in6_addr) + : sizeof(struct in_addr)), + at2->family, &th, tmpbuf, tmpbuflen, + &h, &herrno); + + } + while (rc == errno && herrno == NETDB_INTERNAL); + + if (rc != 0 && herrno == NETDB_INTERNAL) + { + __set_h_errno (herrno); + return -EAI_SYSTEM; + } + + if (h == NULL) + c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer)); + else + c = h->h_name; + + if (c == NULL) + return GAIH_OKIFUNSPEC | -EAI_NONAME; + + namelen = strlen (c) + 1; + } + else + namelen = 0; + +#if __UCLIBC_HAS_IPV6__ + if (at2->family == AF_INET6 || v4mapped) + { + family = AF_INET6; + socklen = sizeof (struct sockaddr_in6); + } + else +#endif + { + family = AF_INET; + socklen = sizeof (struct sockaddr_in); + } + + for (st2 = st; st2 != NULL; st2 = st2->next) + { + *pai = malloc (sizeof (struct addrinfo) + socklen + namelen); + if (*pai == NULL) + return -EAI_MEMORY; + + (*pai)->ai_flags = req->ai_flags; + (*pai)->ai_family = family; + (*pai)->ai_socktype = st2->socktype; + (*pai)->ai_protocol = st2->protocol; + (*pai)->ai_addrlen = socklen; + (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo); +#if SALEN + (*pai)->ai_addr->sa_len = socklen; +#endif /* SALEN */ + (*pai)->ai_addr->sa_family = family; + +#if __UCLIBC_HAS_IPV6__ + if (family == AF_INET6) + { + struct sockaddr_in6 *sin6p = + (struct sockaddr_in6 *) (*pai)->ai_addr; + + sin6p->sin6_flowinfo = 0; + if (at2->family == AF_INET6) + { + memcpy (&sin6p->sin6_addr, + at2->addr, sizeof (struct in6_addr)); + } + else + { + sin6p->sin6_addr.s6_addr32[0] = 0; + sin6p->sin6_addr.s6_addr32[1] = 0; + sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff); + memcpy(&sin6p->sin6_addr.s6_addr32[3], + at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3])); + } + sin6p->sin6_port = st2->port; + sin6p->sin6_scope_id = at2->scopeid; + } + else +#endif + { + struct sockaddr_in *sinp = + (struct sockaddr_in *) (*pai)->ai_addr; + + memcpy (&sinp->sin_addr, + at2->addr, sizeof (struct in_addr)); + sinp->sin_port = st2->port; + memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); + } + + if (c) + { + (*pai)->ai_canonname = ((void *) (*pai) + + sizeof (struct addrinfo) + socklen); + strcpy ((*pai)->ai_canonname, c); + } + else + (*pai)->ai_canonname = NULL; + + (*pai)->ai_next = NULL; + pai = &((*pai)->ai_next); + } + + at2 = at2->next; + } + } + return 0; +} + +static struct gaih gaih[] = +{ +#if __UCLIBC_HAS_IPV6__ + { PF_INET6, gaih_inet }, +#endif + { PF_INET, gaih_inet }, +#if 0 + { PF_LOCAL, gaih_local }, +#endif + { PF_UNSPEC, NULL } +}; + +int +getaddrinfo (const char *name, const char *service, + const struct addrinfo *hints, struct addrinfo **pai) +{ + int i = 0, j = 0, last_i = 0; + struct addrinfo *p = NULL, **end; + struct gaih *g = gaih, *pg = NULL; + struct gaih_service gaih_service, *pservice; + + if (name != NULL && name[0] == '*' && name[1] == 0) + name = NULL; + + if (service != NULL && service[0] == '*' && service[1] == 0) + service = NULL; + + if (name == NULL && service == NULL) + return EAI_NONAME; + + if (hints == NULL) + hints = &default_hints; + + if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST| + AI_ADDRCONFIG|AI_V4MAPPED|AI_ALL)) + return EAI_BADFLAGS; + + if ((hints->ai_flags & AI_CANONNAME) && name == NULL) + return EAI_BADFLAGS; + + if (service && service[0]) + { + char *c; + gaih_service.name = service; + gaih_service.num = strtoul (gaih_service.name, &c, 10); + if (*c) + gaih_service.num = -1; + else + /* + * Can't specify a numerical socket unless a protocol + * family was given. + */ + if (hints->ai_socktype == 0 && hints->ai_protocol == 0) + return EAI_SERVICE; + pservice = &gaih_service; + } + else + pservice = NULL; + + if (pai) + end = &p; + else + end = NULL; + + while (g->gaih) + { + if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) + { + if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) + continue; + j++; + if (pg == NULL || pg->gaih != g->gaih) + { + pg = g; + i = g->gaih (name, pservice, hints, end); + if (i != 0) + { + last_i = i; + + if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC)) + continue; + + if (p) + freeaddrinfo (p); + + return -(i & GAIH_EAI); + } + if (end) + while(*end) end = &((*end)->ai_next); + } + } + ++g; + } + + if (j == 0) + return EAI_FAMILY; + + if (p) + { + *pai = p; + return 0; + } + + if (pai == NULL && last_i == 0) + return 0; + + if (p) + freeaddrinfo (p); + + return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME; +} + +void +freeaddrinfo (struct addrinfo *ai) +{ + struct addrinfo *p; + + while (ai != NULL) + { + p = ai; + ai = ai->ai_next; + free (p); + } +} diff --git a/libc/inet/getservice.c b/libc/inet/getservice.c index 5ba4d4a92..0625a46e1 100644 --- a/libc/inet/getservice.c +++ b/libc/inet/getservice.c @@ -62,13 +62,13 @@ #include #include #include +#include #define MAXALIASES 35 static FILE *servf = NULL; -static char line[BUFSIZ+1]; static struct servent serv; -static char *serv_aliases[MAXALIASES]; +static char buf[BUFSIZ+1 + sizeof(char *)*MAXALIASES]; static int serv_stayopen; void setservent(int f) @@ -90,22 +90,67 @@ void endservent(void) } struct servent * getservent(void) +{ + struct servent *result; + getservent_r(&serv, buf, sizeof(buf), &result); + return result; +} + + +struct servent *getservbyname(const char *name, const char *proto) +{ + struct servent *result; + getservbyname_r(name, proto, &serv, buf, sizeof(buf), &result); + return result; +} + + +struct servent * getservbyport(int port, const char *proto) +{ + struct servent *result; + getservbyport_r(port, proto, &serv, buf, sizeof(buf), &result); + return result; +} + +int getservent_r(struct servent * result_buf, + char * buf, size_t buflen, + struct servent ** result) { char *p; register char *cp, **q; + char **serv_aliases; + char *line; + + *result=NULL; + + if (buflen < sizeof(*serv_aliases)*MAXALIASES) { + errno=ERANGE; + return errno; + } + serv_aliases=(char **)buf; + buf+=sizeof(*serv_aliases)*MAXALIASES; + buflen-=sizeof(*serv_aliases)*MAXALIASES; + + if (buflen < BUFSIZ+1) { + errno=ERANGE; + return errno; + } + line=buf; + buf+=BUFSIZ+1; + buflen-=BUFSIZ+1; if (servf == NULL && (servf = fopen(_PATH_SERVICES, "r" )) == NULL) - return (NULL); + return errno; again: if ((p = fgets(line, BUFSIZ, servf)) == NULL) - return (NULL); + return TRY_AGAIN; if (*p == '#') goto again; cp = strpbrk(p, "#\n"); if (cp == NULL) goto again; *cp = '\0'; - serv.s_name = p; + result_buf->s_name = p; p = strpbrk(p, " \t"); if (p == NULL) goto again; @@ -116,9 +161,9 @@ again: if (cp == NULL) goto again; *cp++ = '\0'; - serv.s_port = htons((u_short)atoi(p)); - serv.s_proto = cp; - q = serv.s_aliases = serv_aliases; + result_buf->s_port = htons((u_short)atoi(p)); + result_buf->s_proto = cp; + q = result_buf->s_aliases = serv_aliases; cp = strpbrk(cp, " \t"); if (cp != NULL) *cp++ = '\0'; @@ -134,45 +179,50 @@ again: *cp++ = '\0'; } *q = NULL; - return (&serv); + *result=result_buf; + return 0; } - -struct servent *getservbyname(const char *name, const char *proto) +int getservbyname_r(const char *name, const char *proto, + struct servent * result_buf, + char * buf, size_t buflen, + struct servent ** result) { - register struct servent *p; register char **cp; + int ret; setservent(serv_stayopen); - while ((p = getservent()) != NULL) { - if (strcmp(name, p->s_name) == 0) + while (!(ret=getservent_r(result_buf, buf, buflen, result))) { + if (strcmp(name, result_buf->s_name) == 0) goto gotname; - for (cp = p->s_aliases; *cp; cp++) + for (cp = result_buf->s_aliases; *cp; cp++) if (strcmp(name, *cp) == 0) goto gotname; continue; gotname: - if (proto == 0 || strcmp(p->s_proto, proto) == 0) + if (proto == 0 || strcmp(result_buf->s_proto, proto) == 0) break; } if (!serv_stayopen) endservent(); - return (p); + return *result?0:ret; } - -struct servent * getservbyport(int port, const char *proto) +int getservbyport_r(int port, const char *proto, + struct servent * result_buf, + char * buf, size_t buflen, + struct servent ** result) { - register struct servent *p; + int ret; setservent(serv_stayopen); - while ((p = getservent()) != NULL) { - if (p->s_port != port) + while (!(ret=getservent_r(result_buf, buf, buflen, result))) { + if (result_buf->s_port != port) continue; - if (proto == 0 || strcmp(p->s_proto, proto) == 0) + if (proto == 0 || strcmp(result_buf->s_proto, proto) == 0) break; } if (!serv_stayopen) endservent(); - return (p); + return *result?0:ret; } diff --git a/libc/inet/if_nametoindex.c b/libc/inet/if_nametoindex.c new file mode 100644 index 000000000..40818aad9 --- /dev/null +++ b/libc/inet/if_nametoindex.c @@ -0,0 +1,40 @@ +/* + * + * Copyright (C) 1998 Kenneth Albanowski , + * The Silver Hammer Group, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + */ +#define __FORCE_GLIBC +#include +#include +#include +#include + +unsigned int if_nametoindex(const char* blub) { + struct ifreq ifr; + int fd; + char *tmp; + int len=sizeof(ifr.ifr_name); + +#ifdef __UCLIBC_HAS_IPV6__ + fd=socket(AF_INET6,SOCK_DGRAM,0); + if (fd<0) +#endif /* __UCLIBC_HAS_IPV6__ */ + fd=socket(AF_INET,SOCK_DGRAM,0); + + for (tmp=ifr.ifr_name; len>0; --len) { + if ((*tmp++ = *blub++)==0) break; + } + + if (ioctl(fd,SIOCGIFINDEX,&ifr)==0) { + close(fd); + return ifr.ifr_ifindex; + } + close(fd); + return 0; +} diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index cbf7b177b..ff2e918b7 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -118,10 +118,23 @@ extern int nameservers; extern char * nameserver[MAX_SERVERS]; extern int searchdomains; extern char * searchdomain[MAX_SEARCH]; -extern struct hostent * get_hosts_byname(const char * name, int type); -extern struct hostent * get_hosts_byaddr(const char * addr, int len, int type); +extern int get_hosts_byname_r(const char * name, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop); +extern int get_hosts_byaddr_r(const char * addr, int len, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop); extern void __open_etc_hosts(FILE **fp); -extern struct hostent * read_etc_hosts(FILE *fp, const char * name, int type, enum etc_hosts_action action); +extern int read_etc_hosts_r(FILE *fp, const char * name, int type, + enum etc_hosts_action action, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop); extern int resolve_address(const char * address, int nscount, char ** nsip, struct in_addr * in); extern int resolve_mailbox(const char * address, int nscount, @@ -982,75 +995,14 @@ const char *resolve_name(const char *name, int mailbox) struct hostent *gethostbyname(const char *name) { static struct hostent h; - static char namebuf[256]; - static struct in_addr in; - static struct in_addr *addr_list[2]; + static char buf[sizeof(struct in_addr) + + sizeof(struct in_addr *)*2 + + 256/*namebuffer*/ + 32/* margin */]; struct hostent *hp; - unsigned char *packet; - struct resolv_answer a; - int i; - int nest = 0; - open_nameservers(); - - if (!name) - return 0; - - if ((hp = get_hosts_byname(name, AF_INET))) /* do /etc/hosts first */ - return(hp); - - memset(&h, 0, sizeof(h)); - - addr_list[0] = ∈ - addr_list[1] = 0; - - strncpy(namebuf, name, sizeof(namebuf)); - - /* First check if this is already an address */ - if (inet_aton(name, &in)) { - h.h_name = namebuf; - h.h_addrtype = AF_INET; - h.h_length = sizeof(in); - h.h_addr_list = (char **) addr_list; - return &h; - } - - for (;;) { - - i = dns_lookup(namebuf, 1, nameservers, nameserver, &packet, &a); - - if (i < 0) - return 0; - - strncpy(namebuf, a.dotted, sizeof(namebuf)); - free(a.dotted); + gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); - - if (a.atype == T_CNAME) { /* CNAME */ - DPRINTF("Got a CNAME in gethostbyname()\n"); - i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); - free(packet); - - if (i < 0) - return 0; - if (++nest > MAX_RECURSE) - return 0; - continue; - } else if (a.atype == T_A) { /* ADDRESS */ - memcpy(&in, a.rdata, sizeof(in)); - h.h_name = namebuf; - h.h_addrtype = AF_INET; - h.h_length = sizeof(in); - h.h_addr_list = (char **) addr_list; - free(packet); - break; - } else { - free(packet); - return 0; - } - } - - return &h; + return hp; } #endif @@ -1070,81 +1022,14 @@ struct hostent *gethostbyname2(const char *name, int family) return family == AF_INET ? gethostbyname(name) : (struct hostent*)0; #else /* __UCLIBC_HAS_IPV6__ */ static struct hostent h; - static char namebuf[256]; - static struct in6_addr in; - static struct in6_addr *addr_list[2]; + static char buf[sizeof(struct in6_addr) + + sizeof(struct in6_addr *)*2 + + 256/*namebuffer*/ + 32/* margin */]; struct hostent *hp; - unsigned char *packet; - struct resolv_answer a; - int i; - int nest = 0; - - if (family == AF_INET) - return gethostbyname(name); - - if (family != AF_INET6) - return NULL; - - open_nameservers(); - if (!name) - return 0; - - if ((hp = get_hosts_byname(name, family))) /* do /etc/hosts first */ - return(hp); + gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); - memset(&h, 0, sizeof(h)); - - addr_list[0] = ∈ - addr_list[1] = 0; - - strncpy(namebuf, name, sizeof(namebuf)); - - /* First check if this is already an address */ - if (inet_pton(AF_INET6, name, &in)) { - h.h_name = namebuf; - h.h_addrtype = AF_INET6; - h.h_length = sizeof(in); - h.h_addr_list = (char **) addr_list; - return &h; - } - - for (;;) { - - i = dns_lookup(namebuf, T_AAAA, nameservers, nameserver, &packet, &a); - - if (i < 0) - return 0; - - strncpy(namebuf, a.dotted, sizeof(namebuf)); - free(a.dotted); - - - if (a.atype == T_CNAME) { /* CNAME */ - DPRINTF("Got a CNAME in gethostbyname()\n"); - i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); - free(packet); - - if (i < 0) - return 0; - if (++nest > MAX_RECURSE) - return 0; - continue; - } else if (a.atype == T_AAAA) { /* ADDRESS */ - memcpy(&in, a.rdata, sizeof(in)); - h.h_name = namebuf; - h.h_addrtype = AF_INET6; - h.h_length = sizeof(in); - h.h_addr_list = (char **) addr_list; - free(packet); - break; - } else { - free(packet); - return 0; - } - } - - return &h; + return hp; #endif /* __UCLIBC_HAS_IPV6__ */ } #endif @@ -1212,120 +1097,23 @@ int res_query(const char *dname, int class, int type, struct hostent *gethostbyaddr (const void *addr, socklen_t len, int type) { static struct hostent h; - static char namebuf[256]; - static struct in_addr in; - static struct in_addr *addr_list[2]; -#ifdef __UCLIBC_HAS_IPV6__ - char *qp; - static struct in6_addr in6; - static struct in6_addr *addr_list6[2]; + static char buf[ +#ifndef __UCLIBC_HAS_IPV6__ + sizeof(struct in_addr) + sizeof(struct in_addr *)*2 + +#else + sizeof(struct in6_addr) + sizeof(struct in6_addr *)*2 + #endif /* __UCLIBC_HAS_IPV6__ */ + 256/*namebuffer*/ + 32/* margin */]; struct hostent *hp; - unsigned char *packet; - struct resolv_answer a; - int i; - int nest = 0; - if (!addr) - return 0; + gethostbyaddr_r(addr, len, type, &h, buf, sizeof(buf), &hp, &h_errno); - switch (type) { - case AF_INET: - if (len != sizeof(struct in_addr)) - return 0; - break; -#ifdef __UCLIBC_HAS_IPV6__ - case AF_INET6: - if (len != sizeof(struct in6_addr)) - return 0; - break; -#endif /* __UCLIBC_HAS_IPV6__ */ - default: - return 0; - } - - if ((hp = get_hosts_byaddr(addr, len, type))) /* do /etc/hosts first */ - return(hp); - - open_nameservers(); - - memset(&h, 0, sizeof(h)); - - if(type == AF_INET) { - unsigned char *tmp_addr = (unsigned char *)addr; - - memcpy(&in.s_addr, addr, len); - - addr_list[0] = ∈ - - sprintf(namebuf, "%u.%u.%u.%u.in-addr.arpa", - tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); -#ifdef __UCLIBC_HAS_IPV6__ - } else { - memcpy(&in6.s6_addr, addr, len); - - addr_list6[0] = &in6; - qp = namebuf; - - for (i = len - 1; i >= 0; i--) { - qp += sprintf(qp, "%x.%x.", in6.s6_addr[i] & 0xf, - (in6.s6_addr[i] >> 4) & 0xf); - } - strcpy(qp, "ip6.int"); -#endif /* __UCLIBC_HAS_IPV6__ */ - } - - addr_list[1] = 0; - - for (;;) { - - i = dns_lookup(namebuf, T_PTR, nameservers, nameserver, &packet, &a); - - if (i < 0) - return 0; - - strncpy(namebuf, a.dotted, sizeof(namebuf)); - free(a.dotted); - - if (a.atype == T_CNAME) { /* CNAME */ - DPRINTF("Got a CNAME in gethostbyaddr()\n"); - i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); - free(packet); - - if (i < 0) - return 0; - if (++nest > MAX_RECURSE) - return 0; - continue; - } else if (a.atype == T_PTR) { /* ADDRESS */ - i = decode_dotted(packet, a.rdoffset, namebuf, sizeof(namebuf)); - free(packet); - - h.h_name = namebuf; - h.h_addrtype = type; - - if(type == AF_INET) { - h.h_length = sizeof(in); -#ifdef __UCLIBC_HAS_IPV6__ - } else { - h.h_length = sizeof(in6); -#endif /* __UCLIBC_HAS_IPV6__ */ - } - - h.h_addr_list = (char **) addr_list; - break; - } else { - free(packet); - return 0; - } - } - - return &h; + return hp; } #endif -#ifdef L_read_etc_hosts +#ifdef L_read_etc_hosts_r void __open_etc_hosts(FILE **fp) { @@ -1335,34 +1123,79 @@ void __open_etc_hosts(FILE **fp) return; } -struct hostent * read_etc_hosts(FILE * fp, const char * name, int type, enum etc_hosts_action action) +int read_etc_hosts_r(FILE * fp, const char * name, int type, + enum etc_hosts_action action, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) { - static struct hostent h; - static struct in_addr in; - static struct in_addr *addr_list[2]; + struct in_addr *in=NULL; + struct in_addr **addr_list=NULL; #ifdef __UCLIBC_HAS_IPV6__ - static struct in6_addr in6; - static struct in6_addr *addr_list6[2]; + struct in6_addr *in6=NULL; + struct in6_addr **addr_list6=NULL; #endif /* __UCLIBC_HAS_IPV6__ */ - static char line[80]; char *cp; #define MAX_ALIAS 5 char *alias[MAX_ALIAS]; int aliases, i; + int ret=HOST_NOT_FOUND; if (action!=GETHOSTENT) { +#ifdef __UCLIBC_HAS_IPV6__ + char *p=buf; + size_t len=buflen; +#endif /* __UCLIBC_HAS_IPV6__ */ + *h_errnop=NETDB_INTERNAL; + if (buflen < sizeof(*in)) + return ERANGE; + in=(struct in_addr*)buf; + buf+=sizeof(*in); + buflen-=sizeof(*in); + + if (buflen < sizeof(*addr_list)*2) + return ERANGE; + addr_list=(struct in_addr **)buf; + buf+=sizeof(*addr_list)*2; + buflen-=sizeof(*addr_list)*2; + +#ifdef __UCLIBC_HAS_IPV6__ + if (len < sizeof(*in6)) + return ERANGE; + in6=(struct in6_addr*)p; + p+=sizeof(*in6); + len-=sizeof(*in6); + + if (len < sizeof(*addr_list6)*2) + return ERANGE; + addr_list6=(struct in6_addr**)p; + p+=sizeof(*addr_list6)*2; + len-=sizeof(*addr_list6)*2; + + if (len < buflen) { + buflen=len; + buf=p; + } +#endif /* __UCLIBC_HAS_IPV6__ */ + if (buflen < 80) + return ERANGE; + __open_etc_hosts(&fp); if (fp == NULL) { - return((struct hostent *)NULL); + result=NULL; + return errno; } } - while (fgets(line, sizeof(line), fp)) { - if ((cp = strchr(line, '#'))) + *h_errnop=HOST_NOT_FOUND; + while (fgets(buf, buflen, fp)) { + if ((cp = strchr(buf, '#'))) *cp = '\0'; + DPRINTF("Looking at: %s\n", buf); aliases = 0; - cp = line; + cp = buf; while (*cp) { while (*cp && isspace(*cp)) *cp++ = '\0'; @@ -1392,35 +1225,43 @@ struct hostent * read_etc_hosts(FILE * fp, const char * name, int type, enum etc continue; } - if (type == AF_INET && inet_pton(AF_INET, alias[0], &in) > 0) { - addr_list[0] = ∈ + if (type == AF_INET && inet_pton(AF_INET, alias[0], in) > 0) { + DPRINTF("Found INET\n"); + addr_list[0] = in; addr_list[1] = 0; - h.h_name = alias[1]; - h.h_addrtype = AF_INET; - h.h_length = sizeof(in); - h.h_addr_list = (char**) addr_list; + result_buf->h_name = alias[1]; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char**) addr_list; + *result=result_buf; + ret=NETDB_SUCCESS; #ifdef __UCLIBC_HAS_IPV6__ - } else if (type == AF_INET6 && inet_pton(AF_INET6, alias[0], &in6) > 0) { - addr_list6[0] = &in6; + } else if (type == AF_INET6 && inet_pton(AF_INET6, alias[0], in6) > 0) { + DPRINTF("Found INET6\n"); + addr_list6[0] = in6; addr_list6[1] = 0; - h.h_name = alias[1]; - h.h_addrtype = AF_INET6; - h.h_length = sizeof(in6); - h.h_addr_list = (char**) addr_list6; + result_buf->h_name = alias[1]; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in6); + result_buf->h_addr_list = (char**) addr_list6; + *result=result_buf; + ret=NETDB_SUCCESS; #endif /* __UCLIBC_HAS_IPV6__ */ } else { + DPRINTF("Error\n"); + ret=TRY_AGAIN; break; /* bad ip address */ } if (action!=GETHOSTENT) { fclose(fp); } - return(&h); + return ret; } if (action!=GETHOSTENT) { fclose(fp); } - return((struct hostent *) NULL); + return ret; } #endif @@ -1451,6 +1292,14 @@ FILE * __gethostent_fp; struct hostent *gethostent (void) { + static struct hostent h; + static char buf[ +#ifndef __UCLIBC_HAS_IPV6__ + sizeof(struct in_addr) + sizeof(struct in_addr *)*2 + +#else + sizeof(struct in6_addr) + sizeof(struct in6_addr *)*2 + +#endif /* __UCLIBC_HAS_IPV6__ */ + 80/*namebuffer*/ + 2/* margin */]; struct hostent *host; if (__gethostent_fp == NULL) { @@ -1460,7 +1309,8 @@ struct hostent *gethostent (void) } } - host = read_etc_hosts(__gethostent_fp, NULL, AF_INET, GETHOSTENT); + read_etc_hosts_r(__gethostent_fp, NULL, AF_INET, GETHOSTENT, + &h, buf, sizeof(buf), &host, &h_errno); if (__stay_open==0) { fclose(__gethostent_fp); } @@ -1468,18 +1318,25 @@ struct hostent *gethostent (void) } #endif -#ifdef L_get_hosts_byname +#ifdef L_get_hosts_byname_r -struct hostent * get_hosts_byname(const char * name, int type) +int get_hosts_byname_r(const char * name, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) { - return(read_etc_hosts(NULL, name, type, GET_HOSTS_BYNAME)); + return(read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME, result_buf, buf, buflen, result, h_errnop)); } #endif +#ifdef L_get_hosts_byaddr_r -#ifdef L_get_hosts_byaddr - -struct hostent * get_hosts_byaddr(const char * addr, int len, int type) +int get_hosts_byaddr_r(const char * addr, int len, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) { #ifndef __UCLIBC_HAS_IPV6__ char ipaddr[INET_ADDRSTRLEN]; @@ -1504,7 +1361,7 @@ struct hostent * get_hosts_byaddr(const char * addr, int len, int type) inet_ntop(type, addr, ipaddr, sizeof(ipaddr)); - return(read_etc_hosts(NULL, ipaddr, type, GET_HOSTS_BYADDR)); + return(read_etc_hosts_r(NULL, ipaddr, type, GET_HOSTS_BYADDR, result_buf, buf, buflen, result, h_errnop)); } #endif @@ -1700,3 +1557,424 @@ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host, return 0; } #endif + + +#ifdef L_gethostbyname_r + +int gethostbyname_r(const char * name, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) +{ + struct in_addr *in; + struct in_addr **addr_list; + unsigned char *packet; + struct resolv_answer a; + int i; + int nest = 0; + + open_nameservers(); + + *result=NULL; + if (!name) + return EINVAL; + + /* do /etc/hosts first */ + if ((i=get_hosts_byname_r(name, AF_INET, result_buf, + buf, buflen, result, h_errnop))==0) + return i; + switch (*h_errnop) { + case HOST_NOT_FOUND: + case NO_ADDRESS: + break; + default: + return i; + } + + DPRINTF("Nothing found in /etc/hosts\n"); + + *h_errnop = NETDB_INTERNAL; + if (buflen < sizeof(*in)) + return ERANGE; + in=(struct in_addr*)buf; + buf+=sizeof(*in); + buflen-=sizeof(*in); + + if (buflen < sizeof(*addr_list)*2) + return ERANGE; + addr_list=(struct in_addr**)buf; + buf+=sizeof(*addr_list)*2; + buflen-=sizeof(*addr_list)*2; + + addr_list[0] = in; + addr_list[1] = 0; + + if (buflen<256) + return ERANGE; + strncpy(buf, name, buflen); + + /* First check if this is already an address */ + if (inet_aton(name, in)) { + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; + } + + for (;;) { + + i = dns_lookup(buf, T_A, nameservers, nameserver, &packet, &a); + + if (i < 0) { + *h_errnop = HOST_NOT_FOUND; + DPRINTF("dns_lookup\n"); + return TRY_AGAIN; + } + + strncpy(buf, a.dotted, buflen); + free(a.dotted); + + if (a.atype == T_CNAME) { /* CNAME */ + DPRINTF("Got a CNAME in gethostbyname()\n"); + i = decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); + + if (i < 0) { + *h_errnop = NO_RECOVERY; + DPRINTF("decode_dotted\n"); + return -1; + } + if (++nest > MAX_RECURSE) { + *h_errnop = NO_RECOVERY; + DPRINTF("recursion\n"); + return -1; + } + continue; + } else if (a.atype == T_A) { /* ADDRESS */ + memcpy(in, a.rdata, sizeof(*in)); + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + free(packet); + break; + } else { + free(packet); + *h_errnop=HOST_NOT_FOUND; + return TRY_AGAIN; + } + } + + *result=result_buf; + return NETDB_SUCCESS; +} +#endif + +#ifdef L_gethostbyname2_r + +#ifdef __UCLIBC_HAS_IPV6__ +/* TBD: Not the right place for defining these, I guess */ +/* +const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +*/ +#endif /* __UCLIBC_HAS_IPV6__ */ + +int gethostbyname2_r(const char *name, int family, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) +{ +#ifndef __UCLIBC_HAS_IPV6__ + return family == AF_INET ? gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop) : HOST_NOT_FOUND; +#else /* __UCLIBC_HAS_IPV6__ */ + struct in6_addr *in; + struct in6_addr **addr_list; + unsigned char *packet; + struct resolv_answer a; + int i; + int nest = 0; + + if (family == AF_INET) + return gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop); + + if (family != AF_INET6) + return EINVAL; + + open_nameservers(); + + *result=NULL; + if (!name) + return EINVAL; + + /* do /etc/hosts first */ + if ((i=get_hosts_byname_r(name, family, result_buf, + buf, buflen, result, h_errnop))==0) + return i; + switch (*h_errnop) { + case HOST_NOT_FOUND: + case NO_ADDRESS: + break; + default: + return i; + } + + DPRINTF("Nothing found in /etc/hosts\n"); + + *h_errnop = NETDB_INTERNAL; + if (buflen < sizeof(*in)) + return ERANGE; + in=(struct in6_addr*)buf; + buf+=sizeof(*in); + buflen-=sizeof(*in); + + if (buflen < sizeof(*addr_list)*2) + return ERANGE; + addr_list=(struct in6_addr**)buf; + buf+=sizeof(*addr_list)*2; + buflen-=sizeof(*addr_list)*2; + + addr_list[0] = in; + addr_list[1] = 0; + + if (buflen<256) + return ERANGE; + strncpy(buf, name, buflen); + + /* First check if this is already an address */ + if (inet_pton(AF_INET6, name, in)) { + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; + } + + for (;;) { + + i = dns_lookup(buf, T_AAAA, nameservers, nameserver, &packet, &a); + + if (i < 0) { + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; + } + + strncpy(buf, a.dotted, buflen); + free(a.dotted); + + if (a.atype == T_CNAME) { /* CNAME */ + DPRINTF("Got a CNAME in gethostbyname()\n"); + i = decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); + + if (i < 0) { + *h_errnop = NO_RECOVERY; + return -1; + } + if (++nest > MAX_RECURSE) { + *h_errnop = NO_RECOVERY; + return -1; + } + continue; + } else if (a.atype == T_AAAA) { /* ADDRESS */ + memcpy(in, a.rdata, sizeof(*in)); + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + free(packet); + break; + } else { + free(packet); + *h_errnop=HOST_NOT_FOUND; + return TRY_AGAIN; + } + } + + *result=result_buf; + return NETDB_SUCCESS; +#endif /* __UCLIBC_HAS_IPV6__ */ +} +#endif + +#ifdef L_gethostbyaddr_r +int gethostbyaddr_r (const void *addr, socklen_t len, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) + +{ + struct in_addr *in; + struct in_addr **addr_list; +#ifdef __UCLIBC_HAS_IPV6__ + char *qp; + size_t plen; + struct in6_addr *in6; + struct in6_addr **addr_list6; +#endif /* __UCLIBC_HAS_IPV6__ */ + unsigned char *packet; + struct resolv_answer a; + int i; + int nest = 0; + + *result=NULL; + if (!addr) + return EINVAL; + + switch (type) { + case AF_INET: + if (len != sizeof(struct in_addr)) + return EINVAL; + break; +#ifdef __UCLIBC_HAS_IPV6__ + case AF_INET6: + if (len != sizeof(struct in6_addr)) + return EINVAL; + break; +#endif /* __UCLIBC_HAS_IPV6__ */ + default: + return EINVAL; + } + + /* do /etc/hosts first */ + if ((i=get_hosts_byaddr_r(addr, len, type, result_buf, + buf, buflen, result, h_errnop))==0) + return i; + switch (*h_errnop) { + case HOST_NOT_FOUND: + case NO_ADDRESS: + break; + default: + return i; + } + + open_nameservers(); + +#ifdef __UCLIBC_HAS_IPV6__ + qp=buf; + plen=buflen; +#endif /* __UCLIBC_HAS_IPV6__ */ + + *h_errnop = NETDB_INTERNAL; + if (buflen < sizeof(*in)) + return ERANGE; + in=(struct in_addr*)buf; + buf+=sizeof(*in); + buflen-=sizeof(*in); + + if (buflen < sizeof(*addr_list)*2) + return ERANGE; + addr_list=(struct in_addr**)buf; + buf+=sizeof(*addr_list)*2; + buflen-=sizeof(*addr_list)*2; + +#ifdef __UCLIBC_HAS_IPV6__ + if (plen < sizeof(*in6)) + return ERANGE; + in6=(struct in6_addr*)qp; + qp+=sizeof(*in6); + plen-=sizeof(*in6); + + if (plen < sizeof(*addr_list6)*2) + return ERANGE; + addr_list6=(struct in6_addr**)qp; + qp+=sizeof(*addr_list6)*2; + plen-=sizeof(*addr_list6)*2; + + if (len < buflen) { + buflen=len; + buf=qp; + } +#endif /* __UCLIBC_HAS_IPV6__ */ + + if (buflen<256) + return ERANGE; + + if(type == AF_INET) { + unsigned char *tmp_addr = (unsigned char *)addr; + + memcpy(&in->s_addr, addr, len); + + addr_list[0] = in; + + sprintf(buf, "%u.%u.%u.%u.in-addr.arpa", + tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); +#ifdef __UCLIBC_HAS_IPV6__ + } else { + memcpy(in6->s6_addr, addr, len); + + addr_list6[0] = in6; + qp = buf; + + for (i = len - 1; i >= 0; i--) { + qp += sprintf(qp, "%x.%x.", in6->s6_addr[i] & 0xf, + (in6->s6_addr[i] >> 4) & 0xf); + } + strcpy(qp, "ip6.int"); +#endif /* __UCLIBC_HAS_IPV6__ */ + } + + addr_list[1] = 0; + + for (;;) { + + i = dns_lookup(buf, T_PTR, nameservers, nameserver, &packet, &a); + + if (i < 0) { + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; + } + + strncpy(buf, a.dotted, buflen); + free(a.dotted); + + if (a.atype == T_CNAME) { /* CNAME */ + DPRINTF("Got a CNAME in gethostbyaddr()\n"); + i = decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); + + if (i < 0) { + *h_errnop = NO_RECOVERY; + return -1; + } + if (++nest > MAX_RECURSE) { + *h_errnop = NO_RECOVERY; + return -1; + } + continue; + } else if (a.atype == T_PTR) { /* ADDRESS */ + i = decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); + + result_buf->h_name = buf; + result_buf->h_addrtype = type; + + if(type == AF_INET) { + result_buf->h_length = sizeof(*in); +#ifdef __UCLIBC_HAS_IPV6__ + } else { + result_buf->h_length = sizeof(*in6); +#endif /* __UCLIBC_HAS_IPV6__ */ + } + + result_buf->h_addr_list = (char **) addr_list; + break; + } else { + free(packet); + *h_errnop = NO_ADDRESS; + return TRY_AGAIN; + } + } + + *result=result_buf; + return NETDB_SUCCESS; +} +#endif -- cgit v1.2.3