Blame support/support_format_dns_packet.c

Packit 6c4009
/* Convert a DNS packet to a human-readable representation.
Packit 6c4009
   Copyright (C) 2016-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <support/format_nss.h>
Packit 6c4009
Packit 6c4009
#include <arpa/inet.h>
Packit 6c4009
#include <resolv.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/support.h>
Packit 6c4009
#include <support/xmemstream.h>
Packit 6c4009
Packit 6c4009
struct in_buffer
Packit 6c4009
{
Packit 6c4009
  const unsigned char *data;
Packit 6c4009
  size_t size;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
extract_8 (struct in_buffer *in, unsigned char *value)
Packit 6c4009
{
Packit 6c4009
  if (in->size == 0)
Packit 6c4009
    return false;
Packit 6c4009
  *value = in->data[0];
Packit 6c4009
  ++in->data;
Packit 6c4009
  --in->size;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
extract_16 (struct in_buffer *in, unsigned short *value)
Packit 6c4009
{
Packit 6c4009
  if (in->size < 2)
Packit 6c4009
    return false;
Packit 6c4009
  *value = (in->data[0] << 8) | in->data[1];
Packit 6c4009
  in->data += 2;
Packit 6c4009
  in->size -= 2;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
extract_32 (struct in_buffer *in, unsigned *value)
Packit 6c4009
{
Packit 6c4009
  if (in->size < 4)
Packit 6c4009
    return false;
Packit 6c4009
  unsigned a = in->data[0];
Packit 6c4009
  unsigned b = in->data[1];
Packit 6c4009
  unsigned c = in->data[2];
Packit 6c4009
  unsigned d = in->data[3];
Packit 6c4009
  *value = (a << 24) | (b << 16) | (c << 8) | d;
Packit 6c4009
  in->data += 4;
Packit 6c4009
  in->size -= 4;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline bool
Packit 6c4009
extract_bytes (struct in_buffer *in, size_t length, struct in_buffer *value)
Packit 6c4009
{
Packit 6c4009
  if (in->size < length)
Packit 6c4009
    return false;
Packit 6c4009
  *value = (struct in_buffer) {in->data, length};
Packit 6c4009
  in->data += length;
Packit 6c4009
  in->size -= length;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
struct dname
Packit 6c4009
{
Packit 6c4009
  char name[MAXDNAME + 1];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static bool
Packit 6c4009
extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value)
Packit 6c4009
{
Packit 6c4009
  const unsigned char *full_end = full.data + full.size;
Packit 6c4009
  /* Sanity checks; these indicate buffer misuse.  */
Packit 6c4009
  TEST_VERIFY_EXIT
Packit 6c4009
    (!(in->data < full.data || in->data > full_end
Packit 6c4009
       || in->size > (size_t) (full_end - in->data)));
Packit 6c4009
  int ret = dn_expand (full.data, full_end, in->data,
Packit 6c4009
                       value->name, sizeof (value->name));
Packit 6c4009
  if (ret < 0)
Packit 6c4009
    return false;
Packit 6c4009
  in->data += ret;
Packit 6c4009
  in->size -= ret;
Packit 6c4009
  return true;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
