(root)/
glibc-2.38/
resolv/
tst-resolv-edns.c
       1  /* Test EDNS handling in the stub resolver.
       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 <netdb.h>
      21  #include <resolv.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <support/check.h>
      26  #include <support/resolv_test.h>
      27  #include <support/support.h>
      28  #include <support/test-driver.h>
      29  #include <support/xthread.h>
      30  
      31  /* Data produced by a test query.  */
      32  struct response_data
      33  {
      34    char *qname;
      35    uint16_t qtype;
      36    struct resolv_edns_info edns;
      37  };
      38  
      39  /* Global array used by put_response and get_response to record
      40     response data.  The test DNS server returns the index of the array
      41     element which contains the actual response data.  This enables the
      42     test case to return arbitrary amounts of data with the limited
      43     number of bits which fit into an IP address.
      44  
      45     The volatile specifier is needed because the test case accesses
      46     these variables from a callback function called from a function
      47     which is marked as __THROW (i.e., a leaf function which actually is
      48     not).  */
      49  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
      50  static struct response_data ** volatile response_data_array;
      51  volatile static size_t response_data_count;
      52  
      53  /* Extract information from the query, store it in a struct
      54     response_data object, and return its index in the
      55     response_data_array.  */
      56  static unsigned int
      57  put_response (const struct resolv_response_context *ctx,
      58                   const char *qname, uint16_t qtype)
      59  {
      60    xpthread_mutex_lock (&mutex);
      61    ++response_data_count;
      62    /* We only can represent 2**24 indexes in 10.0.0.0/8.  */
      63    TEST_VERIFY (response_data_count < (1 << 24));
      64    response_data_array = xrealloc
      65      (response_data_array, sizeof (*response_data_array) * response_data_count);
      66    unsigned int index = response_data_count - 1;
      67    struct response_data *data = xmalloc (sizeof (*data));
      68    *data = (struct response_data)
      69      {
      70        .qname = xstrdup (qname),
      71        .qtype = qtype,
      72        .edns = ctx->edns,
      73      };
      74    response_data_array[index] = data;
      75    xpthread_mutex_unlock (&mutex);
      76    return index;
      77  }
      78  
      79  /* Verify the index into the response_data array and return the data
      80     at it.  */
      81  static struct response_data *
      82  get_response (unsigned int index)
      83  {
      84    xpthread_mutex_lock (&mutex);
      85    TEST_VERIFY_EXIT (index < response_data_count);
      86    struct response_data *result = response_data_array[index];
      87    xpthread_mutex_unlock (&mutex);
      88    return result;
      89  }
      90  
      91  /* Deallocate all response data.  */
      92  static void
      93  free_response_data (void)
      94  {
      95    xpthread_mutex_lock (&mutex);
      96    size_t count = response_data_count;
      97    struct response_data **array = response_data_array;
      98    for (unsigned int i = 0; i < count; ++i)
      99      {
     100        struct response_data *data = array[i];
     101        free (data->qname);
     102        free (data);
     103      }
     104    free (array);
     105    response_data_array = NULL;
     106    response_data_count = 0;
     107    xpthread_mutex_unlock (&mutex);
     108  }
     109  
     110  #define EDNS_PROBE_EXAMPLE "edns-probe.example"
     111  
     112  static void
     113  response (const struct resolv_response_context *ctx,
     114            struct resolv_response_builder *b,
     115            const char *qname, uint16_t qclass, uint16_t qtype)
     116  {
     117    TEST_VERIFY_EXIT (qname != NULL);
     118  
     119    const char *qname_compare = qname;
     120  
     121    /* The "formerr." prefix can be used to request a FORMERR response on the
     122       first server.  */
     123    bool send_formerr;
     124    if (strncmp ("formerr.", qname, strlen ("formerr.")) == 0)
     125      {
     126        send_formerr = true;
     127        qname_compare = qname + strlen ("formerr.");
     128      }
     129    else
     130      {
     131        send_formerr = false;
     132        qname_compare = qname;
     133      }
     134  
     135    /* The "tcp." prefix can be used to request TCP fallback.  */
     136    bool force_tcp;
     137    if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
     138      {
     139        force_tcp = true;
     140        qname_compare += strlen ("tcp.");
     141      }
     142    else
     143      force_tcp = false;
     144  
     145    enum {edns_probe} requested_qname;
     146    if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0)
     147      requested_qname = edns_probe;
     148    else
     149      {
     150        support_record_failure ();
     151        printf ("error: unexpected QNAME: %s (reduced: %s)\n",
     152                qname, qname_compare);
     153        return;
     154      }
     155    TEST_VERIFY_EXIT (qclass == C_IN);
     156    struct resolv_response_flags flags = { };
     157    flags.tc = force_tcp && !ctx->tcp;
     158    if (!flags.tc && send_formerr && ctx->server_index == 0)
     159      /* Send a FORMERR for the first full response from the first
     160         server.  */
     161      flags.rcode = 1;          /* FORMERR */
     162    resolv_response_init (b, flags);
     163    resolv_response_add_question (b, qname, qclass, qtype);
     164    if (flags.tc || flags.rcode != 0)
     165      return;
     166  
     167    if (test_verbose)
     168      printf ("info: edns=%d payload_size=%d\n",
     169              ctx->edns.active, ctx->edns.payload_size);
     170  
     171    /* Encode the response_data object in multiple address records.
     172       Each record carries two bytes of payload data, and an index.  */
     173    resolv_response_section (b, ns_s_an);
     174    switch (requested_qname)
     175      {
     176      case edns_probe:
     177        {
     178          unsigned int index = put_response (ctx, qname, qtype);
     179          switch (qtype)
     180            {
     181            case T_A:
     182              {
     183                uint32_t addr = htonl (0x0a000000 | index);
     184                resolv_response_open_record (b, qname, qclass, qtype, 0);
     185                resolv_response_add_data (b, &addr, sizeof (addr));
     186                resolv_response_close_record (b);
     187              }
     188              break;
     189            case T_AAAA:
     190              {
     191                char addr[16]
     192                  = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     193                     index >> 16, index >> 8, index};
     194                resolv_response_open_record (b, qname, qclass, qtype, 0);
     195                resolv_response_add_data (b, &addr, sizeof (addr));
     196                resolv_response_close_record (b);
     197              }
     198            }
     199        }
     200        break;
     201      }
     202  }
     203  
     204  /* Update *DATA with data from ADDRESS of SIZE.  Set the corresponding
     205     flag in SHADOW for each byte written.  */
     206  static struct response_data *
     207  decode_address (const void *address, size_t size)
     208  {
     209    switch (size)
     210      {
     211      case 4:
     212        TEST_VERIFY (memcmp (address, "\x0a", 1) == 0);
     213        break;
     214      case 16:
     215        TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0);
     216        break;
     217      default:
     218        FAIL_EXIT1 ("unexpected address size %zu", size);
     219      }
     220    const unsigned char *addr = address;
     221    unsigned int index = addr[size - 3] * 256 * 256
     222      + addr[size - 2] * 256
     223      + addr[size - 1];
     224    return get_response (index);
     225  }
     226  
     227  static struct response_data *
     228  decode_hostent (struct hostent *e)
     229  {
     230    TEST_VERIFY_EXIT (e != NULL);
     231    TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL);
     232    TEST_VERIFY (e->h_addr_list[1] == NULL);
     233    return decode_address (e->h_addr_list[0], e->h_length);
     234  }
     235  
     236  static struct response_data *
     237  decode_addrinfo (struct addrinfo *ai, int family)
     238  {
     239    struct response_data *data = NULL;
     240    while (ai != NULL)
     241      {
     242        if (ai->ai_family == family)
     243          {
     244            struct response_data *new_data;
     245            switch (family)
     246              {
     247              case AF_INET:
     248                {
     249                  struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr;
     250                  new_data = decode_address (&pin->sin_addr.s_addr, 4);
     251                }
     252                break;
     253              case AF_INET6:
     254                {
     255                  struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr;
     256                  new_data = decode_address (&pin->sin6_addr.s6_addr, 16);
     257                }
     258                break;
     259              default:
     260                FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
     261              }
     262            if (data == NULL)
     263              data = new_data;
     264            else
     265              /* Check pointer equality because this should be the same
     266                 response (same index).  */
     267              TEST_VERIFY (data == new_data);
     268          }
     269        ai = ai->ai_next;
     270      }
     271    TEST_VERIFY_EXIT (data != NULL);
     272    return data;
     273  }
     274  
     275  /* Updated by the main test loop in accordance with what is set in
     276     _res.options.  */
     277  static bool use_edns;
     278  static bool use_dnssec;
     279  
     280  /* Verify the decoded response data against the flags above.  */
     281  static void
     282  verify_response_data_payload (struct response_data *data,
     283                                size_t expected_payload)
     284  {
     285    bool edns = use_edns || use_dnssec;
     286    TEST_VERIFY (data->edns.active == edns);
     287    if (!edns)
     288      expected_payload = 0;
     289    if (data->edns.payload_size != expected_payload)
     290      {
     291        support_record_failure ();
     292        printf ("error: unexpected payload size %d (edns=%d)\n",
     293                (int) data->edns.payload_size, edns);
     294      }
     295    uint16_t expected_flags = 0;
     296    if (use_dnssec)
     297      expected_flags |= 0x8000;   /* DO flag.  */
     298    if (data->edns.flags != expected_flags)
     299      {
     300        support_record_failure ();
     301        printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
     302                (int) data->edns.flags, edns);
     303      }
     304  }
     305  
     306  /* Same as verify_response_data_payload, but use the default
     307     payload.  */
     308  static void
     309  verify_response_data (struct response_data *data)
     310  {
     311    verify_response_data_payload (data, 1200);
     312  }
     313  
     314  static void
     315  check_hostent (struct hostent *e)
     316  {
     317    TEST_VERIFY_EXIT (e != NULL);
     318    verify_response_data (decode_hostent (e));
     319  }
     320  
     321  static void
     322  do_ai (int family)
     323  {
     324    struct addrinfo hints = { .ai_family = family };
     325    struct addrinfo *ai;
     326    int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai);
     327    TEST_VERIFY_EXIT (ret == 0);
     328    switch (family)
     329      {
     330      case AF_INET:
     331      case AF_INET6:
     332        verify_response_data (decode_addrinfo (ai, family));
     333        break;
     334      case AF_UNSPEC:
     335        verify_response_data (decode_addrinfo (ai, AF_INET));
     336        verify_response_data (decode_addrinfo (ai, AF_INET6));
     337        break;
     338      default:
     339        FAIL_EXIT1 ("invalid address family %d", family);
     340      }
     341    freeaddrinfo (ai);
     342  }
     343  
     344  enum res_op
     345  {
     346    res_op_search,
     347    res_op_query,
     348    res_op_querydomain,
     349    res_op_nsearch,
     350    res_op_nquery,
     351    res_op_nquerydomain,
     352  
     353    res_op_last = res_op_nquerydomain,
     354  };
     355  
     356  static const char *
     357  res_op_string (enum res_op op)
     358  {
     359    switch (op)
     360      {
     361        case res_op_search:
     362          return "res_search";
     363        case res_op_query:
     364          return "res_query";
     365        case res_op_querydomain:
     366          return "res_querydomain";
     367        case res_op_nsearch:
     368          return "res_nsearch";
     369        case res_op_nquery:
     370          return "res_nquery";
     371        case res_op_nquerydomain:
     372          return "res_nquerydomain";
     373      }
     374    FAIL_EXIT1 ("invalid res_op value %d", (int) op);
     375  }
     376  
     377  /* Call libresolv function OP to look up PROBE_NAME, with an answer
     378     buffer of SIZE bytes.  Check that the advertised UDP buffer size is
     379     in fact EXPECTED_BUFFER_SIZE.  */
     380  static void
     381  do_res_search (const char *probe_name, enum res_op op, size_t size,
     382                 size_t expected_buffer_size)
     383  {
     384    if (test_verbose)
     385      printf ("info: testing %s with buffer size %zu\n",
     386              res_op_string (op), size);
     387    unsigned char *buffer = xmalloc (size);
     388    int ret = -1;
     389    switch (op)
     390      {
     391      case res_op_search:
     392        ret = res_search (probe_name, C_IN, T_A, buffer, size);
     393        break;
     394      case res_op_query:
     395        ret = res_query (probe_name, C_IN, T_A, buffer, size);
     396        break;
     397      case res_op_nsearch:
     398        ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size);
     399        break;
     400      case res_op_nquery:
     401        ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size);
     402        break;
     403      case res_op_querydomain:
     404      case res_op_nquerydomain:
     405        {
     406          char *example_stripped = xstrdup (probe_name);
     407          char *dot_example = strstr (example_stripped, ".example");
     408          if (dot_example != NULL && strcmp (dot_example, ".example") == 0)
     409            {
     410              /* Truncate the domain name.  */
     411              *dot_example = '\0';
     412              if (op == res_op_querydomain)
     413                ret = res_querydomain
     414                (example_stripped, "example", C_IN, T_A, buffer, size);
     415              else
     416                ret = res_nquerydomain
     417                  (&_res, example_stripped, "example", C_IN, T_A, buffer, size);
     418            }
     419          else
     420            FAIL_EXIT1 ("invalid probe name: %s", probe_name);
     421          free (example_stripped);
     422        }
     423        break;
     424      }
     425    TEST_VERIFY_EXIT (ret > 12);
     426    unsigned char *end = buffer + ret;
     427  
     428    HEADER *hd = (HEADER *) buffer;
     429    TEST_VERIFY (ntohs (hd->qdcount) == 1);
     430    TEST_VERIFY (ntohs (hd->ancount) == 1);
     431    /* Skip over the header.  */
     432    unsigned char *p = buffer + sizeof (*hd);
     433    /* Skip over the question.  */
     434    ret = dn_skipname (p, end);
     435    TEST_VERIFY_EXIT (ret > 0);
     436    p += ret;
     437    TEST_VERIFY_EXIT (end - p >= 4);
     438    p += 4;
     439    /* Skip over the RNAME and the RR header, but stop at the RDATA
     440       length.  */
     441    ret = dn_skipname (p, end);
     442    TEST_VERIFY_EXIT (ret > 0);
     443    p += ret;
     444    TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4);
     445    p += 2 + 2 + 4;
     446    /* The IP address should be 4 bytes long.  */
     447    TEST_VERIFY_EXIT (p[0] == 0);
     448    TEST_VERIFY_EXIT (p[1] == 4);
     449    /* Extract the address information.   */
     450    p += 2;
     451    struct response_data *data = decode_address (p, 4);
     452  
     453    verify_response_data_payload (data, expected_buffer_size);
     454  
     455    free (buffer);
     456  }
     457  
     458  static void
     459  run_test (const char *probe_name)
     460  {
     461    if (test_verbose)
     462      printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
     463              use_edns, use_dnssec);
     464    check_hostent (gethostbyname (probe_name));
     465    check_hostent (gethostbyname2 (probe_name, AF_INET));
     466    check_hostent (gethostbyname2 (probe_name, AF_INET6));
     467    do_ai (AF_UNSPEC);
     468    do_ai (AF_INET);
     469    do_ai (AF_INET6);
     470  
     471    for (int op = 0; op <= res_op_last; ++op)
     472      {
     473        do_res_search (probe_name, op, 301, 512);
     474        do_res_search (probe_name, op, 511, 512);
     475        do_res_search (probe_name, op, 512, 512);
     476        do_res_search (probe_name, op, 513, 513);
     477        do_res_search (probe_name, op, 657, 657);
     478        do_res_search (probe_name, op, 1199, 1199);
     479        do_res_search (probe_name, op, 1200, 1200);
     480        do_res_search (probe_name, op, 1201, 1200);
     481        do_res_search (probe_name, op, 65535, 1200);
     482      }
     483  }
     484  
     485  static int
     486  do_test (void)
     487  {
     488    for (int do_edns = 0; do_edns < 2; ++do_edns)
     489      for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
     490        for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
     491          for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
     492            {
     493              struct resolv_test *aux = resolv_test_start
     494                ((struct resolv_redirect_config)
     495                 {
     496                   .response_callback = response,
     497                 });
     498  
     499              use_edns = do_edns;
     500              if (do_edns)
     501                _res.options |= RES_USE_EDNS0;
     502              use_dnssec = do_dnssec;
     503              if (do_dnssec)
     504                _res.options |= RES_USE_DNSSEC;
     505  
     506              char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
     507              if (do_tcp)
     508                {
     509                  char *n = xasprintf ("tcp.%s", probe_name);
     510                  free (probe_name);
     511                  probe_name = n;
     512                }
     513              if (do_formerr)
     514                {
     515                  /* Send a garbage query in an attempt to trigger EDNS
     516                     fallback.  */
     517                  char *n = xasprintf ("formerr.%s", probe_name);
     518                  gethostbyname (n);
     519                  free (n);
     520                }
     521  
     522              run_test (probe_name);
     523  
     524              free (probe_name);
     525              resolv_test_end (aux);
     526            }
     527  
     528    free_response_data ();
     529    return 0;
     530  }
     531  
     532  #include <support/test-driver.c>