1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "private-lib-core.h" 26#include "private-lib-async-dns.h" 27 28 29/* updates *dest, returns chars used from ls directly, else -1 for fail */ 30 31static int 32lws_adns_parse_label(const uint8_t *pkt, int len, const uint8_t *ls, int budget, 33 char **dest, size_t dl) 34{ 35 const uint8_t *e = pkt + len, *ols = ls; 36 char pointer = 0, first = 1; 37 uint8_t ll; 38 int n; 39 40 if (budget < 1) 41 return 0; 42 43 /* caller must catch end of labels */ 44 assert(*ls); 45 46again1: 47 if (ls >= e) 48 return -1; 49 50 if (((*ls) & 0xc0) == 0xc0) { 51 if (budget < 2) 52 return -1; 53 /* pointer into message pkt to name to actually use */ 54 n = lws_ser_ru16be(ls) & 0x3fff; 55 if (n >= len) { 56 lwsl_notice("%s: illegal name pointer\n", __func__); 57 58 return -1; 59 } 60 61 /* dereference the label pointer */ 62 ls = pkt + n; 63 64 /* are we being fuzzed or messed with? */ 65 if (((*ls) & 0xc0) == 0xc0) { 66 /* ... pointer to pointer is unreasonable */ 67 lwsl_notice("%s: label ptr to ptr invalid\n", __func__); 68 69 return -1; 70 } 71 pointer = 1; 72 } 73 74 if (ls >= e) 75 return -1; 76 77 ll = *ls++; 78 if (ls + ll + 1 > e) { 79 lwsl_notice("%s: label len invalid, %d vs %d\n", __func__, 80 lws_ptr_diff((ls + ll + 1), pkt), lws_ptr_diff(e, pkt)); 81 82 return -1; 83 } 84 85 if (ls + ll > ols + budget) { 86 lwsl_notice("%s: label too long %d vs %d\n", __func__, ll, budget); 87 88 return -1; 89 } 90 91 if ((unsigned int)ll + 2 > dl) { 92 lwsl_notice("%s: qname too large\n", __func__); 93 94 return -1; 95 } 96 97 /* copy the label content into place */ 98 99 memcpy(*dest, ls, ll); 100 (*dest)[ll] = '.'; 101 (*dest)[ll + 1] = '\0'; 102 *dest += ll + 1; 103 ls += ll; 104 105 if (pointer) { 106 if (*ls) 107 goto again1; 108 109 /* 110 * special fun rule... if whole qname was a pointer label, 111 * it has no 00 terminator afterwards 112 */ 113 if (first) 114 return 2; /* we just took the 16-bit pointer */ 115 116 return 3; 117 } 118 119 first = 0; 120 121 if (*ls) 122 goto again1; 123 124 ls++; 125 126 return lws_ptr_diff(ls, ols); 127} 128 129typedef int (*lws_async_dns_find_t)(const char *name, void *opaque, 130 uint32_t ttl, adns_query_type_t type, 131 const uint8_t *payload); 132 133/* locally query the response packet */ 134 135struct label_stack { 136 char name[DNS_MAX]; 137 int enl; 138 const uint8_t *p; 139}; 140 141/* 142 * Walk the response packet, calling back to the user-provided callback for each 143 * A (and AAAA if LWS_IPV6=1) record with a matching name found in there. 144 * 145 * Able to recurse using an explicit non-CPU stack to resolve CNAME usages 146 * 147 * Return -1: unexpectedly failed 148 * 0: found 149 * 1: didn't find anything matching 150 */ 151 152static int 153lws_adns_iterate(lws_adns_q_t *q, const uint8_t *pkt, int len, 154 const char *expname, lws_async_dns_find_t cb, void *opaque) 155{ 156 const uint8_t *e = pkt + len, *p, *pay; 157 struct label_stack stack[4]; 158 int n = 0, stp = 0, ansc, m; 159 uint16_t rrtype, rrpaylen; 160 char *sp, inq; 161 uint32_t ttl; 162 163 lws_strncpy(stack[0].name, expname, sizeof(stack[0].name)); 164 stack[0].enl = (int)strlen(expname); 165 166start: 167 ansc = lws_ser_ru16be(pkt + DHO_NANSWERS); 168 p = pkt + DHO_SIZEOF; 169 inq = 1; 170 171 /* 172 * The response also includes the query... and we have to parse it 173 * so we can understand we reached the response... there's a QNAME 174 * made up of labels and then 2 x 16-bit fields, for query type and 175 * query class 176 */ 177 178 179 while (p + 14 < e && (inq || ansc)) { 180 181 if (!inq && !stp) 182 ansc--; 183 184 /* 185 * First is the name the query applies to... two main 186 * formats can appear here, one is a pointer to 187 * elsewhere in the message, the other separately 188 * provides len / data for each dotted "label", so for 189 * "warmcat.com" warmcat and com are given each with a 190 * prepended length byte. Any of those may be a pointer 191 * to somewhere else in the packet :-/ 192 * 193 * Paranoia is appropriate since the name length must be 194 * parsed out before the rest of the RR can be used and 195 * we can be attacked with absolutely any crafted 196 * content easily via UDP. 197 * 198 * So parse the name and additionally confirm it matches 199 * what the query the TID belongs to actually asked for. 200 */ 201 202 sp = stack[0].name; 203 204 /* while we have more labels */ 205 206 n = lws_adns_parse_label(pkt, len, p, len, &sp, 207 sizeof(stack[0].name) - 208 lws_ptr_diff_size_t(sp, stack[0].name)); 209 /* includes case name won't fit */ 210 if (n < 0) 211 return -1; 212 213 p += n; 214 215 if (p + (inq ? 5 : 14) > e) 216 return -1; 217 218 /* 219 * p is now just after the decoded RR name, pointing at: type 220 * 221 * We sent class = 1 = IN query... response must match 222 */ 223 224 if (lws_ser_ru16be(&p[2]) != 1) { 225 lwsl_err("%s: non-IN response 0x%x\n", __func__, 226 lws_ser_ru16be(&p[2])); 227 228 return -1; 229 } 230 231 if (inq) { 232 lwsl_debug("%s: reached end of inq\n", __func__); 233 inq = 0; 234 p += 4; 235 continue; 236 } 237 238 /* carefully validate the claimed RR payload length */ 239 240 rrpaylen = lws_ser_ru16be(&p[8]); 241 if (p + 10 + rrpaylen > e) { /* it may be == e */ 242 lwsl_notice("%s: invalid RR data length\n", __func__); 243 244 return -1; 245 } 246 247 ttl = lws_ser_ru32be(&p[4]); 248 rrtype = lws_ser_ru16be(&p[0]); 249 p += 10; /* point to the payload */ 250 pay = p; 251 252 /* 253 * Compare the RR names, allowing for the decoded labelname 254 * to have an extra '.' at the end. 255 */ 256 257 n = lws_ptr_diff(sp, stack[0].name); 258 if (stack[0].name[n - 1] == '.') 259 n--; 260 261 m = stack[stp].enl; 262 if (stack[stp].name[m - 1] == '.') 263 m--; 264 265 if (n < 1 || n != m || 266 strncmp(stack[0].name, stack[stp].name, (unsigned int)n)) { 267 //lwsl_notice("%s: skipping %s vs %s\n", __func__, 268 // stack[0].name, stack[stp].name); 269 goto skip; 270 } 271 272 /* 273 * It's something we could be interested in... 274 * 275 * We can skip RRs we don't understand. But we need to deal 276 * with at least these and their payloads: 277 * 278 * A: 4: ipv4 address 279 * AAAA: 16: ipv6 address (if asked for AAAA) 280 * CNAME: ?: labelized name 281 * 282 * If we hit a CNAME we need to try to dereference it with 283 * stuff that is in the same response packet and judge it 284 * from that, without losing our place here. CNAMEs may 285 * point to CNAMEs to whatever depth we're willing to handle. 286 */ 287 288 switch (rrtype) { 289 290 case LWS_ADNS_RECORD_AAAA: 291 if (rrpaylen != 16) { 292 lwsl_err("%s: unexpected rrpaylen\n", __func__); 293 return -1; 294 } 295#if defined(LWS_WITH_IPV6) 296 goto do_cb; 297#else 298 break; 299#endif 300 301 case LWS_ADNS_RECORD_A: 302 if (rrpaylen != 4) { 303 lwsl_err("%s: unexpected rrpaylen4\n", __func__); 304 305 return -1; 306 } 307#if defined(LWS_WITH_IPV6) 308do_cb: 309#endif 310 cb(stack[0].name, opaque, ttl, rrtype, p); 311 break; 312 313 case LWS_ADNS_RECORD_CNAME: 314 /* 315 * The name the CNAME refers to MAY itself be 316 * included elsewhere in the response packet. 317 * 318 * So switch tack, stack where to resume from and 319 * search for the decoded CNAME label name definition 320 * instead. 321 * 322 * First decode the CNAME label payload into the next 323 * stack level buffer for it. 324 */ 325 326 if (++stp == (int)LWS_ARRAY_SIZE(stack)) { 327 lwsl_notice("%s: CNAMEs too deep\n", __func__); 328 329 return -1; 330 } 331 sp = stack[stp].name; 332 /* get the cname alias */ 333 n = lws_adns_parse_label(pkt, len, p, rrpaylen, &sp, 334 sizeof(stack[stp].name) - 335 lws_ptr_diff_size_t(sp, stack[stp].name)); 336 /* includes case name won't fit */ 337 if (n < 0) 338 return -1; 339 340 p += n; 341 342 if (p + 14 > e) 343 return -1; 344#if 0 345 /* it should have exactly reached rrpaylen if only one 346 * CNAME, else somewhere in the middle */ 347 348 if (p != pay + rrpaylen) { 349 lwsl_err("%s: cname name bad len %d\n", __func__, rrpaylen); 350 351 return -1; 352 } 353#endif 354 lwsl_notice("%s: recursing looking for %s\n", __func__, stack[stp].name); 355 356 lwsl_info("%s: recursing looking for %s\n", __func__, 357 stack[stp].name); 358 359 stack[stp].enl = lws_ptr_diff(sp, stack[stp].name); 360 /* when we unstack, resume from here */ 361 stack[stp].p = pay + rrpaylen; 362 goto start; 363 364 default: 365 break; 366 } 367 368skip: 369 p += rrpaylen; 370 } 371 372 if (!stp) 373 return 1; /* we didn't find anything, but we didn't error */ 374 375 lwsl_info("%s: '%s' -> CNAME '%s' resolution not provided, recursing\n", 376 __func__, ((const char *)&q[1]) + DNS_MAX, 377 stack[stp].name); 378 379 /* 380 * This implies there wasn't any usable definition for the 381 * CNAME in the end, eg, only AAAA when we needed an A. 382 * 383 * It's also legit if the DNS just returns the CNAME, and that server 384 * did not directly know the next step in resolution of the CNAME, so 385 * instead of putting the resolution elsewhere in the response, has 386 * told us just the CNAME and left it to us to find out its resolution 387 * separately. 388 * 389 * Reset this request to be for the CNAME, and restart the request 390 * action with a new tid. 391 */ 392 393 if (lws_async_dns_get_new_tid(q->context, q)) 394 return -1; 395 396 LADNS_MOST_RECENT_TID(q) &= 0xfffe; 397 q->asked = q->responded = 0; 398#if defined(LWS_WITH_IPV6) 399 q->sent[1] = 0; 400#endif 401 q->sent[0] = 0; 402 q->recursion++; 403 if (q->recursion == DNS_RECURSION_LIMIT) { 404 lwsl_err("%s: recursion overflow\n", __func__); 405 406 return -1; 407 } 408 409 if (q->firstcache) 410 lws_adns_cache_destroy(q->firstcache); 411 q->firstcache = NULL; 412 413 /* overwrite the query name with the CNAME */ 414 415 n = 0; 416 { 417 char *cp = (char *)&q[1]; 418 419 while (stack[stp].name[n]) 420 *cp++ = (char)tolower(stack[stp].name[n++]); 421 /* trim the following . if any */ 422 if (n && cp[-1] == '.') 423 cp--; 424 *cp = '\0'; 425 } 426 427 lws_callback_on_writable(q->dns->wsi); 428 429 return 2; 430} 431 432int 433lws_async_dns_estimate(const char *name, void *opaque, uint32_t ttl, 434 adns_query_type_t type, const uint8_t *payload) 435{ 436 size_t *est = (size_t *)opaque, my; 437 438 my = sizeof(struct addrinfo); 439 if (type == LWS_ADNS_RECORD_AAAA) 440 my += sizeof(struct sockaddr_in6); 441 else 442 my += sizeof(struct sockaddr_in); 443 444 *est += my; 445 446 return 0; 447} 448 449struct adstore { 450 const char *name; 451 struct addrinfo *pos; 452 struct addrinfo *prev; 453 int ctr; 454 uint32_t smallest_ttl; 455 uint8_t flags; 456}; 457 458/* 459 * Callback for each A or AAAA record, creating getaddrinfo-compatible results 460 * into the preallocated exact-sized storage. 461 */ 462int 463lws_async_dns_store(const char *name, void *opaque, uint32_t ttl, 464 adns_query_type_t type, const uint8_t *payload) 465{ 466 struct adstore *adst = (struct adstore *)opaque; 467#if defined(_DEBUG) 468 char buf[48]; 469#endif 470 size_t i; 471 472 if (ttl < adst->smallest_ttl || !adst->ctr) 473 adst->smallest_ttl = ttl; 474 475 if (adst->prev) 476 adst->prev->ai_next = adst->pos; 477 adst->prev = adst->pos; 478 479 adst->pos->ai_flags = 0; 480 adst->pos->ai_family = type == LWS_ADNS_RECORD_AAAA ? 481 AF_INET6 : AF_INET; 482 adst->pos->ai_socktype = SOCK_STREAM; 483 adst->pos->ai_protocol = IPPROTO_UDP; /* no meaning */ 484 adst->pos->ai_addrlen = type == LWS_ADNS_RECORD_AAAA ? 485 sizeof(struct sockaddr_in6) : 486 sizeof(struct sockaddr_in); 487 adst->pos->ai_canonname = (char *)adst->name; 488 adst->pos->ai_addr = (struct sockaddr *)&adst->pos[1]; 489 adst->pos->ai_next = NULL; 490 491#if defined(LWS_WITH_IPV6) 492 if (type == LWS_ADNS_RECORD_AAAA) { 493 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&adst->pos[1]; 494 495 i = sizeof(*in6); 496 memset(in6, 0, i); 497 in6->sin6_family = (sa_family_t)adst->pos->ai_family; 498 memcpy(in6->sin6_addr.s6_addr, payload, 16); 499 adst->flags |= 2; 500 } else 501#endif 502 { 503 struct sockaddr_in *in = (struct sockaddr_in *)&adst->pos[1]; 504 505 i = sizeof(*in); 506 memset(in, 0, i); 507 in->sin_family = (sa_family_t)adst->pos->ai_family; 508 memcpy(&in->sin_addr.s_addr, payload, 4); 509 adst->flags |= 1; 510 } 511 512 adst->pos = (struct addrinfo *)((uint8_t *)adst->pos + 513 sizeof(struct addrinfo) + i); 514 515#if defined(_DEBUG) 516 if (lws_write_numeric_address(payload, 517 type == LWS_ADNS_RECORD_AAAA ? 16 : 4, 518 buf, sizeof(buf)) > 0) 519 lwsl_info("%s: %d: %s: %s\n", __func__, adst->ctr, 520 adst->name, buf); 521#endif 522 adst->ctr++; 523 524 return 0; 525} 526 527/* 528 * We want to parse out all A or AAAA records 529 */ 530 531void 532lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len) 533{ 534 const char *nm, *nmcname; 535 lws_adns_cache_t *c; 536 struct adstore adst; 537 lws_adns_q_t *q; 538 int n, ncname; 539 size_t est; 540 541 // lwsl_hexdump_notice(pkt, len); 542 543 /* we have to at least have the header */ 544 545 if (len < DHO_SIZEOF) 546 return; 547 548 /* we asked with one query, so anything else is bogus */ 549 550 if (lws_ser_ru16be(pkt + DHO_NQUERIES) != 1) 551 return; 552 553 /* match both A and AAAA queries if any */ 554 555 q = lws_adns_get_query(dns, 0, &dns->waiting, 556 lws_ser_ru16be(pkt + DHO_TID), NULL); 557 if (!q) { 558 lwsl_info("%s: dropping unknown query tid 0x%x\n", 559 __func__, lws_ser_ru16be(pkt + DHO_TID)); 560 561 return; 562 } 563 564 /* we can get dups... drop any that have already happened */ 565 566 n = 1 << (lws_ser_ru16be(pkt + DHO_TID) & 1); 567 if (q->responded & n) { 568 lwsl_notice("%s: dup\n", __func__); 569 goto fail_out; 570 } 571 572 q->responded = (uint8_t)(q->responded | n); 573 574 /* we want to confirm the results against what we last requested... */ 575 576 nmcname = ((const char *)&q[1]); 577 578 /* 579 * First walk the packet figuring out the allocation needed for all 580 * the results. Produce the following layout at c 581 * 582 * lws_adns_cache_t: new cache object 583 * [struct addrinfo + struct sockaddr_in or _in6]: for each A or AAAA 584 * char []: copy of resolved name 585 */ 586 587 ncname = (int)strlen(nmcname) + 1; 588 589 est = sizeof(lws_adns_cache_t) + (unsigned int)ncname; 590 if (lws_ser_ru16be(pkt + DHO_NANSWERS)) { 591 int ir = lws_adns_iterate(q, pkt, (int)len, nmcname, 592 lws_async_dns_estimate, &est); 593 if (ir < 0) 594 goto fail_out; 595 596 if (ir == 2) /* CNAME recursive resolution */ 597 return; 598 } 599 600 /* but we want to create the cache entry against the original request */ 601 602 nm = ((const char *)&q[1]) + DNS_MAX; 603 n = (int)strlen(nm) + 1; 604 605 lwsl_info("%s: create cache entry for %s, %zu\n", __func__, nm, 606 est - sizeof(lws_adns_cache_t)); 607 c = lws_malloc(est + 1, "async-dns-entry"); 608 if (!c) { 609 lwsl_err("%s: OOM %zu\n", __func__, est); 610 goto fail_out; 611 } 612 memset(c, 0, sizeof(*c)); 613 614 /* place it at end, no need to care about alignment padding */ 615 c->name = adst.name = ((const char *)c) + est - n; 616 memcpy((char *)c->name, nm, (unsigned int)n); 617 618 /* 619 * Then walk the packet again, placing the objects we accounted for 620 * the first time into the result allocation after the cache object 621 * and copy of the name 622 */ 623 624 adst.pos = (struct addrinfo *)&c[1]; 625 adst.prev = NULL; 626 adst.ctr = 0; 627 adst.smallest_ttl = 3600; 628 adst.flags = 0; 629 630 /* 631 * smallest_ttl applies as it is to empty results (NXDOMAIN), or is 632 * set to the minimum ttl seen in all the results. 633 */ 634 635 if (lws_ser_ru16be(pkt + DHO_NANSWERS) && 636 lws_adns_iterate(q, pkt, (int)len, nmcname, lws_async_dns_store, &adst) < 0) { 637 lws_free(c); 638 goto fail_out; 639 } 640 641 if (lws_ser_ru16be(pkt + DHO_NANSWERS)) { 642 c->results = (struct addrinfo *)&c[1]; 643 if (q->last) /* chain the second one on */ 644 *q->last = c->results; 645 else /* first one had no results, set first guy's c->results */ 646 if (q->firstcache) 647 q->firstcache->results = c->results; 648 } 649 650 if (adst.prev) /* so we know where to continue the addrinfo list */ 651 /* can be NULL if first resp empty */ 652 q->last = &adst.prev->ai_next; 653 654 if (q->firstcache) { /* also need to free chain when we free this guy */ 655 q->firstcache->chain = c; 656 c->firstcache = q->firstcache; 657 } else { 658 659 q->firstcache = c; 660 c->incomplete = !q->responded;// != q->asked; 661 662 /* 663 * Only register the first one into the cache... 664 * Trim the oldest cache entry if necessary 665 */ 666 667 lws_async_dns_trim_cache(dns); 668 669 /* 670 * cache the first results object... if a second one comes, 671 * we won't directly register it but will chain it on to this 672 * first one and continue to addinfo ai_next linked list from 673 * the first into the second 674 */ 675 676 c->flags = adst.flags; 677 lws_dll2_add_head(&c->list, &dns->cached); 678 lws_sul_schedule(q->context, 0, &c->sul, sul_cb_expire, 679 lws_now_usecs() + 680 (adst.smallest_ttl * LWS_US_PER_SEC)); 681 } 682 683 if (q->responded != q->asked) 684 return; 685 686 /* 687 * Now we captured everything into the new object, return the 688 * addrinfo results, if any, to all interested wsi, if any... 689 */ 690 691 c->incomplete = 0; 692 lws_async_dns_complete(q, q->firstcache); 693 694 q->go_nogo = METRES_GO; 695 696 /* 697 * the query is completely finished with 698 */ 699 700fail_out: 701 lws_adns_q_destroy(q); 702} 703 704