1/*	$OpenBSD: unpack_dns.c,v 1.3 2022/01/20 14:18:10 naddy Exp $	*/
2
3/*
4 * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <string.h>
20
21#include "unpack_dns.h"
22
23static int unpack_data(struct unpack *, void *, size_t);
24static int unpack_u16(struct unpack *, uint16_t *);
25static int unpack_u32(struct unpack *, uint32_t *);
26static int unpack_inaddr(struct unpack *, struct in_addr *);
27static int unpack_in6addr(struct unpack *, struct in6_addr *);
28static int unpack_dname(struct unpack *, char *, size_t);
29
30void
31unpack_init(struct unpack *unpack, const char *buf, size_t len)
32{
33	unpack->buf = buf;
34	unpack->len = len;
35	unpack->offset = 0;
36	unpack->err = NULL;
37}
38
39int
40unpack_header(struct unpack *p, struct dns_header *h)
41{
42	if (unpack_data(p, h, HFIXEDSZ) == -1)
43		return (-1);
44
45	h->flags = ntohs(h->flags);
46	h->qdcount = ntohs(h->qdcount);
47	h->ancount = ntohs(h->ancount);
48	h->nscount = ntohs(h->nscount);
49	h->arcount = ntohs(h->arcount);
50
51	return (0);
52}
53
54int
55unpack_query(struct unpack *p, struct dns_query *q)
56{
57	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
58	unpack_u16(p, &q->q_type);
59	unpack_u16(p, &q->q_class);
60
61	return (p->err) ? (-1) : (0);
62}
63
64int
65unpack_rr(struct unpack *p, struct dns_rr *rr)
66{
67	uint16_t	rdlen;
68	size_t		save_offset;
69
70	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
71	unpack_u16(p, &rr->rr_type);
72	unpack_u16(p, &rr->rr_class);
73	unpack_u32(p, &rr->rr_ttl);
74	unpack_u16(p, &rdlen);
75
76	if (p->err)
77		return (-1);
78
79	if (p->len - p->offset < rdlen) {
80		p->err = "too short";
81		return (-1);
82	}
83
84	save_offset = p->offset;
85
86	switch (rr->rr_type) {
87
88	case T_CNAME:
89		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
90		break;
91
92	case T_MX:
93		unpack_u16(p, &rr->rr.mx.preference);
94		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
95		break;
96
97	case T_NS:
98		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
99		break;
100
101	case T_PTR:
102		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
103		break;
104
105	case T_SOA:
106		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
107		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
108		unpack_u32(p, &rr->rr.soa.serial);
109		unpack_u32(p, &rr->rr.soa.refresh);
110		unpack_u32(p, &rr->rr.soa.retry);
111		unpack_u32(p, &rr->rr.soa.expire);
112		unpack_u32(p, &rr->rr.soa.minimum);
113		break;
114
115	case T_A:
116		if (rr->rr_class != C_IN)
117			goto other;
118		unpack_inaddr(p, &rr->rr.in_a.addr);
119		break;
120
121	case T_AAAA:
122		if (rr->rr_class != C_IN)
123			goto other;
124		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
125		break;
126	default:
127	other:
128		rr->rr.other.rdata = p->buf + p->offset;
129		rr->rr.other.rdlen = rdlen;
130		p->offset += rdlen;
131	}
132
133	if (p->err)
134		return (-1);
135
136	/* make sure that the advertised rdlen is really ok */
137	if (p->offset - save_offset != rdlen)
138		p->err = "bad dlen";
139
140	return (p->err) ? (-1) : (0);
141}
142
143ssize_t
144dname_expand(const unsigned char *data, size_t len, size_t offset,
145    size_t *newoffset, char *dst, size_t max)
146{
147	size_t		 n, count, end, ptr, start;
148	ssize_t		 res;
149
150	if (offset >= len)
151		return (-1);
152
153	res = 0;
154	end = start = offset;
155
156	for (; (n = data[offset]); ) {
157		if ((n & 0xc0) == 0xc0) {
158			if (offset + 2 > len)
159				return (-1);
160			ptr = 256 * (n & ~0xc0) + data[offset + 1];
161			if (ptr >= start)
162				return (-1);
163			if (end < offset + 2)
164				end = offset + 2;
165			offset = start = ptr;
166			continue;
167		}
168		if (offset + n + 1 > len)
169			return (-1);
170
171		/* copy n + at offset+1 */
172		if (dst != NULL && max != 0) {
173			count = (max < n + 1) ? (max) : (n + 1);
174			memmove(dst, data + offset, count);
175			dst += count;
176			max -= count;
177		}
178		res += n + 1;
179		offset += n + 1;
180		if (end < offset)
181			end = offset;
182	}
183	if (end < offset + 1)
184		end = offset + 1;
185
186	if (dst != NULL && max != 0)
187		dst[0] = 0;
188	if (newoffset)
189		*newoffset = end;
190	return (res + 1);
191}
192
193char *
194print_dname(const char *_dname, char *buf, size_t max)
195{
196	const unsigned char *dname = _dname;
197	char    *res;
198	size_t   left, count;
199
200	if (_dname[0] == 0) {
201		(void)strlcpy(buf, ".", max);
202		return buf;
203	}
204
205	res = buf;
206	left = max - 1;
207	while (dname[0] && left) {
208		count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
209		memmove(buf, dname + 1, count);
210		dname += dname[0] + 1;
211		left -= count;
212		buf += count;
213		if (left) {
214			left -= 1;
215			*buf++ = '.';
216		}
217	}
218	buf[0] = 0;
219
220	return (res);
221}
222
223static int
224unpack_data(struct unpack *p, void *data, size_t len)
225{
226	if (p->err)
227		return (-1);
228
229	if (p->len - p->offset < len) {
230		p->err = "too short";
231		return (-1);
232	}
233
234	memmove(data, p->buf + p->offset, len);
235	p->offset += len;
236
237	return (0);
238}
239
240static int
241unpack_u16(struct unpack *p, uint16_t *u16)
242{
243	if (unpack_data(p, u16, 2) == -1)
244		return (-1);
245
246	*u16 = ntohs(*u16);
247
248	return (0);
249}
250
251static int
252unpack_u32(struct unpack *p, uint32_t *u32)
253{
254	if (unpack_data(p, u32, 4) == -1)
255		return (-1);
256
257	*u32 = ntohl(*u32);
258
259	return (0);
260}
261
262static int
263unpack_inaddr(struct unpack *p, struct in_addr *a)
264{
265	return (unpack_data(p, a, 4));
266}
267
268static int
269unpack_in6addr(struct unpack *p, struct in6_addr *a6)
270{
271	return (unpack_data(p, a6, 16));
272}
273
274static int
275unpack_dname(struct unpack *p, char *dst, size_t max)
276{
277	ssize_t e;
278
279	if (p->err)
280		return (-1);
281
282	e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
283	if (e == -1) {
284		p->err = "bad domain name";
285		return (-1);
286	}
287	if (e < 0 || e > MAXDNAME) {
288		p->err = "domain name too long";
289		return (-1);
290	}
291
292	return (0);
293}
294