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