1  /* Test _nss_dns_getcanonname_r corner cases.
       2     Copyright (C) 2017-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 <dlfcn.h>
      20  #include <errno.h>
      21  #include <gnu/lib-names.h>
      22  #include <netdb.h>
      23  #include <nss.h>
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <support/check.h>
      28  #include <support/resolv_test.h>
      29  #include <support/support.h>
      30  
      31  /* _nss_dns_getcanonname_r is not called during regular operation
      32     because nss_dns directly provides a canonical name, so we have to
      33     test it directly.  The function pointer is initialized by do_test
      34     below.  */
      35  static enum nss_status
      36  (*getcanonname) (const char *name, char *buffer, size_t buflen,
      37                   char **result, int *errnop, int *h_errnop);
      38  
      39  static void
      40  response (const struct resolv_response_context *ctx,
      41            struct resolv_response_builder *b,
      42            const char *qname, uint16_t qclass, uint16_t qtype)
      43  {
      44    int code;
      45    {
      46      char *tail;
      47      if (sscanf (qname, "code%d.%ms", &code, &tail) != 2
      48          || strcmp (tail, "example") != 0)
      49        FAIL_EXIT1 ("error: invalid QNAME: %s\n", qname);
      50      free (tail);
      51    }
      52  
      53    switch (code)
      54      {
      55      case 1:
      56        resolv_response_init (b, (struct resolv_response_flags) {});
      57        resolv_response_add_question (b, qname, qclass, qtype);
      58        resolv_response_section (b, ns_s_an);
      59        resolv_response_open_record (b, "www.example", qclass, qtype, 0);
      60        resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
      61        resolv_response_close_record (b);
      62        break;
      63      case 2:
      64        resolv_response_init (b, (struct resolv_response_flags) {});
      65        resolv_response_add_question (b, qname, qclass, qtype);
      66        resolv_response_section (b, ns_s_an);
      67        if (qtype == T_AAAA)
      68          {
      69            resolv_response_open_record (b, "www.example", qclass, qtype, 0);
      70            resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
      71            resolv_response_close_record (b);
      72            for (int i = 0; i < 30000; ++i)
      73              resolv_response_add_data (b, "", 1);
      74          }
      75        break;
      76      case 3:
      77        resolv_response_init (b, (struct resolv_response_flags) {});
      78        resolv_response_add_question (b, qname, qclass, qtype);
      79        resolv_response_section (b, ns_s_an);
      80        if (qtype == T_AAAA)
      81          {
      82            resolv_response_open_record (b, "www.example", qclass, qtype, 0);
      83            resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
      84            resolv_response_close_record (b);
      85          }
      86        else
      87          {
      88            for (int i = 0; i < 30000; ++i)
      89              resolv_response_add_data (b, "", 1);
      90          }
      91        break;
      92      case 4:
      93        resolv_response_init (b, (struct resolv_response_flags) {});
      94        resolv_response_add_question (b, qname, qclass, qtype);
      95        resolv_response_section (b, ns_s_an);
      96        resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
      97        resolv_response_add_name (b, "www.example");
      98        resolv_response_close_record (b);
      99        resolv_response_open_record (b, "www.example", qclass, qtype, 0);
     100        resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
     101        resolv_response_close_record (b);
     102        break;
     103      case 5:
     104        resolv_response_init (b, (struct resolv_response_flags) {});
     105        resolv_response_add_question (b, qname, qclass, qtype);
     106        resolv_response_section (b, ns_s_an);
     107        resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
     108        resolv_response_add_name (b, "www.example");
     109        resolv_response_close_record (b);
     110        resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
     111        resolv_response_add_name (b, "www1.example");
     112        resolv_response_close_record (b);
     113        resolv_response_open_record (b, "www1.example", qclass, qtype, 0);
     114        resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
     115        resolv_response_close_record (b);
     116        break;
     117      case 6:
     118        resolv_response_init (b, (struct resolv_response_flags) {});
     119        resolv_response_add_question (b, qname, qclass, qtype);
     120        resolv_response_section (b, ns_s_an);
     121        resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
     122        resolv_response_add_name (b, "www.example");
     123        resolv_response_close_record (b);
     124        resolv_response_open_record (b, qname, qclass, 46 /* RRSIG */, 0);
     125        resolv_response_add_name (b, ".");
     126        resolv_response_close_record (b);
     127        resolv_response_open_record (b, "www.example", qclass, qtype, 0);
     128        resolv_response_add_data (b, "\xC0\x00\x02\x01", 4);
     129        resolv_response_close_record (b);
     130        break;
     131      case 102:
     132        if (!ctx->tcp)
     133          {
     134            resolv_response_init (b, (struct resolv_response_flags) {.tc = true});
     135            resolv_response_add_question (b, qname, qclass, qtype);
     136          }
     137        else
     138          {
     139            resolv_response_init
     140              (b, (struct resolv_response_flags) {.ancount = 1});
     141            resolv_response_add_question (b, qname, qclass, qtype);
     142            resolv_response_section (b, ns_s_an);
     143            resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
     144            size_t to_fill = 65535 - resolv_response_length (b)
     145              - 2 /* length, "n" */ - 2 /* compression reference */
     146              - 2 /* RR type */;
     147            for (size_t i = 0; i < to_fill; ++i)
     148              resolv_response_add_data (b, "", 1);
     149            resolv_response_close_record (b);
     150            resolv_response_add_name (b, "n.example");
     151            uint16_t rrtype = htons (T_CNAME);
     152            resolv_response_add_data (b, &rrtype, sizeof (rrtype));
     153          }
     154        break;
     155      case 103:
     156        /* NODATA response.  */
     157        resolv_response_init (b, (struct resolv_response_flags) {});
     158        resolv_response_add_question (b, qname, qclass, qtype);
     159        break;
     160      case 104:
     161        resolv_response_init (b, (struct resolv_response_flags) {.ancount = 1});
     162        resolv_response_add_question (b, qname, qclass, qtype);
     163        /* No RR metadata.  */
     164        resolv_response_add_name (b, "www.example");
     165        break;
     166      case 105:
     167        if (qtype == T_A)
     168          {
     169            resolv_response_init (b, (struct resolv_response_flags) {});
     170            resolv_response_add_question (b, qname, qclass, qtype);
     171            /* No data, trigger AAAA query.  */
     172          }
     173        else
     174          {
     175            resolv_response_init
     176              (b, (struct resolv_response_flags) {.ancount = 1});
     177            resolv_response_add_question (b, qname, qclass, qtype);
     178            /* No RR metadata.  */
     179            resolv_response_add_name
     180              (b, "long-name-exceed-previously-initialized-buffer.example");
     181          }
     182        break;
     183      case 106:
     184        resolv_response_init (b, (struct resolv_response_flags) {.ancount = 1});
     185        resolv_response_add_question (b, qname, qclass, qtype);
     186        /* No RR metadata.  */
     187        resolv_response_add_name (b, "www.example");
     188        resolv_response_add_data (b, "\xff\xff", 2);
     189        break;
     190      case 107:
     191        if (qtype == T_A)
     192          {
     193            resolv_response_init (b, (struct resolv_response_flags) {});
     194            resolv_response_add_question (b, qname, qclass, qtype);
     195            /* No data, trigger AAAA query.  */
     196          }
     197        else
     198          {
     199            resolv_response_init
     200              (b, (struct resolv_response_flags) {.ancount = 1});
     201            resolv_response_add_question (b, qname, qclass, qtype);
     202            /* No RR metadata.  */
     203            resolv_response_add_name (b, "www.example");
     204            resolv_response_add_data (b, "\xff\xff", 2);
     205          }
     206        break;
     207      default:
     208        FAIL_EXIT1 ("error: invalid QNAME: %s (code %d)\n", qname, code);
     209      }
     210  }
     211  
     212  static void
     213  check (int code, const char *expected)
     214  {
     215    char qname[200];
     216    snprintf (qname, sizeof (qname), "code%d.example", code);
     217    char *result;
     218    enum nss_status status;
     219    {
     220      enum { buffer_size = 4096 };
     221      char *buffer = xmalloc (buffer_size);
     222      char *temp_result;
     223      int temp_errno;
     224      int temp_herrno;
     225      status = getcanonname
     226        (qname, buffer, buffer_size, &temp_result, &temp_errno, &temp_herrno);
     227      if (status == NSS_STATUS_SUCCESS)
     228        result = xstrdup (temp_result);
     229      else
     230        {
     231          errno = temp_errno;
     232          h_errno = temp_herrno;
     233        }
     234      free (buffer);
     235    }
     236  
     237    if (status == NSS_STATUS_SUCCESS)
     238      {
     239        if (expected != NULL)
     240          {
     241            if (strcmp (result, expected) != 0)
     242              {
     243                support_record_failure ();
     244                printf ("error: getcanonname (%s) failed\n", qname);
     245                printf ("error:  expected: %s\n", expected);
     246                printf ("error:  actual:   %s\n", result);
     247                free (result);
     248                return;
     249              }
     250          }
     251        else
     252          {
     253            support_record_failure ();
     254            printf ("error: getcanonname (%s) unexpected success\n", qname);
     255            printf ("error:  actual:   %s\n", result);
     256            free (result);
     257            return;
     258          }
     259        free (result);
     260      }
     261    else
     262      {
     263        if (expected != NULL)
     264          {
     265            support_record_failure ();
     266            printf ("error: getcanonname (%s) failed\n", qname);
     267            printf ("error:  expected: %s\n", expected);
     268            return;
     269          }
     270      }
     271  }
     272  
     273  
     274  static int
     275  do_test (void)
     276  {
     277    void *nss_dns_handle = dlopen (LIBNSS_DNS_SO, RTLD_LAZY);
     278    if (nss_dns_handle == NULL)
     279      FAIL_EXIT1 ("could not dlopen %s: %s", LIBNSS_DNS_SO, dlerror ());
     280    {
     281      const char *func = "_nss_dns_getcanonname_r";
     282      void *ptr = dlsym (nss_dns_handle, func);
     283      if (ptr == NULL)
     284        FAIL_EXIT1 ("could not look up %s: %s", func, dlerror ());
     285      getcanonname = ptr;
     286    }
     287  
     288    struct resolv_test *aux = resolv_test_start
     289      ((struct resolv_redirect_config)
     290       {
     291         .response_callback = response,
     292       });
     293  
     294    check (1, "www.example");
     295    check (2, "www.example");
     296    check (3, "www.example");
     297    check (4, "www.example");
     298    check (5, "www1.example");
     299  
     300    /* This should really result in "www.example", but the fake RRSIG
     301       record causes the current implementation to stop parsing.  */
     302    check (6, NULL);
     303  
     304    for (int i = 102; i <= 107; ++i)
     305    check (i, NULL);
     306  
     307    resolv_test_end (aux);
     308  
     309    TEST_VERIFY (dlclose (nss_dns_handle) == 0);
     310    return 0;
     311  }
     312  
     313  #include <support/test-driver.c>