Blame resolv/tst-resolv-txnid-collision.c

Packit Service cc978c
/* Test parallel queries with transaction ID collisions.
Packit Service cc978c
   Copyright (C) 2020 Free Software Foundation, Inc.
Packit Service cc978c
   This file is part of the GNU C Library.
Packit Service cc978c
Packit Service cc978c
   The GNU C Library is free software; you can redistribute it and/or
Packit Service cc978c
   modify it under the terms of the GNU Lesser General Public
Packit Service cc978c
   License as published by the Free Software Foundation; either
Packit Service cc978c
   version 2.1 of the License, or (at your option) any later version.
Packit Service cc978c
Packit Service cc978c
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service cc978c
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cc978c
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service cc978c
   Lesser General Public License for more details.
Packit Service cc978c
Packit Service cc978c
   You should have received a copy of the GNU Lesser General Public
Packit Service cc978c
   License along with the GNU C Library; if not, see
Packit Service cc978c
   <https://www.gnu.org/licenses/>.  */
Packit Service cc978c
Packit Service cc978c
#include <arpa/nameser.h>
Packit Service cc978c
#include <array_length.h>
Packit Service cc978c
#include <resolv-internal.h>
Packit Service cc978c
#include <resolv_context.h>
Packit Service cc978c
#include <stdbool.h>
Packit Service cc978c
#include <stdio.h>
Packit Service cc978c
#include <string.h>
Packit Service cc978c
#include <support/check.h>
Packit Service cc978c
#include <support/check_nss.h>
Packit Service cc978c
#include <support/resolv_test.h>
Packit Service cc978c
#include <support/support.h>
Packit Service cc978c
#include <support/test-driver.h>
Packit Service cc978c
Packit Service cc978c
/* Result of parsing a DNS question name.
Packit Service cc978c
Packit Service cc978c
   A question name has the form reorder-N-M-rcode-C.example.net, where
Packit Service cc978c
   N and M are either 0 and 1, corresponding to the reorder member,
Packit Service cc978c
   and C is a number that will be stored in the rcode field.
Packit Service cc978c
Packit Service cc978c
   Also see parse_qname below.  */
Packit Service cc978c
struct parsed_qname
Packit Service cc978c
{
Packit Service cc978c
  /* The DNS response code requested from the first server.  The
Packit Service cc978c
     second server always responds with RCODE zero.  */
Packit Service cc978c
  int rcode;
Packit Service cc978c
Packit Service cc978c
  /* Indicates whether to perform reordering in the responses from the
Packit Service cc978c
     respective server.  */
Packit Service cc978c
  bool reorder[2];
Packit Service cc978c
};
Packit Service cc978c
Packit Service cc978c
/* Fills *PARSED based on QNAME.  */
Packit Service cc978c
static void
Packit Service cc978c
parse_qname (struct parsed_qname *parsed, const char *qname)
Packit Service cc978c
{
Packit Service cc978c
  int reorder0;
Packit Service cc978c
  int reorder1;
Packit Service cc978c
  int rcode;
Packit Service cc978c
  char *suffix;
Packit Service cc978c
  if (sscanf (qname, "reorder-%d-%d.rcode-%d.%ms",
Packit Service cc978c
              &reorder0, &reorder1, &rcode, &suffix) == 4)
Packit Service cc978c
    {
Packit Service cc978c
      if (reorder0 != 0)
Packit Service cc978c
        TEST_COMPARE (reorder0, 1);
Packit Service cc978c
      if (reorder1 != 0)
Packit Service cc978c
        TEST_COMPARE (reorder1, 1);
Packit Service cc978c
      TEST_VERIFY (rcode >= 0 && rcode <= 15);
Packit Service cc978c
      TEST_COMPARE_STRING (suffix, "example.net");
Packit Service cc978c
      free (suffix);
Packit Service cc978c
Packit Service cc978c
      parsed->rcode = rcode;
Packit Service cc978c
      parsed->reorder[0] = reorder0;
Packit Service cc978c
      parsed->reorder[1] = reorder1;
Packit Service cc978c
    }
Packit Service cc978c
  else
Packit Service cc978c
    FAIL_EXIT1 ("unexpected query: %s", qname);
Packit Service cc978c
}
Packit Service cc978c
Packit Service cc978c
/* Used to construct a response. The first server responds with an
Packit Service cc978c
   error, the second server succeeds.  */
Packit Service cc978c
static void
Packit Service cc978c
build_response (const struct resolv_response_context *ctx,
Packit Service cc978c
                struct resolv_response_builder *b,
Packit Service cc978c
                const char *qname, uint16_t qclass, uint16_t qtype)
