Coverage Report

Created: 2026-06-30 06:26

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