|
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 |
}
|