From c3261cdb5b739c1e814de32e40ecd712ad987cdb Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 4 Apr 2017 17:31:52 +0200 Subject: [PATCH] resolv: Add tst-resolv-canonname --- ChangeLog | 6 + resolv/Makefile | 9 + resolv/tst-resolv-canonname.c | 313 ++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 resolv/tst-resolv-canonname.c diff --git a/ChangeLog b/ChangeLog index f2d13c4640..25c7887853 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-04-04 Florian Weimer + + * resolv/tst-resolv-canonname.c: New file. + * resolv/Makefile (tests): Add tst-resolv-canonname. + (tst-resolv-canonname): Link with -ldl, -lresolv, -lpthread. + 2017-04-04 Florian Weimer * include/arpa/nameser.h (__ns_name_ntop, __ns_name_unpack): diff --git a/resolv/Makefile b/resolv/Makefile index 64a7872d0f..5a867b79e0 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 0000000000..5daac33882 --- /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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* _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