(root)/
glibc-2.38/
resolv/
tst-resolv-byaddr.c
       1  /* Test reverse DNS lookup.
       2     Copyright (C) 2022-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 <arpa/inet.h>
      20  #include <errno.h>
      21  #include <netdb.h>
      22  #include <stdbool.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <support/check.h>
      27  #include <support/check_nss.h>
      28  #include <support/next_to_fault.h>
      29  #include <support/resolv_test.h>
      30  #include <support/support.h>
      31  
      32  #include "tst-resolv-maybe_insert_sig.h"
      33  
      34  /* QNAME format:
      35  
      36     ADDRESSES.CNAMES...(lots of 0s)...8.b.d.0.1.0.0.2.ip6.arpa.
      37     CNAMES|ADDRESSES.2.0.192.in-addr-arpa.
      38  
      39     For the IPv4 reverse lookup, the address count is in the lower
      40     bits.
      41  
      42     CNAMES is the length of the CNAME chain, ADDRESSES is the number of
      43     addresses in the response.  The special value 15 means that there
      44     are no addresses, and the RCODE is NXDOMAIN.  */
      45  static void
      46  response (const struct resolv_response_context *ctx,
      47            struct resolv_response_builder *b,
      48            const char *qname, uint16_t qclass, uint16_t qtype)
      49  {
      50    TEST_COMPARE (qclass, C_IN);
      51    TEST_COMPARE (qtype, T_PTR);
      52  
      53    unsigned int addresses, cnames, bits;
      54    char *tail;
      55    if (strstr (qname, "ip6.arpa") != NULL
      56        && sscanf (qname, "%x.%x.%ms", &addresses, &cnames, &tail) == 3)
      57      TEST_COMPARE_STRING (tail, "\
      58  0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
      59    else if (sscanf (qname, "%u.%ms", &bits, &tail) == 2)
      60      {
      61        TEST_COMPARE_STRING (tail, "2.0.192.in-addr.arpa");
      62        addresses = bits & 0x0f;
      63        cnames = bits >> 4;
      64      }
      65    else
      66      FAIL_EXIT1 ("invalid QNAME: %s", qname);
      67    free (tail);
      68  
      69    int rcode;
      70    if (addresses == 15)
      71      {
      72        /* Special case: Use no addresses with NXDOMAIN response.  */
      73        rcode = ns_r_nxdomain;
      74        addresses = 0;
      75      }
      76    else
      77      rcode = 0;
      78  
      79    struct resolv_response_flags flags = { .rcode = rcode };
      80    resolv_response_init (b, flags);
      81    resolv_response_add_question (b, qname, qclass, qtype);
      82    resolv_response_section (b, ns_s_an);
      83    maybe_insert_sig (b, qname);
      84  
      85    /* Provide the requested number of CNAME records.  */
      86    char *previous_name = (char *) qname;
      87    for (int unique = 0; unique < cnames; ++unique)
      88      {
      89        resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
      90        char *new_name = xasprintf ("%d.alias.example", unique);
      91        resolv_response_add_name (b, new_name);
      92        resolv_response_close_record (b);
      93  
      94        maybe_insert_sig (b, qname);
      95  
      96        if (previous_name != qname)
      97          free (previous_name);
      98        previous_name = new_name;
      99      }
     100  
     101    for (int unique = 0; unique < addresses; ++unique)
     102      {
     103        resolv_response_open_record (b, previous_name, qclass, T_PTR, 60);
     104        char *ptr = xasprintf ("unique-%d.cnames-%u.addresses-%u.example",
     105                               unique, cnames, addresses);
     106        resolv_response_add_name (b, ptr);
     107        free (ptr);
     108        resolv_response_close_record (b);
     109      }
     110  
     111    if (previous_name != qname)
     112      free (previous_name);
     113  }
     114  
     115  /* Used to check that gethostbyaddr_r does not write past the buffer
     116     end.  */
     117  static struct support_next_to_fault ntf;
     118  
     119  /* Perform a gethostbyaddr call and check the result.  */
     120  static void
     121  check_gethostbyaddr (const char *address, const char *expected)
     122  {
     123    unsigned char bytes[16];
     124    unsigned int byteslen;
     125    int family;
     126    if (strchr (address, ':') != NULL)
     127      {
     128        family = AF_INET6;
     129        byteslen = 16;
     130      }
     131    else
     132      {
     133        family = AF_INET;
     134        byteslen = 4;
     135      }
     136    TEST_COMPARE (inet_pton (family, address, bytes), 1);
     137  
     138    struct hostent *e = gethostbyaddr (bytes, byteslen, family);
     139    check_hostent (address, e, expected);
     140  
     141    if (e == NULL)
     142      return;
     143  
     144    /* Try gethostbyaddr_r with increasing sizes until success.  First
     145       compute a reasonable minimum buffer size, to avoid many pointless
     146       attempts.  */
     147    size_t minimum_size = strlen (e->h_name);
     148    for (int i = 0; e->h_addr_list[i] != NULL; ++i)
     149      minimum_size += e->h_length + sizeof (char *);
     150    for (int i = 0; e->h_aliases[i] != NULL; ++i)
     151      minimum_size += strlen (e->h_aliases[i]) + 1 + sizeof (char *);
     152  
     153    /* Gradually increase the size until success.  */
     154    for (size_t size = minimum_size; size < ntf.length; ++size)
     155      {
     156        struct hostent result;
     157        int herrno;
     158        int ret = gethostbyaddr_r (bytes, byteslen, family, &result,
     159                                   ntf.buffer + ntf.length - size, size,
     160                                   &e, &herrno);
     161        if (ret == ERANGE)
     162          /* Retry with larger size.  */
     163          TEST_COMPARE (herrno, NETDB_INTERNAL);
     164        else if (ret == 0)
     165          {
     166           TEST_VERIFY (size > minimum_size);
     167           check_hostent (address, e, expected);
     168           return;
     169          }
     170        else
     171          FAIL_EXIT1 ("Unexpected gethostbyaddr_r failure: %d", ret);
     172      }
     173  
     174    FAIL_EXIT1 ("gethostbyaddr_r always failed for: %s", address);
     175  }
     176  
     177  /* Perform a getnameinfo call and check the result.  */
     178  static void
     179  check_getnameinfo (const char *address, const char *expected)
     180  {
     181    struct sockaddr_in sin = { };
     182    struct sockaddr_in6 sin6 = { };
     183    void *sa;
     184    socklen_t salen;
     185    if (strchr (address, ':') != NULL)
     186      {
     187        sin6.sin6_family = AF_INET6;
     188        TEST_COMPARE (inet_pton (AF_INET6, address, &sin6.sin6_addr), 1);
     189        sin6.sin6_port = htons (80);
     190        sa = &sin6;
     191        salen = sizeof (sin6);
     192      }
     193    else
     194      {
     195        sin.sin_family = AF_INET;
     196        TEST_COMPARE (inet_pton (AF_INET, address, &sin.sin_addr), 1);
     197        sin.sin_port = htons (80);
     198        sa = &sin;
     199        salen = sizeof (sin);
     200      }
     201  
     202    char host[64];
     203    char service[64];
     204    int ret = getnameinfo (sa, salen, host,
     205                           sizeof (host), service, sizeof (service),
     206                           NI_NAMEREQD | NI_NUMERICSERV);
     207    switch (ret)
     208      {
     209      case 0:
     210        TEST_COMPARE_STRING (host, expected);
     211        TEST_COMPARE_STRING (service, "80");
     212        break;
     213      case EAI_SYSTEM:
     214        TEST_COMPARE_STRING (strerror (errno), expected);
     215        break;
     216      default:
     217        TEST_COMPARE_STRING (gai_strerror (ret), expected);
     218      }
     219  }
     220  
     221  static int
     222  do_test (void)
     223  {
     224    /* Some reasonably upper bound for the maximum response size.  */
     225    ntf = support_next_to_fault_allocate (4096);
     226  
     227    struct resolv_test *obj = resolv_test_start
     228      ((struct resolv_redirect_config)
     229       {
     230         .response_callback = response
     231       });
     232  
     233    for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig)
     234      {
     235        insert_sig = do_insert_sig;
     236  
     237        /* No PTR record, RCODE=0.  */
     238        check_gethostbyaddr ("192.0.2.0", "error: NO_RECOVERY\n");
     239        check_getnameinfo ("192.0.2.0", "Name or service not known");
     240        check_gethostbyaddr ("192.0.2.16", "error: NO_RECOVERY\n");
     241        check_getnameinfo ("192.0.2.16", "Name or service not known");
     242        check_gethostbyaddr ("192.0.2.32", "error: NO_RECOVERY\n");
     243        check_getnameinfo ("192.0.2.32", "Name or service not known");
     244        check_gethostbyaddr ("2001:db8::", "error: NO_RECOVERY\n");
     245        check_getnameinfo ("2001:db8::", "Name or service not known");
     246        check_gethostbyaddr ("2001:db8::10", "error: NO_RECOVERY\n");
     247        check_getnameinfo ("2001:db8::10", "Name or service not known");
     248        check_gethostbyaddr ("2001:db8::20", "error: NO_RECOVERY\n");
     249        check_getnameinfo ("2001:db8::20", "Name or service not known");
     250  
     251        /* No PTR record, NXDOMAIN.  */
     252        check_gethostbyaddr ("192.0.2.15", "error: HOST_NOT_FOUND\n");
     253        check_getnameinfo ("192.0.2.15", "Name or service not known");
     254        check_gethostbyaddr ("192.0.2.31", "error: HOST_NOT_FOUND\n");
     255        check_getnameinfo ("192.0.2.31", "Name or service not known");
     256        check_gethostbyaddr ("192.0.2.47", "error: HOST_NOT_FOUND\n");
     257        check_getnameinfo ("192.0.2.47", "Name or service not known");
     258        check_gethostbyaddr ("2001:db8::f", "error: HOST_NOT_FOUND\n");
     259        check_getnameinfo ("2001:db8::f", "Name or service not known");
     260        check_gethostbyaddr ("2001:db8::1f", "error: HOST_NOT_FOUND\n");
     261        check_getnameinfo ("2001:db8::1f", "Name or service not known");
     262        check_gethostbyaddr ("2001:db8::2f", "error: HOST_NOT_FOUND\n");
     263        check_getnameinfo ("2001:db8::2f", "Name or service not known");
     264  
     265        /* Actual response data.  Only the first PTR record is returned.  */
     266        check_gethostbyaddr ("192.0.2.1",
     267                             "name: unique-0.cnames-0.addresses-1.example\n"
     268                             "address: 192.0.2.1\n");
     269        check_getnameinfo ("192.0.2.1",
     270                           "unique-0.cnames-0.addresses-1.example");
     271        check_gethostbyaddr ("192.0.2.17",
     272                             "name: unique-0.cnames-1.addresses-1.example\n"
     273                             "address: 192.0.2.17\n");
     274        check_getnameinfo ("192.0.2.17",
     275                           "unique-0.cnames-1.addresses-1.example");
     276        check_gethostbyaddr ("192.0.2.18",
     277                             "name: unique-0.cnames-1.addresses-2.example\n"
     278                             "address: 192.0.2.18\n");
     279        check_getnameinfo ("192.0.2.18",
     280                           "unique-0.cnames-1.addresses-2.example");
     281        check_gethostbyaddr ("192.0.2.33",
     282                             "name: unique-0.cnames-2.addresses-1.example\n"
     283                             "address: 192.0.2.33\n");
     284        check_getnameinfo ("192.0.2.33",
     285                           "unique-0.cnames-2.addresses-1.example");
     286        check_gethostbyaddr ("192.0.2.34",
     287                             "name: unique-0.cnames-2.addresses-2.example\n"
     288                             "address: 192.0.2.34\n");
     289        check_getnameinfo ("192.0.2.34",
     290                           "unique-0.cnames-2.addresses-2.example");
     291  
     292        /* Same for IPv6 addresses.  */
     293        check_gethostbyaddr ("2001:db8::1",
     294                             "name: unique-0.cnames-0.addresses-1.example\n"
     295                             "address: 2001:db8::1\n");
     296        check_getnameinfo ("2001:db8::1",
     297                           "unique-0.cnames-0.addresses-1.example");
     298        check_gethostbyaddr ("2001:db8::11",
     299                             "name: unique-0.cnames-1.addresses-1.example\n"
     300                             "address: 2001:db8::11\n");
     301        check_getnameinfo ("2001:db8::11",
     302                           "unique-0.cnames-1.addresses-1.example");
     303        check_gethostbyaddr ("2001:db8::12",
     304                             "name: unique-0.cnames-1.addresses-2.example\n"
     305                             "address: 2001:db8::12\n");
     306        check_getnameinfo ("2001:db8::12",
     307                           "unique-0.cnames-1.addresses-2.example");
     308        check_gethostbyaddr ("2001:db8::21",
     309                             "name: unique-0.cnames-2.addresses-1.example\n"
     310                             "address: 2001:db8::21\n");
     311        check_getnameinfo ("2001:db8::21",
     312                           "unique-0.cnames-2.addresses-1.example");
     313        check_gethostbyaddr ("2001:db8::22",
     314                             "name: unique-0.cnames-2.addresses-2.example\n"
     315                             "address: 2001:db8::22\n");
     316        check_getnameinfo ("2001:db8::22",
     317                           "unique-0.cnames-2.addresses-2.example");
     318      }
     319  
     320    resolv_test_end (obj);
     321  
     322    support_next_to_fault_free (&ntf);
     323    return 0;
     324  }
     325  
     326  #include <support/test-driver.c>