/src/libidn2/lib/register.c
Line | Count | Source |
1 | | /* register.c - implementation of IDNA2008 register functions |
2 | | Copyright (C) 2011-2025 Simon Josefsson |
3 | | |
4 | | Libidn2 is free software: you can redistribute it and/or modify it |
5 | | under the terms of either: |
6 | | |
7 | | * the GNU Lesser General Public License as published by the Free |
8 | | Software Foundation; either version 3 of the License, or (at |
9 | | your option) any later version. |
10 | | |
11 | | or |
12 | | |
13 | | * the GNU General Public License as published by the Free |
14 | | Software Foundation; either version 2 of the License, or (at |
15 | | your option) any later version. |
16 | | |
17 | | or both in parallel, as here. |
18 | | |
19 | | This program is distributed in the hope that it will be useful, |
20 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | | GNU General Public License for more details. |
23 | | |
24 | | You should have received copies of the GNU General Public License and |
25 | | the GNU Lesser General Public License along with this program. If |
26 | | not, see <http://www.gnu.org/licenses/>. |
27 | | */ |
28 | | |
29 | | #include <config.h> |
30 | | |
31 | | #include "idn2.h" |
32 | | |
33 | | #include <errno.h> /* errno */ |
34 | | #include <stdlib.h> /* free */ |
35 | | |
36 | | #include <unitypes.h> |
37 | | #include <uniconv.h> /* u8_strconv_from_locale */ |
38 | | #include <unistr.h> /* u32_to_u8 */ |
39 | | |
40 | | #include "idna.h" /* _idn2_label_test */ |
41 | | |
42 | | /** |
43 | | * idn2_register_u8: |
44 | | * @ulabel: input zero-terminated UTF-8 and Unicode NFC string, or NULL. |
45 | | * @alabel: input zero-terminated ACE encoded string (xn--), or NULL. |
46 | | * @insertname: newly allocated output variable with name to register in DNS. |
47 | | * @flags: optional #idn2_flags to modify behaviour. |
48 | | * |
49 | | * Perform IDNA2008 register string conversion on domain label @ulabel |
50 | | * and @alabel, as described in section 4 of RFC 5891. Note that the |
51 | | * input @ulabel must be encoded in UTF-8 and be in Unicode NFC form. |
52 | | * |
53 | | * Pass %IDN2_NFC_INPUT in @flags to convert input @ulabel to NFC form |
54 | | * before further processing. |
55 | | * |
56 | | * It is recommended to supply both @ulabel and @alabel for better |
57 | | * error checking, but supplying just one of them will work. Passing |
58 | | * in only @alabel is better than only @ulabel. See RFC 5891 section |
59 | | * 4 for more information. |
60 | | * |
61 | | * After version 0.11: @insertname may be NULL to test conversion of @src |
62 | | * without allocating memory. |
63 | | * |
64 | | * Returns: On successful conversion %IDN2_OK is returned, when the |
65 | | * given @ulabel and @alabel does not match each other |
66 | | * %IDN2_UALABEL_MISMATCH is returned, when either of the input |
67 | | * labels are too long %IDN2_TOO_BIG_LABEL is returned, when @alabel |
68 | | * does does not appear to be a proper A-label %IDN2_INVALID_ALABEL |
69 | | * is returned, or another error code is returned. |
70 | | **/ |
71 | | int |
72 | | idn2_register_u8 (const uint8_t *ulabel, const uint8_t *alabel, |
73 | | uint8_t **insertname, int flags) |
74 | 6.19k | { |
75 | 6.19k | int rc; |
76 | | |
77 | 6.19k | if (ulabel == NULL && alabel == NULL) |
78 | 0 | { |
79 | 0 | if (insertname) |
80 | 0 | *insertname = NULL; |
81 | 0 | return IDN2_OK; |
82 | 0 | } |
83 | | |
84 | 6.19k | if (alabel) |
85 | 2.30k | { |
86 | 2.30k | size_t alabellen = strlen ((char *) alabel), u32len = |
87 | 2.30k | IDN2_LABEL_MAX_LENGTH * 4; |
88 | 2.30k | uint32_t u32[IDN2_DOMAIN_MAX_LENGTH * 4]; |
89 | 2.30k | uint8_t *tmp; |
90 | 2.30k | uint8_t u8[IDN2_DOMAIN_MAX_LENGTH + 1]; |
91 | 2.30k | size_t u8len; |
92 | | |
93 | 2.30k | if (alabellen > IDN2_LABEL_MAX_LENGTH) |
94 | 20 | return IDN2_TOO_BIG_LABEL; |
95 | | |
96 | 2.28k | if (alabellen <= 4) |
97 | 12 | return IDN2_INVALID_ALABEL; |
98 | 2.27k | if (alabel[0] != 'x' |
99 | 2.27k | || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') |
100 | 0 | return IDN2_INVALID_ALABEL; |
101 | | |
102 | 2.27k | if (!_idn2_ascii_p (alabel, alabellen)) |
103 | 8 | return IDN2_INVALID_ALABEL; |
104 | | |
105 | 2.26k | rc = idn2_punycode_decode ((char *) alabel + 4, alabellen - 4, |
106 | 2.26k | u32, &u32len); |
107 | 2.26k | if (rc != IDN2_OK) |
108 | 177 | return rc; |
109 | | |
110 | 2.08k | u8len = sizeof (u8); |
111 | 2.08k | if (u32_to_u8 (u32, u32len, u8, &u8len) == NULL) |
112 | 12 | return IDN2_ENCODING_ERROR; |
113 | 2.07k | u8[u8len] = '\0'; |
114 | | |
115 | 2.07k | if (ulabel) |
116 | 0 | { |
117 | 0 | if (strcmp ((char *) ulabel, (char *) u8) != 0) |
118 | 0 | return IDN2_UALABEL_MISMATCH; |
119 | 0 | } |
120 | | |
121 | 2.07k | rc = idn2_register_u8 (u8, NULL, &tmp, 0); |
122 | 2.07k | if (rc != IDN2_OK) |
123 | 1.44k | return rc; |
124 | | |
125 | 625 | rc = strcmp ((char *) alabel, (char *) tmp); |
126 | 625 | free (tmp); |
127 | 625 | if (rc != 0) |
128 | 468 | return IDN2_UALABEL_MISMATCH; |
129 | | |
130 | 157 | if (insertname) |
131 | 157 | { |
132 | 157 | uint8_t *m = (uint8_t *) strdup ((char *) alabel); |
133 | 157 | if (!m) |
134 | 0 | return IDN2_MALLOC; |
135 | | |
136 | 157 | *insertname = m; |
137 | 157 | } |
138 | 157 | } |
139 | 3.89k | else /* ulabel only */ |
140 | 3.89k | { |
141 | 3.89k | size_t ulabellen = u8_strlen (ulabel); |
142 | 3.89k | uint32_t *u32; |
143 | 3.89k | size_t u32len; |
144 | 3.89k | size_t tmpl; |
145 | 3.89k | uint8_t tmp[IDN2_LABEL_MAX_LENGTH + 1]; |
146 | | |
147 | 3.89k | if (_idn2_ascii_p (ulabel, ulabellen)) |
148 | 1.81k | { |
149 | 1.81k | if (ulabellen > IDN2_LABEL_MAX_LENGTH) |
150 | 14 | return IDN2_TOO_BIG_LABEL; |
151 | | |
152 | 1.80k | if (insertname) |
153 | 1.80k | { |
154 | 1.80k | uint8_t *m = (uint8_t *) strdup ((char *) ulabel); |
155 | 1.80k | if (!m) |
156 | 0 | return IDN2_MALLOC; |
157 | 1.80k | *insertname = m; |
158 | 1.80k | } |
159 | 1.80k | return IDN2_OK; |
160 | 1.80k | } |
161 | | |
162 | 2.07k | rc = _idn2_u8_to_u32_nfc (ulabel, ulabellen, &u32, &u32len, |
163 | 2.07k | flags & IDN2_NFC_INPUT); |
164 | 2.07k | if (rc != IDN2_OK) |
165 | 0 | return rc; |
166 | | |
167 | 2.07k | rc = _idn2_label_test (TEST_NFC |
168 | 2.07k | | TEST_DISALLOWED |
169 | 2.07k | | TEST_UNASSIGNED |
170 | 2.07k | | TEST_2HYPHEN |
171 | 2.07k | | TEST_HYPHEN_STARTEND |
172 | 2.07k | | TEST_LEADING_COMBINING |
173 | 2.07k | | TEST_CONTEXTJ_RULE |
174 | 2.07k | | TEST_CONTEXTO_RULE | TEST_BIDI, u32, u32len); |
175 | 2.07k | if (rc != IDN2_OK) |
176 | 1.44k | { |
177 | 1.44k | free (u32); |
178 | 1.44k | return rc; |
179 | 1.44k | } |
180 | | |
181 | 625 | tmp[0] = 'x'; |
182 | 625 | tmp[1] = 'n'; |
183 | 625 | tmp[2] = '-'; |
184 | 625 | tmp[3] = '-'; |
185 | | |
186 | 625 | tmpl = IDN2_LABEL_MAX_LENGTH - 4; |
187 | 625 | rc = idn2_punycode_encode (u32, u32len, (char *) tmp + 4, &tmpl); |
188 | 625 | free (u32); |
189 | 625 | if (rc != IDN2_OK) |
190 | 0 | return rc; |
191 | | |
192 | 625 | tmp[4 + tmpl] = '\0'; |
193 | | |
194 | 625 | if (insertname) |
195 | 625 | { |
196 | 625 | uint8_t *m = (uint8_t *) strdup ((char *) tmp); |
197 | 625 | if (!m) |
198 | 0 | return IDN2_MALLOC; |
199 | 625 | *insertname = m; |
200 | 625 | } |
201 | 625 | } |
202 | | |
203 | 782 | return IDN2_OK; |
204 | 6.19k | } |
205 | | |
206 | | /** |
207 | | * idn2_register_ul: |
208 | | * @ulabel: input zero-terminated locale encoded string, or NULL. |
209 | | * @alabel: input zero-terminated ACE encoded string (xn--), or NULL. |
210 | | * @insertname: newly allocated output variable with name to register in DNS. |
211 | | * @flags: optional #idn2_flags to modify behaviour. |
212 | | * |
213 | | * Perform IDNA2008 register string conversion on domain label @ulabel |
214 | | * and @alabel, as described in section 4 of RFC 5891. Note that the |
215 | | * input @ulabel is assumed to be encoded in the locale's default |
216 | | * coding system, and will be transcoded to UTF-8 and NFC normalized |
217 | | * by this function. |
218 | | * |
219 | | * It is recommended to supply both @ulabel and @alabel for better |
220 | | * error checking, but supplying just one of them will work. Passing |
221 | | * in only @alabel is better than only @ulabel. See RFC 5891 section |
222 | | * 4 for more information. |
223 | | * |
224 | | * After version 0.11: @insertname may be NULL to test conversion of @src |
225 | | * without allocating memory. |
226 | | * |
227 | | * Returns: On successful conversion %IDN2_OK is returned, when the |
228 | | * given @ulabel and @alabel does not match each other |
229 | | * %IDN2_UALABEL_MISMATCH is returned, when either of the input |
230 | | * labels are too long %IDN2_TOO_BIG_LABEL is returned, when @alabel |
231 | | * does does not appear to be a proper A-label %IDN2_INVALID_ALABEL |
232 | | * is returned, when @ulabel locale to UTF-8 conversion failed |
233 | | * %IDN2_ICONV_FAIL is returned, or another error code is returned. |
234 | | **/ |
235 | | int |
236 | | idn2_register_ul (const char *ulabel, const char *alabel, |
237 | | char **insertname, int flags) |
238 | 4.60k | { |
239 | 4.60k | uint8_t *utf8ulabel = NULL; |
240 | 4.60k | int rc; |
241 | | |
242 | 4.60k | if (ulabel) |
243 | 2.30k | { |
244 | 2.30k | const char *encoding = locale_charset (); |
245 | | |
246 | 2.30k | utf8ulabel = u8_strconv_from_encoding (ulabel, encoding, iconveh_error); |
247 | | |
248 | 2.30k | if (utf8ulabel == NULL) |
249 | 486 | { |
250 | 486 | if (errno == ENOMEM) |
251 | 0 | return IDN2_MALLOC; |
252 | 486 | return IDN2_ICONV_FAIL; |
253 | 486 | } |
254 | 2.30k | } |
255 | | |
256 | 4.12k | rc = idn2_register_u8 (utf8ulabel, (const uint8_t *) alabel, |
257 | 4.12k | (uint8_t **) insertname, flags | IDN2_NFC_INPUT); |
258 | | |
259 | 4.12k | free (utf8ulabel); |
260 | | |
261 | 4.12k | return rc; |
262 | 4.60k | } |