Line data Source code
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 1017 : idn2_register_u8 (const uint8_t *ulabel, const uint8_t *alabel,
73 : uint8_t **insertname, int flags)
74 : {
75 : int rc;
76 :
77 1017 : if (ulabel == NULL && alabel == NULL)
78 : {
79 0 : if (insertname)
80 0 : *insertname = NULL;
81 0 : return IDN2_OK;
82 : }
83 :
84 1017 : if (alabel)
85 : {
86 402 : size_t alabellen = strlen ((char *) alabel), u32len =
87 : IDN2_LABEL_MAX_LENGTH * 4;
88 : uint32_t u32[IDN2_DOMAIN_MAX_LENGTH * 4];
89 : uint8_t *tmp;
90 : uint8_t u8[IDN2_DOMAIN_MAX_LENGTH + 1];
91 : size_t u8len;
92 :
93 402 : if (alabellen > IDN2_LABEL_MAX_LENGTH)
94 381 : return IDN2_TOO_BIG_LABEL;
95 :
96 401 : if (alabellen <= 4)
97 5 : return IDN2_INVALID_ALABEL;
98 396 : if (alabel[0] != 'x'
99 396 : || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-')
100 0 : return IDN2_INVALID_ALABEL;
101 :
102 396 : if (!_idn2_ascii_p (alabel, alabellen))
103 1 : return IDN2_INVALID_ALABEL;
104 :
105 395 : rc = idn2_punycode_decode ((char *) alabel + 4, alabellen - 4,
106 : u32, &u32len);
107 395 : if (rc != IDN2_OK)
108 65 : return rc;
109 :
110 330 : u8len = sizeof (u8);
111 330 : if (u32_to_u8 (u32, u32len, u8, &u8len) == NULL)
112 1 : return IDN2_ENCODING_ERROR;
113 329 : u8[u8len] = '\0';
114 :
115 329 : if (ulabel)
116 : {
117 0 : if (strcmp ((char *) ulabel, (char *) u8) != 0)
118 0 : return IDN2_UALABEL_MISMATCH;
119 : }
120 :
121 329 : rc = idn2_register_u8 (u8, NULL, &tmp, 0);
122 329 : if (rc != IDN2_OK)
123 223 : return rc;
124 :
125 106 : rc = strcmp ((char *) alabel, (char *) tmp);
126 106 : free (tmp);
127 106 : if (rc != 0)
128 85 : return IDN2_UALABEL_MISMATCH;
129 :
130 21 : if (insertname)
131 : {
132 21 : uint8_t *m = (uint8_t *) strdup ((char *) alabel);
133 21 : if (!m)
134 0 : return IDN2_MALLOC;
135 :
136 21 : *insertname = m;
137 : }
138 : }
139 : else /* ulabel only */
140 : {
141 615 : size_t ulabellen = u8_strlen (ulabel);
142 : uint32_t *u32;
143 : size_t u32len;
144 : size_t tmpl;
145 : uint8_t tmp[IDN2_LABEL_MAX_LENGTH + 1];
146 :
147 615 : if (_idn2_ascii_p (ulabel, ulabellen))
148 : {
149 286 : if (ulabellen > IDN2_LABEL_MAX_LENGTH)
150 509 : return IDN2_TOO_BIG_LABEL;
151 :
152 285 : if (insertname)
153 : {
154 285 : uint8_t *m = (uint8_t *) strdup ((char *) ulabel);
155 285 : if (!m)
156 0 : return IDN2_MALLOC;
157 285 : *insertname = m;
158 : }
159 285 : return IDN2_OK;
160 : }
161 :
162 329 : rc = _idn2_u8_to_u32_nfc (ulabel, ulabellen, &u32, &u32len,
163 : flags & IDN2_NFC_INPUT);
164 329 : if (rc != IDN2_OK)
165 0 : return rc;
166 :
167 329 : rc = _idn2_label_test (TEST_NFC
168 : | TEST_DISALLOWED
169 : | TEST_UNASSIGNED
170 : | TEST_2HYPHEN
171 : | TEST_HYPHEN_STARTEND
172 : | TEST_LEADING_COMBINING
173 : | TEST_CONTEXTJ_RULE
174 : | TEST_CONTEXTO_RULE | TEST_BIDI, u32, u32len);
175 329 : if (rc != IDN2_OK)
176 : {
177 223 : free (u32);
178 223 : return rc;
179 : }
180 :
181 106 : tmp[0] = 'x';
182 106 : tmp[1] = 'n';
183 106 : tmp[2] = '-';
184 106 : tmp[3] = '-';
185 :
186 106 : tmpl = IDN2_LABEL_MAX_LENGTH - 4;
187 106 : rc = idn2_punycode_encode (u32, u32len, (char *) tmp + 4, &tmpl);
188 106 : free (u32);
189 106 : if (rc != IDN2_OK)
190 0 : return rc;
191 :
192 106 : tmp[4 + tmpl] = '\0';
193 :
194 106 : if (insertname)
195 : {
196 106 : uint8_t *m = (uint8_t *) strdup ((char *) tmp);
197 106 : if (!m)
198 0 : return IDN2_MALLOC;
199 106 : *insertname = m;
200 : }
201 : }
202 :
203 127 : return IDN2_OK;
204 : }
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 804 : idn2_register_ul (const char *ulabel, const char *alabel,
237 : char **insertname, int flags)
238 : {
239 804 : uint8_t *utf8ulabel = NULL;
240 : int rc;
241 :
242 804 : if (ulabel)
243 : {
244 402 : const char *encoding = locale_charset ();
245 :
246 402 : utf8ulabel = u8_strconv_from_encoding (ulabel, encoding, iconveh_error);
247 :
248 402 : if (utf8ulabel == NULL)
249 : {
250 116 : if (errno == ENOMEM)
251 0 : return IDN2_MALLOC;
252 116 : return IDN2_ICONV_FAIL;
253 : }
254 : }
255 :
256 688 : rc = idn2_register_u8 (utf8ulabel, (const uint8_t *) alabel,
257 : (uint8_t **) insertname, flags | IDN2_NFC_INPUT);
258 :
259 688 : free (utf8ulabel);
260 :
261 688 : return rc;
262 : }
|