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