• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
test_to_ascii(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 (idn_test_domains[i].unicode_name);
102       g_assert_cmpstr (idn_test_domains[i].ascii_name, ==, ascii);
103       g_free (ascii);
104 
105       ascii = g_hostname_to_ascii (idn_test_domains[i].ascii_name);
106       g_assert_cmpstr (idn_test_domains[i].ascii_name, ==, ascii);
107       g_free (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 (non_round_trip_names[i].orig_name);
123       g_assert_cmpstr (non_round_trip_names[i].ascii_name, ==, ascii);
124       g_free (ascii);
125 
126       ascii = g_hostname_to_ascii (non_round_trip_names[i].ascii_name);
127       g_assert_cmpstr (non_round_trip_names[i].ascii_name, ==, ascii);
128       g_free (ascii);
129     }
130 
131   for (i = 0; i < num_bad_names; i++)
132     {
133       ascii = g_hostname_to_ascii (bad_names[i]);
134       g_assert_cmpstr (ascii, ==, NULL);
135     }
136 }
137 
138 static void
test_to_unicode(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 (idn_test_domains[i].ascii_name);
148       g_assert_cmpstr (idn_test_domains[i].unicode_name, ==, unicode);
149       g_free (unicode);
150 
151       unicode = g_hostname_to_unicode (idn_test_domains[i].unicode_name);
152       g_assert_cmpstr (idn_test_domains[i].unicode_name, ==, unicode);
153       g_free (unicode);
154     }
155 
156   for (i = 0; i < num_bad_names; i++)
157     {
158       unicode = g_hostname_to_unicode (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
test_is_ip_addr(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 (ip_addr_tests[i].addr) != ip_addr_tests[i].is_addr)
322 	{
323 	  char *msg = g_strdup_printf ("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, 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
main(int argc,char * argv[])335 main (int   argc,
336       char *argv[])
337 {
338   g_test_init (&argc, &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 ("to_ascii: %s\n", converted);
349 	  g_free (converted);
350 	}
351       else if (g_hostname_is_ascii_encoded (hostname))
352 	{
353 	  converted = g_hostname_to_unicode (hostname);
354 	  printf ("to_unicode: %s\n", converted);
355 	  g_free (converted);
356 	}
357       else
358 	printf ("hostname is neither unicode nor ACE encoded\n");
359       return 0;
360     }
361 
362   g_test_add_func ("/hostutils/to_ascii", test_to_ascii);
363   g_test_add_func ("/hostutils/to_unicode", test_to_unicode);
364   g_test_add_func ("/hostutils/is_ip_addr", test_is_ip_addr);
365 
366   return g_test_run ();
367 }
368