(root)/
glibc-2.38/
resolv/
tst-resolv-aliases.c
       1  /* Test alias handling (mainly for gethostbyname).
       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 <array_length.h>
      20  #include <arpa/inet.h>
      21  #include <netdb.h>
      22  #include <stdbool.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <support/check.h>
      27  #include <support/check_nss.h>
      28  #include <support/resolv_test.h>
      29  #include <support/support.h>
      30  
      31  #include "tst-resolv-maybe_insert_sig.h"
      32  
      33  /* QNAME format:
      34  
      35     aADDRESSES-cCNAMES.example.net
      36  
      37     CNAMES is the length of the CNAME chain, ADDRESSES is the number of
      38     addresses in the response.  The special value 255 means that there
      39     are no addresses, and the RCODE is NXDOMAIN.  */
      40  static void
      41  response (const struct resolv_response_context *ctx,
      42            struct resolv_response_builder *b,
      43            const char *qname, uint16_t qclass, uint16_t qtype)
      44  {
      45    TEST_COMPARE (qclass, C_IN);
      46    if (qtype != T_A)
      47      TEST_COMPARE (qtype, T_AAAA);
      48  
      49    unsigned int addresses, cnames;
      50    char *tail;
      51    if (sscanf (qname, "a%u-c%u%ms", &addresses, &cnames, &tail) == 3)
      52      {
      53        if (strcmp (tail, ".example.com") == 0
      54            || strcmp (tail, ".example.net.example.net") == 0
      55            || strcmp (tail, ".example.net.example.com") == 0)
      56          /* These only happen after NXDOMAIN.  */
      57          TEST_VERIFY (addresses == 255);
      58        else if (strcmp (tail, ".example.net") != 0)
      59          FAIL_EXIT1 ("invalid QNAME: %s", qname);
      60      }
      61    free (tail);
      62  
      63    int rcode;
      64    if (addresses == 255)
      65      {
      66        /* Special case: Use no addresses with NXDOMAIN response.  */
      67        rcode = ns_r_nxdomain;
      68        addresses = 0;
      69      }
      70    else
      71      rcode = 0;
      72  
      73    struct resolv_response_flags flags = { .rcode = rcode };
      74    resolv_response_init (b, flags);
      75    resolv_response_add_question (b, qname, qclass, qtype);
      76    resolv_response_section (b, ns_s_an);
      77    maybe_insert_sig (b, qname);
      78  
      79    /* Provide the requested number of CNAME records.  */
      80    char *previous_name = (char *) qname;
      81    for (int unique = 0; unique < cnames; ++unique)
      82      {
      83        resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
      84        char *new_name = xasprintf ("%d.alias.example", unique);
      85        resolv_response_add_name (b, new_name);
      86        resolv_response_close_record (b);
      87  
      88        maybe_insert_sig (b, qname);
      89  
      90        if (previous_name != qname)
      91          free (previous_name);
      92        previous_name = new_name;
      93      }
      94  
      95    for (int unique = 0; unique < addresses; ++unique)
      96      {
      97        resolv_response_open_record (b, previous_name, qclass, qtype, 60);
      98  
      99        if (qtype == T_A)
     100          {
     101            char ipv4[4] = {192, 0, 2, 1 + unique};
     102            resolv_response_add_data (b, &ipv4, sizeof (ipv4));
     103          }
     104        else if (qtype == T_AAAA)
     105          {
     106            char ipv6[16] =
     107              {
     108                0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     109                1 + unique
     110              };
     111            resolv_response_add_data (b, &ipv6, sizeof (ipv6));
     112          }
     113        resolv_response_close_record (b);
     114      }
     115  
     116    if (previous_name != qname)
     117      free (previous_name);
     118  }
     119  
     120  static char *
     121  make_qname (bool do_search, int cnames, int addresses)
     122  {
     123    return xasprintf ("a%d-c%d%s",
     124                      addresses, cnames, do_search ? "" : ".example.net");
     125  }
     126  
     127  static void
     128  check_cnames_failure (int af, bool do_search, int cnames, int addresses)
     129  {
     130    char *qname = make_qname (do_search, cnames, addresses);
     131  
     132    struct hostent *e;
     133    if (af == AF_UNSPEC)
     134      e = gethostbyname (qname);
     135    else
     136      e = gethostbyname2 (qname, af);
     137  
     138    if (addresses == 0)
     139      check_hostent (qname, e, "error: NO_RECOVERY\n");
     140    else
     141      check_hostent (qname, e, "error: HOST_NOT_FOUND\n");
     142  
     143    free (qname);
     144  }
     145  
     146  static void
     147  check (int af, bool do_search, int cnames, int addresses)
     148  {
     149    char *qname = make_qname (do_search, cnames, addresses);
     150    char *fqdn = make_qname (false, cnames, addresses);
     151  
     152    struct hostent *e;
     153    if (af == AF_UNSPEC)
     154      e = gethostbyname (qname);
     155    else
     156      e = gethostbyname2 (qname, af);
     157    if (e == NULL)
     158      FAIL_EXIT1 ("unexpected failure for %d, %d, %d", af, cnames, addresses);
     159  
     160    if (af == AF_UNSPEC || af == AF_INET)
     161      {
     162        TEST_COMPARE (e->h_addrtype, AF_INET);
     163        TEST_COMPARE (e->h_length, 4);
     164      }
     165    else
     166      {
     167        TEST_COMPARE (e->h_addrtype, AF_INET6);
     168        TEST_COMPARE (e->h_length, 16);
     169      }
     170  
     171    for (int i = 0; i < addresses; ++i)
     172      {
     173        char ipv4[4] = {192, 0, 2, 1 + i};
     174        char ipv6[16] =
     175          { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + i };
     176        char *expected = e->h_addrtype == AF_INET ? ipv4 : ipv6;
     177        TEST_COMPARE_BLOB (e->h_addr_list[i], e->h_length,
     178                           expected, e->h_length);
     179      }
     180    TEST_VERIFY (e->h_addr_list[addresses] == NULL);
     181  
     182  
     183    if (cnames == 0)
     184      {
     185        /* QNAME is fully qualified.  */
     186        TEST_COMPARE_STRING (e->h_name, fqdn);
     187        TEST_VERIFY (e->h_aliases[0] == NULL);
     188      }
     189    else
     190     {
     191       /* Fully-qualified QNAME is demoted to an aliases.  */
     192       TEST_COMPARE_STRING (e->h_aliases[0], fqdn);
     193  
     194       for (int i = 1; i <= cnames; ++i)
     195         {
     196           char *expected = xasprintf ("%d.alias.example", i - 1);
     197           if (i == cnames)
     198             TEST_COMPARE_STRING (e->h_name, expected);
     199           else
     200             TEST_COMPARE_STRING (e->h_aliases[i], expected);
     201           free (expected);
     202         }
     203       TEST_VERIFY (e->h_aliases[cnames] == NULL);
     204     }
     205  
     206    free (fqdn);
     207    free (qname);
     208  }
     209  
     210  static int
     211  do_test (void)
     212  {
     213    struct resolv_test *obj = resolv_test_start
     214      ((struct resolv_redirect_config)
     215       {
     216         .response_callback = response,
     217         .search = { "example.net", "example.com" },
     218       });
     219  
     220    static const int families[] = { AF_UNSPEC, AF_INET, AF_INET6 };
     221  
     222    for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig)
     223      {
     224        insert_sig = do_insert_sig;
     225  
     226        /* If do_search is true, a bare host name (for example, a1-c1)
     227           is used.  This exercises search path processing and FQDN
     228           qualification.  */
     229        for (int do_search = 0; do_search < 2; ++do_search)
     230          for (const int *paf = families; paf != array_end (families); ++paf)
     231            {
     232              for (int cnames = 0; cnames <= 100; ++cnames)
     233                {
     234                  check_cnames_failure (*paf, do_search, cnames, 0);
     235                  /* Now with NXDOMAIN responses.  */
     236                  check_cnames_failure (*paf, do_search, cnames, 255);
     237                }
     238  
     239              for (int cnames = 0; cnames <= 10; ++cnames)
     240                for (int addresses = 1; addresses <= 10; ++addresses)
     241                  check (*paf, do_search, cnames, addresses);
     242  
     243              /* The current implementation is limited to 47 aliases.
     244                 Addresses do not have such a limit.  */
     245              check (*paf, do_search, 47, 60);
     246            }
     247      }
     248  
     249    resolv_test_end (obj);
     250  
     251    return 0;
     252  }
     253  
     254  #include <support/test-driver.c>