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