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

source code of glibc/inet/idna.c