(root)/
glibc-2.38/
resolv/
res-noaaaa.c
       1  /* Implement suppression of AAAA queries.
       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 <resolv.h>
      20  #include <string.h>
      21  #include <resolv-internal.h>
      22  #include <resolv_context.h>
      23  #include <arpa/nameser.h>
      24  
      25  /* Returns true if the question type at P matches EXPECTED, and the
      26     class is IN.  */
      27  static bool
      28  qtype_matches (const unsigned char *p, int expected)
      29  {
      30    /* This assumes that T_A/C_IN constants are less than 256, which
      31       they are.  */
      32    return p[0] == 0 && p[1] == expected && p[2] == 0 && p[3] == C_IN;
      33  }
      34  
      35  /* Handle RES_NOAAAA translation of AAAA queries.  To produce a Name
      36     Error (NXDOMAIN) response for domain names that do not exist, it is
      37     still necessary to send a query.  Using question type A is a
      38     conservative choice.  In the returned answer, it is necessary to
      39     switch back the question type to AAAA.  */
      40  bool
      41  __res_handle_no_aaaa (struct resolv_context *ctx,
      42                        const unsigned char *buf, int buflen,
      43                        unsigned char *ans, int anssiz, int *result)
      44  {
      45    /* AAAA mode is not active, or the query looks invalid (will not be
      46       able to be parsed).  */
      47    if ((ctx->resp->options & RES_NOAAAA) == 0
      48        || buflen <= sizeof (HEADER))
      49      return false;
      50  
      51    /* The replacement A query is produced here.  */
      52    struct
      53    {
      54      HEADER header;
      55      unsigned char question[NS_MAXCDNAME + 4];
      56    } replacement;
      57    memcpy (&replacement.header, buf, sizeof (replacement.header));
      58  
      59    if (replacement.header.qr
      60        || replacement.header.opcode != 0
      61        || replacement.header.rcode != 0
      62        || ntohs (replacement.header.qdcount) != 1
      63        || ntohs (replacement.header.ancount) != 0
      64        || ntohs (replacement.header.nscount) != 0)
      65      /* Not a well-formed question.  Let the core resolver code produce
      66         the proper error.  */
      67      return false;
      68  
      69    /* Disable EDNS0.  */
      70    replacement.header.arcount = htons (0);
      71  
      72    /* Extract the QNAME.  */
      73    int ret = __ns_name_unpack (buf, buf + buflen, buf + sizeof (HEADER),
      74                                replacement.question, NS_MAXCDNAME);
      75    if (ret < 0)
      76      /* Format error.  */
      77      return false;
      78  
      79    /* Compute the end of the question name.  */
      80    const unsigned char *after_question = buf + sizeof (HEADER) + ret;
      81  
      82    /* Check that we are dealing with an AAAA query.  */
      83    if (buf + buflen - after_question < 4
      84        || !qtype_matches (after_question, T_AAAA))
      85      return false;
      86  
      87    /* Find the place to store the type/class data in the replacement
      88       query.  */
      89    after_question = replacement.question;
      90    /* This cannot fail because __ns_name_unpack above produced a valid
      91       domain name.  */
      92    (void) __ns_name_skip (&after_question, &replacement.question[NS_MAXCDNAME]);
      93    unsigned char *start_of_query = (unsigned char *) &replacement;
      94    const unsigned char *end_of_query = after_question + 4;
      95  
      96    /* Produce an A/IN query.  */
      97    {
      98      unsigned char *p = (unsigned char *) after_question;
      99      p[0] = 0;
     100      p[1] = T_A;
     101      p[2] = 0;
     102      p[3] = C_IN;
     103    }
     104  
     105    /* Clear the output buffer, to avoid reading undefined data when
     106       rewriting the result from A to AAAA.  */
     107    memset (ans, 0, anssiz);
     108  
     109    /* Always perform the message translation, independent of the error
     110       code.  */
     111    ret = __res_context_send (ctx,
     112                              start_of_query, end_of_query - start_of_query,
     113                              NULL, 0, ans, anssiz,
     114                              NULL, NULL, NULL, NULL, NULL);
     115  
     116    /* Patch in the AAAA question type if there is room and the A query
     117       type was received.  */
     118    after_question = ans + sizeof (HEADER);
     119    if (__ns_name_skip (&after_question, ans + anssiz) == 0
     120        && ans + anssiz - after_question >= 4
     121        && qtype_matches (after_question, T_A))
     122      {
     123        ((unsigned char *) after_question)[1] = T_AAAA;
     124  
     125        /* Create an aligned copy of the header.  Hide all data except
     126           the question from the response.  Put back the header.  There is
     127           no need to change the response code.  The zero answer count turns
     128           a positive response with data into a no-data response.  */
     129        memcpy (&replacement.header, ans, sizeof (replacement.header));
     130        replacement.header.ancount = htons (0);
     131        replacement.header.nscount = htons (0);
     132        replacement.header.arcount = htons (0);
     133        memcpy (ans, &replacement.header, sizeof (replacement.header));
     134  
     135        /* Truncate the reply.  */
     136        if (ret <= 0)
     137          *result = ret;
     138        else
     139          *result = after_question - ans + 4;
     140      }
     141  
     142    return true;
     143  }