xref: /openbsd-src/lib/libc/asr/asr_utils.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /*	$OpenBSD: asr_utils.c,v 1.17 2017/02/27 11:38:08 jca 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 #include <net/if.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <netdb.h>
25 
26 #include <asr.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "asr_private.h"
36 
37 static int dname_check_label(const char *, size_t);
38 static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
39     char *, size_t);
40 
41 static int unpack_data(struct asr_unpack *, void *, size_t);
42 static int unpack_u16(struct asr_unpack *, uint16_t *);
43 static int unpack_u32(struct asr_unpack *, uint32_t *);
44 static int unpack_inaddr(struct asr_unpack *, struct in_addr *);
45 static int unpack_in6addr(struct asr_unpack *, struct in6_addr *);
46 static int unpack_dname(struct asr_unpack *, char *, size_t);
47 
48 static int pack_data(struct asr_pack *, const void *, size_t);
49 static int pack_u16(struct asr_pack *, uint16_t);
50 static int pack_dname(struct asr_pack *, const char *);
51 
52 static int
53 dname_check_label(const char *s, size_t l)
54 {
55 	if (l == 0 || l > 63)
56 		return (-1);
57 
58 	return (0);
59 }
60 
61 ssize_t
62 _asr_dname_from_fqdn(const char *str, char *dst, size_t max)
63 {
64 	ssize_t	 res;
65 	size_t	 l, n;
66 	char	*d;
67 
68 	res = 0;
69 
70 	/* special case: the root domain */
71 	if (str[0] == '.') {
72 		if (str[1] != '\0')
73 			return (-1);
74 		if (dst && max >= 1)
75 			*dst = '\0';
76 		return (1);
77 	}
78 
79 	for (; *str; str = d + 1) {
80 
81 		d = strchr(str, '.');
82 		if (d == NULL || d == str)
83 			return (-1);
84 
85 		l = (d - str);
86 
87 		if (dname_check_label(str, l) == -1)
88 			return (-1);
89 
90 		res += l + 1;
91 
92 		if (dst) {
93 			*dst++ = l;
94 			max -= 1;
95 			n = (l > max) ? max : l;
96 			memmove(dst, str, n);
97 			max -= n;
98 			if (max == 0)
99 				dst = NULL;
100 			else
101 				dst += n;
102 		}
103 	}
104 
105 	if (dst)
106 		*dst++ = '\0';
107 
108 	return (res + 1);
109 }
110 
111 static ssize_t
112 dname_expand(const unsigned char *data, size_t len, size_t offset,
113     size_t *newoffset, char *dst, size_t max)
114 {
115 	size_t		 n, count, end, ptr, start;
116 	ssize_t		 res;
117 
118 	if (offset >= len)
119 		return (-1);
120 
121 	res = 0;
122 	end = start = offset;
123 
124 	for (; (n = data[offset]); ) {
125 		if ((n & 0xc0) == 0xc0) {
126 			if (offset + 2 > len)
127 				return (-1);
128 			ptr = 256 * (n & ~0xc0) + data[offset + 1];
129 			if (ptr >= start)
130 				return (-1);
131 			if (end < offset + 2)
132 				end = offset + 2;
133 			offset = start = ptr;
134 			continue;
135 		}
136 		if (offset + n + 1 > len)
137 			return (-1);
138 
139 		if (dname_check_label(data + offset + 1, n) == -1)
140 			return (-1);
141 
142 		/* copy n + at offset+1 */
143 		if (dst != NULL && max != 0) {
144 			count = (max < n + 1) ? (max) : (n + 1);
145 			memmove(dst, data + offset, count);
146 			dst += count;
147 			max -= count;
148 		}
149 		res += n + 1;
150 		offset += n + 1;
151 		if (end < offset)
152 			end = offset;
153 	}
154 	if (end < offset + 1)
155 		end = offset + 1;
156 
157 	if (dst != NULL && max != 0)
158 		dst[0] = 0;
159 	if (newoffset)
160 		*newoffset = end;
161 	return (res + 1);
162 }
163 
164 void
165 _asr_pack_init(struct asr_pack *pack, char *buf, size_t len)
166 {
167 	pack->buf = buf;
168 	pack->len = len;
169 	pack->offset = 0;
170 	pack->err = 0;
171 }
172 
173 void
174 _asr_unpack_init(struct asr_unpack *unpack, const char *buf, size_t len)
175 {
176 	unpack->buf = buf;
177 	unpack->len = len;
178 	unpack->offset = 0;
179 	unpack->err = 0;
180 }
181 
182 static int
183 unpack_data(struct asr_unpack *p, void *data, size_t len)
184 {
185 	if (p->err)
186 		return (-1);
187 
188 	if (p->len - p->offset < len) {
189 		p->err = EOVERFLOW;
190 		return (-1);
191 	}
192 
193 	memmove(data, p->buf + p->offset, len);
194 	p->offset += len;
195 
196 	return (0);
197 }
198 
199 static int
200 unpack_u16(struct asr_unpack *p, uint16_t *u16)
201 {
202 	if (unpack_data(p, u16, 2) == -1)
203 		return (-1);
204 
205 	*u16 = ntohs(*u16);
206 
207 	return (0);
208 }
209 
210 static int
211 unpack_u32(struct asr_unpack *p, uint32_t *u32)
212 {
213 	if (unpack_data(p, u32, 4) == -1)
214 		return (-1);
215 
216 	*u32 = ntohl(*u32);
217 
218 	return (0);
219 }
220 
221 static int
222 unpack_inaddr(struct asr_unpack *p, struct in_addr *a)
223 {
224 	return (unpack_data(p, a, 4));
225 }
226 
227 static int
228 unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6)
229 {
230 	return (unpack_data(p, a6, 16));
231 }
232 
233 static int
234 unpack_dname(struct asr_unpack *p, char *dst, size_t max)
235 {
236 	ssize_t e;
237 
238 	if (p->err)
239 		return (-1);
240 
241 	e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
242 	if (e == -1) {
243 		p->err = EINVAL;
244 		return (-1);
245 	}
246 	if (e < 0 || e > MAXDNAME) {
247 		p->err = ERANGE;
248 		return (-1);
249 	}
250 
251 	return (0);
252 }
253 
254 int
255 _asr_unpack_header(struct asr_unpack *p, struct asr_dns_header *h)
256 {
257 	if (unpack_data(p, h, HFIXEDSZ) == -1)
258 		return (-1);
259 
260 	h->flags = ntohs(h->flags);
261 	h->qdcount = ntohs(h->qdcount);
262 	h->ancount = ntohs(h->ancount);
263 	h->nscount = ntohs(h->nscount);
264 	h->arcount = ntohs(h->arcount);
265 
266 	return (0);
267 }
268 
269 int
270 _asr_unpack_query(struct asr_unpack *p, struct asr_dns_query *q)
271 {
272 	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
273 	unpack_u16(p, &q->q_type);
274 	unpack_u16(p, &q->q_class);
275 
276 	return (p->err) ? (-1) : (0);
277 }
278 
279 int
280 _asr_unpack_rr(struct asr_unpack *p, struct asr_dns_rr *rr)
281 {
282 	uint16_t	rdlen;
283 	size_t		save_offset;
284 
285 	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
286 	unpack_u16(p, &rr->rr_type);
287 	unpack_u16(p, &rr->rr_class);
288 	unpack_u32(p, &rr->rr_ttl);
289 	unpack_u16(p, &rdlen);
290 
291 	if (p->err)
292 		return (-1);
293 
294 	if (p->len - p->offset < rdlen) {
295 		p->err = EOVERFLOW;
296 		return (-1);
297 	}
298 
299 	save_offset = p->offset;
300 
301 	switch (rr->rr_type) {
302 
303 	case T_CNAME:
304 		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
305 		break;
306 
307 	case T_MX:
308 		unpack_u16(p, &rr->rr.mx.preference);
309 		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
310 		break;
311 
312 	case T_NS:
313 		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
314 		break;
315 
316 	case T_PTR:
317 		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
318 		break;
319 
320 	case T_SOA:
321 		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
322 		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
323 		unpack_u32(p, &rr->rr.soa.serial);
324 		unpack_u32(p, &rr->rr.soa.refresh);
325 		unpack_u32(p, &rr->rr.soa.retry);
326 		unpack_u32(p, &rr->rr.soa.expire);
327 		unpack_u32(p, &rr->rr.soa.minimum);
328 		break;
329 
330 	case T_A:
331 		if (rr->rr_class != C_IN)
332 			goto other;
333 		unpack_inaddr(p, &rr->rr.in_a.addr);
334 		break;
335 
336 	case T_AAAA:
337 		if (rr->rr_class != C_IN)
338 			goto other;
339 		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
340 		break;
341 	default:
342 	other:
343 		rr->rr.other.rdata = p->buf + p->offset;
344 		rr->rr.other.rdlen = rdlen;
345 		p->offset += rdlen;
346 	}
347 
348 	if (p->err)
349 		return (-1);
350 
351 	/* make sure that the advertised rdlen is really ok */
352 	if (p->offset - save_offset != rdlen)
353 		p->err = EINVAL;
354 
355 	return (p->err) ? (-1) : (0);
356 }
357 
358 static int
359 pack_data(struct asr_pack *p, const void *data, size_t len)
360 {
361 	if (p->err)
362 		return (-1);
363 
364 	if (p->len < p->offset + len) {
365 		p->err = EOVERFLOW;
366 		return (-1);
367 	}
368 
369 	memmove(p->buf + p->offset, data, len);
370 	p->offset += len;
371 
372 	return (0);
373 }
374 
375 static int
376 pack_u16(struct asr_pack *p, uint16_t v)
377 {
378 	v = htons(v);
379 
380 	return (pack_data(p, &v, 2));
381 }
382 
383 static int
384 pack_u32(struct asr_pack *p, uint32_t v)
385 {
386 	v = htonl(v);
387 
388 	return (pack_data(p, &v, 4));
389 }
390 
391 static int
392 pack_dname(struct asr_pack *p, const char *dname)
393 {
394 	/* dname compression would be nice to have here.
395 	 * need additionnal context.
396 	 */
397 	return (pack_data(p, dname, strlen(dname) + 1));
398 }
399 
400 int
401 _asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h)
402 {
403 	struct asr_dns_header c;
404 
405 	c.id = h->id;
406 	c.flags = htons(h->flags);
407 	c.qdcount = htons(h->qdcount);
408 	c.ancount = htons(h->ancount);
409 	c.nscount = htons(h->nscount);
410 	c.arcount = htons(h->arcount);
411 
412 	return (pack_data(p, &c, HFIXEDSZ));
413 }
414 
415 int
416 _asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname)
417 {
418 	pack_dname(p, dname);
419 	pack_u16(p, type);
420 	pack_u16(p, class);
421 
422 	return (p->err) ? (-1) : (0);
423 }
424 
425 int
426 _asr_pack_edns0(struct asr_pack *p, uint16_t pktsz, int dnssec_do)
427 {
428 	DPRINT("asr EDNS0 pktsz:%hu dnssec:%s\n", pktsz,
429 	    dnssec_do ? "yes" : "no");
430 
431 	pack_dname(p, "");	/* root */
432 	pack_u16(p, T_OPT);	/* OPT */
433 	pack_u16(p, pktsz);	/* UDP payload size */
434 
435 	/* extended RCODE and flags */
436 	pack_u16(p, 0);
437 	pack_u16(p, dnssec_do ? DNS_MESSAGEEXTFLAG_DO : 0);
438 
439 	pack_u16(p, 0);		/* RDATA len */
440 
441 	return (p->err) ? (-1) : (0);
442 }
443 
444 int
445 _asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
446 {
447 	struct in_addr		 ina;
448 	struct in6_addr		 in6a;
449 	struct sockaddr_in	*sin;
450 	struct sockaddr_in6	*sin6;
451 	char			*cp, *str2;
452 	const char		*errstr;
453 
454 	switch (family) {
455 	case PF_UNSPEC:
456 		if (_asr_sockaddr_from_str(sa, PF_INET, str) == 0)
457 			return (0);
458 		return _asr_sockaddr_from_str(sa, PF_INET6, str);
459 
460 	case PF_INET:
461 		if (inet_pton(PF_INET, str, &ina) != 1)
462 			return (-1);
463 
464 		sin = (struct sockaddr_in *)sa;
465 		memset(sin, 0, sizeof *sin);
466 		sin->sin_len = sizeof(struct sockaddr_in);
467 		sin->sin_family = PF_INET;
468 		sin->sin_addr.s_addr = ina.s_addr;
469 		return (0);
470 
471 	case PF_INET6:
472 		cp = strchr(str, SCOPE_DELIMITER);
473 		if (cp) {
474 			str2 = strdup(str);
475 			if (str2 == NULL)
476 				return (-1);
477 			str2[cp - str] = '\0';
478 			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
479 				free(str2);
480 				return (-1);
481 			}
482 			cp++;
483 			free(str2);
484 		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
485 			return (-1);
486 
487 		sin6 = (struct sockaddr_in6 *)sa;
488 		memset(sin6, 0, sizeof *sin6);
489 		sin6->sin6_len = sizeof(struct sockaddr_in6);
490 		sin6->sin6_family = PF_INET6;
491 		sin6->sin6_addr = in6a;
492 
493 		if (cp == NULL)
494 			return (0);
495 
496 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
497 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
498 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
499 			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
500 				return (0);
501 
502 		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
503 		if (errstr)
504 			return (-1);
505 		return (0);
506 
507 	default:
508 		break;
509 	}
510 
511 	return (-1);
512 }
513 
514 ssize_t
515 _asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
516 {
517 	const struct in6_addr	*in6_addr;
518 	in_addr_t		 in_addr;
519 
520 	switch (family) {
521 	case AF_INET:
522 		in_addr = ntohl(*((const in_addr_t *)addr));
523 		snprintf(dst, max,
524 		    "%d.%d.%d.%d.in-addr.arpa.",
525 		    in_addr & 0xff,
526 		    (in_addr >> 8) & 0xff,
527 		    (in_addr >> 16) & 0xff,
528 		    (in_addr >> 24) & 0xff);
529 		break;
530 	case AF_INET6:
531 		in6_addr = (const struct in6_addr *)addr;
532 		snprintf(dst, max,
533 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
534 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
535 		    "ip6.arpa.",
536 		    in6_addr->s6_addr[15] & 0xf,
537 		    (in6_addr->s6_addr[15] >> 4) & 0xf,
538 		    in6_addr->s6_addr[14] & 0xf,
539 		    (in6_addr->s6_addr[14] >> 4) & 0xf,
540 		    in6_addr->s6_addr[13] & 0xf,
541 		    (in6_addr->s6_addr[13] >> 4) & 0xf,
542 		    in6_addr->s6_addr[12] & 0xf,
543 		    (in6_addr->s6_addr[12] >> 4) & 0xf,
544 		    in6_addr->s6_addr[11] & 0xf,
545 		    (in6_addr->s6_addr[11] >> 4) & 0xf,
546 		    in6_addr->s6_addr[10] & 0xf,
547 		    (in6_addr->s6_addr[10] >> 4) & 0xf,
548 		    in6_addr->s6_addr[9] & 0xf,
549 		    (in6_addr->s6_addr[9] >> 4) & 0xf,
550 		    in6_addr->s6_addr[8] & 0xf,
551 		    (in6_addr->s6_addr[8] >> 4) & 0xf,
552 		    in6_addr->s6_addr[7] & 0xf,
553 		    (in6_addr->s6_addr[7] >> 4) & 0xf,
554 		    in6_addr->s6_addr[6] & 0xf,
555 		    (in6_addr->s6_addr[6] >> 4) & 0xf,
556 		    in6_addr->s6_addr[5] & 0xf,
557 		    (in6_addr->s6_addr[5] >> 4) & 0xf,
558 		    in6_addr->s6_addr[4] & 0xf,
559 		    (in6_addr->s6_addr[4] >> 4) & 0xf,
560 		    in6_addr->s6_addr[3] & 0xf,
561 		    (in6_addr->s6_addr[3] >> 4) & 0xf,
562 		    in6_addr->s6_addr[2] & 0xf,
563 		    (in6_addr->s6_addr[2] >> 4) & 0xf,
564 		    in6_addr->s6_addr[1] & 0xf,
565 		    (in6_addr->s6_addr[1] >> 4) & 0xf,
566 		    in6_addr->s6_addr[0] & 0xf,
567 		    (in6_addr->s6_addr[0] >> 4) & 0xf);
568 		break;
569 	default:
570 		return (-1);
571 	}
572 	return (0);
573 }
574