Packit Service cc978c
{
Packit Service cc978c
  struct parsed_qname parsed;
Packit Service cc978c
  parse_qname (&parsed, qname);
Packit Service cc978c
Packit Service cc978c
  switch (ctx->server_index)
Packit Service cc978c
    {
Packit Service cc978c
    case 0:
Packit Service cc978c
      {
Packit Service cc978c
        struct resolv_response_flags flags = { 0 };
Packit Service cc978c
        if (parsed.rcode == 0)
Packit Service cc978c
          /* Simulate a delegation in case a NODATA (RCODE zero)
Packit Service cc978c
             response is requested.  */
Packit Service cc978c
          flags.clear_ra = true;
Packit Service cc978c
        else
Packit Service cc978c
          flags.rcode = parsed.rcode;
Packit Service cc978c
Packit Service cc978c
        resolv_response_init (b, flags);
Packit Service cc978c
        resolv_response_add_question (b, qname, qclass, qtype);
Packit Service cc978c
      }
Packit Service cc978c
      break;
Packit Service cc978c
Packit Service cc978c
    case 1:
Packit Service cc978c
      {
Packit Service cc978c
        struct resolv_response_flags flags = { 0, };
Packit Service cc978c
        resolv_response_init (b, flags);
Packit Service cc978c
        resolv_response_add_question (b, qname, qclass, qtype);
Packit Service cc978c
Packit Service cc978c
        resolv_response_section (b, ns_s_an);
Packit Service cc978c
        resolv_response_open_record (b, qname, qclass, qtype, 0);
Packit Service cc978c
        if (qtype == T_A)
Packit Service cc978c
          {
Packit Service cc978c
            char ipv4[4] = { 192, 0, 2, 1 };
Packit Service cc978c
            resolv_response_add_data (b, &ipv4, sizeof (ipv4));
Packit Service cc978c
          }
Packit Service cc978c
        else
Packit Service cc978c
          {
Packit Service cc978c
            char ipv6[16]
Packit Service cc978c
              = { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
Packit Service cc978c
            resolv_response_add_data (b, &ipv6, sizeof (ipv6));
Packit Service cc978c
          }
Packit Service cc978c
        resolv_response_close_record (b);
Packit Service cc978c
      }
Packit Service cc978c
      break;
Packit Service cc978c
    }
Packit Service cc978c
}
Packit Service cc978c
Packit Service cc978c
/* Used to reorder responses.  */
Packit Service cc978c
struct resolv_response_context *previous_query;
Packit Service cc978c
Packit Service cc978c
/* Used to keep track of the queries received.  */
Packit Service cc978c
static int previous_server_index = -1;
Packit Service cc978c
static uint16_t previous_qtype;
Packit Service cc978c
Packit Service cc978c
/* For each server, buffer the first query and then send both answers
Packit Service cc978c
   to the second query, reordered if requested.  */
Packit Service cc978c
static void
Packit Service cc978c
response (const struct resolv_response_context *ctx,
Packit Service cc978c
          struct resolv_response_builder *b,
Packit Service cc978c
          const char *qname, uint16_t qclass, uint16_t qtype)
Packit Service cc978c
{
Packit Service cc978c
  TEST_VERIFY (qtype == T_A || qtype == T_AAAA);
Packit Service cc978c
  if (ctx->server_index != 0)
Packit Service cc978c
    TEST_COMPARE (ctx->server_index, 1);
Packit Service cc978c
Packit Service cc978c
  struct parsed_qname parsed;
Packit Service cc978c
  parse_qname (&parsed, qname);
Packit Service cc978c
Packit Service cc978c
  if (previous_query == NULL)
Packit Service cc978c
    {
Packit Service cc978c
      /* No buffered query.  Record this query and do not send a
Packit Service cc978c
         response.  */
Packit Service cc978c
      TEST_COMPARE (previous_qtype, 0);
Packit Service cc978c
      previous_query = resolv_response_context_duplicate (ctx);
Packit Service cc978c
      previous_qtype = qtype;
Packit Service cc978c
      resolv_response_drop (b);
Packit Service cc978c
      previous_server_index = ctx->server_index;
Packit Service cc978c
Packit Service cc978c
      if (test_verbose)
Packit Service cc978c
        printf ("info: buffering first query for: %s\n", qname);
Packit Service cc978c
    }
Packit Service cc978c
  else
Packit Service cc978c
    {
Packit Service cc978c
      TEST_VERIFY (previous_query != 0);
Packit Service cc978c
      TEST_COMPARE (ctx->server_index, previous_server_index);
Packit Service cc978c
      TEST_VERIFY (previous_qtype != qtype); /* Not a duplicate.  */
Packit Service cc978c
Packit Service cc978c
      /* If reordering, send a response for this query explicitly, and
Packit Service cc978c
         then skip the implicit send.  */
Packit Service cc978c
      if (parsed.reorder[ctx->server_index])
Packit Service cc978c
        {
Packit Service cc978c
          if (test_verbose)
Packit Service cc978c
            printf ("info: sending reordered second response for: %s\n",
Packit Service cc978c
                    qname);
Packit Service cc978c
          build_response (ctx, b, qname, qclass, qtype);
Packit Service cc978c
          resolv_response_send_udp (ctx, b);
Packit Service cc978c
          resolv_response_drop (b);
Packit Service cc978c
        }
Packit Service cc978c
Packit Service cc978c
      /* Build a response for the previous query and send it, thus
Packit Service cc978c
         reordering the two responses.  */
Packit Service cc978c
      {
Packit Service cc978c
        if (test_verbose)
Packit Service cc978c
          printf ("info: sending first response for: %s\n", qname);
Packit Service cc978c
        struct resolv_response_builder *btmp
Packit Service cc978c
          = resolv_response_builder_allocate (previous_query->query_buffer,
Packit Service cc978c
                                              previous_query->query_length);
Packit Service cc978c
        build_response (ctx, btmp, qname, qclass, previous_qtype);
Packit Service cc978c
        resolv_response_send_udp (ctx, btmp);
Packit Service cc978c
        resolv_response_builder_free (btmp);
Packit Service cc978c
      }
Packit Service cc978c
Packit Service cc978c
      /* If not reordering, send the reply as usual.  */
Packit Service cc978c
      if (!parsed.reorder[ctx->server_index])
Packit Service cc978c
        {
Packit Service cc978c
          if (test_verbose)
Packit Service cc978c
            printf ("info: sending non-reordered second response for: %s\n",
Packit Service cc978c
                    qname);
Packit Service cc978c
          build_response (ctx, b, qname, qclass, qtype);
Packit Service cc978c
        }
Packit Service cc978c
Packit Service cc978c
      /* Unbuffer the response and prepare for the next query.  */
Packit Service cc978c
      resolv_response_context_free (previous_query);
Packit Service cc978c
      previous_query = NULL;
Packit Service cc978c
      previous_qtype = 0;
Packit Service cc978c
      previous_server_index = -1;
Packit Service cc978c
    }
Packit Service cc978c
}
Packit Service cc978c
Packit Service cc978c
/* Runs a query for QNAME and checks for the expected reply.  See
Packit Service cc978c
   struct parsed_qname for the expected format for QNAME.  */
Packit Service cc978c
static void
Packit Service cc978c
test_qname (const char *qname, int rcode)
Packit Service cc978c
{
Packit Service cc978c
  struct resolv_context *ctx = __resolv_context_get ();
Packit Service cc978c
  TEST_VERIFY_EXIT (ctx != NULL);
Packit Service cc978c
Packit Service cc978c
  unsigned char q1[512];
Packit Service cc978c
  int q1len = res_mkquery (QUERY, qname, C_IN, T_A, NULL, 0, NULL,
Packit Service cc978c
                           q1, sizeof (q1));
Packit Service cc978c
  TEST_VERIFY_EXIT (q1len > 12);
Packit Service cc978c
Packit Service cc978c
  unsigned char q2[512];
Packit Service cc978c
  int q2len = res_mkquery (QUERY, qname, C_IN, T_AAAA, NULL, 0, NULL,
Packit Service cc978c
                           q2, sizeof (q2));
Packit Service cc978c
  TEST_VERIFY_EXIT (q2len > 12);
Packit Service cc978c
Packit Service cc978c
  /* Produce a transaction ID collision.  */
Packit Service cc978c
  memcpy (q2, q1, 2);
Packit Service cc978c
Packit Service cc978c
  unsigned char ans1[512];
Packit Service cc978c
  unsigned char *ans1p = ans1;
Packit Service cc978c
  unsigned char *ans2p = NULL;
Packit Service cc978c
  int nans2p = 0;
Packit Service cc978c
  int resplen2 = 0;
Packit Service cc978c
  int ans2p_malloced = 0;
Packit Service cc978c
Packit Service cc978c
  /* Perform a parallel A/AAAA query.  */
Packit Service cc978c
  int resplen1 = __res_context_send (ctx, q1, q1len, q2, q2len,
Packit Service cc978c
                                     ans1, sizeof (ans1), &ans1p,
Packit Service cc978c
                                     &ans2p, &nans2p,
Packit Service cc978c
                                     &resplen2, &ans2p_malloced);
Packit Service cc978c
Packit Service cc978c
  TEST_VERIFY (resplen1 > 12);
Packit Service cc978c
  TEST_VERIFY (resplen2 > 12);
Packit Service cc978c
  if (resplen1 <= 12 || resplen2 <= 12)
Packit Service cc978c
    return;
Packit Service cc978c
Packit Service cc978c
  if (rcode == 1 || rcode == 3)
Packit Service cc978c
    {
Packit Service cc978c
      /* Format Error and Name Error responses does not trigger
Packit Service cc978c
         switching to the next server.  */
Packit Service cc978c
      TEST_COMPARE (ans1p[3] & 0x0f, rcode);
Packit Service cc978c
      TEST_COMPARE (ans2p[3] & 0x0f, rcode);
Packit Service cc978c
      return;
Packit Service cc978c
    }
Packit Service cc978c
Packit Service cc978c
  /* The response should be successful.  */
Packit Service cc978c
  TEST_COMPARE (ans1p[3] & 0x0f, 0);
Packit Service cc978c
  TEST_COMPARE (ans2p[3] & 0x0f, 0);
Packit Service cc978c
Packit Service cc978c
  /* Due to bug 19691, the answer may not be in the slot matching the
Packit Service cc978c
     query.  Assume that the AAAA response is the longer one.  */
Packit Service cc978c
  unsigned char *a_answer;
Packit Service cc978c
  int a_answer_length;
Packit Service cc978c
  unsigned char *aaaa_answer;
Packit Service cc978c
  int aaaa_answer_length;
Packit Service cc978c
  if (resplen2 > resplen1)
Packit Service cc978c
    {
Packit Service cc978c
      a_answer = ans1p;
Packit Service cc978c
      a_answer_length = resplen1;
Packit Service cc978c
      aaaa_answer = ans2p;
Packit Service cc978c
      aaaa_answer_length = resplen2;
Packit Service cc978c
    }
Packit Service cc978c
  else
Packit Service cc978c
    {
Packit Service cc978c
      a_answer = ans2p;
Packit Service cc978c
      a_answer_length = resplen2;
Packit Service cc978c
      aaaa_answer = ans1p;
Packit Service cc978c
      aaaa_answer_length = resplen1;
Packit Service cc978c
    }
Packit Service cc978c
Packit Service cc978c
  {
Packit Service cc978c
    char *expected = xasprintf ("name: %s\n"
Packit Service cc978c
                                "address: 192.0.2.1\n",
Packit Service cc978c
                                qname);
Packit Service cc978c
    check_dns_packet (qname, a_answer, a_answer_length, expected);
Packit Service cc978c
    free (expected);
Packit Service cc978c
  }
Packit Service cc978c
  {
Packit Service cc978c
    char *expected = xasprintf ("name: %s\n"
Packit Service cc978c
                                "address: 2001:db8::1\n",
Packit Service cc978c
                                qname);
Packit Service cc978c
    check_dns_packet (qname, aaaa_answer, aaaa_answer_length, expected);
Packit Service cc978c
    free (expected);
Packit Service cc978c
  }
Packit Service cc978c
Packit Service cc978c
  if (ans2p_malloced)
Packit Service cc978c
    free (ans2p);
Packit Service cc978c
Packit Service cc978c
  __resolv_context_put (ctx);
Packit Service cc978c
}
Packit Service cc978c
Packit Service cc978c
static int
Packit Service cc978c
do_test (void)
Packit Service cc978c
{
Packit Service cc978c
  struct resolv_test *aux = resolv_test_start
Packit Service cc978c
    ((struct resolv_redirect_config)
Packit Service cc978c
     {
Packit Service cc978c
       .response_callback = response,
Packit Service 9783f1
Packit Service 9783f1
       /* The response callback use global state (the previous_*
Packit Service 9783f1
          variables), and query processing must therefore be
Packit Service 9783f1
          serialized.  */
Packit Service 9783f1
       .single_thread_udp = true,
Packit Service cc978c
     });
Packit Service cc978c
Packit Service cc978c
  for (int rcode = 0; rcode <= 5; ++rcode)
Packit Service cc978c
    for (int do_reorder_0 = 0; do_reorder_0 < 2; ++do_reorder_0)
Packit Service cc978c
      for (int do_reorder_1 = 0; do_reorder_1 < 2; ++do_reorder_1)
Packit Service cc978c
        {
Packit Service cc978c
          char *qname = xasprintf ("reorder-%d-%d.rcode-%d.example.net",
Packit Service cc978c
                                   do_reorder_0, do_reorder_1, rcode);
Packit Service cc978c
          test_qname (qname, rcode);
Packit Service cc978c
          free (qname);
Packit Service cc978c
        }
Packit Service cc978c
Packit Service cc978c
  resolv_test_end (aux);
Packit Service cc978c
Packit Service cc978c
  return 0;
Packit Service cc978c
}
Packit Service cc978c
Packit Service cc978c
#include <support/test-driver.c>