aboutsummaryrefslogtreecommitdiff
path: root/nss
diff options
context:
space:
mode:
authorArjun Shankar <arjun@redhat.com>2023-10-02 14:55:27 +0200
committerArjun Shankar <arjun@redhat.com>2023-10-24 12:31:00 +0200
commit7f602256ab5b85db1dbfb5f40bd109c4b37b68c8 (patch)
tree88cf9fb3bc04eae6ae9ca3816fac75d78f691740 /nss
parent1d74d2f042a405982661267394e16126db70dc5f (diff)
downloadglibc-7f602256ab5b85db1dbfb5f40bd109c4b37b68c8.tar.xz
glibc-7f602256ab5b85db1dbfb5f40bd109c4b37b68c8.zip
Move getaddrinfo from 'posix' into 'nss'
getaddrinfo is an entry point for nss functionality. This commit moves it from 'sysdeps/posix' to 'nss', gets rid of the stub in 'posix', and moves all associated tests as well. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'nss')
-rw-r--r--nss/Makefile19
-rw-r--r--nss/Versions1
-rw-r--r--nss/getaddrinfo.c2625
-rw-r--r--nss/tst-getaddrinfo.c68
-rw-r--r--nss/tst-getaddrinfo2.c78
-rw-r--r--nss/tst-getaddrinfo3.c151
-rw-r--r--nss/tst-getaddrinfo4.c67
-rw-r--r--nss/tst-getaddrinfo5.c69
-rw-r--r--nss/tst-rfc3484-2.c189
-rw-r--r--nss/tst-rfc3484-3.c161
-rw-r--r--nss/tst-rfc3484.c153
11 files changed, 3579 insertions, 2 deletions
diff --git a/nss/Makefile b/nss/Makefile
index e88754bf51..da5bd207aa 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -127,6 +127,7 @@ endif
# hosts routines:
routines += \
+ getaddrinfo \
gethstbyad \
gethstbyad_r \
gethstbynm \
@@ -138,6 +139,7 @@ routines += \
# routines
ifeq ($(have-thread-library),yes)
+CFLAGS-getaddrinfo.c += -fexceptions
CFLAGS-gethstbyad.c += -fexceptions
CFLAGS-gethstbyad_r.c += -fexceptions
CFLAGS-gethstbynm.c += -fexceptions
@@ -300,7 +302,13 @@ others-extras = $(makedb-modules)
extra-objs += $(makedb-modules:=.o)
tests-static = tst-field
-tests-internal = tst-field
+
+tests-internal := \
+ tst-field \
+ tst-rfc3484 \
+ tst-rfc3484-2 \
+ tst-rfc3484-3 \
+ # tests-internal
tests := \
bug17079 \
@@ -309,6 +317,9 @@ tests := \
test-rpcent \
testgrp \
tst-fgetsgent_r \
+ tst-getaddrinfo \
+ tst-getaddrinfo2 \
+ tst-getaddrinfo3 \
tst-gethnm \
tst-getpw \
tst-gshadow \
@@ -327,7 +338,11 @@ tests := \
tst-shadow \
# tests
-xtests = bug-erange
+xtests := \
+ bug-erange \
+ tst-getaddrinfo4 \
+ tst-getaddrinfo5 \
+ # xtests
tests-container := \
tst-initgroups1 \
diff --git a/nss/Versions b/nss/Versions
index d8c4e373c9..158a9175a4 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -19,6 +19,7 @@ libc {
fgetspent; fgetspent_r;
# g*
+ getaddrinfo;
getaliasbyname; getaliasbyname_r; getaliasent; getaliasent_r;
getgrent; getgrent_r; getgrgid; getgrgid_r; getgrnam; getgrnam_r;
getgroups;
diff --git a/nss/getaddrinfo.c b/nss/getaddrinfo.c
new file mode 100644
index 0000000000..531124958d
--- /dev/null
+++ b/nss/getaddrinfo.c
@@ -0,0 +1,2625 @@
+/* Host and service name lookups using Name Service Switch modules.
+ Copyright (C) 1996-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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, see
+ <https://www.gnu.org/licenses/>. */
+
+/* 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. [The copyright holder has authorized the removal of this clause.]
+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. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <nss.h>
+#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <nsswitch.h>
+#include <libc-lock.h>
+#include <not-cancel.h>
+#include <nscd/nscd-client.h>
+#include <nscd/nscd_proto.h>
+#include <scratch_buffer.h>
+#include <inet/net-internal.h>
+
+/* Former AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES
+ flags, now ignored. */
+#define DEPRECATED_AI_IDN 0x300
+
+#if IS_IN (libc)
+# define feof_unlocked(fp) __feof_unlocked (fp)
+#endif
+
+struct gaih_service
+ {
+ const char *name;
+ int num;
+ };
+
+struct gaih_servtuple
+ {
+ int socktype;
+ int protocol;
+ int port;
+ bool set;
+ };
+
+
+struct gaih_typeproto
+ {
+ int socktype;
+ int protocol;
+ uint8_t protoflag;
+ bool defaultflag;
+ char name[8];
+ };
+
+struct gaih_result
+{
+ struct gaih_addrtuple *at;
+ char *canon;
+ char *h_name;
+ bool free_at;
+ bool got_ipv6;
+};
+
+/* Values for `protoflag'. */
+#define GAI_PROTO_NOSERVICE 1
+#define GAI_PROTO_PROTOANY 2
+
+static const struct gaih_typeproto gaih_inet_typeproto[] =
+{
+ { 0, 0, 0, false, "" },
+ { SOCK_STREAM, IPPROTO_TCP, 0, true, "tcp" },
+ { SOCK_DGRAM, IPPROTO_UDP, 0, true, "udp" },
+#if defined SOCK_DCCP && defined IPPROTO_DCCP
+ { SOCK_DCCP, IPPROTO_DCCP, 0, false, "dccp" },
+#endif
+#ifdef IPPROTO_UDPLITE
+ { SOCK_DGRAM, IPPROTO_UDPLITE, 0, false, "udplite" },
+#endif
+#ifdef IPPROTO_SCTP
+ { SOCK_STREAM, IPPROTO_SCTP, 0, false, "sctp" },
+ { SOCK_SEQPACKET, IPPROTO_SCTP, 0, false, "sctp" },
+#endif
+ { SOCK_RAW, 0, GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, true, "raw" },
+ { 0, 0, 0, false, "" }
+};
+
+static const struct addrinfo default_hints =
+ {
+ .ai_flags = AI_DEFAULT,
+ .ai_family = PF_UNSPEC,
+ .ai_socktype = 0,
+ .ai_protocol = 0,
+ .ai_addrlen = 0,
+ .ai_addr = NULL,
+ .ai_canonname = NULL,
+ .ai_next = NULL
+ };
+
+static void
+gaih_result_reset (struct gaih_result *res)
+{
+ if (res->free_at)
+ free (res->at);
+ free (res->canon);
+ free (res->h_name);
+ memset (res, 0, sizeof (*res));
+}
+
+static int
+gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
+ const struct addrinfo *req, struct gaih_servtuple *st,
+ struct scratch_buffer *tmpbuf)
+{
+ struct servent *s;
+ struct servent ts;
+ int r;
+
+ do
+ {
+ r = __getservbyname_r (servicename, tp->name, &ts,
+ tmpbuf->data, tmpbuf->length, &s);
+ if (r != 0 || s == NULL)
+ {
+ if (r == ERANGE)
+ {
+ if (!scratch_buffer_grow (tmpbuf))
+ return -EAI_MEMORY;
+ }
+ else
+ return -EAI_SERVICE;
+ }
+ }
+ while (r);
+
+ st->socktype = tp->socktype;
+ st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+ ? req->ai_protocol : tp->protocol);
+ st->port = s->s_port;
+ st->set = true;
+
+ return 0;
+}
+
+/* Convert struct hostent to a list of struct gaih_addrtuple objects. The new
+ addresses are appended to the tuple array in RES. */
+static bool
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family,
+ struct hostent *h, struct gaih_result *res)
+{
+ /* Count the number of addresses in h->h_addr_list. */
+ size_t count = 0;
+ for (char **p = h->h_addr_list; *p != NULL; ++p)
+ ++count;
+
+ /* Report no data if no addresses are available, or if the incoming
+ address size is larger than what we can store. */
+ if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
+ return true;
+
+ struct gaih_addrtuple *array = res->at;
+ size_t old = 0;
+
+ while (array != NULL)
+ {
+ old++;
+ array = array->next;
+ }
+
+ array = realloc (res->at, (old + count) * sizeof (*array));
+
+ if (array == NULL)
+ return false;
+
+ res->got_ipv6 = family == AF_INET6;
+ res->at = array;
+ res->free_at = true;
+
+ /* Duplicate h_name because it may get reclaimed when the underlying storage
+ is freed. */
+ if (res->h_name == NULL)
+ {
+ res->h_name = __strdup (h->h_name);
+ if (res->h_name == NULL)
+ return false;
+ }
+
+ /* Update the next pointers on reallocation. */
+ for (size_t i = 0; i < old; i++)
+ array[i].next = array + i + 1;
+
+ array += old;
+
+ memset (array, 0, count * sizeof (*array));
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ if (family == AF_INET && req->ai_family == AF_INET6)
+ {
+ /* Perform address mapping. */
+ array[i].family = AF_INET6;
+ memcpy(array[i].addr + 3, h->h_addr_list[i], sizeof (uint32_t));
+ array[i].addr[2] = htonl (0xffff);
+ }
+ else
+ {
+ array[i].family = family;
+ memcpy (array[i].addr, h->h_addr_list[i], h->h_length);
+ }
+ array[i].next = array + i + 1;
+ }
+ array[count - 1].next = NULL;
+
+ return true;
+}
+
+static int
+gethosts (nss_gethostbyname3_r fct, int family, const char *name,
+ const struct addrinfo *req, struct scratch_buffer *tmpbuf,
+ struct gaih_result *res, enum nss_status *statusp, int *no_datap)
+{
+ struct hostent th;
+ char *localcanon = NULL;
+ enum nss_status status;
+
+ *no_datap = 0;
+ while (1)
+ {
+ *statusp = status = DL_CALL_FCT (fct, (name, family, &th,
+ tmpbuf->data, tmpbuf->length,
+ &errno, &h_errno, NULL,
+ &localcanon));
+ if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL
+ || errno != ERANGE)
+ break;
+ if (!scratch_buffer_grow (tmpbuf))
+ return -EAI_MEMORY;
+ }
+ if (status == NSS_STATUS_NOTFOUND
+ || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+ {
+ if (h_errno == NETDB_INTERNAL)
+ return -EAI_SYSTEM;
+ if (h_errno == TRY_AGAIN)
+ *no_datap = EAI_AGAIN;
+ else
+ *no_datap = h_errno == NO_DATA;
+ }
+ else if (status == NSS_STATUS_SUCCESS)
+ {
+ if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res))
+ return -EAI_MEMORY;
+
+ if (localcanon != NULL && res->canon == NULL)
+ {
+ char *canonbuf = __strdup (localcanon);
+ if (canonbuf == NULL)
+ return -EAI_MEMORY;
+ res->canon = canonbuf;
+ }
+ }
+
+ return 0;
+}
+
+/* This function is called if a canonical name is requested, but if
+ the service function did not provide it. It tries to obtain the
+ name using getcanonname_r from the same service NIP. If the name
+ cannot be canonicalized, return a copy of NAME. Return NULL on
+ memory allocation failure. The returned string is allocated on the
+ heap; the caller has to free it. */
+static char *
+getcanonname (nss_action_list nip, const char *hname, const char *name)
+{
+ nss_getcanonname_r *cfct = __nss_lookup_function (nip, "getcanonname_r");
+ char *s = (char *) name;
+ if (cfct != NULL)
+ {
+ char buf[256];
+ if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno,
+ &h_errno)) != NSS_STATUS_SUCCESS)
+ /* If the canonical name cannot be determined, use the passed
+ string. */
+ s = (char *) name;
+ }
+ return __strdup (s);
+}
+
+/* Process looked up canonical name and if necessary, decode to IDNA. Result
+ is a new string written to CANONP and the earlier string is freed. */
+
+static int
+process_canonname (const struct addrinfo *req, const char *orig_name,
+ struct gaih_result *res)
+{
+ char *canon = res->canon;
+
+ if ((req->ai_flags & AI_CANONNAME) != 0)
+ {
+ bool do_idn = req->ai_flags & AI_CANONIDN;
+ if (do_idn)
+ {
+ char *out;
+ int rc = __idna_from_dns_encoding (canon ?: orig_name, &out);
+ if (rc == 0)
+ {
+ free (canon);
+ canon = out;
+ }
+ else if (rc == EAI_IDN_ENCODE)
+ /* Use the punycode name as a fallback. */
+ do_idn = false;
+ else
+ return -rc;
+ }
+ if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL)
+ return -EAI_MEMORY;
+ }
+
+ res->canon = canon;
+ return 0;
+}
+
+static int
+get_servtuples (const struct gaih_service *service, const struct addrinfo *req,
+ struct gaih_servtuple *st, struct scratch_buffer *tmpbuf)
+{
+ int i;
+ const struct gaih_typeproto *tp = gaih_inet_typeproto;
+
+ 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 -EAI_SOCKTYPE;
+ else
+ return -EAI_SERVICE;
+ }
+ }
+
+ if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
+ return -EAI_SERVICE;
+
+ if (service == NULL || service->num >= 0)
+ {
+ int port = service != NULL ? htons (service->num) : 0;
+
+ if (req->ai_socktype || req->ai_protocol)
+ {
+ st[0].socktype = tp->socktype;
+ st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
+ ? req->ai_protocol : tp->protocol);
+ st[0].port = port;
+ st[0].set = true;
+
+ return 0;
+ }
+
+ /* Neither socket type nor protocol is set. Return all socket types
+ we know about. */
+ for (i = 0, ++tp; tp->name[0]; ++tp)
+ if (tp->defaultflag)
+ {
+ st[i].socktype = tp->socktype;
+ st[i].protocol = tp->protocol;
+ st[i].port = port;
+ st[i++].set = true;
+ }
+
+ return 0;
+ }
+
+ if (tp->name[0])
+ return gaih_inet_serv (service->name, tp, req, st, tmpbuf);
+
+ for (i = 0, tp++; tp->name[0]; tp++)
+ {
+ 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;
+
+ if (gaih_inet_serv (service->name,
+ tp, req, &st[i], tmpbuf) != 0)
+ continue;
+
+ i++;
+ }
+
+ if (!st[0].set)
+ return -EAI_SERVICE;
+
+ return 0;
+}
+
+#ifdef USE_NSCD
+/* Query addresses from nscd cache, returning a non-zero value on error.
+ RES members have the lookup result; RES->AT is NULL if there were no errors
+ but also no results. */
+
+static int
+get_nscd_addresses (const char *name, const struct addrinfo *req,
+ struct gaih_result *res)
+{
+ if (__nss_not_use_nscd_hosts > 0
+ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY)
+ __nss_not_use_nscd_hosts = 0;
+
+ res->at = NULL;
+
+ if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts])
+ return 0;
+
+ /* Try to use nscd. */
+ struct nscd_ai_result *air = NULL;
+ int err = __nscd_getai (name, &air, &h_errno);
+
+ if (__glibc_unlikely (air == NULL))
+ {
+ /* The database contains a negative entry. */
+ if (err == 0)
+ return -EAI_NONAME;
+ if (__nss_not_use_nscd_hosts == 0)
+ {
+ if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
+ return -EAI_MEMORY;
+ if (h_errno == TRY_AGAIN)
+ return -EAI_AGAIN;
+ return -EAI_SYSTEM;
+ }
+ return 0;
+ }
+
+ /* Transform into gaih_addrtuple list. */
+ int result = 0;
+ char *addrs = air->addrs;
+
+ struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree));
+ struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at));
+ if (at == NULL)
+ {
+ result = -EAI_MEMORY;
+ goto out;
+ }
+
+ res->free_at = true;
+
+ int count = 0;
+ for (int i = 0; i < air->naddrs; ++i)
+ {
+ socklen_t size = (air->family[i] == AF_INET
+ ? INADDRSZ : IN6ADDRSZ);
+
+ if (!((air->family[i] == AF_INET
+ && req->ai_family == AF_INET6
+ && (req->ai_flags & AI_V4MAPPED) != 0)
+ || req->ai_family == AF_UNSPEC
+ || air->family[i] == req->ai_family))
+ {
+ /* Skip over non-matching result. */
+ addrs += size;
+ continue;
+ }
+
+ if (air->family[i] == AF_INET && req->ai_family == AF_INET6
+ && (req->ai_flags & AI_V4MAPPED))
+ {
+ at[count].family = AF_INET6;
+ at[count].addr[3] = *(uint32_t *) addrs;
+ at[count].addr[2] = htonl (0xffff);
+ }
+ else if (req->ai_family == AF_UNSPEC
+ || air->family[i] == req->ai_family)
+ {
+ at[count].family = air->family[i];
+ memcpy (at[count].addr, addrs, size);
+ if (air->family[i] == AF_INET6)
+ res->got_ipv6 = true;
+ }
+ at[count].next = at + count + 1;
+ count++;
+ addrs += size;
+ }
+
+ if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL)
+ {
+ char *canonbuf = __strdup (air->canon);
+ if (canonbuf == NULL)
+ {
+ result = -EAI_MEMORY;
+ goto out;
+ }
+ res->canon = canonbuf;
+ }
+
+ if (count == 0)
+ {
+ result = -EAI_NONAME;
+ goto out;
+ }
+
+ at[count - 1].next = NULL;
+
+ res->at = at;
+
+out:
+ free (air);
+ if (result != 0)
+ {
+ free (at);
+ res->free_at = false;
+ }
+
+ return result;
+}
+#endif
+
+static int
+get_nss_addresses (const char *name, const struct addrinfo *req,
+ struct scratch_buffer *tmpbuf, struct gaih_result *res)
+{
+ int no_data = 0;
+ int no_inet6_data = 0;
+ nss_action_list nip;
+ enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
+ enum nss_status status = NSS_STATUS_UNAVAIL;
+ int no_more;
+ struct resolv_context *res_ctx = NULL;
+ bool do_merge = false;
+ int result = 0;
+
+ no_more = !__nss_database_get (nss_database_hosts, &nip);
+
+ /* 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, so we use the no_inet6
+ function variant. */
+ res_ctx = __resolv_context_get ();
+ if (r