char *
Packit 6c4009
support_format_dns_packet (const unsigned char *buffer, size_t length)
Packit 6c4009
{
Packit 6c4009
  struct in_buffer full = { buffer, length };
Packit 6c4009
  struct in_buffer in = full;
Packit 6c4009
  struct xmemstream mem;
Packit 6c4009
  xopen_memstream (&mem;;
Packit 6c4009
Packit 6c4009
  unsigned short txnid;
Packit 6c4009
  unsigned short flags;
Packit 6c4009
  unsigned short qdcount;
Packit 6c4009
  unsigned short ancount;
Packit 6c4009
  unsigned short nscount;
Packit 6c4009
  unsigned short adcount;
Packit 6c4009
  if (!(extract_16 (&in, &txnid)
Packit 6c4009
        && extract_16 (&in, &flags)
Packit 6c4009
        && extract_16 (&in, &qdcount)
Packit 6c4009
        && extract_16 (&in, &ancount)
Packit 6c4009
        && extract_16 (&in, &nscount)
Packit 6c4009
        && extract_16 (&in, &adcount)))
Packit 6c4009
    {
Packit 6c4009
      fprintf (mem.out, "error: could not parse DNS header\n");
Packit 6c4009
      goto out;
Packit 6c4009
    }
Packit 6c4009
  if (qdcount != 1)
Packit 6c4009
    {
Packit 6c4009
      fprintf (mem.out, "error: question count is %d, not 1\n", qdcount);
Packit 6c4009
      goto out;
Packit 6c4009
    }
Packit 6c4009
  struct dname qname;
Packit 6c4009
  if (!extract_name (full, &in, &qname))
Packit 6c4009
    {
Packit 6c4009
      fprintf (mem.out, "error: malformed QNAME\n");
Packit 6c4009
      goto out;
Packit 6c4009
    }
Packit 6c4009
  unsigned short qtype;
Packit 6c4009
  unsigned short qclass;
Packit 6c4009
  if (!(extract_16 (&in, &qtype)
Packit 6c4009
        && extract_16 (&in, &qclass)))
Packit 6c4009
    {
Packit 6c4009
      fprintf (mem.out, "error: malformed question\n");
Packit 6c4009
      goto out;
Packit 6c4009
    }
Packit 6c4009
  if (qtype != T_A && qtype != T_AAAA && qtype != T_PTR)
Packit 6c4009
    {
Packit 6c4009
      fprintf (mem.out, "error: unsupported QTYPE %d\n", qtype);
Packit 6c4009
      goto out;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  fprintf (mem.out, "name: %s\n", qname.name);
Packit 6c4009
Packit 6c4009
  for (int i = 0; i < ancount; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct dname rname;
Packit 6c4009
      if (!extract_name (full, &in, &rname))
Packit 6c4009
        {
Packit 6c4009
          fprintf (mem.out, "error: malformed record name\n");
Packit 6c4009
          goto out;
Packit 6c4009
        }
Packit 6c4009
      unsigned short rtype;
Packit 6c4009
      unsigned short rclass;
Packit 6c4009
      unsigned ttl;
Packit 6c4009
      unsigned short rdlen;
Packit 6c4009
      struct in_buffer rdata;
Packit 6c4009
      if (!(extract_16 (&in, &rtype)
Packit 6c4009
            && extract_16 (&in, &rclass)
Packit 6c4009
            && extract_32 (&in, &ttl)
Packit 6c4009
            && extract_16 (&in, &rdlen)
Packit 6c4009
            && extract_bytes (&in, rdlen, &rdata)))
Packit 6c4009
        {
Packit 6c4009
          fprintf (mem.out, "error: malformed record header\n");
Packit 6c4009
          goto out;
Packit 6c4009
        }
Packit 6c4009
      /* Skip non-matching record types.  */
Packit 6c4009
      if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass)
Packit 6c4009
        continue;
Packit 6c4009
      switch (rtype)
Packit 6c4009
        {
Packit 6c4009
        case T_A:
Packit 6c4009
          if (rdlen == 4)
Packit 6c4009
              fprintf (mem.out, "address: %d.%d.%d.%d\n",
Packit 6c4009
                       rdata.data[0],
Packit 6c4009
                       rdata.data[1],
Packit 6c4009
                       rdata.data[2],
Packit 6c4009
                       rdata.data[3]);
Packit 6c4009
          else
Packit 6c4009
            fprintf (mem.out, "error: A record of size %d: %s\n",
Packit 6c4009
                     rdlen, rname.name);
Packit 6c4009
          break;
Packit 6c4009
        case T_AAAA:
Packit 6c4009
          {
Packit 6c4009
            if (rdlen == 16)
Packit 6c4009
              {
Packit 6c4009
                char buf[100];
Packit 6c4009
                if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL)
Packit 6c4009
                  fprintf (mem.out, "error: AAAA record decoding failed: %m\n");
Packit 6c4009
                else
Packit 6c4009
                  fprintf (mem.out, "address: %s\n", buf);
Packit 6c4009
              }
Packit 6c4009
            else
Packit 6c4009
              fprintf (mem.out, "error: AAAA record of size %d: %s\n",
Packit 6c4009
                       rdlen, rname.name);
Packit 6c4009
          }
Packit 6c4009
          break;
Packit 6c4009
        case T_CNAME:
Packit 6c4009
        case T_PTR:
Packit 6c4009
          {
Packit 6c4009
            struct dname name;
Packit 6c4009
            if (extract_name (full, &rdata, &name))
Packit 6c4009
              fprintf (mem.out, "name: %s\n", name.name);
Packit 6c4009
            else
Packit 6c4009
              fprintf (mem.out, "error: malformed CNAME/PTR record\n");
Packit 6c4009
          }
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  xfclose_memstream (&mem;;
Packit 6c4009
  return mem.buffer;
Packit 6c4009
}