1 | /* |
2 | * Copyright (C) 2008 Red Hat, Inc |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library 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 GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General |
15 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include <glib/glib.h> |
19 | |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | |
24 | static const struct { |
25 | const gchar *ascii_name, *unicode_name; |
26 | } idn_test_domains[] = { |
27 | /* "example.test" in various languages */ |
28 | { "xn--mgbh0fb.xn--kgbechtv" , "\xd9\x85\xd8\xab\xd8\xa7\xd9\x84.\xd8\xa5\xd8\xae\xd8\xaa\xd8\xa8\xd8\xa7\xd8\xb1" }, |
29 | { "xn--fsqu00a.xn--0zwm56d" , "\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb5\x8b\xe8\xaf\x95" }, |
30 | { "xn--fsqu00a.xn--g6w251d" , "\xe4\xbe\x8b\xe5\xad\x90.\xe6\xb8\xac\xe8\xa9\xa6" }, |
31 | { "xn--hxajbheg2az3al.xn--jxalpdlp" , "\xcf\x80\xce\xb1\xcf\x81\xce\xac\xce\xb4\xce\xb5\xce\xb9\xce\xb3\xce\xbc\xce\xb1.\xce\xb4\xce\xbf\xce\xba\xce\xb9\xce\xbc\xce\xae" }, |
32 | { "xn--p1b6ci4b4b3a.xn--11b5bs3a9aj6g" , "\xe0\xa4\x89\xe0\xa4\xa6\xe0\xa4\xbe\xe0\xa4\xb9\xe0\xa4\xb0\xe0\xa4\xa3.\xe0\xa4\xaa\xe0\xa4\xb0\xe0\xa5\x80\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb7\xe0\xa4\xbe" }, |
33 | { "xn--r8jz45g.xn--zckzah" , "\xe4\xbe\x8b\xe3\x81\x88.\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88" }, |
34 | { "xn--9n2bp8q.xn--9t4b11yi5a" , "\xec\x8b\xa4\xeb\xa1\x80.\xed\x85\x8c\xec\x8a\xa4\xed\x8a\xb8" }, |
35 | { "xn--mgbh0fb.xn--hgbk6aj7f53bba" , "\xd9\x85\xd8\xab\xd8\xa7\xd9\x84.\xd8\xa2\xd8\xb2\xd9\x85\xd8\xa7\xdb\x8c\xd8\xb4\xdb\x8c" }, |
36 | { "xn--e1afmkfd.xn--80akhbyknj4f" , "\xd0\xbf\xd1\x80\xd0\xb8\xd0\xbc\xd0\xb5\xd1\x80.\xd0\xb8\xd1\x81\xd0\xbf\xd1\x8b\xd1\x82\xd0\xb0\xd0\xbd\xd0\xb8\xd0\xb5" }, |
37 | { "xn--zkc6cc5bi7f6e.xn--hlcj6aya9esc7a" , "\xe0\xae\x89\xe0\xae\xa4\xe0\xae\xbe\xe0\xae\xb0\xe0\xae\xa3\xe0\xae\xae\xe0\xaf\x8d.\xe0\xae\xaa\xe0\xae\xb0\xe0\xae\xbf\xe0\xae\x9f\xe0\xaf\x8d\xe0\xae\x9a\xe0\xaf\x88" }, |
38 | { "xn--fdbk5d8ap9b8a8d.xn--deba0ad" , "\xd7\x91\xd7\xb2\xd6\xb7\xd7\xa9\xd7\xa4\xd6\xbc\xd7\x99\xd7\x9c.\xd7\x98\xd7\xa2\xd7\xa1\xd7\x98" }, |
39 | |
40 | /* further examples without their own IDN-ized TLD */ |
41 | { "xn--1xd0bwwra.idn.icann.org" , "\xe1\x8a\xa0\xe1\x88\x9b\xe1\x88\xad\xe1\x8a\x9b.idn.icann.org" }, |
42 | { "xn--54b7fta0cc.idn.icann.org" , "\xe0\xa6\xac\xe0\xa6\xbe\xe0\xa6\x82\xe0\xa6\xb2\xe0\xa6\xbe.idn.icann.org" }, |
43 | { "xn--5dbqzzl.idn.icann.org" , "\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa.idn.icann.org" }, |
44 | { "xn--j2e7beiw1lb2hqg.idn.icann.org" , "\xe1\x9e\x97\xe1\x9e\xb6\xe1\x9e\x9f\xe1\x9e\xb6\xe1\x9e\x81\xe1\x9f\x92\xe1\x9e\x98\xe1\x9f\x82\xe1\x9e\x9a.idn.icann.org" }, |
45 | { "xn--o3cw4h.idn.icann.org" , "\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2.idn.icann.org" }, |
46 | { "xn--mgbqf7g.idn.icann.org" , "\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x88.idn.icann.org" } |
47 | }; |
48 | static const gint num_idn_test_domains = G_N_ELEMENTS (idn_test_domains); |
49 | |
50 | static const struct { |
51 | const gchar *orig_name, *ascii_name; |
52 | gboolean orig_is_unicode, ascii_is_encoded; |
53 | } non_round_trip_names[] = { |
54 | /* uppercase characters */ |
55 | { "EXAMPLE.COM" , "example.com" , FALSE, FALSE }, |
56 | { "\xc3\x89XAMPLE.COM" , "xn--xample-9ua.com" , TRUE, TRUE }, |
57 | |
58 | /* unicode that decodes to ascii */ |
59 | { "\xe2\x93\x94\xe2\x93\xa7\xe2\x93\x90\xe2\x93\x9c\xe2\x93\x9f\xe2\x93\x9b\xe2\x93\x94.com" , "example.com" , TRUE, FALSE }, |
60 | |
61 | /* non-standard dot characters */ |
62 | { "example\xe3\x80\x82" "com" , "example.com" , TRUE, FALSE }, |
63 | { "\xc3\xa9xample\xe3\x80\x82" "com" , "xn--xample-9ua.com" , TRUE, TRUE }, |
64 | { "Å.idn.icann.org" , "xn--5ca.idn.icann.org" , TRUE, TRUE }, |
65 | { "ℵℶℷ\xcd\x8f.idn.icann.org" , "xn--4dbcd.idn.icann.org" , TRUE, TRUE } |
66 | }; |
67 | static const gint num_non_round_trip_names = G_N_ELEMENTS (non_round_trip_names); |
68 | |
69 | static const gchar *bad_names[] = { |
70 | "disallowed\xef\xbf\xbd" "character" , |
71 | "non-utf\x88" , |
72 | "xn--mixed-\xc3\xbcp" , |
73 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
74 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
75 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
76 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
77 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
78 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
79 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
80 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
81 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
82 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
83 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
84 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
85 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
86 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
87 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" |
88 | "verylongverylongverylongverylongverylongverylongverylongverylongverylong" , |
89 | }; |
90 | static const gint num_bad_names = G_N_ELEMENTS (bad_names); |
91 | |
92 | static void |
93 | test_to_ascii (void) |
94 | { |
95 | gint i; |
96 | gchar *ascii; |
97 | |
98 | for (i = 0; i < num_idn_test_domains; i++) |
99 | { |
100 | g_assert_true (g_hostname_is_non_ascii (idn_test_domains[i].unicode_name)); |
101 | ascii = g_hostname_to_ascii (hostname: idn_test_domains[i].unicode_name); |
102 | g_assert_cmpstr (idn_test_domains[i].ascii_name, ==, ascii); |
103 | g_free (mem: ascii); |
104 | |
105 | ascii = g_hostname_to_ascii (hostname: idn_test_domains[i].ascii_name); |
106 | g_assert_cmpstr (idn_test_domains[i].ascii_name, ==, ascii); |
107 | g_free (mem: ascii); |
108 | } |
109 | |
110 | for (i = 0; i < num_non_round_trip_names; i++) |
111 | { |
112 | if (non_round_trip_names[i].orig_is_unicode) |
113 | g_assert_true (g_hostname_is_non_ascii (non_round_trip_names[i].orig_name)); |
114 | else |
115 | g_assert_true (!g_hostname_is_non_ascii (non_round_trip_names[i].orig_name)); |
116 | |
117 | if (non_round_trip_names[i].ascii_is_encoded) |
118 | g_assert_true (g_hostname_is_ascii_encoded (non_round_trip_names[i].ascii_name)); |
119 | else |
120 | g_assert_true (!g_hostname_is_ascii_encoded (non_round_trip_names[i].ascii_name)); |
121 | |
122 | ascii = g_hostname_to_ascii (hostname: non_round_trip_names[i].orig_name); |
123 | g_assert_cmpstr (non_round_trip_names[i].ascii_name, ==, ascii); |
124 | g_free (mem: ascii); |
125 | |
126 | ascii = g_hostname_to_ascii (hostname: non_round_trip_names[i].ascii_name); |
127 | g_assert_cmpstr (non_round_trip_names[i].ascii_name, ==, ascii); |
128 | g_free (mem: ascii); |
129 | } |
130 | |
131 | for (i = 0; i < num_bad_names; i++) |
132 | { |
133 | ascii = g_hostname_to_ascii (hostname: bad_names[i]); |
134 | g_assert_cmpstr (ascii, ==, NULL); |
135 | } |
136 | } |
137 | |
138 | static void |
139 | test_to_unicode (void) |
140 | { |
141 | gint i; |
142 | gchar *unicode; |
143 | |
144 | for (i = 0; i < num_idn_test_domains; i++) |
145 | { |
146 | g_assert_true (g_hostname_is_ascii_encoded (idn_test_domains[i].ascii_name)); |
147 | unicode = g_hostname_to_unicode (hostname: idn_test_domains[i].ascii_name); |
148 | g_assert_cmpstr (idn_test_domains[i].unicode_name, ==, unicode); |
149 | g_free (mem: unicode); |
150 | |
151 | unicode = g_hostname_to_unicode (hostname: idn_test_domains[i].unicode_name); |
152 | g_assert_cmpstr (idn_test_domains[i].unicode_name, ==, unicode); |
153 | g_free (mem: unicode); |
154 | } |
155 | |
156 | for (i = 0; i < num_bad_names; i++) |
157 | { |
158 | unicode = g_hostname_to_unicode (hostname: bad_names[i]); |
159 | g_assert_cmpstr (unicode, ==, NULL); |
160 | } |
161 | } |
162 | |
163 | static const struct { |
164 | const gchar *addr; |
165 | gboolean is_addr; |
166 | } ip_addr_tests[] = { |
167 | /* IPv6 tests */ |
168 | |
169 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeDc" , TRUE }, |
170 | |
171 | { "0123:4567:89AB:cdef:3210:7654:ba98::" , TRUE }, |
172 | { "0123:4567:89AB:cdef:3210:7654::" , TRUE }, |
173 | { "0123:4567:89AB:cdef:3210::" , TRUE }, |
174 | { "0123:4567:89AB:cdef::" , TRUE }, |
175 | { "0123:4567:89AB::" , TRUE }, |
176 | { "0123:4567::" , TRUE }, |
177 | { "0123::" , TRUE }, |
178 | |
179 | { "::4567:89AB:cdef:3210:7654:ba98:FeDc" , TRUE }, |
180 | { "::89AB:cdef:3210:7654:ba98:FeDc" , TRUE }, |
181 | { "::cdef:3210:7654:ba98:FeDc" , TRUE }, |
182 | { "::3210:7654:ba98:FeDc" , TRUE }, |
183 | { "::7654:ba98:FeDc" , TRUE }, |
184 | { "::ba98:FeDc" , TRUE }, |
185 | { "::FeDc" , TRUE }, |
186 | |
187 | { "0123::89AB:cdef:3210:7654:ba98:FeDc" , TRUE }, |
188 | { "0123:4567::cdef:3210:7654:ba98:FeDc" , TRUE }, |
189 | { "0123:4567:89AB::3210:7654:ba98:FeDc" , TRUE }, |
190 | { "0123:4567:89AB:cdef::7654:ba98:FeDc" , TRUE }, |
191 | { "0123:4567:89AB:cdef:3210::ba98:FeDc" , TRUE }, |
192 | { "0123:4567:89AB:cdef:3210:7654::FeDc" , TRUE }, |
193 | |
194 | { "0123::cdef:3210:7654:ba98:FeDc" , TRUE }, |
195 | { "0123:4567::3210:7654:ba98:FeDc" , TRUE }, |
196 | { "0123:4567:89AB::7654:ba98:FeDc" , TRUE }, |
197 | { "0123:4567:89AB:cdef::ba98:FeDc" , TRUE }, |
198 | { "0123:4567:89AB:cdef:3210::FeDc" , TRUE }, |
199 | |
200 | { "0123::3210:7654:ba98:FeDc" , TRUE }, |
201 | { "0123:4567::7654:ba98:FeDc" , TRUE }, |
202 | { "0123:4567:89AB::ba98:FeDc" , TRUE }, |
203 | { "0123:4567:89AB:cdef::FeDc" , TRUE }, |
204 | |
205 | { "0123::7654:ba98:FeDc" , TRUE }, |
206 | { "0123:4567::ba98:FeDc" , TRUE }, |
207 | { "0123:4567:89AB::FeDc" , TRUE }, |
208 | |
209 | { "0123::ba98:FeDc" , TRUE }, |
210 | { "0123:4567::FeDc" , TRUE }, |
211 | |
212 | { "0123::FeDc" , TRUE }, |
213 | |
214 | { "::" , TRUE }, |
215 | |
216 | { "0:12:345:6789:a:bc:def::" , TRUE }, |
217 | |
218 | { "0123:4567:89AB:cdef:3210:7654:123.45.67.89" , TRUE }, |
219 | { "0123:4567:89AB:cdef:3210::123.45.67.89" , TRUE }, |
220 | { "0123:4567:89AB:cdef::123.45.67.89" , TRUE }, |
221 | { "0123:4567:89AB::123.45.67.89" , TRUE }, |
222 | { "0123:4567::123.45.67.89" , TRUE }, |
223 | { "0123::123.45.67.89" , TRUE }, |
224 | { "::123.45.67.89" , TRUE }, |
225 | |
226 | /* accept zone-id from rfc6874 */ |
227 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeDc%zoneid0" , TRUE }, |
228 | { "fe80::dead:beef%zonÉid0%weird" , TRUE }, |
229 | { "fe80::dead:beef%" , FALSE }, |
230 | |
231 | /* Contain non-hex chars */ |
232 | { "012x:4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
233 | { "0123:45x7:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
234 | { "0123:4567:8xAB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
235 | { "0123:4567:89AB:xdef:3210:7654:ba98:FeDc" , FALSE }, |
236 | { "0123:4567:89AB:cdef:321;:7654:ba98:FeDc" , FALSE }, |
237 | { "0123:4567:89AB:cdef:3210:76*4:ba98:FeDc" , FALSE }, |
238 | { "0123:4567:89AB:cdef:3210:7654:b-98:FeDc" , FALSE }, |
239 | { "0123:4567:89AB:cdef:3210:7654:ba98:+eDc" , FALSE }, |
240 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeDc and some trailing junk" , FALSE }, |
241 | { " 123:4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
242 | { "012 :4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
243 | { "0123: 567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
244 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeD " , FALSE }, |
245 | |
246 | /* Contains too-long/out-of-range segments */ |
247 | { "00123:4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
248 | { "0123:04567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
249 | { "0123:4567:189AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
250 | |
251 | /* Too short */ |
252 | { "0123:4567:89AB:cdef:3210:7654:ba98" , FALSE }, |
253 | { "0123:4567:89AB:cdef:3210:7654" , FALSE }, |
254 | { "0123:4567:89AB:cdef:3210" , FALSE }, |
255 | { "0123" , FALSE }, |
256 | { "" , FALSE }, |
257 | |
258 | /* Too long */ |
259 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeDc:9999" , FALSE }, |
260 | { "0123::4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
261 | { "0123:4567::89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
262 | { "0123:4567:89AB::cdef:3210:7654:ba98:FeDc" , FALSE }, |
263 | { "0123:4567:89AB:cdef::3210:7654:ba98:FeDc" , FALSE }, |
264 | { "0123:4567:89AB:cdef:3210::7654:ba98:FeDc" , FALSE }, |
265 | { "0123:4567:89AB:cdef:3210:7654::ba98:FeDc" , FALSE }, |
266 | { "0123:4567:89AB:cdef:3210:7654:ba98::FeDc" , FALSE }, |
267 | |
268 | /* Invalid use of ":"s */ |
269 | { "0123::89AB::3210:7654:ba98:FeDc" , FALSE }, |
270 | { "::4567:89AB:cdef:3210:7654::FeDc" , FALSE }, |
271 | { "0123::89AB:cdef:3210:7654:ba98::" , FALSE }, |
272 | { ":4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
273 | { "0123:4567:89AB:cdef:3210:7654:ba98:" , FALSE }, |
274 | { "0123:::cdef:3210:7654:ba98:FeDc" , FALSE }, |
275 | { "0123:4567:89AB:cdef:3210:7654:ba98:FeDc:" , FALSE }, |
276 | { ":0123:4567:89AB:cdef:3210:7654:ba98:FeDc" , FALSE }, |
277 | { ":::" , FALSE }, |
278 | |
279 | /* IPv4 address at wrong place */ |
280 | { "0123:4567:89AB:cdef:3210:123.45.67.89" , FALSE }, |
281 | { "0123:4567:89AB:cdef:3210:7654::123.45.67.89" , FALSE }, |
282 | { "0123:4567:89AB:cdef:123.45.67.89" , FALSE }, |
283 | { "0123:4567:89AB:cdef:3210:123.45.67.89:FeDc" , FALSE }, |
284 | |
285 | /* IPv4 tests */ |
286 | |
287 | { "123.45.67.89" , TRUE }, |
288 | { "1.2.3.4" , TRUE }, |
289 | { "1.2.3.0" , TRUE }, |
290 | |
291 | { "023.045.067.089" , FALSE }, |
292 | { "1234.5.67.89" , FALSE }, |
293 | { "123.45.67.00" , FALSE }, |
294 | { " 1.2.3.4" , FALSE }, |
295 | { "1 .2.3.4" , FALSE }, |
296 | { "1. 2.3.4" , FALSE }, |
297 | { "1.2.3.4 " , FALSE }, |
298 | { "1.2.3" , FALSE }, |
299 | { "1.2.3.4.5" , FALSE }, |
300 | { "1.b.3.4" , FALSE }, |
301 | { "1.2.3:4" , FALSE }, |
302 | { "1.2.3.4, etc" , FALSE }, |
303 | { "1,2,3,4" , FALSE }, |
304 | { "1.2.3.com" , FALSE }, |
305 | { "1.2.3.4." , FALSE }, |
306 | { "1.2.3." , FALSE }, |
307 | { ".1.2.3.4" , FALSE }, |
308 | { ".2.3.4" , FALSE }, |
309 | { "1..2.3.4" , FALSE }, |
310 | { "1..3.4" , FALSE } |
311 | }; |
312 | static const gint num_ip_addr_tests = G_N_ELEMENTS (ip_addr_tests); |
313 | |
314 | static void |
315 | test_is_ip_addr (void) |
316 | { |
317 | gint i; |
318 | |
319 | for (i = 0; i < num_ip_addr_tests; i++) |
320 | { |
321 | if (g_hostname_is_ip_address (hostname: ip_addr_tests[i].addr) != ip_addr_tests[i].is_addr) |
322 | { |
323 | char *msg = g_strdup_printf (format: "g_hostname_is_ip_address (\"%s\") == %s" , |
324 | ip_addr_tests[i].addr, |
325 | ip_addr_tests[i].is_addr ? "TRUE" : "FALSE" ); |
326 | g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, message: msg); |
327 | } |
328 | } |
329 | } |
330 | |
331 | /* FIXME: test names with both unicode and ACE-encoded labels */ |
332 | /* FIXME: test invalid unicode names */ |
333 | |
334 | int |
335 | main (int argc, |
336 | char *argv[]) |
337 | { |
338 | g_test_init (argc: &argc, argv: &argv, NULL); |
339 | |
340 | if (argc == 2 && argv[1][0] != '-') |
341 | { |
342 | const gchar *hostname = argv[1]; |
343 | gchar *converted; |
344 | |
345 | if (g_hostname_is_non_ascii (hostname)) |
346 | { |
347 | converted = g_hostname_to_ascii (hostname); |
348 | printf (format: "to_ascii: %s\n" , converted); |
349 | g_free (mem: converted); |
350 | } |
351 | else if (g_hostname_is_ascii_encoded (hostname)) |
352 | { |
353 | converted = g_hostname_to_unicode (hostname); |
354 | printf (format: "to_unicode: %s\n" , converted); |
355 | g_free (mem: converted); |
356 | } |
357 | else |
358 | printf (format: "hostname is neither unicode nor ACE encoded\n" ); |
359 | return 0; |
360 | } |
361 | |
362 | g_test_add_func (testpath: "/hostutils/to_ascii" , test_func: test_to_ascii); |
363 | g_test_add_func (testpath: "/hostutils/to_unicode" , test_func: test_to_unicode); |
364 | g_test_add_func (testpath: "/hostutils/is_ip_addr" , test_func: test_is_ip_addr); |
365 | |
366 | return g_test_run (); |
367 | } |
368 | |