(root)/
glibc-2.38/
resolv/
tst-resolv-search.c
       1  /* Test search/default domain name behavior.
       2     Copyright (C) 2016-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 <resolv.h>
      20  #include <stdlib.h>
      21  #include <string.h>
      22  #include <support/check.h>
      23  #include <support/check_nss.h>
      24  #include <support/resolv_test.h>
      25  #include <support/support.h>
      26  #include <support/xmemstream.h>
      27  
      28  struct item
      29  {
      30    const char *name;
      31    int response;
      32  };
      33  
      34  const struct item items[] =
      35    {
      36      {"hostname.usersys.example.com", 1},
      37      {"hostname.corp.example.com", 1},
      38      {"hostname.example.com", 1},
      39  
      40      {"mail.corp.example.com", 1},
      41      {"mail.example.com", 1},
      42  
      43      {"file.corp.example.com", 2},
      44      {"file.corp", 1},
      45      {"file.example.com", 1},
      46      {"servfail-usersys.usersys.example.com", -ns_r_servfail},
      47      {"servfail-usersys.corp.example.com", 1},
      48      {"servfail-usersys.example.com", 1},
      49      {"servfail-corp.usersys.example.com", 1},
      50      {"servfail-corp.corp.example.com", -ns_r_servfail},
      51      {"servfail-corp.example.com", 1},
      52      {"www.example.com", 1},
      53      {"large.example.com", 200},
      54  
      55      /* Test query amplification with a SERVFAIL response combined with
      56         a large RRset.  */
      57      {"large-servfail.usersys.example.com", -ns_r_servfail},
      58      {"large-servfail.example.com", 2000},
      59      {}
      60    };
      61  
      62  enum
      63    {
      64      name_not_found = -1,
      65      name_no_data = -2
      66    };
      67  
      68  static int
      69  find_name (const char *name)
      70  {
      71    for (int i = 0; items[i].name != NULL; ++i)
      72      {
      73        if (strcmp (name, items[i].name) == 0)
      74          return i;
      75      }
      76    if (strcmp (name, "example.com") == 0
      77        || strcmp (name, "usersys.example.com") == 0
      78        || strcmp (name, "corp.example.com") == 0)
      79      return name_no_data;
      80    return name_not_found;
      81  }
      82  
      83  static int rcode_override_server_index = -1;
      84  static int rcode_override;
      85  
      86  static void
      87  response (const struct resolv_response_context *ctx,
      88            struct resolv_response_builder *b,
      89            const char *qname, uint16_t qclass, uint16_t qtype)
      90  {
      91    if (ctx->server_index == rcode_override_server_index)
      92      {
      93        struct resolv_response_flags flags = {.rcode = rcode_override};
      94        resolv_response_init (b, flags);
      95        resolv_response_add_question (b, qname, qclass, qtype);
      96        return;
      97      }
      98  
      99    int index = find_name (qname);
     100    struct resolv_response_flags flags = {};
     101    if (index == name_not_found)
     102      flags.rcode = ns_r_nxdomain;
     103    else if (index >= 0 && items[index].response < 0)
     104      flags.rcode = -items[index].response;
     105    else if (index >= 0 && items[index].response > 5 && !ctx->tcp)
     106      /* Force TCP if more than 5 addresses where requested.  */
     107      flags.tc = true;
     108    resolv_response_init (b, flags);
     109    resolv_response_add_question (b, qname, qclass, qtype);
     110  
     111    if (flags.tc || index < 0 || items[index].response < 0)
     112      return;
     113  
     114    resolv_response_section (b, ns_s_an);
     115  
     116    for (int i = 0; i < items[index].response; ++i)
     117      {
     118        resolv_response_open_record (b, qname, qclass, qtype, 0);
     119  
     120        switch (qtype)
     121          {
     122          case T_A:
     123            {
     124              char addr[4] = {10, index, i >> 8, i};
     125              resolv_response_add_data (b, addr, sizeof (addr));
     126            }
     127            break;
     128          case T_AAAA:
     129            {
     130              char addr[16]
     131                = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0,
     132                   0, index + 1, (i + 1) >> 8, i + 1};
     133              resolv_response_add_data (b, addr, sizeof (addr));
     134            }
     135            break;
     136          default:
     137            support_record_failure ();
     138            printf ("error: unexpected QTYPE: %s/%u/%u\n",
     139                    qname, qclass, qtype);
     140          }
     141        resolv_response_close_record (b);
     142      }
     143  }
     144  
     145  enum output_format
     146    {
     147      format_get, format_gai
     148    };
     149  
     150  static void
     151  format_expected_1 (FILE *out, int family, enum output_format format, int index)
     152  {
     153    for (int i = 0; i < items[index].response; ++i)
     154      {
     155        char address[200];
     156        switch (family)
     157          {
     158          case AF_INET:
     159            snprintf (address, sizeof (address), "10.%d.%d.%d",
     160                      index, (i >> 8) & 0xff, i & 0xff);
     161            break;
     162          case AF_INET6:
     163            snprintf (address, sizeof (address), "2001:db8::%x:%x",
     164                      index + 1, i + 1);
     165            break;
     166          default:
     167            FAIL_EXIT1 ("unreachable");
     168          }
     169  
     170        switch (format)
     171          {
     172          case format_get:
     173            fprintf (out, "address: %s\n", address);
     174            break;
     175          case format_gai:
     176            fprintf (out, "address: STREAM/TCP %s 80\n", address);
     177          }
     178      }
     179  }
     180  
     181  static char *
     182  format_expected (const char *fqdn, int family, enum output_format format)
     183  {
     184    int index = find_name (fqdn);
     185    TEST_VERIFY_EXIT (index >= 0);
     186    struct xmemstream stream;
     187    xopen_memstream (&stream);
     188  
     189    TEST_VERIFY_EXIT (items[index].response >= 0);
     190    if (format == format_get)
     191      fprintf (stream.out, "name: %s\n", items[index].name);
     192    if (family == AF_INET || family == AF_UNSPEC)
     193      format_expected_1 (stream.out, AF_INET, format, index);
     194    if (family == AF_INET6 || family == AF_UNSPEC)
     195      format_expected_1 (stream.out, AF_INET6, format, index);
     196  
     197    xfclose_memstream (&stream);
     198    return stream.buffer;
     199  }
     200  
     201  static void
     202  do_get (const char *name, const char *fqdn, int family)
     203  {
     204    char *expected = format_expected (fqdn, family, format_get);
     205    if (family == AF_INET)
     206      {
     207        char *query = xasprintf ("gethostbyname (\"%s\")", name);
     208        check_hostent (query, gethostbyname (name), expected);
     209        free (query);
     210      }
     211    char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family);
     212    check_hostent (query, gethostbyname2 (name, family), expected);
     213  
     214    /* Test res_search.  */
     215    int qtype;
     216    switch (family)
     217      {
     218      case AF_INET:
     219        qtype = T_A;
     220        break;
     221      case AF_INET6:
     222        qtype = T_AAAA;
     223        break;
     224      default:
     225        qtype = -1;
     226      }
     227    if (qtype >= 0)
     228      {
     229        int sz = 512;
     230        unsigned char *response = xmalloc (sz);
     231        int ret = res_search (name, C_IN, qtype, response, sz);
     232        TEST_VERIFY_EXIT (ret >= 0);
     233        if (ret > sz)
     234          {
     235            /* Truncation.  Retry with a larger buffer.  */
     236            sz = 65535;
     237            unsigned char *newptr = xrealloc (response, sz);
     238            response = newptr;
     239  
     240            ret = res_search (name, C_IN, qtype, response, sz);
     241            TEST_VERIFY_EXIT (ret >= 0);
     242            TEST_VERIFY_EXIT (ret < sz);
     243          }
     244        check_dns_packet (query, response, ret, expected);
     245        free (response);
     246      }
     247  
     248    free (query);
     249    free (expected);
     250  }
     251  
     252  static void
     253  do_gai (const char *name, const char *fqdn, int family)
     254  {
     255    struct addrinfo hints =
     256      {
     257        .ai_family = family,
     258        .ai_protocol = IPPROTO_TCP,
     259        .ai_socktype = SOCK_STREAM
     260      };
     261    struct addrinfo *ai;
     262    char *query = xasprintf ("%s:80 [%d]", name, family);
     263    int ret = getaddrinfo (name, "80", &hints, &ai);
     264    char *expected = format_expected (fqdn, family, format_gai);
     265    check_addrinfo (query, ai, ret, expected);
     266    if (ret == 0)
     267      freeaddrinfo (ai);
     268    free (expected);
     269    free (query);
     270  }
     271  
     272  static void
     273  do_both (const char *name, const char *fqdn)
     274  {
     275    do_get (name, fqdn, AF_INET);
     276    do_get (name, fqdn, AF_INET6);
     277    do_gai (name, fqdn, AF_INET);
     278    do_gai (name, fqdn, AF_INET6);
     279    do_gai (name, fqdn, AF_UNSPEC);
     280  }
     281  
     282  static void
     283  do_test_all (bool unconnectable_server)
     284  {
     285    struct resolv_redirect_config config =
     286      {
     287        .response_callback = response,
     288        .search = {"usersys.example.com", "corp.example.com", "example.com"},
     289      };
     290    struct resolv_test *obj = resolv_test_start (config);
     291  
     292    if (unconnectable_server)
     293      {
     294        /* 255.255.255.255 results in an immediate connect failure.  The
     295           next server will supply the answer instead.  This is a
     296           triggering condition for bug 19791.  */
     297        _res.nsaddr_list[0].sin_addr.s_addr = -1;
     298        _res.nsaddr_list[0].sin_port = htons (53);
     299      }
     300  
     301    do_both ("file", "file.corp.example.com");
     302    do_both ("www", "www.example.com");
     303    do_both ("servfail-usersys", "servfail-usersys.corp.example.com");
     304    do_both ("servfail-corp", "servfail-corp.usersys.example.com");
     305    do_both ("large", "large.example.com");
     306    do_both ("large-servfail", "large-servfail.example.com");
     307    do_both ("file.corp", "file.corp");
     308  
     309    /* Check that SERVFAIL and REFUSED responses do not alter the search
     310       path resolution.  */
     311    rcode_override_server_index = 0;
     312    rcode_override = ns_r_servfail;
     313    do_both ("hostname", "hostname.usersys.example.com");
     314    do_both ("large", "large.example.com");
     315    do_both ("large-servfail", "large-servfail.example.com");
     316    rcode_override = ns_r_refused;
     317    do_both ("hostname", "hostname.usersys.example.com");
     318    do_both ("large", "large.example.com");
     319    do_both ("large-servfail", "large-servfail.example.com");
     320    /* Likewise, but with an NXDOMAIN for the first search path
     321       entry.  */
     322    rcode_override = ns_r_servfail;
     323    do_both ("mail", "mail.corp.example.com");
     324    rcode_override = ns_r_refused;
     325    do_both ("mail", "mail.corp.example.com");
     326    /* Likewise, but with ndots handling.  */
     327    rcode_override = ns_r_servfail;
     328    do_both ("file.corp", "file.corp");
     329    rcode_override = ns_r_refused;
     330    do_both ("file.corp", "file.corp");
     331  
     332    resolv_test_end (obj);
     333  }
     334  
     335  static int
     336  do_test (void)
     337  {
     338    for (int unconnectable_server = 0; unconnectable_server < 2;
     339         ++unconnectable_server)
     340      do_test_all (unconnectable_server);
     341    return 0;
     342  }
     343  
     344  #include <support/test-driver.c>