1/* IDNA functions, forwarding to implementations in libidn2.
2 Copyright (C) 2018-2019 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 <http://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. */
31enum
32 {
33 IDN2_OK = 0,
34 IDN2_MALLOC = -100,
35 };
36
37/* Functions from libidn2. */
38struct 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
45static void *
46functions_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
83static void
84functions_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. */
93static inline struct functions *
94get_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. */
102static int
103gai_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
112int
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}
155libc_hidden_def (__idna_to_dns_encoding)
156
157int
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}
182libc_hidden_def (__idna_from_dns_encoding)
183