Branch data Line data Source code
1 : : /* lookup.c - implementation of IDNA2008 lookup 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> /* malloc, free */
24 : :
25 : : #include "punycode.h"
26 : :
27 : : #include "uniconv.h" /* u8_strconv_from_locale */
28 : : #include "uninorm.h" /* u32_normalize */
29 : :
30 : : #include "idna.h" /* _idn2_label_test */
31 : :
32 : : static int
33 : 592 : label (const uint8_t * src, size_t srclen, uint8_t * dst, size_t * dstlen,
34 : : int flags)
35 : : {
36 : : size_t plen;
37 : : uint32_t *p;
38 : : int rc;
39 : : size_t tmpl;
40 : :
41 [ + + ]: 592 : if (_idn2_ascii_p (src, srclen))
42 : : {
43 [ - + ]: 145 : if (flags & IDN2_ALABEL_ROUNDTRIP)
44 : : /* FIXME implement this MAY:
45 : :
46 : : If the input to this procedure appears to be an A-label
47 : : (i.e., it starts in "xn--", interpreted
48 : : case-insensitively), the lookup application MAY attempt to
49 : : convert it to a U-label, first ensuring that the A-label is
50 : : entirely in lowercase (converting it to lowercase if
51 : : necessary), and apply the tests of Section 5.4 and the
52 : : conversion of Section 5.5 to that form. */
53 : 0 : return -1;
54 : :
55 [ + + ]: 145 : if (srclen > IDN2_LABEL_MAX_LENGTH)
56 : 3 : return IDN2_TOO_BIG_LABEL;
57 [ - + ]: 142 : if (srclen > *dstlen)
58 : 0 : return IDN2_TOO_BIG_DOMAIN;
59 : :
60 : 142 : memcpy (dst, src, srclen);
61 : 142 : *dstlen = srclen;
62 : 142 : return IDN2_OK;
63 : : }
64 : :
65 : 447 : rc = _idn2_u8_to_u32_nfc (src, srclen, &p, &plen, flags & IDN2_NFC_INPUT);
66 [ + + ]: 447 : if (rc != IDN2_OK)
67 : 12 : return rc;
68 : :
69 : 435 : rc = _idn2_label_test (TEST_NFC |
70 : : TEST_2HYPHEN |
71 : : TEST_LEADING_COMBINING |
72 : : TEST_DISALLOWED |
73 : : TEST_CONTEXTJ_RULE |
74 : : TEST_CONTEXTO_WITH_RULE |
75 : : TEST_UNASSIGNED | TEST_BIDI, p, plen);
76 [ + + ]: 435 : if (rc != IDN2_OK)
77 : : {
78 : 90 : free (p);
79 : 90 : return rc;
80 : : }
81 : :
82 : 345 : dst[0] = 'x';
83 : 345 : dst[1] = 'n';
84 : 345 : dst[2] = '-';
85 : 345 : dst[3] = '-';
86 : :
87 : 345 : tmpl = *dstlen - 4;
88 : 345 : rc = _idn2_punycode_encode (plen, p, NULL, &tmpl, (char *) dst + 4);
89 : 345 : free (p);
90 [ + + ]: 345 : if (rc != IDN2_OK)
91 : 4 : return rc;
92 : :
93 : 341 : *dstlen = 4 + tmpl;
94 : :
95 : 592 : return IDN2_OK;
96 : : }
97 : :
98 : : /**
99 : : * idn2_lookup_u8:
100 : : * @src: input zero-terminated UTF-8 string in Unicode NFC normalized form.
101 : : * @lookupname: newly allocated output variable with name to lookup in DNS.
102 : : * @flags: optional #idn2_flags to modify behaviour.
103 : : *
104 : : * Perform IDNA2008 lookup string conversion on domain name @src, as
105 : : * described in section 5 of RFC 5891. Note that the input string
106 : : * must be encoded in UTF-8 and be in Unicode NFC form.
107 : : *
108 : : * Pass %IDN2_NFC_INPUT in @flags to convert input to NFC form before
109 : : * further processing. Pass %IDN2_ALABEL_ROUNDTRIP in @flags to
110 : : * convert any input A-labels to U-labels and perform additional
111 : : * testing. Multiple flags may be specified by binary or:ing them
112 : : * together, for example %IDN2_NFC_INPUT | %IDN2_ALABEL_ROUNDTRIP.
113 : : *
114 : : * Returns: On successful conversion %IDN2_OK is returned, if the
115 : : * output domain or any label would have been too long
116 : : * %IDN2_TOO_BIG_DOMAIN or %IDN2_TOO_BIG_LABEL is returned, or
117 : : * another error code is returned.
118 : : **/
119 : : int
120 : 458 : idn2_lookup_u8 (const uint8_t * src, uint8_t ** lookupname, int flags)
121 : : {
122 : 458 : size_t lookupnamelen = 0;
123 : : int rc;
124 : :
125 [ - + ]: 458 : if (src == NULL)
126 : 0 : return IDN2_OK;
127 : :
128 : 458 : *lookupname = malloc (IDN2_DOMAIN_MAX_LENGTH + 1);
129 [ - + ]: 458 : if (*lookupname == NULL)
130 : 0 : return IDN2_MALLOC;
131 : :
132 : : do
133 : : {
134 : 592 : const uint8_t *end = strchrnul ((const char *) src, '.');
135 : : /* XXX Do we care about non-U+002E dots such as U+3002, U+FF0E
136 : : and U+FF61 here? Perhaps when IDN2_NFC_INPUT? */
137 : 592 : size_t labellen = end - src;
138 : : uint8_t tmp[IDN2_LABEL_MAX_LENGTH];
139 : 592 : size_t tmplen = IDN2_LABEL_MAX_LENGTH;
140 : :
141 : 592 : rc = label (src, labellen, tmp, &tmplen, flags);
142 [ + + ]: 592 : if (rc != IDN2_OK)
143 : : {
144 : 109 : free (*lookupname);
145 : 109 : return rc;
146 : : }
147 : :
148 [ + + ][ + + ]: 483 : if (lookupnamelen + tmplen
[ + + ]
149 : 498 : > IDN2_DOMAIN_MAX_LENGTH - (tmplen == 0 && *end == '\0' ? 1 : 2))
150 : : {
151 : 2 : free (*lookupname);
152 : 2 : return IDN2_TOO_BIG_DOMAIN;
153 : : }
154 : :
155 : 481 : memcpy (*lookupname + lookupnamelen, tmp, tmplen);
156 : 481 : lookupnamelen += tmplen;
157 : :
158 [ + + ]: 481 : if (*end == '.')
159 : : {
160 [ - + ]: 134 : if (lookupnamelen + 1 > IDN2_DOMAIN_MAX_LENGTH)
161 : : {
162 : 0 : free (*lookupname);
163 : 0 : return IDN2_TOO_BIG_DOMAIN;
164 : : }
165 : :
166 : 134 : (*lookupname)[lookupnamelen] = '.';
167 : 134 : lookupnamelen++;
168 : : }
169 : 481 : (*lookupname)[lookupnamelen] = '\0';
170 : :
171 : 481 : src = end;
172 : : }
173 [ + + ]: 481 : while (*src++);
174 : :
175 : 458 : return IDN2_OK;
176 : : }
177 : :
178 : : /**
179 : : * idn2_lookup_ul:
180 : : * @src: input zero-terminated locale encoded string.
181 : : * @lookupname: newly allocated output variable with name to lookup in DNS.
182 : : * @flags: optional #idn2_flags to modify behaviour.
183 : : *
184 : : * Perform IDNA2008 lookup string conversion on domain name @src, as
185 : : * described in section 5 of RFC 5891. Note that the input is assumed
186 : : * to be encoded in the locale's default coding system, and will be
187 : : * transcoded to UTF-8 and NFC normalized by this function.
188 : : *
189 : : * Pass %IDN2_ALABEL_ROUNDTRIP in @flags to convert any input A-labels
190 : : * to U-labels and perform additional testing.
191 : : *
192 : : * Returns: On successful conversion %IDN2_OK is returned, if
193 : : * conversion from locale to UTF-8 fails then %IDN2_ICONV_FAIL is
194 : : * returned, if the output domain or any label would have been too
195 : : * long %IDN2_TOO_BIG_DOMAIN or %IDN2_TOO_BIG_LABEL is returned, or
196 : : * another error code is returned.
197 : : **/
198 : : int
199 : 0 : idn2_lookup_ul (const char *src, char **lookupname, int flags)
200 : : {
201 : 0 : uint8_t *utf8src = u8_strconv_from_locale (src);
202 : : int rc;
203 : :
204 [ # # ]: 0 : if (utf8src == NULL)
205 : : {
206 [ # # ]: 0 : if (errno == ENOMEM)
207 : 0 : return IDN2_MALLOC;
208 : 0 : return IDN2_ICONV_FAIL;
209 : : }
210 : :
211 : 0 : rc = idn2_lookup_u8 (utf8src, (uint8_t **) lookupname,
212 : : flags | IDN2_NFC_INPUT);
213 : :
214 : 0 : free (utf8src);
215 : :
216 : 0 : return rc;
217 : : }
|