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
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
dname_check_label(const char * s,size_t l)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
_asr_dname_from_fqdn(const char * str,char * dst,size_t max)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
dname_expand(const unsigned char * data,size_t len,size_t offset,size_t * newoffset,char * dst,size_t max)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 + 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
164 void
_asr_pack_init(struct asr_pack * pack,char * buf,size_t len)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
_asr_unpack_init(struct asr_unpack * unpack,const char * buf,size_t len)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
unpack_data(struct asr_unpack * p,void * data,size_t len)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
unpack_u16(struct asr_unpack * p,uint16_t * u16)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
unpack_u32(struct asr_unpack * p,uint32_t * u32)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
unpack_inaddr(struct asr_unpack * p,struct in_addr * a)222 unpack_inaddr(struct asr_unpack *p, struct in_addr *a)
223 {
224 return (unpack_data(p, a, 4));
225 }
226
227 static int
unpack_in6addr(struct asr_unpack * p,struct in6_addr * a6)228 unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6)
229 {
230 return (unpack_data(p, a6, 16));
231 }
232
233 static int
unpack_dname(struct asr_unpack * p,char * dst,size_t max)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
_asr_unpack_header(struct asr_unpack * p,struct asr_dns_header * h)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
_asr_unpack_query(struct asr_unpack * p,struct asr_dns_query * q)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
_asr_unpack_rr(struct asr_unpack * p,struct asr_dns_rr * rr)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
pack_data(struct asr_pack * p,const void * data,size_t len)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
pack_u16(struct asr_pack * p,uint16_t v)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
pack_dname(struct asr_pack * p,const char * dname)384 pack_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
392 int
_asr_pack_header(struct asr_pack * p,const struct asr_dns_header * h)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
407 int
_asr_pack_query(struct asr_pack * p,uint16_t type,uint16_t class,const char * dname)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
417 int
_asr_pack_edns0(struct asr_pack * p,uint16_t pktsz,int dnssec_do)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
436 int
_asr_sockaddr_from_str(struct sockaddr * sa,int family,const char * str)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
506 ssize_t
_asr_addr_as_fqdn(const char * addr,int family,char * dst,size_t max)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
567 int
hnok_lenient(const char * dn)568 hnok_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 */
587 int
_asr_is_localhost(const char * hn)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