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