(root)/
glibc-2.38/
resolv/
tst-resolv-invalid-cname.c
       1  /* Test handling of CNAMEs with non-host domain names (bug 12154).
       2     Copyright (C) 2022-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  #include <errno.h>
      20  #include <netdb.h>
      21  #include <resolv.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <support/check.h>
      25  #include <support/check_nss.h>
      26  #include <support/resolv_test.h>
      27  #include <support/support.h>
      28  #include <support/xmemstream.h>
      29  
      30  /* Query strings describe the CNAME chain in the response.  They have
      31     the format "bitsBITS.countCOUNT.example.", where BITS and COUNT are
      32     replaced by unsigned decimal numbers.  COUNT is the number of CNAME
      33     records in the response.  BITS has two bits for each CNAME record,
      34     describing a special prefix that is added to that CNAME.
      35  
      36     0: No special leading label.
      37     1: Starting with "*.".
      38     2: Starting with "-x.".
      39     3: Starting with "star.*.".
      40  
      41     The first CNAME in the response using the two least significant
      42     bits.
      43  
      44     For PTR queries, the QNAME format is different, it is either
      45     COUNT.BITS.168.192.in-addr.arpa. (with BITS and COUNT still
      46     decimal), or:
      47  
      48  COUNT.BITS0.BITS1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
      49  
      50     where BITS and COUNT are hexadecimal.  */
      51  
      52  static void
      53  response (const struct resolv_response_context *ctx,
      54            struct resolv_response_builder *b,
      55            const char *qname, uint16_t qclass, uint16_t qtype)
      56  {
      57    TEST_COMPARE (qclass, C_IN);
      58  
      59    /* The only other query type besides A is PTR.  */
      60    if (qtype != T_A && qtype != T_AAAA)
      61      TEST_COMPARE (qtype, T_PTR);
      62  
      63    unsigned int bits, bits1, count;
      64    char *tail = NULL;
      65    if (sscanf (qname, "bits%u.count%u.%ms", &bits, &count, &tail) == 3)
      66      TEST_COMPARE_STRING (tail, "example");
      67    else if (strstr (qname, "in-addr.arpa") != NULL
      68             && sscanf (qname, "%u.%u.%ms", &bits, &count, &tail) == 3)
      69      TEST_COMPARE_STRING (tail, "168.192.in-addr.arpa");
      70    else if (sscanf (qname, "%x.%x.%x.%ms", &bits, &bits1, &count, &tail) == 4)
      71      {
      72        TEST_COMPARE_STRING (tail, "\
      73  0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
      74        bits |= bits1 << 4;
      75      }
      76    else
      77      FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
      78    free (tail);
      79  
      80    struct resolv_response_flags flags = {};
      81    resolv_response_init (b, flags);
      82    resolv_response_add_question (b, qname, qclass, qtype);
      83    resolv_response_section (b, ns_s_an);
      84  
      85    /* Provide the requested number of CNAME records.  */
      86    char *previous_name = (char *) qname;
      87    unsigned int original_bits = bits;
      88    for (int unique = 0; unique < count; ++unique)
      89      {
      90        resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
      91  
      92        static const char bits_to_prefix[4][8] = { "", "*.", "-x.", "star.*." };
      93        char *new_name = xasprintf ("%sunique%d.example",
      94                                    bits_to_prefix[bits & 3], unique);
      95        bits >>= 2;
      96        resolv_response_add_name (b, new_name);
      97        resolv_response_close_record (b);
      98  
      99        if (previous_name != qname)
     100          free (previous_name);
     101        previous_name = new_name;
     102      }
     103  
     104    /* Actual answer record.  */
     105    resolv_response_open_record (b, previous_name, qclass, qtype, 60);
     106    switch (qtype)
     107      {
     108      case T_A:
     109        {
     110          char ipv4[4] = {192, 168, count, original_bits};
     111          resolv_response_add_data (b, &ipv4, sizeof (ipv4));
     112        }
     113        break;
     114      case T_AAAA:
     115        {
     116          char ipv6[16] =
     117            {
     118              0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     119              count, original_bits
     120            };
     121          resolv_response_add_data (b, &ipv6, sizeof (ipv6));
     122        }
     123        break;
     124  
     125      case T_PTR:
     126        {
     127          char *name = xasprintf ("bits%u.count%u.example",
     128                                  original_bits, count);
     129          resolv_response_add_name (b, name);
     130          free (name);
     131        }
     132        break;
     133      }
     134    resolv_response_close_record (b);
     135  
     136    if (previous_name != qname)
     137      free (previous_name);
     138  }
     139  
     140  /* Controls which name resolution function is invoked.  */
     141  enum test_mode
     142    {
     143      byname,                     /* gethostbyname.  */
     144      byname2,                    /* gethostbyname2.  */
     145      gai,                        /* getaddrinfo without AI_CANONNAME.  */
     146      gai_canon,                  /* getaddrinfo with AI_CANONNAME.  */
     147  
     148      test_mode_num               /* Number of enum values.  */
     149    };
     150  
     151  static const char *
     152  test_mode_to_string (enum test_mode mode)
     153  {
     154    switch (mode)
     155      {
     156      case byname:
     157        return "byname";
     158      case byname2:
     159        return "byname2";
     160      case gai:
     161        return "gai";
     162      case gai_canon:
     163        return "gai_canon";
     164      case test_mode_num:
     165        break;                    /* Report error below.  */
     166      }
     167    FAIL_EXIT1 ("invalid test_mode: %d", mode);
     168  }
     169  
     170  /* Append the name and aliases to OUT.  */
     171  static void
     172  append_names (FILE *out, const char *qname, int bits, int count,
     173                enum test_mode mode)
     174  {
     175    /* Largest valid index which has a corresponding zero in bits
     176       (meaning a syntactically valid CNAME).  */
     177    int last_valid_cname = -1;
     178  
     179    for (int i = 0; i < count; ++i)
     180      if ((bits & (3 << (i * 2))) == 0)
     181        last_valid_cname = i;
     182  
     183    if (mode != gai)
     184      {
     185        const char *label;
     186        if (mode == gai_canon)
     187          label = "canonname";
     188        else
     189          label = "name";
     190        if (last_valid_cname >= 0)
     191          fprintf (out, "%s: unique%d.example\n", label, last_valid_cname);
     192        else
     193          fprintf (out, "%s: %s\n", label, qname);
     194      }
     195  
     196    if (mode == byname || mode == byname2)
     197      {
     198        if (last_valid_cname >= 0)
     199          fprintf (out, "alias: %s\n", qname);
     200        for (int i = 0; i < count; ++i)
     201          {
     202            if ((bits & (3 << (i * 2))) == 0 && i != last_valid_cname)
     203              fprintf (out, "alias: unique%d.example\n", i);
     204          }
     205      }
     206  }
     207  
     208  /* Append the address information to OUT.  */
     209  static void
     210  append_addresses (FILE *out, int af, int bits, int count, enum test_mode mode)
     211  {
     212    int last = count * 256 + bits;
     213    if (mode == gai || mode == gai_canon)
     214      {
     215        if (af == AF_INET || af == AF_UNSPEC)
     216          fprintf (out, "address: STREAM/TCP 192.168.%d.%d 80\n", count, bits);
     217        if (af == AF_INET6 || af == AF_UNSPEC)
     218          {
     219            if (last == 0)
     220              fprintf (out, "address: STREAM/TCP 2001:db8:: 80\n");
     221            else
     222              fprintf (out, "address: STREAM/TCP 2001:db8::%x 80\n", last);
     223          }
     224      }
     225    else
     226      {
     227        TEST_VERIFY (af != AF_UNSPEC);
     228        if (af == AF_INET)
     229          fprintf (out, "address: 192.168.%d.%d\n", count, bits);
     230        if (af == AF_INET6)
     231          {
     232            if (last == 0)
     233              fprintf (out, "address: 2001:db8::\n");
     234            else
     235              fprintf (out, "address: 2001:db8::%x\n", last);
     236          }
     237      }
     238  }
     239  
     240  /* Perform one test using a forward lookup.  */
     241  static void
     242  check_forward (int af, int bits, int count, enum test_mode mode)
     243  {
     244    char *qname = xasprintf ("bits%d.count%d.example", bits, count);
     245    char *label = xasprintf ("af=%d bits=%d count=%d mode=%s qname=%s",
     246                             af, bits, count, test_mode_to_string (mode), qname);
     247  
     248    struct xmemstream expected;
     249    xopen_memstream (&expected);
     250    if (mode == gai_canon)
     251      fprintf (expected.out, "flags: AI_CANONNAME\n");
     252    append_names (expected.out, qname, bits, count, mode);
     253    append_addresses (expected.out, af, bits, count, mode);
     254    xfclose_memstream (&expected);
     255  
     256    if (mode == gai || mode == gai_canon)
     257      {
     258        struct addrinfo *ai;
     259        struct addrinfo hints =
     260          {
     261            .ai_family = af,
     262            .ai_socktype = SOCK_STREAM,
     263          };
     264        if (mode == gai_canon)
     265          hints.ai_flags |= AI_CANONNAME;
     266        int ret = getaddrinfo (qname, "80", &hints, &ai);
     267        check_addrinfo (label, ai, ret, expected.buffer);
     268        if (ret == 0)
     269          freeaddrinfo (ai);
     270      }
     271    else
     272      {
     273        struct hostent *e;
     274        if (mode == gai)
     275          {
     276            TEST_COMPARE (af, AF_INET);
     277            e = gethostbyname (qname);
     278          }
     279        else
     280          {
     281            if (af != AF_INET)
     282              TEST_COMPARE (af, AF_INET6);
     283            e = gethostbyname2 (qname, af);
     284          }
     285        check_hostent (label, e, expected.buffer);
     286      }
     287  
     288    free (expected.buffer);
     289    free (label);
     290    free (qname);
     291  }
     292  
     293  /* Perform one check using a reverse lookup.  */
     294  
     295  static void
     296  check_reverse (int af, int bits, int count)
     297  {
     298    TEST_VERIFY (af == AF_INET || af == AF_INET6);
     299  
     300    char *label = xasprintf ("af=%d bits=%d count=%d", af, bits, count);
     301    char *fqdn = xasprintf ("bits%d.count%d.example", bits, count);
     302  
     303    struct xmemstream expected;
     304    xopen_memstream (&expected);
     305    fprintf (expected.out, "name: %s\n", fqdn);
     306    append_addresses (expected.out, af, bits, count, byname);
     307    xfclose_memstream (&expected);
     308  
     309    char addr[16] = { 0 };
     310    socklen_t addrlen;
     311    if (af == AF_INET)
     312      {
     313        addr[0] = 192;
     314        addr[1] = 168;
     315        addr[2] = count;
     316        addr[3] = bits;
     317        addrlen = 4;
     318      }
     319    else
     320      {
     321        addr[0] = 0x20;
     322        addr[1] = 0x01;
     323        addr[2] = 0x0d;
     324        addr[3] = 0xb8;
     325        addr[14] = count;
     326        addr[15] = bits;
     327        addrlen = 16;
     328      }
     329  
     330    struct hostent *e = gethostbyaddr (addr, addrlen, af);
     331    check_hostent (label, e, expected.buffer);
     332  
     333    /* getnameinfo check is different.  There is no generic check_*
     334       function for it.  */
     335    {
     336      struct sockaddr_in sin = { };
     337      struct sockaddr_in6 sin6 = { };
     338      void *sa;
     339      socklen_t salen;
     340      if (af == AF_INET)
     341        {
     342          sin.sin_family = AF_INET;
     343          memcpy (&sin.sin_addr, addr, addrlen);
     344          sin.sin_port = htons (80);
     345          sa = &sin;
     346          salen = sizeof (sin);
     347        }
     348      else
     349        {
     350          sin6.sin6_family = AF_INET6;
     351          memcpy (&sin6.sin6_addr, addr, addrlen);
     352          sin6.sin6_port = htons (80);
     353          sa = &sin6;
     354          salen = sizeof (sin6);
     355        }
     356  
     357      char host[64];
     358      char service[64];
     359      int ret = getnameinfo (sa, salen, host,
     360                             sizeof (host), service, sizeof (service),
     361                             NI_NAMEREQD | NI_NUMERICSERV);
     362      TEST_COMPARE (ret, 0);
     363      TEST_COMPARE_STRING (host, fqdn);
     364      TEST_COMPARE_STRING (service, "80");
     365    }
     366  
     367    free (expected.buffer);
     368    free (fqdn);
     369    free (label);
     370  }
     371  
     372  static int
     373  do_test (void)
     374  {
     375    struct resolv_test *obj = resolv_test_start
     376      ((struct resolv_redirect_config)
     377       {
     378         .response_callback = response
     379       });
     380  
     381    for (int count = 0; count <= 3; ++count)
     382      for (int bits = 0; bits <= 1 << (count * 2); ++bits)
     383        {
     384          if (count > 0 && bits == count)
     385            /* The last bits value is only checked if count == 0.  */
     386            continue;
     387  
     388          for (enum test_mode mode = 0; mode < test_mode_num; ++mode)
     389            {
     390              check_forward (AF_INET, bits, count, mode);
     391              if (mode != byname)
     392                check_forward (AF_INET6, bits, count, mode);
     393              if (mode == gai || mode == gai_canon)
     394                check_forward (AF_UNSPEC, bits, count, mode);
     395            }
     396  
     397          check_reverse (AF_INET, bits, count);
     398          check_reverse (AF_INET6, bits, count);
     399        }
     400  
     401    resolv_test_end (obj);
     402  
     403    return 0;
     404  }
     405  
     406  #include <support/test-driver.c>