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