diff options
Diffstat (limited to 'REORG.TODO/inet/getnameinfo.c')
-rw-r--r-- | REORG.TODO/inet/getnameinfo.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/REORG.TODO/inet/getnameinfo.c b/REORG.TODO/inet/getnameinfo.c new file mode 100644 index 0000000000..5d2b9b7559 --- /dev/null +++ b/REORG.TODO/inet/getnameinfo.c @@ -0,0 +1,563 @@ +/* Convert socket address to string using Name Service Switch modules. + Copyright (C) 1997-2017 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 + <http://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> + +#ifdef HAVE_LIBIDN +# include <libidn/idna.h> +extern int __idna_to_unicode_lzlz (const char *input, char **output, + int flags); +#endif + +#ifndef min +# define min(x,y) (((x) > (y)) ? (y) : (x)) +#endif /* min */ + +libc_freeres_ptr (static char *domain); + + +static char * +internal_function +nrl_domainname (void) +{ + static int not_first; + + if (! not_first) + { + __libc_lock_define_initialized (static, lock); + __libc_lock_lock (lock); + + if (! not_first) + { + char *c; + struct hostent *h, th; + int herror; + struct scratch_buffer tmpbuf; + + scratch_buffer_init (&tmpbuf); + not_first = 1; + + while (__gethostbyname_r ("localhost", &th, + tmpbuf.data, tmpbuf.length, + &h, &herror)) + { + if (herror == NETDB_INTERNAL && errno == ERANGE) + { + if (!scratch_buffer_grow (&tmpbuf)) + goto done; + } + else + break; + } + + if (h && (c = strchr (h->h_name, '.'))) + domain = __strdup (++c); + else + { + /* 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)) + goto done; + + if ((c = strchr (tmpbuf.data, '.'))) + domain = __strdup (++c); + else + { + /* We need to preserve the hostname. */ + const char *hstname = strdupa (tmpbuf.data); + + while (__gethostbyname_r (hstname, &th, + tmpbuf.data, tmpbuf.length, + &h, &herror)) + { + if (herror == NETDB_INTERNAL && errno == ERANGE) + { + if (!scratch_buffer_grow (&tmpbuf)) + goto done; + } + else + break; + } + + if (h && (c = strchr(h->h_name, '.'))) + domain = __strdup (++c); + else + { + struct in_addr 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)) + goto done; + } + else + break; + } + + if (h && (c = strchr (h->h_name, '.'))) + domain = __strdup (++c); + } + } + } + done: + scratch_buffer_free (&tmpbuf); + } + + __libc_lock_unlock (lock); + } + + return domain; +}; + +/* 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) + { + char *c; + if ((flags & NI_NOFQDN) + && (c = nrl_domainname ()) + && (c = strstr (h->h_name, c)) + && (c != h->h_name) && (*(--c) == '.')) + /* Terminate the string after the prefix. */ + *c = '\0'; + +#ifdef HAVE_LIBIDN + /* If requested, convert from the IDN format. */ + if (flags & NI_IDN) + { + int idn_flags = 0; + if (flags & NI_IDN_ALLOW_UNASSIGNED) + idn_flags |= IDNA_ALLOW_UNASSIGNED; + if (flags & NI_IDN_USE_STD3_ASCII_RULES) + idn_flags |= IDNA_USE_STD3_ASCII_RULES; + + char *out; + int rc = __idna_to_unicode_lzlz (h->h_name, &out, + idn_flags); + if (rc != IDNA_SUCCESS) + { + if (rc == IDNA_MALLOC_ERROR) + return EAI_MEMORY; + if (rc == IDNA_DLOPEN_ERROR) + return EAI_SYSTEM; + return EAI_IDN_ENCODE; + } + + if (out != h->h_name) + { + h->h_name = strdupa (out); + free (out); + } + } +#endif + + size_t len = strlen (h->h_name) + 1; + if (len > hostlen) + return EAI_OVERFLOW; + + memcpy (host, h->h_name, len); + + 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 +#ifdef HAVE_LIBIDN + |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES +#endif + )) + 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) |