xref: /openbsd-src/lib/libc/asr/asr_utils.c (revision ac9b4aacc1da35008afea06a5d23c2f2dea9b93e)
1 /*	$OpenBSD: asr_utils.c,v 1.1 2012/04/14 09:24:18 eric Exp $	*/
2 /*
3  * Copyright (c) 2009-2012	Eric Faurot	<eric@faurot.net>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "asr.h"
32 #include "asr_private.h"
33 
34 static int dname_check_label(const char*, size_t);
35 static ssize_t dname_expand(const unsigned char*, size_t, size_t, size_t*,
36     char *, size_t);
37 
38 static int unpack_data(struct packed*, void*, size_t);
39 static int unpack_u16(struct packed*, uint16_t*);
40 static int unpack_u32(struct packed*, uint32_t*);
41 static int unpack_inaddr(struct packed*, struct in_addr*);
42 static int unpack_in6addr(struct packed*, struct in6_addr*);
43 static int unpack_dname(struct packed*, char*, size_t);
44 
45 static int pack_data(struct packed*, const void*, size_t);
46 static int pack_u16(struct packed*, uint16_t);
47 static int pack_dname(struct packed*, const char*);
48 
49 static int
50 dname_check_label(const char *s, size_t l)
51 {
52 	if (l == 0 || l > 63)
53 		return (-1);
54 
55 	for(l--; l; l--, s++)
56 		if (!(isalnum(*s) || *s == '_' || *s == '-'))
57 			return (-1);
58 
59 	return (0);
60 }
61 
62 ssize_t
63 dname_from_fqdn(const char *str, char *dst, size_t max)
64 {
65 	ssize_t	 res;
66 	size_t	 l, n;
67 	char	*d;
68 
69 	res = 0;
70 
71 	/* special case: the root domain */
72 	if (str[0] == '.') {
73 		if (str[1] != '\0')
74 			return (-1);
75 		if (dst && max >= 1)
76 			*dst = '\0';
77 		return (1);
78 	}
79 
80 	for(; *str; str = d + 1) {
81 
82 		d = strchr(str, '.');
83 		if (d == NULL || d == str)
84 			return (-1);
85 
86 		l = (d - str);
87 
88 		if (dname_check_label(str, l) == -1)
89 			return (-1);
90 
91 		res += l + 1;
92 
93 		if (dst) {
94 			*dst++ = l;
95 			max -= 1;
96 			n = (l > max) ? max : l;
97 			memmove(dst, str, n);
98 			max -= n;
99 			if (max == 0)
100 				dst = NULL;
101 			else
102 				dst += n;
103 		}
104 	}
105 
106 	if (dst)
107 		*dst++ = '\0';
108 
109 	return (res + 1);
110 }
111 
112 static ssize_t
113 dname_expand(const unsigned char *data, size_t len, size_t offset,
114     size_t *newoffset, char *dst, size_t max)
115 {
116 	size_t		 n, count, end, ptr, start;
117 	ssize_t		 res;
118 
119 	if (offset >= len)
120 		return (-1);
121 
122 	res = 0;
123 	end = start = offset;
124 
125 	for(; (n = data[offset]); ) {
126 		if ((n & 0xc0) == 0xc0) {
127 			if (offset + 2 > len)
128 				return (-1);
129 			ptr = 256 * (n & ~0xc0) + data[offset + 1];
130 			if (ptr >= start)
131 				return (-1);
132 			if (end < offset + 2)
133 				end = offset + 2;
134 			offset = ptr;
135 			continue;
136 		}
137 		if (offset + n + 1 > len)
138 			return (-1);
139 
140 		if (dname_check_label(data + offset + 1, n) == -1)
141 			return (-1);
142 
143 		/* copy n + at offset+1 */
144 		if (dst != NULL && max != 0) {
145 			count = (max < n + 1) ? (max) : (n + 1);
146 			memmove(dst, data + offset, count);
147 			dst += count;
148 			max -= count;
149 		}
150 		res += n + 1;
151 		offset += n + 1;
152 		if (end < offset)
153 			end = offset;
154 	}
155 	if (end < offset + 1)
156 		end = offset + 1;
157 
158 	if (dst != NULL && max != 0)
159 		dst[0] = 0;
160 	if (newoffset)
161 		*newoffset = end;
162 	return (res + 1);
163 }
164 
165 void
166 packed_init(struct packed *pack, char *data, size_t len)
167 {
168 	pack->data = data;
169 	pack->len = len;
170 	pack->offset = 0;
171 	pack->err = NULL;
172 }
173 
174 static int
175 unpack_data(struct packed *p, void *data, size_t len)
176 {
177 	if (p->err)
178 		return (-1);
179 
180 	if (p->len - p->offset < len) {
181 		p->err = "too short";
182 		return (-1);
183 	}
184 
185 	memmove(data, p->data + p->offset, len);
186 	p->offset += len;
187 
188 	return (0);
189 }
190 
191 static int
192 unpack_u16(struct packed *p, uint16_t *u16)
193 {
194 	if (unpack_data(p, u16, 2) == -1)
195 		return (-1);
196 
197 	*u16 = ntohs(*u16);
198 
199 	return (0);
200 }
201 
202 static int
203 unpack_u32(struct packed *p, uint32_t *u32)
204 {
205 	if (unpack_data(p, u32, 4) == -1)
206 		return (-1);
207 
208 	*u32 = ntohl(*u32);
209 
210 	return (0);
211 }
212 
213 static int
214 unpack_inaddr(struct packed *p, struct in_addr *a)
215 {
216 	return (unpack_data(p, a, 4));
217 }
218 
219 static int
220 unpack_in6addr(struct packed *p, struct in6_addr *a6)
221 {
222 	return (unpack_data(p, a6, 16));
223 }
224 
225 static int
226 unpack_dname(struct packed *p, char *dst, size_t max)
227 {
228 	ssize_t e;
229 
230 	if (p->err)
231 		return (-1);
232 
233 	e = dname_expand(p->data, p->len, p->offset, &p->offset, dst, max);
234 	if (e == -1) {
235 		p->err = "bad domain name";
236 		return (-1);
237 	}
238 	if (e < 0 || e > MAXDNAME) {
239 		p->err = "domain name too long";
240 		return (-1);
241 	}
242 
243 	return (0);
244 }
245 
246 int
247 unpack_header(struct packed *p, struct header *h)
248 {
249 	if (unpack_data(p, h, HFIXEDSZ) == -1)
250 		return (-1);
251 
252 	h->flags = ntohs(h->flags);
253 	h->qdcount = ntohs(h->qdcount);
254 	h->ancount = ntohs(h->ancount);
255 	h->nscount = ntohs(h->nscount);
256 	h->arcount = ntohs(h->arcount);
257 
258 	return (0);
259 }
260 
261 int
262 unpack_query(struct packed *p, struct query *q)
263 {
264 	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
265 	unpack_u16(p, &q->q_type);
266 	unpack_u16(p, &q->q_class);
267 
268 	return (p->err) ? (-1) : (0);
269 }
270 
271 int
272 unpack_rr(struct packed *p, struct rr *rr)
273 {
274 	uint16_t	rdlen;
275 	size_t		save_offset;
276 
277 	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
278 	unpack_u16(p, &rr->rr_type);
279 	unpack_u16(p, &rr->rr_class);
280 	unpack_u32(p, &rr->rr_ttl);
281 	unpack_u16(p, &rdlen);
282 
283 	if (p->err)
284 		return (-1);
285 
286 	if (p->len - p->offset < rdlen) {
287 		p->err = "too short";
288 		return (-1);
289 	}
290 
291 	save_offset = p->offset;
292 
293 	switch(rr->rr_type) {
294 
295 	case T_CNAME:
296 		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
297 		break;
298 
299 	case T_MX:
300 		unpack_u16(p, &rr->rr.mx.preference);
301 		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
302 		break;
303 
304 	case T_NS:
305 		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
306 		break;
307 
308 	case T_PTR:
309 		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
310 		break;
311 
312 	case T_SOA:
313 		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
314 		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
315 		unpack_u32(p, &rr->rr.soa.serial);
316 		unpack_u32(p, &rr->rr.soa.refresh);
317 		unpack_u32(p, &rr->rr.soa.retry);
318 		unpack_u32(p, &rr->rr.soa.expire);
319 		unpack_u32(p, &rr->rr.soa.minimum);
320 		break;
321 
322 	case T_A:
323 		if (rr->rr_class != C_IN)
324 			goto other;
325 		unpack_inaddr(p, &rr->rr.in_a.addr);
326 		break;
327 
328 	case T_AAAA:
329 		if (rr->rr_class != C_IN)
330 			goto other;
331 		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
332 		break;
333 	default:
334 	other:
335 		rr->rr.other.rdata = p->data + p->offset;
336 		rr->rr.other.rdlen = rdlen;
337 		p->offset += rdlen;
338 	}
339 
340 	if (p->err)
341 		return (-1);
342 
343 	/* make sure that the advertised rdlen is really ok */
344 	if (p->offset - save_offset != rdlen)
345 		p->err = "bad dlen";
346 
347 	return (p->err) ? (-1) : (0);
348 }
349 
350 static int
351 pack_data(struct packed *p, const void *data, size_t len)
352 {
353 	if (p->err)
354 		return (-1);
355 
356 	if (p->len < p->offset + len) {
357 		p->err = "no space";
358 		return (-1);
359 	}
360 
361 	memmove(p->data + p->offset, data, len);
362 	p->offset += len;
363 
364 	return (0);
365 }
366 
367 static int
368 pack_u16(struct packed *p, uint16_t v)
369 {
370 	v = htons(v);
371 
372 	return (pack_data(p, &v, 2));
373 }
374 
375 static int
376 pack_dname(struct packed *p, const char *dname)
377 {
378 	/* dname compression would be nice to have here.
379 	 * need additionnal context.
380 	 */
381 	return (pack_data(p, dname, strlen(dname) + 1));
382 }
383 
384 int
385 pack_header(struct packed *p, const struct header *h)
386 {
387 	struct header c;
388 
389 	c.id = h->id;
390 	c.flags = htons(h->flags);
391 	c.qdcount = htons(h->qdcount);
392 	c.ancount = htons(h->ancount);
393 	c.nscount = htons(h->nscount);
394 	c.arcount = htons(h->arcount);
395 
396 	return (pack_data(p, &c, HFIXEDSZ));
397 }
398 
399 int
400 pack_query(struct packed *p, uint16_t type, uint16_t class, const char *dname)
401 {
402 	pack_dname(p, dname);
403 	pack_u16(p, type);
404 	pack_u16(p, class);
405 
406 	return (p->err) ? (-1) : (0);
407 }
408 
409 int
410 sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
411 {
412 	struct in_addr		 ina;
413 	struct in6_addr		 in6a;
414 	struct sockaddr_in	*sin;
415 	struct sockaddr_in6	*sin6;
416 
417 	switch (family) {
418 	case PF_UNSPEC:
419 		if (sockaddr_from_str(sa, PF_INET, str) == 0)
420 			return (0);
421 		return sockaddr_from_str(sa, PF_INET6, str);
422 
423 	case PF_INET:
424 		if (inet_pton(PF_INET, str, &ina) != 1)
425 			return (-1);
426 
427 		sin = (struct sockaddr_in *)sa;
428 		memset(sin, 0, sizeof *sin);
429 		sin->sin_len = sizeof(struct sockaddr_in);
430 		sin->sin_family = PF_INET;
431 		sin->sin_addr.s_addr = ina.s_addr;
432 		return (0);
433 
434 	case PF_INET6:
435 		if (inet_pton(PF_INET6, str, &in6a) != 1)
436 			return (-1);
437 
438 		sin6 = (struct sockaddr_in6 *)sa;
439 		memset(sin6, 0, sizeof *sin6);
440 		sin6->sin6_len = sizeof(struct sockaddr_in6);
441 		sin6->sin6_family = PF_INET6;
442 		sin6->sin6_addr = in6a;
443 		return (0);
444 
445 	default:
446 		break;
447 	}
448 
449 	return (-1);
450 }
451