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
23 static int unpack_data(struct unpack *, void *, size_t);
24 static int unpack_u16(struct unpack *, uint16_t *);
25 static int unpack_u32(struct unpack *, uint32_t *);
26 static int unpack_inaddr(struct unpack *, struct in_addr *);
27 static int unpack_in6addr(struct unpack *, struct in6_addr *);
28 static int unpack_dname(struct unpack *, char *, size_t);
29
30 void
unpack_init(struct unpack * unpack,const char * buf,size_t len)31 unpack_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
39 int
unpack_header(struct unpack * p,struct dns_header * h)40 unpack_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
54 int
unpack_query(struct unpack * p,struct dns_query * q)55 unpack_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
64 int
unpack_rr(struct unpack * p,struct dns_rr * rr)65 unpack_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
143 ssize_t
dname_expand(const unsigned char * data,size_t len,size_t offset,size_t * newoffset,char * dst,size_t max)144 dname_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
193 char *
print_dname(const char * _dname,char * buf,size_t max)194 print_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
223 static int
unpack_data(struct unpack * p,void * data,size_t len)224 unpack_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
240 static int
unpack_u16(struct unpack * p,uint16_t * u16)241 unpack_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
251 static int
unpack_u32(struct unpack * p,uint32_t * u32)252 unpack_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
262 static int
unpack_inaddr(struct unpack * p,struct in_addr * a)263 unpack_inaddr(struct unpack *p, struct in_addr *a)
264 {
265 return (unpack_data(p, a, 4));
266 }
267
268 static int
unpack_in6addr(struct unpack * p,struct in6_addr * a6)269 unpack_in6addr(struct unpack *p, struct in6_addr *a6)
270 {
271 return (unpack_data(p, a6, 16));
272 }
273
274 static int
unpack_dname(struct unpack * p,char * dst,size_t max)275 unpack_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