| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /* Convert socket address to string using Name Service Switch modules.
- Copyright (C) 1997-2026 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 <errno.h>
- #include <netdb.h>
- #include <stddef.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdint.h>
- #include <arpa/inet.h>
- #include <net/if.h>
- #include <netinet/in.h>
- #include <sys/param.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <sys/un.h>
- #include <sys/utsname.h>
- #include <libc-lock.h>
- #include <scratch_buffer.h>
- #include <inet/net-internal.h>
- #include <set-freeres.h>
- #ifndef min
- # define min(x,y) (((x) > (y)) ? (y) : (x))
- #endif /* min */
- static char *domain;
- /* Former NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES flags,
- now ignored. */
- #define DEPRECATED_NI_IDN 192
- /* Return true if no memory allocation failure happened (even if domain
- name could not be obtained) or false otherwise. */
- static bool
- nrl_domainname_core (struct scratch_buffer *tmpbuf)
- {
- char *c;
- struct hostent *h, th;
- int herror;
- while (__gethostbyname_r ("localhost", &th, tmpbuf->data, tmpbuf->length,
- &h, &herror))
- {
- if (herror == NETDB_INTERNAL && errno == ERANGE)
- {
- if (!scratch_buffer_grow (tmpbuf))
- return false;
- }
- else
- break;
- }
- if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
- {
- domain = __strdup (++c);
- return domain != NULL;
- }
- /* The name contains no domain information. Use the name
- now to get more information. */
- while (__gethostname (tmpbuf->data, tmpbuf->length))
- if (!scratch_buffer_grow (tmpbuf))
- return false;
- if ((c = strchr (tmpbuf->data, '.')) != NULL)
- {
- domain = __strdup (++c);
- return domain != NULL;
- }
- /* We need to preserve the hostname. */
- size_t hstnamelen = strlen (tmpbuf->data) + 1;
- while (__gethostbyname_r (tmpbuf->data, &th, tmpbuf->data + hstnamelen,
- tmpbuf->length - hstnamelen, &h, &herror))
- {
- if (herror == NETDB_INTERNAL && errno == ERANGE)
- {
- if (!scratch_buffer_grow_preserve (tmpbuf))
- return false;
- }
- else
- break;
- }
- if (h != NULL && (c = strchr(h->h_name, '.')) != NULL)
- {
- domain = __strdup (++c);
- return domain != NULL;
- }
- struct in_addr in_addr = { .s_addr = htonl (INADDR_LOOPBACK) };
- while (__gethostbyaddr_r ((const char *) &in_addr, sizeof (struct in_addr),
- AF_INET, &th, tmpbuf->data, tmpbuf->length, &h,
- &herror))
- {
- if (herror == NETDB_INTERNAL && errno == ERANGE)
- {
- if (!scratch_buffer_grow (tmpbuf))
- return false;
- }
- else
- break;
- }
- if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
- {
- domain = __strdup (++c);
- return domain != NULL;
- }
- return true;
- }
- static bool
- nrl_domainname (void)
- {
- static int not_first;
- if (__glibc_likely (atomic_load_acquire (¬_first) != 0))
- return true;
- int r = true;
- __libc_lock_define_initialized (static, lock);
- __libc_lock_lock (lock);
- if (atomic_load_relaxed (¬_first) == 0)
- {
- struct scratch_buffer tmpbuf;
- scratch_buffer_init (&tmpbuf);
- if ((r = nrl_domainname_core (&tmpbuf)))
- atomic_store_release (¬_first, 1);
- scratch_buffer_free (&tmpbuf);
- }
- __libc_lock_unlock (lock);
- return r;
- };
- /* Copy a string to a destination buffer with length checking. Return
- EAI_OVERFLOW if the buffer is not large enough, and 0 on
- success. */
- static int
- checked_copy (char *dest, size_t destlen, const char *source)
- {
- size_t source_length = strlen (source);
- if (source_length + 1 > destlen)
- return EAI_OVERFLOW;
- memcpy (dest, source, source_length + 1);
- return 0;
- }
- /* Helper function for CHECKED_SNPRINTF below. */
- static int
- check_sprintf_result (int result, size_t destlen)
- {
- if (result < 0)
- return EAI_SYSTEM;
- if ((size_t) result >= destlen)
- /* If ret == destlen, there was no room for the terminating NUL
- character. */
- return EAI_OVERFLOW;
- return 0;
- }
- /* Format a string in the destination buffer. Return 0 on success,
- EAI_OVERFLOW in case the buffer is too small, or EAI_SYSTEM on any
- other error. */
- #define CHECKED_SNPRINTF(dest, destlen, format, ...) \
- check_sprintf_result \
- (__snprintf (dest, destlen, format, __VA_ARGS__), destlen)
- /* Convert host name, AF_INET/AF_INET6 case, name only. */
- static int
- gni_host_inet_name (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *host, socklen_t hostlen, int flags)
- {
- int herrno;
- struct hostent th;
- struct hostent *h = NULL;
- if (sa->sa_family == AF_INET6)
- {
- const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
- while (__gethostbyaddr_r (&sin6p->sin6_addr, sizeof(struct in6_addr),
- AF_INET6, &th, tmpbuf->data, tmpbuf->length,
- &h, &herrno))
- if (herrno == NETDB_INTERNAL && errno == ERANGE)
- {
- if (!scratch_buffer_grow (tmpbuf))
- {
- __set_h_errno (herrno);
- return EAI_MEMORY;
- }
- }
- else
- break;
- }
- else
- {
- const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
- while (__gethostbyaddr_r (&sinp->sin_addr, sizeof(struct in_addr),
- AF_INET, &th, tmpbuf->data, tmpbuf->length,
- &h, &herrno))
- if (herrno == NETDB_INTERNAL && errno == ERANGE)
- {
- if (!scratch_buffer_grow (tmpbuf))
- {
- __set_h_errno (herrno);
- return EAI_MEMORY;
- }
- }
- else
- break;
- }
- if (h == NULL)
- {
- if (herrno == NETDB_INTERNAL)
- {
- __set_h_errno (herrno);
- return EAI_SYSTEM;
- }
- if (herrno == TRY_AGAIN)
- {
- __set_h_errno (herrno);
- return EAI_AGAIN;
- }
- }
- if (h)
- {
- if (flags & NI_NOFQDN)
- {
- if (!nrl_domainname ())
- return EAI_MEMORY;
- char *c = domain;
- if (c != NULL && (c = strstr (h->h_name, c))
- && (c != h->h_name) && (*(--c) == '.'))
- /* Terminate the string after the prefix. */
- *c = '\0';
- }
- /* If requested, convert from the IDN format. */
- bool do_idn = flags & NI_IDN;
- char *h_name;
- if (do_idn)
- {
- int rc = __idna_from_dns_encoding (h->h_name, &h_name);
- if (rc == EAI_IDN_ENCODE)
- /* Use the punycode name as a fallback. */
- do_idn = false;
- else if (rc != 0)
- return rc;
- }
- if (!do_idn)
- h_name = h->h_name;
- size_t len = strlen (h_name) + 1;
- if (len > hostlen)
- return EAI_OVERFLOW;
- memcpy (host, h_name, len);
- if (do_idn)
- free (h_name);
- return 0;
- }
- return EAI_NONAME;
- }
- /* Convert host name, AF_INET/AF_INET6 case, numeric conversion. */
- static int
- gni_host_inet_numeric (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *host, socklen_t hostlen, int flags)
- {
- if (sa->sa_family == AF_INET6)
- {
- const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
- if (__inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL)
- return EAI_OVERFLOW;
- uint32_t scopeid = sin6p->sin6_scope_id;
- if (scopeid != 0)
- {
- size_t used_hostlen = __strnlen (host, hostlen);
- /* Location of the scope string in the host buffer. */
- char *scope_start = host + used_hostlen;
- size_t scope_length = hostlen - used_hostlen;
- if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
- || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
- {
- char scopebuf[IFNAMSIZ];
- if (if_indextoname (scopeid, scopebuf) != NULL)
- return CHECKED_SNPRINTF
- (scope_start, scope_length,
- "%c%s", SCOPE_DELIMITER, scopebuf);
- }
- return CHECKED_SNPRINTF
- (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
- }
- }
- else
- {
- const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
- if (__inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL)
- return EAI_OVERFLOW;
- }
- return 0;
- }
- /* Convert AF_INET or AF_INET6 socket address, host part. */
- static int
- gni_host_inet (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *host, socklen_t hostlen, int flags)
- {
- if (!(flags & NI_NUMERICHOST))
- {
- int result = gni_host_inet_name
- (tmpbuf, sa, addrlen, host, hostlen, flags);
- if (result != EAI_NONAME)
- return result;
- }
- if (flags & NI_NAMEREQD)
- return EAI_NONAME;
- else
- return gni_host_inet_numeric
- (tmpbuf, sa, addrlen, host, hostlen, flags);
- }
- /* Convert AF_LOCAL socket address, host part. */
- static int
- gni_host_local (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *host, socklen_t hostlen, int flags)
- {
- if (!(flags & NI_NUMERICHOST))
- {
- struct utsname utsname;
- if (uname (&utsname) == 0)
- return checked_copy (host, hostlen, utsname.nodename);
- }
- if (flags & NI_NAMEREQD)
- return EAI_NONAME;
- return checked_copy (host, hostlen, "localhost");
- }
- /* Convert the host part of an AF_LOCAK socket address. */
- static int
- gni_host (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *host, socklen_t hostlen, int flags)
- {
- switch (sa->sa_family)
- {
- case AF_INET:
- case AF_INET6:
- return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
- case AF_LOCAL:
- return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
- default:
- return EAI_FAMILY;
- }
- }
- /* Convert service to string, AF_INET and AF_INET6 variant. */
- static int
- gni_serv_inet (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *serv, socklen_t servlen, int flags)
- {
- _Static_assert
- (offsetof (struct sockaddr_in, sin_port)
- == offsetof (struct sockaddr_in6, sin6_port)
- && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t)
- && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t),
- "AF_INET and AF_INET6 port consistency");
- const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
- if (!(flags & NI_NUMERICSERV))
- {
- struct servent *s, ts;
- int e;
- while ((e = __getservbyport_r (sinp->sin_port,
- ((flags & NI_DGRAM)
- ? "udp" : "tcp"), &ts,
- tmpbuf->data, tmpbuf->length, &s)))
- {
- if (e == ERANGE)
- {
- if (!scratch_buffer_grow (tmpbuf))
- return EAI_MEMORY;
- }
- else
- break;
- }
- if (s)
- return checked_copy (serv, servlen, s->s_name);
- /* Fall through to numeric conversion. */
- }
- return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
- }
- /* Convert service to string, AF_LOCAL variant. */
- static int
- gni_serv_local (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *serv, socklen_t servlen, int flags)
- {
- return checked_copy
- (serv, servlen, ((const struct sockaddr_un *) sa)->sun_path);
- }
- /* Convert service to string, dispatching to the implementations
- above. */
- static int
- gni_serv (struct scratch_buffer *tmpbuf,
- const struct sockaddr *sa, socklen_t addrlen,
- char *serv, socklen_t servlen, int flags)
- {
- switch (sa->sa_family)
- {
- case AF_INET:
- case AF_INET6:
- return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags);
- case AF_LOCAL:
- return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags);
- default:
- return EAI_FAMILY;
- }
- }
- int
- getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
- socklen_t hostlen, char *serv, socklen_t servlen,
- int flags)
- {
- if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
- |NI_IDN|DEPRECATED_NI_IDN))
- return EAI_BADFLAGS;
- if (sa == NULL || addrlen < sizeof (sa_family_t))
- return EAI_FAMILY;
- if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
- return EAI_NONAME;
- switch (sa->sa_family)
- {
- case AF_LOCAL:
- if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
- return EAI_FAMILY;
- break;
- case AF_INET:
- if (addrlen < sizeof (struct sockaddr_in))
- return EAI_FAMILY;
- break;
- case AF_INET6:
- if (addrlen < sizeof (struct sockaddr_in6))
- return EAI_FAMILY;
- break;
- default:
- return EAI_FAMILY;
- }
- struct scratch_buffer tmpbuf;
- scratch_buffer_init (&tmpbuf);
- if (host != NULL && hostlen > 0)
- {
- int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags);
- if (result != 0)
- {
- scratch_buffer_free (&tmpbuf);
- return result;
- }
- }
- if (serv && (servlen > 0))
- {
- int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags);
- if (result != 0)
- {
- scratch_buffer_free (&tmpbuf);
- return result;
- }
- }
- scratch_buffer_free (&tmpbuf);
- return 0;
- }
- libc_hidden_def (getnameinfo)
- weak_alias (domain, __libc_getnameinfo_freemem_ptr)
|