1/*	$OpenBSD: asr_utils.c,v 1.22 2023/11/20 12:15:16 florian 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
37static int dname_check_label(const char *, size_t);
38static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
39    char *, size_t);
40
41static int unpack_data(struct asr_unpack *, void *, size_t);
42static int unpack_u16(struct asr_unpack *, uint16_t *);
43static int unpack_u32(struct asr_unpack *, uint32_t *);
44static int unpack_inaddr(struct asr_unpack *, struct in_addr *);
45static int unpack_in6addr(struct asr_unpack *, struct in6_addr *);
46static int unpack_dname(struct asr_unpack *, char *, size_t);
47
48static int pack_data(struct asr_pack *, const void *, size_t);
49static int pack_u16(struct asr_pack *, uint16_t);
50static int pack_dname(struct asr_pack *, const char *);
51
52static int
53dname_check_label(const char *s, size_t l)
54{
55	if (l == 0 || l > 63)
56		return (-1);
57
58	return (0);
59}
60
61ssize_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
111static ssize_t
112dname_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 + 1 >= 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
164void
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
173void
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
182static int
183unpack_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
199static int
200unpack_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
210static int
211unpack_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
221static int
222unpack_inaddr(struct asr_unpack *p, struct in_addr *a)
223{
224	return (unpack_data(p, a, 4));
225}
226
227static int
228unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6)
229{
230	return (unpack_data(p, a6, 16));
231}
232
233static int
234unpack_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
254int
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
269int
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
279int
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
358static int
359pack_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
375static int
376pack_u16(struct asr_pack *p, uint16_t v)
377{
378	v = htons(v);
379
380	return (pack_data(p, &v, 2));
381}
382
383static int
384pack_dname(struct asr_pack *p, const char *dname)
385{
386	/* dname compression would be nice to have here.
387	 * need additional context.
388	 */
389	return (pack_data(p, dname, strlen(dname) + 1));
390}
391
392int
393_asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h)
394{
395	struct asr_dns_header c;
396
397	c.id = h->id;
398	c.flags = htons(h->flags);
399	c.qdcount = htons(h->qdcount);
400	c.ancount = htons(h->ancount);
401	c.nscount = htons(h->nscount);
402	c.arcount = htons(h->arcount);
403
404	return (pack_data(p, &c, HFIXEDSZ));
405}
406
407int
408_asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname)
409{
410	pack_dname(p, dname);
411	pack_u16(p, type);
412	pack_u16(p, class);
413
414	return (p->err) ? (-1) : (0);
415}
416
417int
418_asr_pack_edns0(struct asr_pack *p, uint16_t pktsz, int dnssec_do)
419{
420	DPRINT("asr EDNS0 pktsz:%hu dnssec:%s\n", pktsz,
421	    dnssec_do ? "yes" : "no");
422
423	pack_dname(p, "");	/* root */
424	pack_u16(p, T_OPT);	/* OPT */
425	pack_u16(p, pktsz);	/* UDP payload size */
426
427	/* extended RCODE and flags */
428	pack_u16(p, 0);
429	pack_u16(p, dnssec_do ? DNS_MESSAGEEXTFLAG_DO : 0);
430
431	pack_u16(p, 0);		/* RDATA len */
432
433	return (p->err) ? (-1) : (0);
434}
435
436int
437_asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
438{
439	struct in_addr		 ina;
440	struct in6_addr		 in6a;
441	struct sockaddr_in	*sin;
442	struct sockaddr_in6	*sin6;
443	char			*cp, *str2;
444	const char		*errstr;
445
446	switch (family) {
447	case PF_UNSPEC:
448		if (_asr_sockaddr_from_str(sa, PF_INET, str) == 0)
449			return (0);
450		return _asr_sockaddr_from_str(sa, PF_INET6, str);
451
452	case PF_INET:
453		if (inet_pton(PF_INET, str, &ina) != 1)
454			return (-1);
455
456		sin = (struct sockaddr_in *)sa;
457		memset(sin, 0, sizeof *sin);
458		sin->sin_len = sizeof(struct sockaddr_in);
459		sin->sin_family = PF_INET;
460		sin->sin_addr.s_addr = ina.s_addr;
461		return (0);
462
463	case PF_INET6:
464		cp = strchr(str, SCOPE_DELIMITER);
465		if (cp) {
466			str2 = strdup(str);
467			if (str2 == NULL)
468				return (-1);
469			str2[cp - str] = '\0';
470			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
471				free(str2);
472				return (-1);
473			}
474			cp++;
475			free(str2);
476		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
477			return (-1);
478
479		sin6 = (struct sockaddr_in6 *)sa;
480		memset(sin6, 0, sizeof *sin6);
481		sin6->sin6_len = sizeof(struct sockaddr_in6);
482		sin6->sin6_family = PF_INET6;
483		sin6->sin6_addr = in6a;
484
485		if (cp == NULL)
486			return (0);
487
488		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
489		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
490		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
491			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
492				return (0);
493
494		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
495		if (errstr)
496			return (-1);
497		return (0);
498
499	default:
500		break;
501	}
502
503	return (-1);
504}
505
506ssize_t
507_asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
508{
509	const struct in6_addr	*in6_addr;
510	in_addr_t		 in_addr;
511
512	switch (family) {
513	case AF_INET:
514		in_addr = ntohl(*((const in_addr_t *)addr));
515		snprintf(dst, max,
516		    "%d.%d.%d.%d.in-addr.arpa.",
517		    in_addr & 0xff,
518		    (in_addr >> 8) & 0xff,
519		    (in_addr >> 16) & 0xff,
520		    (in_addr >> 24) & 0xff);
521		break;
522	case AF_INET6:
523		in6_addr = (const struct in6_addr *)addr;
524		snprintf(dst, max,
525		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
526		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
527		    "ip6.arpa.",
528		    in6_addr->s6_addr[15] & 0xf,
529		    (in6_addr->s6_addr[15] >> 4) & 0xf,
530		    in6_addr->s6_addr[14] & 0xf,
531		    (in6_addr->s6_addr[14] >> 4) & 0xf,
532		    in6_addr->s6_addr[13] & 0xf,
533		    (in6_addr->s6_addr[13] >> 4) & 0xf,
534		    in6_addr->s6_addr[12] & 0xf,
535		    (in6_addr->s6_addr[12] >> 4) & 0xf,
536		    in6_addr->s6_addr[11] & 0xf,
537		    (in6_addr->s6_addr[11] >> 4) & 0xf,
538		    in6_addr->s6_addr[10] & 0xf,
539		    (in6_addr->s6_addr[10] >> 4) & 0xf,
540		    in6_addr->s6_addr[9] & 0xf,
541		    (in6_addr->s6_addr[9] >> 4) & 0xf,
542		    in6_addr->s6_addr[8] & 0xf,
543		    (in6_addr->s6_addr[8] >> 4) & 0xf,
544		    in6_addr->s6_addr[7] & 0xf,
545		    (in6_addr->s6_addr[7] >> 4) & 0xf,
546		    in6_addr->s6_addr[6] & 0xf,
547		    (in6_addr->s6_addr[6] >> 4) & 0xf,
548		    in6_addr->s6_addr[5] & 0xf,
549		    (in6_addr->s6_addr[5] >> 4) & 0xf,
550		    in6_addr->s6_addr[4] & 0xf,
551		    (in6_addr->s6_addr[4] >> 4) & 0xf,
552		    in6_addr->s6_addr[3] & 0xf,
553		    (in6_addr->s6_addr[3] >> 4) & 0xf,
554		    in6_addr->s6_addr[2] & 0xf,
555		    (in6_addr->s6_addr[2] >> 4) & 0xf,
556		    in6_addr->s6_addr[1] & 0xf,
557		    (in6_addr->s6_addr[1] >> 4) & 0xf,
558		    in6_addr->s6_addr[0] & 0xf,
559		    (in6_addr->s6_addr[0] >> 4) & 0xf);
560		break;
561	default:
562		return (-1);
563	}
564	return (0);
565}
566
567int
568hnok_lenient(const char *dn)
569{
570	int pch = '\0', ch = *dn++;
571
572	while (ch != '\0') {
573		/* can't start with . or - */
574		if (pch == '\0' && (ch == '.' || ch == '-'))
575			return 0;
576		if (pch == '.' && ch == '.')
577			return 0;
578		if (!(isalpha((unsigned char)ch) || isdigit((unsigned char)ch) ||
579		    ch == '.' || ch == '-' || ch == '_'))
580			return 0;
581		pch = ch; ch = *dn++;
582	}
583	return 1;
584}
585
586/* Check if the hostname is localhost or if it's in the localhost domain */
587int
588_asr_is_localhost(const char *hn)
589{
590	size_t	 hnlen, localhostlen;
591
592	if (hn == NULL)
593		return 0;
594
595	if (strcasecmp(hn, "localhost") == 0 ||
596	    strcasecmp(hn, "localhost.") == 0)
597		return 1;
598
599	hnlen = strlen(hn);
600	localhostlen = strlen(".localhost");
601
602	if (hnlen < localhostlen)
603		return 0;
604
605	if (strcasecmp(hn + hnlen - localhostlen, ".localhost") == 0)
606		return 1;
607
608	localhostlen++;
609	if (hnlen < localhostlen)
610		return 0;
611
612	if (strcasecmp(hn + hnlen - localhostlen, ".localhost.") == 0)
613		return 1;
614
615	return 0;
616}
617