(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
getaddrinfo.c
       1  /* Get address information (partial implementation).
       2     Copyright (C) 1997, 2001-2002, 2004-2023 Free Software Foundation, Inc.
       3     Contributed by Simon Josefsson <simon@josefsson.org>.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file 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
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
      19     optimizes away the sa == NULL test below.  */
      20  #define _GL_ARG_NONNULL(params)
      21  
      22  #include <config.h>
      23  
      24  #include <netdb.h>
      25  
      26  #if HAVE_NETINET_IN_H
      27  # include <netinet/in.h>
      28  #endif
      29  
      30  /* Get inet_ntop.  */
      31  #include <arpa/inet.h>
      32  
      33  /* Get calloc. */
      34  #include <stdlib.h>
      35  
      36  /* Get memcpy, strdup. */
      37  #include <string.h>
      38  
      39  /* Get snprintf. */
      40  #include <stdio.h>
      41  
      42  #include "gettext.h"
      43  #define _(String) gettext (String)
      44  #define N_(String) String
      45  
      46  /* BeOS has AF_INET, but not PF_INET.  */
      47  #ifndef PF_INET
      48  # define PF_INET AF_INET
      49  #endif
      50  /* BeOS also lacks PF_UNSPEC.  */
      51  #ifndef PF_UNSPEC
      52  # define PF_UNSPEC 0
      53  #endif
      54  
      55  #if HAVE_GETADDRINFO
      56  
      57  /* Override with cdecl calling convention.  */
      58  
      59  int
      60  getaddrinfo (const char *restrict nodename,
      61               const char *restrict servname,
      62               const struct addrinfo *restrict hints,
      63               struct addrinfo **restrict res)
      64  # undef getaddrinfo
      65  {
      66    return getaddrinfo (nodename, servname, hints, res);
      67  }
      68  
      69  void
      70  freeaddrinfo (struct addrinfo *ai)
      71  # undef freeaddrinfo
      72  {
      73    freeaddrinfo (ai);
      74  }
      75  
      76  #else
      77  
      78  # if defined _WIN32 && !defined __CYGWIN__
      79  #  define WINDOWS_NATIVE
      80  # endif
      81  
      82  /* gl_sockets_startup */
      83  # include "sockets.h"
      84  
      85  # ifdef WINDOWS_NATIVE
      86  
      87  /* Don't assume that UNICODE is not defined.  */
      88  #  undef GetModuleHandle
      89  #  define GetModuleHandle GetModuleHandleA
      90  
      91  #  if !(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
      92  
      93  /* Avoid warnings from gcc -Wcast-function-type.  */
      94  #   define GetProcAddress \
      95       (void *) GetProcAddress
      96  
      97  typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*,
      98                                          const struct addrinfo*,
      99                                          struct addrinfo**);
     100  typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*);
     101  typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*,
     102                                          socklen_t, char*, DWORD,
     103                                          char*, DWORD, int);
     104  
     105  static getaddrinfo_func getaddrinfo_ptr = NULL;
     106  static freeaddrinfo_func freeaddrinfo_ptr = NULL;
     107  static getnameinfo_func getnameinfo_ptr = NULL;
     108  
     109  static int
     110  use_win32_p (void)
     111  {
     112    static int done = 0;
     113    HMODULE h;
     114  
     115    if (done)
     116      return getaddrinfo_ptr ? 1 : 0;
     117  
     118    done = 1;
     119  
     120    h = GetModuleHandle ("ws2_32.dll");
     121  
     122    if (h)
     123      {
     124        getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo");
     125        freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo");
     126        getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo");
     127      }
     128  
     129    /* If either is missing, something is odd. */
     130    if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr)
     131      {
     132        getaddrinfo_ptr = NULL;
     133        freeaddrinfo_ptr = NULL;
     134        getnameinfo_ptr = NULL;
     135        return 0;
     136      }
     137  
     138    gl_sockets_startup (SOCKETS_1_1);
     139  
     140    return 1;
     141  }
     142  
     143  #  else
     144  
     145  static int
     146  use_win32_p (void)
     147  {
     148    static int done = 0;
     149  
     150    if (!done)
     151      {
     152        done = 1;
     153  
     154        gl_sockets_startup (SOCKETS_1_1);
     155      }
     156  
     157    return 1;
     158  }
     159  
     160  #   define getaddrinfo_ptr getaddrinfo
     161  #   define freeaddrinfo_ptr freeaddrinfo
     162  #   define getnameinfo_ptr getnameinfo
     163  
     164  #  endif
     165  # endif
     166  
     167  static bool
     168  validate_family (int family)
     169  {
     170    /* FIXME: Support more families. */
     171  # if HAVE_IPV4
     172       if (family == PF_INET)
     173         return true;
     174  # endif
     175  # if HAVE_IPV6
     176       if (family == PF_INET6)
     177         return true;
     178  # endif
     179       if (family == PF_UNSPEC)
     180         return true;
     181       return false;
     182  }
     183  
     184  /* Translate name of a service location and/or a service name to set of
     185     socket addresses. */
     186  int
     187  getaddrinfo (const char *restrict nodename,
     188               const char *restrict servname,
     189               const struct addrinfo *restrict hints,
     190               struct addrinfo **restrict res)
     191  #undef getaddrinfo
     192  {
     193    struct addrinfo *tmp;
     194    int port = 0;
     195    struct hostent *he;
     196    void *storage;
     197    size_t size;
     198  # if HAVE_IPV6
     199    struct v6_pair {
     200      struct addrinfo addrinfo;
     201      struct sockaddr_in6 sockaddr_in6;
     202    };
     203  # endif
     204  # if HAVE_IPV4
     205    struct v4_pair {
     206      struct addrinfo addrinfo;
     207      struct sockaddr_in sockaddr_in;
     208    };
     209  # endif
     210  
     211  # ifdef WINDOWS_NATIVE
     212    if (use_win32_p ())
     213      return getaddrinfo_ptr (nodename, servname, hints, res);
     214  # endif
     215  
     216    if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE)))
     217      /* FIXME: Support more flags. */
     218      return EAI_BADFLAGS;
     219  
     220    if (hints && !validate_family (hints->ai_family))
     221      return EAI_FAMILY;
     222  
     223    if (hints &&
     224        hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM)
     225      /* FIXME: Support other socktype. */
     226      return EAI_SOCKTYPE; /* FIXME: Better return code? */
     227  
     228    if (!nodename)
     229      {
     230        if (!(hints->ai_flags & AI_PASSIVE))
     231          return EAI_NONAME;
     232  
     233  # ifdef HAVE_IPV6
     234        nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0";
     235  # else
     236        nodename = "0.0.0.0";
     237  # endif
     238      }
     239  
     240    if (servname)
     241      {
     242        struct servent *se = NULL;
     243        const char *proto =
     244          (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
     245  
     246        if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV))
     247          /* FIXME: Use getservbyname_r if available. */
     248          se = getservbyname (servname, proto);
     249  
     250        if (!se)
     251          {
     252            char *c;
     253            if (!(*servname >= '0' && *servname <= '9'))
     254              return EAI_NONAME;
     255            port = strtoul (servname, &c, 10);
     256            if (*c || port > 0xffff)
     257              return EAI_NONAME;
     258            port = htons (port);
     259          }
     260        else
     261          port = se->s_port;
     262      }
     263  
     264    /* FIXME: Use gethostbyname_r if available. */
     265    he = gethostbyname (nodename);
     266    if (!he || he->h_addr_list[0] == NULL)
     267      return EAI_NONAME;
     268  
     269    switch (he->h_addrtype)
     270      {
     271  # if HAVE_IPV6
     272      case PF_INET6:
     273        size = sizeof (struct v6_pair);
     274        break;
     275  # endif
     276  
     277  # if HAVE_IPV4
     278      case PF_INET:
     279        size = sizeof (struct v4_pair);
     280        break;
     281  # endif
     282  
     283      default:
     284        return EAI_NODATA;
     285      }
     286  
     287    storage = calloc (1, size);
     288    if (!storage)
     289      return EAI_MEMORY;
     290  
     291    switch (he->h_addrtype)
     292      {
     293  # if HAVE_IPV6
     294      case PF_INET6:
     295        {
     296          struct v6_pair *p = storage;
     297          struct sockaddr_in6 *sinp = &p->sockaddr_in6;
     298          tmp = &p->addrinfo;
     299  
     300          if (port)
     301            sinp->sin6_port = port;
     302  
     303          if (he->h_length != sizeof (sinp->sin6_addr))
     304            {
     305              free (storage);
     306              return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
     307            }
     308  
     309          memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr);
     310  
     311          tmp->ai_addr = (struct sockaddr *) sinp;
     312          tmp->ai_addrlen = sizeof *sinp;
     313        }
     314        break;
     315  # endif
     316  
     317  # if HAVE_IPV4
     318      case PF_INET:
     319        {
     320          struct v4_pair *p = storage;
     321          struct sockaddr_in *sinp = &p->sockaddr_in;
     322          tmp = &p->addrinfo;
     323  
     324          if (port)
     325            sinp->sin_port = port;
     326  
     327          if (he->h_length != sizeof (sinp->sin_addr))
     328            {
     329              free (storage);
     330              return EAI_SYSTEM; /* FIXME: Better return code?  Set errno? */
     331            }
     332  
     333          memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr);
     334  
     335          tmp->ai_addr = (struct sockaddr *) sinp;
     336          tmp->ai_addrlen = sizeof *sinp;
     337        }
     338        break;
     339  # endif
     340  
     341      default:
     342        free (storage);
     343        return EAI_NODATA;
     344      }
     345  
     346    if (hints && hints->ai_flags & AI_CANONNAME)
     347      {
     348        const char *cn;
     349        if (he->h_name)
     350          cn = he->h_name;
     351        else
     352          cn = nodename;
     353  
     354        tmp->ai_canonname = strdup (cn);
     355        if (!tmp->ai_canonname)
     356          {
     357            free (storage);
     358            return EAI_MEMORY;
     359          }
     360      }
     361  
     362    tmp->ai_protocol = (hints) ? hints->ai_protocol : 0;
     363    tmp->ai_socktype = (hints) ? hints->ai_socktype : 0;
     364    tmp->ai_addr->sa_family = he->h_addrtype;
     365    tmp->ai_family = he->h_addrtype;
     366  
     367  # ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
     368    switch (he->h_addrtype)
     369      {
     370  #  if HAVE_IPV4
     371      case AF_INET:
     372        tmp->ai_addr->sa_len = sizeof (struct sockaddr_in);
     373        break;
     374  #  endif
     375  #  if HAVE_IPV6
     376      case AF_INET6:
     377        tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6);
     378        break;
     379  #  endif
     380      }
     381  # endif
     382  
     383    /* FIXME: If more than one address, create linked list of addrinfo's. */
     384  
     385    *res = tmp;
     386  
     387    return 0;
     388  }
     389  
     390  /* Free 'addrinfo' structure AI including associated storage.  */
     391  void
     392  freeaddrinfo (struct addrinfo *ai)
     393  #undef freeaddrinfo
     394  {
     395  # ifdef WINDOWS_NATIVE
     396    if (use_win32_p ())
     397      {
     398        freeaddrinfo_ptr (ai);
     399        return;
     400      }
     401  # endif
     402  
     403    while (ai)
     404      {
     405        struct addrinfo *cur;
     406  
     407        cur = ai;
     408        ai = ai->ai_next;
     409  
     410        free (cur->ai_canonname);
     411        free (cur);
     412      }
     413  }
     414  
     415  int
     416  getnameinfo (const struct sockaddr *restrict sa, socklen_t salen,
     417               char *restrict node, socklen_t nodelen,
     418               char *restrict service, socklen_t servicelen,
     419               int flags)
     420  #undef getnameinfo
     421  {
     422  # ifdef WINDOWS_NATIVE
     423    if (use_win32_p ())
     424      return getnameinfo_ptr (sa, salen, node, nodelen,
     425                              service, servicelen, flags);
     426  # endif
     427  
     428    /* FIXME: Support other flags. */
     429    if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) ||
     430        (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) ||
     431        (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV)))
     432      return EAI_BADFLAGS;
     433  
     434    if (sa == NULL || salen < sizeof (sa->sa_family))
     435      return EAI_FAMILY;
     436  
     437    switch (sa->sa_family)
     438      {
     439  # if HAVE_IPV4
     440      case AF_INET:
     441        if (salen < sizeof (struct sockaddr_in))
     442          return EAI_FAMILY;
     443        break;
     444  # endif
     445  # if HAVE_IPV6
     446      case AF_INET6:
     447        if (salen < sizeof (struct sockaddr_in6))
     448          return EAI_FAMILY;
     449        break;
     450  # endif
     451      default:
     452        return EAI_FAMILY;
     453      }
     454  
     455    if (node && nodelen > 0 && flags & NI_NUMERICHOST)
     456      {
     457        switch (sa->sa_family)
     458          {
     459  # if HAVE_IPV4
     460          case AF_INET:
     461            if (!inet_ntop (AF_INET,
     462                            &(((const struct sockaddr_in *) sa)->sin_addr),
     463                            node, nodelen))
     464              return EAI_SYSTEM;
     465            break;
     466  # endif
     467  
     468  # if HAVE_IPV6
     469          case AF_INET6:
     470            if (!inet_ntop (AF_INET6,
     471                            &(((const struct sockaddr_in6 *) sa)->sin6_addr),
     472                            node, nodelen))
     473              return EAI_SYSTEM;
     474            break;
     475  # endif
     476  
     477          default:
     478            return EAI_FAMILY;
     479          }
     480      }
     481  
     482    if (service && servicelen > 0 && flags & NI_NUMERICSERV)
     483      switch (sa->sa_family)
     484        {
     485  # if HAVE_IPV4
     486        case AF_INET:
     487  # endif
     488  # if HAVE_IPV6
     489        case AF_INET6:
     490  # endif
     491          {
     492            unsigned short int port
     493              = ntohs (((const struct sockaddr_in *) sa)->sin_port);
     494            if (servicelen <= snprintf (service, servicelen, "%u", port))
     495              return EAI_OVERFLOW;
     496          }
     497          break;
     498        }
     499  
     500    return 0;
     501  }
     502  
     503  #endif