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