Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 : :
3 : : /* GLIB - Library of useful routines for C programming
4 : : * Copyright (C) 2008 Red Hat, Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General
19 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include "config.h"
23 : : #include "glibconfig.h"
24 : :
25 : : #include <string.h>
26 : :
27 : : #ifdef G_OS_UNIX
28 : : #include <unistd.h>
29 : : #endif
30 : :
31 : : #include "ghostutils.h"
32 : :
33 : : #include "garray.h"
34 : : #include "gmem.h"
35 : : #include "gmessages.h"
36 : : #include "gstring.h"
37 : : #include "gstrfuncs.h"
38 : : #include "gtestutils.h"
39 : : #include "glibintl.h"
40 : :
41 : : #ifdef G_PLATFORM_WIN32
42 : : #include <windows.h>
43 : : #endif
44 : :
45 : :
46 : : #define IDNA_ACE_PREFIX "xn--"
47 : : #define IDNA_ACE_PREFIX_LEN 4
48 : :
49 : : /* Punycode constants, from RFC 3492. */
50 : :
51 : : #define PUNYCODE_BASE 36
52 : : #define PUNYCODE_TMIN 1
53 : : #define PUNYCODE_TMAX 26
54 : : #define PUNYCODE_SKEW 38
55 : : #define PUNYCODE_DAMP 700
56 : : #define PUNYCODE_INITIAL_BIAS 72
57 : : #define PUNYCODE_INITIAL_N 0x80
58 : :
59 : : #define IS_ASCII(cp) ((guint) (cp) < 0x80)
60 : : #define PUNYCODE_IS_BASIC(cp) IS_ASCII (cp)
61 : :
62 : : /* Encode/decode a single base-36 digit */
63 : : static inline gchar
64 : 297 : encode_digit (guint dig)
65 : : {
66 : 297 : if (dig < 26)
67 : 220 : return dig + 'a';
68 : : else
69 : 77 : return dig - 26 + '0';
70 : : }
71 : :
72 : : static inline guint
73 : 265 : decode_digit (gchar dig)
74 : : {
75 : 265 : if (dig >= 'A' && dig <= 'Z')
76 : 0 : return dig - 'A';
77 : 265 : else if (dig >= 'a' && dig <= 'z')
78 : 192 : return dig - 'a';
79 : 73 : else if (dig >= '0' && dig <= '9')
80 : 72 : return dig - '0' + 26;
81 : : else
82 : 1 : return G_MAXUINT;
83 : : }
84 : :
85 : : /* Punycode bias adaptation algorithm, RFC 3492 section 6.1 */
86 : : static guint
87 : 293 : adapt (guint delta,
88 : : guint numpoints,
89 : : gboolean firsttime)
90 : : {
91 : : guint k;
92 : :
93 : 293 : delta = firsttime ? delta / PUNYCODE_DAMP : delta / 2;
94 : 293 : delta += delta / numpoints;
95 : :
96 : 293 : k = 0;
97 : 311 : while (delta > ((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) / 2)
98 : : {
99 : 18 : delta /= PUNYCODE_BASE - PUNYCODE_TMIN;
100 : 18 : k += PUNYCODE_BASE;
101 : : }
102 : :
103 : 586 : return k + ((PUNYCODE_BASE - PUNYCODE_TMIN + 1) * delta /
104 : 293 : (delta + PUNYCODE_SKEW));
105 : : }
106 : :
107 : : /* Punycode encoder, RFC 3492 section 6.3. The algorithm is
108 : : * sufficiently bizarre that it's not really worth trying to explain
109 : : * here.
110 : : */
111 : : static gboolean
112 : 37 : punycode_encode (const gchar *input_utf8,
113 : : gsize input_utf8_length,
114 : : GString *output)
115 : : {
116 : : guint delta, handled_chars, num_basic_chars, bias, j, q, k, t, digit;
117 : : gunichar n, m, *input;
118 : : glong written_chars;
119 : : gsize input_length;
120 : 37 : gboolean success = FALSE;
121 : :
122 : : /* Convert from UTF-8 to Unicode code points */
123 : 37 : input = g_utf8_to_ucs4 (input_utf8, input_utf8_length, NULL,
124 : : &written_chars, NULL);
125 : 37 : if (!input)
126 : 0 : return FALSE;
127 : :
128 : 37 : input_length = (gsize) (written_chars > 0 ? written_chars : 0);
129 : :
130 : : /* Copy basic chars */
131 : 209 : for (j = num_basic_chars = 0; j < input_length; j++)
132 : : {
133 : 172 : if (PUNYCODE_IS_BASIC (input[j]))
134 : : {
135 : 18 : g_string_append_c (output, g_ascii_tolower (input[j]));
136 : 18 : num_basic_chars++;
137 : : }
138 : : }
139 : 37 : if (num_basic_chars)
140 : : g_string_append_c (output, '-');
141 : :
142 : 37 : handled_chars = num_basic_chars;
143 : :
144 : : /* Encode non-basic chars */
145 : 37 : delta = 0;
146 : 37 : bias = PUNYCODE_INITIAL_BIAS;
147 : 37 : n = PUNYCODE_INITIAL_N;
148 : 184 : while (handled_chars < input_length)
149 : : {
150 : : /* let m = the minimum {non-basic} code point >= n in the input */
151 : 991 : for (m = G_MAXUINT, j = 0; j < input_length; j++)
152 : : {
153 : 844 : if (input[j] >= n && input[j] < m)
154 : 235 : m = input[j];
155 : : }
156 : :
157 : 147 : if (m - n > (G_MAXUINT - delta) / (handled_chars + 1))
158 : 0 : goto fail;
159 : 147 : delta += (m - n) * (handled_chars + 1);
160 : 147 : n = m;
161 : :
162 : 991 : for (j = 0; j < input_length; j++)
163 : : {
164 : 844 : if (input[j] < n)
165 : : {
166 : 352 : if (++delta == 0)
167 : 0 : goto fail;
168 : : }
169 : 492 : else if (input[j] == n)
170 : : {
171 : 154 : q = delta;
172 : 154 : for (k = PUNYCODE_BASE; ; k += PUNYCODE_BASE)
173 : : {
174 : 297 : if (k <= bias)
175 : 76 : t = PUNYCODE_TMIN;
176 : 221 : else if (k >= bias + PUNYCODE_TMAX)
177 : 183 : t = PUNYCODE_TMAX;
178 : : else
179 : 38 : t = k - bias;
180 : 297 : if (q < t)
181 : 154 : break;
182 : 143 : digit = t + (q - t) % (PUNYCODE_BASE - t);
183 : 143 : g_string_append_c (output, encode_digit (digit));
184 : 143 : q = (q - t) / (PUNYCODE_BASE - t);
185 : : }
186 : :
187 : 154 : g_string_append_c (output, encode_digit (q));
188 : 154 : bias = adapt (delta, handled_chars + 1, handled_chars == num_basic_chars);
189 : 154 : delta = 0;
190 : 154 : handled_chars++;
191 : : }
192 : : }
193 : :
194 : 147 : delta++;
195 : 147 : n++;
196 : : }
197 : :
198 : 37 : success = TRUE;
199 : :
200 : 37 : fail:
201 : 37 : g_free (input);
202 : 37 : return success;
203 : : }
204 : :
205 : : /* From RFC 3454, Table B.1 */
206 : : #define idna_is_junk(ch) ((ch) == 0x00AD || (ch) == 0x1806 || (ch) == 0x200B || (ch) == 0x2060 || (ch) == 0xFEFF || (ch) == 0x034F || (ch) == 0x180B || (ch) == 0x180C || (ch) == 0x180D || (ch) == 0x200C || (ch) == 0x200D || ((ch) >= 0xFE00 && (ch) <= 0xFE0F))
207 : :
208 : : /* Scan @str for "junk" and return a cleaned-up string if any junk
209 : : * is found. Else return %NULL.
210 : : */
211 : : static gchar *
212 : 127 : remove_junk (const gchar *str,
213 : : gssize len)
214 : : {
215 : 127 : GString *cleaned = NULL;
216 : : const gchar *p;
217 : : gunichar ch;
218 : :
219 : 1460 : for (p = str; len == -1 ? *p : p < str + len; p = g_utf8_next_char (p))
220 : : {
221 : 1333 : ch = g_utf8_get_char (p);
222 : 1333 : if (idna_is_junk (ch))
223 : : {
224 : 1 : if (!cleaned)
225 : : {
226 : 1 : cleaned = g_string_new (NULL);
227 : 1 : g_string_append_len (cleaned, str, p - str);
228 : : }
229 : : }
230 : 1332 : else if (cleaned)
231 : 14 : g_string_append_unichar (cleaned, ch);
232 : : }
233 : :
234 : 127 : if (cleaned)
235 : 1 : return g_string_free (cleaned, FALSE);
236 : : else
237 : 126 : return NULL;
238 : : }
239 : :
240 : : static inline gboolean
241 : 188 : contains_uppercase_letters (const gchar *str,
242 : : gssize len)
243 : : {
244 : : const gchar *p;
245 : :
246 : 2004 : for (p = str; len == -1 ? *p : p < str + len; p = g_utf8_next_char (p))
247 : : {
248 : 1821 : if (g_unichar_isupper (g_utf8_get_char (p)))
249 : 5 : return TRUE;
250 : : }
251 : 183 : return FALSE;
252 : : }
253 : :
254 : : static inline gboolean
255 : 732 : contains_non_ascii (const gchar *str,
256 : : gssize len)
257 : : {
258 : : const gchar *p;
259 : :
260 : 5399 : for (p = str; len == -1 ? *p : p < str + len; p++)
261 : : {
262 : 4780 : if (!IS_ASCII (*p))
263 : 113 : return TRUE;
264 : : }
265 : 619 : return FALSE;
266 : : }
267 : :
268 : : /* RFC 3454, Appendix C. ish. */
269 : : static inline gboolean
270 : 522 : idna_is_prohibited (gunichar ch)
271 : : {
272 : 522 : switch (g_unichar_type (ch))
273 : : {
274 : 0 : case G_UNICODE_CONTROL:
275 : : case G_UNICODE_FORMAT:
276 : : case G_UNICODE_UNASSIGNED:
277 : : case G_UNICODE_PRIVATE_USE:
278 : : case G_UNICODE_SURROGATE:
279 : : case G_UNICODE_LINE_SEPARATOR:
280 : : case G_UNICODE_PARAGRAPH_SEPARATOR:
281 : : case G_UNICODE_SPACE_SEPARATOR:
282 : 0 : return TRUE;
283 : :
284 : 2 : case G_UNICODE_OTHER_SYMBOL:
285 : 2 : if (ch == 0xFFFC || ch == 0xFFFD ||
286 : 0 : (ch >= 0x2FF0 && ch <= 0x2FFB))
287 : 2 : return TRUE;
288 : 0 : return FALSE;
289 : :
290 : 12 : case G_UNICODE_NON_SPACING_MARK:
291 : 12 : if (ch == 0x0340 || ch == 0x0341)
292 : 0 : return TRUE;
293 : 12 : return FALSE;
294 : :
295 : 508 : default:
296 : 508 : return FALSE;
297 : : }
298 : : }
299 : :
300 : : /* RFC 3491 IDN cleanup algorithm. */
301 : : static gchar *
302 : 127 : nameprep (const gchar *hostname,
303 : : gssize len,
304 : : gboolean *is_unicode)
305 : : {
306 : : const char *name, *p;
307 : 127 : char *name_owned = NULL, *name_normalized = NULL;
308 : :
309 : : /* It would be nice if we could do this without repeatedly
310 : : * allocating strings and converting back and forth between
311 : : * gunichars and UTF-8... The code does at least avoid doing most of
312 : : * the sub-operations when they would just be equivalent to a
313 : : * g_strdup().
314 : : */
315 : :
316 : : /* Remove presentation-only characters */
317 : 127 : name = name_owned = remove_junk (hostname, len);
318 : 127 : if (name)
319 : 1 : len = -1;
320 : : else
321 : 126 : name = hostname;
322 : :
323 : : /* Convert to lowercase */
324 : 127 : if (contains_uppercase_letters (name, len))
325 : : {
326 : 4 : char *name_owned_lower = NULL;
327 : :
328 : 4 : name = name_owned_lower = g_utf8_strdown (name, len);
329 : 4 : g_free (name_owned);
330 : 4 : name_owned = g_steal_pointer (&name_owned_lower);
331 : 4 : len = -1;
332 : : }
333 : :
334 : : /* If there are no UTF8 characters, we're done. */
335 : 127 : if (!contains_non_ascii (name, len))
336 : : {
337 : 62 : *is_unicode = FALSE;
338 : 62 : if (name == hostname)
339 : 86 : return len == -1 ? g_strdup (hostname) : g_strndup (hostname, len);
340 : : else
341 : 1 : return g_steal_pointer (&name_owned);
342 : : }
343 : :
344 : 65 : *is_unicode = TRUE;
345 : :
346 : : /* Normalize */
347 : 65 : name = name_normalized = g_utf8_normalize (name, len, G_NORMALIZE_NFKC);
348 : 65 : g_free (name_owned);
349 : 65 : name_owned = g_steal_pointer (&name_normalized);
350 : 65 : len = -1;
351 : :
352 : 65 : if (!name)
353 : 4 : return NULL;
354 : :
355 : : /* KC normalization may have created more capital letters (eg,
356 : : * angstrom -> capital A with ring). So we have to lowercasify a
357 : : * second time. (This is more-or-less how the nameprep algorithm
358 : : * does it. If tolower(nfkc(tolower(X))) is guaranteed to be the
359 : : * same as tolower(nfkc(X)), then we could skip the first tolower,
360 : : * but I'm not sure it is.)
361 : : */
362 : 61 : if (contains_uppercase_letters (name, len))
363 : : {
364 : 1 : char *name_owned_lower = NULL;
365 : :
366 : 1 : name = name_owned_lower = g_utf8_strdown (name, len);
367 : 1 : g_free (name_owned);
368 : 1 : name_owned = g_steal_pointer (&name_owned_lower);
369 : 1 : len = -1;
370 : : }
371 : :
372 : : /* Check for prohibited characters */
373 : 581 : for (p = name; *p; p = g_utf8_next_char (p))
374 : : {
375 : 522 : if (idna_is_prohibited (g_utf8_get_char (p)))
376 : : {
377 : 2 : name = NULL;
378 : 2 : g_clear_pointer (&name_owned, g_free);
379 : 2 : len = -1;
380 : 2 : goto done;
381 : : }
382 : : }
383 : :
384 : : /* FIXME: We're supposed to verify certain constraints on bidi
385 : : * characters, but glib does not appear to have that information.
386 : : */
387 : :
388 : 59 : done:
389 : 61 : return g_steal_pointer (&name_owned);
390 : : }
391 : :
392 : : /* RFC 3490, section 3.1 says '.', 0x3002, 0xFF0E, and 0xFF61 count as
393 : : * label-separating dots. @str must be '\0'-terminated.
394 : : */
395 : : #define idna_is_dot(str) ( \
396 : : ((guchar)(str)[0] == '.') || \
397 : : ((guchar)(str)[0] == 0xE3 && (guchar)(str)[1] == 0x80 && (guchar)(str)[2] == 0x82) || \
398 : : ((guchar)(str)[0] == 0xEF && (guchar)(str)[1] == 0xBC && (guchar)(str)[2] == 0x8E) || \
399 : : ((guchar)(str)[0] == 0xEF && (guchar)(str)[1] == 0xBD && (guchar)(str)[2] == 0xA1) )
400 : :
401 : : static const gchar *
402 : 104 : idna_end_of_label (const gchar *str)
403 : : {
404 : 850 : for (; *str; str = g_utf8_next_char (str))
405 : : {
406 : 808 : if (idna_is_dot (str))
407 : 62 : return str;
408 : : }
409 : 42 : return str;
410 : : }
411 : :
412 : : static gsize
413 : 100 : get_hostname_max_length_bytes (void)
414 : : {
415 : : #if defined(G_OS_WIN32)
416 : : wchar_t tmp[MAX_COMPUTERNAME_LENGTH];
417 : : return sizeof (tmp) / sizeof (tmp[0]);
418 : : #elif defined(_SC_HOST_NAME_MAX)
419 : 100 : glong max = sysconf (_SC_HOST_NAME_MAX);
420 : 100 : if (max > 0)
421 : 100 : return (gsize) max;
422 : :
423 : : #ifdef HOST_NAME_MAX
424 : 0 : return HOST_NAME_MAX;
425 : : #else
426 : : return _POSIX_HOST_NAME_MAX;
427 : : #endif /* HOST_NAME_MAX */
428 : : #else
429 : : /* Fallback to some reasonable value
430 : : * See https://stackoverflow.com/questions/8724954/what-is-the-maximum-number-of-characters-for-a-host-name-in-unix/28918017#28918017 */
431 : : return 255;
432 : : #endif
433 : : }
434 : :
435 : : /* Returns %TRUE if `strlen (str) > comparison_length`, but without actually
436 : : * running `strlen(str)`, as that would take a very long time for long
437 : : * (untrusted) input strings. */
438 : : static gboolean
439 : 100 : strlen_greater_than (const gchar *str,
440 : : gsize comparison_length)
441 : : {
442 : : gsize i;
443 : :
444 : 4386 : for (i = 0; str[i] != '\0'; i++)
445 : 4288 : if (i > comparison_length)
446 : 2 : return TRUE;
447 : :
448 : 98 : return FALSE;
449 : : }
450 : :
451 : : /**
452 : : * g_hostname_to_ascii:
453 : : * @hostname: a valid UTF-8 or ASCII hostname
454 : : *
455 : : * Converts @hostname to its canonical ASCII form; an ASCII-only
456 : : * string containing no uppercase letters and not ending with a
457 : : * trailing dot.
458 : : *
459 : : * Returns: (nullable) (transfer full): an ASCII hostname, which must be freed,
460 : : * or %NULL if @hostname is in some way invalid.
461 : : *
462 : : * Since: 2.22
463 : : **/
464 : : gchar *
465 : 61 : g_hostname_to_ascii (const gchar *hostname)
466 : : {
467 : : gchar *name, *label, *p;
468 : : GString *out;
469 : : gssize llen, oldlen;
470 : : gboolean unicode;
471 : 61 : gsize hostname_max_length_bytes = get_hostname_max_length_bytes ();
472 : :
473 : : /* Do an initial check on the hostname length, as overlong hostnames take a
474 : : * long time in the IDN cleanup algorithm in nameprep(). The ultimate
475 : : * restriction is that the IDN-decoded (i.e. pure ASCII) hostname cannot be
476 : : * longer than 255 bytes. That’s the least restrictive limit on hostname
477 : : * length of all the ways hostnames can be interpreted. Typically, the
478 : : * hostname will be an FQDN, which is limited to 253 bytes long. POSIX
479 : : * hostnames are limited to `get_hostname_max_length_bytes()` (typically 255
480 : : * bytes).
481 : : *
482 : : * See https://stackoverflow.com/a/28918017/2931197
483 : : *
484 : : * It’s possible for a hostname to be %-encoded, in which case its decoded
485 : : * length will be as much as 3× shorter.
486 : : *
487 : : * It’s also possible for a hostname to use overlong UTF-8 encodings, in which
488 : : * case its decoded length will be as much as 4× shorter.
489 : : *
490 : : * Note: This check is not intended as an absolute guarantee that a hostname
491 : : * is the right length and will be accepted by other systems. It’s intended to
492 : : * stop wildly-invalid hostnames from taking forever in nameprep().
493 : : */
494 : 122 : if (hostname_max_length_bytes <= G_MAXSIZE / 4 &&
495 : 61 : strlen_greater_than (hostname, 4 * MAX (255, hostname_max_length_bytes)))
496 : 1 : return NULL;
497 : :
498 : 60 : label = name = nameprep (hostname, -1, &unicode);
499 : 60 : if (!name || !unicode)
500 : 29 : return name;
501 : :
502 : 31 : out = g_string_new (NULL);
503 : :
504 : : do
505 : : {
506 : 72 : unicode = FALSE;
507 : 622 : for (p = label; *p && !idna_is_dot (p); p++)
508 : : {
509 : 550 : if (!IS_ASCII (*p))
510 : 386 : unicode = TRUE;
511 : : }
512 : :
513 : 72 : oldlen = out->len;
514 : 72 : llen = p - label;
515 : 72 : if (unicode)
516 : : {
517 : 39 : if (!strncmp (label, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
518 : 2 : goto fail;
519 : :
520 : 37 : g_string_append (out, IDNA_ACE_PREFIX);
521 : 37 : if (!punycode_encode (label, llen, out))
522 : 0 : goto fail;
523 : : }
524 : : else
525 : : g_string_append_len (out, label, llen);
526 : :
527 : 70 : if (out->len - oldlen > 63)
528 : 0 : goto fail;
529 : :
530 : 70 : label += llen;
531 : 70 : if (*label)
532 : 41 : label = g_utf8_next_char (label);
533 : 70 : if (*label)
534 : : g_string_append_c (out, '.');
535 : : }
536 : 70 : while (*label);
537 : :
538 : 29 : g_free (name);
539 : 29 : return g_string_free (out, FALSE);
540 : :
541 : 2 : fail:
542 : 2 : g_free (name);
543 : 2 : g_string_free (out, TRUE);
544 : 2 : return NULL;
545 : : }
546 : :
547 : : /**
548 : : * g_hostname_is_non_ascii:
549 : : * @hostname: a hostname
550 : : *
551 : : * Tests if @hostname contains Unicode characters. If this returns
552 : : * %TRUE, you need to encode the hostname with g_hostname_to_ascii()
553 : : * before using it in non-IDN-aware contexts.
554 : : *
555 : : * Note that a hostname might contain a mix of encoded and unencoded
556 : : * segments, and so it is possible for g_hostname_is_non_ascii() and
557 : : * g_hostname_is_ascii_encoded() to both return %TRUE for a name.
558 : : *
559 : : * Returns: %TRUE if @hostname contains any non-ASCII characters
560 : : *
561 : : * Since: 2.22
562 : : **/
563 : : gboolean
564 : 605 : g_hostname_is_non_ascii (const gchar *hostname)
565 : : {
566 : 605 : return contains_non_ascii (hostname, -1);
567 : : }
568 : :
569 : : /* Punycode decoder, RFC 3492 section 6.2. As with punycode_encode(),
570 : : * read the RFC if you want to understand what this is actually doing.
571 : : */
572 : : static gboolean
573 : 29 : punycode_decode (const gchar *input,
574 : : gsize input_length,
575 : : GString *output)
576 : : {
577 : : GArray *output_chars;
578 : : gunichar n;
579 : : guint i, bias;
580 : : guint oldi, w, k, digit, t;
581 : : const gchar *split;
582 : :
583 : 29 : n = PUNYCODE_INITIAL_N;
584 : 29 : i = 0;
585 : 29 : bias = PUNYCODE_INITIAL_BIAS;
586 : :
587 : 29 : split = input + input_length - 1;
588 : 268 : while (split > input && *split != '-')
589 : 239 : split--;
590 : 29 : if (split > input)
591 : : {
592 : : g_assert ((guint) (split - input) <= G_MAXUINT);
593 : :
594 : 1 : output_chars = g_array_sized_new (FALSE, FALSE, sizeof (gunichar),
595 : 1 : (guint) (split - input));
596 : 1 : input_length -= (split - input) + 1;
597 : 6 : while (input < split)
598 : : {
599 : 5 : gunichar ch = (gunichar)*input++;
600 : 5 : if (!PUNYCODE_IS_BASIC (ch))
601 : 0 : goto fail;
602 : 5 : g_array_append_val (output_chars, ch);
603 : : }
604 : 1 : input++;
605 : : }
606 : : else
607 : 28 : output_chars = g_array_new (FALSE, FALSE, sizeof (gunichar));
608 : :
609 : 168 : while (input_length)
610 : : {
611 : 140 : oldi = i;
612 : 140 : w = 1;
613 : 140 : for (k = PUNYCODE_BASE; ; k += PUNYCODE_BASE)
614 : : {
615 : 265 : if (!input_length--)
616 : 0 : goto fail;
617 : 265 : digit = decode_digit (*input++);
618 : 265 : if (digit >= PUNYCODE_BASE)
619 : 1 : goto fail;
620 : 264 : if (digit > (G_MAXUINT - i) / w)
621 : 0 : goto fail;
622 : 264 : i += digit * w;
623 : 264 : if (k <= bias)
624 : 58 : t = PUNYCODE_TMIN;
625 : 206 : else if (k >= bias + PUNYCODE_TMAX)
626 : 168 : t = PUNYCODE_TMAX;
627 : : else
628 : 38 : t = k - bias;
629 : 264 : if (digit < t)
630 : 139 : break;
631 : 125 : if (w > G_MAXUINT / (PUNYCODE_BASE - t))
632 : 0 : goto fail;
633 : 125 : w *= (PUNYCODE_BASE - t);
634 : : }
635 : :
636 : 139 : bias = adapt (i - oldi, output_chars->len + 1, oldi == 0);
637 : :
638 : 139 : if (i / (output_chars->len + 1) > G_MAXUINT - n)
639 : 0 : goto fail;
640 : 139 : n += i / (output_chars->len + 1);
641 : 139 : i %= (output_chars->len + 1);
642 : :
643 : 139 : g_array_insert_val (output_chars, i++, n);
644 : : }
645 : :
646 : 167 : for (i = 0; i < output_chars->len; i++)
647 : 139 : g_string_append_unichar (output, g_array_index (output_chars, gunichar, i));
648 : 28 : g_array_free (output_chars, TRUE);
649 : 28 : return TRUE;
650 : :
651 : 1 : fail:
652 : 1 : g_array_free (output_chars, TRUE);
653 : 1 : return FALSE;
654 : : }
655 : :
656 : : /**
657 : : * g_hostname_to_unicode:
658 : : * @hostname: a valid UTF-8 or ASCII hostname
659 : : *
660 : : * Converts @hostname to its canonical presentation form; a UTF-8
661 : : * string in Unicode normalization form C, containing no uppercase
662 : : * letters, no forbidden characters, and no ASCII-encoded segments,
663 : : * and not ending with a trailing dot.
664 : : *
665 : : * Of course if @hostname is not an internationalized hostname, then
666 : : * the canonical presentation form will be entirely ASCII.
667 : : *
668 : : * Returns: (nullable) (transfer full): a UTF-8 hostname, which must be freed,
669 : : * or %NULL if @hostname is in some way invalid.
670 : : *
671 : : * Since: 2.22
672 : : **/
673 : : gchar *
674 : 39 : g_hostname_to_unicode (const gchar *hostname)
675 : : {
676 : : GString *out;
677 : : gssize llen;
678 : 39 : gsize hostname_max_length_bytes = get_hostname_max_length_bytes ();
679 : :
680 : 39 : g_return_val_if_fail (hostname != NULL, NULL);
681 : :
682 : : /* See the comment at the top of g_hostname_to_ascii(). */
683 : 78 : if (hostname_max_length_bytes <= G_MAXSIZE / 4 &&
684 : 39 : strlen_greater_than (hostname, 4 * MAX (255, hostname_max_length_bytes)))
685 : 1 : return NULL;
686 : :
687 : 38 : out = g_string_new (NULL);
688 : :
689 : : do
690 : : {
691 : 96 : llen = idna_end_of_label (hostname) - hostname;
692 : 96 : if (!g_ascii_strncasecmp (hostname, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
693 : : {
694 : 29 : hostname += IDNA_ACE_PREFIX_LEN;
695 : 29 : llen -= IDNA_ACE_PREFIX_LEN;
696 : 29 : if (!punycode_decode (hostname, llen, out))
697 : : {
698 : 1 : g_string_free (out, TRUE);
699 : 1 : return NULL;
700 : : }
701 : : }
702 : : else
703 : : {
704 : : gboolean unicode;
705 : 67 : gchar *canonicalized = nameprep (hostname, llen, &unicode);
706 : :
707 : 67 : if (!canonicalized)
708 : : {
709 : 3 : g_string_free (out, TRUE);
710 : 3 : return NULL;
711 : : }
712 : : g_string_append (out, canonicalized);
713 : 64 : g_free (canonicalized);
714 : : }
715 : :
716 : 92 : hostname += llen;
717 : 92 : if (*hostname)
718 : 58 : hostname = g_utf8_next_char (hostname);
719 : 92 : if (*hostname)
720 : : g_string_append_c (out, '.');
721 : : }
722 : 92 : while (*hostname);
723 : :
724 : 34 : return g_string_free (out, FALSE);
725 : : }
726 : :
727 : : /**
728 : : * g_hostname_is_ascii_encoded:
729 : : * @hostname: a hostname
730 : : *
731 : : * Tests if @hostname contains segments with an ASCII-compatible
732 : : * encoding of an Internationalized Domain Name. If this returns
733 : : * %TRUE, you should decode the hostname with g_hostname_to_unicode()
734 : : * before displaying it to the user.
735 : : *
736 : : * Note that a hostname might contain a mix of encoded and unencoded
737 : : * segments, and so it is possible for g_hostname_is_non_ascii() and
738 : : * g_hostname_is_ascii_encoded() to both return %TRUE for a name.
739 : : *
740 : : * Returns: %TRUE if @hostname contains any ASCII-encoded
741 : : * segments.
742 : : *
743 : : * Since: 2.22
744 : : **/
745 : : gboolean
746 : 25 : g_hostname_is_ascii_encoded (const gchar *hostname)
747 : : {
748 : : while (1)
749 : : {
750 : 29 : if (!g_ascii_strncasecmp (hostname, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
751 : 21 : return TRUE;
752 : 8 : hostname = idna_end_of_label (hostname);
753 : 8 : if (*hostname)
754 : 4 : hostname = g_utf8_next_char (hostname);
755 : 8 : if (!*hostname)
756 : 4 : return FALSE;
757 : : }
758 : : }
759 : :
760 : : /**
761 : : * g_hostname_is_ip_address:
762 : : * @hostname: a hostname (or IP address in string form)
763 : : *
764 : : * Tests if @hostname is the string form of an IPv4 or IPv6 address.
765 : : * (Eg, "192.168.0.1".)
766 : : *
767 : : * Since 2.66, IPv6 addresses with a zone-id are accepted (RFC6874).
768 : : *
769 : : * Returns: %TRUE if @hostname is an IP address
770 : : *
771 : : * Since: 2.22
772 : : **/
773 : : gboolean
774 : 1716 : g_hostname_is_ip_address (const gchar *hostname)
775 : : {
776 : : gchar *p, *end;
777 : : gint nsegments, octet;
778 : :
779 : : /* On Linux we could implement this using inet_pton, but the Windows
780 : : * equivalent of that requires linking against winsock, so we just
781 : : * figure this out ourselves. Tested by tests/hostutils.c.
782 : : */
783 : :
784 : 1716 : p = (char *)hostname;
785 : :
786 : 1716 : if (strchr (p, ':'))
787 : : {
788 : : gboolean skipped;
789 : :
790 : : /* If it contains a ':', it's an IPv6 address (assuming it's an
791 : : * IP address at all). This consists of eight ':'-separated
792 : : * segments, each containing a 1-4 digit hex number, except that
793 : : * optionally: (a) the last two segments can be replaced by an
794 : : * IPv4 address, and (b) a single span of 1 to 8 "0000" segments
795 : : * can be replaced with just "::".
796 : : */
797 : :
798 : 401 : nsegments = 0;
799 : 401 : skipped = FALSE;
800 : 1864 : while (*p && *p != '%' && nsegments < 8)
801 : : {
802 : : /* Each segment after the first must be preceded by a ':'.
803 : : * (We also handle half of the "string starts with ::" case
804 : : * here.)
805 : : */
806 : 1507 : if (p != (char *)hostname || (p[0] == ':' && p[1] == ':'))
807 : : {
808 : 1194 : if (*p != ':')
809 : 9 : return FALSE;
810 : 1185 : p++;
811 : : }
812 : :
813 : : /* If there's another ':', it means we're skipping some segments */
814 : 1498 : if (*p == ':' && !skipped)
815 : : {
816 : 365 : skipped = TRUE;
817 : 365 : nsegments++;
818 : :
819 : : /* Handle the "string ends with ::" case */
820 : 365 : if (!p[1])
821 : 75 : p++;
822 : :
823 : 365 : continue;
824 : : }
825 : :
826 : : /* Read the segment, make sure it's valid. */
827 : 5013 : for (end = p; g_ascii_isxdigit (*end); end++)
828 : : ;
829 : 1133 : if (end == p || end > p + 4)
830 : 17 : return FALSE;
831 : :
832 : 1116 : if (*end == '.')
833 : : {
834 : 18 : if ((nsegments == 6 && !skipped) || (nsegments <= 6 && skipped))
835 : 12 : goto parse_ipv4;
836 : : else
837 : 6 : return FALSE;
838 : : }
839 : :
840 : 1098 : nsegments++;
841 : 1098 : p = end;
842 : : }
843 : :
844 : 357 : return (!*p || (p[0] == '%' && p[1])) && (nsegments == 8 || skipped);
845 : : }
846 : :
847 : 1315 : parse_ipv4:
848 : :
849 : : /* Parse IPv4: N.N.N.N, where each N <= 255 and doesn't have leading 0s. */
850 : 4729 : for (nsegments = 0; nsegments < 4; nsegments++)
851 : : {
852 : 3887 : if (nsegments != 0)
853 : : {
854 : 2560 : if (*p != '.')
855 : 13 : return FALSE;
856 : 2547 : p++;
857 : : }
858 : :
859 : : /* Check the segment; a little tricker than the IPv6 case since
860 : : * we can't allow extra leading 0s, and we can't assume that all
861 : : * strings of valid length are within range.
862 : : */
863 : 3874 : octet = 0;
864 : 3874 : if (*p == '0')
865 : 1341 : end = p + 1;
866 : : else
867 : : {
868 : 7384 : for (end = p; g_ascii_isdigit (*end); end++)
869 : : {
870 : 4855 : octet = 10 * octet + (*end - '0');
871 : :
872 : 4855 : if (octet > 255)
873 : 4 : break;
874 : : }
875 : : }
876 : 3874 : if (end == p || end > p + 3 || octet > 255)
877 : 472 : return FALSE;
878 : :
879 : 3402 : p = end;
880 : : }
881 : :
882 : : /* If there's nothing left to parse, then it's ok. */
883 : 842 : return !*p;
884 : : }
|