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