(root)/
glibc-2.38/
support/
support_format_dns_packet.c
       1  /* Convert a DNS packet to a human-readable representation.
       2     Copyright (C) 2016-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 <support/format_nss.h>
      20  
      21  #include <arpa/inet.h>
      22  #include <resolv.h>
      23  #include <stdbool.h>
      24  #include <support/check.h>
      25  #include <support/support.h>
      26  #include <support/xmemstream.h>
      27  
      28  struct in_buffer
      29  {
      30    const unsigned char *data;
      31    size_t size;
      32  };
      33  
      34  static inline bool
      35  extract_16 (struct in_buffer *in, unsigned short *value)
      36  {
      37    if (in->size < 2)
      38      return false;
      39    *value = (in->data[0] << 8) | in->data[1];
      40    in->data += 2;
      41    in->size -= 2;
      42    return true;
      43  }
      44  
      45  static inline bool
      46  extract_32 (struct in_buffer *in, unsigned *value)
      47  {
      48    if (in->size < 4)
      49      return false;
      50    unsigned a = in->data[0];
      51    unsigned b = in->data[1];
      52    unsigned c = in->data[2];
      53    unsigned d = in->data[3];
      54    *value = (a << 24) | (b << 16) | (c << 8) | d;
      55    in->data += 4;
      56    in->size -= 4;
      57    return true;
      58  }
      59  
      60  static inline bool
      61  extract_bytes (struct in_buffer *in, size_t length, struct in_buffer *value)
      62  {
      63    if (in->size < length)
      64      return false;
      65    *value = (struct in_buffer) {in->data, length};
      66    in->data += length;
      67    in->size -= length;
      68    return true;
      69  }
      70  
      71  struct dname
      72  {
      73    char name[MAXDNAME + 1];
      74  };
      75  
      76  static bool
      77  extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value)
      78  {
      79    const unsigned char *full_end = full.data + full.size;
      80    /* Sanity checks; these indicate buffer misuse.  */
      81    TEST_VERIFY_EXIT
      82      (!(in->data < full.data || in->data > full_end
      83         || in->size > (size_t) (full_end - in->data)));
      84    int ret = dn_expand (full.data, full_end, in->data,
      85                         value->name, sizeof (value->name));
      86    if (ret < 0)
      87      return false;
      88    in->data += ret;
      89    in->size -= ret;
      90    return true;
      91  }
      92  
      93  static void
      94  extract_name_data (struct in_buffer full, struct in_buffer *rdata,
      95                     const struct dname *owner, const char *typename, FILE *out)
      96  {
      97    struct dname name;
      98    if (extract_name (full, rdata, &name))
      99      fprintf (out, "data: %s %s %s\n", owner->name, typename, name.name);
     100    else
     101      fprintf (out, "error: malformed CNAME/PTR record\n");
     102  }
     103  
     104  char *
     105  support_format_dns_packet (const unsigned char *buffer, size_t length)
     106  {
     107    struct in_buffer full = { buffer, length };
     108    struct in_buffer in = full;
     109    struct xmemstream mem;
     110    xopen_memstream (&mem);
     111  
     112    unsigned short txnid;
     113    unsigned short flags;
     114    unsigned short qdcount;
     115    unsigned short ancount;
     116    unsigned short nscount;
     117    unsigned short adcount;
     118    if (!(extract_16 (&in, &txnid)
     119          && extract_16 (&in, &flags)
     120          && extract_16 (&in, &qdcount)
     121          && extract_16 (&in, &ancount)
     122          && extract_16 (&in, &nscount)
     123          && extract_16 (&in, &adcount)))
     124      {
     125        fprintf (mem.out, "error: could not parse DNS header\n");
     126        goto out;
     127      }
     128    if (qdcount != 1)
     129      {
     130        fprintf (mem.out, "error: question count is %d, not 1\n", qdcount);
     131        goto out;
     132      }
     133    struct dname qname;
     134    if (!extract_name (full, &in, &qname))
     135      {
     136        fprintf (mem.out, "error: malformed QNAME\n");
     137        goto out;
     138      }
     139    unsigned short qtype;
     140    unsigned short qclass;
     141    if (!(extract_16 (&in, &qtype)
     142          && extract_16 (&in, &qclass)))
     143      {
     144        fprintf (mem.out, "error: malformed question\n");
     145        goto out;
     146      }
     147    if (qtype != T_A && qtype != T_AAAA && qtype != T_PTR)
     148      {
     149        fprintf (mem.out, "error: unsupported QTYPE %d\n", qtype);
     150        goto out;
     151      }
     152  
     153    fprintf (mem.out, "name: %s\n", qname.name);
     154  
     155    for (int i = 0; i < ancount; ++i)
     156      {
     157        struct dname rname;
     158        if (!extract_name (full, &in, &rname))
     159          {
     160            fprintf (mem.out, "error: malformed record name\n");
     161            goto out;
     162          }
     163        unsigned short rtype;
     164        unsigned short rclass;
     165        unsigned ttl;
     166        unsigned short rdlen;
     167        struct in_buffer rdata;
     168        if (!(extract_16 (&in, &rtype)
     169              && extract_16 (&in, &rclass)
     170              && extract_32 (&in, &ttl)
     171              && extract_16 (&in, &rdlen)
     172              && extract_bytes (&in, rdlen, &rdata)))
     173          {
     174            fprintf (mem.out, "error: malformed record header\n");
     175            goto out;
     176          }
     177        /* Skip non-matching record types.  */
     178        if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass)
     179          continue;
     180        switch (rtype)
     181          {
     182          case T_A:
     183            if (rdlen == 4)
     184                fprintf (mem.out, "address: %d.%d.%d.%d\n",
     185                         rdata.data[0],
     186                         rdata.data[1],
     187                         rdata.data[2],
     188                         rdata.data[3]);
     189            else
     190              fprintf (mem.out, "error: A record of size %d: %s\n",
     191                       rdlen, rname.name);
     192            break;
     193          case T_AAAA:
     194            {
     195              if (rdlen == 16)
     196                {
     197                  char buf[100];
     198                  if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
     199                    fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
     200                  else
     201                    fprintf (mem.out, "address: %s\n", buf);
     202                }
     203              else
     204                fprintf (mem.out, "error: AAAA record of size %d: %s\n",
     205                         rdlen, rname.name);
     206            }
     207            break;
     208          case T_CNAME:
     209            extract_name_data (full, &rdata, &rname, "CNAME", mem.out);
     210            break;
     211          case T_PTR:
     212            extract_name_data (full, &rdata, &rname, "PTR", mem.out);
     213            break;
     214          }
     215      }
     216  
     217   out:
     218    xfclose_memstream (&mem);
     219    return mem.buffer;
     220  }