(root)/
glibc-2.38/
resolv/
tst-ns_name.c
       1  /* Test ns_name-related functions.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This test program processes the tst-ns_name.data file.  */
      20  
      21  #include <ctype.h>
      22  #include <resolv.h>
      23  #include <stdbool.h>
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <support/check.h>
      28  #include <support/support.h>
      29  #include <support/xstdio.h>
      30  
      31  /* A byte buffer and its length.  */
      32  struct buffer
      33  {
      34    unsigned char *data;
      35    size_t length;
      36  };
      37  
      38  /* Convert a base64-encoded string to its binary representation.  */
      39  static bool
      40  base64_to_buffer (const char *base64, struct buffer *result)
      41  {
      42    /* "-" denotes an empty input.  */
      43    if (strcmp (base64, "-") == 0)
      44      {
      45        result->data = xmalloc (1);
      46        result->length = 0;
      47        return true;
      48      }
      49  
      50    size_t size = strlen (base64);
      51    unsigned char *data = xmalloc (size);
      52    int ret = b64_pton (base64, data, size);
      53    if (ret < 0 || ret > size)
      54      return false;
      55    result->data = xrealloc (data, ret);
      56    result->length = ret;
      57    return true;
      58  }
      59  
      60  /* A test case for ns_name_unpack and ns_name_ntop.  */
      61  struct test_case
      62  {
      63    char *path;
      64    size_t lineno;
      65    struct buffer input;
      66    size_t input_offset;
      67    int unpack_result;
      68    struct buffer unpack_output;
      69    int ntop_result;
      70    char *ntop_text;
      71  };
      72  
      73  /* Deallocate the buffers associated with the test case.  */
      74  static void
      75  free_test_case (struct test_case *t)
      76  {
      77    free (t->path);
      78    free (t->input.data);
      79    free (t->unpack_output.data);
      80    free (t->ntop_text);
      81  }
      82  
      83  /* Extract the test case information from a test file line.  */
      84  static bool
      85  parse_test_case (const char *path, size_t lineno, const char *line,
      86                   struct test_case *result)
      87  {
      88    memset (result, 0, sizeof (*result));
      89    result->path = xstrdup (path);
      90    result->lineno = lineno;
      91    result->ntop_result = -1;
      92    char *input = NULL;
      93    char *unpack_output = NULL;
      94    int ret = sscanf (line, "%ms %zu %d %ms %d %ms",
      95                      &input, &result->input_offset,
      96                      &result->unpack_result, &unpack_output,
      97                      &result->ntop_result, &result->ntop_text);
      98    if (ret < 3)
      99      {
     100        printf ("%s:%zu: error: missing input fields\n", path, lineno);
     101        free (input);
     102        return false;
     103      }
     104    if (!base64_to_buffer (input, &result->input))
     105      {
     106        printf ("%s:%zu: error: malformed base64 input data\n", path, lineno);
     107        free (input);
     108        free (unpack_output);
     109        free (result->ntop_text);
     110        return false;
     111      }
     112    free (input);
     113  
     114    if (unpack_output == NULL)
     115      result->unpack_output = (struct buffer) { NULL, 0 };
     116    else if (!base64_to_buffer (unpack_output, &result->unpack_output))
     117      {
     118        printf ("%s:%zu: error: malformed base64 unpack data\n", path, lineno);
     119        free (result->input.data);
     120        free (unpack_output);
     121        free (result->ntop_text);
     122        return false;
     123      }
     124    free (unpack_output);
     125  
     126    /* At this point, all allocated buffers have been transferred to
     127       *result.  */
     128  
     129    if (result->input_offset > result->input.length)
     130      {
     131        printf ("%s:%zu: error: input offset %zu exceeds buffer size %zu\n",
     132                path, lineno, result->input_offset, result->input.length);
     133        free_test_case (result);
     134        return false;
     135      }
     136    if (result->unpack_result < -1)
     137      {
     138        printf ("%s:%zu: error: invalid unpack result %d\n",
     139                path, lineno, result->unpack_result);
     140        free_test_case (result);
     141        return false;
     142      }
     143    if (result->ntop_result < -1)
     144      {
     145        printf ("%s:%zu: error: invalid ntop result %d\n",
     146                path, lineno, result->ntop_result);
     147        free_test_case (result);
     148        return false;
     149      }
     150  
     151    bool fields_consistent;
     152    switch (ret)
     153      {
     154      case 3:
     155        fields_consistent = result->unpack_result == -1;
     156        break;
     157      case 5:
     158        fields_consistent = result->unpack_result != -1
     159          && result->ntop_result == -1;
     160        break;
     161      case 6:
     162        fields_consistent = result->unpack_result != -1
     163          && result->ntop_result != -1;
     164        break;
     165      default:
     166        fields_consistent = false;
     167      }
     168    if (!fields_consistent)
     169      {
     170        printf ("%s:%zu: error: wrong number of fields: %d\n",
     171                path, lineno, ret);
     172        free_test_case (result);
     173        return false;
     174      }
     175    return true;
     176  }
     177  
     178  /* Format the buffer as a hexadecimal string and write it to standard
     179     output.  */
     180  static void
     181  print_hex (const char *label, struct buffer buffer)
     182  {
     183    printf ("  %s ", label);
     184    unsigned char *p = buffer.data;
     185    unsigned char *end = p + buffer.length;
     186    while (p < end)
     187      {
     188        printf ("%02X", *p & 0xFF);
     189        ++p;
     190      }
     191    putchar ('\n');
     192  }
     193  
     194  /* Run the test case specified in *T.  */
     195  static void
     196  run_test_case (struct test_case *t)
     197  {
     198    /* Test ns_name_unpack.  */
     199    unsigned char *unpacked = xmalloc (NS_MAXCDNAME);
     200    int consumed = ns_name_unpack
     201      (t->input.data, t->input.data + t->input.length,
     202       t->input.data + t->input_offset,
     203       unpacked, NS_MAXCDNAME);
     204    if (consumed != t->unpack_result)
     205      {
     206        support_record_failure ();
     207        printf ("%s:%zu: error: wrong result from ns_name_unpack\n"
     208                "  expected: %d\n"
     209                "  actual:   %d\n",
     210                t->path, t->lineno, t->unpack_result, consumed);
     211        return;
     212      }
     213    if (consumed != -1)
     214      {
     215        if (memcmp (unpacked, t->unpack_output.data,
     216                    t->unpack_output.length) != 0)
     217          {
     218            support_record_failure ();
     219            printf ("%s:%zu: error: wrong data from ns_name_unpack\n",
     220                    t->path, t->lineno);
     221            print_hex ("expected:", t->unpack_output);
     222            print_hex ("actual:  ",
     223                       (struct buffer) { unpacked, t->unpack_output.length });
     224            return;
     225          }
     226  
     227        /* Test ns_name_ntop.  */
     228        char *text = xmalloc (NS_MAXDNAME);
     229        int ret = ns_name_ntop (unpacked, text, NS_MAXDNAME);
     230        if (ret != t->ntop_result)
     231          {
     232            support_record_failure ();
     233            printf ("%s:%zu: error: wrong result from ns_name_top\n"
     234                    "  expected: %d\n"
     235                    "  actual:   %d\n",
     236                    t->path, t->lineno, t->ntop_result, ret);
     237            return;
     238          }
     239        if (ret != -1)
     240          {
     241            if (strcmp (text, t->ntop_text) != 0)
     242              {
     243                support_record_failure ();
     244                printf ("%s:%zu: error: wrong data from ns_name_ntop\n",
     245                        t->path, t->lineno);
     246                printf ("  expected: \"%s\"\n", t->ntop_text);
     247                printf ("  actual:   \"%s\"\n", text);
     248                return;
     249              }
     250  
     251            /* Test ns_name_pton.  Unpacking does not check the
     252               NS_MAXCDNAME limit, but packing does, so we need to
     253               adjust the expected result.  */
     254            int expected;
     255            if (t->unpack_output.length > NS_MAXCDNAME)
     256              expected = -1;
     257            else if (strcmp (text, ".") == 0)
     258              /* The root domain is fully qualified.  */
     259              expected = 1;
     260            else
     261              /* The domain name is never fully qualified.  */
     262              expected = 0;
     263            unsigned char *repacked = xmalloc (NS_MAXCDNAME);
     264            ret = ns_name_pton (text, repacked, NS_MAXCDNAME);
     265            if (ret != expected)
     266              {
     267                support_record_failure ();
     268                printf ("%s:%zu: error: wrong result from ns_name_pton\n"
     269                        "  expected: %d\n"
     270                        "  actual:   %d\n",
     271                        t->path, t->lineno, expected, ret);
     272                return;
     273              }
     274            if (ret >= 0
     275                && memcmp (repacked, unpacked, t->unpack_output.length) != 0)
     276              {
     277                support_record_failure ();
     278                printf ("%s:%zu: error: wrong data from ns_name_pton\n",
     279                        t->path, t->lineno);
     280                print_hex ("expected:", t->unpack_output);
     281                print_hex ("actual:  ",
     282                           (struct buffer) { repacked, t->unpack_output.length });
     283                return;
     284              }
     285  
     286            /* Test ns_name_compress, no compression case.  */
     287            if (t->unpack_output.length > NS_MAXCDNAME)
     288              expected = -1;
     289            else
     290              expected = t->unpack_output.length;
     291            memset (repacked, '$', NS_MAXCDNAME);
     292            {
     293              enum { ptr_count = 5 };
     294              const unsigned char *dnptrs[ptr_count] = { repacked, };
     295              ret = ns_name_compress (text, repacked, NS_MAXCDNAME,
     296                                      dnptrs, dnptrs + ptr_count);
     297              if (ret != expected)
     298                {
     299                  support_record_failure ();
     300                  printf ("%s:%zu: error: wrong result from ns_name_compress\n"
     301                          "  expected: %d\n"
     302                          "  actual:   %d\n",
     303                          t->path, t->lineno, expected, ret);
     304                  return;
     305                }
     306              if (ret < 0)
     307                {
     308                  TEST_VERIFY (dnptrs[0] == repacked);
     309                  TEST_VERIFY (dnptrs[1] == NULL);
     310                }
     311              else
     312                {
     313                  if (memcmp (repacked, unpacked, t->unpack_output.length) != 0)
     314                    {
     315                      support_record_failure ();
     316                      printf ("%s:%zu: error: wrong data from ns_name_compress\n",
     317                              t->path, t->lineno);
     318                      print_hex ("expected:", t->unpack_output);
     319                      print_hex ("actual:  ", (struct buffer) { repacked, ret });
     320                      return;
     321                    }
     322                  TEST_VERIFY (dnptrs[0] == repacked);
     323                  if (unpacked[0] == '\0')
     324                    /* The root domain is not a compression target.  */
     325                    TEST_VERIFY (dnptrs[1] == NULL);
     326                  else
     327                    {
     328                      TEST_VERIFY (dnptrs[1] == repacked);
     329                      TEST_VERIFY (dnptrs[2] == NULL);
     330                    }
     331                }
     332            }
     333  
     334            /* Test ns_name_compress, full compression case.  Skip this
     335               test for invalid names and the root domain.  */
     336            if (expected >= 0 && unpacked[0] != '\0')
     337              {
     338                /* The destination buffer needs additional room for the
     339                   offset, the initial name, and the compression
     340                   reference.  */
     341                enum { name_offset = 259 };
     342                size_t target_offset = name_offset + t->unpack_output.length;
     343                size_t repacked_size = target_offset + 2;
     344                repacked = xrealloc (repacked, repacked_size);
     345                memset (repacked, '@', repacked_size);
     346                memcpy (repacked + name_offset,
     347                        t->unpack_output.data, t->unpack_output.length);
     348                enum { ptr_count = 5 };
     349                const unsigned char *dnptrs[ptr_count]
     350                  = { repacked, repacked + name_offset, };
     351                ret = ns_name_compress
     352                  (text, repacked + target_offset, NS_MAXCDNAME,
     353                   dnptrs, dnptrs + ptr_count);
     354                if (ret != 2)
     355                  {
     356                    support_record_failure ();
     357                    printf ("%s:%zu: error: wrong result from ns_name_compress"
     358                            " (2)\n"
     359                            "  expected: 2\n"
     360                            "  actual:   %d\n",
     361                            t->path, t->lineno, ret);
     362                    return;
     363                  }
     364                if (memcmp (repacked + target_offset, "\xc1\x03", 2) != 0)
     365                  {
     366                    support_record_failure ();
     367                    printf ("%s:%zu: error: wrong data from ns_name_compress"
     368                            " (2)\n"
     369                            "  expected: C103\n",
     370                            t->path, t->lineno);
     371                    print_hex ("actual:  ",
     372                               (struct buffer) { repacked + target_offset, ret });
     373                    return;
     374                  }
     375                TEST_VERIFY (dnptrs[0] == repacked);
     376                TEST_VERIFY (dnptrs[1] == repacked + name_offset);
     377                TEST_VERIFY (dnptrs[2] == NULL);
     378              }
     379  
     380            free (repacked);
     381          }
     382        free (text);
     383      }
     384    free (unpacked);
     385  }
     386  
     387  /* Open the file at PATH, parse the test cases contained in it, and
     388     run them.  */
     389  static void
     390  run_test_file (const char *path)
     391  {
     392    FILE *fp = xfopen (path, "re");
     393    char *line = NULL;
     394    size_t line_allocated = 0;
     395    size_t lineno = 0;
     396  
     397    while (true)
     398      {
     399        ssize_t ret = getline (&line, &line_allocated, fp);
     400        if (ret < 0)
     401          {
     402            if (ferror (fp))
     403              {
     404                printf ("%s: error reading file: %m\n", path);
     405                exit (1);
     406              }
     407            TEST_VERIFY (feof (fp));
     408            break;
     409          }
     410  
     411        ++lineno;
     412        char *p = line;
     413        while (isspace (*p))
     414          ++p;
     415        if (*p == '\0' || *p == '#')
     416          continue;
     417  
     418        struct test_case test_case;
     419        if (!parse_test_case (path, lineno, line, &test_case))
     420          {
     421            support_record_failure ();
     422            continue;
     423          }
     424        run_test_case (&test_case);
     425        free_test_case (&test_case);
     426      }
     427    free (line);
     428    xfclose (fp);
     429  }
     430  
     431  static int
     432  do_test (void)
     433  {
     434    run_test_file ("tst-ns_name.data");
     435    return 0;
     436  }
     437  
     438  #include <support/test-driver.c>