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