Blame inet/idna.c

Packit 6c4009
/* IDNA functions, forwarding to implementations in libidn2.
Packit 6c4009
   Copyright (C) 2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <allocate_once.h>
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <inet/net-internal.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
Packit 6c4009
/* Use the soname and version to locate libidn2, to ensure a
Packit 6c4009
   compatible ABI.  */
Packit 6c4009
#define LIBIDN2_SONAME "libidn2.so.0"
Packit 6c4009
#define LIBIDN2_VERSION "IDN2_0.0.0"
Packit 6c4009
Packit 6c4009
/* Return codes from libidn2.  */
Packit 6c4009
enum
Packit 6c4009
  {
Packit 6c4009
    IDN2_OK = 0,
Packit 6c4009
    IDN2_MALLOC = -100,
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
/* Functions from libidn2.  */
Packit 6c4009
struct functions
Packit 6c4009
{
Packit 6c4009
  void *handle;
Packit 6c4009
  int (*lookup_ul) (const char *src, char **result, int flags);
Packit 6c4009
  int (*to_unicode_lzlz) (const char *name, char **result, int flags);
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static void *
Packit 6c4009
functions_allocate (void *closure)
Packit 6c4009
{
Packit 6c4009
  struct functions *result = malloc (sizeof (*result));
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  void *handle = __libc_dlopen (LIBIDN2_SONAME);
Packit 6c4009
  if (handle == NULL)
Packit 6c4009
    /* Do not cache open failures.  The library may appear
Packit 6c4009
       later.  */
Packit 6c4009
    {
Packit 6c4009
      free (result);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  void *ptr_lookup_ul
Packit 6c4009
    = __libc_dlvsym (handle, "idn2_lookup_ul", LIBIDN2_VERSION);
Packit 6c4009
  void *ptr_to_unicode_lzlz
Packit 6c4009
    = __libc_dlvsym (handle, "idn2_to_unicode_lzlz", LIBIDN2_VERSION);
Packit 6c4009
  if (ptr_lookup_ul == NULL || ptr_to_unicode_lzlz == NULL)
Packit 6c4009
    {
Packit 6c4009
      __libc_dlclose (handle);
Packit 6c4009
      free (result);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  result->handle = handle;
Packit 6c4009
  result->lookup_ul = ptr_lookup_ul;
Packit 6c4009
  result->to_unicode_lzlz = ptr_to_unicode_lzlz;
Packit 6c4009
#ifdef PTR_MANGLE
Packit 6c4009
  PTR_MANGLE (result->lookup_ul);
Packit 6c4009
  PTR_MANGLE (result->to_unicode_lzlz);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
functions_deallocate (void *closure, void *ptr)
Packit 6c4009
{
Packit 6c4009
  struct functions *functions = ptr;
Packit 6c4009
  __libc_dlclose (functions->handle);
Packit 6c4009
  free (functions);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Ensure that *functions is initialized and return the value of the
Packit 6c4009
   pointer.  If the library cannot be loaded, return NULL.  */
Packit 6c4009
static inline struct functions *
Packit 6c4009
get_functions (void)
Packit 6c4009
{
Packit 6c4009
  static void *functions;
Packit 6c4009
  return allocate_once (&functions, functions_allocate, functions_deallocate,
Packit 6c4009
                        NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* strdup with an EAI_* error code.  */
Packit 6c4009
static int
Packit 6c4009
gai_strdup (const char *name, char **result)
Packit 6c4009
{
Packit 6c4009
  char *ptr = __strdup (name);
Packit 6c4009
  if (ptr == NULL)
Packit 6c4009
    return EAI_MEMORY;
Packit 6c4009
  *result = ptr;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__idna_to_dns_encoding (const char *name, char **result)
Packit 6c4009
{
Packit 6c4009
  switch (__idna_name_classify (name))
Packit 6c4009
    {
Packit 6c4009
    case idna_name_ascii:
Packit 6c4009
      /* Nothing to convert.  */
Packit 6c4009
      return gai_strdup (name, result);
Packit 6c4009
    case idna_name_nonascii:
Packit 6c4009
      /* Encoding needed.  Handled below.  */
Packit 6c4009
      break;
Packit 6c4009
    case idna_name_nonascii_backslash:
Packit 6c4009
    case idna_name_encoding_error:
Packit 6c4009
      return EAI_IDN_ENCODE;
Packit 6c4009
    case idna_name_memory_error:
Packit 6c4009
      return EAI_MEMORY;
Packit 6c4009
    case idna_name_error:
Packit 6c4009
      return EAI_SYSTEM;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct functions *functions = get_functions ();
Packit 6c4009
  if (functions == NULL)
Packit 6c4009
    /* We report this as an encoding error (assuming that libidn2 is
Packit 6c4009
       not installed), although the root cause may be a temporary
Packit 6c4009
       error condition due to resource shortage.  */
Packit 6c4009
    return EAI_IDN_ENCODE;
Packit 6c4009
  char *ptr = NULL;
Packit 6c4009
  __typeof__ (functions->lookup_ul) fptr = functions->lookup_ul;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
  PTR_DEMANGLE (fptr);
Packit 6c4009
#endif
Packit 6c4009
  int ret = fptr (name, &ptr, 0);
Packit 6c4009
  if (ret == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Assume that idn2_free is equivalent to free.  */
Packit 6c4009
      *result = ptr;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  else if (ret == IDN2_MALLOC)
Packit 6c4009
    return EAI_MEMORY;
Packit 6c4009
  else
Packit 6c4009
    return EAI_IDN_ENCODE;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__idna_to_dns_encoding)
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__idna_from_dns_encoding (const char *name, char **result)
Packit 6c4009
{
Packit 6c4009
  struct functions *functions = get_functions ();
Packit 6c4009
  if (functions == NULL)
Packit 6c4009
    /* Simply use the encoded name, assuming that it is not punycode
Packit 6c4009
       (but even a punycode name would be syntactically valid).  */
Packit 6c4009
    return gai_strdup (name, result);
Packit 6c4009
  char *ptr = NULL;
Packit 6c4009
  __typeof__ (functions->to_unicode_lzlz) fptr = functions->to_unicode_lzlz;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
  PTR_DEMANGLE (fptr);
Packit 6c4009
#endif
Packit 6c4009
  int ret = fptr (name, &ptr, 0);
Packit 6c4009
  if (ret == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Assume that idn2_free is equivalent to free.  */
Packit 6c4009
      *result = ptr;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  else if (ret == IDN2_MALLOC)
Packit 6c4009
    return EAI_MEMORY;
Packit 6c4009
  else
Packit 6c4009
    return EAI_IDN_ENCODE;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__idna_from_dns_encoding)