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
15 static int interrupted, dtest, ok, fail, exp = 26;
16 struct 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
25 static 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
70 static 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, 0, } },
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, 0, } },
95 #endif
96 };
97
98 static lws_sorted_usec_list_t sul;
99
100 struct lws *
101 cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
102 void *opaque);
103
104 static void
next_test_cb(lws_sorted_usec_list_t * sul)105 next_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 adt[dtest].recordtype, cb1, NULL,
114 context);
115 if (m != LADNS_RET_CONTINUING && m != LADNS_RET_FOUND) {
116 lwsl_err("%s: adns 1 failed: %d\n", __func__, m);
117 interrupted = 1;
118 }
119 }
120
121
122 struct lws *
cb1(struct lws * wsi_unused,const char * ads,const struct addrinfo * a,int n,void * opaque)123 cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
124 void *opaque)
125 {
126 const struct addrinfo *ac = a;
127 int ctr = 0, alen;
128 uint8_t *addr;
129 char buf[64];
130
131 dtest++;
132
133 if (!ac)
134 lwsl_warn("%s: no results\n", __func__);
135
136 /* dump the results */
137
138 while (ac) {
139 if (ac->ai_family == AF_INET) {
140 addr = (uint8_t *)&(((struct sockaddr_in *)
141 ac->ai_addr)->sin_addr.s_addr);
142 alen = 4;
143 } else {
144 addr = (uint8_t *)&(((struct sockaddr_in6 *)
145 ac->ai_addr)->sin6_addr.s6_addr);
146 alen = 16;
147 }
148 strcpy(buf, "unknown");
149 lws_write_numeric_address(addr, alen, buf, sizeof(buf));
150
151 lwsl_warn("%s: %d: %s %d %s\n", __func__, ctr++, ads, alen, buf);
152
153 ac = ac->ai_next;
154 }
155
156 ac = a;
157 while (ac) {
158 if (ac->ai_family == AF_INET) {
159 addr = (uint8_t *)&(((struct sockaddr_in *)
160 ac->ai_addr)->sin_addr.s_addr);
161 alen = 4;
162 } else {
163 #if defined(LWS_WITH_IPV6)
164 addr = (uint8_t *)&(((struct sockaddr_in6 *)
165 ac->ai_addr)->sin6_addr.s6_addr);
166 alen = 16;
167 #else
168 goto again;
169 #endif
170 }
171 if (alen == adt[dtest - 1].addrlen &&
172 !memcmp(adt[dtest - 1].ads, addr, alen)) {
173 ok++;
174 goto next;
175 }
176 #if !defined(LWS_WITH_IPV6)
177 again:
178 #endif
179 ac = ac->ai_next;
180 }
181
182 /* testing for NXDOMAIN? */
183
184 if (!a && !adt[dtest - 1].addrlen) {
185 ok++;
186 goto next;
187 }
188
189 lwsl_err("%s: dns test %d: no match\n", __func__, dtest);
190 fail++;
191
192 next:
193 lws_async_dns_freeaddrinfo(&a);
194 if (dtest == (int)LWS_ARRAY_SIZE(adt))
195 interrupted = 1;
196 else
197 lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
198
199 return NULL;
200 }
201
202
sigint_handler(int sig)203 void sigint_handler(int sig)
204 {
205 interrupted = 1;
206 }
207
208 int
main(int argc,const char ** argv)209 main(int argc, const char **argv)
210 {
211 int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
212 struct lws_context_creation_info info;
213 const char *p;
214
215 /* the normal lws init */
216
217 signal(SIGINT, sigint_handler);
218
219 if ((p = lws_cmdline_option(argc, argv, "-d")))
220 logs = atoi(p);
221
222 lws_set_log_level(logs, NULL);
223 lwsl_user("LWS API selftest: Async DNS\n");
224
225 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
226 info.port = CONTEXT_PORT_NO_LISTEN;
227 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
228
229 context = lws_create_context(&info);
230 if (!context) {
231 lwsl_err("lws init failed\n");
232 return 1;
233 }
234
235
236 /* ip address parser tests */
237
238 for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
239 uint8_t u[16];
240 int m = lws_parse_numeric_address(ipt[n].test, u, sizeof(u));
241
242 if (m != ipt[n].rlen) {
243 lwsl_err("%s: fail %s ret %d\n",
244 __func__, ipt[n].test, m);
245 fail++;
246 continue;
247 }
248
249 if (m > 0) {
250 if (memcmp(ipt[n].b, u, m)) {
251 lwsl_err("%s: fail %s compare\n", __func__,
252 ipt[n].test);
253 lwsl_hexdump_notice(u, m);
254 fail++;
255 continue;
256 }
257 }
258 ok++;
259 }
260
261 /* ip address formatter tests */
262
263 for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
264 char buf[64];
265 int m;
266
267 /* don't attempt to reverse the ones that are meant to fail */
268 if (ipt[n].rlen < 0)
269 continue;
270
271 m = lws_write_numeric_address(ipt[n].b, ipt[n].rlen, buf,
272 sizeof(buf));
273 if (m != ipt[n].emit_len) {
274 lwsl_err("%s: fail %s ret %d\n",
275 __func__, ipt[n].emit_test, m);
276 fail++;
277 continue;
278 }
279
280 if (m > 0) {
281 if (strcmp(ipt[n].emit_test, buf)) {
282 lwsl_err("%s: fail %s compare\n", __func__,
283 ipt[n].test);
284 lwsl_hexdump_notice(buf, m);
285 fail++;
286 continue;
287 }
288 }
289 ok++;
290 }
291
292 #if !defined(LWS_WITH_IPV6)
293 exp -= 2;
294 #endif
295
296 /* kick off the async dns tests */
297
298 lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
299
300 /* the usual lws event loop */
301
302 n = 1;
303 while (n >= 0 && !interrupted)
304 n = lws_service(context, 0);
305
306 lws_context_destroy(context);
307
308 if (fail || ok != exp)
309 lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, exp,
310 fail);
311 else
312 lwsl_user("Completed: ALL PASS: %d / %d\n", ok, exp);
313
314 return !(ok == exp && !fail);
315 }
316