{"schema":"libjg2-1",
"vpath":"/git/",
"avatar":"/git/avatar/",
"alang":"",
"gen_ut":1756926951,
"reponame":"libwebsockets",
"desc":"libwebsockets lightweight C networking library",
"owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://libwebsockets.org/repo/libwebsockets",
"f":3,
"items": [
{"schema":"libjg2-1",
"cid":"a8c48467fb0366a695856407294e63cf",
"commit": {"type":"commit",
"time": 1568872493,
"time_ofs": 60,
"oid_tree": { "oid": "6eeea091b7f74780498ace68af10e779811e7934", "alias": []},
"oid":{ "oid": "c591e1adfc716f7f83c56d14827c76b3b9d0fb6e", "alias": []},
"msg": "asynchronous dns for ipv4 and ipv6",
"sig_commit": { "git_time": { "time": 1568872493, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },
"sig_author": { "git_time": { "time": 1568872493, "offset": 60 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" }},
"body": "asynchronous dns for ipv4 and ipv6\n\nThis adds the option to have lws do its own dns resolution on\nthe event loop, without blocking. Existing implementations get\nthe name resolution done by the libc, which is blocking. In\nthe case you are opening client connections but need to carefully\nmanage latency, another connection opening and doing the name\nresolution becomes a big problem.\n\nCurrently it supports\n\n - ipv4 / A records\n - ipv6 / AAAA records\n - ipv4-over-ipv6 ::ffff:1.2.3.4 A record promotion for ipv6\n - only one server supported over UDP :53\n - nameserver discovery on linux, windows, freertos\n\nIt also has some nice advantages\n\n - lws-style paranoid response parsing\n - random unique tid generation to increase difficulty of poisoning\n - it's really integrated with the lws event loop, it does not spawn\n threads or use the libc resolver, and of course no blocking at all\n - platform-specific server address capturing (from /etc/resolv.conf\n on linux, windows apis on windows)\n - it has LRU caching\n - piggybacking (multiple requests before the first completes go on\n a list on the first request, not spawn multiple requests)\n - observes TTL in cache\n - TTL and timeout use lws_sul timers on the event loop\n - ipv6 pieces only built if cmake LWS_IPV6 enabled"
,
"diff": "diff --git a/.travis.yml b/.travis.yml\nindex df21b90..247a136 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -4,7 +4,7 @@ env:\n global:\n - secure: \u0022KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI\u003d\u0022\n matrix:\n- - LWS_METHOD\u003dlwsws CMAKE_ARGS\u003d\u0022-DLWS_WITH_LWSWS\u003dON -DLWS_WITHOUT_EXTENSIONS\u003d0 -DLWS_WITH_HTTP2\u003d1 -DLWS_WITH_ACME\u003d1 -DLWS_WITH_MINIMAL_EXAMPLES\u003d1 -DCMAKE_BUILD_TYPE\u003dDEBUG -DLWS_ROLE_DBUS\u003d1 -DLWS_DBUS_INCLUDE2\u003d/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO\u003d1 -DLWS_WITH_JOSE\u003d1\u0022\n+ - LWS_METHOD\u003dlwsws CMAKE_ARGS\u003d\u0022-DLWS_WITH_LWSWS\u003dON -DLWS_WITHOUT_EXTENSIONS\u003d0 -DLWS_WITH_HTTP2\u003d1 -DLWS_WITH_ACME\u003d1 -DLWS_WITH_MINIMAL_EXAMPLES\u003d1 -DCMAKE_BUILD_TYPE\u003dDEBUG -DLWS_ROLE_DBUS\u003d1 -DLWS_DBUS_INCLUDE2\u003d/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO\u003d1 -DLWS_WITH_JOSE\u003d1 -DLWS_WITH_SYS_ASYNC_DNS\u003d1\u0022\n - LWS_METHOD\u003dlwsws2 CMAKE_ARGS\u003d\u0022-DLWS_WITH_LWSWS\u003dON -DLWS_WITHOUT_EXTENSIONS\u003d0 -DLWS_WITH_HTTP2\u003d1 -DLWS_WITH_ACME\u003d1 -DLWS_WITH_MINIMAL_EXAMPLES\u003d1 -DCMAKE_BUILD_TYPE\u003dDEBUG -DLWS_ROLE_DBUS\u003d1 -DLWS_DBUS_INCLUDE2\u003d/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_LWS_DSH\u003d1\u0022\n - LWS_METHOD\u003ddefault CMAKE_ARGS\u003d\u0022-DLWS_WITH_MINIMAL_EXAMPLES\u003d1\u0022\n - LWS_METHOD\u003dmbedtls CMAKE_ARGS\u003d\u0022-DLWS_WITH_MBEDTLS\u003d1 -DLWS_WITH_HTTP2\u003d1 -DLWS_WITH_LWSWS\u003d1 -DLWS_WITH_MINIMAL_EXAMPLES\u003d1 -DLWS_WITH_JOSE\u003d1 -DCMAKE_BUILD_TYPE\u003dDEBUG\u0022\n@@ -13,7 +13,7 @@ env:\n - LWS_METHOD\u003dnoext CMAKE_ARGS\u003d\u0022-DLWS_WITHOUT_EXTENSIONS\u003dON -DLWS_WITH_MINIMAL_EXAMPLES\u003d1\u0022\n - LWS_METHOD\u003dnonetwork CMAKE_ARGS\u003d\u0022-DLWS_WITH_NETWORK\u003d0\u0022\n - LWS_METHOD\u003dlibev CMAKE_ARGS\u003d\u0022-DLWS_WITH_LIBEV\u003dON\u0022\n- - LWS_METHOD\u003dnoipv6 CMAKE_ARGS\u003d\u0022-DLWS_IPV6\u003dOFF\u0022\n+ - LWS_METHOD\u003dipv6 CMAKE_ARGS\u003d\u0022-DLWS_IPV6\u003dON\u0022\n - LWS_METHOD\u003dnossl CMAKE_ARGS\u003d\u0022-DLWS_WITH_SSL\u003dOFF\u0022\n - LWS_METHOD\u003dnodaemon CMAKE_ARGS\u003d\u0022-DLWS_WITHOUT_DAEMONIZE\u003dON\u0022\n - LWS_METHOD\u003dcgi CMAKE_ARGS\u003d\u0022-DLWS_WITH_CGI\u003dON\u0022\ndiff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 63fa67e..3779924 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -47,6 +47,7 @@ option(LWS_WITH_HTTP_BROTLI \u0022Also offer brotli http stream compression (requires\n option(LWS_WITH_ACME \u0022Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)\u0022 OFF)\n option(LWS_WITH_HUBBUB \u0022Enable libhubbub rewriting support\u0022 OFF)\n option(LWS_WITH_FTS \u0022Full Text Search support\u0022 OFF)\n+option(LWS_WITH_SYS_ASYNC_DNS \u0022Nonblocking internal IPv4 DNS resolver\u0022 OFF)\n #\n # TLS library options... all except mbedTLS are basically OpenSSL variants.\n #\n@@ -76,6 +77,7 @@ option(LWS_WITH_ESP32 \u0022Build for ESP32\u0022 OFF)\n option(LWS_WITH_ESP32_HELPER \u0022Build ESP32 helper\u0022 OFF)\n option(LWS_PLAT_OPTEE \u0022Build for OPTEE\u0022 OFF)\n option(LWS_PLAT_FREERTOS \u0022Build for FreeRTOS\u0022 OFF)\n+option(LWS_PLAT_ANDROID \u0022Android flavour of unix platform\u0022 OFF)\n \n #\n # Client / Server / Test Apps build control\n@@ -351,7 +353,7 @@ if (LWS_WITH_MBEDTLS)\n \tinclude_directories(lib/tls/mbedtls/wrapper/include)\n endif()\n \n-include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract)\n+include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract lib/system/async-dns)\n \n if (LWS_PLAT_FREERTOS)\n \tinclude_directories(lib/plat/freertos lib/plat/freertos/esp32)\n@@ -1022,6 +1024,12 @@ if (LWS_WITH_NETWORK)\n \t\tlib/roles/pipe/ops-pipe.c\n \t)\n \n+\tif (LWS_WITH_SYS_ASYNC_DNS)\n+\t\tlist(APPEND SOURCES\n+\t\t\tlib/system/async-dns/async-dns.c\n+\t\t\tlib/system/async-dns/async-dns-parse.c)\n+\tendif()\n+\n \tif (LWS_WITH_LWS_DSH)\n \t\tlist(APPEND SOURCES\n \t\t\tlib/core-net/lws-dsh.c)\n@@ -1334,6 +1342,7 @@ else()\n \t\t\t\tlib/plat/freertos/freertos-pipe.c\n \t\t\t\tlib/plat/freertos/freertos-service.c\n \t\t\t\tlib/plat/freertos/freertos-sockets.c\n+\t\t\t\tlib/plat/freertos/freertos-resolv.c\n \t\t\t\tlib/misc/romfs.c)\n \t\t\tif (LWS_WITH_ESP32_HELPER)\n \t\t\t\tlist(APPEND SOURCES lib/plat/freertos/esp32/esp32-helpers.c)\n@@ -1358,6 +1367,13 @@ else()\n \t\t\t\t\tlib/plat/unix/unix-sockets.c\n \t\t\t\t\tlib/plat/unix/unix-fds.c\n \t\t\t\t)\n+\t\t\t\tif (LWS_WITH_SYS_ASYNC_DNS)\n+\t\t\t\t\tif (LWS_PLAT_ANDROID)\n+\t\t\t\t\t\tlist(APPEND SOURCES lib/plat/unix/android/android-resolv.c)\n+\t\t\t\t\telse()\n+\t\t\t\t\t\tlist(APPEND SOURCES lib/plat/unix/unix-resolv.c)\n+\t\t\t\t\tendif()\n+\t\t\t\tendif()\n \t\t\tendif()\n \t\t\t\t\n \t\t\tif (LWS_WITH_PLUGINS AND LWS_WITH_LIBUV)\ndiff --git a/LICENSE b/LICENSE\nindex 71663ae..b3c9edb 100644\n--- a/LICENSE\n+++ b/LICENSE\n@@ -6,15 +6,16 @@ them.\n \n Original liberal license retained:\n \n- - lib/misc/sha-1.c - 3-clause BSD license retained, link to original\n- - win32port/zlib - ZLIB license (see zlib.h)\n- - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)\n+ - lib/misc/sha-1.c - 3-clause BSD license retained, link to original\n+ - win32port/zlib - ZLIB license (see zlib.h)\n+ - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)\n+ - lib/misc/base64-decode.c - already MIT\n \n Relicensed to MIT:\n \n- - lib/misc/base64-decode.c - relicensed, link to original\n- - lib/misc/daemonize.c - relicensed from Public Domain to MIT,\n- link to original Public Domain version\n+ - lib/misc/daemonize.c - relicensed from Public Domain to MIT,\n+ link to original Public Domain version\n+ - lib/plat/windows/windows-resolv.c - relicensed from \u0022Beerware v42\u0022 to MIT\n \n Public Domain (CC-zero) to simplify reuse:\n \ndiff --git a/READMEs/README.async-dns.md b/READMEs/README.async-dns.md\nnew file mode 100644\nindex 0000000..7defcae\n--- /dev/null\n+++ b/READMEs/README.async-dns.md\n@@ -0,0 +1,76 @@\n+# Asynchronous DNS\n+\n+## Introduction\n+\n+Lws now features optional asynchronous, ie, nonblocking DNS\n+resolution done on the event loop, enable `-DLWS_WITH_SYS_ASYNC_DNS\u003d1`\n+at cmake to build it in.\n+\n+## Description\n+\n+The default libc name resolution is via libc `getaddrinfo()`, which is\n+blocking, possibly for quite long periods (seconds). If you are\n+taking care about latency, but want to create outgoing connections,\n+you can't tolerate this exception from the rule that everything in\n+lws is nonblocking.\n+\n+Lws' asynchronous DNS resolver creates a caching name resolver\n+that directly queries the configured nameserver itself over UDP,\n+from the event loop.\n+\n+It supports both ipv4 / A records and ipv6 / AAAA records (see later\n+for a description about how). One server supported over UDP :53,\n+and the nameserver is autodicovered on linux, windows, and freertos.\n+ \n+Other features\n+\n+ - lws-style paranoid response parsing\n+ - random unique tid generation to increase difficulty of poisoning\n+ - it's really integrated with the lws event loop, it does not spawn\n+ threads or use the libc resolver, and of course no blocking at all\n+ - platform-specific server address capturing (from /etc/resolv.conf\n+ on linux, windows apis on windows)\n+ - LRU caching\n+ - piggybacking (multiple requests before the first completes go on\n+ a list on the first request, not spawn multiple requests)\n+ - observes TTL in cache\n+ - TTL and timeout use `lws_sul` timers on the event loop\n+ - ipv6 pieces only built if cmake `LWS_IPV6` enabled\n+\n+## Api\n+\n+If enabled at cmake, the async DNS implementation is used automatically\n+for lws client connections. It's also possible to call it directly, see\n+the api-test-async-dns example for how.\n+\n+The Api follows that of `getaddrinfo()` but results are not created on\n+the heap. Instead a single, const cached copy of the addrinfo struct\n+chain is reference-counted, with `lws_async_dns_freeaddrinfo()` provided\n+to deduct from the reference count. Cached items with a nonzero\n+reference count can't be destroyed from the cache, so it's safe to keep\n+a pointer to the results and iterate through them.\n+\n+## Dealing with IPv4 and IPv6\n+\n+DNS is a very old standard that has some quirks... one of them is that\n+multiple queries are not supported in one packet, even though the protocol\n+suggests it is. This creates problems on ipv6 enabled systems, where\n+it may prefer to have AAAA results, but the server may only have A records.\n+\n+To square the circle, for ipv4 only systems (`LWS_IPV6\u003d0`) the resolver\n+requests only A records. For ipv6-capable systems, it always requests\n+first A and then immediately afterwards AAAA records.\n+\n+To simplify the implementation, the tid b0 is used to differentiate\n+between A (b0 \u003d 0) and AAAA (b0 \u003d 1) requests and responses using the\n+same query body.\n+\n+The first response to come back is parsed, and a cache entry made...\n+it leaves a note in the query about the address of the last `struct addrinfo`\n+record. When the second response comes, a second allocation is made,\n+but not added to the logical cache... instead it's chained on to the\n+first cache entry and the `struct addrinfo` linked-list from the\n+first cache entry is extended into the second one. At the time the\n+second result arrives, the query is destroyed and the cached results\n+provided on the result callback.\n+\ndiff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in\nindex 6dc7ee3..ef12034 100644\n--- a/cmake/lws_config.h.in\n+++ b/cmake/lws_config.h.in\n@@ -95,6 +95,7 @@\n #cmakedefine LWS_WITH_ABSTRACT\n #cmakedefine LWS_WITH_ACCESS_LOG\n #cmakedefine LWS_WITH_ACME\n+#cmakedefine LWS_WITH_SYS_ASYNC_DNS\n #cmakedefine LWS_WITH_BORINGSSL\n #cmakedefine LWS_WITH_CGI\n #cmakedefine LWS_WITH_CUSTOM_HEADERS\ndiff --git a/include/libwebsockets.h b/include/libwebsockets.h\nindex 38391c9..8c582a7 100644\n--- a/include/libwebsockets.h\n+++ b/include/libwebsockets.h\n@@ -314,6 +314,7 @@ lws_pthread_mutex_unlock(pthread_mutex_t *lock)\n #ifndef lws_container_of\n #define lws_container_of(P,T,M)\t((T *)((char *)(P) - offsetof(T, M)))\n #endif\n+#define LWS_ALIGN_TO(x, bou) x +\u003d ((bou) - ((x) % (bou))) % (bou)\n \n struct lws;\n \n@@ -569,6 +570,7 @@ struct lws;\n #include \u003clibwebsockets/abstract/abstract.h\u003e\n \n #include \u003clibwebsockets/lws-test-sequencer.h\u003e\n+#include \u003clibwebsockets/lws-async-dns.h\u003e\n \n #if defined(LWS_WITH_TLS)\n \ndiff --git a/include/libwebsockets/lws-adopt.h b/include/libwebsockets/lws-adopt.h\nindex 30c91de..2d4fc5d 100644\n--- a/include/libwebsockets/lws-adopt.h\n+++ b/include/libwebsockets/lws-adopt.h\n@@ -173,6 +173,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,\n * lws_create_adopt_udp() - create, bind and adopt a UDP socket\n *\n * \u005cparam vhost:\t lws vhost\n+ * \u005cparam ads:\t\t NULL or address to do dns lookup on\n * \u005cparam port:\t\t UDP port to bind to, -1 means unbound\n * \u005cparam flags:\t 0 or LWS_CAUDP_NO_BIND\n * \u005cparam protocol_name: Name of protocol on vhost to bind wsi to\n@@ -182,6 +183,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,\n * returns NULL, having cleaned up any new wsi pieces.\n * */\n LWS_VISIBLE LWS_EXTERN struct lws *\n-lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,\n-\t\t const char *protocol_name, struct lws *parent_wsi);\n+lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,\n+\t\t int flags, const char *protocol_name,\n+\t\t struct lws *parent_wsi);\n ///@}\ndiff --git a/include/libwebsockets/lws-async-dns.h b/include/libwebsockets/lws-async-dns.h\nnew file mode 100644\nindex 0000000..a83f062\n--- /dev/null\n+++ b/include/libwebsockets/lws-async-dns.h\n@@ -0,0 +1,79 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+typedef enum dns_query_type {\n+\tLWS_ADNS_RECORD_A\t\t\t\t\t\u003d 0x01,\n+\tLWS_ADNS_RECORD_CNAME\t\t\t\t\t\u003d 0x05,\n+\tLWS_ADNS_RECORD_MX\t\t\t\t\t\u003d 0x0f,\n+\tLWS_ADNS_RECORD_AAAA\t\t\t\t\t\u003d 0x1c,\n+} adns_query_type_t;\n+\n+typedef enum {\n+\tLADNS_RET_FAILED_WSI_CLOSED\t\t\t\t\u003d -4,\n+\tLADNS_RET_NXDOMAIN\t\t\t\t\t\u003d -3,\n+\tLADNS_RET_TIMEDOUT\t\t\t\t\t\u003d -2,\n+\tLADNS_RET_FAILED\t\t\t\t\t\u003d -1,\n+\tLADNS_RET_FOUND,\n+\tLADNS_RET_CONTINUING\n+} lws_async_dns_retcode_t;\n+\n+typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads,\n+\t\t\t\t\t const struct addrinfo *result, int n,\n+\t\t\t\t\t void *opaque);\n+\n+/**\n+ * lws_async_dns_query() - perform a dns lookup using async dns\n+ *\n+ * \u005cparam context: the lws_context\n+ * \u005cparam tsi: thread service index (usually 0)\n+ * \u005cparam name: DNS name to look up\n+ * \u005cparam qtype: type of query (A, AAAA etc)\n+ * \u005cparam cb: query completion callback\n+ * \u005cparam wsi: wsi if the query is related to one\n+ *\n+ * Starts an asynchronous DNS lookup, on completion the \u005cp cb callback will\n+ * be called.\n+ *\n+ * The reference count on the cached object is incremented for every callback\n+ * that was called with the cached addrinfo results.\n+ *\n+ * The cached object can't be evicted until the reference count reaches zero...\n+ * use lws_async_dns_freeaddrinfo() to indicate you're finsihed with the\n+ * results for each callback that happened with them.\n+ */\n+LWS_VISIBLE LWS_EXTERN lws_async_dns_retcode_t\n+lws_async_dns_query(struct lws_context *context, int tsi, const char *name,\n+\t\t adns_query_type_t qtype, lws_async_dns_cb_t cb,\n+\t\t struct lws *wsi, void *opaque);\n+\n+/**\n+ * lws_async_dns_freeaddrinfo() - decrement refcount on cached addrinfo results\n+ *\n+ * \u005cparam ai: the first addrinfo returned as result in the callback\n+ *\n+ * Decrements the cache object's reference count. When it reaches zero, the\n+ * cached object may be reaped subject to LRU rules.\n+ */\n+LWS_VISIBLE LWS_EXTERN void\n+lws_async_dns_freeaddrinfo(const struct addrinfo *ai);\ndiff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c\nindex e4d05d9..d356b90 100644\n--- a/lib/core-net/adopt.c\n+++ b/lib/core-net/adopt.c\n@@ -72,6 +72,11 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)\n \tnew_wsi-\u003epending_timeout \u003d NO_PENDING_TIMEOUT;\n \tnew_wsi-\u003erxflow_change_to \u003d LWS_RXFLOW_ALLOW;\n \n+#if defined(LWS_WITH_DETAILED_LATENCY)\n+\tif (vhost-\u003econtext-\u003edetailed_latency_cb)\n+\t\tnew_wsi-\u003edetlat.earliest_write_req_pre_write \u003d lws_now_usecs();\n+#endif\n+\n \t/* initialize the instance struct */\n \n \tlwsi_set_state(new_wsi, LRS_UNCONNECTED);\n@@ -107,34 +112,15 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)\n \n /* if not a socket, it's a raw, non-ssl file descriptor */\n \n-LWS_VISIBLE struct lws *\n-lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n-\t\t\t lws_sock_file_fd_type fd, const char *vh_prot_name,\n-\t\t\t struct lws *parent)\n+static struct lws *\n+lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type,\n+\t\t\t const char *vh_prot_name, struct lws *parent)\n {\n \tstruct lws_context *context \u003d vh-\u003econtext;\n \tstruct lws_context_per_thread *pt;\n \tstruct lws *new_wsi;\n \tint n;\n \n-#if defined(LWS_WITH_PEER_LIMITS)\n-\tstruct lws_peer *peer \u003d NULL;\n-\n-\tif (type \u0026 LWS_ADOPT_SOCKET) {\n-\t\tpeer \u003d lws_get_or_create_peer(vh, fd.sockfd);\n-\n-\t\tif (peer \u0026\u0026 context-\u003eip_limit_wsi \u0026\u0026\n-\t\t peer-\u003ecount_wsi \u003e\u003d context-\u003eip_limit_wsi) {\n-\t\t\tlwsl_notice(\u0022Peer reached wsi limit %d\u005cn\u0022,\n-\t\t\t\t\tcontext-\u003eip_limit_wsi);\n-\t\t\tlws_stats_bump(\u0026context-\u003ept[0],\n-\t\t\t\t\t LWSSTATS_C_PEER_LIMIT_WSI_DENIED,\n-\t\t\t\t\t 1);\n-\t\t\treturn NULL;\n-\t\t}\n-\t}\n-#endif\n-\n \t/*\n \t * Notice that in SMP case, the wsi may be being created on an\n \t * entirely different pt / tsi for load balancing. In that case as\n@@ -145,15 +131,9 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \tif (parent)\n \t\tn \u003d parent-\u003etsi;\n \tnew_wsi \u003d lws_create_new_server_wsi(vh, n);\n-\tif (!new_wsi) {\n-\t\tif (type \u0026 LWS_ADOPT_SOCKET)\n-\t\t\tcompatible_close(fd.sockfd);\n+\tif (!new_wsi)\n \t\treturn NULL;\n-\t}\n-#if defined(LWS_WITH_PEER_LIMITS)\n-\tif (peer)\n-\t\tlws_peer_add_wsi(context, peer, new_wsi);\n-#endif\n+\n \tpt \u003d \u0026context-\u003ept[(int)new_wsi-\u003etsi];\n \tlws_stats_bump(pt, LWSSTATS_C_CONNECTIONS, 1);\n \n@@ -163,26 +143,6 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \t\tparent-\u003echild_list \u003d new_wsi;\n \t}\n \n-\t/* enforce that every fd is nonblocking */\n-\n-\tif (type \u0026 LWS_ADOPT_SOCKET) {\n-\t\tif (lws_plat_set_nonblocking(fd.sockfd)) {\n-\t\t\tlwsl_err(\u0022%s: unable to set sockfd nonblocking\u005cn\u0022,\n-\t\t\t\t __func__);\n-\t\t\tgoto bail;\n-\t\t}\n-\t}\n-#if !defined(WIN32)\n-\telse\n-\t\tif (lws_plat_set_nonblocking(fd.filefd)) {\n-\t\t\tlwsl_err(\u0022%s: unable to set filefd nonblocking\u005cn\u0022,\n-\t\t\t\t __func__);\n-\t\t\tgoto bail;\n-\t\t}\n-#endif\n-\n-\tnew_wsi-\u003edesc \u003d fd;\n-\n \tif (vh_prot_name) {\n \t\tnew_wsi-\u003eprotocol \u003d lws_vhost_name_to_protocol(new_wsi-\u003evhost,\n \t\t\t\t\t\t\t vh_prot_name);\n@@ -197,14 +157,60 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \t\t}\n \t}\n \n-\tif (!LWS_SSL_ENABLED(new_wsi-\u003evhost) || !(type \u0026 LWS_ADOPT_SOCKET))\n-\t\ttype \u0026\u003d ~LWS_ADOPT_ALLOW_SSL;\n-\n \tif (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) {\n-\t\tlwsl_err(\u0022Unable to find a role that can adopt descriptor type 0x%x\u005cn\u0022, type);\n+\t\tlwsl_err(\u0022%s: no role for desc type 0x%x\u005cn\u0022, __func__, type);\n \t\tgoto bail;\n \t}\n \n+\treturn new_wsi;\n+\n+bail:\n+ lwsl_notice(\u0022%s: exiting on bail\u005cn\u0022, __func__);\n+\tif (parent)\n+\t\tparent-\u003echild_list \u003d new_wsi-\u003esibling_list;\n+\tif (new_wsi-\u003euser_space)\n+\t\tlws_free(new_wsi-\u003euser_space);\n+\n+\tvh-\u003econtext-\u003ecount_wsi_allocated--;\n+\n+\tlws_vhost_unbind_wsi(new_wsi);\n+\tlws_free(new_wsi);\n+\n+\treturn NULL;\n+}\n+\n+static struct lws *\n+lws_adopt_descriptor_vhost2(struct lws *new_wsi, lws_adoption_type type,\n+\t\t\t lws_sock_file_fd_type fd)\n+{\n+\tstruct lws_context_per_thread *pt \u003d\n+\t\t\t\u0026new_wsi-\u003econtext-\u003ept[(int)new_wsi-\u003etsi];\n+\tint n;\n+\n+\t/* enforce that every fd is nonblocking */\n+\n+\tif (type \u0026 LWS_ADOPT_SOCKET) {\n+\t\tif (lws_plat_set_nonblocking(fd.sockfd)) {\n+\t\t\tlwsl_err(\u0022%s: unable to set sockfd %d nonblocking\u005cn\u0022,\n+\t\t\t\t __func__, fd.sockfd);\n+\t\t\tgoto fail;\n+\t\t}\n+\t}\n+#if !defined(WIN32)\n+\telse\n+\t\tif (lws_plat_set_nonblocking(fd.filefd)) {\n+\t\t\tlwsl_err(\u0022%s: unable to set filefd nonblocking\u005cn\u0022,\n+\t\t\t\t __func__);\n+\t\t\tgoto fail;\n+\t\t}\n+#endif\n+\n+\tnew_wsi-\u003edesc \u003d fd;\n+\n+\tif (!LWS_SSL_ENABLED(new_wsi-\u003evhost) ||\n+\t !(type \u0026 LWS_ADOPT_SOCKET))\n+\t\ttype \u0026\u003d ~LWS_ADOPT_ALLOW_SSL;\n+\n \t/*\n \t * A new connection was accepted. Give the user a chance to\n \t * set properties of the newly created wsi. There's no protocol\n@@ -217,8 +223,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \t\tn \u003d new_wsi-\u003erole_ops-\u003eadoption_cb[lwsi_role_server(new_wsi)];\n \n #if !defined(LWS_AMAZON_RTOS)\n-\tif (context-\u003eevent_loop_ops-\u003eaccept)\n-\t\tif (context-\u003eevent_loop_ops-\u003eaccept(new_wsi))\n+\tif (new_wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept)\n+\t\tif (new_wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept(new_wsi))\n \t\t\tgoto fail;\n #endif\n \n@@ -233,7 +239,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \n \tif (!(type \u0026 LWS_ADOPT_ALLOW_SSL)) {\n \t\tlws_pt_lock(pt, __func__);\n-\t\tif (__insert_wsi_socket_into_fds(context, new_wsi)) {\n+\t\tif (__insert_wsi_socket_into_fds(new_wsi-\u003econtext, new_wsi)) {\n \t\t\tlws_pt_unlock(pt);\n \t\t\tlwsl_err(\u0022%s: fail inserting socket\u005cn\u0022, __func__);\n \t\t\tgoto fail;\n@@ -259,7 +265,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n \t/* role may need to do something after all adoption completed */\n \n \tlws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH,\n-\t\t\t\t vh_prot_name);\n+\t\t\t\t new_wsi-\u003eprotocol-\u003ename);\n \n #if LWS_MAX_SMP \u003e 1\n \t/* its actual pt can service it now */\n@@ -277,22 +283,48 @@ fail:\n \t\t\t\t \u0022adopt skt fail\u0022);\n \n \treturn NULL;\n+}\n \n-bail:\n- lwsl_notice(\u0022%s: exiting on bail\u005cn\u0022, __func__);\n-\tif (parent)\n-\t\tparent-\u003echild_list \u003d new_wsi-\u003esibling_list;\n-\tif (new_wsi-\u003euser_space)\n-\t\tlws_free(new_wsi-\u003euser_space);\n \n-\tvh-\u003econtext-\u003ecount_wsi_allocated--;\n+/* if not a socket, it's a raw, non-ssl file descriptor */\n \n-\tlws_vhost_unbind_wsi(new_wsi);\n-\tlws_free(new_wsi);\n+LWS_VISIBLE struct lws *\n+lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,\n+\t\t\t lws_sock_file_fd_type fd, const char *vh_prot_name,\n+\t\t\t struct lws *parent)\n+{\n+\tstruct lws *new_wsi;\n+#if defined(LWS_WITH_PEER_LIMITS)\n+\tstruct lws_peer *peer \u003d NULL;\n \n-\tcompatible_close(fd.sockfd);\n+\tif (type \u0026 LWS_ADOPT_SOCKET) {\n+\t\tpeer \u003d lws_get_or_create_peer(vh, fd.sockfd);\n \n-\treturn NULL;\n+\t\tif (peer \u0026\u0026 vh-\u003econtext-\u003eip_limit_wsi \u0026\u0026\n+\t\t peer-\u003ecount_wsi \u003e\u003d vh-\u003econtext-\u003eip_limit_wsi) {\n+\t\t\tlwsl_notice(\u0022Peer reached wsi limit %d\u005cn\u0022,\n+\t\t\t\t\tvh-\u003econtext-\u003eip_limit_wsi);\n+\t\t\tlws_stats_bump(\u0026vh-\u003econtext-\u003ept[0],\n+\t\t\t\t\t LWSSTATS_C_PEER_LIMIT_WSI_DENIED,\n+\t\t\t\t\t 1);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+#endif\n+\n+\tnew_wsi \u003d lws_adopt_descriptor_vhost1(vh, type, vh_prot_name, parent);\n+\tif (!new_wsi) {\n+\t\tif (type \u0026 LWS_ADOPT_SOCKET)\n+\t\t\tcompatible_close(fd.sockfd);\n+\t\treturn NULL;\n+\t}\n+\n+#if defined(LWS_WITH_PEER_LIMITS)\n+\tif (peer)\n+\t\tlws_peer_add_wsi(vh-\u003econtext, peer, new_wsi);\n+#endif\n+\n+\treturn lws_adopt_descriptor_vhost2(new_wsi, type, fd);\n }\n \n LWS_VISIBLE struct lws *\n@@ -377,76 +409,182 @@ bail:\n \treturn NULL;\n }\n \n-LWS_EXTERN struct lws *\n-lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,\n-\t\t const char *protocol_name, struct lws *parent_wsi)\n+#if defined(LWS_WITH_CLIENT)\n+static struct lws *\n+lws_create_adopt_udp2(struct lws *wsi, const char *ads,\n+\t\t const struct addrinfo *r, int n, void *opaque)\n {\n-#if !defined(LWS_PLAT_OPTEE)\n \tlws_sock_file_fd_type sock;\n-\tstruct addrinfo h, *r, *rp;\n-\tstruct lws *wsi \u003d NULL;\n-\tchar buf[16];\n-\tint n;\n \n-\tmemset(\u0026h, 0, sizeof(h));\n-\th.ai_family \u003d AF_UNSPEC; /* Allow IPv4 or IPv6 */\n-\th.ai_socktype \u003d SOCK_DGRAM;\n-\th.ai_protocol \u003d IPPROTO_UDP;\n-\th.ai_flags \u003d AI_PASSIVE;\n-#ifdef AI_ADDRCONFIG\n-\th.ai_flags |\u003d AI_ADDRCONFIG;\n-#endif\n+\tassert(wsi);\n \n-\tlws_snprintf(buf, sizeof(buf), \u0022%u\u0022, port);\n-\tn \u003d getaddrinfo(NULL, buf, \u0026h, \u0026r);\n-\tif (n) {\n-#if !defined(LWS_PLAT_FREERTOS)\n-\t\tlwsl_info(\u0022%s: getaddrinfo error: %s\u005cn\u0022, __func__, gai_strerror(n));\n+\tif (!wsi-\u003edns_results)\n+\t\twsi-\u003edns_results_next \u003d wsi-\u003edns_results \u003d r;\n+\n+\tif (n \u003c 0 || !r)\n+\t\tgoto bail;\n+\n+\twhile (wsi-\u003edns_results_next) {\n+\n+\t\t/*\n+\t\t * We have done the dns lookup, identify the result we want\n+\t\t * if any, and then complete the adoption by binding wsi to\n+\t\t * socket opened on it.\n+\t\t *\n+\t\t * Ignore the weak assumptions about protocol driven by port\n+\t\t * number and force to DGRAM / UDP since that's what this\n+\t\t * function is for.\n+\t\t */\n+\n+\t\tsock.sockfd \u003d socket(wsi-\u003edns_results_next-\u003eai_family,\n+\t\t\t\t SOCK_DGRAM, IPPROTO_UDP);\n+\n+\t\tif (sock.sockfd \u003d\u003d LWS_SOCK_INVALID)\n+\t\t\tgoto resume;\n+\n+\t\tif (wsi-\u003edo_bind \u0026\u0026\n+\t\t bind(sock.sockfd, wsi-\u003edns_results_next-\u003eai_addr,\n+#if defined(_WIN32)\n+\t\t\t (int)wsi-\u003edns_results_next-\u003eai_addrlen\n #else\n- lwsl_info(\u0022%s: getaddrinfo error: %s\u005cn\u0022, __func__, strerror(n));\n+\t\t\t wsi-\u003edns_results_next-\u003eai_addrlen\n #endif\n-\t\tgoto bail;\n-\t}\n+\t\t\t\t\t\t\t\t ) \u003d\u003d -1) {\n+\t\t\tlwsl_notice(\u0022%s: bind failed\u005cn\u0022, __func__);\n+\t\t\tgoto resume;\n+\t\t}\n+\n+\t\tif (!wsi-\u003edo_bind) {\n+\t\t\t((struct sockaddr_in *)wsi-\u003edns_results_next-\u003eai_addr)-\u003e\n+\t\t\t\t\t\tsin_port \u003d htons(wsi-\u003ec_port);\n+\n+\t\t\tif (connect(sock.sockfd, wsi-\u003edns_results_next-\u003eai_addr,\n+\t\t\t\t wsi-\u003edns_results_next-\u003eai_addrlen) \u003d\u003d -1) {\n+\t\t\t\tlwsl_err(\u0022%s: conn fd %d fam %d %s:%u failed \u0022\n+\t\t\t\t\t \u0022(salen %d) errno %d\u005cn\u0022, __func__,\n+\t\t\t\t\t sock.sockfd,\n+\t\t\t\t\t wsi-\u003edns_results_next-\u003eai_addr-\u003esa_family,\n+\t\t\t\t\t ads ? ads : \u0022null\u0022, wsi-\u003ec_port,\n+\t\t\t\t\t (int)wsi-\u003edns_results_next-\u003eai_addrlen,\n+\t\t\t\t\t LWS_ERRNO);\n+\t\t\t\tcompatible_close(sock.sockfd);\n+\t\t\t\tgoto resume;\n+\t\t\t}\n+\n+\t\t\tmemcpy(\u0026wsi-\u003eudp-\u003esa, wsi-\u003edns_results_next-\u003eai_addr,\n+\t\t\t wsi-\u003edns_results_next-\u003eai_addrlen);\n+\t\t\twsi-\u003eudp-\u003esalen \u003d wsi-\u003edns_results_next-\u003eai_addrlen;\n+\t\t}\n+\n+\t\t/* complete the udp socket adoption flow */\n \n-\tfor (rp \u003d r; rp; rp \u003d rp-\u003eai_next) {\n-\t\tsock.sockfd \u003d socket(rp-\u003eai_family, rp-\u003eai_socktype,\n-\t\t\t\t rp-\u003eai_protocol);\n-\t\tif (sock.sockfd !\u003d LWS_SOCK_INVALID)\n-\t\t\tbreak;\n+\t\tlws_addrinfo_clean(wsi);\n+\t\treturn lws_adopt_descriptor_vhost2(wsi,\n+\t\t\t\t\t\tLWS_ADOPT_RAW_SOCKET_UDP, sock);\n+\n+resume:\n+\t\twsi-\u003edns_results_next \u003d wsi-\u003edns_results_next-\u003eai_next;\n \t}\n-\tif (!rp) {\n-\t\tlwsl_err(\u0022%s: unable to create INET socket\u005cn\u0022, __func__);\n-\t\tgoto bail1;\n+\n+\tlwsl_err(\u0022%s: unable to create INET socket\u005cn\u0022, __func__);\n+\tlws_addrinfo_clean(wsi);\n+\n+bail:\n+\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, \u0022adopt udp2 fail\u0022);\n+\n+\treturn NULL;\n+}\n+\n+struct lws *\n+lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,\n+\t\t int flags, const char *protocol_name,\n+\t\t struct lws *parent_wsi)\n+{\n+#if !defined(LWS_PLAT_OPTEE)\n+\tstruct lws *wsi;\n+\n+\tlwsl_info(\u0022%s: %s:%u\u005cn\u0022, __func__, ads ? ads : \u0022null\u0022, port);\n+\n+\t/* create the logical wsi without any valid fd */\n+\n+\twsi \u003d lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_RAW_SOCKET_UDP,\n+\t\t\t\t\t\t protocol_name, parent_wsi);\n+\tif (!wsi) {\n+\t\tlwsl_err(\u0022%s: udp wsi creation failed\u005cn\u0022, __func__);\n+\t\tgoto bail;\n \t}\n+\twsi-\u003edo_bind \u003d !!(flags \u0026 LWS_CAUDP_BIND);\n+\twsi-\u003ec_port \u003d port;\n+\n+#if !defined(LWS_WITH_SYS_ASYNC_DNS)\n+\t{\n+\t\tstruct addrinfo *r, h;\n+\t\tchar buf[16];\n+\t\tint n;\n+\n+\t\tmemset(\u0026h, 0, sizeof(h));\n+\t\th.ai_family \u003d AF_UNSPEC; /* Allow IPv4 or IPv6 */\n+\t\th.ai_socktype \u003d SOCK_DGRAM;\n+\t\th.ai_protocol \u003d IPPROTO_UDP;\n+\t\th.ai_flags \u003d AI_PASSIVE;\n+#ifdef AI_ADDRCONFIG\n+\t\th.ai_flags |\u003d AI_ADDRCONFIG;\n+#endif\n \n-\tif ((flags \u0026 LWS_CAUDP_BIND) \u0026\u0026 bind(sock.sockfd, rp-\u003eai_addr,\n-#if defined(_WIN32)\n-\t\t\t (int)rp-\u003eai_addrlen\n+\t\t/* if the dns lookup is synchronous, do the whole thing now */\n+\t\tlws_snprintf(buf, sizeof(buf), \u0022%u\u0022, port);\n+\t\tn \u003d getaddrinfo(ads, buf, \u0026h, \u0026r);\n+\t\tif (n) {\n+#if !defined(LWS_PLAT_FREERTOS)\n+\t\t\tlwsl_info(\u0022%s: getaddrinfo error: %s\u005cn\u0022, __func__,\n+\t\t\t\t gai_strerror(n));\n #else\n-\t\t\t rp-\u003eai_addrlen\n+\t\t\tlwsl_info(\u0022%s: getaddrinfo error: %s\u005cn\u0022, __func__, strerror(n));\n #endif\n-\t ) \u003d\u003d -1) {\n-\t\tlwsl_err(\u0022%s: bind failed\u005cn\u0022, __func__);\n-\t\tgoto bail2;\n+\t\t\tfreeaddrinfo(r);\n+\t\t\tgoto bail1;\n+\t\t}\n+\t\t/* complete it immediately after the blocking dns lookup\n+\t\t * finished... free r when connect either completed or failed */\n+\t\twsi \u003d lws_create_adopt_udp2(wsi, ads, r, 0, NULL);\n+\n+\t\treturn wsi;\n \t}\n+#else\n+\tif (ads) {\n+\t\t/*\n+\t\t * with async dns, use the wsi as the point about which to do\n+\t\t * the dns lookup and have it call the second part when it's\n+\t\t * done.\n+\t\t *\n+\t\t * Keep a refcount on the results and free it when we connected\n+\t\t * or definitively failed.\n+\t\t */\n+\t\tif (lws_async_dns_query(vhost-\u003econtext, 0, ads,\n+\t\t\t\t\tLWS_ADNS_RECORD_A,\n+\t\t\t\t\tlws_create_adopt_udp2, wsi, NULL) \u003d\u003d\n+\t\t\t\t\t\t\t LADNS_RET_FAILED) {\n+\t\t\tlwsl_err(\u0022%s: async dns failed\u005cn\u0022, __func__);\n+\t\t\tgoto bail1;\n+\t\t}\n+\t} else\n+\t\twsi \u003d lws_create_adopt_udp2(wsi, ads, NULL, 0, NULL);\n \n-\twsi \u003d lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,\n-\t\t\t\t protocol_name, parent_wsi);\n-\tif (!wsi)\n-\t\tlwsl_err(\u0022%s: udp adoption failed\u005cn\u0022, __func__);\n+\t/* dns lookup is happening asynchronously */\n \n-bail2:\n-\tif (!wsi)\n-\t\tcompatible_close((int)sock.sockfd);\n-bail1:\n-\tfreeaddrinfo(r);\n+\treturn wsi;\n+#endif\n \n+bail1:\n+\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, \u0022udp create fail\u0022);\n+\twsi \u003d NULL;\n bail:\n \treturn wsi;\n #else\n \treturn NULL;\n #endif\n }\n+#endif\n \n LWS_VISIBLE struct lws *\n lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,\ndiff --git a/lib/core-net/close.c b/lib/core-net/close.c\nindex eb8a5e3..989ae38 100644\n--- a/lib/core-net/close.c\n+++ b/lib/core-net/close.c\n@@ -133,6 +133,8 @@ lws_close_trans_q_leader(struct lws_dll2 *d, void *user)\n void\n lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)\n {\n+\tlws_addrinfo_clean(wsi);\n+\n \tif (wsi-\u003ealready_did_cce)\n \t\treturn;\n \n@@ -150,6 +152,22 @@ lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)\n #endif\n \n void\n+lws_addrinfo_clean(struct lws *wsi)\n+{\n+#if defined(LWS_WITH_CLIENT)\n+\tif (!wsi-\u003edns_results)\n+\t\treturn;\n+\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tlws_async_dns_freeaddrinfo(wsi-\u003edns_results);\n+#else\n+\tfreeaddrinfo((struct addrinfo *)wsi-\u003edns_results);\n+#endif\n+\twsi-\u003edns_results \u003d NULL;\n+#endif\n+}\n+\n+void\n __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,\n \t\t const char *caller)\n {\n@@ -176,6 +194,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,\n \n \tlws_free_set_NULL(wsi-\u003ecli_hostname_copy);\n \n+\tlws_addrinfo_clean(wsi);\n+\n \t/*\n \t * if we have wsi in our transaction queue, if we are closing we\n \t * must go through and close all those first\n@@ -317,6 +337,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,\n \t}\n \n \tif (lwsi_state(wsi) \u003d\u003d LRS_WAITING_CONNECT ||\n+\t lwsi_state(wsi) \u003d\u003d LRS_WAITING_DNS ||\n \t lwsi_state(wsi) \u003d\u003d LRS_H1C_ISSUE_HANDSHAKE)\n \t\tgoto just_kill_connection;\n \n@@ -347,6 +368,10 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,\n \n just_kill_connection:\n \n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tlws_async_dns_cancel(wsi);\n+#endif\n+\n #if defined(LWS_WITH_HTTP_PROXY)\n \tif (wsi-\u003ehttp.buflist_post_body)\n \t\tlws_buflist_destroy_all_segments(\u0026wsi-\u003ehttp.buflist_post_body);\n@@ -371,6 +396,7 @@ just_kill_connection:\n \n #if defined(LWS_WITH_CLIENT)\n \tif ((lwsi_state(wsi) \u003d\u003d LRS_WAITING_SERVER_REPLY ||\n+\t lwsi_state(wsi) \u003d\u003d LRS_WAITING_DNS ||\n \t lwsi_state(wsi) \u003d\u003d LRS_WAITING_CONNECT) \u0026\u0026\n \t !wsi-\u003ealready_did_cce \u0026\u0026 wsi-\u003eprotocol)\n \t\tlws_inform_client_conn_fail(wsi,\ndiff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h\nindex c1d18b0..0898c2f 100644\n--- a/lib/core-net/private-lib-core-net.h\n+++ b/lib/core-net/private-lib-core-net.h\n@@ -165,14 +165,6 @@ enum pmd_return {\n \tPMDR_FAILED \u003d -1\n };\n \n-typedef union {\n-#ifdef LWS_WITH_IPV6\n-\tstruct sockaddr_in6 sa6;\n-#endif\n-\tstruct sockaddr_in sa4;\n-} sockaddr46;\n-\n-\n #if defined(LWS_WITH_PEER_LIMITS)\n struct lws_peer {\n \tstruct lws_peer *next;\n@@ -284,7 +276,7 @@ typedef struct lws_dsh_obj_head {\n \n typedef struct lws_dsh_obj {\n \tlws_dll2_t\t\t\tlist;\t/* must be first */\n-\tstruct lws_dsh\t\t\t*dsh;\t/* invalid when on free list */\n+\tstruct lws_dsh\t \t\t*dsh;\t/* invalid when on free list */\n \tsize_t\t\t\t\tsize;\t/* invalid when on free list */\n \tsize_t\t\t\t\tasize;\n } lws_dsh_obj_t;\n@@ -307,6 +299,34 @@ typedef struct lws_dsh {\n } lws_dsh_t;\n \n /*\n+ * lws_async_dns\n+ */\n+\n+typedef struct lws_async_dns {\n+\tlws_sockaddr46 \t\tsa46; /* nameserver */\n+\tlws_dll2_owner_t\twaiting_send;\n+\tlws_dll2_owner_t\twaiting_resp;\n+\tlws_dll2_owner_t\tcached;\n+\tstruct lws\t\t*wsi;\n+\ttime_t\t\t\ttime_set_server;\n+\tchar\t\t\tdns_server_set;\n+} lws_async_dns_t;\n+\n+typedef enum {\n+\tLADNS_CONF_SERVER_UNKNOWN\t\t\t\t\u003d -1,\n+\tLADNS_CONF_SERVER_SAME,\n+\tLADNS_CONF_SERVER_CHANGED\n+} lws_async_dns_server_check_t;\n+\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+void\n+lws_aysnc_dns_completed(struct lws *wsi, void *sa, size_t salen,\n+\t\t\tlws_async_dns_retcode_t ret);\n+#endif\n+void\n+lws_async_dns_cancel(struct lws *wsi);\n+\n+/*\n * so we can have n connections being serviced simultaneously,\n * these things need to be isolated per-thread.\n */\n@@ -395,6 +415,10 @@ struct lws_context_per_thread {\n \tstruct lws_signal_watcher w_sigint;\n #endif\n \n+#if defined(LWS_WITH_DETAILED_LATENCY)\n+\tlws_usec_t\tust_left_poll;\n+#endif\n+\n \t/* --- */\n \n \tunsigned long count_conns;\n@@ -448,7 +472,6 @@ struct lws_conn_stats {\n * SSL SNI -\u003e wsi -\u003e bind after SSL negotiation\n */\n \n-\n struct lws_vhost {\n #if defined(LWS_WITH_CLIENT) \u0026\u0026 defined(LWS_CLIENT_HTTP_PROXYING)\n \tchar proxy_basic_auth_token[128];\n@@ -545,6 +568,10 @@ struct lws_vhost {\n void\n __lws_vhost_destroy2(struct lws_vhost *vh);\n \n+/*\n+ * struct lws\n+ */\n+\n struct lws {\n \t/* structs */\n \n@@ -562,11 +589,6 @@ struct lws {\n \tstruct _lws_dbus_mode_related dbus;\n #endif\n \n-\n-\tconst struct lws_role_ops *role_ops;\n-\tlws_wsi_state_t\twsistate;\n-\tlws_wsi_state_t wsistate_pre_close;\n-\n \t/* lifetime members */\n \n #if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \u005c\n@@ -577,9 +599,23 @@ struct lws {\n \tstruct lws_io_watcher w_write;\n #endif\n \n+#if defined(LWS_WITH_DETAILED_LATENCY)\n+\tlws_detlat_t\tdetlat;\n+#endif\n+\n \tlws_sorted_usec_list_t sul_timeout;\n \tlws_sorted_usec_list_t sul_hrtimer;\n-\n+\tstruct lws_dll2 dll_buflist; /* guys with pending rxflow */\n+\tstruct lws_dll2 same_vh_protocol;\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tstruct lws_dll2 adns;\t/* on adns list of guys to tell result */\n+\tlws_async_dns_cb_t adns_cb;\t/* callback with result */\n+#endif\n+#if defined(LWS_WITH_CLIENT)\n+\tstruct lws_dll2 dll_cli_active_conns;\n+\tstruct lws_dll2_owner dll2_cli_txn_queue_owner;\n+\tstruct lws_dll2 dll2_cli_txn_queue;\n+#endif\n \t/* pointers */\n \n \tstruct lws_context *context;\n@@ -587,14 +623,10 @@ struct lws {\n \tstruct lws *parent; /* points to parent, if any */\n \tstruct lws *child_list; /* points to first child */\n \tstruct lws *sibling_list; /* subsequent children at same level */\n-\n+\tconst struct lws_role_ops *role_ops;\n \tconst struct lws_protocols *protocol;\n-\tstruct lws_dll2 same_vh_protocol;\n-\n \tstruct lws_sequencer *seq;\t/* associated sequencer if any */\n \n-\tstruct lws_dll2 dll_buflist; /* guys with pending rxflow */\n-\n #if defined(LWS_WITH_THREADPOOL)\n \tstruct lws_threadpool_task *tp_task;\n #endif\n@@ -607,9 +639,8 @@ struct lws {\n #if defined(LWS_WITH_CLIENT)\n \tstruct client_info_stash *stash;\n \tchar *cli_hostname_copy;\n-\tstruct lws_dll2 dll_cli_active_conns;\n-\tstruct lws_dll2_owner dll2_cli_txn_queue_owner;\n-\tstruct lws_dll2 dll2_cli_txn_queue;\n+\tconst struct addrinfo *dns_results;\n+\tconst struct addrinfo *dns_results_next;\n #endif\n \tvoid *user_space;\n \tvoid *opaque_parent_data;\n@@ -629,11 +660,8 @@ struct lws {\n \tuint64_t accept_start_us;\n #endif\n #endif\n-\n-#ifdef LWS_LATENCY\n-\tunsigned long action_start;\n-\tunsigned long latency_start;\n-#endif\n+\tlws_wsi_state_t\twsistate;\n+\tlws_wsi_state_t wsistate_pre_close;\n \n \t/* ints */\n #define LWS_NO_FDS_POS (-1)\n@@ -676,8 +704,10 @@ struct lws {\n \tunsigned int protocol_bind_balance:1;\n \tunsigned int unix_skt:1;\n \tunsigned int close_when_buffered_out_drained:1;\n-\tunsigned int h1_ws_proxied;\n-\tunsigned int proxied_ws_parent;\n+\tunsigned int h1_ws_proxied:1;\n+\tunsigned int proxied_ws_parent:1;\n+\tunsigned int do_bind:1;\n+\tunsigned int oom4:1;\n \n \tunsigned int could_have_pending:1; /* detect back-to-back writes */\n \tunsigned int outer_will_close:1;\n@@ -703,9 +733,7 @@ struct lws {\n \tunsigned int sock_send_blocking:1;\n #endif\n \n-#if defined(LWS_WITH_CLIENT)\n-\tunsigned short c_port;\n-#endif\n+\tuint16_t c_port;\n \n \t/* chars */\n \n@@ -993,7 +1021,7 @@ LWS_EXTERN lws_usec_t\n __lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow);\n \n LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT\n-lws_client_connect_2(struct lws *wsi);\n+lws_client_connect_2_dnsreq(struct lws *wsi);\n \n LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT\n lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,\n@@ -1077,6 +1105,9 @@ lws_plat_pipe_signal(struct lws *wsi);\n void\n lws_plat_pipe_close(struct lws *wsi);\n \n+void\n+lws_addrinfo_clean(struct lws *wsi);\n+\n LWS_EXTERN void\n lws_add_wsi_to_draining_ext_list(struct lws *wsi);\n LWS_EXTERN void\n@@ -1168,8 +1199,20 @@ extern const struct lws_protocols protocol_abs_client_raw_skt,\n \t\t\t\t protocol_abs_client_unit_test;\n \n void\n+__lws_reset_wsi(struct lws *wsi);\n+\n+void\n lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len);\n \n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+lws_async_dns_server_check_t\n+lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa);\n+int\n+lws_async_dns_init(struct lws_context *context);\n+void\n+lws_async_dns_deinit(lws_async_dns_t *dns);\n+#endif\n+\n #ifdef __cplusplus\n };\n #endif\ndiff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c\nindex 18b5d1e..096299f 100644\n--- a/lib/core-net/vhost.c\n+++ b/lib/core-net/vhost.c\n@@ -444,6 +444,9 @@ lws_create_vhost(struct lws_context *context,\n defined(LWS_WITH_CLIENT) \u0026\u0026 defined(LWS_HAVE_GETENV)\n \tchar *p;\n #endif\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\textern struct lws_protocols lws_async_dns_protocol;\n+#endif\n \tint n;\n \n \tif (!vh)\n@@ -558,6 +561,7 @@ lws_create_vhost(struct lws_context *context,\n \t/*\n \t * give the vhost a unified list of protocols including:\n \t *\n+\t * - internal, async_dns if enabled (first vhost only)\n \t * - internal, abstracted ones\n \t * - the ones that came from plugins\n \t * - his user protocols\n@@ -594,6 +598,16 @@ lws_create_vhost(struct lws_context *context,\n \t\tvh-\u003ecount_protocols++;\n \t}\n #endif\n+\t/*\n+\t * 3: async dns protocol (first vhost only)\n+\t */\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tif (!context-\u003evhost_list) {\n+\t\tmemcpy(\u0026lwsp[m++], \u0026lws_async_dns_protocol,\n+\t\t sizeof(struct lws_protocols));\n+\t\tvh-\u003ecount_protocols++;\n+\t}\n+#endif\n \n \t/*\n \t * 3: For compatibility, all protocols enabled on vhost if only\n@@ -763,6 +777,7 @@ lws_create_vhost(struct lws_context *context,\n \t\tgoto bail1;\n \t}\n #endif\n+\tn \u003d !!context-\u003evhost_list;\n \n \twhile (1) {\n \t\tif (!(*vh1)) {\n@@ -772,6 +787,11 @@ lws_create_vhost(struct lws_context *context,\n \t\tvh1 \u003d \u0026(*vh1)-\u003evhost_next;\n \t};\n \n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tif (!n \u0026\u0026 lws_async_dns_init(context))\n+\t\tgoto bail1;\n+#endif\n+\n \t/* for the case we are adding a vhost much later, after server init */\n \n \tif (context-\u003eprotocol_init_done)\ndiff --git a/lib/core/context.c b/lib/core/context.c\nindex c0ed762..ec9ba44 100644\n--- a/lib/core/context.c\n+++ b/lib/core/context.c\n@@ -639,6 +639,10 @@ lws_context_destroy3(struct lws_context *context)\n #endif\n \t}\n \n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tlws_async_dns_deinit(\u0026context-\u003easync_dns);\n+#endif\n+\n \tif (context-\u003ept[0].fds)\n \t\tlws_free_set_NULL(context-\u003ept[0].fds);\n #endif\ndiff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h\nindex 9fe69a3..7c318c4 100644\n--- a/lib/core/private-lib-core.h\n+++ b/lib/core/private-lib-core.h\n@@ -259,34 +259,72 @@ struct lws_deferred_free\n */\n \n struct lws_context {\n-\ttime_t last_ws_ping_pong_check_s;\n-\tlws_usec_t time_up; /* monotonic */\n+ #if defined(LWS_WITH_SERVER)\n+\tchar canonical_hostname[96];\n+ #endif\n+\n #if defined(LWS_WITH_FILE_OPS)\n-\tconst struct lws_plat_file_ops *fops;\n \tstruct lws_plat_file_ops fops_platform;\n #endif\n-\tstruct lws_context **pcontext_finalize;\n \n-#if defined(LWS_WITH_TLS)\n-\tconst struct lws_tls_ops *tls_ops;\n+#if defined(LWS_WITH_ZIP_FOPS)\n+\tstruct lws_plat_file_ops fops_zip;\n #endif\n \n-\tconst char *username, *groupname;\n+#if defined(LWS_WITH_NETWORK)\n+\n+\tstruct lws_context_per_thread pt[LWS_MAX_SMP];\n \n #if defined(LWS_WITH_HTTP2)\n \tstruct http2_settings set;\n #endif\n-#if defined(LWS_WITH_ZIP_FOPS)\n-\tstruct lws_plat_file_ops fops_zip;\n-#endif\n-#if defined(LWS_WITH_NETWORK)\n-\tstruct lws_context_per_thread pt[LWS_MAX_SMP];\n+\n #if defined(LWS_WITH_SERVER_STATUS)\n \tstruct lws_conn_stats conn_stats;\n #endif\n+#if LWS_MAX_SMP \u003e 1\n+\tstruct lws_mutex_refcount mr;\n+#endif\n+\n+#if defined(LWS_WITH_LIBEV)\n+\tstruct lws_context_eventlibs_libev ev;\n+#endif\n+#if defined(LWS_WITH_LIBUV)\n+\tstruct lws_context_eventlibs_libuv uv;\n+#endif\n+#if defined(LWS_WITH_LIBEVENT)\n+\tstruct lws_context_eventlibs_libevent event;\n+#endif\n+\n+#if defined(LWS_WITH_TLS)\n+\tstruct lws_context_tls tls;\n+#endif\n+\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tlws_async_dns_t\t\tasync_dns;\n+#endif\n+\n+\t/* pointers */\n+\n \tstruct lws_vhost *vhost_list;\n \tstruct lws_vhost *no_listener_vhost_list;\n \tstruct lws_vhost *vhost_pending_destruction_list;\n+\tstruct lws_context **pcontext_finalize;\n+\tconst char *username, *groupname;\n+\n+#if defined(LWS_WITH_SERVER)\n+\tconst char *server_string;\n+#endif\n+\n+\tstruct lws_event_loop_ops *event_loop_ops;\n+\n+#if defined(LWS_WITH_FILE_OPS)\n+\tconst struct lws_plat_file_ops *fops;\n+#endif\n+\n+#if defined(LWS_WITH_TLS)\n+\tconst struct lws_tls_ops *tls_ops;\n+#endif\n #if defined(LWS_WITH_PLUGINS)\n \tstruct lws_plugin *plugin_list;\n #endif\n@@ -297,10 +335,7 @@ struct lws_context {\n \tstruct lws **lws_lookup;\n \n #endif\n-#endif\n-#if LWS_MAX_SMP \u003e 1\n-\tstruct lws_mutex_refcount mr;\n-#endif\n+#endif /* NETWORK */\n \n #if defined(LWS_AMAZON_RTOS)\n \tmbedtls_entropy_context mec;\n@@ -329,38 +364,14 @@ struct lws_context {\n #endif\n \tvoid (*eventlib_signal_cb)(void *event_lib_handle, int signum);\n \n+\ttime_t last_ws_ping_pong_check_s;\n+\tlws_usec_t time_up; /* monotonic */\n+\n #if defined(LWS_HAVE_SYS_CAPABILITY_H) \u0026\u0026 defined(LWS_HAVE_LIBCAP)\n \tcap_value_t caps[4];\n \tchar count_caps;\n #endif\n \n-#if defined(LWS_WITH_NETWORK)\n-#if defined(LWS_WITH_LIBEV)\n-\tstruct lws_context_eventlibs_libev ev;\n-#endif\n-#if defined(LWS_WITH_LIBUV)\n-\tstruct lws_context_eventlibs_libuv uv;\n-#endif\n-#if defined(LWS_WITH_LIBEVENT)\n-\tstruct lws_context_eventlibs_libevent event;\n-#endif\n-\tstruct lws_event_loop_ops *event_loop_ops;\n-#endif\n-\n-#if defined(LWS_WITH_TLS) \u0026\u0026 defined(LWS_WITH_NETWORK)\n-\tstruct lws_context_tls tls;\n-#endif\n-\n-#if defined(LWS_WITH_SERVER)\n-\tchar canonical_hostname[128];\n-\tconst char *server_string;\n-#endif\n-\n-#ifdef LWS_LATENCY\n-\tunsigned long worst_latency;\n-\tchar worst_latency_info[256];\n-#endif\n-\n #if defined(LWS_PLAT_FREERTOS)\n \tunsigned long time_last_state_dump;\n \tuint32_t last_free_heap;\ndiff --git a/lib/misc/base64-decode.c b/lib/misc/base64-decode.c\nindex 3262685..4bd9e01 100644\n--- a/lib/misc/base64-decode.c\n+++ b/lib/misc/base64-decode.c\n@@ -3,7 +3,7 @@\n *\n * http://base64.sourceforge.net/b64.c\n *\n- * with the following license:\n+ * already with MIT license, which is retained.\n *\n * LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.\n *\n@@ -33,9 +33,7 @@\n * Bob Trower 08/04/01 -- Create Version 0.00.00B\n *\n * I cleaned it up quite a bit to match the (linux kernel) style of the rest\n- * of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws\n- * since he explicitly allows sublicensing, but I give the URL above so you can\n- * get the original with Bob's super-liberal terms directly if you prefer.\n+ * of libwebsockets\n */\n \n #include \u003cstdio.h\u003e\ndiff --git a/lib/plat/freertos/freertos-resolv.c b/lib/plat/freertos/freertos-resolv.c\nnew file mode 100644\nindex 0000000..2c76018\n--- /dev/null\n+++ b/lib/plat/freertos/freertos-resolv.c\n@@ -0,0 +1,42 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+\n+lws_async_dns_server_check_t\n+lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)\n+{\n+\tuint32_t ipv4;\n+\tlws_async_dns_server_check_t s \u003d LADNS_CONF_SERVER_CHANGED;\n+\n+\tFreeRTOS_GetAddressConfiguration(NULL, NULL, NULL, \u0026ipv4);\n+\n+\tsa46-\u003esa4.sin_family \u003d AF_INET;\n+\tif (sa46-\u003esa4.sin_addr.s_addr \u003d\u003d ipv4)\n+\t\ts \u003d LADNS_CONF_SERVER_SAME;\n+\n+\tsa46-\u003esa4.sin_addr.s_addr \u003d ipv4;\n+\n+\treturn s;\n+}\ndiff --git a/lib/plat/freertos/private-lib-plat-freertos.h b/lib/plat/freertos/private-lib-plat-freertos.h\nindex a376d27..84b2e1a 100644\n--- a/lib/plat/freertos/private-lib-plat-freertos.h\n+++ b/lib/plat/freertos/private-lib-plat-freertos.h\n@@ -55,6 +55,9 @@ gai_strerror(int);\n \n #if defined(LWS_AMAZON_RTOS)\n #include \u0022FreeRTOS.h\u0022\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+ #include \u0022FreeRTOS_IP.h\u0022\n+#endif\n #include \u0022timers.h\u0022\n #include \u003cesp_attr.h\u003e\n #else\ndiff --git a/lib/plat/unix/android/android-resolv.c b/lib/plat/unix/android/android-resolv.c\nnew file mode 100644\nindex 0000000..0f51ed0\n--- /dev/null\n+++ b/lib/plat/unix/android/android-resolv.c\n@@ -0,0 +1,54 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+#include \u003csys/system_properties.h\u003e\n+\n+lws_async_dns_server_check_t\n+lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)\n+{\n+\tchar d[PROP_VALUE_MAX], *p \u003d d;\n+\tuint32_t ip32;\n+\tuint8_t i[4];\n+\tint n;\n+\n+\td[0] \u003d '\u005c0';\n+\tif (__system_property_get(\u0022net.dns1\u0022, d) \u003c\u003d 0)\n+\t\treturn LADNS_CONF_SERVER_UNKNOWN;\n+\n+\tfor (n \u003d 0; n \u003c 4; n++) {\n+\t\ti[n] \u003d atoi(d);\n+\t\tp \u003d strchr(d, '.');\n+\t\tif (n !\u003d 3 \u0026\u0026 !p)\n+\t\t\treturn LADNS_CONF_SERVER_UNKNOWN;\n+\t}\n+\n+\tip32 \u003d (i[0] \u003c\u003c 24) | (i[1] \u003c\u003c 16) | (i[2] \u003c\u003c 8) | i[3];\n+\tn \u003d ip32 \u003d\u003d sa-\u003esin_addr.s_addr;\n+\tsa46-\u003esa4.sin_family \u003d AF_INET;\n+\tsa46-\u003esa4.sin_addr.s_addr \u003d ip32;\n+\n+\treturn n ? LADNS_CONF_SERVER_SAME : LADNS_CONF_SERVER_CHANGED;\n+}\n+\ndiff --git a/lib/plat/unix/unix-resolv.c b/lib/plat/unix/unix-resolv.c\nnew file mode 100644\nindex 0000000..9765c2d\n--- /dev/null\n+++ b/lib/plat/unix/unix-resolv.c\n@@ -0,0 +1,88 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+\n+lws_async_dns_server_check_t\n+lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)\n+{\n+\tlws_async_dns_server_check_t s \u003d LADNS_CONF_SERVER_CHANGED;\n+\tchar resolv[512], ads[48];\n+\tlws_sockaddr46 sa46t;\n+\tlws_tokenize_t ts;\n+\tint fd, n, ns \u003d 0;\n+\n+\t/* grab the first chunk of /etc/resolv.conf */\n+\n+\tfd \u003d open(\u0022/etc/resolv.conf\u0022, LWS_O_RDONLY);\n+\tif (fd \u003c 0)\n+\t\treturn LADNS_CONF_SERVER_UNKNOWN;\n+\n+\tn \u003d read(fd, resolv, sizeof(resolv) - 1);\n+\tclose(fd);\n+\tif (n \u003c 0)\n+\t\treturn LADNS_CONF_SERVER_UNKNOWN;\n+\n+\tresolv[n] \u003d '\u005c0';\n+\tlws_tokenize_init(\u0026ts, resolv, LWS_TOKENIZE_F_DOT_NONTERM |\n+\t\t\t\t LWS_TOKENIZE_F_NO_FLOATS |\n+\t\t\t\t LWS_TOKENIZE_F_NO_INTEGERS |\n+\t\t\t\t LWS_TOKENIZE_F_MINUS_NONTERM |\n+\t\t\t\t LWS_TOKENIZE_F_HASH_COMMENT);\n+\tdo {\n+\t\tts.e \u003d lws_tokenize(\u0026ts);\n+\t\tif (ts.e !\u003d LWS_TOKZE_TOKEN) {\n+\t\t\tns \u003d 0;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!ns \u0026\u0026 !strncmp(\u0022nameserver\u0022, ts.token, ts.token_len)) {\n+\t\t\tns \u003d 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!ns)\n+\t\t\tcontinue;\n+\n+\t\t/* we are a token just after the \u0022nameserver\u0022 token */\n+\n+\t\tns \u003d 0;\n+\t\tif (ts.token_len \u003e (int)sizeof(ads) - 1)\n+\t\t\tcontinue;\n+\n+\t\tmemcpy(ads, ts.token, ts.token_len);\n+\t\tads[ts.token_len] \u003d '\u005c0';\n+\t\tif (lws_sa46_parse_numeric_address(ads, \u0026sa46t) \u003c 0)\n+\t\t\tcontinue;\n+\n+\t\tif (!lws_sa46_compare_ads(sa46, \u0026sa46t))\n+\t\t\ts \u003d LADNS_CONF_SERVER_SAME;\n+\n+\t\t*sa46 \u003d sa46t;\n+\n+\t\treturn s;\n+\n+\t} while (ts.e \u003e 0);\n+\n+\treturn LADNS_CONF_SERVER_UNKNOWN;\n+}\ndiff --git a/lib/plat/windows/windows-resolv.c b/lib/plat/windows/windows-resolv.c\nnew file mode 100644\nindex 0000000..1cfbcc6\n--- /dev/null\n+++ b/lib/plat/windows/windows-resolv.c\n@@ -0,0 +1,73 @@\n+/*\n+ * Adapted from tadns 1.1, from http://adns.sourceforge.net/\n+ * Original license --\u003e\n+ *\n+ * Copyright (c) 2004-2005 Sergey Lyubka \u003cvalenok@gmail.com\u003e\n+ *\n+ * \u0022THE BEER-WARE LICENSE\u0022 (Revision 42):\n+ * Sergey Lyubka wrote this file. As long as you retain this notice you\n+ * can do whatever you want with this stuff. If we meet some day, and you think\n+ * this stuff is worth it, you can buy me a beer in return.\n+ *\n+ * Integrated into lws, largely rewritten and relicensed (as allowed above)\n+ *\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+\n+lws_async_dns_server_check_t\n+lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)\n+{\n+\tchar\tsubkey[512], dhcpns[512], ns[512], value[128], *key \u003d\n+\t\u0022SYSTEM\u005c\u005cControlSet001\u005c\u005cServices\u005c\u005cTcpip\u005c\u005cParameters\u005c\u005cInterfaces\u0022;\n+\tHKEY\thKey, hSub;\n+\tLONG\terr;\n+\tint\ti, n;\n+\n+\tif ((err \u003d RegOpenKey(HKEY_LOCAL_MACHINE, key, \u0026hKey)) !\u003d ERROR_SUCCESS) {\n+\t\tlwsl_err(\u0022%s: cannot open reg key %s: %d\u005cn\u0022, __func__, key, err);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tfor (i \u003d 0; RegEnumKey(hKey, i, subkey, sizeof(subkey)) \u003d\u003d ERROR_SUCCESS; i++) {\n+\t\tDWORD type, len \u003d sizeof(value);\n+\n+\t\tif (RegOpenKey(hKey, subkey, \u0026hSub) \u003d\u003d ERROR_SUCCESS \u0026\u0026\n+\t\t (RegQueryValueEx(hSub, \u0022NameServer\u0022, 0,\n+\t\t \u0026type, value, \u0026len) \u003d\u003d ERROR_SUCCESS ||\n+\t\t RegQueryValueEx(hSub, \u0022DhcpNameServer\u0022, 0,\n+\t\t \u0026type, value, \u0026len) \u003d\u003d ERROR_SUCCESS)) {\n+\t\t\tn \u003d lws_sa46_parse_numeric_address(value, sa46)\n+\t\t\tRegCloseKey(hSub);\n+\t\t\tRegCloseKey(hKey);\n+\t\t\treturn n \u003d\u003d 0 ? LADNS_CONF_SERVER_CHANGED :\n+\t\t\t\t\tLADNS_CONF_SERVER_UNKNOWN;\n+\t\t}\n+\t}\n+\tRegCloseKey(hKey);\n+\n+\treturn LADNS_CONF_SERVER_UNKNOWN;\n+}\n+\ndiff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c\nindex 002d7c9..688f9bf 100644\n--- a/lib/roles/http/client/client-handshake.c\n+++ b/lib/roles/http/client/client-handshake.c\n@@ -24,6 +24,7 @@\n \n #include \u0022private-lib-core.h\u0022\n \n+#if !defined(LWS_WITH_SYS_ASYNC_DNS)\n static int\n lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)\n {\n@@ -36,7 +37,7 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)\n \tif (wsi-\u003eipv6) {\n \n #if !defined(__ANDROID__)\n-\t\thints.ai_family \u003d AF_INET6;\n+\t\thints.ai_family \u003d AF_UNSPEC;\n \t\thints.ai_flags \u003d AI_V4MAPPED;\n #endif\n \t} else\n@@ -48,10 +49,11 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)\n \n \treturn getaddrinfo(ads, NULL, \u0026hints, result);\n }\n-\n+#endif\n \n struct lws *\n-lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen)\n+lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback,\n+\t\t\t\t ssize_t plen)\n {\n #if defined(LWS_CLIENT_HTTP_PROXYING)\n \tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003econtext-\u003ept[(int)wsi-\u003etsi];\n@@ -155,9 +157,9 @@ send_hs:\n \t\tlwsl_info(\u0022%s: wsi %p: waiting to send hdrs (par state 0x%x)\u005cn\u0022,\n \t\t\t __func__, wsi, lwsi_state(wsi_piggyback));\n \t} else {\n-\t\tlwsl_info(\u0022%s: wsi %p: %s %s client created own conn (raw %d)\u005cn\u0022,\n+\t\tlwsl_info(\u0022%s: wsi %p: %s %s client created own conn (raw %d) vh %s\u005cn\u0022,\n \t\t\t __func__, wsi, wsi-\u003erole_ops-\u003ename,\n-\t\t\t wsi-\u003eprotocol-\u003ename, rawish);\n+\t\t\t wsi-\u003eprotocol-\u003ename, rawish, wsi-\u003evhost-\u003ename);\n \n \t\t/* we are making our own connection */\n \t\tif (!rawish)\n@@ -228,38 +230,438 @@ failed:\n }\n \n struct lws *\n-lws_client_connect_2(struct lws *wsi)\n+lws_client_connect_3_connect(struct lws *wsi, const char *ads,\n+\t\t\t const struct addrinfo *result, int n, void *opaque)\n {\n+#if defined(LWS_WITH_UNIX_SOCK)\n+\tstruct sockaddr_un sau;\n+#endif\n #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n #if defined(LWS_CLIENT_HTTP_PROXYING)\n \tstruct lws_context *context \u003d wsi-\u003econtext;\n \tstruct lws_context_per_thread *pt \u003d \u0026context-\u003ept[(int)wsi-\u003etsi];\n #endif\n-\tconst char *adsin;\n-\tssize_t plen \u003d 0;\n #endif\n-#if defined(LWS_WITH_UNIX_SOCK)\n-\tstruct sockaddr_un sau;\n-\tchar unix_skt \u003d 0;\n+#ifdef LWS_WITH_IPV6\n+\tchar ipv6only \u003d lws_check_opt(wsi-\u003evhost-\u003eoptions,\n+\t\t\t\t LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |\n+\t\t\t\t LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);\n #endif\n-\tint n, port \u003d 0;\n+\tconst struct sockaddr *psa \u003d NULL;\n \tconst char *cce \u003d \u0022\u0022, *iface;\n-\tconst struct sockaddr *psa;\n-\tconst char *meth \u003d NULL;\n-\tstruct addrinfo *result;\n-\tconst char *ads;\n-\tsockaddr46 sa46;\n+\tssize_t plen \u003d 0;\n+\tlws_sockaddr46 sa46;\n+\tchar ni[48];\n+\tint m;\n \n #ifdef LWS_WITH_IPV6\n-\tchar ipv6only \u003d lws_check_opt(wsi-\u003evhost-\u003eoptions,\n-\t\t\tLWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |\n-\t\t\tLWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);\n-\tstruct sockaddr_in addr;\n #if defined(__ANDROID__)\n \tipv6only \u003d 0;\n #endif\n #endif\n \n+\t/*\n+\t * async dns calls back here for everybody who cares when it gets a\n+\t * result... but if we are piggybacking, we do not want to connect\n+\t * ourselves\n+\t */\n+\n+\tif (!lws_dll2_is_detached(\u0026wsi-\u003edll2_cli_txn_queue))\n+\t\treturn wsi;\n+#if 0\n+\tif (!ads \u0026\u0026 !result) {\n+\t\tcce \u003d \u0022dns resolution failed\u0022;\n+\t\tif (!wsi-\u003eoom4)\n+\t\t\tgoto oom4;\n+\t\telse\n+\t\t\tgoto failed;\n+\t}\n+#endif\n+\n+\t/*\n+\t* We can check using getsockopt if our connect actually completed\n+\t*/\n+\n+\tif (lwsi_state(wsi) \u003d\u003d LRS_WAITING_CONNECT \u0026\u0026\n+\t lws_socket_is_valid(wsi-\u003edesc.sockfd)) {\n+\t\tsocklen_t sl \u003d sizeof(int);\n+\t\tint e \u003d 0;\n+\n+\t\t/*\n+\t\t* this resets SO_ERROR after reading it. If there's an error\n+\t\t* condition the connect definitively failed.\n+\t\t*/\n+\n+\t\tif (!getsockopt(wsi-\u003edesc.sockfd, SOL_SOCKET, SO_ERROR,\n+\t\t\t\t\u0026e, \u0026sl)) {\n+\t\t\tif (!e) {\n+\t\t\t\tlwsl_info(\u0022%s: getsockopt check: conn OK\u005cn\u0022,\n+\t\t\t\t\t\t__func__);\n+\n+\t\t\t\tgoto conn_good;\n+\t\t\t}\n+\n+\t\t\tlwsl_debug(\u0022%s: getsockopt says err %d\u005cn\u0022, __func__, e);\n+\t\t}\n+\n+\t\tlwsl_debug(\u0022%s: getsockopt check: conn fail: errno %d\u005cn\u0022,\n+\t\t\t\t__func__, LWS_ERRNO);\n+\t\tgoto try_next_result_fds;\n+\t}\n+\n+#if defined(LWS_WITH_UNIX_SOCK)\n+\tif (*ads \u003d\u003d '+') {\n+\t\tads++;\n+\t\tmemset(\u0026sau, 0, sizeof(sau));\n+\t\tsau.sun_family \u003d AF_UNIX;\n+\t\tstrncpy(sau.sun_path, ads, sizeof(sau.sun_path));\n+\t\tsau.sun_path[sizeof(sau.sun_path) - 1] \u003d '\u005c0';\n+\n+\t\tlwsl_info(\u0022%s: Unix skt: %s\u005cn\u0022, __func__, ads);\n+\n+\t\tif (sau.sun_path[0] \u003d\u003d '@')\n+\t\t\tsau.sun_path[0] \u003d '\u005c0';\n+\n+\t\tgoto ads_known;\n+\t}\n+#endif\n+\n+#if defined(LWS_WITH_SYS_ASYNC_DNS)\n+\tif (n \u003d\u003d LADNS_RET_FAILED) {\n+\t\tlwsl_notice(\u0022%s: adns failed %s\u005cn\u0022, __func__, ads);\n+\t\tgoto oom4;\n+\t}\n+#endif\n+\n+\tif (!wsi-\u003edns_results) {\n+\t\twsi-\u003edns_results_next \u003d wsi-\u003edns_results \u003d result;\n+\t\tif (result)\n+\t\t\tlwsl_debug(\u0022%s: result %p result-\u003eai_next %p\u005cn\u0022,\n+\t\t\t\t\t__func__, result, result-\u003eai_next);\n+\t}\n+\n+#if defined(LWS_CLIENT_HTTP_PROXYING) \u0026\u0026 \u005c\n+\t(defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))\n+\n+\t/* Decide what it is we need to connect to:\n+\t *\n+\t * Priority 1: connect to http proxy */\n+\n+\tif (wsi-\u003evhost-\u003ehttp.http_proxy_port) {\n+\t\tplen \u003d lws_snprintf((char *)pt-\u003eserv_buf, 256,\n+\t\t\t\u0022CONNECT %s:%u HTTP/1.0\u005cx0d\u005cx0a\u0022\n+\t\t\t\u0022User-agent: libwebsockets\u005cx0d\u005cx0a\u0022,\n+\t\t\tads, wsi-\u003ec_port);\n+\n+\t\tif (wsi-\u003evhost-\u003eproxy_basic_auth_token[0])\n+\t\t\tplen +\u003d lws_snprintf((char *)pt-\u003eserv_buf + plen, 256,\n+\t\t\t\t\t\u0022Proxy-authorization: basic %s\u005cx0d\u005cx0a\u0022,\n+\t\t\t\t\twsi-\u003evhost-\u003eproxy_basic_auth_token);\n+\n+\t\tplen +\u003d lws_snprintf((char *)pt-\u003eserv_buf + plen, 5, \u0022\u005cx0d\u005cx0a\u0022);\n+\t\tads \u003d wsi-\u003evhost-\u003ehttp.http_proxy_address;\n+\t\twsi-\u003ec_port \u003d wsi-\u003evhost-\u003ehttp.http_proxy_port;\n+#else\n+\t\tif (0) {\n+#endif\n+\n+#if defined(LWS_WITH_SOCKS5)\n+\n+\t/* Priority 2: Connect to SOCK5 Proxy */\n+\n+\t} else if (wsi-\u003evhost-\u003esocks_proxy_port) {\n+\t\tif (socks_generate_msg(wsi, SOCKS_MSG_GREETING, \u0026plen)) {\n+\t\t\tcce \u003d \u0022socks msg too large\u0022;\n+\t\t\tgoto oom4;\n+\t\t}\n+\n+\t\tlwsl_client(\u0022Sending SOCKS Greeting\u005cn\u0022);\n+\t\tads \u003d wsi-\u003evhost-\u003esocks_proxy_address;\n+\t\twsi-\u003ec_port \u003d wsi-\u003evhost-\u003esocks_proxy_port;\n+#endif\n+\t}\n+\n+\tmemset(\u0026sa46, 0, sizeof(sa46));\n+\n+\tif (n || !wsi-\u003edns_results) {\n+\t\t/* lws_getaddrinfo46 failed, there is no usable result */\n+\t\tlwsl_notice(\u0022%s: lws_getaddrinfo46 failed %d\u005cn\u0022,\n+\t\t\t\t__func__, n);\n+\t\tcce \u003d \u0022ipv6 lws_getaddrinfo46 failed\u0022;\n+\t\tgoto oom4;\n+\t}\n+\n+\t/*\n+\t * Let's try connecting to each of the results in turn until one works\n+\t * or we run out of results\n+\t */\n+\n+next_result:\n+\n+\tpsa \u003d (const struct sockaddr *)\u0026sa46;\n+\tn \u003d sizeof(sa46);\n+\tmemset(\u0026sa46, 0, sizeof(sa46));\n+\n+\tswitch (wsi-\u003edns_results_next-\u003eai_family) {\n+\tcase AF_INET:\n+#if defined(LWS_WITH_IPV6)\n+\t\tif (ipv6only) {\n+\t\t\tsa46.sa4.sin_family \u003d AF_INET6;\n+\n+\t\t\t/* map IPv4 to IPv6 */\n+\t\t\tmemset((char *)\u0026sa46.sa6.sin6_addr, 0,\n+\t\t\t\t\t\tsizeof(sa46.sa6.sin6_addr));\n+\t\t\tsa46.sa6.sin6_addr.s6_addr[10] \u003d 0xff;\n+\t\t\tsa46.sa6.sin6_addr.s6_addr[11] \u003d 0xff;\n+\t\t\tmemcpy(\u0026sa46.sa6.sin6_addr.s6_addr[12],\n+\t\t\t\t\u0026((struct sockaddr_in *)\n+\t\t\t\t wsi-\u003edns_results_next-\u003eai_addr)-\u003esin_addr,\n+\t\t\t\t\t\t\tsizeof(struct in_addr));\n+\t\t\tsa46.sa6.sin6_port \u003d htons(wsi-\u003ec_port);\n+\t\t\tni[0] \u003d '\u005c0';\n+\t\t\tlws_write_numeric_address(sa46.sa6.sin6_addr.s6_addr,\n+\t\t\t\t\t\t 16, ni, sizeof(ni));\n+\t\t\tlwsl_info(\u0022%s: %s ipv4-\u003eipv6 %s\u005cn\u0022, __func__, ads, ni);\n+\t\t\tbreak;\n+\t\t}\n+#endif\n+\t\tsa46.sa4.sin_family \u003d AF_INET;\n+\t\tsa46.sa4.sin_addr.s_addr \u003d\n+\t\t\t((struct sockaddr_in *)wsi-\u003edns_results_next-\u003eai_addr)-\u003e\n+\t\t\t\t\t\t\t\tsin_addr.s_addr;\n+\t\tmemset(\u0026sa46.sa4.sin_zero, 0, sizeof(sa46.sa4.sin_zero));\n+\t\tsa46.sa4.sin_port \u003d htons(wsi-\u003ec_port);\n+\t\tn \u003d sizeof(struct sockaddr_in);\n+\t\tlws_write_numeric_address((uint8_t *)\u0026sa46.sa4.sin_addr.s_addr,\n+\t\t\t\t\t 4, ni, sizeof(ni));\n+\t\tlwsl_info(\u0022%s: %s ipv4 %s\u005cn\u0022, __func__, ads, ni);\n+\t\tbreak;\n+\tcase AF_INET6:\n+#if defined(LWS_WITH_IPV6)\n+\t\tif (!wsi-\u003eipv6)\n+\t\t\tgoto try_next_result;\n+\t\tsa46.sa4.sin_family \u003d AF_INET6;\n+\t\tmemcpy(\u0026sa46.sa6.sin6_addr,\n+\t\t \u0026((struct sockaddr_in6 *)wsi-\u003edns_results_next-\u003eai_addr)-\u003e\n+\t\t\t\t sin6_addr, sizeof(struct in6_addr));\n+\t\tsa46.sa6.sin6_scope_id \u003d ((struct sockaddr_in6 *)\n+\t\t\t\twsi-\u003edns_results_next-\u003eai_addr)-\u003esin6_scope_id;\n+\t\tsa46.sa6.sin6_flowinfo \u003d ((struct sockaddr_in6 *)\n+\t\t\t\twsi-\u003edns_results_next-\u003eai_addr)-\u003esin6_flowinfo;\n+\t\tsa46.sa6.sin6_port \u003d htons(wsi-\u003ec_port);\n+\t\tlws_write_numeric_address((uint8_t *)\u0026sa46.sa6.sin6_addr,\n+\t\t\t\t16, ni, sizeof(ni));\n+\t\tlwsl_info(\u0022%s: %s ipv6 %s\u005cn\u0022, __func__, ads, ni);\n+#else\n+\t\tgoto try_next_result;\t/* ipv4 only can't use this */\n+#endif\n+\t\tbreak;\n+\t}\n+\n+#if defined(LWS_WITH_UNIX_SOCK)\n+ads_known:\n+#endif\n+\n+\t/* now we decided on ipv4 or ipv6, set the port and create socket*/\n+\n+\tif (!lws_socket_is_valid(wsi-\u003edesc.sockfd)) {\n+\n+\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003echeck_client_connect_ok \u0026\u0026\n+\t\t wsi-\u003econtext-\u003eevent_loop_ops-\u003echeck_client_connect_ok(wsi)) {\n+\t\t\tcce \u003d \u0022waiting for event loop watcher to close\u0022;\n+\t\t\tgoto oom4;\n+\t\t}\n+\n+#if defined(LWS_WITH_UNIX_SOCK)\n+\t\tif (wsi-\u003eunix_skt)\n+\t\t\twsi-\u003edesc.sockfd \u003d socket(AF_UNIX, SOCK_STREAM, 0);\n+\t\telse\n+#endif\n+\t\t\twsi-\u003edesc.sockfd \u003d socket(sa46.sa4.sin_family,\n+\t\t\t\t\t\t SOCK_STREAM, 0);\n+\n+\t\tif (!lws_socket_is_valid(wsi-\u003edesc.sockfd)) {\n+\t\t\tlwsl_warn(\u0022Unable to open socket\u005cn\u0022);\n+\t\t\tgoto try_next_result;\n+\t\t}\n+\n+\t\tif (lws_plat_set_socket_options(wsi-\u003evhost, wsi-\u003edesc.sockfd,\n+#if defined(LWS_WITH_UNIX_SOCK)\n+\t\t\t\t\t\twsi-\u003eunix_skt)) {\n+#else\n+\t\t\t\t\t\t0)) {\n+#endif\n+\t\t\tlwsl_err(\u0022Failed to set wsi socket options\u005cn\u0022);\n+\t\t\tgoto try_next_result_closesock;\n+\t\t}\n+\n+\t\tlwsl_debug(\u0022%s: %p: WAITING_CONNECT\u005cn\u0022, __func__, wsi);\n+\t\tlwsi_set_state(wsi, LRS_WAITING_CONNECT);\n+\n+#if !defined(LWS_AMAZON_RTOS)\n+\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept)\n+\t\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept(wsi))\n+\t\t\t\tgoto try_next_result_closesock;\n+#endif\n+\n+\t\tif (__insert_wsi_socket_into_fds(wsi-\u003econtext, wsi))\n+\t\t\tgoto try_next_result_closesock;\n+\n+\t\tif (lws_change_pollfd(wsi, 0, LWS_POLLIN))\n+\t\t\tgoto try_next_result_fds;\n+\n+\t\t/*\n+\t\t * Past here, we can't simply free the structs as error\n+\t\t * handling as oom4 does.\n+\t\t *\n+\t\t * We can run the whole close flow, or unpick the fds inclusion\n+\t\t * and anything else we have done.\n+\t\t */\n+\t\twsi-\u003eoom4 \u003d 1;\n+\t\tif (!wsi-\u003eprotocol)\n+\t\t\twsi-\u003eprotocol \u003d \u0026wsi-\u003evhost-\u003eprotocols[0];\n+\n+\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,\n+\t\t\t\tAWAITING_TIMEOUT);\n+\n+\t\tif (wsi-\u003estash)\n+\t\t\tiface \u003d wsi-\u003estash-\u003ecis[CIS_IFACE];\n+\t\telse\n+\t\t\tiface \u003d lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);\n+\n+\t\tif (iface) {\n+\t\t\tn \u003d lws_socket_bind(wsi-\u003evhost, wsi-\u003edesc.sockfd, 0,\n+\t\t\t\t\t iface, wsi-\u003eipv6);\n+\t\t\tif (n \u003c 0)\n+\t\t\t\tgoto try_next_result_fds;\n+\t\t}\n+\t}\n+\n+#if defined(LWS_WITH_UNIX_SOCK)\n+\tif (wsi-\u003eunix_skt) {\n+\t\tpsa \u003d (const struct sockaddr *)\u0026sau;\n+\t\tn \u003d sizeof(sau);\n+\t} else\n+#endif\n+\n+\tif (!psa) /* coverity */\n+\t\tgoto try_next_result_fds;\n+\n+\t/*\n+\t * The actual connection attempt\n+\t */\n+\n+\tm \u003d connect(wsi-\u003edesc.sockfd, (const struct sockaddr *)psa, n);\n+\tif (m \u003d\u003d -1) {\n+\n+\t\tlwsl_debug(\u0022%s: connect says errno: %d\u005cn\u0022, __func__, LWS_ERRNO);\n+\n+\t\tif (LWS_ERRNO !\u003d LWS_EALREADY \u0026\u0026\n+\t\t LWS_ERRNO !\u003d LWS_EINPROGRESS \u0026\u0026\n+\t\t LWS_ERRNO !\u003d LWS_EWOULDBLOCK\n+#ifdef _WIN32\n+\t\t\t\u0026\u0026 LWS_ERRNO !\u003d WSAEINVAL\n+#endif\n+\t\t) {\n+#if defined(_DEBUG)\n+\t\t\tchar nads[48];\n+\t\t\tlws_sa46_write_numeric_address(\u0026sa46, nads, sizeof(nads));\n+\t\t\tlwsl_info(\u0022%s: Connect failed: %s port %d\u005cn\u0022,\n+\t\t\t\t __func__, nads, wsi-\u003ec_port);\n+#endif\n+\t\t\tgoto try_next_result_fds;\n+\t\t}\n+\n+\t\tif (lws_plat_check_connection_error(wsi))\n+\t\t\tgoto try_next_result_fds;\n+\n+\t\t/*\n+\t\t * must do specifically a POLLOUT poll to hear\n+\t\t * about the connect completion\n+\t\t */\n+\t\tif (lws_change_pollfd(wsi, 0, LWS_POLLOUT))\n+\t\t\tgoto try_next_result_fds;\n+\n+\t\treturn wsi;\n+\t}\n+\n+conn_good:\n+\n+\tlwsl_debug(\u0022%s: Connection started\u005cn\u0022, __func__);\n+\n+\tlws_addrinfo_clean(wsi);\n+\n+\tif (wsi-\u003eprotocol)\n+\t\twsi-\u003eprotocol-\u003ecallback(wsi, LWS_CALLBACK_WSI_CREATE,\n+\t\t\t\t\twsi-\u003euser_space, NULL, 0);\n+\n+\treturn lws_client_connect_4_established(wsi, NULL, plen);\n+\n+\n+oom4:\n+\tif (lwsi_role_client(wsi) \u0026\u0026 wsi-\u003eprotocol /* \u0026\u0026 lwsi_state_est(wsi) */)\n+\t\tlws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce));\n+\n+\t/* take care that we might be inserted in fds already */\n+\tif (wsi-\u003eposition_in_fds_table !\u003d LWS_NO_FDS_POS)\n+\t\tgoto failed1;\n+\n+\t/*\n+\t * We can't be an active client connection any more, if we thought\n+\t * that was what we were going to be doing. It should be if we are\n+\t * failing by oom4 path, we are still called by\n+\t * lws_client_connect_via_info() and will be returning NULL to that,\n+\t * so nobody else should have had a chance to queue on us.\n+\t */\n+\t{\n+\t\tstruct lws_vhost *vhost \u003d wsi-\u003evhost;\n+\n+\t\tlws_vhost_lock(vhost);\n+\t\t__lws_free_wsi(wsi);\n+\t\tlws_vhost_unlock(vhost);\n+\t}\n+\n+\treturn NULL;\n+\n+try_next_result_fds:\n+\twsi-\u003eoom4 \u003d 0;\n+\t__remove_wsi_socket_from_fds(wsi);\n+\n+try_next_result_closesock:\n+\tcompatible_close(wsi-\u003edesc.sockfd);\n+\twsi-\u003edesc.sockfd \u003d LWS_SOCK_INVALID;\n+\n+try_next_result:\n+\tif (wsi-\u003edns_results_next) {\n+\t\twsi-\u003edns_results_next \u003d wsi-\u003edns_results_next-\u003eai_next;\n+\t\tif (wsi-\u003edns_results_next)\n+\t\t\tgoto next_result;\n+\t}\n+\tcce \u003d \u0022Unable to connect\u0022;\n+\n+//failed:\n+\tlws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));\n+\n+failed1:\n+\tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, \u0022client_connect2\u0022);\n+\n+\treturn NULL;\n+}\n+\n+struct lws *\n+lws_client_connect_2_dnsreq(struct lws *wsi)\n+{\n+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n+\tconst char *adsin;\n+#endif\n+\tconst char *cce \u003d \u0022\u0022, *meth \u003d NULL, *ads;\n+\tstruct addrinfo *result \u003d NULL;\n+#if defined(LWS_WITH_IPV6)\n+\tstruct sockaddr_in addr;\n+\tconst char *iface;\n+#endif\n+\tint n, port \u003d 0;\n+\n #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n \tif (!wsi-\u003ehttp.ah \u0026\u0026 !wsi-\u003estash) {\n \t\tcce \u003d \u0022ah was NULL at cc2\u0022;\n@@ -291,8 +693,8 @@ lws_client_connect_2(struct lws *wsi)\n \n \tlws_vhost_lock(wsi-\u003evhost); /* ----------------------------------- { */\n \n-\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n-\t\t\tlws_dll2_get_head(\u0026wsi-\u003evhost-\u003edll_cli_active_conns_owner)) {\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, lws_dll2_get_head(\n+\t\t\t\t\u0026wsi-\u003evhost-\u003edll_cli_active_conns_owner)) {\n \t\tstruct lws *w \u003d lws_container_of(d, struct lws,\n \t\t\t\t\t\t dll_cli_active_conns);\n \n@@ -351,7 +753,7 @@ lws_client_connect_2(struct lws *wsi)\n \t\t\t * to take over parsing the rx.\n \t\t\t */\n \t\t\tlws_vhost_unlock(wsi-\u003evhost); /* } ---------- */\n-\t\t\treturn lws_client_connect_3(wsi, w, plen);\n+\t\t\treturn lws_client_connect_4_established(wsi, w, 0);\n \t\t}\n \n \t} lws_end_foreach_dll_safe(d, d1);\n@@ -407,19 +809,9 @@ create_new_conn:\n \t\tads \u003d lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);\n #if defined(LWS_WITH_UNIX_SOCK)\n \tif (*ads \u003d\u003d '+') {\n-\t\tads++;\n-\t\tmemset(\u0026sau, 0, sizeof(sau));\n-\t\tsau.sun_family \u003d AF_UNIX;\n-\t\tstrncpy(sau.sun_path, ads, sizeof(sau.sun_path));\n-\t\tsau.sun_path[sizeof(sau.sun_path) - 1] \u003d '\u005c0';\n-\n-\t\tlwsl_info(\u0022%s: Unix skt: %s\u005cn\u0022, __func__, ads);\n-\n-\t\tif (sau.sun_path[0] \u003d\u003d '@')\n-\t\t\tsau.sun_path[0] \u003d '\u005c0';\n-\n-\t\tunix_skt \u003d 1;\n-\t\tgoto ads_known;\n+\t\twsi-\u003eunix_skt \u003d 1;\n+\t\tn \u003d 0;\n+\t\tgoto next_step;\n \t}\n #endif\n \n@@ -448,17 +840,6 @@ create_new_conn:\n \t * Priority 1: connect to http proxy */\n \n \tif (wsi-\u003evhost-\u003ehttp.http_proxy_port) {\n-\t\tplen \u003d lws_snprintf((char *)pt-\u003eserv_buf, 256,\n-\t\t\t\u0022CONNECT %s:%u HTTP/1.0\u005cx0d\u005cx0a\u0022\n-\t\t\t\u0022User-agent: libwebsockets\u005cx0d\u005cx0a\u0022,\n-\t\t\tads, wsi-\u003ec_port);\n-\n-\t\tif (wsi-\u003evhost-\u003eproxy_basic_auth_token[0])\n-\t\t\tplen +\u003d lws_snprintf((char *)pt-\u003eserv_buf + plen, 256,\n-\t\t\t\t\t\u0022Proxy-authorization: basic %s\u005cx0d\u005cx0a\u0022,\n-\t\t\t\t\twsi-\u003evhost-\u003eproxy_basic_auth_token);\n-\n-\t\tplen +\u003d lws_snprintf((char *)pt-\u003eserv_buf + plen, 5, \u0022\u005cx0d\u005cx0a\u0022);\n \t\tads \u003d wsi-\u003evhost-\u003ehttp.http_proxy_address;\n \t\tport \u003d wsi-\u003evhost-\u003ehttp.http_proxy_port;\n #else\n@@ -470,11 +851,6 @@ create_new_conn:\n \t/* Priority 2: Connect to SOCK5 Proxy */\n \n \t} else if (wsi-\u003evhost-\u003esocks_proxy_port) {\n-\t\tif (socks_generate_msg(wsi, SOCKS_MSG_GREETING, \u0026plen)) {\n-\t\t\tcce \u003d \u0022socks msg too large\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n \t\tlwsl_client(\u0022Sending SOCKS Greeting\u005cn\u0022);\n \t\tads \u003d wsi-\u003evhost-\u003esocks_proxy_address;\n \t\tport \u003d wsi-\u003evhost-\u003esocks_proxy_port;\n@@ -491,271 +867,31 @@ create_new_conn:\n \t * prepare the actual connection\n \t * to whatever we decided to connect to\n \t */\n+\tlwsi_set_state(wsi, LRS_WAITING_DNS);\n \n-\tlwsl_info(\u0022%s: %p: address %s:%u\u005cn\u0022, __func__, wsi, ads, port);\n+\tlwsl_warn(\u0022%s: %p: lookup %s:%u\u005cn\u0022, __func__, wsi, ads, port);\n+\t(void)port;\n \n+#if !defined(LWS_WITH_SYS_ASYNC_DNS)\n \tn \u003d lws_getaddrinfo46(wsi, ads, \u0026result);\n-\tmemset(\u0026sa46, 0, sizeof(sa46));\n-#ifdef LWS_WITH_IPV6\n-\tif (wsi-\u003eipv6) {\n-\t\tstruct sockaddr_in6 *sa6;\n-\n-\t\tif (n || !result) {\n-\t\t\t/* lws_getaddrinfo46 failed, there is no usable result */\n-\t\t\tlwsl_notice(\u0022%s: lws_getaddrinfo46 failed %d\u005cn\u0022,\n-\t\t\t\t\t__func__, n);\n-\t\t\tcce \u003d \u0022ipv6 lws_getaddrinfo46 failed\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tsa6 \u003d ((struct sockaddr_in6 *)result-\u003eai_addr);\n-\t\tsa46.sa6.sin6_family \u003d AF_INET6;\n-\t\tswitch (result-\u003eai_family) {\n-\t\tcase AF_INET:\n-\t\t\tif (ipv6only)\n-\t\t\t\tbreak;\n-\t\t\t/* map IPv4 to IPv6 */\n-\t\t\tmemset((char *)\u0026sa46.sa6.sin6_addr, 0,\n-\t\t\t\t\t\tsizeof(sa46.sa6.sin6_addr));\n-\t\t\tsa46.sa6.sin6_addr.s6_addr[10] \u003d 0xff;\n-\t\t\tsa46.sa6.sin6_addr.s6_addr[11] \u003d 0xff;\n-\t\t\tmemcpy(\u0026sa46.sa6.sin6_addr.s6_addr[12],\n-\t\t\t\t\u0026((struct sockaddr_in *)result-\u003eai_addr)-\u003esin_addr,\n-\t\t\t\t\t\t\tsizeof(struct in_addr));\n-\t\t\tlwsl_notice(\u0022uplevelling AF_INET to AF_INET6\u005cn\u0022);\n-\t\t\tbreak;\n-\n-\t\tcase AF_INET6:\n-\t\t\tmemcpy(\u0026sa46.sa6.sin6_addr, \u0026sa6-\u003esin6_addr,\n-\t\t\t\t\t\tsizeof(struct in6_addr));\n-\t\t\tsa46.sa6.sin6_scope_id \u003d sa6-\u003esin6_scope_id;\n-\t\t\tsa46.sa6.sin6_flowinfo \u003d sa6-\u003esin6_flowinfo;\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tlwsl_err(\u0022Unknown address family\u005cn\u0022);\n-\t\t\tfreeaddrinfo(result);\n-\t\t\tcce \u003d \u0022unknown address family\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\t} else\n-#endif /* use ipv6 */\n-\n-\t/* use ipv4 */\n-\t{\n-\t\tvoid *p \u003d NULL;\n-\n-\t\tif (!n) {\n-\t\t\tstruct addrinfo *res \u003d result;\n-\n-\t\t\t/* pick the first AF_INET (IPv4) result */\n-\n-\t\t\twhile (!p \u0026\u0026 res) {\n-\t\t\t\tswitch (res-\u003eai_family) {\n-\t\t\t\tcase AF_INET:\n-\t\t\t\t\tp \u003d \u0026((struct sockaddr_in *)res-\u003eai_addr)-\u003esin_addr;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\n-\t\t\t\tres \u003d res-\u003eai_next;\n-\t\t\t}\n-#if defined(LWS_FALLBACK_GETHOSTBYNAME)\n-\t\t} else if (n \u003d\u003d EAI_SYSTEM) {\n-\t\t\tstruct hostent *host;\n-\n-\t\t\tlwsl_info(\u0022ipv4 getaddrinfo err, try gethostbyname\u005cn\u0022);\n-\t\t\thost \u003d gethostbyname(ads);\n-\t\t\tif (host) {\n-\t\t\t\tp \u003d host-\u003eh_addr;\n-\t\t\t} else {\n-\t\t\t\tlwsl_err(\u0022gethostbyname failed\u005cn\u0022);\n-\t\t\t\tcce \u003d \u0022gethostbyname (ipv4) failed\u0022;\n-\t\t\t\tgoto oom4;\n-\t\t\t}\n-#endif\n-\t\t} else {\n-\t\t\tlwsl_err(\u0022getaddrinfo failed: %s: %d\u005cn\u0022, ads, n);\n-\t\t\tcce \u003d \u0022getaddrinfo failed\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tif (!p) {\n-\t\t\tif (result)\n-\t\t\t\tfreeaddrinfo(result);\n-\t\t\tlwsl_err(\u0022Couldn't identify address\u005cn\u0022);\n-\t\t\tcce \u003d \u0022unable to lookup address\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tsa46.sa4.sin_family \u003d AF_INET;\n-\t\tsa46.sa4.sin_addr \u003d *((struct in_addr *)p);\n-\t\tmemset(\u0026sa46.sa4.sin_zero, 0, sizeof(sa46.sa4.sin_zero));\n-\t}\n-\n-\tif (result)\n-\t\tfreeaddrinfo(result);\n-\n-#if defined(LWS_WITH_UNIX_SOCK)\n-ads_known:\n-#endif\n-\n-\t/* now we decided on ipv4 or ipv6, set the port */\n-\n-\tif (!lws_socket_is_valid(wsi-\u003edesc.sockfd)) {\n-\n-\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003echeck_client_connect_ok \u0026\u0026\n-\t\t wsi-\u003econtext-\u003eevent_loop_ops-\u003echeck_client_connect_ok(wsi)) {\n-\t\t\tcce \u003d \u0022waiting for event loop watcher to close\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-#if defined(LWS_WITH_UNIX_SOCK)\n-\t\tif (unix_skt) {\n-\t\t\twsi-\u003eunix_skt \u003d 1;\n-\t\t\twsi-\u003edesc.sockfd \u003d socket(AF_UNIX, SOCK_STREAM, 0);\n-\t\t} else\n-#endif\n-\t\t{\n-\n-#ifdef LWS_WITH_IPV6\n-\t\tif (wsi-\u003eipv6)\n-\t\t\twsi-\u003edesc.sockfd \u003d socket(AF_INET6, SOCK_STREAM, 0);\n-\t\telse\n-#endif\n-\t\t\twsi-\u003edesc.sockfd \u003d socket(AF_INET, SOCK_STREAM, 0);\n-\t\t}\n-\n-\t\tif (!lws_socket_is_valid(wsi-\u003edesc.sockfd)) {\n-\t\t\tlwsl_warn(\u0022Unable to open socket\u005cn\u0022);\n-\t\t\tcce \u003d \u0022unable to open socket\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tif (lws_plat_set_socket_options(wsi-\u003evhost, wsi-\u003edesc.sockfd,\n-#if defined(LWS_WITH_UNIX_SOCK)\n-\t\t\t\t\t\tunix_skt)) {\n #else\n-\t\t\t\t\t\t0)) {\n-#endif\n-\t\t\tlwsl_err(\u0022Failed to set wsi socket options\u005cn\u0022);\n-\t\t\tcompatible_close(wsi-\u003edesc.sockfd);\n-\t\t\tcce \u003d \u0022set socket opts failed\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tlwsi_set_state(wsi, LRS_WAITING_CONNECT);\n-\n-#if !defined(LWS_AMAZON_RTOS)\n-\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept)\n-\t\t\tif (wsi-\u003econtext-\u003eevent_loop_ops-\u003eaccept(wsi)) {\n-\t\t\t\tcompatible_close(wsi-\u003edesc.sockfd);\n-\t\t\t\tcce \u003d \u0022event loop accept failed\u0022;\n-\t\t\t\tgoto oom4;\n-\t\t\t}\n-#endif\n-\n-\t\tif (__insert_wsi_socket_into_fds(wsi-\u003econtext, wsi)) {\n-\t\t\tcompatible_close(wsi-\u003edesc.sockfd);\n-\t\t\tcce \u003d \u0022insert wsi failed\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\tif (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {\n-\t\t\tcompatible_close(wsi-\u003edesc.sockfd);\n-\t\t\tcce \u003d \u0022change_pollfd failed\u0022;\n-\t\t\tgoto oom4;\n-\t\t}\n-\n-\t\t/*\n-\t\t * past here, we can't simply free the structs as error\n-\t\t * handling as oom4 does. We have to run the whole close flow.\n-\t\t */\n-\n-\t\tif (!wsi-\u003eprotocol)\n-\t\t\twsi-\u003eprotocol \u003d \u0026wsi-\u003evhost-\u003eprotocols[0];\n-\n-\t\twsi-\u003eprotocol-\u003ecallback(wsi, LWS_CALLBACK_WSI_CREATE,\n-\t\t\t\t\twsi-\u003euser_space, NULL, 0);\n-\n-\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,\n-\t\t\t\tAWAITING_TIMEOUT);\n+\t/* this is either FAILED, CONTINUING, or already called connect_4 */\n \n-\t\tif (wsi-\u003estash)\n-\t\t\tiface \u003d wsi-\u003estash-\u003ecis[CIS_IFACE];\n-\t\telse\n-\t\t\tiface \u003d lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);\n-\n-\t\tif (iface) {\n-\t\t\tn \u003d lws_socket_bind(wsi-\u003evhost, wsi-\u003edesc.sockfd, 0,\n-\t\t\t\t\t iface, wsi-\u003eipv6);\n-\t\t\tif (n \u003c 0) {\n-\t\t\t\tcce \u003d \u0022unable to bind socket\u0022;\n-\t\t\t\tgoto failed;\n-\t\t\t}\n-\t\t}\n-\t}\n+\tn \u003d lws_async_dns_query(wsi-\u003econtext, wsi-\u003etsi, ads, LWS_ADNS_RECORD_A,\n+\t\t\t\tlws_client_connect_3_connect, wsi, NULL);\n+\tif (n \u003d\u003d LADNS_RET_FAILED_WSI_CLOSED)\n+\t\treturn NULL;\n \n-#if defined(LWS_WITH_UNIX_SOCK)\n-\tif (unix_skt) {\n-\t\tpsa \u003d (const struct sockaddr *)\u0026sau;\n-\t\tn \u003d sizeof(sau);\n-\t} else\n-#endif\n+\tif (n \u003d\u003d LADNS_RET_FAILED)\n+\t\tgoto failed1;\n \n-\t{\n-#ifdef LWS_WITH_IPV6\n-\t\tif (wsi-\u003eipv6) {\n-\t\t\tsa46.sa6.sin6_port \u003d htons(port);\n-\t\t\tn \u003d sizeof(struct sockaddr_in6);\n-\t\t\tpsa \u003d (const struct sockaddr *)\u0026sa46;\n-\t\t} else\n+\treturn wsi;\n #endif\n-\t\t{\n-\t\t\tsa46.sa4.sin_port \u003d htons(port);\n-\t\t\tn \u003d sizeof(struct sockaddr);\n-\t\t\tpsa \u003d (const struct sockaddr *)\u0026sa46;\n-\t\t}\n-\t}\n \n-\tif (connect(wsi-\u003edesc.sockfd, (const struct sockaddr *)psa, n) \u003d\u003d -1 ||\n-\t LWS_ERRNO \u003d\u003d LWS_EISCONN) {\n-\t\tif (LWS_ERRNO \u003d\u003d LWS_EALREADY ||\n-\t\t LWS_ERRNO \u003d\u003d LWS_EINPROGRESS ||\n-\t\t LWS_ERRNO \u003d\u003d LWS_EWOULDBLOCK\n-#ifdef _WIN32\n-\t\t\t|| LWS_ERRNO \u003d\u003d WSAEINVAL\n+#if defined(LWS_WITH_UNIX_SOCK)\n+next_step:\n #endif\n-\t\t) {\n-\t\t\tlwsl_client(\u0022nonblocking connect retry (errno \u003d %d)\u005cn\u0022,\n-\t\t\t\t LWS_ERRNO);\n-\n-\t\t\tif (lws_plat_check_connection_error(wsi)) {\n-\t\t\t\tcce \u003d \u0022socket connect failed\u0022;\n-\t\t\t\tgoto failed;\n-\t\t\t}\n-\n-\t\t\t/*\n-\t\t\t * must do specifically a POLLOUT poll to hear\n-\t\t\t * about the connect completion\n-\t\t\t */\n-\t\t\tif (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {\n-\t\t\t\tcce \u003d \u0022POLLOUT set failed\u0022;\n-\t\t\t\tgoto failed;\n-\t\t\t}\n-\n-\t\t\treturn wsi;\n-\t\t}\n-\n-\t\tif (LWS_ERRNO !\u003d LWS_EISCONN) {\n-\t\t\tlwsl_notice(\u0022Connect failed errno\u003d%d\u005cn\u0022, LWS_ERRNO);\n-\t\t\tcce \u003d \u0022connect failed\u0022;\n-\t\t\tgoto failed;\n-\t\t}\n-\t}\n-\n-\n-\n-\treturn lws_client_connect_3(wsi, NULL, plen);\n-\n+\treturn lws_client_connect_3_connect(wsi, ads, result, n, NULL);\n \n oom4:\n \tif (lwsi_role_client(wsi) \u0026\u0026 wsi-\u003eprotocol /* \u0026\u0026 lwsi_state_est(wsi) */)\n@@ -782,16 +918,12 @@ oom4:\n \n \treturn NULL;\n \n-failed:\n-\tlws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));\n-\n failed1:\n \tlws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, \u0022client_connect2\u0022);\n \n \treturn NULL;\n }\n \n-\n #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n \n static uint8_t hnames2[] \u003d {\n@@ -909,7 +1041,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,\n \n \tlws_free_set_NULL(stash);\n \n-\t*pwsi \u003d lws_client_connect_2(wsi);\n+\t*pwsi \u003d lws_client_connect_2_dnsreq(wsi);\n \n \treturn *pwsi;\n \n@@ -1095,7 +1227,7 @@ lws_http_client_connect_via_info2(struct lws *wsi)\n no_ah:\n \twsi-\u003econtext-\u003ecount_wsi_allocated++;\n \n-\treturn lws_client_connect_2(wsi);\n+\treturn lws_client_connect_2_dnsreq(wsi);\n \n bail1:\n #if defined(LWS_WITH_SOCKS5)\ndiff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c\nindex 39c5c39..1a4b66b 100644\n--- a/lib/roles/http/client/client-http.c\n+++ b/lib/roles/http/client/client-http.c\n@@ -137,14 +137,14 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,\n \n \tswitch (lwsi_state(wsi)) {\n \n-\tcase LRS_WAITING_CONNECT:\n+\tcase LRS_WAITING_DNS:\n \n \t\t/*\n \t\t * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE\n \t\t * timeout protection set in client-handshake.c\n \t\t */\n-\n-\t\tif (!lws_client_connect_2(wsi)) {\n+\t\tlwsl_err(\u0022%s: wsi %p: WAITING_DNS\u005cn\u0022, __func__, wsi);\n+\t\tif (!lws_client_connect_2_dnsreq(wsi)) {\n \t\t\t/* closed */\n \t\t\tlwsl_client(\u0022closed\u005cn\u0022);\n \t\t\treturn -1;\n@@ -152,6 +152,15 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,\n \n \t\t/* either still pending connection, or changed mode */\n \t\treturn 0;\n+\tcase LRS_WAITING_CONNECT:\n+\n+\t\t/*\n+\t\t * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE\n+\t\t * timeout protection set in client-handshake.c\n+\t\t */\n+\t\tif (pollfd-\u003erevents \u0026 LWS_POLLOUT)\n+\t\t\tlws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);\n+\t\tbreak;\n \n #if defined(LWS_WITH_SOCKS5)\n \t/* SOCKS Greeting Reply */\ndiff --git a/lib/roles/private-lib-roles.h b/lib/roles/private-lib-roles.h\nindex 898b9a0..d4664d3 100644\n--- a/lib/roles/private-lib-roles.h\n+++ b/lib/roles/private-lib-roles.h\n@@ -87,43 +87,44 @@ enum lwsi_state {\n \t/* Phase 1: pre-transport */\n \n \tLRS_UNCONNECTED\t\t\t\t\u003d LWSIFS_NOT_EST | 0,\n-\tLRS_WAITING_CONNECT\t\t\t\u003d LWSIFS_NOT_EST | 1,\n+\tLRS_WAITING_DNS\t\t\t\t\u003d LWSIFS_NOT_EST | 1,\n+\tLRS_WAITING_CONNECT\t\t\t\u003d LWSIFS_NOT_EST | 2,\n \n \t/* Phase 2: establishing intermediaries on top of transport */\n \n-\tLRS_WAITING_PROXY_REPLY\t\t\t\u003d LWSIFS_NOT_EST | 2,\n-\tLRS_WAITING_SSL\t\t\t\t\u003d LWSIFS_NOT_EST | 3,\n-\tLRS_WAITING_SOCKS_GREETING_REPLY\t\u003d LWSIFS_NOT_EST | 4,\n-\tLRS_WAITING_SOCKS_CONNECT_REPLY\t\t\u003d LWSIFS_NOT_EST | 5,\n-\tLRS_WAITING_SOCKS_AUTH_REPLY\t\t\u003d LWSIFS_NOT_EST | 6,\n+\tLRS_WAITING_PROXY_REPLY\t\t\t\u003d LWSIFS_NOT_EST | 3,\n+\tLRS_WAITING_SSL\t\t\t\t\u003d LWSIFS_NOT_EST | 4,\n+\tLRS_WAITING_SOCKS_GREETING_REPLY\t\u003d LWSIFS_NOT_EST | 5,\n+\tLRS_WAITING_SOCKS_CONNECT_REPLY\t\t\u003d LWSIFS_NOT_EST | 6,\n+\tLRS_WAITING_SOCKS_AUTH_REPLY\t\t\u003d LWSIFS_NOT_EST | 7,\n \n \t/* Phase 3: establishing tls tunnel */\n \n-\tLRS_SSL_INIT\t\t\t\t\u003d LWSIFS_NOT_EST | 7,\n-\tLRS_SSL_ACK_PENDING\t\t\t\u003d LWSIFS_NOT_EST | 8,\n-\tLRS_PRE_WS_SERVING_ACCEPT\t\t\u003d LWSIFS_NOT_EST | 9,\n+\tLRS_SSL_INIT\t\t\t\t\u003d LWSIFS_NOT_EST | 8,\n+\tLRS_SSL_ACK_PENDING\t\t\t\u003d LWSIFS_NOT_EST | 9,\n+\tLRS_PRE_WS_SERVING_ACCEPT\t\t\u003d LWSIFS_NOT_EST | 10,\n \n \t/* Phase 4: connected */\n \n-\tLRS_WAITING_SERVER_REPLY\t\t\u003d LWSIFS_NOT_EST | 10,\n-\tLRS_H2_AWAIT_PREFACE\t\t\t\u003d LWSIFS_NOT_EST | 11,\n+\tLRS_WAITING_SERVER_REPLY\t\t\u003d LWSIFS_NOT_EST | 11,\n+\tLRS_H2_AWAIT_PREFACE\t\t\t\u003d LWSIFS_NOT_EST | 12,\n \tLRS_H2_AWAIT_SETTINGS\t\t\t\u003d LWSIFS_NOT_EST |\n-\t\t\t\t\t\t LWSIFS_POCB | 12,\n+\t\t\t\t\t\t LWSIFS_POCB | 13,\n \n \t/* Phase 5: protocol logically established */\n \n-\tLRS_H2_CLIENT_SEND_SETTINGS\t\t\u003d LWSIFS_POCB | 13,\n-\tLRS_H2_WAITING_TO_SEND_HEADERS\t\t\u003d LWSIFS_POCB | 14,\n-\tLRS_DEFERRING_ACTION\t\t\t\u003d LWSIFS_POCB | 15,\n-\tLRS_IDLING\t\t\t\t\u003d 16,\n-\tLRS_H1C_ISSUE_HANDSHAKE\t\t\t\u003d 17,\n-\tLRS_H1C_ISSUE_HANDSHAKE2\t\t\u003d 18,\n-\tLRS_ISSUE_HTTP_BODY\t\t\t\u003d 19,\n-\tLRS_ISSUING_FILE\t\t\t\u003d 20,\n-\tLRS_HEADERS\t\t\t\t\u003d 21,\n-\tLRS_BODY\t\t\t\t\u003d 22,\n-\tLRS_DISCARD_BODY\t\t\t\u003d 31,\n-\tLRS_ESTABLISHED\t\t\t\t\u003d LWSIFS_POCB | 23,\n+\tLRS_H2_CLIENT_SEND_SETTINGS\t\t\u003d LWSIFS_POCB | 14,\n+\tLRS_H2_WAITING_TO_SEND_HEADERS\t\t\u003d LWSIFS_POCB | 15,\n+\tLRS_DEFERRING_ACTION\t\t\t\u003d LWSIFS_POCB | 16,\n+\tLRS_IDLING\t\t\t\t\u003d 17,\n+\tLRS_H1C_ISSUE_HANDSHAKE\t\t\t\u003d 18,\n+\tLRS_H1C_ISSUE_HANDSHAKE2\t\t\u003d 19,\n+\tLRS_ISSUE_HTTP_BODY\t\t\t\u003d 20,\n+\tLRS_ISSUING_FILE\t\t\t\u003d 21,\n+\tLRS_HEADERS\t\t\t\t\u003d 22,\n+\tLRS_BODY\t\t\t\t\u003d 23,\n+\tLRS_DISCARD_BODY\t\t\t\u003d 24,\n+\tLRS_ESTABLISHED\t\t\t\t\u003d LWSIFS_POCB | 25,\n \t/* we are established, but we have embarked on serving a single\n \t * transaction. Other transaction input may be pending, but we will\n \t * not service it while we are busy dealing with the current\n@@ -132,19 +133,19 @@ enum lwsi_state {\n \t * When we complete the current transaction, we would reset our state\n \t * back to ESTABLISHED and start to process the next transaction.\n \t */\n-\tLRS_DOING_TRANSACTION\t\t\t\u003d LWSIFS_POCB | 24,\n+\tLRS_DOING_TRANSACTION\t\t\t\u003d LWSIFS_POCB | 26,\n \n \t/* Phase 6: finishing */\n \n-\tLRS_WAITING_TO_SEND_CLOSE\t\t\u003d LWSIFS_POCB | 25,\n-\tLRS_RETURNED_CLOSE\t\t\t\u003d LWSIFS_POCB | 26,\n-\tLRS_AWAITING_CLOSE_ACK\t\t\t\u003d LWSIFS_POCB | 27,\n-\tLRS_FLUSHING_BEFORE_CLOSE\t\t\u003d LWSIFS_POCB | 28,\n-\tLRS_SHUTDOWN\t\t\t\t\u003d 29,\n+\tLRS_WAITING_TO_SEND_CLOSE\t\t\u003d LWSIFS_POCB | 27,\n+\tLRS_RETURNED_CLOSE\t\t\t\u003d LWSIFS_POCB | 28,\n+\tLRS_AWAITING_CLOSE_ACK\t\t\t\u003d LWSIFS_POCB | 29,\n+\tLRS_FLUSHING_BEFORE_CLOSE\t\t\u003d LWSIFS_POCB | 30,\n+\tLRS_SHUTDOWN\t\t\t\t\u003d 31,\n \n \t/* Phase 7: dead */\n \n-\tLRS_DEAD_SOCKET\t\t\t\t\u003d 30,\n+\tLRS_DEAD_SOCKET\t\t\t\t\u003d 32,\n \n \tLRS_MASK\t\t\t\t\u003d 0xffff\n };\n@@ -333,4 +334,8 @@ int\n lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);\n \n struct lws *\n-lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen);\n+lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen);\n+\n+struct lws *\n+lws_client_connect_3_connect(struct lws *wsi, const char *ads,\n+\t\t\tconst struct addrinfo *result, int n, void *opaque);\ndiff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c\nindex 86a5684..6907756 100644\n--- a/lib/roles/raw-skt/ops-raw-skt.c\n+++ b/lib/roles/raw-skt/ops-raw-skt.c\n@@ -116,7 +116,7 @@ try_pollout:\n \n #if defined(LWS_WITH_CLIENT)\n \tif (lwsi_state(wsi) \u003d\u003d LRS_WAITING_CONNECT)\n-\t\tlws_client_connect_3(wsi, NULL, 0);\n+\t\tlws_client_connect_4_established(wsi, NULL, 0);\n #endif\n \n \t/* one shot */\ndiff --git a/lib/system/async-dns/async-dns-parse.c b/lib/system/async-dns/async-dns-parse.c\nnew file mode 100644\nindex 0000000..9f0af03\n--- /dev/null\n+++ b/lib/system/async-dns/async-dns-parse.c\n@@ -0,0 +1,625 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+#include \u0022private-lib-async-dns.h\u0022\n+\n+\n+/* updates *dest, returns chars used from ls directly, else -1 for fail */\n+\n+static int\n+lws_adns_parse_label(const uint8_t *pkt, int len, const uint8_t *ls, int budget,\n+\t\t char **dest, int dl)\n+{\n+\tconst uint8_t *e \u003d pkt + len, *ols \u003d ls;\n+\tchar pointer \u003d 0, first \u003d 1;\n+\tuint8_t ll;\n+\tint n;\n+\n+\tif (budget \u003c 1)\n+\t\treturn 0;\n+\n+\t/* caller must catch end of labels */\n+\tassert(*ls);\n+\n+again1:\n+\tif (ls \u003e\u003d e)\n+\t\treturn -1;\n+\n+\tif (((*ls) \u0026 0xc0) \u003d\u003d 0xc0) {\n+\t\tif (budget \u003c 2)\n+\t\t\treturn -1;\n+\t\t/* pointer into message pkt to name to actually use */\n+\t\tn \u003d lws_ser_ru16be(ls) \u0026 0x3fff;\n+\t\tif (n \u003e\u003d len) {\n+\t\t\tlwsl_notice(\u0022%s: illegal name pointer\u005cn\u0022, __func__);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* dereference the label pointer */\n+\t\tls \u003d pkt + n;\n+\n+\t\t/* are we being fuzzed or messed with? */\n+\t\tif (((*ls) \u0026 0xc0) \u003d\u003d 0xc0) {\n+\t\t\t/* ... pointer to pointer is unreasonable */\n+\t\t\tlwsl_notice(\u0022%s: label ptr to ptr invalid\u005cn\u0022, __func__);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\t\tpointer \u003d 1;\n+\t}\n+\n+again:\n+\tif (ls \u003e\u003d e)\n+\t\treturn -1;\n+\tll \u003d *ls++;\n+\tif (ls + ll + 4 \u003e e || ll \u003e budget) {\n+\t\tlwsl_notice(\u0022%s: label len invalid\u005cn\u0022, __func__);\n+\n+\t\treturn -1;\n+\t}\n+\n+\tif (ll + 2 \u003e dl) {\n+\t\tlwsl_notice(\u0022%s: qname too large\u005cn\u0022, __func__);\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/* copy the label content into place */\n+\n+\tmemcpy(*dest, ls, ll);\n+\t(*dest)[ll] \u003d '.';\n+\t(*dest)[ll + 1] \u003d '\u005c0';\n+\t*dest +\u003d ll + 1;\n+\tls +\u003d ll;\n+\n+\tif (pointer) {\n+\t\tif (*ls)\n+\t\t\tgoto again;\n+\n+\t\t/*\n+\t\t * special fun rule... if whole qname was a pointer label,\n+\t\t * it has no 00 terminator afterwards\n+\t\t */\n+\t\tif (first)\n+\t\t\treturn 2; /* we just took the 16-bit pointer */\n+\n+\t\treturn 3;\n+\t}\n+\n+\tfirst \u003d 0;\n+\n+\tif (*ls)\n+\t\tgoto again1;\n+\n+\tls++;\n+\n+\treturn ls - ols;\n+}\n+\n+typedef int (*lws_async_dns_find_t)(const char *name, void *opaque,\n+\t\t\t\t uint32_t ttl, adns_query_type_t type,\n+\t\t\t\t const uint8_t *payload);\n+\n+/* locally query the response packet */\n+\n+struct label_stack {\n+\tchar name[64];\n+\tint enl;\n+\tconst uint8_t *p;\n+};\n+\n+/*\n+ * Walk the response packet, calling back to the user-provided callback for each\n+ * A (and AAAA if LWS_IPV6\u003d1) record with a matching name found in there.\n+ *\n+ * Able to recurse using an explicit non-CPU stack to resolve CNAME usages\n+ *\n+ * Return -1: unexpectedly failed\n+ * 0: found\n+ * 1: didn't find anything matching\n+ */\n+\n+static int\n+lws_adns_iterate(const uint8_t *pkt, int len, const char *expname,\n+\t\t lws_async_dns_find_t cb, void *opaque)\n+{\n+\tconst uint8_t *e \u003d pkt + len, *p, *pay;\n+\tstruct label_stack stack[4];\n+\tuint16_t rrtype, rrpaylen;\n+\tint n \u003d 0, stp \u003d 0, ansc;\n+\tchar *sp, inq;\n+\tuint32_t ttl;\n+\n+\tlws_strncpy(stack[stp].name, expname, sizeof(stack[stp].name));\n+\tstack[stp].enl \u003d strlen(expname);\n+\n+start:\n+\tansc \u003d lws_ser_ru16be(pkt + DHO_NANSWERS);\n+\tp \u003d pkt + DHO_SIZEOF;\n+\tinq \u003d 1;\n+\n+\t/*\n+\t * The response also includes the query... and we have to parse it\n+\t * so we can understand we reached the response... there's a QNAME\n+\t * made up of labels and then 2 x 16-bit fields, for query type and\n+\t * query class\n+\t */\n+\n+resume:\n+\twhile (p + 14 \u003c e \u0026\u0026 (inq || ansc)) {\n+\n+\t\tif (!inq \u0026\u0026 !stp)\n+\t\t\tansc--;\n+\n+\t\t/*\n+\t\t * First is the name the query applies to... two main\n+\t\t * formats can appear here, one is a pointer to\n+\t\t * elsewhere in the message, the other separately\n+\t\t * provides len / data for each dotted \u0022label\u0022, so for\n+\t\t * \u0022warmcat.com\u0022 warmcat and com are given each with a\n+\t\t * prepended length byte. Any of those may be a pointer\n+\t\t * to somewhere else in the packet :-/\n+\t\t *\n+\t\t * Paranoia is appropriate since the name length must be\n+\t\t * parsed out before the rest of the RR can be used and\n+\t\t * we can be attacked with absolutely any crafted\n+\t\t * content easily via UDP.\n+\t\t *\n+\t\t * So parse the name and additionally confirm it matches\n+\t\t * what the query the TID belongs to actually asked for.\n+\t\t */\n+\n+\t\tsp \u003d stack[0].name;\n+\n+\t\t/* while we have more labels */\n+\n+\t\tn \u003d lws_adns_parse_label(pkt, len, p, len, \u0026sp,\n+\t\t\t\t\t sizeof(stack[0].name) -\n+\t\t\t\t\t lws_ptr_diff(sp, stack[0].name));\n+\t\t/* includes case name won't fit */\n+\t\tif (n \u003c 0)\n+\t\t\treturn -1;\n+\n+\t\tp +\u003d n;\n+\n+\t\tif (p + (inq ? 5 : 14) \u003e e)\n+\t\t\treturn -1;\n+\n+\t\t/*\n+\t\t * p is now just after the decoded RR name, pointing at: type\n+\t\t *\n+\t\t * We sent class \u003d 1 \u003d IN query... response must match\n+\t\t */\n+\n+\t\tif (lws_ser_ru16be(\u0026p[2]) !\u003d 1) {\n+\t\t\tlwsl_debug(\u0022%s: non-IN response 0x%x\u005cn\u0022, __func__,\n+\t\t\t\t\t\tlws_ser_ru16be(\u0026p[2]));\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (inq) {\n+\t\t\tlwsl_debug(\u0022%s: reached end of inq\u005cn\u0022, __func__);\n+\t\t\tinq \u003d 0;\n+\t\t\tp +\u003d 4;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* carefully validate the claimed RR payload length */\n+\n+\t\trrpaylen \u003d lws_ser_ru16be(\u0026p[8]);\n+\t\tif (p + 10 + rrpaylen \u003e e) { /* it may be \u003d\u003d e */\n+\t\t\tlwsl_notice(\u0022%s: invalid RR data length\u005cn\u0022, __func__);\n+\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tttl \u003d lws_ser_ru32be(\u0026p[4]);\n+\t\trrtype \u003d lws_ser_ru16be(\u0026p[0]);\n+\t\tp +\u003d 10; /* point to the payload */\n+\t\tpay \u003d p;\n+\n+\t\t/*\n+\t\t * Compare the RR names, allowing for the decoded labelname\n+\t\t * to have an extra '.' at the end.\n+\t\t */\n+\n+\t\tn \u003d lws_ptr_diff(sp, stack[0].name);\n+\t\tif (stack[0].name[n - 1] \u003d\u003d '.')\n+\t\t\tn--;\n+\n+\t\tif (n \u003c 1 || n !\u003d stack[stp].enl ||\n+\t\t strcmp(stack[0].name, stack[stp].name)) {\n+\t\t\tlwsl_debug(\u0022%s: skipping %s vs %s\u005cn\u0022, __func__,\n+\t\t\t\t\tstack[0].name, stack[stp].name);\n+\t\t\tgoto skip;\n+\t\t}\n+\n+\t\t/*\n+\t\t * It's something we could be interested in...\n+\t\t *\n+\t\t * We can skip RRs we don't understand. But we need to deal\n+\t\t * with at least these and their payloads:\n+\t\t *\n+\t\t * A: 4: ipv4 address\n+\t\t * AAAA: 16: ipv6 address (if asked for AAAA)\n+\t\t * CNAME: ?: labelized name\n+\t\t *\n+\t\t * If we hit a CNAME we need to try to dereference it with\n+\t\t * stuff that is in the same response packet and judge it\n+\t\t * from that, without losing our place here. CNAMEs may\n+\t\t * point to CNAMEs to whatever depth we're willing to handle.\n+\t\t */\n+\n+\t\tswitch (rrtype) {\n+#if defined(LWS_WITH_IPV6)\n+\t\tcase LWS_ADNS_RECORD_AAAA:\n+\t\t\tif (rrpaylen !\u003d 16) {\n+\t\t\t\tlwsl_err(\u0022%s: unexpected rrpaylen\u005cn\u0022, __func__);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tgoto do_cb;\n+#endif\n+\t\tcase LWS_ADNS_RECORD_A:\n+\t\t\tif (rrpaylen !\u003d 4) {\n+\t\t\t\tlwsl_err(\u0022%s: unexpected rrpaylen4\u005cn\u0022, __func__);\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+#if defined(LWS_WITH_IPV6)\n+do_cb:\n+#endif\n+\t\t\tcb(stack[0].name, opaque, ttl, rrtype, p);\n+\t\t\tbreak;\n+\n+\t\tcase LWS_ADNS_RECORD_CNAME:\n+\t\t\t/*\n+\t\t\t * The name the CNAME refers to should itself be\n+\t\t\t * included elsewhere in the response packet.\n+\t\t\t *\n+\t\t\t * So switch tack, stack where to resume from and\n+\t\t\t * search for the decoded CNAME label name definition\n+\t\t\t * instead.\n+\t\t\t *\n+\t\t\t * First decode the CNAME label payload into the next\n+\t\t\t * stack level buffer for it.\n+\t\t\t */\n+\n+\t\t\tif (++stp \u003d\u003d (int)LWS_ARRAY_SIZE(stack)) {\n+\t\t\t\tlwsl_notice(\u0022%s: CNAMEs too deep\u005cn\u0022, __func__);\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tsp \u003d stack[stp].name;\n+\t\t\tn \u003d lws_adns_parse_label(pkt, len, p, rrpaylen, \u0026sp,\n+\t\t\t\t\t\t sizeof(stack[stp].name) -\n+\t\t\t\t\t\t lws_ptr_diff(sp, stack[stp].name));\n+\t\t\t/* includes case name won't fit */\n+\t\t\tif (n \u003c 0)\n+\t\t\t\treturn -1;\n+\n+\t\t\tp +\u003d n;\n+\n+\t\t\tif (p + 14 \u003e e)\n+\t\t\t\treturn -1;\n+\n+\t\t\t/* it should have exactly reached rrpaylen */\n+\n+\t\t\tif (p !\u003d pay + rrpaylen) {\n+\t\t\t\tlwsl_err(\u0022%s: cname name bad len\u005cn\u0022, __func__);\n+\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tstack[stp].enl \u003d lws_ptr_diff(sp, stack[stp].name);\n+\t\t\t/* when we unstack, resume from here */\n+\t\t\tstack[stp].p \u003d pay + rrpaylen;\n+\t\t\tgoto start;\n+\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\n+skip:\n+\t\tp +\u003d rrpaylen;\n+\t}\n+\n+\tif (!stp)\n+\t\treturn 1; /* we didn't find anything, but we didn't error */\n+\n+\t/*\n+\t * This implies there wasn't any usable definition for the\n+\t * CNAME in the end, eg, only AAAA when we needed and A.\n+\t *\n+\t * Short-circuit the whole stack and resume from after the\n+\t * original CNAME reference.\n+\t */\n+\tp \u003d stack[1].p;\n+\tstp \u003d 0;\n+\tgoto resume;\n+}\n+\n+int\n+lws_async_dns_estimate(const char *name, void *opaque, uint32_t ttl,\n+\t\t\tadns_query_type_t type, const uint8_t *payload)\n+{\n+\tsize_t *est \u003d (size_t *)opaque, my;\n+\n+\tmy \u003d sizeof(struct addrinfo);\n+\tif (type \u003d\u003d LWS_ADNS_RECORD_AAAA)\n+\t\tmy +\u003d sizeof(struct sockaddr_in6);\n+\telse\n+\t\tmy +\u003d sizeof(struct sockaddr_in);\n+\n+\t*est +\u003d my;\n+\n+\treturn 0;\n+}\n+\n+struct adstore {\n+\tconst char *name;\n+\tstruct addrinfo *pos;\n+\tstruct addrinfo *prev;\n+\tint ctr;\n+\tuint32_t smallest_ttl;\n+\tuint8_t flags;\n+};\n+\n+/*\n+ * Callback for each A or AAAA record, creating getaddrinfo-compatible results\n+ * into the preallocated exact-sized storage.\n+ */\n+int\n+lws_async_dns_store(const char *name, void *opaque, uint32_t ttl,\n+\t\t adns_query_type_t type, const uint8_t *payload)\n+{\n+\tstruct adstore *adst \u003d (struct adstore *)opaque;\n+#if defined(_DEBUG)\n+\tchar buf[48];\n+#endif\n+\tsize_t i;\n+\n+\tif (ttl \u003c adst-\u003esmallest_ttl || !adst-\u003ectr)\n+\t\tadst-\u003esmallest_ttl \u003d ttl;\n+\n+\tif (adst-\u003eprev)\n+\t\tadst-\u003eprev-\u003eai_next \u003d adst-\u003epos;\n+\tadst-\u003eprev \u003d adst-\u003epos;\n+\n+\tadst-\u003epos-\u003eai_flags \u003d 0;\n+\tadst-\u003epos-\u003eai_family \u003d type \u003d\u003d LWS_ADNS_RECORD_AAAA ?\n+\t\t\t\t\t\tAF_INET6 : AF_INET;\n+\tadst-\u003epos-\u003eai_socktype \u003d SOCK_STREAM;\n+\tadst-\u003epos-\u003eai_protocol \u003d IPPROTO_UDP; /* no meaning */\n+\tadst-\u003epos-\u003eai_addrlen \u003d type \u003d\u003d LWS_ADNS_RECORD_AAAA ?\n+\t\t\t\t\t\tsizeof(struct sockaddr_in6) :\n+\t\t\t\t\t\tsizeof(struct sockaddr_in);\n+\tadst-\u003epos-\u003eai_canonname \u003d (char *)adst-\u003ename;\n+\tadst-\u003epos-\u003eai_addr \u003d (struct sockaddr *)\u0026adst-\u003epos[1];\n+\tadst-\u003epos-\u003eai_next \u003d NULL;\n+\n+#if defined(LWS_WITH_IPV6)\n+\tif (type \u003d\u003d LWS_ADNS_RECORD_AAAA) {\n+\t\tstruct sockaddr_in6 *in6 \u003d (struct sockaddr_in6 *)\u0026adst-\u003epos[1];\n+\n+\t\ti \u003d sizeof(*in6);\n+\t\tmemset(in6, 0, i);\n+\t\tin6-\u003esin6_family \u003d adst-\u003epos-\u003eai_family;\n+\t\tmemcpy(in6-\u003esin6_addr.s6_addr, payload, 16);\n+\t\tadst-\u003eflags |\u003d 2;\n+\t} else\n+#endif\n+\t{\n+\t\tstruct sockaddr_in *in \u003d (struct sockaddr_in *)\u0026adst-\u003epos[1];\n+\n+\t\ti \u003d sizeof(*in);\n+\t\tmemset(in, 0, i);\n+\t\tin-\u003esin_family \u003d adst-\u003epos-\u003eai_family;\n+\t\tmemcpy(\u0026in-\u003esin_addr.s_addr, payload, 4);\n+\t\tadst-\u003eflags |\u003d 1;\n+\t}\n+\n+\tadst-\u003epos \u003d (struct addrinfo *)((uint8_t *)adst-\u003epos +\n+\t\t\t\t\tsizeof(struct addrinfo) + i);\n+\n+#if defined(_DEBUG)\n+\tif (lws_write_numeric_address(payload,\n+\t\t\t\ttype \u003d\u003d LWS_ADNS_RECORD_AAAA ? 16 : 4,\n+\t\t\t\t\t\t\tbuf, sizeof(buf)) \u003e 0)\n+\t\tlwsl_info(\u0022%s: %d: %s: %s\u005cn\u0022, __func__, adst-\u003ectr,\n+\t\t\t\tadst-\u003ename, buf);\n+#endif\n+\tadst-\u003ectr++;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * We want to parse out all A or AAAA records\n+ */\n+\n+void\n+lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len)\n+{\n+\tlws_adns_cache_t *c;\n+\tstruct adstore adst;\n+\tlws_adns_q_t *q;\n+\tconst char *nm;\n+\tsize_t est;\n+\tint n;\n+\n+\t// lwsl_hexdump_notice(pkt, len);\n+\n+\t/* we have to at least have the header */\n+\n+\tif (len \u003c DHO_SIZEOF)\n+\t\treturn;\n+\n+\t/* we asked with one query, so anything else is bogus */\n+\n+\tif (lws_ser_ru16be(pkt + DHO_NQUERIES) !\u003d 1)\n+\t\treturn;\n+\n+\t/* match both A and AAAA queries if any */\n+\n+\tq \u003d lws_adns_get_query(dns, 0, \u0026dns-\u003ewaiting_resp,\n+\t\t\t lws_ser_ru16be(pkt + DHO_TID), NULL);\n+\tif (!q) {\n+\t\t/*\n+\t\t * if he's still waiting to send the second query, he's still\n+\t\t * on the .waiting_send list\n+\t\t */\n+\t\tq \u003d lws_adns_get_query(dns, 0, \u0026dns-\u003ewaiting_send,\n+\t\t\t\t lws_ser_ru16be(pkt + DHO_TID), NULL);\n+\n+\t\tif (!q) {\n+\t\t\tlwsl_notice(\u0022%s: dropping unknown query tid 0x%x\u005cn\u0022,\n+\t\t\t\t__func__, lws_ser_ru16be(pkt + DHO_TID));\n+\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/* we can get dups... drop any that have already happened */\n+\n+\tn \u003d 1 \u003c\u003c (lws_ser_ru16be(pkt + DHO_TID) \u0026 1);\n+\tif (q-\u003eresponded \u0026 n) {\n+\t\tlwsl_notice(\u0022%s: dup\u005cn\u0022, __func__);\n+\t\tgoto fail_out;\n+\t}\n+\n+\tq-\u003eresponded |\u003d n;\n+\tnm \u003d (const char *)\u0026q[1];\n+\n+\t/*\n+\t * First walk the packet figuring out the allocation needed for all\n+\t * the results. Produce the following layout at c\n+\t *\n+\t * lws_adns_cache_t: new cache object\n+\t * [struct addrinfo + struct sockaddr_in or _in6]: for each A or AAAA\n+\t * char []: copy of resolved name\n+\t */\n+\n+\tn \u003d strlen(nm) + 1;\n+\n+\test \u003d sizeof(lws_adns_cache_t) + n;\n+\tif (lws_ser_ru16be(pkt + DHO_NANSWERS) \u0026\u0026\n+\t lws_adns_iterate(pkt, len, nm, lws_async_dns_estimate, \u0026est) \u003c 0)\n+\t\t\tgoto fail_out;\n+\n+\tc \u003d lws_malloc(est, \u0022async-dns-entry\u0022);\n+\tif (!c) {\n+\t\tlwsl_err(\u0022%s: OOM %zu\u005cn\u0022, __func__, est);\n+\t\tgoto fail_out;\n+\t}\n+\tmemset(c, 0, sizeof(*c));\n+\n+\t/* place it at end, no need to care about alignment padding */\n+\tadst.name \u003d ((const char *)c) + est - n;\n+\tmemcpy((char *)adst.name, nm, n);\n+\n+\t/*\n+\t * Then walk the packet again, placing the objects we accounted for\n+\t * the first time into the result allocation after the cache object\n+\t * and copy of the name\n+\t */\n+\n+\tadst.pos \u003d (struct addrinfo *)\u0026c[1];\n+\tadst.prev \u003d NULL;\n+\tadst.ctr \u003d 0;\n+\tadst.smallest_ttl \u003d 3600;\n+\tadst.flags \u003d 0;\n+\n+\t/*\n+\t * smallest_ttl applies as it is to empty results (NXDOMAIN), or is\n+\t * set to the minimum ttl seen in all the results.\n+\t */\n+\n+\tif (lws_ser_ru16be(pkt + DHO_NANSWERS) \u0026\u0026\n+\t lws_adns_iterate(pkt, len, nm, lws_async_dns_store, \u0026adst) \u003c 0) {\n+\t\tlws_free(c);\n+\t\tgoto fail_out;\n+\t}\n+\n+\tif (lws_ser_ru16be(pkt + DHO_NANSWERS)) {\n+\t\tc-\u003eresults \u003d (struct addrinfo *)\u0026c[1];\n+\t\tif (q-\u003elast) /* chain the second one on */\n+\t\t\t*q-\u003elast \u003d c-\u003eresults;\n+\t\telse /* first one had no results, set first guy's c-\u003eresults */\n+\t\t\tif (q-\u003efirstcache)\n+\t\t\t\tq-\u003efirstcache-\u003eresults \u003d c-\u003eresults;\n+\t}\n+\n+\tif (adst.prev) /* so we know where to continue the addrinfo list */\n+\t\t/* can be NULL if first resp empty */\n+\t\tq-\u003elast \u003d \u0026adst.prev-\u003eai_next;\n+\n+\tif (q-\u003efirstcache) { /* also need to free chain when we free this guy */\n+\t\tq-\u003efirstcache-\u003echain \u003d c;\n+\t\tc-\u003efirstcache \u003d q-\u003efirstcache;\n+\t} else {\n+\n+\t\tq-\u003efirstcache \u003d c;\n+\n+\t\t/*\n+\t\t * Only register the first one into the cache...\n+\t\t * Trim the oldest cache entry if necessary\n+\t\t */\n+\n+\t\tlws_async_dns_trim_cache(dns);\n+\n+\t\t/*\n+\t\t * cache the first results object... if a second one comes,\n+\t\t * we won't directly register it but will chain it on to this\n+\t\t * first one and continue to addinfo ai_next linked list from\n+\t\t * the first into the second\n+\t\t */\n+\n+\t\tc-\u003eflags \u003d adst.flags;\n+\t\tlws_dll2_add_head(\u0026c-\u003elist, \u0026dns-\u003ecached);\n+\t\tlws_sul_schedule(q-\u003econtext, 0, \u0026c-\u003esul, sul_cb_expire,\n+\t\t\t\t lws_now_usecs() +\n+\t\t\t\t (adst.smallest_ttl * LWS_US_PER_SEC));\n+\t}\n+\n+\tif (q-\u003eresponded !\u003d q-\u003easked)\n+\t\treturn;\n+\n+\t/*\n+\t * Now we captured everything into the new object, return the\n+\t * addrinfo results, if any, to all interested wsi, if any...\n+\t */\n+\n+\tlws_async_dns_complete(q, q-\u003efirstcache);\n+\n+\t/*\n+\t * the query is completely finished with\n+\t */\n+\n+fail_out:\n+\tlws_adns_q_destroy(q);\n+}\n+\ndiff --git a/lib/system/async-dns/async-dns.c b/lib/system/async-dns/async-dns.c\nnew file mode 100644\nindex 0000000..2d44d15\n--- /dev/null\n+++ b/lib/system/async-dns/async-dns.c\n@@ -0,0 +1,671 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+#include \u0022private-lib-core.h\u0022\n+#include \u0022private-lib-async-dns.h\u0022\n+\n+void\n+lws_adns_q_destroy(lws_adns_q_t *q)\n+{\n+\tlws_dll2_remove(\u0026q-\u003esul.list);\n+\tlws_dll2_remove(\u0026q-\u003elist);\n+\tlws_free(q);\n+}\n+\n+lws_adns_q_t *\n+lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype,\n+\t\t lws_dll2_owner_t *owner, uint16_t tid, const char *name)\n+{\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t lws_dll2_get_head(owner)) {\n+\t\tlws_adns_q_t *q \u003d lws_container_of(d, lws_adns_q_t, list);\n+\n+\t\tif (!name \u0026\u0026 (tid \u0026 0xfffe) \u003d\u003d (q-\u003etid \u0026 0xfffe))\n+\t\t\treturn q;\n+\n+\t\tif (name \u0026\u0026 q-\u003eqtype \u003d\u003d ((tid \u0026 1) ? LWS_ADNS_RECORD_AAAA :\n+\t\t\t\t\t\t LWS_ADNS_RECORD_A) \u0026\u0026\n+\t\t !strcasecmp(name, (const char *)\u0026q[1])) {\n+\t\t\tif (owner \u003d\u003d \u0026dns-\u003ecached) {\n+\t\t\t\t/* Keep sorted by LRU: move to the head */\n+\t\t\t\tlws_dll2_remove(\u0026q-\u003elist);\n+\t\t\t\tlws_dll2_add_head(\u0026q-\u003elist, \u0026dns-\u003ecached);\n+\t\t\t}\n+\n+\t\t\treturn q;\n+\t\t}\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\treturn NULL;\n+}\n+\n+void\n+lws_async_dns_drop_server(struct lws_context *context)\n+{\n+\tcontext-\u003easync_dns.dns_server_set \u003d 0;\n+\tlws_set_timeout(context-\u003easync_dns.wsi, 1, LWS_TO_KILL_ASYNC);\n+\tcontext-\u003easync_dns.wsi \u003d NULL;\n+}\n+\n+int\n+lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c)\n+{\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t lws_dll2_get_head(\u0026q-\u003ewsi_adns)) {\n+\t\tstruct lws *w \u003d lws_container_of(d, struct lws, adns);\n+\n+\t\tlws_dll2_remove(d);\n+\t\tif (c \u0026\u0026 c-\u003eresults) {\n+\t\t\tlwsl_debug(\u0022%s: q: %p, c: %p, refcount %d -\u003e %d\u005cn\u0022,\n+\t\t\t\t __func__, q, c, c-\u003erefcount, c-\u003erefcount + 1);\n+\t\t\tc-\u003erefcount++;\n+\t\t}\n+\t\tw-\u003eadns_cb(w, (const char *)\u0026q[1], c ? c-\u003eresults : NULL, 0,\n+\t\t\t\tq-\u003eopaque);\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\tif (q-\u003estandalone_cb) {\n+\t\tif (c \u0026\u0026 c-\u003eresults) {\n+\t\t\tlwsl_debug(\u0022%s: q: %p, c: %p, refcount %d -\u003e %d\u005cn\u0022,\n+\t\t\t\t __func__, q, c, c-\u003erefcount, c-\u003erefcount + 1);\n+\t\t\tc-\u003erefcount++;\n+\t\t}\n+\n+\t\tq-\u003estandalone_cb(NULL, (const char *)\u0026q[1],\n+\t\t\t\t c ? c-\u003eresults : NULL, 0, q-\u003eopaque);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void\n+sul_cb_timeout(struct lws_sorted_usec_list *sul)\n+{\n+\tlws_adns_q_t *q \u003d lws_container_of(sul, lws_adns_q_t, sul);\n+\n+\tlws_async_dns_complete(q, NULL);\n+\tlws_adns_q_destroy(q);\n+\n+\t/*\n+\t * our policy is to force reloading the dns server info if our\n+\t * connection ever timed out, in case it or the routing state changed\n+\t */\n+\n+\tlws_async_dns_drop_server(q-\u003econtext);\n+}\n+\n+static int\n+callback_async_dns(struct lws *wsi, enum lws_callback_reasons reason,\n+\t\t void *user, void *in, size_t len)\n+{\n+\tuint8_t pkt[LWS_PRE + DNS_PACKET_LEN], *e \u003d \u0026pkt[sizeof(pkt)], *p, *pl;\n+\tstruct lws_async_dns *dns \u003d \u0026(lws_get_context(wsi)-\u003easync_dns);\n+\tstruct lws_dll2 *d;\n+\tconst char *name;\n+\tlws_adns_q_t *q;\n+\tint fd, m, n;\n+\n+\tswitch (reason) {\n+\n+\t/* callbacks related to raw socket descriptor */\n+\n+ case LWS_CALLBACK_RAW_ADOPT:\n+\t\t// lwsl_user(\u0022LWS_CALLBACK_RAW_ADOPT\u005cn\u0022);\n+ break;\n+\n+\tcase LWS_CALLBACK_RAW_CLOSE:\n+\t\t// lwsl_user(\u0022LWS_CALLBACK_RAW_CLOSE\u005cn\u0022);\n+\t\tbreak;\n+\n+\tcase LWS_CALLBACK_RAW_RX:\n+\t\t// lwsl_user(\u0022LWS_CALLBACK_RAW_RX (%d)\u005cn\u0022, (int)len);\n+\t\t//lwsl_hexdump_level(LLL_NOTICE, in, len);\n+\t\tlws_adns_parse_udp(dns, in, len);\n+\n+\t\treturn 0;\n+\n+\tcase LWS_CALLBACK_RAW_WRITEABLE:\n+\n+\t\td \u003d lws_dll2_get_head(\u0026dns-\u003ewaiting_send);\n+\t\tif (!d)\n+\t\t\treturn 0;\n+\t\tq \u003d lws_container_of(d, lws_adns_q_t, list);\n+\t\tname \u003d (const char *)\u0026q[1];\n+\n+\t\tp \u003d \u0026pkt[LWS_PRE];\n+\t\tmemset(p, 0, DHO_SIZEOF);\n+\n+\t\t/* we hack b0 of the tid to be 0 \u003d A, 1 \u003d AAAA */\n+\n+\t\tlws_ser_wu16be(\u0026p[DHO_TID], q-\u003easked ? q-\u003etid | 1 :\n+\t\t\t\tq-\u003etid);\n+\t\tlws_ser_wu16be(\u0026p[DHO_FLAGS], (1 \u003c\u003c 8));\n+\t\tlws_ser_wu16be(\u0026p[DHO_NQUERIES], 1);\n+\n+\t\tp +\u003d DHO_SIZEOF;\n+\n+\t\t/* start of label-formatted qname */\n+\n+\t\tpl \u003d p++;\n+\n+\t\tdo {\n+\t\t\tif (*name \u003d\u003d '.' || !*name) {\n+\t\t\t\t*pl \u003d lws_ptr_diff(p, pl + 1);\n+\t\t\t\tpl \u003d p;\n+\t\t\t\t*p++ \u003d 0; /* also serves as terminal length */\n+\t\t\t\tif (!*name++)\n+\t\t\t\t\tbreak;\n+\t\t\t} else\n+\t\t\t\t*p++ \u003d *name++;\n+\t\t} while (p + 6 \u003c e);\n+\n+\t\tif (p + 6 \u003e\u003d e) {\n+\t\t\tassert(0);\n+\t\t\tlwsl_err(\u0022%s: name too big\u005cn\u0022, __func__);\n+\t\t\tgoto qfail;\n+\t\t}\n+\n+\t\tlws_ser_wu16be(p, q-\u003easked ? LWS_ADNS_RECORD_AAAA :\n+\t\t\t\t\t LWS_ADNS_RECORD_A);\n+\t\tp +\u003d 2;\n+\n+\t\tlws_ser_wu16be(p, 1); /* IN class */\n+\t\tp +\u003d 2;\n+\n+\t\tassert(p \u003c pkt + sizeof(pkt) - LWS_PRE);\n+\t\tn \u003d lws_ptr_diff(p, pkt + LWS_PRE);\n+\n+\t\tfd \u003d lws_get_socket_fd(wsi);\n+\t\tif (fd \u003c 0)\n+\t\t\tbreak;\n+\n+\t\tm \u003d send(fd, pkt + LWS_PRE, n, 0);\n+\t\tif (m !\u003d n) {\n+\t\t\tlwsl_notice(\u0022%s: dns write failed %d %d\u005cn\u0022, __func__,\n+\t\t\t\t m, n);\n+\t\t\tgoto qfail;\n+\t\t}\n+\n+\t\t/* move us to the \u0022waiting for response\u0022 list */\n+\n+#if defined(LWS_WITH_IPV6)\n+\t\t/* don't move to waiting resp until we sent both */\n+\t\tif (q-\u003easked) {\n+\t\t\tq-\u003easked |\u003d 2;\n+#endif\n+\t\t\tlws_dll2_remove(\u0026q-\u003elist);\n+\t\t\tlws_dll2_add_head(\u0026q-\u003elist, \u0026dns-\u003ewaiting_resp);\n+#if defined(LWS_WITH_IPV6)\n+\t\t} else\n+\t\t\tlws_callback_on_writable(wsi);\n+#endif\n+\t\tq-\u003easked |\u003d 1;\n+\n+\t\tif (lws_dll2_get_head(\u0026dns-\u003ewaiting_send))\n+\t\t\t/* more to do */\n+\t\t\tlws_callback_on_writable(wsi);\n+\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn 0;\n+\n+qfail:\n+\tlws_async_dns_complete(q, NULL);\n+\tlws_adns_q_destroy(q);\n+\n+\treturn 0;\n+}\n+\n+struct lws_protocols lws_async_dns_protocol \u003d {\n+\t\u0022lws-async-dns\u0022, callback_async_dns, 0, 0\n+};\n+\n+int\n+lws_async_dns_init(struct lws_context *context)\n+{\n+\tlws_async_dns_t *dns \u003d \u0026context-\u003easync_dns;\n+\tchar ads[48];\n+\tint n;\n+\n+\tmemset(\u0026dns-\u003esa46, 0, sizeof(dns-\u003esa46));\n+\n+\tn \u003d lws_plat_asyncdns_init(context, \u0026dns-\u003esa46);\n+\tif (n \u003c 0) {\n+\t\tlwsl_warn(\u0022%s: no valid dns server, retry\u005cn\u0022, __func__);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tdns-\u003esa46.sa4.sin_port \u003d htons(53);\n+\tlws_write_numeric_address((uint8_t *)\u0026dns-\u003esa46.sa4.sin_addr.s_addr, 4,\n+\t\t\t\t ads, sizeof(ads));\n+\n+\tcontext-\u003easync_dns.wsi \u003d lws_create_adopt_udp(context-\u003evhost_list, ads,\n+\t\t\t\t 53, 0, lws_async_dns_protocol.name, NULL);\n+\tif (!dns-\u003ewsi) {\n+\t\tlwsl_err(\u0022%s: foreign socket adoption failed\u005cn\u0022, __func__);\n+\t\treturn 1;\n+\t}\n+\n+\tdns-\u003edns_server_set \u003d 1;\n+\n+\treturn 0;\n+}\n+\n+lws_adns_cache_t *\n+lws_adns_get_cache(lws_async_dns_t *dns, const char *name)\n+{\n+\tlws_adns_cache_t *c;\n+\tconst char *cn;\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,\n+\t\t\t\t lws_dll2_get_head(\u0026dns-\u003ecached)) {\n+\t\tc \u003d lws_container_of(d, lws_adns_cache_t, list);\n+\t\tcn \u003d (const char *)\u0026c[1];\n+\n+\t\tif (name \u0026\u0026 !strcasecmp(name, cn)) {\n+\t\t\t/* Keep sorted by LRU: move to the head */\n+\t\t\tlws_dll2_remove(\u0026c-\u003elist);\n+\t\t\tlws_dll2_add_head(\u0026c-\u003elist, \u0026dns-\u003ecached);\n+\n+\t\t\treturn c;\n+\t\t}\n+\t} lws_end_foreach_dll_safe(d, d1);\n+\n+\treturn NULL;\n+}\n+\n+void\n+lws_adns_cache_destroy(lws_adns_cache_t *c)\n+{\n+\tlws_dll2_remove(\u0026c-\u003esul.list);\n+\tlws_dll2_remove(\u0026c-\u003elist);\n+\tif (c-\u003echain)\n+\t\tlws_free(c-\u003echain);\n+\tlws_free(c);\n+}\n+\n+static int\n+cache_clean(struct lws_dll2 *d, void *user)\n+{\n+\tlws_adns_cache_destroy(lws_container_of(d, lws_adns_cache_t, list));\n+\n+\treturn 0;\n+}\n+\n+void\n+sul_cb_expire(struct lws_sorted_usec_list *sul)\n+{\n+\tlws_adns_cache_t *c \u003d lws_container_of(sul, lws_adns_cache_t, sul);\n+\n+\tlws_adns_cache_destroy(c);\n+}\n+\n+void\n+lws_async_dns_freeaddrinfo(const struct addrinfo *ai)\n+{\n+\tlws_adns_cache_t *c;\n+\n+\tif (!ai)\n+\t\treturn;\n+\n+\t/*\n+\t * First query may have been empty... if second has something, we\n+\t * fixed up the first result to point to second... but it means\n+\t * looking backwards from ai, which is c-\u003eresult, which is the second\n+\t * packet's results, doesn't get us to the firstcache pointer.\n+\t *\n+\t * Adjust c to the firstcache in this case.\n+\t */\n+\n+\tc \u003d \u0026((lws_adns_cache_t *)ai)[-1];\n+\tif (c-\u003efirstcache)\n+\t\tc \u003d c-\u003efirstcache;\n+\n+\tlwsl_debug(\u0022%s: c %p, %s, refcount %d -\u003e %d\u005cn\u0022, __func__, c,\n+\t\t (c-\u003eresults \u0026\u0026 c-\u003eresults-\u003eai_canonname) ?\n+\t\t\t\tc-\u003eresults-\u003eai_canonname : \u0022none\u0022,\n+\t\t\t\t\t\tc-\u003erefcount, c-\u003erefcount - 1);\n+\n+\tassert(c-\u003erefcount \u003e 0);\n+\tc-\u003erefcount--;\n+}\n+\n+void\n+lws_async_dns_trim_cache(lws_async_dns_t *dns)\n+{\n+\tlws_adns_cache_t *c1;\n+\n+\tif (dns-\u003ecached.count + 1\u003c MAX_CACHE_ENTRIES)\n+\t\treturn;\n+\n+\tc1 \u003d lws_container_of(lws_dll2_get_tail(\u0026dns-\u003ecached),\n+\t\t\t\t\t\tlws_adns_cache_t, list);\n+\tif (c1-\u003erefcount)\n+\t\tlwsl_notice(\u0022%s: wsi %p: refcount %d on purge\u005cn\u0022,\n+\t\t\t\t__func__, c1, c1-\u003erefcount);\n+\telse\n+\t\tlws_adns_cache_destroy(c1);\n+}\n+\n+\n+static int\n+clean(struct lws_dll2 *d, void *user)\n+{\n+\tlws_adns_q_destroy(lws_container_of(d, lws_adns_q_t, list));\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_async_dns_deinit(lws_async_dns_t *dns)\n+{\n+\tlws_dll2_foreach_safe(\u0026dns-\u003ewaiting_send, NULL, clean);\n+\tlws_dll2_foreach_safe(\u0026dns-\u003ewaiting_resp, NULL, clean);\n+\tlws_dll2_foreach_safe(\u0026dns-\u003ecached, NULL, cache_clean);\n+}\n+\n+\n+static int\n+cancel(struct lws_dll2 *d, void *user)\n+{\n+\tlws_adns_q_t *q \u003d lws_container_of(d, lws_adns_q_t, list);\n+\n+\tlws_start_foreach_dll_safe(struct lws_dll2 *, d3, d4,\n+\t\t\t lws_dll2_get_head(\u0026q-\u003ewsi_adns)) {\n+\t\tstruct lws *w \u003d lws_container_of(d3, struct lws, adns);\n+\n+\t\tif (user \u003d\u003d w) {\n+\t\t\tlws_dll2_remove(d3);\n+\t\t\tif (!q-\u003ewsi_adns.count)\n+\t\t\t\tlws_adns_q_destroy(q);\n+\t\t\treturn 1;\n+\t\t}\n+\t} lws_end_foreach_dll_safe(d3, d4);\n+\n+\treturn 0;\n+}\n+\n+void\n+lws_async_dns_cancel(struct lws *wsi)\n+{\n+\tlws_async_dns_t *dns \u003d \u0026wsi-\u003econtext-\u003easync_dns;\n+\n+\tif (!lws_dll2_foreach_safe(\u0026dns-\u003ewaiting_send, wsi, cancel))\n+\t\tlws_dll2_foreach_safe(\u0026dns-\u003ewaiting_resp, wsi, cancel);\n+}\n+\n+\n+static int\n+check_tid(struct lws_dll2 *d, void *user)\n+{\n+\tlws_adns_q_t *q \u003d lws_container_of(d, lws_adns_q_t, list);\n+\n+\treturn q-\u003etid \u003d\u003d (uint16_t)(long)user;\n+}\n+\n+static int\n+lws_async_dns_get_new_tid(struct lws_context *context, lws_adns_q_t *q)\n+{\n+\tlws_async_dns_t *dns \u003d \u0026context-\u003easync_dns;\n+\tint budget \u003d 10;\n+\n+\t/*\n+\t * Make the TID unpredictable, but must be unique amongst ongoing ones\n+\t */\n+\tdo {\n+\t\tif (lws_get_random(context, \u0026q-\u003etid, 2) !\u003d 2)\n+\t\t\treturn -1;\n+\n+\t\tif (lws_dll2_foreach_safe(\u0026dns-\u003ewaiting_send,\n+\t\t\t\t\t (void *)(long)q-\u003etid, check_tid))\n+\t\t\tcontinue;\n+\n+\t\tif (lws_dll2_foreach_safe(\u0026dns-\u003ewaiting_resp,\n+\t\t\t\t\t (void *)(long)q-\u003etid, check_tid))\n+\t\t\tcontinue;\n+\n+\t\treturn 0;\n+\n+\t} while (budget--);\n+\n+\tlwsl_err(\u0022%s: unable to get unique tid\u005cn\u0022, __func__);\n+\n+\treturn -1;\n+}\n+\n+struct temp_q {\n+\tlws_adns_q_t tq;\n+\tchar name[48];\n+};\n+\n+lws_async_dns_retcode_t\n+lws_async_dns_query(struct lws_context *context, int tsi, const char *name,\n+\t\t adns_query_type_t qtype, lws_async_dns_cb_t cb,\n+\t\t struct lws *wsi, void *opaque)\n+{\n+\tlws_async_dns_t *dns \u003d \u0026context-\u003easync_dns;\n+\tsize_t nlen \u003d strlen(name);\n+\tlws_sockaddr46 *sa46;\n+\tlws_adns_cache_t *c;\n+\tstruct addrinfo *ai;\n+\tstruct temp_q tmq;\n+\tlws_adns_q_t *q;\n+\tuint8_t ads[16];\n+\tchar *p;\n+\tint m;\n+\n+#if !defined(LWS_WITH_IPV6)\n+\tif (qtype \u003d\u003d LWS_ADNS_RECORD_AAAA) {\n+\t\tlwsl_err(\u0022%s: ipv6 not enabled\u005cn\u0022, __func__);\n+\t\tgoto failed;\n+\t}\n+#endif\n+\n+\t/*\n+\t * we magically know 'localhost' and 'localhost6' if IPv6, this is a\n+\t * sort of canned /etc/hosts\n+\t */\n+\n+\tif (!strcmp(name, \u0022localhost\u0022))\n+\t\tname \u003d \u0022127.0.0.1\u0022;\n+\n+#if defined(LWS_WITH_IPV6)\n+\tif (!strcmp(name, \u0022localhost6\u0022))\n+\t\tname \u003d \u0022::1\u0022;\n+#endif\n+\n+\tif (wsi) {\n+\t\tif (!lws_dll2_is_detached(\u0026wsi-\u003eadns)) {\n+\t\t\tlwsl_err(\u0022%s: wsi %p already bound to query %p\u005cn\u0022,\n+\t\t\t\t\t__func__, wsi, wsi-\u003eadns.owner);\n+\t\t\tgoto failed;\n+\t\t}\n+\t\twsi-\u003eadns_cb \u003d cb;\n+\t}\n+\n+\t/* there's a done, cached query we can just reuse? */\n+\n+\tc \u003d lws_adns_get_cache(dns, name);\n+\tif (c) {\n+\t\tcb(wsi, name, c-\u003eresults, 0, opaque);\n+\n+\t\treturn LADNS_RET_FOUND;\n+\t}\n+\n+\t/*\n+\t * It's a 1.2.3.4 type IP address already? We don't need a dns\n+\t * server set up to be able to create an addrinfo result for that.\n+\t *\n+\t * Create it as a cached object so it follows the refcount lifecycle\n+\t * of any other result\n+\t */\n+\n+\tm \u003d lws_parse_numeric_address(name, ads, sizeof(ads));\n+\tif (m \u003d\u003d 4\n+#if defined(LWS_WITH_IPV6)\n+\t\t|| m \u003d\u003d 16\n+#endif\n+\t) {\n+\t\tlws_async_dns_trim_cache(dns);\n+\n+\t\tc \u003d lws_zalloc(sizeof(lws_adns_cache_t) +\n+\t\t\t sizeof(struct addrinfo) +\n+\t\t\t sizeof(lws_sockaddr46) + nlen + 1, \u0022adns-numip\u0022);\n+\t\tif (!c)\n+\t\t\tgoto failed;\n+\n+\t\tai \u003d (struct addrinfo *)\u0026c[1];\n+\t\tsa46 \u003d (lws_sockaddr46 *)\u0026ai[1];\n+\n+\t\tai-\u003eai_socktype \u003d SOCK_STREAM;\n+\t\tmemcpy(\u0026sa46[1], name, nlen + 1);\n+\t\tai-\u003eai_canonname \u003d (char *)\u0026sa46[1];\n+\n+\t\tc-\u003eresults \u003d ai;\n+\t\tmemset(\u0026tmq.tq, 0, sizeof(tmq.tq));\n+\t\ttmq.tq.opaque \u003d opaque;\n+\t\tif (wsi) {\n+\t\t\twsi-\u003eadns_cb \u003d cb;\n+\t\t\tlws_dll2_add_head(\u0026wsi-\u003eadns, \u0026tmq.tq.wsi_adns);\n+\t\t} else\n+\t\t\ttmq.tq.standalone_cb \u003d cb;\n+\t\tlws_strncpy(tmq.name, name, sizeof(tmq.name));\n+\n+\t\tlws_dll2_add_head(\u0026c-\u003elist, \u0026dns-\u003ecached);\n+\t\tlws_sul_schedule(context, 0, \u0026c-\u003esul, sul_cb_expire,\n+\t\t\t\t lws_now_usecs() + (3600ll * LWS_US_PER_SEC));\n+\t}\n+\n+\tif (m \u003d\u003d 4) {\n+\t\tai-\u003eai_family \u003d sa46-\u003esa4.sin_family \u003d AF_INET;\n+\t\tai-\u003eai_addrlen \u003d sizeof(sa46-\u003esa4);\n+\t\tai-\u003eai_addr \u003d (struct sockaddr *)\u0026sa46-\u003esa4;\n+\t\tmemcpy(\u0026sa46-\u003esa4.sin_addr, ads, m);\n+\n+\t\tlws_async_dns_complete(\u0026tmq.tq, c);\n+\n+\t\treturn LADNS_RET_FOUND;\n+\t}\n+\n+#if defined(LWS_WITH_IPV6)\n+\tif (m \u003d\u003d 16) {\n+\t\tai-\u003eai_family \u003d sa46-\u003esa6.sin6_family \u003d AF_INET6;\n+\t\tai-\u003eai_addrlen \u003d sizeof(sa46-\u003esa6);\n+\t\tai-\u003eai_addr \u003d (struct sockaddr *)\u0026sa46-\u003esa6;\n+\t\tmemcpy(\u0026sa46-\u003esa6.sin6_addr, ads, m);\n+\n+\t\tlws_async_dns_complete(\u0026tmq.tq, c);\n+\n+\t\treturn LADNS_RET_FOUND;\n+\t}\n+#endif\n+\n+\t/*\n+\t * to try anything else we need a remote server configured...\n+\t */\n+\n+\tif (!context-\u003easync_dns.dns_server_set \u0026\u0026\n+\t lws_async_dns_init(context)) {\n+\t\tlwsl_notice(\u0022%s: init failed\u005cn\u0022, __func__);\n+\t\tgoto failed;\n+\t}\n+\n+\t/* there's an ongoing query we can share the result of */\n+\n+\tq \u003d lws_adns_get_query(dns, qtype, \u0026dns-\u003ewaiting_send, 0, name);\n+\tif (!q)\n+\t\tq \u003d lws_adns_get_query(dns, qtype, \u0026dns-\u003ewaiting_resp, 0, name);\n+\tif (q) {\n+\t\tlwsl_debug(\u0022%s: dns piggybacking: %d:%s\u005cn\u0022, __func__,\n+\t\t\t\tqtype, name);\n+\t\tif (wsi)\n+\t\t\tlws_dll2_add_head(\u0026wsi-\u003eadns, \u0026q-\u003ewsi_adns);\n+\n+\t\treturn LADNS_RET_CONTINUING;\n+\t}\n+\n+\t/*\n+\t * Allocate new query / queries... this is a bit complicated because\n+\t * multiple queries in one packet are not supported peoperly in DNS\n+\t * itself, and there's no reliable other way to get both ipv6 and ipv4\n+\t * (AAAA and A) responses in one hit.\n+\t *\n+\t * If we don't support ipv6, it's simple, we just ask for A and that's\n+\t * it. But if we do support ipv6, we need to ask twice, once for A\n+\t * and in a separate query, again for AAAA.\n+\t *\n+\t * For ipv6, A / ipv4 is routable over ipv6. So we always ask for A\n+\t * first and then if ipv6, AAAA separately.\n+\t *\n+\t * The results need binding into\n+\t */\n+\n+\tq \u003d (lws_adns_q_t *)lws_zalloc(sizeof(*q) + nlen + 1, __func__);\n+\tif (!q)\n+\t\tgoto failed;\n+\n+\tif (wsi)\n+\t\tlws_dll2_add_head(\u0026wsi-\u003eadns, \u0026q-\u003ewsi_adns);\n+\n+\tq-\u003eqtype \u003d (uint16_t)qtype;\n+\n+\tif (lws_async_dns_get_new_tid(context, q))\n+\t\tgoto failed;\n+\n+\tq-\u003etid \u0026\u003d 0xfffe;\n+\tq-\u003econtext \u003d context;\n+\tq-\u003etsi \u003d tsi;\n+\tq-\u003eopaque \u003d opaque;\n+\n+\tif (!wsi)\n+\t\tq-\u003estandalone_cb \u003d cb;\n+\n+\tlws_sul_schedule(context, tsi, \u0026q-\u003esul, sul_cb_timeout,\n+\t\t\t lws_now_usecs() +\n+\t\t\t (DNS_QUERY_TIMEOUT * LWS_US_PER_SEC));\n+\n+\tp \u003d (char *)\u0026q[1];\n+\twhile (nlen--)\n+\t\t*p++ \u003d tolower(*name++);\n+\t*p \u003d '\u005c0';\n+\n+\tlws_callback_on_writable(dns-\u003ewsi);\n+\n+\tlws_dll2_add_head(\u0026q-\u003elist, \u0026dns-\u003ewaiting_send);\n+\n+\tlwsl_debug(\u0022%s: created new query\u005cn\u0022, __func__);\n+\n+\treturn LADNS_RET_CONTINUING;\n+\n+failed:\n+\tcb(wsi, NULL, NULL, LADNS_RET_FAILED, opaque);\n+\n+\treturn LADNS_RET_FAILED;\n+}\ndiff --git a/lib/system/async-dns/private-lib-async-dns.h b/lib/system/async-dns/private-lib-async-dns.h\nnew file mode 100644\nindex 0000000..9165fd8\n--- /dev/null\n+++ b/lib/system/async-dns/private-lib-async-dns.h\n@@ -0,0 +1,110 @@\n+/*\n+ * libwebsockets - small server side websockets and web server implementation\n+ *\n+ * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \u0022Software\u0022), to\n+ * deal in the Software without restriction, including without limitation the\n+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n+ * sell copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n+ * IN THE SOFTWARE.\n+ */\n+\n+\n+#define\tDNS_MAX\t\t\t128\t/* Maximum host name\t\t*/\n+#define\tDNS_PACKET_LEN\t\t1400\t/* Buffer size for DNS packet\t*/\n+#define\tMAX_CACHE_ENTRIES\t10\t/* Dont cache more than that\t*/\n+#define\tDNS_QUERY_TIMEOUT\t30\t/* Query timeout, seconds\t*/\n+\n+/*\n+ * ... when we completed a query then the query object is destroyed and a\n+ * cache object below is created with the results in getaddrinfo format\n+ * appended to the allocation\n+ */\n+\n+typedef struct lws_adns_cache {\n+\tlws_sorted_usec_list_t\tsul;\t/* for cache TTL management */\n+\tlws_dll2_t\t\tlist;\n+\n+\tstruct lws_adns_cache\t*firstcache;\n+\tstruct lws_adns_cache\t*chain;\n+\tstruct addrinfo\t\t*results;\n+\tuint8_t\t\t\tflags;\t/* b0 \u003d has ipv4, b1 \u003d has ipv6 */\n+\tchar\t\t\trefcount;\n+\t/* name, and then result struct addrinfos overallocated here */\n+} lws_adns_cache_t;\n+\n+/*\n+ * these objects are used while a query is ongoing...\n+ */\n+\n+typedef struct {\n+\tlws_sorted_usec_list_t\tsul;\t/* for query network timeout */\n+\tlws_dll2_t\t\tlist;\n+\n+\tlws_dll2_owner_t\twsi_adns;\n+\tlws_async_dns_cb_t\tstandalone_cb;\t/* if not associated to wsi */\n+\tstruct lws_context\t*context;\n+\tvoid\t\t\t*opaque;\n+\tstruct addrinfo\t\t**last;\n+\n+\tlws_adns_cache_t\t*firstcache;\n+\n+\tlws_async_dns_retcode_t\tret;\n+\tuint16_t\t\ttid;\n+\tuint16_t\t\tqtype;\n+\tuint8_t\t\t\ttsi;\n+\n+\tuint8_t\t\t\tasked;\n+\tuint8_t\t\t\tresponded;\n+\n+\t/* name overallocated here */\n+} lws_adns_q_t;\n+\n+enum {\n+\tDHO_TID,\n+\tDHO_FLAGS \u003d 2,\n+\tDHO_NQUERIES \u003d 4,\n+\tDHO_NANSWERS \u003d 6,\n+\tDHO_NAUTH \u003d 8,\n+\tDHO_NOTHER \u003d 10,\n+\n+\tDHO_SIZEOF \u003d 12 /* last */\n+};\n+\n+void\n+lws_adns_q_destroy(lws_adns_q_t *q);\n+\n+void\n+sul_cb_expire(struct lws_sorted_usec_list *sul);\n+\n+void\n+lws_adns_cache_destroy(lws_adns_cache_t *c);\n+\n+int\n+lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c);\n+\n+lws_adns_cache_t *\n+lws_adns_get_cache(lws_async_dns_t *dns, const char *name);\n+\n+void\n+lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len);\n+\n+lws_adns_q_t *\n+lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype,\n+\t\t lws_dll2_owner_t *owner, uint16_t tid, const char *name);\n+\n+void\n+lws_async_dns_trim_cache(lws_async_dns_t *dns);\ndiff --git a/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt b/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt\nnew file mode 100644\nindex 0000000..2d8d0c5\n--- /dev/null\n+++ b/minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt\n@@ -0,0 +1,78 @@\n+cmake_minimum_required(VERSION 2.8)\n+include(CheckCSourceCompiles)\n+\n+set(SAMP lws-api-test-async-dns)\n+set(SRCS main.c)\n+\n+# If we are being built as part of lws, confirm current build config supports\n+# reqconfig, else skip building ourselves.\n+#\n+# If we are being built externally, confirm installed lws was configured to\n+# support reqconfig, else error out with a helpful message about the problem.\n+#\n+MACRO(require_lws_config reqconfig _val result)\n+\n+\tif (DEFINED ${reqconfig})\n+\tif (${reqconfig})\n+\t\tset (rq 1)\n+\telse()\n+\t\tset (rq 0)\n+\tendif()\n+\telse()\n+\t\tset(rq 0)\n+\tendif()\n+\n+\tif (${_val} EQUAL ${rq})\n+\t\tset(SAME 1)\n+\telse()\n+\t\tset(SAME 0)\n+\tendif()\n+\n+\tif (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})\n+\t\tif (${_val})\n+\t\t\tmessage(\u0022${SAMP}: skipping as lws being built without ${reqconfig}\u0022)\n+\t\telse()\n+\t\t\tmessage(\u0022${SAMP}: skipping as lws built with ${reqconfig}\u0022)\n+\t\tendif()\n+\t\tset(${result} 0)\n+\telse()\n+\t\tif (LWS_WITH_MINIMAL_EXAMPLES)\n+\t\t\tset(MET ${SAME})\n+\t\telse()\n+\t\t\tCHECK_C_SOURCE_COMPILES(\u0022#include \u003clibwebsockets.h\u003e\u005cnint main(void) {\u005cn#if defined(${reqconfig})\u005cn return 0;\u005cn#else\u005cn fail;\u005cn#endif\u005cn return 0;\u005cn}\u005cn\u0022 HAS_${reqconfig})\n+\t\t\tif (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})\n+\t\t\t\tset(HAS_${reqconfig} 0)\n+\t\t\telse()\n+\t\t\t\tset(HAS_${reqconfig} 1)\n+\t\t\tendif()\n+\t\t\tif ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))\n+\t\t\t\tset(MET 1)\n+\t\t\telse()\n+\t\t\t\tset(MET 0)\n+\t\t\tendif()\n+\t\tendif()\n+\t\tif (NOT MET)\n+\t\t\tif (${_val})\n+\t\t\t\tmessage(FATAL_ERROR \u0022This project requires lws must have been configured with ${reqconfig}\u0022)\n+\t\t\telse()\n+\t\t\t\tmessage(FATAL_ERROR \u0022Lws configuration of ${reqconfig} is incompatible with this project\u0022)\n+\t\t\tendif()\n+\t\tendif()\n+\tendif()\n+ENDMACRO()\n+\n+set(requirements 1)\n+require_lws_config(LWS_ROLE_H1 1 requirements)\n+require_lws_config(LWS_WITH_CLIENT 1 requirements)\n+require_lws_config(LWS_WITH_SYS_ASYNC_DNS 1 requirements)\n+\n+if (requirements)\n+\tadd_executable(${SAMP} ${SRCS})\n+\n+\tif (websockets_shared)\n+\t\ttarget_link_libraries(${SAMP} websockets_shared)\n+\t\tadd_dependencies(${SAMP} websockets_shared)\n+\telse()\n+\t\ttarget_link_libraries(${SAMP} websockets)\n+\tendif()\n+endif()\ndiff --git a/minimal-examples/api-tests/api-test-async-dns/main.c b/minimal-examples/api-tests/api-test-async-dns/main.c\nnew file mode 100644\nindex 0000000..3761b19\n--- /dev/null\n+++ b/minimal-examples/api-tests/api-test-async-dns/main.c\n@@ -0,0 +1,315 @@\n+/*\n+ * lws-api-test-async-dns\n+ *\n+ * Written in 2019 by Andy Green \u003candy@warmcat.com\u003e\n+ *\n+ * This file is made available under the Creative Commons CC0 1.0\n+ * Universal Public Domain Dedication.\n+ *\n+ * This api test confirms various kinds of async dns apis\n+ */\n+\n+#include \u003clibwebsockets.h\u003e\n+#include \u003csignal.h\u003e\n+\n+static int interrupted, dtest, ok, fail, exp \u003d 26;\n+struct lws_context *context;\n+\n+/*\n+ * These are used to test the apis to parse and print ipv4 / ipv6 literal\n+ * address strings for various cases.\n+ *\n+ * Expected error cases are not used to test the ip data -\u003e string api.\n+ */\n+\n+static const struct ipparser_tests {\n+\tconst char\t*test;\n+\tint\t\trlen;\n+\tconst char\t*emit_test;\n+\tint\t\temit_len;\n+\tuint8_t\t\tb[16];\n+} ipt[] \u003d {\n+\t{ \u00222001:db8:85a3:0:0:8a2e:370:7334\u0022, 16,\n+\t \u00222001:db8:85a3::8a2e:370:7334\u0022, 28,\n+\t\t{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,\n+\t\t 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },\n+\n+\t{ \u00222001:db8:85a3::8a2e:370:7334\u0022, 16,\n+\t \u00222001:db8:85a3::8a2e:370:7334\u0022, 28,\n+\t\t{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,\n+\t\t 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },\n+\n+\t{ \u0022::1\u0022, 16, \u0022::1\u0022, 3,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u0022::\u0022, 16, \u0022::\u0022, 2,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },\n+\n+\t{ \u0022::ffff:192.0.2.128\u0022, 16, \u0022::ffff:192.0.2.128\u0022, 18,\n+\t\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n+\t\t 0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0x02, 0x80 } },\n+\n+\t{ \u0022cats\u0022, -1, \u0022\u0022, 0,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u0022onevalid.bogus.warmcat.com\u0022, -1, \u0022\u0022, 0,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u00221.cat.dog.com\u0022, -1, \u0022\u0022, 0,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u0022:::1\u0022, -8, \u0022\u0022, 0,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u00220:0::0:1\u0022, 16, \u0022::1\u0022, 3,\n+\t\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },\n+\n+\t{ \u00221.2.3.4\u0022, 4, \u00221.2.3.4\u0022, 7, { 1, 2, 3, 4 } },\n+};\n+\n+static const struct async_dns_tests {\n+\tconst char *dns_name;\n+\tint recordtype;\n+\tint addrlen;\n+\tuint8_t ads[16];\n+} adt[] \u003d {\n+\t{ \u0022warmcat.com\u0022, LWS_ADNS_RECORD_A, 4,\n+\t\t{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022libwebsockets.org\u0022, LWS_ADNS_RECORD_A, 4,\n+\t\t{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022doesntexist\u0022, LWS_ADNS_RECORD_A, 0,\n+\t\t{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022localhost\u0022, LWS_ADNS_RECORD_A, 4,\n+\t\t{ 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022ipv4only.warmcat.com\u0022, LWS_ADNS_RECORD_A, 4,\n+\t\t{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022onevalid.bogus.warmcat.com\u0022, LWS_ADNS_RECORD_A, 4,\n+\t\t{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },\n+#if defined(LWS_WITH_IPV6)\n+\t{ \u0022warmcat.com\u0022, LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */\n+\t\t{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,\n+\t\t\t\t0, 0, 0, 0, 0, 0, 0, 0, } },\n+\t{ \u0022ipv6only.warmcat.com\u0022, LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */\n+\t\t{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,\n+\t\t\t\t0, 0, 0, 0, 0, 0, 0, 0, } },\n+#endif\n+};\n+\n+static lws_sorted_usec_list_t sul;\n+\n+struct lws *\n+cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,\n+ void *opaque);\n+\n+static void\n+next_test_cb(lws_sorted_usec_list_t *sul)\n+{\n+\tint m;\n+\n+\tlwsl_notice(\u0022%s: querying %s\u005cn\u0022, __func__, adt[dtest].dns_name);\n+\n+\tm \u003d lws_async_dns_query(context, 0,\n+\t\t\t\tadt[dtest].dns_name,\n+\t\t\t\tadt[dtest].recordtype, cb1, NULL,\n+\t\t\t\tcontext);\n+\tif (m !\u003d LADNS_RET_CONTINUING \u0026\u0026 m !\u003d LADNS_RET_FOUND) {\n+\t\tlwsl_err(\u0022%s: adns 1 failed: %d\u005cn\u0022, __func__, m);\n+\t\tinterrupted \u003d 1;\n+\t}\n+}\n+\n+\n+struct lws *\n+cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,\n+ void *opaque)\n+{\n+\tconst struct addrinfo *ac \u003d a;\n+\tint ctr \u003d 0, alen;\n+\tuint8_t *addr;\n+\tchar buf[64];\n+\n+\tdtest++;\n+\n+\tif (!ac)\n+\t\tlwsl_warn(\u0022%s: no results\u005cn\u0022, __func__);\n+\n+\t/* dump the results */\n+\n+\twhile (ac) {\n+\t\tif (ac-\u003eai_family \u003d\u003d AF_INET) {\n+\t\t\taddr \u003d (uint8_t *)\u0026(((struct sockaddr_in *)\n+\t\t\t\t\tac-\u003eai_addr)-\u003esin_addr.s_addr);\n+\t\t\talen \u003d 4;\n+\t\t} else {\n+\t\t\taddr \u003d (uint8_t *)\u0026(((struct sockaddr_in6 *)\n+\t\t\t\t\tac-\u003eai_addr)-\u003esin6_addr.s6_addr);\n+\t\t\talen \u003d 16;\n+\t\t}\n+\t\tstrcpy(buf, \u0022unknown\u0022);\n+\t\tlws_write_numeric_address(addr, alen, buf, sizeof(buf));\n+\n+\t\tlwsl_warn(\u0022%s: %d: %s %d %s\u005cn\u0022, __func__, ctr++, ads, alen, buf);\n+\n+\t\tac \u003d ac-\u003eai_next;\n+\t}\n+\n+\tac \u003d a;\n+\twhile (ac) {\n+\t\tif (ac-\u003eai_family \u003d\u003d AF_INET) {\n+\t\t\taddr \u003d (uint8_t *)\u0026(((struct sockaddr_in *)\n+\t\t\t\t\tac-\u003eai_addr)-\u003esin_addr.s_addr);\n+\t\t\talen \u003d 4;\n+\t\t} else {\n+#if defined(LWS_WITH_IPV6)\n+\t\t\taddr \u003d (uint8_t *)\u0026(((struct sockaddr_in6 *)\n+\t\t\t\t\tac-\u003eai_addr)-\u003esin6_addr.s6_addr);\n+\t\t\talen \u003d 16;\n+#else\n+\t\t\tgoto again;\n+#endif\n+\t\t}\n+\t\tif (alen \u003d\u003d adt[dtest - 1].addrlen \u0026\u0026\n+\t\t !memcmp(adt[dtest - 1].ads, addr, alen)) {\n+\t\t\tok++;\n+\t\t\tgoto next;\n+\t\t}\n+#if !defined(LWS_WITH_IPV6)\n+again:\n+#endif\n+\t\tac \u003d ac-\u003eai_next;\n+\t}\n+\n+\t/* testing for NXDOMAIN? */\n+\n+\tif (!a \u0026\u0026 !adt[dtest - 1].addrlen) {\n+\t\tok++;\n+\t\tgoto next;\n+\t}\n+\n+\tlwsl_err(\u0022%s: dns test %d: no match\u005cn\u0022, __func__, dtest);\n+\tfail++;\n+\n+next:\n+\tlws_async_dns_freeaddrinfo(a);\n+\tif (dtest \u003d\u003d (int)LWS_ARRAY_SIZE(adt))\n+\t\tinterrupted \u003d 1;\n+\telse\n+\t\tlws_sul_schedule(context, 0, \u0026sul, next_test_cb, 1);\n+\n+\treturn NULL;\n+}\n+\n+\n+void sigint_handler(int sig)\n+{\n+\tinterrupted \u003d 1;\n+}\n+\n+int\n+main(int argc, const char **argv)\n+{\n+\tint n \u003d 1, logs \u003d LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;\n+\tstruct lws_context_creation_info info;\n+\tconst char *p;\n+\n+\t/* the normal lws init */\n+\n+\tsignal(SIGINT, sigint_handler);\n+\n+\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-d\u0022)))\n+\t\tlogs \u003d atoi(p);\n+\n+\tlws_set_log_level(logs, NULL);\n+\tlwsl_user(\u0022LWS API selftest: Async DNS\u005cn\u0022);\n+\n+\tmemset(\u0026info, 0, sizeof info); /* otherwise uninitialized garbage */\n+\tinfo.port \u003d CONTEXT_PORT_NO_LISTEN;\n+\tinfo.options \u003d LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n+\n+\tcontext \u003d lws_create_context(\u0026info);\n+\tif (!context) {\n+\t\tlwsl_err(\u0022lws init failed\u005cn\u0022);\n+\t\treturn 1;\n+\t}\n+\n+\n+\t/* ip address parser tests */\n+\n+\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(ipt); n++) {\n+\t\tuint8_t u[16];\n+\t\tint m \u003d lws_parse_numeric_address(ipt[n].test, u, sizeof(u));\n+\n+\t\tif (m !\u003d ipt[n].rlen) {\n+\t\t\tlwsl_err(\u0022%s: fail %s ret %d\u005cn\u0022,\n+\t\t\t\t\t__func__, ipt[n].test, m);\n+\t\t\tfail++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (m \u003e 0) {\n+\t\t\tif (memcmp(ipt[n].b, u, m)) {\n+\t\t\t\tlwsl_err(\u0022%s: fail %s compare\u005cn\u0022, __func__,\n+\t\t\t\t\t\tipt[n].test);\n+\t\t\t\tlwsl_hexdump_notice(u, m);\n+\t\t\t\tfail++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\tok++;\n+\t}\n+\n+\t/* ip address formatter tests */\n+\n+\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(ipt); n++) {\n+\t\tchar buf[64];\n+\t\tint m;\n+\n+\t\t/* don't attempt to reverse the ones that are meant to fail */\n+\t\tif (ipt[n].rlen \u003c 0)\n+\t\t\tcontinue;\n+\n+\t\tm \u003d lws_write_numeric_address(ipt[n].b, ipt[n].rlen, buf,\n+\t\t\t\t\t\tsizeof(buf));\n+\t\tif (m !\u003d ipt[n].emit_len) {\n+\t\t\tlwsl_err(\u0022%s: fail %s ret %d\u005cn\u0022,\n+\t\t\t\t\t__func__, ipt[n].emit_test, m);\n+\t\t\tfail++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (m \u003e 0) {\n+\t\t\tif (strcmp(ipt[n].emit_test, buf)) {\n+\t\t\t\tlwsl_err(\u0022%s: fail %s compare\u005cn\u0022, __func__,\n+\t\t\t\t\t\tipt[n].test);\n+\t\t\t\tlwsl_hexdump_notice(buf, m);\n+\t\t\t\tfail++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\tok++;\n+\t}\n+\n+#if !defined(LWS_WITH_IPV6)\n+\texp -\u003d 2;\n+#endif\n+\n+\t/* kick off the async dns tests */\n+\n+\tlws_sul_schedule(context, 0, \u0026sul, next_test_cb, 1);\n+\n+\t/* the usual lws event loop */\n+\n+\tn \u003d 1;\n+\twhile (n \u003e\u003d 0 \u0026\u0026 !interrupted)\n+\t\tn \u003d lws_service(context, 0);\n+\n+\tlws_context_destroy(context);\n+\n+\tif (fail || ok !\u003d exp)\n+\t\tlwsl_user(\u0022Completed: PASS: %d / %d, FAIL: %d\u005cn\u0022, ok, exp,\n+\t\t\t\tfail);\n+\telse\n+\t\tlwsl_user(\u0022Completed: ALL PASS: %d / %d\u005cn\u0022, ok, exp);\n+\n+\treturn !(ok \u003d\u003d exp \u0026\u0026 !fail);\n+}\ndiff --git a/minimal-examples/api-tests/api-test-async-dns/selftest.sh b/minimal-examples/api-tests/api-test-async-dns/selftest.sh\nnew file mode 100755\nindex 0000000..16d1e2e\n--- /dev/null\n+++ b/minimal-examples/api-tests/api-test-async-dns/selftest.sh\n@@ -0,0 +1,24 @@\n+#!/bin/bash\n+#\n+# $1: path to minimal example binaries...\n+# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES\u003d1\n+# that will be ./bin from your build dir\n+#\n+# $2: path for logs and results. The results will go\n+# in a subdir named after the directory this script\n+# is in\n+#\n+# $3: offset for test index count\n+#\n+# $4: total test count\n+#\n+# $5: path to ./minimal-examples dir in lws\n+#\n+# Test return code 0: OK, 254: timed out, other: error indication\n+\n+. $5/selftests-library.sh\n+\n+COUNT_TESTS\u003d1\n+\n+dotest $1 $2 apiselftest\n+exit $FAILS\ndiff --git a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c\nindex de348f6..ffd1070 100644\n--- a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c\n+++ b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c\n@@ -307,6 +307,9 @@ int main(int argc, const char **argv)\n \tif (lws_cmdline_option(argc, argv, \u0022-n\u0022))\n \t\tnumbered \u003d 1;\n \n+\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--server\u0022)))\n+\t\ti.address \u003d p;\n+\n \tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--port\u0022)))\n \t\ti.port \u003d atoi(p);\n \ndiff --git a/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt b/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt\nindex cc6ac62..aad6f03 100644\n--- a/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt\n+++ b/minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt\n@@ -63,6 +63,8 @@ ENDMACRO()\n \n set(requirements 1)\n require_lws_config(LWS_WITH_SERVER 1 requirements)\n+require_lws_config(LWS_WITH_CLIENT 1 requirements)\n+\n \n if (requirements)\n \tadd_executable(${SAMP} ${SRCS})\ndiff --git a/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c b/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c\nindex 5689563..ae28835 100644\n--- a/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c\n+++ b/minimal-examples/raw/minimal-raw-adopt-udp/minimal-raw-adopt-udp.c\n@@ -169,7 +169,7 @@ int main(int argc, const char **argv)\n \t/*\n \t * Create our own \u0022foreign\u0022 UDP socket bound to 7681/udp\n \t */\n-\tif (!lws_create_adopt_udp(vhost, 7681, LWS_CAUDP_BIND,\n+\tif (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND,\n \t\t\t\t protocols[0].name, NULL)) {\n \t\tlwsl_err(\u0022%s: foreign socket adoption failed\u005cn\u0022, __func__);\n \t\tgoto bail;\n","s":{"c":1756926951,"u": 55157}}
],"g": 69157,"chitpc": 0,"ehitpc": 0,"indexed":0
,
"ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}