(root)/
glibc-2.38/
inet/
getnameinfo.c
       1  /* Convert socket address to string using Name Service Switch modules.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* The Inner Net License, Version 2.00
      20  
      21    The author(s) grant permission for redistribution and use in source and
      22  binary forms, with or without modification, of the software and documentation
      23  provided that the following conditions are met:
      24  
      25  0. If you receive a version of the software that is specifically labelled
      26     as not being for redistribution (check the version message and/or README),
      27     you are not permitted to redistribute that version of the software in any
      28     way or form.
      29  1. All terms of the all other applicable copyrights and licenses must be
      30     followed.
      31  2. Redistributions of source code must retain the authors' copyright
      32     notice(s), this list of conditions, and the following disclaimer.
      33  3. Redistributions in binary form must reproduce the authors' copyright
      34     notice(s), this list of conditions, and the following disclaimer in the
      35     documentation and/or other materials provided with the distribution.
      36  4. [The copyright holder has authorized the removal of this clause.]
      37  5. Neither the name(s) of the author(s) nor the names of its contributors
      38     may be used to endorse or promote products derived from this software
      39     without specific prior written permission.
      40  
      41  THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
      42  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
      43  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      44  DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
      45  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      46  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      47  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
      48  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      49  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      50  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      51  
      52    If these license terms cause you a real problem, contact the author.  */
      53  
      54  /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
      55  
      56  #include <errno.h>
      57  #include <netdb.h>
      58  #include <stddef.h>
      59  #include <stdlib.h>
      60  #include <stdio.h>
      61  #include <string.h>
      62  #include <unistd.h>
      63  #include <stdint.h>
      64  #include <arpa/inet.h>
      65  #include <net/if.h>
      66  #include <netinet/in.h>
      67  #include <sys/param.h>
      68  #include <sys/socket.h>
      69  #include <sys/types.h>
      70  #include <sys/un.h>
      71  #include <sys/utsname.h>
      72  #include <libc-lock.h>
      73  #include <scratch_buffer.h>
      74  #include <net-internal.h>
      75  #include <set-freeres.h>
      76  
      77  #ifndef min
      78  # define min(x,y) (((x) > (y)) ? (y) : (x))
      79  #endif /* min */
      80  
      81  static char *domain;
      82  
      83  /* Former NI_IDN_ALLOW_UNASSIGNED, NI_IDN_USE_STD3_ASCII_RULES flags,
      84     now ignored.  */
      85  #define DEPRECATED_NI_IDN 192
      86  
      87  /* Return true if no memory allocation failure happened (even if domain
      88     name could not be obtained) or false otherwise.  */
      89  static bool
      90  nrl_domainname_core (struct scratch_buffer *tmpbuf)
      91  {
      92    char *c;
      93    struct hostent *h, th;
      94    int herror;
      95  
      96    while (__gethostbyname_r ("localhost", &th, tmpbuf->data, tmpbuf->length,
      97  			    &h, &herror))
      98      {
      99        if (herror == NETDB_INTERNAL && errno == ERANGE)
     100  	{
     101  	  if (!scratch_buffer_grow (tmpbuf))
     102  	    return false;
     103  	}
     104        else
     105  	break;
     106      }
     107  
     108    if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
     109      {
     110        domain = __strdup (++c);
     111        return domain != NULL;
     112      }
     113  
     114    /* The name contains no domain information.  Use the name
     115       now to get more information.  */
     116    while (__gethostname (tmpbuf->data, tmpbuf->length))
     117      if (!scratch_buffer_grow (tmpbuf))
     118        return false;
     119  
     120    if ((c = strchr (tmpbuf->data, '.')) != NULL)
     121      {
     122        domain = __strdup (++c);
     123        return domain != NULL;
     124      }
     125  
     126    /* We need to preserve the hostname.  */
     127    size_t hstnamelen = strlen (tmpbuf->data) + 1;
     128    while (__gethostbyname_r (tmpbuf->data, &th, tmpbuf->data + hstnamelen,
     129  			    tmpbuf->length - hstnamelen, &h, &herror))
     130      {
     131        if (herror == NETDB_INTERNAL && errno == ERANGE)
     132  	{
     133  	  if (!scratch_buffer_grow_preserve (tmpbuf))
     134  	    return false;
     135  	}
     136        else
     137  	break;
     138      }
     139  
     140    if (h != NULL && (c = strchr(h->h_name, '.')) != NULL)
     141      {
     142        domain = __strdup (++c);
     143        return domain != NULL;
     144      }
     145  
     146    struct in_addr in_addr = { .s_addr = htonl (INADDR_LOOPBACK) };
     147  
     148    while (__gethostbyaddr_r ((const char *) &in_addr, sizeof (struct in_addr),
     149  			    AF_INET, &th, tmpbuf->data, tmpbuf->length, &h,
     150  			    &herror))
     151      {
     152        if (herror == NETDB_INTERNAL && errno == ERANGE)
     153  	{
     154  	  if (!scratch_buffer_grow (tmpbuf))
     155  	    return false;
     156  	}
     157        else
     158  	break;
     159      }
     160  
     161    if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
     162      {
     163        domain = __strdup (++c);
     164        return domain != NULL;
     165      }
     166    return true;
     167  }
     168  
     169  static bool
     170  nrl_domainname (void)
     171  {
     172    static int not_first;
     173  
     174    if (__glibc_likely (atomic_load_acquire (&not_first) != 0))
     175      return true;
     176  
     177    int r = true;
     178  
     179    __libc_lock_define_initialized (static, lock);
     180    __libc_lock_lock (lock);
     181  
     182    if (atomic_load_relaxed (&not_first) == 0)
     183      {
     184        struct scratch_buffer tmpbuf;
     185        scratch_buffer_init (&tmpbuf);
     186  
     187        if ((r = nrl_domainname_core (&tmpbuf)))
     188  	atomic_store_release (&not_first, 1);
     189  
     190        scratch_buffer_free (&tmpbuf);
     191      }
     192  
     193    __libc_lock_unlock (lock);
     194  
     195    return r;
     196  };
     197  
     198  /* Copy a string to a destination buffer with length checking.  Return
     199     EAI_OVERFLOW if the buffer is not large enough, and 0 on
     200     success.  */
     201  static int
     202  checked_copy (char *dest, size_t destlen, const char *source)
     203  {
     204    size_t source_length = strlen (source);
     205    if (source_length + 1 > destlen)
     206      return EAI_OVERFLOW;
     207    memcpy (dest, source, source_length + 1);
     208    return 0;
     209  }
     210  
     211  /* Helper function for CHECKED_SNPRINTF below.  */
     212  static int
     213  check_sprintf_result (int result, size_t destlen)
     214  {
     215    if (result < 0)
     216      return EAI_SYSTEM;
     217    if ((size_t) result >= destlen)
     218      /* If ret == destlen, there was no room for the terminating NUL
     219         character.  */
     220      return EAI_OVERFLOW;
     221    return 0;
     222  }
     223  
     224  /* Format a string in the destination buffer.  Return 0 on success,
     225     EAI_OVERFLOW in case the buffer is too small, or EAI_SYSTEM on any
     226     other error.  */
     227  #define CHECKED_SNPRINTF(dest, destlen, format, ...)			\
     228    check_sprintf_result							\
     229      (__snprintf (dest, destlen, format, __VA_ARGS__), destlen)
     230  
     231  /* Convert host name, AF_INET/AF_INET6 case, name only.  */
     232  static int
     233  gni_host_inet_name (struct scratch_buffer *tmpbuf,
     234  		    const struct sockaddr *sa, socklen_t addrlen,
     235  		    char *host, socklen_t hostlen, int flags)
     236  {
     237    int herrno;
     238    struct hostent th;
     239    struct hostent *h = NULL;
     240    if (sa->sa_family == AF_INET6)
     241      {
     242        const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
     243        while (__gethostbyaddr_r (&sin6p->sin6_addr, sizeof(struct in6_addr),
     244  				AF_INET6, &th, tmpbuf->data, tmpbuf->length,
     245  				&h, &herrno))
     246  	if (herrno == NETDB_INTERNAL && errno == ERANGE)
     247  	  {
     248  	    if (!scratch_buffer_grow (tmpbuf))
     249  	      {
     250  		__set_h_errno (herrno);
     251  		return EAI_MEMORY;
     252  	      }
     253  	  }
     254  	else
     255  	  break;
     256      }
     257    else
     258      {
     259        const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
     260        while (__gethostbyaddr_r (&sinp->sin_addr, sizeof(struct in_addr),
     261  				AF_INET, &th, tmpbuf->data, tmpbuf->length,
     262  				&h, &herrno))
     263  	if (herrno == NETDB_INTERNAL && errno == ERANGE)
     264  	    {
     265  	      if (!scratch_buffer_grow (tmpbuf))
     266  		{
     267  		  __set_h_errno (herrno);
     268  		  return EAI_MEMORY;
     269  		}
     270  	    }
     271  	else
     272  	  break;
     273      }
     274  
     275    if (h == NULL)
     276      {
     277        if (herrno == NETDB_INTERNAL)
     278  	{
     279  	  __set_h_errno (herrno);
     280  	  return EAI_SYSTEM;
     281  	}
     282        if (herrno == TRY_AGAIN)
     283  	{
     284  	  __set_h_errno (herrno);
     285  	  return EAI_AGAIN;
     286  	}
     287      }
     288  
     289    if (h)
     290      {
     291        if (flags & NI_NOFQDN)
     292  	{
     293  	  if (!nrl_domainname ())
     294  	    return EAI_MEMORY;
     295  
     296  	  char *c = domain;
     297  	  if (c != NULL && (c = strstr (h->h_name, c))
     298  	       && (c != h->h_name) && (*(--c) == '.'))
     299  	    /* Terminate the string after the prefix.  */
     300  	    *c = '\0';
     301  	}
     302  
     303        /* If requested, convert from the IDN format.  */
     304        bool do_idn = flags & NI_IDN;
     305        char *h_name;
     306        if (do_idn)
     307  	{
     308  	  int rc = __idna_from_dns_encoding (h->h_name, &h_name);
     309  	  if (rc == EAI_IDN_ENCODE)
     310  	    /* Use the punycode name as a fallback.  */
     311  	    do_idn = false;
     312  	  else if (rc != 0)
     313  	    return rc;
     314  	}
     315        if (!do_idn)
     316  	h_name = h->h_name;
     317  
     318        size_t len = strlen (h_name) + 1;
     319        if (len > hostlen)
     320  	return EAI_OVERFLOW;
     321        memcpy (host, h_name, len);
     322  
     323        if (do_idn)
     324  	free (h_name);
     325  
     326        return 0;
     327      }
     328  
     329    return EAI_NONAME;
     330  }
     331  
     332  /* Convert host name, AF_INET/AF_INET6 case, numeric conversion.  */
     333  static int
     334  gni_host_inet_numeric (struct scratch_buffer *tmpbuf,
     335  		       const struct sockaddr *sa, socklen_t addrlen,
     336  		       char *host, socklen_t hostlen, int flags)
     337  {
     338    if (sa->sa_family == AF_INET6)
     339      {
     340        const struct sockaddr_in6 *sin6p = (const struct sockaddr_in6 *) sa;
     341        if (inet_ntop (AF_INET6, &sin6p->sin6_addr, host, hostlen) == NULL)
     342  	return EAI_OVERFLOW;
     343  
     344        uint32_t scopeid = sin6p->sin6_scope_id;
     345        if (scopeid != 0)
     346  	{
     347  	  size_t used_hostlen = __strnlen (host, hostlen);
     348  	  /* Location of the scope string in the host buffer.  */
     349  	  char *scope_start = host + used_hostlen;
     350  	  size_t scope_length = hostlen - used_hostlen;
     351  
     352  	  if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
     353  	      || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
     354  	    {
     355  	      char scopebuf[IFNAMSIZ];
     356  	      if (if_indextoname (scopeid, scopebuf) != NULL)
     357  		return CHECKED_SNPRINTF
     358  		  (scope_start, scope_length,
     359  		   "%c%s", SCOPE_DELIMITER, scopebuf);
     360  	    }
     361  	  return CHECKED_SNPRINTF
     362  	    (scope_start, scope_length, "%c%u", SCOPE_DELIMITER, scopeid);
     363  	}
     364      }
     365    else
     366      {
     367        const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
     368        if (inet_ntop (AF_INET, &sinp->sin_addr, host, hostlen) == NULL)
     369  	return EAI_OVERFLOW;
     370      }
     371    return 0;
     372  }
     373  
     374  /* Convert AF_INET or AF_INET6 socket address, host part.  */
     375  static int
     376  gni_host_inet (struct scratch_buffer *tmpbuf,
     377  	       const struct sockaddr *sa, socklen_t addrlen,
     378  	       char *host, socklen_t hostlen, int flags)
     379  {
     380    if (!(flags & NI_NUMERICHOST))
     381      {
     382        int result = gni_host_inet_name
     383  	(tmpbuf, sa, addrlen, host, hostlen, flags);
     384        if (result != EAI_NONAME)
     385  	return result;
     386      }
     387  
     388    if (flags & NI_NAMEREQD)
     389      return EAI_NONAME;
     390    else
     391      return gni_host_inet_numeric
     392        (tmpbuf, sa, addrlen, host, hostlen, flags);
     393  }
     394  
     395  /* Convert AF_LOCAL socket address, host part.   */
     396  static int
     397  gni_host_local (struct scratch_buffer *tmpbuf,
     398  		const struct sockaddr *sa, socklen_t addrlen,
     399  		char *host, socklen_t hostlen, int flags)
     400  {
     401    if (!(flags & NI_NUMERICHOST))
     402      {
     403        struct utsname utsname;
     404        if (uname (&utsname) == 0)
     405  	return checked_copy (host, hostlen, utsname.nodename);
     406      }
     407  
     408    if (flags & NI_NAMEREQD)
     409      return EAI_NONAME;
     410  
     411    return checked_copy (host, hostlen, "localhost");
     412  }
     413  
     414  /* Convert the host part of an AF_LOCAK socket address.   */
     415  static int
     416  gni_host (struct scratch_buffer *tmpbuf,
     417  	  const struct sockaddr *sa, socklen_t addrlen,
     418  	  char *host, socklen_t hostlen, int flags)
     419  {
     420    switch (sa->sa_family)
     421      {
     422      case AF_INET:
     423      case AF_INET6:
     424        return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags);
     425  
     426      case AF_LOCAL:
     427        return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags);
     428  
     429      default:
     430        return EAI_FAMILY;
     431      }
     432  }
     433  
     434  /* Convert service to string, AF_INET and AF_INET6 variant.  */
     435  static int
     436  gni_serv_inet (struct scratch_buffer *tmpbuf,
     437  	       const struct sockaddr *sa, socklen_t addrlen,
     438  	       char *serv, socklen_t servlen, int flags)
     439  {
     440    _Static_assert
     441      (offsetof (struct sockaddr_in, sin_port)
     442       == offsetof (struct sockaddr_in6, sin6_port)
     443       && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t)
     444       && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t),
     445       "AF_INET and AF_INET6 port consistency");
     446    const struct sockaddr_in *sinp = (const struct sockaddr_in *) sa;
     447    if (!(flags & NI_NUMERICSERV))
     448      {
     449        struct servent *s, ts;
     450        int e;
     451        while ((e = __getservbyport_r (sinp->sin_port,
     452  				     ((flags & NI_DGRAM)
     453  				      ? "udp" : "tcp"), &ts,
     454  				     tmpbuf->data, tmpbuf->length, &s)))
     455  	{
     456  	  if (e == ERANGE)
     457  	    {
     458  	      if (!scratch_buffer_grow (tmpbuf))
     459  		return EAI_MEMORY;
     460  	    }
     461  	  else
     462  	    break;
     463  	}
     464        if (s)
     465  	return checked_copy (serv, servlen, s->s_name);
     466        /* Fall through to numeric conversion.  */
     467      }
     468    return CHECKED_SNPRINTF (serv, servlen, "%d", ntohs (sinp->sin_port));
     469  }
     470  
     471  /* Convert service to string, AF_LOCAL variant.  */
     472  static int
     473  gni_serv_local (struct scratch_buffer *tmpbuf,
     474  	       const struct sockaddr *sa, socklen_t addrlen,
     475  	       char *serv, socklen_t servlen, int flags)
     476  {
     477    return checked_copy
     478      (serv, servlen, ((const struct sockaddr_un *) sa)->sun_path);
     479  }
     480  
     481  /* Convert service to string, dispatching to the implementations
     482     above.  */
     483  static int
     484  gni_serv (struct scratch_buffer *tmpbuf,
     485  	  const struct sockaddr *sa, socklen_t addrlen,
     486  	  char *serv, socklen_t servlen, int flags)
     487  {
     488    switch (sa->sa_family)
     489      {
     490      case AF_INET:
     491      case AF_INET6:
     492        return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags);
     493      case AF_LOCAL:
     494        return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags);
     495      default:
     496        return EAI_FAMILY;
     497      }
     498  }
     499  
     500  int
     501  getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
     502  	     socklen_t hostlen, char *serv, socklen_t servlen,
     503  	     int flags)
     504  {
     505    if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
     506  		|NI_IDN|DEPRECATED_NI_IDN))
     507      return EAI_BADFLAGS;
     508  
     509    if (sa == NULL || addrlen < sizeof (sa_family_t))
     510      return EAI_FAMILY;
     511  
     512    if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL)
     513      return EAI_NONAME;
     514  
     515    switch (sa->sa_family)
     516      {
     517      case AF_LOCAL:
     518        if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
     519  	return EAI_FAMILY;
     520        break;
     521      case AF_INET:
     522        if (addrlen < sizeof (struct sockaddr_in))
     523  	return EAI_FAMILY;
     524        break;
     525      case AF_INET6:
     526        if (addrlen < sizeof (struct sockaddr_in6))
     527  	return EAI_FAMILY;
     528        break;
     529      default:
     530        return EAI_FAMILY;
     531      }
     532  
     533    struct scratch_buffer tmpbuf;
     534    scratch_buffer_init (&tmpbuf);
     535  
     536    if (host != NULL && hostlen > 0)
     537      {
     538        int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags);
     539        if (result != 0)
     540  	{
     541  	  scratch_buffer_free (&tmpbuf);
     542  	  return result;
     543  	}
     544      }
     545  
     546    if (serv && (servlen > 0))
     547      {
     548        int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags);
     549        if (result != 0)
     550  	{
     551  	  scratch_buffer_free (&tmpbuf);
     552  	  return result;
     553  	}
     554      }
     555  
     556    scratch_buffer_free (&tmpbuf);
     557    return 0;
     558  }
     559  libc_hidden_def (getnameinfo)
     560  
     561  weak_alias (domain, __libc_getnameinfo_freemem_ptr)