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