1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2020 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_cistatic const uint32_t botable[] = { 300, 500, 700, 1250, 5000
29d4afb5ceSopenharmony_ci				/* in case everything just dog slow */ };
30d4afb5ceSopenharmony_cistatic const lws_retry_bo_t retry_policy = {
31d4afb5ceSopenharmony_ci	botable, LWS_ARRAY_SIZE(botable), LWS_RETRY_CONCEAL_ALWAYS,
32d4afb5ceSopenharmony_ci	/* don't conceal after the last table entry */ 0, 0, 20 };
33d4afb5ceSopenharmony_ci
34d4afb5ceSopenharmony_civoid
35d4afb5ceSopenharmony_cilws_adns_q_destroy(lws_adns_q_t *q)
36d4afb5ceSopenharmony_ci{
37d4afb5ceSopenharmony_ci	lws_metrics_caliper_report(q->metcal, (char)q->go_nogo);
38d4afb5ceSopenharmony_ci
39d4afb5ceSopenharmony_ci	lws_sul_cancel(&q->sul);
40d4afb5ceSopenharmony_ci	lws_sul_cancel(&q->write_sul);
41d4afb5ceSopenharmony_ci	lws_dll2_remove(&q->list);
42d4afb5ceSopenharmony_ci	lws_free(q);
43d4afb5ceSopenharmony_ci}
44d4afb5ceSopenharmony_ci
45d4afb5ceSopenharmony_cilws_adns_q_t *
46d4afb5ceSopenharmony_cilws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype,
47d4afb5ceSopenharmony_ci		   lws_dll2_owner_t *owner, uint16_t tid, const char *name)
48d4afb5ceSopenharmony_ci{
49d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
50d4afb5ceSopenharmony_ci				   lws_dll2_get_head(owner)) {
51d4afb5ceSopenharmony_ci		lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
52d4afb5ceSopenharmony_ci		int n = 0, nmax = q->tids >= LWS_ARRAY_SIZE(q->tid) ?
53d4afb5ceSopenharmony_ci				  LWS_ARRAY_SIZE(q->tid) : q->tids;
54d4afb5ceSopenharmony_ci
55d4afb5ceSopenharmony_ci		if (!name)
56d4afb5ceSopenharmony_ci			for (n = 0; n < nmax; n++)
57d4afb5ceSopenharmony_ci				if ((tid & 0xfffe) == (q->tid[n] & 0xfffe))
58d4afb5ceSopenharmony_ci					return q;
59d4afb5ceSopenharmony_ci
60d4afb5ceSopenharmony_ci		if (name && q->qtype == ((tid & 1) ? LWS_ADNS_RECORD_AAAA :
61d4afb5ceSopenharmony_ci						     LWS_ADNS_RECORD_A) &&
62d4afb5ceSopenharmony_ci		    !strcasecmp(name, (const char *)&q[1])) {
63d4afb5ceSopenharmony_ci			if (owner == &dns->cached) {
64d4afb5ceSopenharmony_ci				/* Keep sorted by LRU: move to the head */
65d4afb5ceSopenharmony_ci				lws_dll2_remove(&q->list);
66d4afb5ceSopenharmony_ci				lws_dll2_add_head(&q->list, &dns->cached);
67d4afb5ceSopenharmony_ci			}
68d4afb5ceSopenharmony_ci
69d4afb5ceSopenharmony_ci			return q;
70d4afb5ceSopenharmony_ci		}
71d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
72d4afb5ceSopenharmony_ci
73d4afb5ceSopenharmony_ci	return NULL;
74d4afb5ceSopenharmony_ci}
75d4afb5ceSopenharmony_ci
76d4afb5ceSopenharmony_civoid
77d4afb5ceSopenharmony_cilws_async_dns_drop_server(struct lws_context *context)
78d4afb5ceSopenharmony_ci{
79d4afb5ceSopenharmony_ci	context->async_dns.dns_server_set = 0;
80d4afb5ceSopenharmony_ci	lws_set_timeout(context->async_dns.wsi, 1, LWS_TO_KILL_ASYNC);
81d4afb5ceSopenharmony_ci	context->async_dns.wsi = NULL;
82d4afb5ceSopenharmony_ci	context->async_dns.dns_server_connected = 0;
83d4afb5ceSopenharmony_ci}
84d4afb5ceSopenharmony_ci
85d4afb5ceSopenharmony_ciint
86d4afb5ceSopenharmony_cilws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c)
87d4afb5ceSopenharmony_ci{
88d4afb5ceSopenharmony_ci
89d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
90d4afb5ceSopenharmony_ci				   lws_dll2_get_head(&q->wsi_adns)) {
91d4afb5ceSopenharmony_ci		struct lws *w = lws_container_of(d, struct lws, adns);
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci		lws_dll2_remove(d);
94d4afb5ceSopenharmony_ci		if (c && c->results) {
95d4afb5ceSopenharmony_ci			lwsl_wsi_debug(w, "q: %p, c: %p, refcount %d -> %d",
96d4afb5ceSopenharmony_ci				    q, c, c->refcount, c->refcount + 1);
97d4afb5ceSopenharmony_ci			c->refcount++;
98d4afb5ceSopenharmony_ci		}
99d4afb5ceSopenharmony_ci		lws_set_timeout(w, NO_PENDING_TIMEOUT, 0);
100d4afb5ceSopenharmony_ci		/*
101d4afb5ceSopenharmony_ci		 * This may decide to close / delete w
102d4afb5ceSopenharmony_ci		 */
103d4afb5ceSopenharmony_ci		if (w->adns_cb(w, (const char *)&q[1], c ? c->results : NULL, 0,
104d4afb5ceSopenharmony_ci				q->opaque) == NULL)
105d4afb5ceSopenharmony_ci			lwsl_info("%s: failed\n", __func__);
106d4afb5ceSopenharmony_ci	//		lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
107d4afb5ceSopenharmony_ci	//				   "adopt udp2 fail");
108d4afb5ceSopenharmony_ci
109d4afb5ceSopenharmony_ci
110d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
111d4afb5ceSopenharmony_ci
112d4afb5ceSopenharmony_ci	if (q->standalone_cb) {
113d4afb5ceSopenharmony_ci		if (c && c->results) {
114d4afb5ceSopenharmony_ci			lwsl_wsi_debug(q->dns ? q->dns->wsi : NULL, "q: %p, c: %p, refcount %d -> %d",
115d4afb5ceSopenharmony_ci				    q, c, c->refcount, c->refcount + 1);
116d4afb5ceSopenharmony_ci			c->refcount++;
117d4afb5ceSopenharmony_ci		}
118d4afb5ceSopenharmony_ci
119d4afb5ceSopenharmony_ci		q->standalone_cb(NULL, (const char *)&q[1],
120d4afb5ceSopenharmony_ci				 c ? c->results : NULL, 0, q->opaque);
121d4afb5ceSopenharmony_ci	}
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_ci	lws_adns_dump(q->dns);
124d4afb5ceSopenharmony_ci
125d4afb5ceSopenharmony_ci	return 0;
126d4afb5ceSopenharmony_ci}
127d4afb5ceSopenharmony_ci
128d4afb5ceSopenharmony_cistatic void
129d4afb5ceSopenharmony_cilws_async_dns_sul_cb_retry(struct lws_sorted_usec_list *sul)
130d4afb5ceSopenharmony_ci{
131d4afb5ceSopenharmony_ci	lws_adns_q_t *q = lws_container_of(sul, lws_adns_q_t, sul);
132d4afb5ceSopenharmony_ci
133d4afb5ceSopenharmony_ci	lwsl_wsi_info(q->dns ? q->dns->wsi : NULL, "in");
134d4afb5ceSopenharmony_ci	lws_adns_dump(q->dns);
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	if (q->dns && q->dns->wsi) {
137d4afb5ceSopenharmony_ci		q->is_retry = 1;
138d4afb5ceSopenharmony_ci		lws_callback_on_writable(q->dns->wsi);
139d4afb5ceSopenharmony_ci	}
140d4afb5ceSopenharmony_ci}
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_cistatic void
143d4afb5ceSopenharmony_cilws_async_dns_writeable(struct lws *wsi, lws_adns_q_t *q)
144d4afb5ceSopenharmony_ci{
145d4afb5ceSopenharmony_ci	uint8_t pkt[LWS_PRE + DNS_PACKET_LEN], *e = &pkt[sizeof(pkt)], *p, *pl;
146d4afb5ceSopenharmony_ci	int m, n, which;
147d4afb5ceSopenharmony_ci	const char *name;
148d4afb5ceSopenharmony_ci
149d4afb5ceSopenharmony_ci	/*
150d4afb5ceSopenharmony_ci	 * We managed to get to the point of being WRITEABLE, which is not a
151d4afb5ceSopenharmony_ci	 * given if no routes.  So call off the write_sul timeout for that.
152d4afb5ceSopenharmony_ci	 */
153d4afb5ceSopenharmony_ci	lws_sul_cancel(&q->write_sul);
154d4afb5ceSopenharmony_ci
155d4afb5ceSopenharmony_ci	if (!q->is_retry && q->sent[0]
156d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
157d4afb5ceSopenharmony_ci	         && q->sent[0] == q->sent[1]
158d4afb5ceSopenharmony_ci#endif
159d4afb5ceSopenharmony_ci	)
160d4afb5ceSopenharmony_ci		return;
161d4afb5ceSopenharmony_ci
162d4afb5ceSopenharmony_ci	q->is_retry = 0;
163d4afb5ceSopenharmony_ci
164d4afb5ceSopenharmony_ci	/*
165d4afb5ceSopenharmony_ci	 * UDP is not reliable, it can be locally dropped, or dropped
166d4afb5ceSopenharmony_ci	 * by any intermediary or the remote peer.  So even though we
167d4afb5ceSopenharmony_ci	 * will do the write in a moment, we schedule another request
168d4afb5ceSopenharmony_ci	 * for rewrite according to the wsi retry policy.
169d4afb5ceSopenharmony_ci	 *
170d4afb5ceSopenharmony_ci	 * If the result came before, we'll cancel it as part of the
171d4afb5ceSopenharmony_ci	 * wsi close.
172d4afb5ceSopenharmony_ci	 *
173d4afb5ceSopenharmony_ci	 * If we have already reached the end of our concealed retries
174d4afb5ceSopenharmony_ci	 * in the policy, just close without another write.
175d4afb5ceSopenharmony_ci	 */
176d4afb5ceSopenharmony_ci	if (lws_dll2_is_detached(&q->sul.list) &&
177d4afb5ceSopenharmony_ci	    lws_retry_sul_schedule_retry_wsi(wsi, &q->sul,
178d4afb5ceSopenharmony_ci				       lws_async_dns_sul_cb_retry, &q->retry)) {
179d4afb5ceSopenharmony_ci		/* we have reached the end of our concealed retries */
180d4afb5ceSopenharmony_ci		lwsl_wsi_info(wsi, "failing query");
181d4afb5ceSopenharmony_ci		/*
182d4afb5ceSopenharmony_ci		 * our policy is to force reloading the dns server info
183d4afb5ceSopenharmony_ci		 * if our connection ever timed out, in case it or the
184d4afb5ceSopenharmony_ci		 * routing state changed
185d4afb5ceSopenharmony_ci		 */
186d4afb5ceSopenharmony_ci
187d4afb5ceSopenharmony_ci		lws_async_dns_drop_server(q->context);
188d4afb5ceSopenharmony_ci		goto qfail;
189d4afb5ceSopenharmony_ci	}
190d4afb5ceSopenharmony_ci
191d4afb5ceSopenharmony_ci	name = (const char *)&q[1];
192d4afb5ceSopenharmony_ci
193d4afb5ceSopenharmony_ci	p = &pkt[LWS_PRE];
194d4afb5ceSopenharmony_ci	memset(p, 0, DHO_SIZEOF);
195d4afb5ceSopenharmony_ci
196d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
197d4afb5ceSopenharmony_ci	if (!q->responded) {
198d4afb5ceSopenharmony_ci		/* must pick between ipv6 and ipv4 */
199d4afb5ceSopenharmony_ci		which = q->sent[0] >= q->sent[1];
200d4afb5ceSopenharmony_ci		q->sent[which]++;
201d4afb5ceSopenharmony_ci		q->asked = 3; /* want results for 4 & 6 before done */
202d4afb5ceSopenharmony_ci	} else
203d4afb5ceSopenharmony_ci		which = q->responded & 1;
204d4afb5ceSopenharmony_ci#else
205d4afb5ceSopenharmony_ci	which = 0;
206d4afb5ceSopenharmony_ci	q->asked = 1;
207d4afb5ceSopenharmony_ci#endif
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci	lwsl_wsi_info(wsi, "%s, which %d", name, which);
210d4afb5ceSopenharmony_ci
211d4afb5ceSopenharmony_ci	/* we hack b0 of the tid to be 0 = A, 1 = AAAA */
212d4afb5ceSopenharmony_ci
213d4afb5ceSopenharmony_ci	lws_ser_wu16be(&p[DHO_TID],
214d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
215d4afb5ceSopenharmony_ci			which ? (LADNS_MOST_RECENT_TID(q) | 1) :
216d4afb5ceSopenharmony_ci#endif
217d4afb5ceSopenharmony_ci					LADNS_MOST_RECENT_TID(q));
218d4afb5ceSopenharmony_ci	lws_ser_wu16be(&p[DHO_FLAGS], (1 << 8));
219d4afb5ceSopenharmony_ci	lws_ser_wu16be(&p[DHO_NQUERIES], 1);
220d4afb5ceSopenharmony_ci
221d4afb5ceSopenharmony_ci	p += DHO_SIZEOF;
222d4afb5ceSopenharmony_ci
223d4afb5ceSopenharmony_ci	/* start of label-formatted qname */
224d4afb5ceSopenharmony_ci
225d4afb5ceSopenharmony_ci	pl = p++;
226d4afb5ceSopenharmony_ci
227d4afb5ceSopenharmony_ci	do {
228d4afb5ceSopenharmony_ci		if (*name == '.' || !*name) {
229d4afb5ceSopenharmony_ci			*pl = (uint8_t)(unsigned int)lws_ptr_diff(p, pl + 1);
230d4afb5ceSopenharmony_ci			pl = p;
231d4afb5ceSopenharmony_ci			*p++ = 0; /* also serves as terminal length */
232d4afb5ceSopenharmony_ci			if (!*name++)
233d4afb5ceSopenharmony_ci				break;
234d4afb5ceSopenharmony_ci		} else
235d4afb5ceSopenharmony_ci			*p++ = (uint8_t)*name++;
236d4afb5ceSopenharmony_ci	} while (p + 6 < e);
237d4afb5ceSopenharmony_ci
238d4afb5ceSopenharmony_ci	if (p + 6 >= e) {
239d4afb5ceSopenharmony_ci		assert(0);
240d4afb5ceSopenharmony_ci		lwsl_wsi_err(wsi, "name too big");
241d4afb5ceSopenharmony_ci		goto qfail;
242d4afb5ceSopenharmony_ci	}
243d4afb5ceSopenharmony_ci
244d4afb5ceSopenharmony_ci	lws_ser_wu16be(p, which ? LWS_ADNS_RECORD_AAAA : LWS_ADNS_RECORD_A);
245d4afb5ceSopenharmony_ci	p += 2;
246d4afb5ceSopenharmony_ci
247d4afb5ceSopenharmony_ci	lws_ser_wu16be(p, 1); /* IN class */
248d4afb5ceSopenharmony_ci	p += 2;
249d4afb5ceSopenharmony_ci
250d4afb5ceSopenharmony_ci	assert(p < pkt + sizeof(pkt) - LWS_PRE);
251d4afb5ceSopenharmony_ci	n = lws_ptr_diff(p, pkt + LWS_PRE);
252d4afb5ceSopenharmony_ci
253d4afb5ceSopenharmony_ci	m = lws_write(wsi, pkt + LWS_PRE, (unsigned int)n, 0);
254d4afb5ceSopenharmony_ci	if (m != n) {
255d4afb5ceSopenharmony_ci		lwsl_wsi_notice(wsi, "dns write failed %d %d errno %d",
256d4afb5ceSopenharmony_ci			    m, n, errno);
257d4afb5ceSopenharmony_ci		goto qfail;
258d4afb5ceSopenharmony_ci	}
259d4afb5ceSopenharmony_ci
260d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
261d4afb5ceSopenharmony_ci	if (!q->responded && q->sent[0] != q->sent[1]) {
262d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "request writeable for ipv6");
263d4afb5ceSopenharmony_ci		lws_callback_on_writable(wsi);
264d4afb5ceSopenharmony_ci	}
265d4afb5ceSopenharmony_ci#endif
266d4afb5ceSopenharmony_ci
267d4afb5ceSopenharmony_ci	return;
268d4afb5ceSopenharmony_ci
269d4afb5ceSopenharmony_ciqfail:
270d4afb5ceSopenharmony_ci	lwsl_wsi_warn(wsi, "failing query doing NULL completion");
271d4afb5ceSopenharmony_ci	/*
272d4afb5ceSopenharmony_ci	 * in ipv6 case, we made a cache entry for the first response but
273d4afb5ceSopenharmony_ci	 * evidently the second response didn't come in time, purge the
274d4afb5ceSopenharmony_ci	 * incomplete cache entry
275d4afb5ceSopenharmony_ci	 */
276d4afb5ceSopenharmony_ci	if (q->firstcache) {
277d4afb5ceSopenharmony_ci		lwsl_wsi_debug(wsi, "destroy firstcache");
278d4afb5ceSopenharmony_ci		lws_adns_cache_destroy(q->firstcache);
279d4afb5ceSopenharmony_ci		q->firstcache = NULL;
280d4afb5ceSopenharmony_ci	}
281d4afb5ceSopenharmony_ci	lws_async_dns_complete(q, NULL);
282d4afb5ceSopenharmony_ci	lws_adns_q_destroy(q);
283d4afb5ceSopenharmony_ci}
284d4afb5ceSopenharmony_ci
285d4afb5ceSopenharmony_cistatic int
286d4afb5ceSopenharmony_cicallback_async_dns(struct lws *wsi, enum lws_callback_reasons reason,
287d4afb5ceSopenharmony_ci		   void *user, void *in, size_t len)
288d4afb5ceSopenharmony_ci{
289d4afb5ceSopenharmony_ci	struct lws_async_dns *dns = &(lws_get_context(wsi)->async_dns);
290d4afb5ceSopenharmony_ci
291d4afb5ceSopenharmony_ci	switch (reason) {
292d4afb5ceSopenharmony_ci
293d4afb5ceSopenharmony_ci	/* callbacks related to raw socket descriptor */
294d4afb5ceSopenharmony_ci
295d4afb5ceSopenharmony_ci        case LWS_CALLBACK_RAW_ADOPT:
296d4afb5ceSopenharmony_ci		//lwsl_wsi_user(wsi, "LWS_CALLBACK_RAW_ADOPT");
297d4afb5ceSopenharmony_ci                break;
298d4afb5ceSopenharmony_ci
299d4afb5ceSopenharmony_ci	case LWS_CALLBACK_RAW_CLOSE:
300d4afb5ceSopenharmony_ci		//lwsl_wsi_user(wsi, "LWS_CALLBACK_RAW_CLOSE");
301d4afb5ceSopenharmony_ci		break;
302d4afb5ceSopenharmony_ci
303d4afb5ceSopenharmony_ci	case LWS_CALLBACK_RAW_RX:
304d4afb5ceSopenharmony_ci		//lwsl_wsi_user(wsi, "LWS_CALLBACK_RAW_RX (%d)", (int)len);
305d4afb5ceSopenharmony_ci		// lwsl_hexdump_wsi_notice(wsi, in, len);
306d4afb5ceSopenharmony_ci		lws_adns_parse_udp(dns, in, len);
307d4afb5ceSopenharmony_ci		break;
308d4afb5ceSopenharmony_ci
309d4afb5ceSopenharmony_ci	case LWS_CALLBACK_RAW_WRITEABLE:
310d4afb5ceSopenharmony_ci		//lwsl_wsi_user(wsi, "LWS_CALLBACK_RAW_WRITEABLE");
311d4afb5ceSopenharmony_ci		lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
312d4afb5ceSopenharmony_ci					   dns->waiting.head) {
313d4afb5ceSopenharmony_ci			lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t,
314d4afb5ceSopenharmony_ci							   list);
315d4afb5ceSopenharmony_ci
316d4afb5ceSopenharmony_ci			if (//lws_dll2_is_detached(&q->sul.list) &&
317d4afb5ceSopenharmony_ci			    (!q->asked || q->responded != q->asked))
318d4afb5ceSopenharmony_ci				lws_async_dns_writeable(wsi, q);
319d4afb5ceSopenharmony_ci		} lws_end_foreach_dll_safe(d, d1);
320d4afb5ceSopenharmony_ci		break;
321d4afb5ceSopenharmony_ci
322d4afb5ceSopenharmony_ci	default:
323d4afb5ceSopenharmony_ci		break;
324d4afb5ceSopenharmony_ci	}
325d4afb5ceSopenharmony_ci
326d4afb5ceSopenharmony_ci	return 0;
327d4afb5ceSopenharmony_ci}
328d4afb5ceSopenharmony_ci
329d4afb5ceSopenharmony_cistruct lws_protocols lws_async_dns_protocol = {
330d4afb5ceSopenharmony_ci	"lws-async-dns", callback_async_dns, 0, 0, 0, NULL, 0
331d4afb5ceSopenharmony_ci};
332d4afb5ceSopenharmony_ci
333d4afb5ceSopenharmony_ciint
334d4afb5ceSopenharmony_cilws_async_dns_init(struct lws_context *context)
335d4afb5ceSopenharmony_ci{
336d4afb5ceSopenharmony_ci	lws_async_dns_t *dns = &context->async_dns;
337d4afb5ceSopenharmony_ci	char ads[48];
338d4afb5ceSopenharmony_ci	int n;
339d4afb5ceSopenharmony_ci
340d4afb5ceSopenharmony_ci	if (dns->wsi)
341d4afb5ceSopenharmony_ci		return 0;
342d4afb5ceSopenharmony_ci
343d4afb5ceSopenharmony_ci	if (!context->vhost_list) { /* coverity... system vhost always present */
344d4afb5ceSopenharmony_ci		lwsl_cx_err(context, "no system vhost");
345d4afb5ceSopenharmony_ci		return 1;
346d4afb5ceSopenharmony_ci	}
347d4afb5ceSopenharmony_ci
348d4afb5ceSopenharmony_ci	memset(&dns->sa46, 0, sizeof(dns->sa46));
349d4afb5ceSopenharmony_ci
350d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_DHCP_CLIENT)
351d4afb5ceSopenharmony_ci	if (lws_dhcpc_status(context, &dns->sa46))
352d4afb5ceSopenharmony_ci		goto ok;
353d4afb5ceSopenharmony_ci#endif
354d4afb5ceSopenharmony_ci
355d4afb5ceSopenharmony_ci	n = lws_plat_asyncdns_init(context, &dns->sa46);
356d4afb5ceSopenharmony_ci	if (n < 0) {
357d4afb5ceSopenharmony_ci		lwsl_cx_warn(context, "no valid dns server, retry");
358d4afb5ceSopenharmony_ci
359d4afb5ceSopenharmony_ci		return 1;
360d4afb5ceSopenharmony_ci	}
361d4afb5ceSopenharmony_ci
362d4afb5ceSopenharmony_ci	if (n != LADNS_CONF_SERVER_CHANGED)
363d4afb5ceSopenharmony_ci		return 0;
364d4afb5ceSopenharmony_ci
365d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_DHCP_CLIENT)
366d4afb5ceSopenharmony_ciok:
367d4afb5ceSopenharmony_ci#endif
368d4afb5ceSopenharmony_ci	dns->sa46.sa4.sin_port = htons(53);
369d4afb5ceSopenharmony_ci	lws_write_numeric_address((uint8_t *)&dns->sa46.sa4.sin_addr.s_addr, 4,
370d4afb5ceSopenharmony_ci				  ads, sizeof(ads));
371d4afb5ceSopenharmony_ci
372d4afb5ceSopenharmony_ci	dns->wsi = lws_create_adopt_udp(context->vhost_list, ads, 53, 0,
373d4afb5ceSopenharmony_ci					lws_async_dns_protocol.name, NULL,
374d4afb5ceSopenharmony_ci				        NULL, NULL, &retry_policy, "asyncdns");
375d4afb5ceSopenharmony_ci	if (!dns->wsi) {
376d4afb5ceSopenharmony_ci		lwsl_cx_err(context, "foreign socket adoption failed");
377d4afb5ceSopenharmony_ci		return 1;
378d4afb5ceSopenharmony_ci	}
379d4afb5ceSopenharmony_ci
380d4afb5ceSopenharmony_ci	context->async_dns.wsi->udp->sa46 = dns->sa46;
381d4afb5ceSopenharmony_ci
382d4afb5ceSopenharmony_ci	dns->dns_server_set = 1;
383d4afb5ceSopenharmony_ci
384d4afb5ceSopenharmony_ci	return 0;
385d4afb5ceSopenharmony_ci}
386d4afb5ceSopenharmony_ci
387d4afb5ceSopenharmony_cilws_adns_cache_t *
388d4afb5ceSopenharmony_cilws_adns_get_cache(lws_async_dns_t *dns, const char *name)
389d4afb5ceSopenharmony_ci{
390d4afb5ceSopenharmony_ci	lws_adns_cache_t *c;
391d4afb5ceSopenharmony_ci
392d4afb5ceSopenharmony_ci	if (!name) {
393d4afb5ceSopenharmony_ci		assert(0);
394d4afb5ceSopenharmony_ci		return NULL;
395d4afb5ceSopenharmony_ci	}
396d4afb5ceSopenharmony_ci
397d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
398d4afb5ceSopenharmony_ci				   lws_dll2_get_head(&dns->cached)) {
399d4afb5ceSopenharmony_ci		c = lws_container_of(d, lws_adns_cache_t, list);
400d4afb5ceSopenharmony_ci
401d4afb5ceSopenharmony_ci		// lwsl_wsi_notice(dns->wsi, "%s vs %s (inc %d)", name, c->name, c->incomplete);
402d4afb5ceSopenharmony_ci
403d4afb5ceSopenharmony_ci		if (!c->incomplete && !strcasecmp(name, c->name)) {
404d4afb5ceSopenharmony_ci			/* Keep sorted by LRU: move to the head */
405d4afb5ceSopenharmony_ci			lws_dll2_remove(&c->list);
406d4afb5ceSopenharmony_ci			lws_dll2_add_head(&c->list, &dns->cached);
407d4afb5ceSopenharmony_ci
408d4afb5ceSopenharmony_ci			return c;
409d4afb5ceSopenharmony_ci		}
410d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
411d4afb5ceSopenharmony_ci
412d4afb5ceSopenharmony_ci	return NULL;
413d4afb5ceSopenharmony_ci}
414d4afb5ceSopenharmony_ci
415d4afb5ceSopenharmony_ci#if defined(_DEBUG)
416d4afb5ceSopenharmony_civoid
417d4afb5ceSopenharmony_cilws_adns_dump(lws_async_dns_t *dns)
418d4afb5ceSopenharmony_ci{
419d4afb5ceSopenharmony_ci	lws_adns_cache_t *c;
420d4afb5ceSopenharmony_ci
421d4afb5ceSopenharmony_ci	if (!dns)
422d4afb5ceSopenharmony_ci		return;
423d4afb5ceSopenharmony_ci
424d4afb5ceSopenharmony_ci	lwsl_wsi_info(dns->wsi, "ADNS cache %u entries",
425d4afb5ceSopenharmony_ci			(unsigned int)dns->cached.count);
426d4afb5ceSopenharmony_ci
427d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, d,
428d4afb5ceSopenharmony_ci			      lws_dll2_get_head(&dns->cached)) {
429d4afb5ceSopenharmony_ci		c = lws_container_of(d, lws_adns_cache_t, list);
430d4afb5ceSopenharmony_ci
431d4afb5ceSopenharmony_ci		lwsl_wsi_info(dns->wsi, "cache: '%s', exp: %lldus, incomp %d, "
432d4afb5ceSopenharmony_ci			  "fl 0x%x, refc %d, res %p\n", c->name,
433d4afb5ceSopenharmony_ci			  (long long)(c->sul.us - lws_now_usecs()),
434d4afb5ceSopenharmony_ci			  c->incomplete, c->flags, c->refcount, c->results);
435d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(d);
436d4afb5ceSopenharmony_ci
437d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, d,
438d4afb5ceSopenharmony_ci				   lws_dll2_get_head(&dns->waiting)) {
439d4afb5ceSopenharmony_ci		lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
440d4afb5ceSopenharmony_ci
441d4afb5ceSopenharmony_ci		lwsl_wsi_info(dns->wsi, "q: '%s', sent %d, resp %d",
442d4afb5ceSopenharmony_ci			    (const char *)&q[1], q->sent[0],
443d4afb5ceSopenharmony_ci			    q->responded);
444d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(d);
445d4afb5ceSopenharmony_ci}
446d4afb5ceSopenharmony_ci#endif
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_civoid
449d4afb5ceSopenharmony_cilws_adns_cache_destroy(lws_adns_cache_t *c)
450d4afb5ceSopenharmony_ci{
451d4afb5ceSopenharmony_ci	lws_dll2_remove(&c->sul.list);
452d4afb5ceSopenharmony_ci	lws_dll2_remove(&c->list);
453d4afb5ceSopenharmony_ci	if (c->chain)
454d4afb5ceSopenharmony_ci		lws_free(c->chain);
455d4afb5ceSopenharmony_ci	lws_free(c);
456d4afb5ceSopenharmony_ci}
457d4afb5ceSopenharmony_ci
458d4afb5ceSopenharmony_cistatic int
459d4afb5ceSopenharmony_cicache_clean(struct lws_dll2 *d, void *user)
460d4afb5ceSopenharmony_ci{
461d4afb5ceSopenharmony_ci	lws_adns_cache_destroy(lws_container_of(d, lws_adns_cache_t, list));
462d4afb5ceSopenharmony_ci
463d4afb5ceSopenharmony_ci	return 0;
464d4afb5ceSopenharmony_ci}
465d4afb5ceSopenharmony_ci
466d4afb5ceSopenharmony_civoid
467d4afb5ceSopenharmony_cisul_cb_expire(struct lws_sorted_usec_list *sul)
468d4afb5ceSopenharmony_ci{
469d4afb5ceSopenharmony_ci	lws_adns_cache_t *c = lws_container_of(sul, lws_adns_cache_t, sul);
470d4afb5ceSopenharmony_ci
471d4afb5ceSopenharmony_ci	lws_adns_cache_destroy(c);
472d4afb5ceSopenharmony_ci}
473d4afb5ceSopenharmony_ci
474d4afb5ceSopenharmony_civoid
475d4afb5ceSopenharmony_cisul_cb_write(struct lws_sorted_usec_list *sul)
476d4afb5ceSopenharmony_ci{
477d4afb5ceSopenharmony_ci	lws_adns_q_t *q = lws_container_of(sul, lws_adns_q_t, write_sul);
478d4afb5ceSopenharmony_ci
479d4afb5ceSopenharmony_ci	/*
480d4afb5ceSopenharmony_ci	 * Something's up, we couldn't even get from write request to
481d4afb5ceSopenharmony_ci	 * WRITEABLE within the timeout, let alone the result... fail
482d4afb5ceSopenharmony_ci	 * the query and everyone riding on it...
483d4afb5ceSopenharmony_ci	 */
484d4afb5ceSopenharmony_ci
485d4afb5ceSopenharmony_ci	lwsl_wsi_info(q->dns ? q->dns->wsi : NULL, "failing");
486d4afb5ceSopenharmony_ci	lws_adns_dump(q->dns);
487d4afb5ceSopenharmony_ci
488d4afb5ceSopenharmony_ci	lws_async_dns_complete(q, NULL); /* no cache to relate to */
489d4afb5ceSopenharmony_ci	lws_adns_q_destroy(q);
490d4afb5ceSopenharmony_ci}
491d4afb5ceSopenharmony_ci
492d4afb5ceSopenharmony_civoid
493d4afb5ceSopenharmony_cilws_async_dns_freeaddrinfo(const struct addrinfo **pai)
494d4afb5ceSopenharmony_ci{
495d4afb5ceSopenharmony_ci	lws_adns_cache_t *c;
496d4afb5ceSopenharmony_ci
497d4afb5ceSopenharmony_ci	if (!*pai)
498d4afb5ceSopenharmony_ci		return;
499d4afb5ceSopenharmony_ci
500d4afb5ceSopenharmony_ci	/*
501d4afb5ceSopenharmony_ci	 * First query may have been empty... if second has something, we
502d4afb5ceSopenharmony_ci	 * fixed up the first result to point to second... but it means
503d4afb5ceSopenharmony_ci	 * looking backwards from ai, which is c->result, which is the second
504d4afb5ceSopenharmony_ci	 * packet's results, doesn't get us to the firstcache pointer.
505d4afb5ceSopenharmony_ci	 *
506d4afb5ceSopenharmony_ci	 * Adjust c to the firstcache in this case.
507d4afb5ceSopenharmony_ci	 */
508d4afb5ceSopenharmony_ci
509d4afb5ceSopenharmony_ci	c = &((lws_adns_cache_t *)(*pai))[-1];
510d4afb5ceSopenharmony_ci	if (c->firstcache)
511d4afb5ceSopenharmony_ci		c = c->firstcache;
512d4afb5ceSopenharmony_ci
513d4afb5ceSopenharmony_ci	lwsl_debug("%s: c %p, %s, refcount %d -> %d\n", __func__, c,
514d4afb5ceSopenharmony_ci		   (c->results && c->results->ai_canonname) ?
515d4afb5ceSopenharmony_ci				c->results->ai_canonname : "none",
516d4afb5ceSopenharmony_ci						c->refcount, c->refcount - 1);
517d4afb5ceSopenharmony_ci
518d4afb5ceSopenharmony_ci	assert(c->refcount > 0);
519d4afb5ceSopenharmony_ci	c->refcount--;
520d4afb5ceSopenharmony_ci	*pai = NULL;
521d4afb5ceSopenharmony_ci}
522d4afb5ceSopenharmony_ci
523d4afb5ceSopenharmony_civoid
524d4afb5ceSopenharmony_cilws_async_dns_trim_cache(lws_async_dns_t *dns)
525d4afb5ceSopenharmony_ci{
526d4afb5ceSopenharmony_ci	lws_adns_cache_t *c1;
527d4afb5ceSopenharmony_ci
528d4afb5ceSopenharmony_ci	if (dns->cached.count + 1< MAX_CACHE_ENTRIES)
529d4afb5ceSopenharmony_ci		return;
530d4afb5ceSopenharmony_ci
531d4afb5ceSopenharmony_ci	c1 = lws_container_of(lws_dll2_get_tail(&dns->cached),
532d4afb5ceSopenharmony_ci						lws_adns_cache_t, list);
533d4afb5ceSopenharmony_ci	if (c1->refcount)
534d4afb5ceSopenharmony_ci		lwsl_wsi_info(dns->wsi, "acache %p: refcount %d on purge",
535d4afb5ceSopenharmony_ci				c1, c1->refcount);
536d4afb5ceSopenharmony_ci	else
537d4afb5ceSopenharmony_ci		lws_adns_cache_destroy(c1);
538d4afb5ceSopenharmony_ci}
539d4afb5ceSopenharmony_ci
540d4afb5ceSopenharmony_ci
541d4afb5ceSopenharmony_cistatic int
542d4afb5ceSopenharmony_ciclean(struct lws_dll2 *d, void *user)
543d4afb5ceSopenharmony_ci{
544d4afb5ceSopenharmony_ci	lws_adns_q_destroy(lws_container_of(d, lws_adns_q_t, list));
545d4afb5ceSopenharmony_ci
546d4afb5ceSopenharmony_ci	return 0;
547d4afb5ceSopenharmony_ci}
548d4afb5ceSopenharmony_ci
549d4afb5ceSopenharmony_civoid
550d4afb5ceSopenharmony_cilws_async_dns_deinit(lws_async_dns_t *dns)
551d4afb5ceSopenharmony_ci{
552d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&dns->waiting, NULL, clean);
553d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&dns->cached, NULL, cache_clean);
554d4afb5ceSopenharmony_ci
555d4afb5ceSopenharmony_ci	if (dns->wsi && !dns->dns_server_connected) {
556d4afb5ceSopenharmony_ci		lwsl_wsi_notice(dns->wsi, "late free of incomplete dns wsi");
557d4afb5ceSopenharmony_ci		__lws_lc_untag(dns->wsi->a.context, &dns->wsi->lc);
558d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_METRICS)
559d4afb5ceSopenharmony_ci		lws_metrics_tags_destroy(&dns->wsi->cal_conn.mtags_owner);
560d4afb5ceSopenharmony_ci#endif
561d4afb5ceSopenharmony_ci		lws_free_set_NULL(dns->wsi->udp);
562d4afb5ceSopenharmony_ci		lws_free_set_NULL(dns->wsi);
563d4afb5ceSopenharmony_ci	}
564d4afb5ceSopenharmony_ci}
565d4afb5ceSopenharmony_ci
566d4afb5ceSopenharmony_ci
567d4afb5ceSopenharmony_cistatic int
568d4afb5ceSopenharmony_cicancel(struct lws_dll2 *d, void *user)
569d4afb5ceSopenharmony_ci{
570d4afb5ceSopenharmony_ci	lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
571d4afb5ceSopenharmony_ci
572d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d3, d4,
573d4afb5ceSopenharmony_ci				   lws_dll2_get_head(&q->wsi_adns)) {
574d4afb5ceSopenharmony_ci		struct lws *w = lws_container_of(d3, struct lws, adns);
575d4afb5ceSopenharmony_ci
576d4afb5ceSopenharmony_ci		if (user == w) {
577d4afb5ceSopenharmony_ci			lws_dll2_remove(d3);
578d4afb5ceSopenharmony_ci			if (!q->wsi_adns.count)
579d4afb5ceSopenharmony_ci				lws_adns_q_destroy(q);
580d4afb5ceSopenharmony_ci			return 1;
581d4afb5ceSopenharmony_ci		}
582d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d3, d4);
583d4afb5ceSopenharmony_ci
584d4afb5ceSopenharmony_ci	return 0;
585d4afb5ceSopenharmony_ci}
586d4afb5ceSopenharmony_ci
587d4afb5ceSopenharmony_civoid
588d4afb5ceSopenharmony_cilws_async_dns_cancel(struct lws *wsi)
589d4afb5ceSopenharmony_ci{
590d4afb5ceSopenharmony_ci	lws_async_dns_t *dns = &wsi->a.context->async_dns;
591d4afb5ceSopenharmony_ci
592d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&dns->waiting, wsi, cancel);
593d4afb5ceSopenharmony_ci}
594d4afb5ceSopenharmony_ci
595d4afb5ceSopenharmony_ci
596d4afb5ceSopenharmony_cistatic int
597d4afb5ceSopenharmony_cicheck_tid(struct lws_dll2 *d, void *user)
598d4afb5ceSopenharmony_ci{
599d4afb5ceSopenharmony_ci	lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
600d4afb5ceSopenharmony_ci	int n = 0, nmax = q->tids >= LWS_ARRAY_SIZE(q->tid) ?
601d4afb5ceSopenharmony_ci			  LWS_ARRAY_SIZE(q->tid) : q->tids;
602d4afb5ceSopenharmony_ci	uint16_t check = (uint16_t)(intptr_t)user;
603d4afb5ceSopenharmony_ci
604d4afb5ceSopenharmony_ci	for (n = 0; n < nmax; n++)
605d4afb5ceSopenharmony_ci		if (check == q->tid[n])
606d4afb5ceSopenharmony_ci			return 1;
607d4afb5ceSopenharmony_ci
608d4afb5ceSopenharmony_ci	return 0;
609d4afb5ceSopenharmony_ci}
610d4afb5ceSopenharmony_ci
611d4afb5ceSopenharmony_ciint
612d4afb5ceSopenharmony_cilws_async_dns_get_new_tid(struct lws_context *context, lws_adns_q_t *q)
613d4afb5ceSopenharmony_ci{
614d4afb5ceSopenharmony_ci	lws_async_dns_t *dns = &context->async_dns;
615d4afb5ceSopenharmony_ci	int budget = 10;
616d4afb5ceSopenharmony_ci
617d4afb5ceSopenharmony_ci	/*
618d4afb5ceSopenharmony_ci	 * Make the TID unpredictable, but must be unique amongst ongoing ones
619d4afb5ceSopenharmony_ci	 */
620d4afb5ceSopenharmony_ci	do {
621d4afb5ceSopenharmony_ci		uint16_t tid;
622d4afb5ceSopenharmony_ci
623d4afb5ceSopenharmony_ci		if (lws_get_random(context, &tid, 2) != 2)
624d4afb5ceSopenharmony_ci			return -1;
625d4afb5ceSopenharmony_ci
626d4afb5ceSopenharmony_ci		if (lws_dll2_foreach_safe(&dns->waiting,
627d4afb5ceSopenharmony_ci					  (void *)(intptr_t)tid, check_tid))
628d4afb5ceSopenharmony_ci			continue;
629d4afb5ceSopenharmony_ci
630d4afb5ceSopenharmony_ci		q->tids++;
631d4afb5ceSopenharmony_ci		LADNS_MOST_RECENT_TID(q) = tid;
632d4afb5ceSopenharmony_ci
633d4afb5ceSopenharmony_ci		return 0;
634d4afb5ceSopenharmony_ci
635d4afb5ceSopenharmony_ci	} while (budget--);
636d4afb5ceSopenharmony_ci
637d4afb5ceSopenharmony_ci	lwsl_cx_err(context, "unable to get unique tid");
638d4afb5ceSopenharmony_ci
639d4afb5ceSopenharmony_ci	return -1;
640d4afb5ceSopenharmony_ci}
641d4afb5ceSopenharmony_ci
642d4afb5ceSopenharmony_cistruct temp_q {
643d4afb5ceSopenharmony_ci	lws_adns_q_t tq;
644d4afb5ceSopenharmony_ci	char name[48];
645d4afb5ceSopenharmony_ci};
646d4afb5ceSopenharmony_ci
647d4afb5ceSopenharmony_cilws_async_dns_retcode_t
648d4afb5ceSopenharmony_cilws_async_dns_query(struct lws_context *context, int tsi, const char *name,
649d4afb5ceSopenharmony_ci		    adns_query_type_t qtype, lws_async_dns_cb_t cb,
650d4afb5ceSopenharmony_ci		    struct lws *wsi, void *opaque)
651d4afb5ceSopenharmony_ci{
652d4afb5ceSopenharmony_ci	lws_async_dns_t *dns = &context->async_dns;
653d4afb5ceSopenharmony_ci	size_t nlen = strlen(name);
654d4afb5ceSopenharmony_ci	lws_sockaddr46 *sa46;
655d4afb5ceSopenharmony_ci	lws_adns_cache_t *c;
656d4afb5ceSopenharmony_ci	struct addrinfo *ai;
657d4afb5ceSopenharmony_ci	struct temp_q tmq;
658d4afb5ceSopenharmony_ci	lws_adns_q_t *q;
659d4afb5ceSopenharmony_ci	uint8_t ads[16];
660d4afb5ceSopenharmony_ci	char *p;
661d4afb5ceSopenharmony_ci	int m;
662d4afb5ceSopenharmony_ci
663d4afb5ceSopenharmony_ci	lwsl_cx_info(context, "entry %s", name);
664d4afb5ceSopenharmony_ci	lws_adns_dump(dns);
665d4afb5ceSopenharmony_ci
666d4afb5ceSopenharmony_ci#if !defined(LWS_WITH_IPV6)
667d4afb5ceSopenharmony_ci	if (qtype == LWS_ADNS_RECORD_AAAA) {
668d4afb5ceSopenharmony_ci		lwsl_cx_err(context, "ipv6 not enabled");
669d4afb5ceSopenharmony_ci		goto failed;
670d4afb5ceSopenharmony_ci	}
671d4afb5ceSopenharmony_ci#endif
672d4afb5ceSopenharmony_ci
673d4afb5ceSopenharmony_ci	if (nlen >= DNS_MAX - 1)
674d4afb5ceSopenharmony_ci		goto failed;
675d4afb5ceSopenharmony_ci
676d4afb5ceSopenharmony_ci	/*
677d4afb5ceSopenharmony_ci	 * we magically know 'localhost' and 'localhost6' if IPv6, this is a
678d4afb5ceSopenharmony_ci	 * sort of canned /etc/hosts
679d4afb5ceSopenharmony_ci	 */
680d4afb5ceSopenharmony_ci
681d4afb5ceSopenharmony_ci	if (!strcmp(name, "localhost"))
682d4afb5ceSopenharmony_ci		name = "127.0.0.1";
683d4afb5ceSopenharmony_ci
684d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
685d4afb5ceSopenharmony_ci	if (!strcmp(name, "localhost6"))
686d4afb5ceSopenharmony_ci		name = "::1";
687d4afb5ceSopenharmony_ci#endif
688d4afb5ceSopenharmony_ci
689d4afb5ceSopenharmony_ci	if (wsi) {
690d4afb5ceSopenharmony_ci		if (!lws_dll2_is_detached(&wsi->adns)) {
691d4afb5ceSopenharmony_ci			lwsl_cx_err(context, "%s already bound to query %p",
692d4afb5ceSopenharmony_ci					lws_wsi_tag(wsi), wsi->adns.owner);
693d4afb5ceSopenharmony_ci			goto failed;
694d4afb5ceSopenharmony_ci		}
695d4afb5ceSopenharmony_ci		wsi->adns_cb = cb;
696d4afb5ceSopenharmony_ci	}
697d4afb5ceSopenharmony_ci
698d4afb5ceSopenharmony_ci	/* there's a done, cached query we can just reuse? */
699d4afb5ceSopenharmony_ci
700d4afb5ceSopenharmony_ci	c = lws_adns_get_cache(dns, name);
701d4afb5ceSopenharmony_ci	if (c) {
702d4afb5ceSopenharmony_ci		lwsl_cx_info(context, "%s: using cached, c->results %p",
703d4afb5ceSopenharmony_ci			  name, c->results);
704d4afb5ceSopenharmony_ci		m = c->results ? LADNS_RET_FOUND : LADNS_RET_FAILED;
705d4afb5ceSopenharmony_ci		if (c->results)
706d4afb5ceSopenharmony_ci			c->refcount++;
707d4afb5ceSopenharmony_ci
708d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_METRICS)
709d4afb5ceSopenharmony_ci		lws_metric_event(context->mt_adns_cache,  METRES_GO, 0);
710d4afb5ceSopenharmony_ci#endif
711d4afb5ceSopenharmony_ci
712d4afb5ceSopenharmony_ci		if (cb(wsi, name, c->results, m, opaque) == NULL)
713d4afb5ceSopenharmony_ci			return LADNS_RET_FAILED_WSI_CLOSED;
714d4afb5ceSopenharmony_ci
715d4afb5ceSopenharmony_ci		return m;
716d4afb5ceSopenharmony_ci	} else
717d4afb5ceSopenharmony_ci		lwsl_cx_info(context, "%s uncached", name);
718d4afb5ceSopenharmony_ci
719d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_METRICS)
720d4afb5ceSopenharmony_ci	lws_metric_event(context->mt_adns_cache, METRES_NOGO, 0);
721d4afb5ceSopenharmony_ci#endif
722d4afb5ceSopenharmony_ci
723d4afb5ceSopenharmony_ci	/*
724d4afb5ceSopenharmony_ci	 * It's a 1.2.3.4 or ::1 type IP address already?  We don't need a dns
725d4afb5ceSopenharmony_ci	 * server set up to be able to create an addrinfo result for that.
726d4afb5ceSopenharmony_ci	 *
727d4afb5ceSopenharmony_ci	 * Create it as a cached object so it follows the refcount lifecycle
728d4afb5ceSopenharmony_ci	 * of any other result
729d4afb5ceSopenharmony_ci	 */
730d4afb5ceSopenharmony_ci
731d4afb5ceSopenharmony_ci	m = lws_parse_numeric_address(name, ads, sizeof(ads));
732d4afb5ceSopenharmony_ci	if (m == 4
733d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
734d4afb5ceSopenharmony_ci		|| m == 16
735d4afb5ceSopenharmony_ci#endif
736d4afb5ceSopenharmony_ci	) {
737d4afb5ceSopenharmony_ci		lws_async_dns_trim_cache(dns);
738d4afb5ceSopenharmony_ci
739d4afb5ceSopenharmony_ci		c = lws_zalloc(sizeof(lws_adns_cache_t) +
740d4afb5ceSopenharmony_ci			       sizeof(struct addrinfo) +
741d4afb5ceSopenharmony_ci			       sizeof(lws_sockaddr46) + nlen + 1, "adns-numip");
742d4afb5ceSopenharmony_ci		if (!c)
743d4afb5ceSopenharmony_ci			goto failed;
744d4afb5ceSopenharmony_ci
745d4afb5ceSopenharmony_ci		ai = (struct addrinfo *)&c[1];
746d4afb5ceSopenharmony_ci		sa46 = (lws_sockaddr46 *)&ai[1];
747d4afb5ceSopenharmony_ci
748d4afb5ceSopenharmony_ci		ai->ai_socktype = SOCK_STREAM;
749d4afb5ceSopenharmony_ci		c->name = (const char *)&sa46[1];
750d4afb5ceSopenharmony_ci		memcpy((char *)c->name, name, nlen + 1);
751d4afb5ceSopenharmony_ci		ai->ai_canonname = (char *)&sa46[1];
752d4afb5ceSopenharmony_ci
753d4afb5ceSopenharmony_ci		c->results = ai;
754d4afb5ceSopenharmony_ci		memset(&tmq.tq, 0, sizeof(tmq.tq));
755d4afb5ceSopenharmony_ci		tmq.tq.opaque = opaque;
756d4afb5ceSopenharmony_ci		if (wsi) {
757d4afb5ceSopenharmony_ci			wsi->adns_cb = cb;
758d4afb5ceSopenharmony_ci			lws_dll2_add_head(&wsi->adns, &tmq.tq.wsi_adns);
759d4afb5ceSopenharmony_ci		} else
760d4afb5ceSopenharmony_ci			tmq.tq.standalone_cb = cb;
761d4afb5ceSopenharmony_ci		lws_strncpy(tmq.name, name, sizeof(tmq.name));
762d4afb5ceSopenharmony_ci
763d4afb5ceSopenharmony_ci		lws_dll2_add_head(&c->list, &dns->cached);
764d4afb5ceSopenharmony_ci		lws_sul_schedule(context, 0, &c->sul, sul_cb_expire,
765d4afb5ceSopenharmony_ci				 3600ll * LWS_US_PER_SEC);
766d4afb5ceSopenharmony_ci
767d4afb5ceSopenharmony_ci		lws_adns_dump(dns);
768d4afb5ceSopenharmony_ci	}
769d4afb5ceSopenharmony_ci
770d4afb5ceSopenharmony_ci	if (m == 4) {
771d4afb5ceSopenharmony_ci		ai = (struct addrinfo *)&c[1];
772d4afb5ceSopenharmony_ci		sa46 = (lws_sockaddr46 *)&ai[1];
773d4afb5ceSopenharmony_ci		ai->ai_family = sa46->sa4.sin_family = AF_INET;
774d4afb5ceSopenharmony_ci		ai->ai_addrlen = sizeof(sa46->sa4);
775d4afb5ceSopenharmony_ci		ai->ai_addr = (struct sockaddr *)&sa46->sa4;
776d4afb5ceSopenharmony_ci		memcpy(&sa46->sa4.sin_addr, ads, (unsigned int)m);
777d4afb5ceSopenharmony_ci
778d4afb5ceSopenharmony_ci		lws_async_dns_complete(&tmq.tq, c);
779d4afb5ceSopenharmony_ci
780d4afb5ceSopenharmony_ci		return LADNS_RET_FOUND;
781d4afb5ceSopenharmony_ci	}
782d4afb5ceSopenharmony_ci
783d4afb5ceSopenharmony_ci#if defined(LWS_WITH_IPV6)
784d4afb5ceSopenharmony_ci	if (m == 16) {
785d4afb5ceSopenharmony_ci		ai->ai_family = sa46->sa6.sin6_family = AF_INET6;
786d4afb5ceSopenharmony_ci		ai->ai_addrlen = sizeof(sa46->sa6);
787d4afb5ceSopenharmony_ci		ai->ai_addr = (struct sockaddr *)&sa46->sa6;
788d4afb5ceSopenharmony_ci		memcpy(&sa46->sa6.sin6_addr, ads, (unsigned int)m);
789d4afb5ceSopenharmony_ci
790d4afb5ceSopenharmony_ci		lws_async_dns_complete(&tmq.tq, c);
791d4afb5ceSopenharmony_ci
792d4afb5ceSopenharmony_ci		return LADNS_RET_FOUND;
793d4afb5ceSopenharmony_ci	}
794d4afb5ceSopenharmony_ci#endif
795d4afb5ceSopenharmony_ci
796d4afb5ceSopenharmony_ci	/*
797d4afb5ceSopenharmony_ci	 * to try anything else we need a remote server configured...
798d4afb5ceSopenharmony_ci	 */
799d4afb5ceSopenharmony_ci
800d4afb5ceSopenharmony_ci	if (!context->async_dns.dns_server_set &&
801d4afb5ceSopenharmony_ci	    lws_async_dns_init(context)) {
802d4afb5ceSopenharmony_ci		lwsl_cx_notice(context, "init failed");
803d4afb5ceSopenharmony_ci		goto failed;
804d4afb5ceSopenharmony_ci	}
805d4afb5ceSopenharmony_ci
806d4afb5ceSopenharmony_ci	/* there's an ongoing query we can share the result of */
807d4afb5ceSopenharmony_ci
808d4afb5ceSopenharmony_ci	q = lws_adns_get_query(dns, qtype, &dns->waiting, 0, name);
809d4afb5ceSopenharmony_ci	if (q) {
810d4afb5ceSopenharmony_ci		lwsl_cx_debug(context, "dns piggybacking: %d:%s",
811d4afb5ceSopenharmony_ci				qtype, name);
812d4afb5ceSopenharmony_ci		if (wsi)
813d4afb5ceSopenharmony_ci			lws_dll2_add_head(&wsi->adns, &q->wsi_adns);
814d4afb5ceSopenharmony_ci
815d4afb5ceSopenharmony_ci		return LADNS_RET_CONTINUING;
816d4afb5ceSopenharmony_ci	}
817d4afb5ceSopenharmony_ci
818d4afb5ceSopenharmony_ci	/*
819d4afb5ceSopenharmony_ci	 * Allocate new query / queries... this is a bit complicated because
820d4afb5ceSopenharmony_ci	 * multiple queries in one packet are not supported peoperly in DNS
821d4afb5ceSopenharmony_ci	 * itself, and there's no reliable other way to get both ipv6 and ipv4
822d4afb5ceSopenharmony_ci	 * (AAAA and A) responses in one hit.
823d4afb5ceSopenharmony_ci	 *
824d4afb5ceSopenharmony_ci	 * If we don't support ipv6, it's simple, we just ask for A and that's
825d4afb5ceSopenharmony_ci	 * it.  But if we do support ipv6, we need to ask twice, once for A
826d4afb5ceSopenharmony_ci	 * and in a separate query, again for AAAA.
827d4afb5ceSopenharmony_ci	 *
828d4afb5ceSopenharmony_ci	 * For ipv6, A / ipv4 is routable over ipv6.  So we always ask for A
829d4afb5ceSopenharmony_ci	 * first and then if ipv6, AAAA separately.
830d4afb5ceSopenharmony_ci	 *
831d4afb5ceSopenharmony_ci	 * Allocate for DNS_MAX, because we may recurse and alter what we're
832d4afb5ceSopenharmony_ci	 * looking for.
833d4afb5ceSopenharmony_ci	 *
834d4afb5ceSopenharmony_ci	 * 0             sizeof(*q)                  sizeof(*q) + DNS_MAX
835d4afb5ceSopenharmony_ci	 * [lws_adns_q_t][ name (DNS_MAX reserved) ] [ name \0 ]
836d4afb5ceSopenharmony_ci	 */
837d4afb5ceSopenharmony_ci
838d4afb5ceSopenharmony_ci	q = (lws_adns_q_t *)lws_malloc(sizeof(*q) + DNS_MAX + nlen + 1,
839d4afb5ceSopenharmony_ci					__func__);
840d4afb5ceSopenharmony_ci	if (!q)
841d4afb5ceSopenharmony_ci		goto failed;
842d4afb5ceSopenharmony_ci	memset(q, 0, sizeof(*q));
843d4afb5ceSopenharmony_ci
844d4afb5ceSopenharmony_ci	if (wsi)
845d4afb5ceSopenharmony_ci		lws_dll2_add_head(&wsi->adns, &q->wsi_adns);
846d4afb5ceSopenharmony_ci
847d4afb5ceSopenharmony_ci	q->qtype = (uint16_t)qtype;
848d4afb5ceSopenharmony_ci
849d4afb5ceSopenharmony_ci	if (lws_async_dns_get_new_tid(context, q)) {
850d4afb5ceSopenharmony_ci		lwsl_cx_err(context, "tid fail");
851d4afb5ceSopenharmony_ci		goto failed;
852d4afb5ceSopenharmony_ci	}
853d4afb5ceSopenharmony_ci
854d4afb5ceSopenharmony_ci	LADNS_MOST_RECENT_TID(q) &= 0xfffe;
855d4afb5ceSopenharmony_ci	q->context = context;
856d4afb5ceSopenharmony_ci	q->tsi = (uint8_t)tsi;
857d4afb5ceSopenharmony_ci	q->opaque = opaque;
858d4afb5ceSopenharmony_ci	q->dns = dns;
859d4afb5ceSopenharmony_ci
860d4afb5ceSopenharmony_ci	if (!wsi)
861d4afb5ceSopenharmony_ci		q->standalone_cb = cb;
862d4afb5ceSopenharmony_ci
863d4afb5ceSopenharmony_ci	/* schedule a retry according to the retry policy on the wsi */
864d4afb5ceSopenharmony_ci	if (lws_retry_sul_schedule_retry_wsi(dns->wsi, &q->sul,
865d4afb5ceSopenharmony_ci					 lws_async_dns_sul_cb_retry, &q->retry))
866d4afb5ceSopenharmony_ci		goto failed;
867d4afb5ceSopenharmony_ci
868d4afb5ceSopenharmony_ci	/* fail us if we can't write by this timeout */
869d4afb5ceSopenharmony_ci	lws_sul_schedule(context, 0, &q->write_sul, sul_cb_write, LWS_US_PER_SEC);
870d4afb5ceSopenharmony_ci
871d4afb5ceSopenharmony_ci	/*
872d4afb5ceSopenharmony_ci	 * We may rewrite the copy at +sizeof(*q) for CNAME recursion.  Keep
873d4afb5ceSopenharmony_ci	 * a second copy at + sizeof(*q) + DNS_MAX so we can create the cache
874d4afb5ceSopenharmony_ci	 * entry for the original name, not the last CNAME we met.
875d4afb5ceSopenharmony_ci	 */
876d4afb5ceSopenharmony_ci
877d4afb5ceSopenharmony_ci	p = (char *)&q[1];
878d4afb5ceSopenharmony_ci	while (nlen--) {
879d4afb5ceSopenharmony_ci		*p++ = (char)tolower(*name++);
880d4afb5ceSopenharmony_ci		p[DNS_MAX - 1] = p[-1];
881d4afb5ceSopenharmony_ci	}
882d4afb5ceSopenharmony_ci	*p = '\0';
883d4afb5ceSopenharmony_ci	p[DNS_MAX] = '\0';
884d4afb5ceSopenharmony_ci
885d4afb5ceSopenharmony_ci	lws_callback_on_writable(dns->wsi);
886d4afb5ceSopenharmony_ci
887d4afb5ceSopenharmony_ci	lws_dll2_add_head(&q->list, &dns->waiting);
888d4afb5ceSopenharmony_ci
889d4afb5ceSopenharmony_ci	lws_metrics_caliper_bind(q->metcal, context->mt_conn_dns);
890d4afb5ceSopenharmony_ci	q->go_nogo = METRES_NOGO;
891d4afb5ceSopenharmony_ci	/* caliper is reported in lws_adns_q_destroy */
892d4afb5ceSopenharmony_ci
893d4afb5ceSopenharmony_ci	lwsl_cx_info(context, "created new query: %s", name);
894d4afb5ceSopenharmony_ci	lws_adns_dump(dns);
895d4afb5ceSopenharmony_ci
896d4afb5ceSopenharmony_ci	return LADNS_RET_CONTINUING;
897d4afb5ceSopenharmony_ci
898d4afb5ceSopenharmony_cifailed:
899d4afb5ceSopenharmony_ci	lwsl_cx_notice(context, "failed");
900d4afb5ceSopenharmony_ci	if (!cb(wsi, NULL, NULL, LADNS_RET_FAILED, opaque))
901d4afb5ceSopenharmony_ci		return LADNS_RET_FAILED_WSI_CLOSED;
902d4afb5ceSopenharmony_ci
903d4afb5ceSopenharmony_ci	return LADNS_RET_FAILED;
904d4afb5ceSopenharmony_ci}
905