(root)/
glibc-2.38/
resolv/
tst-resolv-basic.c
       1  /* Test basic nss_dns functionality and the resolver test harness itself.
       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 <errno.h>
      20  #include <stdio.h>
      21  #include <stdlib.h>
      22  #include <string.h>
      23  #include <support/check.h>
      24  #include <support/check_nss.h>
      25  #include <support/format_nss.h>
      26  #include <support/resolv_test.h>
      27  #include <support/support.h>
      28  
      29  #define LONG_NAME                                                       \
      30    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax."    \
      31    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay."    \
      32    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz."    \
      33    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat"
      34  
      35  static void
      36  response (const struct resolv_response_context *ctx,
      37            struct resolv_response_builder *b,
      38            const char *qname, uint16_t qclass, uint16_t qtype)
      39  {
      40    TEST_VERIFY_EXIT (qname != NULL);
      41  
      42    /* The "t." prefix can be used to request TCP fallback.  */
      43    bool force_tcp;
      44    if (strncmp ("t.", qname, 2) == 0)
      45      force_tcp = true;
      46    else
      47      force_tcp = false;
      48    const char *qname_compare;
      49    if (force_tcp)
      50      qname_compare = qname + 2;
      51    else
      52      qname_compare = qname;
      53    enum {www, alias, nxdomain, long_name, nodata} requested_qname;
      54    if (strcmp (qname_compare, "www.example") == 0)
      55      requested_qname = www;
      56    else if (strcmp (qname_compare, "alias.example") == 0)
      57      requested_qname = alias;
      58    else if (strcmp (qname_compare, "nxdomain.example") == 0)
      59      requested_qname = nxdomain;
      60    else if (strcmp (qname_compare, LONG_NAME) == 0)
      61      requested_qname = long_name;
      62    else if (strcmp (qname_compare, "nodata.example") == 0)
      63      requested_qname = nodata;
      64    else
      65      {
      66        support_record_failure ();
      67        printf ("error: unexpected QNAME: %s\n", qname);
      68        return;
      69      }
      70    TEST_VERIFY_EXIT (qclass == C_IN);
      71    struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
      72    if (requested_qname == nxdomain)
      73      flags.rcode = 3;            /* NXDOMAIN */
      74    resolv_response_init (b, flags);
      75    resolv_response_add_question (b, qname, qclass, qtype);
      76    if (requested_qname == nxdomain || flags.tc)
      77      return;
      78  
      79    resolv_response_section (b, ns_s_an);
      80    switch (requested_qname)
      81      {
      82      case www:
      83      case long_name:
      84        resolv_response_open_record (b, qname, qclass, qtype, 0);
      85        break;
      86      case alias:
      87        resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
      88        resolv_response_add_name (b, "www.example");
      89        resolv_response_close_record (b);
      90        resolv_response_open_record (b, "www.example", qclass, qtype, 0);
      91        break;
      92      case nodata:
      93        return;
      94      case nxdomain:
      95        FAIL_EXIT1 ("unreachable");
      96      }
      97    switch (qtype)
      98      {
      99      case T_A:
     100        {
     101          char ipv4[4] = {192, 0, 2, 17};
     102          ipv4[3] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index;
     103          resolv_response_add_data (b, &ipv4, sizeof (ipv4));
     104        }
     105        break;
     106      case T_AAAA:
     107        {
     108          char ipv6[16]
     109            = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     110          ipv6[15] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index;
     111          resolv_response_add_data (b, &ipv6, sizeof (ipv6));
     112        }
     113        break;
     114      default:
     115        support_record_failure ();
     116        printf ("error: unexpected QTYPE: %s/%u/%u\n",
     117                qname, qclass, qtype);
     118      }
     119    resolv_response_close_record (b);
     120  }
     121  
     122  static void
     123  check_h (const char *name, int family, const char *expected)
     124  {
     125    if (family == AF_INET)
     126      {
     127        char *query = xasprintf ("gethostbyname (\"%s\")", name);
     128        check_hostent (query, gethostbyname (name), expected);
     129        free (query);
     130      }
     131    {
     132      char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family);
     133      check_hostent (query, gethostbyname2 (name, family), expected);
     134      free (query);
     135    }
     136  
     137    bool too_small = true;
     138    for (unsigned int offset = 0; offset < 8; ++offset)
     139      for (unsigned int size = 1; too_small; ++size)
     140        {
     141          char *buf = xmalloc (offset + size);
     142          too_small = false;
     143  
     144          struct hostent hostbuf;
     145          struct hostent *result;
     146          int herror;
     147          if (family == AF_INET)
     148            {
     149              char *query = xasprintf ("gethostbyname (\"%s\") %u/%u",
     150                                       name, offset, size);
     151              int ret = gethostbyname_r
     152                (name, &hostbuf, buf + offset, size, &result, &herror);
     153              if (ret == 0)
     154                {
     155                  h_errno = herror;
     156                  check_hostent (query, result, expected);
     157                }
     158              else if (ret == ERANGE)
     159                too_small = true;
     160              else
     161                {
     162                  errno = ret;
     163                  FAIL_EXIT1 ("gethostbyname_r: %m");
     164                }
     165              free (query);
     166              memset (buf, 0, offset + size);
     167            }
     168          char *query = xasprintf ("gethostbyname2 (\"%s\", %d) %u/%u",
     169                                   name, family, offset, size);
     170          int ret = gethostbyname2_r
     171            (name, family, &hostbuf, buf + offset, size, &result, &herror);
     172          if (ret == 0)
     173            {
     174              h_errno = herror;
     175              check_hostent (query, result, expected);
     176            }
     177          else if (ret == ERANGE)
     178            too_small = true;
     179          else
     180            {
     181              errno = ret;
     182              FAIL_EXIT1 ("gethostbyname_r: %m");
     183            }
     184          free (buf);
     185          free (query);
     186        }
     187  }
     188  
     189  static void
     190  check_ai_hints (const char *name, const char *service,
     191                  struct addrinfo hints, const char *expected)
     192  {
     193    struct addrinfo *ai;
     194    char *query = xasprintf ("%s:%s [%d]/0x%x", name, service,
     195                             hints.ai_family, hints.ai_flags);
     196    int ret = getaddrinfo (name, service, &hints, &ai);
     197    check_addrinfo (query, ai, ret, expected);
     198    if (ret == 0)
     199      freeaddrinfo (ai);
     200    free (query);
     201  }
     202  
     203  static void
     204  check_ai (const char *name, const char *service,
     205            int family, const char *expected)
     206  {
     207    return check_ai_hints (name, service,
     208                           (struct addrinfo) { .ai_family = family, },
     209                           expected);
     210  }
     211  
     212  /* Test for bug 21295: getaddrinfo used to discard address information
     213     instead of merging it.  */
     214  static void
     215  test_bug_21295 (void)
     216  {
     217    /* The address order is unpredictable.  There are two factors which
     218       contribute to that: The stub resolver does not perform proper
     219       response matching for A/AAAA queries (an A response could be
     220       associated with an AAAA query and vice versa), and without
     221       namespaces, system configuration could affect address
     222       ordering.  */
     223    for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
     224      {
     225        const struct addrinfo hints =
     226          {
     227            .ai_family = AF_INET6,
     228            .ai_socktype = SOCK_STREAM,
     229            .ai_flags = AI_V4MAPPED | AI_ALL,
     230          };
     231        const char *qname;
     232        if (do_tcp)
     233          qname = "t.www.example";
     234        else
     235          qname = "www.example";
     236        struct addrinfo *ai = NULL;
     237        int ret = getaddrinfo (qname, "80", &hints, &ai);
     238        TEST_VERIFY_EXIT (ret == 0);
     239  
     240        const char *expected_a;
     241        const char *expected_b;
     242        if (do_tcp)
     243          {
     244            expected_a = "flags: AI_V4MAPPED AI_ALL\n"
     245              "address: STREAM/TCP 2001:db8::3 80\n"
     246              "address: STREAM/TCP ::ffff:192.0.2.19 80\n";
     247            expected_b = "flags: AI_V4MAPPED AI_ALL\n"
     248              "address: STREAM/TCP ::ffff:192.0.2.19 80\n"
     249              "address: STREAM/TCP 2001:db8::3 80\n";
     250          }
     251        else
     252          {
     253            expected_a = "flags: AI_V4MAPPED AI_ALL\n"
     254              "address: STREAM/TCP 2001:db8::1 80\n"
     255              "address: STREAM/TCP ::ffff:192.0.2.17 80\n";
     256            expected_b = "flags: AI_V4MAPPED AI_ALL\n"
     257              "address: STREAM/TCP ::ffff:192.0.2.17 80\n"
     258              "address: STREAM/TCP 2001:db8::1 80\n";
     259          }
     260  
     261        char *actual = support_format_addrinfo (ai, ret);
     262        if (!(strcmp (actual, expected_a) == 0
     263              || strcmp (actual, expected_b) == 0))
     264          {
     265            support_record_failure ();
     266            printf ("error: %s: unexpected response (TCP: %d):\n%s\n",
     267                    __func__, do_tcp, actual);
     268          }
     269        free (actual);
     270        freeaddrinfo (ai);
     271      }
     272  }
     273  
     274  /* Run tests which do not expect any data.  */
     275  static void
     276  test_nodata_nxdomain (void)
     277  {
     278    /* Iterate through different address families.  */
     279    int families[] = { AF_UNSPEC, AF_INET, AF_INET6, -1 };
     280    for (int i = 0; families[i] >= 0; ++i)
     281      /* If do_tcp, prepend "t." to the name to trigger TCP
     282         fallback.  */
     283      for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
     284        /* If do_nxdomain, trigger an NXDOMAIN error (DNS failure),
     285           otherwise use a NODATA response (empty but successful
     286           answer).  */
     287        for (int do_nxdomain = 0; do_nxdomain < 2; ++do_nxdomain)
     288          {
     289            int family = families[i];
     290            char *name = xasprintf ("%s%s.example",
     291                                    do_tcp ? "t." : "",
     292                                    do_nxdomain ? "nxdomain" : "nodata");
     293  
     294            if (family != AF_UNSPEC)
     295              {
     296                if (do_nxdomain)
     297                  check_h (name, family, "error: HOST_NOT_FOUND\n");
     298                else
     299                  check_h (name, family, "error: NO_ADDRESS\n");
     300              }
     301  
     302            const char *expected;
     303            if (do_nxdomain)
     304              expected = "error: Name or service not known\n";
     305            else
     306              expected = "error: No address associated with hostname\n";
     307  
     308            check_ai (name, "80", family, expected);
     309  
     310            struct addrinfo hints =
     311              {
     312                .ai_family = family,
     313                .ai_flags = AI_V4MAPPED | AI_ALL,
     314              };
     315            check_ai_hints (name, "80", hints, expected);
     316            hints.ai_flags |= AI_CANONNAME;
     317            check_ai_hints (name, "80", hints, expected);
     318  
     319            free (name);
     320          }
     321  }
     322  
     323  static int
     324  do_test (void)
     325  {
     326    struct resolv_test *aux = resolv_test_start
     327      ((struct resolv_redirect_config)
     328       {
     329         .response_callback = response,
     330       });
     331  
     332    check_h ("www.example", AF_INET,
     333             "name: www.example\n"
     334             "address: 192.0.2.17\n");
     335    check_h ("alias.example", AF_INET,
     336             "name: www.example\n"
     337             "alias: alias.example\n"
     338             "address: 192.0.2.18\n");
     339    check_h ("www.example", AF_INET6,
     340             "name: www.example\n"
     341             "address: 2001:db8::1\n");
     342    check_h ("alias.example", AF_INET6,
     343             "name: www.example\n"
     344             "alias: alias.example\n"
     345             "address: 2001:db8::2\n");
     346    check_h (LONG_NAME, AF_INET,
     347             "name: " LONG_NAME "\n"
     348             "address: 192.0.2.20\n");
     349  
     350    check_ai ("www.example", "80", AF_UNSPEC,
     351              "address: STREAM/TCP 192.0.2.17 80\n"
     352              "address: DGRAM/UDP 192.0.2.17 80\n"
     353              "address: RAW/IP 192.0.2.17 80\n"
     354              "address: STREAM/TCP 2001:db8::1 80\n"
     355              "address: DGRAM/UDP 2001:db8::1 80\n"
     356              "address: RAW/IP 2001:db8::1 80\n");
     357    check_ai_hints ("www.example", "80",
     358                    (struct addrinfo) { .ai_family = AF_UNSPEC,
     359                        .ai_flags = AI_CANONNAME, },
     360                    "flags: AI_CANONNAME\n"
     361                    "canonname: www.example\n"
     362                    "address: STREAM/TCP 192.0.2.17 80\n"
     363                    "address: DGRAM/UDP 192.0.2.17 80\n"
     364                    "address: RAW/IP 192.0.2.17 80\n"
     365                    "address: STREAM/TCP 2001:db8::1 80\n"
     366                    "address: DGRAM/UDP 2001:db8::1 80\n"
     367                    "address: RAW/IP 2001:db8::1 80\n");
     368    check_ai ("alias.example", "80", AF_UNSPEC,
     369              "address: STREAM/TCP 192.0.2.18 80\n"
     370              "address: DGRAM/UDP 192.0.2.18 80\n"
     371              "address: RAW/IP 192.0.2.18 80\n"
     372              "address: STREAM/TCP 2001:db8::2 80\n"
     373              "address: DGRAM/UDP 2001:db8::2 80\n"
     374              "address: RAW/IP 2001:db8::2 80\n");
     375    check_ai_hints ("alias.example", "80",
     376                    (struct addrinfo) { .ai_family = AF_UNSPEC,
     377                        .ai_flags = AI_CANONNAME, },
     378                    "flags: AI_CANONNAME\n"
     379                    "canonname: www.example\n"
     380                    "address: STREAM/TCP 192.0.2.18 80\n"
     381                    "address: DGRAM/UDP 192.0.2.18 80\n"
     382                    "address: RAW/IP 192.0.2.18 80\n"
     383                    "address: STREAM/TCP 2001:db8::2 80\n"
     384                    "address: DGRAM/UDP 2001:db8::2 80\n"
     385                    "address: RAW/IP 2001:db8::2 80\n");
     386    check_ai (LONG_NAME, "80", AF_UNSPEC,
     387              "address: STREAM/TCP 192.0.2.20 80\n"
     388              "address: DGRAM/UDP 192.0.2.20 80\n"
     389              "address: RAW/IP 192.0.2.20 80\n"
     390              "address: STREAM/TCP 2001:db8::4 80\n"
     391              "address: DGRAM/UDP 2001:db8::4 80\n"
     392              "address: RAW/IP 2001:db8::4 80\n");
     393    check_ai ("www.example", "80", AF_INET,
     394              "address: STREAM/TCP 192.0.2.17 80\n"
     395              "address: DGRAM/UDP 192.0.2.17 80\n"
     396              "address: RAW/IP 192.0.2.17 80\n");
     397    check_ai_hints ("www.example", "80",
     398                    (struct addrinfo) { .ai_family = AF_INET,
     399                        .ai_flags = AI_CANONNAME, },
     400                    "flags: AI_CANONNAME\n"
     401                    "canonname: www.example\n"
     402                    "address: STREAM/TCP 192.0.2.17 80\n"
     403                    "address: DGRAM/UDP 192.0.2.17 80\n"
     404                    "address: RAW/IP 192.0.2.17 80\n");
     405    check_ai ("alias.example", "80", AF_INET,
     406              "address: STREAM/TCP 192.0.2.18 80\n"
     407              "address: DGRAM/UDP 192.0.2.18 80\n"
     408              "address: RAW/IP 192.0.2.18 80\n");
     409    check_ai_hints ("alias.example", "80",
     410                    (struct addrinfo) { .ai_family = AF_INET,
     411                        .ai_flags = AI_CANONNAME, },
     412                    "flags: AI_CANONNAME\n"
     413                    "canonname: www.example\n"
     414                    "address: STREAM/TCP 192.0.2.18 80\n"
     415                    "address: DGRAM/UDP 192.0.2.18 80\n"
     416                    "address: RAW/IP 192.0.2.18 80\n");
     417    check_ai (LONG_NAME, "80", AF_INET,
     418              "address: STREAM/TCP 192.0.2.20 80\n"
     419              "address: DGRAM/UDP 192.0.2.20 80\n"
     420              "address: RAW/IP 192.0.2.20 80\n");
     421    check_ai ("www.example", "80", AF_INET6,
     422              "address: STREAM/TCP 2001:db8::1 80\n"
     423              "address: DGRAM/UDP 2001:db8::1 80\n"
     424              "address: RAW/IP 2001:db8::1 80\n");
     425    check_ai_hints ("www.example", "80",
     426                    (struct addrinfo) { .ai_family = AF_INET6,
     427                        .ai_flags = AI_CANONNAME, },
     428                    "flags: AI_CANONNAME\n"
     429                    "canonname: www.example\n"
     430                    "address: STREAM/TCP 2001:db8::1 80\n"
     431                    "address: DGRAM/UDP 2001:db8::1 80\n"
     432                    "address: RAW/IP 2001:db8::1 80\n");
     433    check_ai ("alias.example", "80", AF_INET6,
     434              "address: STREAM/TCP 2001:db8::2 80\n"
     435              "address: DGRAM/UDP 2001:db8::2 80\n"
     436              "address: RAW/IP 2001:db8::2 80\n");
     437    check_ai_hints ("alias.example", "80",
     438                    (struct addrinfo) { .ai_family = AF_INET6,
     439                        .ai_flags = AI_CANONNAME, },
     440                    "flags: AI_CANONNAME\n"
     441                    "canonname: www.example\n"
     442                    "address: STREAM/TCP 2001:db8::2 80\n"
     443                    "address: DGRAM/UDP 2001:db8::2 80\n"
     444                    "address: RAW/IP 2001:db8::2 80\n");
     445    check_ai (LONG_NAME, "80", AF_INET6,
     446              "address: STREAM/TCP 2001:db8::4 80\n"
     447              "address: DGRAM/UDP 2001:db8::4 80\n"
     448              "address: RAW/IP 2001:db8::4 80\n");
     449  
     450    check_h ("t.www.example", AF_INET,
     451             "name: t.www.example\n"
     452             "address: 192.0.2.19\n");
     453    check_h ("t.alias.example", AF_INET,
     454             "name: www.example\n"
     455             "alias: t.alias.example\n"
     456             "address: 192.0.2.20\n");
     457    check_h ("t.www.example", AF_INET6,
     458             "name: t.www.example\n"
     459             "address: 2001:db8::3\n");
     460    check_h ("t.alias.example", AF_INET6,
     461             "name: www.example\n"
     462             "alias: t.alias.example\n"
     463             "address: 2001:db8::4\n");
     464    check_ai ("t.www.example", "80", AF_UNSPEC,
     465              "address: STREAM/TCP 192.0.2.19 80\n"
     466              "address: DGRAM/UDP 192.0.2.19 80\n"
     467              "address: RAW/IP 192.0.2.19 80\n"
     468              "address: STREAM/TCP 2001:db8::3 80\n"
     469              "address: DGRAM/UDP 2001:db8::3 80\n"
     470              "address: RAW/IP 2001:db8::3 80\n");
     471    check_ai ("t.alias.example", "80", AF_UNSPEC,
     472              "address: STREAM/TCP 192.0.2.20 80\n"
     473              "address: DGRAM/UDP 192.0.2.20 80\n"
     474              "address: RAW/IP 192.0.2.20 80\n"
     475              "address: STREAM/TCP 2001:db8::4 80\n"
     476              "address: DGRAM/UDP 2001:db8::4 80\n"
     477              "address: RAW/IP 2001:db8::4 80\n");
     478    check_ai ("t.www.example", "80", AF_INET,
     479              "address: STREAM/TCP 192.0.2.19 80\n"
     480              "address: DGRAM/UDP 192.0.2.19 80\n"
     481              "address: RAW/IP 192.0.2.19 80\n");
     482    check_ai ("t.alias.example", "80", AF_INET,
     483              "address: STREAM/TCP 192.0.2.20 80\n"
     484              "address: DGRAM/UDP 192.0.2.20 80\n"
     485              "address: RAW/IP 192.0.2.20 80\n");
     486    check_ai ("t.www.example", "80", AF_INET6,
     487              "address: STREAM/TCP 2001:db8::3 80\n"
     488              "address: DGRAM/UDP 2001:db8::3 80\n"
     489              "address: RAW/IP 2001:db8::3 80\n");
     490    check_ai ("t.alias.example", "80", AF_INET6,
     491              "address: STREAM/TCP 2001:db8::4 80\n"
     492              "address: DGRAM/UDP 2001:db8::4 80\n"
     493              "address: RAW/IP 2001:db8::4 80\n");
     494  
     495    test_bug_21295 ();
     496    test_nodata_nxdomain ();
     497  
     498    resolv_test_end (aux);
     499  
     500    return 0;
     501  }
     502  
     503  #include <support/test-driver.c>