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