1/*
2 * lws-api-test-async-dns
3 *
4 * Written in 2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * This api test confirms various kinds of async dns apis
10 */
11
12#include <libwebsockets.h>
13#include <signal.h>
14
15static int interrupted, dtest, ok, fail, _exp = 26;
16struct lws_context *context;
17
18/*
19 * These are used to test the apis to parse and print ipv4 / ipv6 literal
20 * address strings for various cases.
21 *
22 * Expected error cases are not used to test the ip data -> string api.
23 */
24
25static const struct ipparser_tests {
26	const char	*test;
27	int		rlen;
28	const char	*emit_test;
29	int		emit_len;
30	uint8_t		b[16];
31} ipt[] = {
32	{ "2001:db8:85a3:0:0:8a2e:370:7334", 16,
33	  "2001:db8:85a3::8a2e:370:7334", 28,
34		{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,
35		  0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },
36
37	{ "2001:db8:85a3::8a2e:370:7334", 16,
38	  "2001:db8:85a3::8a2e:370:7334", 28,
39		{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,
40		  0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },
41
42	{ "::1", 16, "::1", 3,
43			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
44
45	{ "::",  16, "::", 2,
46			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
47
48	{ "::ffff:192.0.2.128", 16,  "::ffff:192.0.2.128", 18,
49		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50		  0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0x02, 0x80 } },
51
52	{ "cats", -1, "", 0,
53			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
54
55	{ "onevalid.bogus.warmcat.com", -1, "", 0,
56			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
57
58	{ "1.cat.dog.com", -1, "", 0,
59			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
60
61	{ ":::1", -8, "", 0,
62			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
63
64	{ "0:0::0:1", 16, "::1", 3,
65			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
66
67	{ "1.2.3.4", 4, "1.2.3.4", 7, { 1, 2, 3, 4 } },
68};
69
70static const struct async_dns_tests {
71	const char *dns_name;
72	int recordtype;
73	int addrlen;
74	uint8_t ads[16];
75} adt[] = {
76	{ "warmcat.com", LWS_ADNS_RECORD_A, 4,
77		{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
78	{ "libwebsockets.org", LWS_ADNS_RECORD_A, 4,
79		{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
80	{ "doesntexist", LWS_ADNS_RECORD_A, 0,
81		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
82	{ "localhost", LWS_ADNS_RECORD_A, 4,
83		{ 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
84	{ "ipv4only.warmcat.com", LWS_ADNS_RECORD_A, 4,
85		{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
86	{ "onevalid.bogus.warmcat.com", LWS_ADNS_RECORD_A, 4,
87		{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
88#if defined(LWS_WITH_IPV6)
89	{ "warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */
90		{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,
91				0, 0, 0, 0, 0, 0, 0, 1, } },
92	{ "ipv6only.warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */
93		{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,
94				0, 0, 0, 0, 0, 0, 0, 1, } },
95#endif
96};
97
98static lws_sorted_usec_list_t sul;
99
100struct lws *
101cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
102    void *opaque);
103
104static void
105next_test_cb(lws_sorted_usec_list_t *sul)
106{
107	int m;
108
109	lwsl_notice("%s: querying %s\n", __func__, adt[dtest].dns_name);
110
111	m = lws_async_dns_query(context, 0,
112				adt[dtest].dns_name,
113				(adns_query_type_t)adt[dtest].recordtype, cb1, NULL,
114				context);
115	if (m != LADNS_RET_CONTINUING && m != LADNS_RET_FOUND && m != LADNS_RET_FAILED_WSI_CLOSED) {
116		lwsl_err("%s: adns 1: %s failed: %d\n", __func__, adt[dtest].dns_name, m);
117		interrupted = 1;
118	}
119}
120
121struct lws *
122cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
123    void *opaque)
124{
125	const struct addrinfo *ac = a;
126	int ctr = 0, alen;
127	uint8_t *addr;
128	char buf[64];
129
130	dtest++;
131
132	if (!ac)
133		lwsl_warn("%s: no results\n", __func__);
134
135	/* dump the results */
136
137	while (ac) {
138		if (ac->ai_family == AF_INET) {
139			addr = (uint8_t *)&(((struct sockaddr_in *)
140					ac->ai_addr)->sin_addr.s_addr);
141			alen = 4;
142		} else {
143			addr = (uint8_t *)&(((struct sockaddr_in6 *)
144					ac->ai_addr)->sin6_addr.s6_addr);
145			alen = 16;
146		}
147		strcpy(buf, "unknown");
148		lws_write_numeric_address(addr, alen, buf, sizeof(buf));
149
150		lwsl_warn("%s: %d: %s %d %s\n", __func__, ctr++, ads, alen, buf);
151
152		ac = ac->ai_next;
153	}
154
155	ac = a;
156	while (ac) {
157		if (ac->ai_family == AF_INET) {
158			addr = (uint8_t *)&(((struct sockaddr_in *)
159					ac->ai_addr)->sin_addr.s_addr);
160			alen = 4;
161		} else {
162#if defined(LWS_WITH_IPV6)
163			addr = (uint8_t *)&(((struct sockaddr_in6 *)
164					ac->ai_addr)->sin6_addr.s6_addr);
165			alen = 16;
166#else
167			goto again;
168#endif
169		}
170		if (alen == adt[dtest - 1].addrlen &&
171		    !memcmp(adt[dtest - 1].ads, addr, (unsigned int)alen)) {
172			ok++;
173			goto next;
174		}
175#if !defined(LWS_WITH_IPV6)
176again:
177#endif
178		ac = ac->ai_next;
179	}
180
181	/* testing for NXDOMAIN? */
182
183	if (!a && !adt[dtest - 1].addrlen) {
184		ok++;
185		goto next;
186	}
187
188	lwsl_err("%s: dns test %d: no match\n", __func__, dtest);
189	fail++;
190
191next:
192	lws_async_dns_freeaddrinfo(&a);
193	if (dtest == (int)LWS_ARRAY_SIZE(adt))
194		interrupted = 1;
195	else
196		lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
197
198	return NULL;
199}
200
201static lws_sorted_usec_list_t sul_l;
202
203struct lws *
204cb_loop(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
205		void *opaque)
206{
207	if (!a) {
208		lwsl_err("%s: no results\n", __func__);
209		return NULL;
210	}
211
212	lwsl_notice("%s: addrinfo %p\n", __func__, a);\
213	lws_async_dns_freeaddrinfo(&a);
214
215	return NULL;
216}
217
218
219static void
220sul_retry_l(struct lws_sorted_usec_list *sul)
221{
222	int m;
223
224	lwsl_user("%s: starting new query\n", __func__);
225
226	m = lws_async_dns_query(context, 0, "warmcat.com",
227				    (adns_query_type_t)LWS_ADNS_RECORD_A,
228				    cb_loop, NULL, context);
229	switch (m) {
230	case LADNS_RET_FAILED_WSI_CLOSED:
231		lwsl_warn("%s: LADNS_RET_FAILED_WSI_CLOSED "
232			  "(== from cache / success in this test)\n", __func__);
233		break;
234	case LADNS_RET_NXDOMAIN:
235		lwsl_warn("%s: LADNS_RET_NXDOMAIN\n", __func__);
236		break;
237	case LADNS_RET_TIMEDOUT:
238		lwsl_warn("%s: LADNS_RET_TIMEDOUT\n", __func__);
239		break;
240	case LADNS_RET_FAILED:
241		lwsl_warn("%s: LADNS_RET_FAILED\n", __func__);
242		break;
243	case LADNS_RET_FOUND:
244		lwsl_warn("%s: LADNS_RET_FOUND\n", __func__);
245		break;
246	case LADNS_RET_CONTINUING:
247		lwsl_warn("%s: LADNS_RET_CONTINUING\n", __func__);
248		break;
249	}
250
251	lws_sul_schedule(context, 0, &sul_l, sul_retry_l, 5 * LWS_US_PER_SEC);
252}
253
254void sigint_handler(int sig)
255{
256	interrupted = 1;
257}
258
259int
260main(int argc, const char **argv)
261{
262	int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
263	struct lws_context_creation_info info;
264	const char *p;
265
266	/* the normal lws init */
267
268	signal(SIGINT, sigint_handler);
269
270	if ((p = lws_cmdline_option(argc, argv, "-d")))
271		logs = atoi(p);
272
273	lws_set_log_level(logs, NULL);
274	lwsl_user("LWS API selftest: Async DNS\n");
275
276	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
277	info.port = CONTEXT_PORT_NO_LISTEN;
278	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
279
280	context = lws_create_context(&info);
281	if (!context) {
282		lwsl_err("lws init failed\n");
283		return 1;
284	}
285
286	if (lws_cmdline_option(argc, argv, "-l")) {
287		lws_sul_schedule(context, 0, &sul_l, sul_retry_l, LWS_US_PER_SEC);
288		goto evloop;
289	}
290
291
292	/* ip address parser tests */
293
294	for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
295		uint8_t u[16];
296		int m = lws_parse_numeric_address(ipt[n].test, u, sizeof(u));
297
298		if (m != ipt[n].rlen) {
299			lwsl_err("%s: fail %s ret %d\n",
300					__func__, ipt[n].test, m);
301			fail++;
302			continue;
303		}
304
305		if (m > 0) {
306			if (memcmp(ipt[n].b, u, (unsigned int)m)) {
307				lwsl_err("%s: fail %s compare\n", __func__,
308						ipt[n].test);
309				lwsl_hexdump_notice(u, (unsigned int)m);
310				fail++;
311				continue;
312			}
313		}
314		ok++;
315	}
316
317	/* ip address formatter tests */
318
319	for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
320		char buf[64];
321		int m;
322
323		/* don't attempt to reverse the ones that are meant to fail */
324		if (ipt[n].rlen < 0)
325			continue;
326
327		m = lws_write_numeric_address(ipt[n].b, ipt[n].rlen, buf,
328						sizeof(buf));
329		if (m != ipt[n].emit_len) {
330			lwsl_err("%s: fail %s ret %d\n",
331					__func__, ipt[n].emit_test, m);
332			fail++;
333			continue;
334		}
335
336		if (m > 0) {
337			if (strcmp(ipt[n].emit_test, buf)) {
338				lwsl_err("%s: fail %s compare\n", __func__,
339						ipt[n].test);
340				lwsl_hexdump_notice(buf, (unsigned int)m);
341				fail++;
342				continue;
343			}
344		}
345		ok++;
346	}
347
348#if !defined(LWS_WITH_IPV6)
349	_exp -= 2;
350#endif
351
352	/* kick off the async dns tests */
353
354	lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
355
356evloop:
357	/* the usual lws event loop */
358
359	n = 1;
360	while (n >= 0 && !interrupted)
361		n = lws_service(context, 0);
362
363	lws_context_destroy(context);
364
365	if (fail || ok != _exp)
366		lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, _exp,
367				fail);
368	else
369		lwsl_user("Completed: ALL PASS: %d / %d\n", ok, _exp);
370
371	return !(ok == _exp && !fail);
372}
373