[COMMITTED] resolv: Add tst-resolv-canonname

Submitted by Florian Weimer on April 4, 2017, 7:01 p.m.

Details

Message ID 20170404190133.A999F4035F5CB@oldenburg.str.redhat.com
State New
Headers show

Commit Message

Florian Weimer April 4, 2017, 7:01 p.m.
2017-04-04  Florian Weimer  <fweimer@redhat.com>

	* resolv/tst-resolv-canonname.c: New file.
	* resolv/Makefile (tests): Add tst-resolv-canonname.
	(tst-resolv-canonname): Link with -ldl, -lresolv, -lpthread.

Patch hide | download patch | download mbox

diff --git a/resolv/Makefile b/resolv/Makefile
index 64a7872..5a867b7 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -50,6 +50,13 @@  tests += \
   tst-resolv-network \
   tst-resolv-search \
 
+# These tests need libdl.
+ifeq (yes,$(build-shared))
+tests += \
+  tst-resolv-canonname \
+
+endif
+
 # This test sends millions of packets and is rather slow.
 xtests += tst-resolv-qtypes
 endif
@@ -128,6 +135,8 @@  $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library)
+$(objpfx)tst-resolv-canonname: \
+  $(libdl) $(objpfx)libresolv.so $(shared-thread-library)
 
 $(objpfx)tst-ns_name: $(objpfx)libresolv.so
 $(objpfx)tst-ns_name.out: tst-ns_name.data
