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