LCOV - code coverage report
Current view: top level - glib - ghostutils.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 94.8 % 327 310
Test Date: 2026-01-13 05:17:23 Functions: 100.0 % 18 18
Branches: - 0 0

             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                 :             : }
        

Generated by: LCOV version 2.0-1