LCOV - code coverage report
Current view: top level - glib/glib - ghostutils.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 302 322 93.8 %
Date: 2024-04-16 05:15:53 Functions: 18 18 100.0 %
Branches: 270 334 80.8 %

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

Generated by: LCOV version 1.14