diff --git a/resolv/tst-resolv-canonname.c b/resolv/tst-resolv-canonname.c
new file mode 100644
index 0000000..5daac33
--- /dev/null
+++ b/resolv/tst-resolv-canonname.c
@@ -0,0 +1,313 @@ 
+/* Test _nss_dns_getcanonname_r corner cases.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <gnu/lib-names.h>
+#include <netdb.h>
+#include <nss.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+/* _nss_dns_getcanonname_r is not called during regular operation
+   because nss_dns directly provides a canonical name, so we have to
+   test it directly.  The function pointer is initialized by do_test
+   below.  */
+static enum nss_status
+(*getcanonname) (const char *name, char *buffer, size_t buflen,
+                 char **result, int *errnop, int *h_errnop);
+
+static void
+response (const struct resolv_response_context *ctx,
+          struct resolv_response_builder *b,
+          const char *qname, uint16_t qclass, uint16_t qtype)
+{
+  int code;
+  {
+    char *tail;
+    if (sscanf (qname, "code%d.%ms", &code, &tail) != 2
+        || strcmp (tail, "example") != 0)
+      FAIL_EXIT1 ("error: invalid QNAME: %s\n", qname);
+    free (tail);
+  }
+
+  switch (code)
+    {
+    case 1:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      resolv_response_open_record (b, "www.example", qclass, qtype, 0);
+      resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+      resolv_response_close_record (b);
+      break;
+    case 2:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      if (qtype == T_AAAA)
+        {
+          resolv_response_open_record (b, "www.example", qclass, qtype, 0);
+          resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+          resolv_response_close_record (b);
+          for (int i = 0; i < 30000; ++i)
+            resolv_response_add_data (b, "", 1);
+        }
+      break;
+    case 3:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      if (qtype == T_AAAA)
+        {
+          resolv_response_open_record (b, "www.example", qclass, qtype, 0);
+          resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+          resolv_response_close_record (b);
+        }
+      else
+        {
+          for (int i = 0; i < 30000; ++i)
+            resolv_response_add_data (b, "", 1);
+        }
+      break;
+    case 4:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
+      resolv_response_add_name (b, "www.example");
+      resolv_response_close_record (b);
+      resolv_response_open_record (b, "www.example", qclass, qtype, 0);
+      resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+      resolv_response_close_record (b);
+      break;
+    case 5:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
+      resolv_response_add_name (b, "www.example");
+      resolv_response_close_record (b);
+      resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
+      resolv_response_add_name (b, "www1.example");
+      resolv_response_close_record (b);
+      resolv_response_open_record (b, "www1.example", qclass, qtype, 0);
+      resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+      resolv_response_close_record (b);
+      break;
+    case 6:
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      resolv_response_section (b, ns_s_an);
+      resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
+      resolv_response_add_name (b, "www.example");
+      resolv_response_close_record (b);
+      resolv_response_open_record (b, qname, qclass, 46 /* RRSIG */, 0);
+      resolv_response_add_name (b, ".");
+      resolv_response_close_record (b);
+      resolv_response_open_record (b, "www.example", qclass, qtype, 0);
+      resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
+      resolv_response_close_record (b);
+      break;
+    case 102:
+      if (!ctx->tcp)
+        {
+          resolv_response_init (b, (struct resolv_response_flags) {.tc = true});
+          resolv_response_add_question (b, qname, qclass, qtype);
+        }
+      else
+        {
+          resolv_response_init
+            (b, (struct resolv_response_flags) {.ancount = 1});
+          resolv_response_add_question (b, qname, qclass, qtype);
+          resolv_response_section (b, ns_s_an);
+          resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
+          size_t to_fill = 65535 - resolv_response_length (b)
+            - 2 /* length, "n" */ - 2 /* compression reference */
+            - 2 /* RR type */;
+          for (size_t i = 0; i < to_fill; ++i)
+            resolv_response_add_data (b, "", 1);
+          resolv_response_close_record (b);
+          resolv_response_add_name (b, "n.example");
+          uint16_t rrtype = htons (T_CNAME);
+          resolv_response_add_data (b, &rrtype, sizeof (rrtype));
+        }
+      break;
+    case 103:
+      /* NODATA repsonse.  */
+      resolv_response_init (b, (struct resolv_response_flags) {});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      break;
+    case 104:
+      resolv_response_init (b, (struct resolv_response_flags) {.ancount = 1});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      /* No RR metadata.  */
+      resolv_response_add_name (b, "www.example");
+      break;
+    case 105:
+      if (qtype == T_A)
+        {
+          resolv_response_init (b, (struct resolv_response_flags) {});
+          resolv_response_add_question (b, qname, qclass, qtype);
+          /* No data, trigger AAAA query.  */
+        }
+      else
+        {
+          resolv_response_init
+            (b, (struct resolv_response_flags) {.ancount = 1});
+          resolv_response_add_question (b, qname, qclass, qtype);
+          /* No RR metadata.  */
+          resolv_response_add_name
+            (b, "long-name-exceed-previously-initialized-buffer.example");
+        }
+      break;
+    case 106:
+      resolv_response_init (b, (struct resolv_response_flags) {.ancount = 1});
+      resolv_response_add_question (b, qname, qclass, qtype);
+      /* No RR metadata.  */
+      resolv_response_add_name (b, "www.example");
+      resolv_response_add_data (b, "\xff\xff", 2);
+      break;
+    case 107:
+      if (qtype == T_A)
+        {
+          resolv_response_init (b, (struct resolv_response_flags) {});
+          resolv_response_add_question (b, qname, qclass, qtype);
+          /* No data, trigger AAAA query.  */
+        }
+      else
+        {
+          resolv_response_init
+            (b, (struct resolv_response_flags) {.ancount = 1});
+          resolv_response_add_question (b, qname, qclass, qtype);
+          /* No RR metadata.  */
+          resolv_response_add_name (b, "www.example");
+          resolv_response_add_data (b, "\xff\xff", 2);
+        }
+      break;
+    default:
+      FAIL_EXIT1 ("error: invalid QNAME: %s (code %d)\n", qname, code);
+    }
+}
+
+static void
+check (int code, const char *expected)
+{
+  char qname[200];
+  snprintf (qname, sizeof (qname), "code%d.example", code);
+  char *result;
+  enum nss_status status;
+  {
+    enum { buffer_size = 4096 };
+    char *buffer = xmalloc (buffer_size);
+    char *temp_result;
+    int temp_errno;
+    int temp_herrno;
+    status = getcanonname
+      (qname, buffer, buffer_size, &temp_result, &temp_errno, &temp_herrno);
+    if (status == NSS_STATUS_SUCCESS)
+      result = xstrdup (temp_result);
+    else
+      {
+        errno = temp_errno;
+        h_errno = temp_herrno;
+      }
+    free (buffer);
+  }
+
+  if (status == NSS_STATUS_SUCCESS)
+    {
+      if (expected != NULL)
+        {
+          if (strcmp (result, expected) != 0)
+            {
+              support_record_failure ();
+              printf ("error: getcanonname (%s) failed\n", qname);
+              printf ("error:  expected: %s\n", expected);
+              printf ("error:  actual:   %s\n", result);
+              free (result);
+              return;
+            }
+        }
+      else
+        {
+          support_record_failure ();
+          printf ("error: getcanonname (%s) unexpected success\n", qname);
+          printf ("error:  actual:   %s\n", result);
+          free (result);
+          return;
+        }
+      free (result);
+    }
+  else
+    {
+      if (expected != NULL)
+        {
+          support_record_failure ();
+          printf ("error: getcanonname (%s) failed\n", qname);
+          printf ("error:  expected: %s\n", expected);
+          return;
+        }
+    }
+}
+
+
+static int
+do_test (void)
+{
+  void *nss_dns_handle = dlopen (LIBNSS_DNS_SO, RTLD_LAZY);
+  if (nss_dns_handle == NULL)
+    FAIL_EXIT1 ("could not dlopen %s: %s", LIBNSS_DNS_SO, dlerror ());
+  {
+    const char *func = "_nss_dns_getcanonname_r";
+    void *ptr = dlsym (nss_dns_handle, func);
+    if (ptr == NULL)
+      FAIL_EXIT1 ("could not look up %s: %s", func, dlerror ());
+    getcanonname = ptr;
+  }
+
+  struct resolv_test *aux = resolv_test_start
+    ((struct resolv_redirect_config)
+     {
+       .response_callback = response,
+     });
+
+  check (1, "www.example");
+  check (2, "www.example");
+  check (3, "www.example");
+  check (4, "www.example");
+  check (5, "www1.example");
+
+  /* This should really result in "www.example", but the fake RRSIG
+     record causes the current implementation to stop parsing.  */
+  check (6, NULL);
+
+  for (int i = 102; i <= 107; ++i)
+  check (i, NULL);
+
+  resolv_test_end (aux);
+
+  TEST_VERIFY (dlclose (nss_dns_handle) == 0);
+  return 0;
+}
+
+#include <support/test-driver.c>