1 /* IDNA functions, forwarding to implementations in libidn2.
2    Copyright (C) 2018-2022 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 #include <allocate_once.h>
20 #include <dlfcn.h>
21 #include <inet/net-internal.h>
22 #include <netdb.h>
23 #include <stdbool.h>
24 
25 /* Use the soname and version to locate libidn2, to ensure a
26    compatible ABI.  */
27 #define LIBIDN2_SONAME "libidn2.so.0"
28 #define LIBIDN2_VERSION "IDN2_0.0.0"
29 
30 /* Return codes from libidn2.  */
31 enum
32   {
33     IDN2_OK = 0,
34     IDN2_MALLOC = -100,
35   };
36 
37 /* Functions from libidn2.  */
38 struct functions
39 {
40   void *handle;
41   int (*lookup_ul) (const char *src, char **result, int flags);
42   int (*to_unicode_lzlz) (const char *name, char **result, int flags);
43 };
44 
45 static void *
functions_allocate(void * closure)46 functions_allocate (void *closure)
47 {
48   struct functions *result = malloc (sizeof (*result));
49   if (result == NULL)
50     return NULL;
51 
52   void *handle = __libc_dlopen (LIBIDN2_SONAME);
53   if (handle == NULL)
54     /* Do not cache open failures.  The library may appear
55        later.  */
56     {
57       free (result);
58       return NULL;
59     }
60 
61   void *ptr_lookup_ul
62     = __libc_dlvsym (handle, "idn2_lookup_ul", LIBIDN2_VERSION);
63   void *ptr_to_unicode_lzlz
64     = __libc_dlvsym (handle, "idn2_to_unicode_lzlz", LIBIDN2_VERSION);
65   if (ptr_lookup_ul == NULL || ptr_to_unicode_lzlz == NULL)
66     {
67       __libc_dlclose (handle);
68       free (result);
69       return NULL;
70     }
71 
72   result->handle = handle;
73   result->lookup_ul = ptr_lookup_ul;
74   result->to_unicode_lzlz = ptr_to_unicode_lzlz;
75 #ifdef PTR_MANGLE
76   PTR_MANGLE (result->lookup_ul);
77   PTR_MANGLE (result->to_unicode_lzlz);
78 #endif
79 
80   return result;
81 }
82 
83 static void
functions_deallocate(void * closure,void * ptr)84 functions_deallocate (void *closure, void *ptr)
85 {
86   struct functions *functions = ptr;
87   __libc_dlclose (functions->handle);
88   free (functions);
89 }
90 
91 /* Ensure that *functions is initialized and return the value of the
92    pointer.  If the library cannot be loaded, return NULL.  */
93 static inline struct functions *
get_functions(void)94 get_functions (void)
95 {
96   static void *functions;
97   return allocate_once (&functions, functions_allocate, functions_deallocate,
98                         NULL);
99 }
100 
101 /* strdup with an EAI_* error code.  */
102 static int
gai_strdup(const char * name,char ** result)103 gai_strdup (const char *name, char **result)
104 {
105   char *ptr = __strdup (name);
106   if (ptr == NULL)
107     return EAI_MEMORY;
108   *result = ptr;
109   return 0;
110 }
111 
112 int
__idna_to_dns_encoding(const char * name,char ** result)113 __idna_to_dns_encoding (const char *name, char **result)
114 {
115   switch (__idna_name_classify (name))
116     {
117     case idna_name_ascii:
118       /* Nothing to convert.  */
119       return gai_strdup (name, result);
120     case idna_name_nonascii:
121       /* Encoding needed.  Handled below.  */
122       break;
123     case idna_name_nonascii_backslash:
124     case idna_name_encoding_error:
125       return EAI_IDN_ENCODE;
126     case idna_name_memory_error:
127       return EAI_MEMORY;
128     case idna_name_error:
129       return EAI_SYSTEM;
130     }
131 
132   struct functions *functions = get_functions ();
133   if (functions == NULL)
134     /* We report this as an encoding error (assuming that libidn2 is
135        not installed), although the root cause may be a temporary
136        error condition due to resource shortage.  */
137     return EAI_IDN_ENCODE;
138   char *ptr = NULL;
139   __typeof__ (functions->lookup_ul) fptr = functions->lookup_ul;
140 #ifdef PTR_DEMANGLE
141   PTR_DEMANGLE (fptr);
142 #endif
143   int ret = fptr (name, &ptr, 0);
144   if (ret == 0)
145     {
146       /* Assume that idn2_free is equivalent to free.  */
147       *result = ptr;
148       return 0;
149     }
150   else if (ret == IDN2_MALLOC)
151     return EAI_MEMORY;
152   else
153     return EAI_IDN_ENCODE;
154 }
libc_hidden_def(__idna_to_dns_encoding)155 libc_hidden_def (__idna_to_dns_encoding)
156 
157 int
158 __idna_from_dns_encoding (const char *name, char **result)
159 {
160   struct functions *functions = get_functions ();
161   if (functions == NULL)
162     /* Simply use the encoded name, assuming that it is not punycode
163        (but even a punycode name would be syntactically valid).  */
164     return gai_strdup (name, result);
165   char *ptr = NULL;
166   __typeof__ (functions->to_unicode_lzlz) fptr = functions->to_unicode_lzlz;
167 #ifdef PTR_DEMANGLE
168   PTR_DEMANGLE (fptr);
169 #endif
170   int ret = fptr (name, &ptr, 0);
171   if (ret == 0)
172     {
173       /* Assume that idn2_free is equivalent to free.  */
174       *result = ptr;
175       return 0;
176     }
177   else if (ret == IDN2_MALLOC)
178     return EAI_MEMORY;
179   else
180     return EAI_IDN_ENCODE;
181 }
182 libc_hidden_def (__idna_from_dns_encoding)
183