mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
asynchronous dns for ipv4 and ipv6
This adds the option to have lws do its own dns resolution on the event loop, without blocking. Existing implementations get the name resolution done by the libc, which is blocking. In the case you are opening client connections but need to carefully manage latency, another connection opening and doing the name resolution becomes a big problem. Currently it supports - ipv4 / A records - ipv6 / AAAA records - ipv4-over-ipv6 ::ffff:1.2.3.4 A record promotion for ipv6 - only one server supported over UDP :53 - nameserver discovery on linux, windows, freertos It also has some nice advantages - lws-style paranoid response parsing - random unique tid generation to increase difficulty of poisoning - it's really integrated with the lws event loop, it does not spawn threads or use the libc resolver, and of course no blocking at all - platform-specific server address capturing (from /etc/resolv.conf on linux, windows apis on windows) - it has LRU caching - piggybacking (multiple requests before the first completes go on a list on the first request, not spawn multiple requests) - observes TTL in cache - TTL and timeout use lws_sul timers on the event loop - ipv6 pieces only built if cmake LWS_IPV6 enabled
This commit is contained in:
parent
8b37f98feb
commit
c591e1adfc
33 changed files with 3219 additions and 568 deletions
|
@ -4,7 +4,7 @@ env:
|
|||
global:
|
||||
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
|
||||
matrix:
|
||||
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1"
|
||||
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1 -DLWS_WITH_SYS_ASYNC_DNS=1"
|
||||
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_LWS_DSH=1"
|
||||
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_JOSE=1 -DCMAKE_BUILD_TYPE=DEBUG"
|
||||
|
@ -13,7 +13,7 @@ env:
|
|||
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=nonetwork CMAKE_ARGS="-DLWS_WITH_NETWORK=0"
|
||||
- LWS_METHOD=libev CMAKE_ARGS="-DLWS_WITH_LIBEV=ON"
|
||||
- LWS_METHOD=noipv6 CMAKE_ARGS="-DLWS_IPV6=OFF"
|
||||
- LWS_METHOD=ipv6 CMAKE_ARGS="-DLWS_IPV6=ON"
|
||||
- LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF"
|
||||
- LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON"
|
||||
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
|
||||
|
|
|
@ -47,6 +47,7 @@ option(LWS_WITH_HTTP_BROTLI "Also offer brotli http stream compression (requires
|
|||
option(LWS_WITH_ACME "Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)" OFF)
|
||||
option(LWS_WITH_HUBBUB "Enable libhubbub rewriting support" OFF)
|
||||
option(LWS_WITH_FTS "Full Text Search support" OFF)
|
||||
option(LWS_WITH_SYS_ASYNC_DNS "Nonblocking internal IPv4 DNS resolver" OFF)
|
||||
#
|
||||
# TLS library options... all except mbedTLS are basically OpenSSL variants.
|
||||
#
|
||||
|
@ -76,6 +77,7 @@ option(LWS_WITH_ESP32 "Build for ESP32" OFF)
|
|||
option(LWS_WITH_ESP32_HELPER "Build ESP32 helper" OFF)
|
||||
option(LWS_PLAT_OPTEE "Build for OPTEE" OFF)
|
||||
option(LWS_PLAT_FREERTOS "Build for FreeRTOS" OFF)
|
||||
option(LWS_PLAT_ANDROID "Android flavour of unix platform" OFF)
|
||||
|
||||
#
|
||||
# Client / Server / Test Apps build control
|
||||
|
@ -351,7 +353,7 @@ if (LWS_WITH_MBEDTLS)
|
|||
include_directories(lib/tls/mbedtls/wrapper/include)
|
||||
endif()
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
if (LWS_PLAT_FREERTOS)
|
||||
include_directories(lib/plat/freertos lib/plat/freertos/esp32)
|
||||
|
@ -1022,6 +1024,12 @@ if (LWS_WITH_NETWORK)
|
|||
lib/roles/pipe/ops-pipe.c
|
||||
)
|
||||
|
||||
if (LWS_WITH_SYS_ASYNC_DNS)
|
||||
list(APPEND SOURCES
|
||||
lib/system/async-dns/async-dns.c
|
||||
lib/system/async-dns/async-dns-parse.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LWS_DSH)
|
||||
list(APPEND SOURCES
|
||||
lib/core-net/lws-dsh.c)
|
||||
|
@ -1334,6 +1342,7 @@ else()
|
|||
lib/plat/freertos/freertos-pipe.c
|
||||
lib/plat/freertos/freertos-service.c
|
||||
lib/plat/freertos/freertos-sockets.c
|
||||
lib/plat/freertos/freertos-resolv.c
|
||||
lib/misc/romfs.c)
|
||||
if (LWS_WITH_ESP32_HELPER)
|
||||
list(APPEND SOURCES lib/plat/freertos/esp32/esp32-helpers.c)
|
||||
|
@ -1358,6 +1367,13 @@ else()
|
|||
lib/plat/unix/unix-sockets.c
|
||||
lib/plat/unix/unix-fds.c
|
||||
)
|
||||
if (LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (LWS_PLAT_ANDROID)
|
||||
list(APPEND SOURCES lib/plat/unix/android/android-resolv.c)
|
||||
else()
|
||||
list(APPEND SOURCES lib/plat/unix/unix-resolv.c)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_PLUGINS AND LWS_WITH_LIBUV)
|
||||
|
|
13
LICENSE
13
LICENSE
|
@ -6,15 +6,16 @@ them.
|
|||
|
||||
Original liberal license retained:
|
||||
|
||||
- lib/misc/sha-1.c - 3-clause BSD license retained, link to original
|
||||
- win32port/zlib - ZLIB license (see zlib.h)
|
||||
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
|
||||
- lib/misc/sha-1.c - 3-clause BSD license retained, link to original
|
||||
- win32port/zlib - ZLIB license (see zlib.h)
|
||||
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
|
||||
- lib/misc/base64-decode.c - already MIT
|
||||
|
||||
Relicensed to MIT:
|
||||
|
||||
- lib/misc/base64-decode.c - relicensed, link to original
|
||||
- lib/misc/daemonize.c - relicensed from Public Domain to MIT,
|
||||
link to original Public Domain version
|
||||
- lib/misc/daemonize.c - relicensed from Public Domain to MIT,
|
||||
link to original Public Domain version
|
||||
- lib/plat/windows/windows-resolv.c - relicensed from "Beerware v42" to MIT
|
||||
|
||||
Public Domain (CC-zero) to simplify reuse:
|
||||
|
||||
|
|
76
READMEs/README.async-dns.md
Normal file
76
READMEs/README.async-dns.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Asynchronous DNS
|
||||
|
||||
## Introduction
|
||||
|
||||
Lws now features optional asynchronous, ie, nonblocking DNS
|
||||
resolution done on the event loop, enable `-DLWS_WITH_SYS_ASYNC_DNS=1`
|
||||
at cmake to build it in.
|
||||
|
||||
## Description
|
||||
|
||||
The default libc name resolution is via libc `getaddrinfo()`, which is
|
||||
blocking, possibly for quite long periods (seconds). If you are
|
||||
taking care about latency, but want to create outgoing connections,
|
||||
you can't tolerate this exception from the rule that everything in
|
||||
lws is nonblocking.
|
||||
|
||||
Lws' asynchronous DNS resolver creates a caching name resolver
|
||||
that directly queries the configured nameserver itself over UDP,
|
||||
from the event loop.
|
||||
|
||||
It supports both ipv4 / A records and ipv6 / AAAA records (see later
|
||||
for a description about how). One server supported over UDP :53,
|
||||
and the nameserver is autodicovered on linux, windows, and freertos.
|
||||
|
||||
Other features
|
||||
|
||||
- lws-style paranoid response parsing
|
||||
- random unique tid generation to increase difficulty of poisoning
|
||||
- it's really integrated with the lws event loop, it does not spawn
|
||||
threads or use the libc resolver, and of course no blocking at all
|
||||
- platform-specific server address capturing (from /etc/resolv.conf
|
||||
on linux, windows apis on windows)
|
||||
- LRU caching
|
||||
- piggybacking (multiple requests before the first completes go on
|
||||
a list on the first request, not spawn multiple requests)
|
||||
- observes TTL in cache
|
||||
- TTL and timeout use `lws_sul` timers on the event loop
|
||||
- ipv6 pieces only built if cmake `LWS_IPV6` enabled
|
||||
|
||||
## Api
|
||||
|
||||
If enabled at cmake, the async DNS implementation is used automatically
|
||||
for lws client connections. It's also possible to call it directly, see
|
||||
the api-test-async-dns example for how.
|
||||
|
||||
The Api follows that of `getaddrinfo()` but results are not created on
|
||||
the heap. Instead a single, const cached copy of the addrinfo struct
|
||||
chain is reference-counted, with `lws_async_dns_freeaddrinfo()` provided
|
||||
to deduct from the reference count. Cached items with a nonzero
|
||||
reference count can't be destroyed from the cache, so it's safe to keep
|
||||
a pointer to the results and iterate through them.
|
||||
|
||||
## Dealing with IPv4 and IPv6
|
||||
|
||||
DNS is a very old standard that has some quirks... one of them is that
|
||||
multiple queries are not supported in one packet, even though the protocol
|
||||
suggests it is. This creates problems on ipv6 enabled systems, where
|
||||
it may prefer to have AAAA results, but the server may only have A records.
|
||||
|
||||
To square the circle, for ipv4 only systems (`LWS_IPV6=0`) the resolver
|
||||
requests only A records. For ipv6-capable systems, it always requests
|
||||
first A and then immediately afterwards AAAA records.
|
||||
|
||||
To simplify the implementation, the tid b0 is used to differentiate
|
||||
between A (b0 = 0) and AAAA (b0 = 1) requests and responses using the
|
||||
same query body.
|
||||
|
||||
The first response to come back is parsed, and a cache entry made...
|
||||
it leaves a note in the query about the address of the last `struct addrinfo`
|
||||
record. When the second response comes, a second allocation is made,
|
||||
but not added to the logical cache... instead it's chained on to the
|
||||
first cache entry and the `struct addrinfo` linked-list from the
|
||||
first cache entry is extended into the second one. At the time the
|
||||
second result arrives, the query is destroyed and the cached results
|
||||
provided on the result callback.
|
||||
|
|
@ -95,6 +95,7 @@
|
|||
#cmakedefine LWS_WITH_ABSTRACT
|
||||
#cmakedefine LWS_WITH_ACCESS_LOG
|
||||
#cmakedefine LWS_WITH_ACME
|
||||
#cmakedefine LWS_WITH_SYS_ASYNC_DNS
|
||||
#cmakedefine LWS_WITH_BORINGSSL
|
||||
#cmakedefine LWS_WITH_CGI
|
||||
#cmakedefine LWS_WITH_CUSTOM_HEADERS
|
||||
|
|
|
@ -314,6 +314,7 @@ lws_pthread_mutex_unlock(pthread_mutex_t *lock)
|
|||
#ifndef lws_container_of
|
||||
#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
|
||||
#endif
|
||||
#define LWS_ALIGN_TO(x, bou) x += ((bou) - ((x) % (bou))) % (bou)
|
||||
|
||||
struct lws;
|
||||
|
||||
|
@ -569,6 +570,7 @@ struct lws;
|
|||
#include <libwebsockets/abstract/abstract.h>
|
||||
|
||||
#include <libwebsockets/lws-test-sequencer.h>
|
||||
#include <libwebsockets/lws-async-dns.h>
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
|
|
|
@ -173,6 +173,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
|
|||
* lws_create_adopt_udp() - create, bind and adopt a UDP socket
|
||||
*
|
||||
* \param vhost: lws vhost
|
||||
* \param ads: NULL or address to do dns lookup on
|
||||
* \param port: UDP port to bind to, -1 means unbound
|
||||
* \param flags: 0 or LWS_CAUDP_NO_BIND
|
||||
* \param protocol_name: Name of protocol on vhost to bind wsi to
|
||||
|
@ -182,6 +183,7 @@ lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
|
|||
* returns NULL, having cleaned up any new wsi pieces.
|
||||
* */
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi);
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
|
||||
int flags, const char *protocol_name,
|
||||
struct lws *parent_wsi);
|
||||
///@}
|
||||
|
|
79
include/libwebsockets/lws-async-dns.h
Normal file
79
include/libwebsockets/lws-async-dns.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef enum dns_query_type {
|
||||
LWS_ADNS_RECORD_A = 0x01,
|
||||
LWS_ADNS_RECORD_CNAME = 0x05,
|
||||
LWS_ADNS_RECORD_MX = 0x0f,
|
||||
LWS_ADNS_RECORD_AAAA = 0x1c,
|
||||
} adns_query_type_t;
|
||||
|
||||
typedef enum {
|
||||
LADNS_RET_FAILED_WSI_CLOSED = -4,
|
||||
LADNS_RET_NXDOMAIN = -3,
|
||||
LADNS_RET_TIMEDOUT = -2,
|
||||
LADNS_RET_FAILED = -1,
|
||||
LADNS_RET_FOUND,
|
||||
LADNS_RET_CONTINUING
|
||||
} lws_async_dns_retcode_t;
|
||||
|
||||
typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads,
|
||||
const struct addrinfo *result, int n,
|
||||
void *opaque);
|
||||
|
||||
/**
|
||||
* lws_async_dns_query() - perform a dns lookup using async dns
|
||||
*
|
||||
* \param context: the lws_context
|
||||
* \param tsi: thread service index (usually 0)
|
||||
* \param name: DNS name to look up
|
||||
* \param qtype: type of query (A, AAAA etc)
|
||||
* \param cb: query completion callback
|
||||
* \param wsi: wsi if the query is related to one
|
||||
*
|
||||
* Starts an asynchronous DNS lookup, on completion the \p cb callback will
|
||||
* be called.
|
||||
*
|
||||
* The reference count on the cached object is incremented for every callback
|
||||
* that was called with the cached addrinfo results.
|
||||
*
|
||||
* The cached object can't be evicted until the reference count reaches zero...
|
||||
* use lws_async_dns_freeaddrinfo() to indicate you're finsihed with the
|
||||
* results for each callback that happened with them.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN lws_async_dns_retcode_t
|
||||
lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
|
||||
adns_query_type_t qtype, lws_async_dns_cb_t cb,
|
||||
struct lws *wsi, void *opaque);
|
||||
|
||||
/**
|
||||
* lws_async_dns_freeaddrinfo() - decrement refcount on cached addrinfo results
|
||||
*
|
||||
* \param ai: the first addrinfo returned as result in the callback
|
||||
*
|
||||
* Decrements the cache object's reference count. When it reaches zero, the
|
||||
* cached object may be reaped subject to LRU rules.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_async_dns_freeaddrinfo(const struct addrinfo *ai);
|
|
@ -72,6 +72,11 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
|
|||
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
if (vhost->context->detailed_latency_cb)
|
||||
new_wsi->detlat.earliest_write_req_pre_write = lws_now_usecs();
|
||||
#endif
|
||||
|
||||
/* initialize the instance struct */
|
||||
|
||||
lwsi_set_state(new_wsi, LRS_UNCONNECTED);
|
||||
|
@ -107,34 +112,15 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi)
|
|||
|
||||
/* if not a socket, it's a raw, non-ssl file descriptor */
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
||||
lws_sock_file_fd_type fd, const char *vh_prot_name,
|
||||
struct lws *parent)
|
||||
static struct lws *
|
||||
lws_adopt_descriptor_vhost1(struct lws_vhost *vh, lws_adoption_type type,
|
||||
const char *vh_prot_name, struct lws *parent)
|
||||
{
|
||||
struct lws_context *context = vh->context;
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws *new_wsi;
|
||||
int n;
|
||||
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
struct lws_peer *peer = NULL;
|
||||
|
||||
if (type & LWS_ADOPT_SOCKET) {
|
||||
peer = lws_get_or_create_peer(vh, fd.sockfd);
|
||||
|
||||
if (peer && context->ip_limit_wsi &&
|
||||
peer->count_wsi >= context->ip_limit_wsi) {
|
||||
lwsl_notice("Peer reached wsi limit %d\n",
|
||||
context->ip_limit_wsi);
|
||||
lws_stats_bump(&context->pt[0],
|
||||
LWSSTATS_C_PEER_LIMIT_WSI_DENIED,
|
||||
1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Notice that in SMP case, the wsi may be being created on an
|
||||
* entirely different pt / tsi for load balancing. In that case as
|
||||
|
@ -145,15 +131,9 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
if (parent)
|
||||
n = parent->tsi;
|
||||
new_wsi = lws_create_new_server_wsi(vh, n);
|
||||
if (!new_wsi) {
|
||||
if (type & LWS_ADOPT_SOCKET)
|
||||
compatible_close(fd.sockfd);
|
||||
if (!new_wsi)
|
||||
return NULL;
|
||||
}
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
if (peer)
|
||||
lws_peer_add_wsi(context, peer, new_wsi);
|
||||
#endif
|
||||
|
||||
pt = &context->pt[(int)new_wsi->tsi];
|
||||
lws_stats_bump(pt, LWSSTATS_C_CONNECTIONS, 1);
|
||||
|
||||
|
@ -163,26 +143,6 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
parent->child_list = new_wsi;
|
||||
}
|
||||
|
||||
/* enforce that every fd is nonblocking */
|
||||
|
||||
if (type & LWS_ADOPT_SOCKET) {
|
||||
if (lws_plat_set_nonblocking(fd.sockfd)) {
|
||||
lwsl_err("%s: unable to set sockfd nonblocking\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
#if !defined(WIN32)
|
||||
else
|
||||
if (lws_plat_set_nonblocking(fd.filefd)) {
|
||||
lwsl_err("%s: unable to set filefd nonblocking\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
|
||||
new_wsi->desc = fd;
|
||||
|
||||
if (vh_prot_name) {
|
||||
new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost,
|
||||
vh_prot_name);
|
||||
|
@ -197,14 +157,60 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
}
|
||||
}
|
||||
|
||||
if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_SOCKET))
|
||||
type &= ~LWS_ADOPT_ALLOW_SSL;
|
||||
|
||||
if (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) {
|
||||
lwsl_err("Unable to find a role that can adopt descriptor type 0x%x\n", type);
|
||||
lwsl_err("%s: no role for desc type 0x%x\n", __func__, type);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
return new_wsi;
|
||||
|
||||
bail:
|
||||
lwsl_notice("%s: exiting on bail\n", __func__);
|
||||
if (parent)
|
||||
parent->child_list = new_wsi->sibling_list;
|
||||
if (new_wsi->user_space)
|
||||
lws_free(new_wsi->user_space);
|
||||
|
||||
vh->context->count_wsi_allocated--;
|
||||
|
||||
lws_vhost_unbind_wsi(new_wsi);
|
||||
lws_free(new_wsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct lws *
|
||||
lws_adopt_descriptor_vhost2(struct lws *new_wsi, lws_adoption_type type,
|
||||
lws_sock_file_fd_type fd)
|
||||
{
|
||||
struct lws_context_per_thread *pt =
|
||||
&new_wsi->context->pt[(int)new_wsi->tsi];
|
||||
int n;
|
||||
|
||||
/* enforce that every fd is nonblocking */
|
||||
|
||||
if (type & LWS_ADOPT_SOCKET) {
|
||||
if (lws_plat_set_nonblocking(fd.sockfd)) {
|
||||
lwsl_err("%s: unable to set sockfd %d nonblocking\n",
|
||||
__func__, fd.sockfd);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#if !defined(WIN32)
|
||||
else
|
||||
if (lws_plat_set_nonblocking(fd.filefd)) {
|
||||
lwsl_err("%s: unable to set filefd nonblocking\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
new_wsi->desc = fd;
|
||||
|
||||
if (!LWS_SSL_ENABLED(new_wsi->vhost) ||
|
||||
!(type & LWS_ADOPT_SOCKET))
|
||||
type &= ~LWS_ADOPT_ALLOW_SSL;
|
||||
|
||||
/*
|
||||
* A new connection was accepted. Give the user a chance to
|
||||
* set properties of the newly created wsi. There's no protocol
|
||||
|
@ -217,8 +223,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)];
|
||||
|
||||
#if !defined(LWS_AMAZON_RTOS)
|
||||
if (context->event_loop_ops->accept)
|
||||
if (context->event_loop_ops->accept(new_wsi))
|
||||
if (new_wsi->context->event_loop_ops->accept)
|
||||
if (new_wsi->context->event_loop_ops->accept(new_wsi))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
|
@ -233,7 +239,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
|
||||
if (!(type & LWS_ADOPT_ALLOW_SSL)) {
|
||||
lws_pt_lock(pt, __func__);
|
||||
if (__insert_wsi_socket_into_fds(context, new_wsi)) {
|
||||
if (__insert_wsi_socket_into_fds(new_wsi->context, new_wsi)) {
|
||||
lws_pt_unlock(pt);
|
||||
lwsl_err("%s: fail inserting socket\n", __func__);
|
||||
goto fail;
|
||||
|
@ -259,7 +265,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
/* role may need to do something after all adoption completed */
|
||||
|
||||
lws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH,
|
||||
vh_prot_name);
|
||||
new_wsi->protocol->name);
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
/* its actual pt can service it now */
|
||||
|
@ -277,22 +283,48 @@ fail:
|
|||
"adopt skt fail");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bail:
|
||||
lwsl_notice("%s: exiting on bail\n", __func__);
|
||||
if (parent)
|
||||
parent->child_list = new_wsi->sibling_list;
|
||||
if (new_wsi->user_space)
|
||||
lws_free(new_wsi->user_space);
|
||||
|
||||
vh->context->count_wsi_allocated--;
|
||||
/* if not a socket, it's a raw, non-ssl file descriptor */
|
||||
|
||||
lws_vhost_unbind_wsi(new_wsi);
|
||||
lws_free(new_wsi);
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
||||
lws_sock_file_fd_type fd, const char *vh_prot_name,
|
||||
struct lws *parent)
|
||||
{
|
||||
struct lws *new_wsi;
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
struct lws_peer *peer = NULL;
|
||||
|
||||
compatible_close(fd.sockfd);
|
||||
if (type & LWS_ADOPT_SOCKET) {
|
||||
peer = lws_get_or_create_peer(vh, fd.sockfd);
|
||||
|
||||
return NULL;
|
||||
if (peer && vh->context->ip_limit_wsi &&
|
||||
peer->count_wsi >= vh->context->ip_limit_wsi) {
|
||||
lwsl_notice("Peer reached wsi limit %d\n",
|
||||
vh->context->ip_limit_wsi);
|
||||
lws_stats_bump(&vh->context->pt[0],
|
||||
LWSSTATS_C_PEER_LIMIT_WSI_DENIED,
|
||||
1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
new_wsi = lws_adopt_descriptor_vhost1(vh, type, vh_prot_name, parent);
|
||||
if (!new_wsi) {
|
||||
if (type & LWS_ADOPT_SOCKET)
|
||||
compatible_close(fd.sockfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
if (peer)
|
||||
lws_peer_add_wsi(vh->context, peer, new_wsi);
|
||||
#endif
|
||||
|
||||
return lws_adopt_descriptor_vhost2(new_wsi, type, fd);
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
|
@ -377,76 +409,182 @@ bail:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi)
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
static struct lws *
|
||||
lws_create_adopt_udp2(struct lws *wsi, const char *ads,
|
||||
const struct addrinfo *r, int n, void *opaque)
|
||||
{
|
||||
lws_sock_file_fd_type sock;
|
||||
|
||||
assert(wsi);
|
||||
|
||||
if (!wsi->dns_results)
|
||||
wsi->dns_results_next = wsi->dns_results = r;
|
||||
|
||||
if (n < 0 || !r)
|
||||
goto bail;
|
||||
|
||||
while (wsi->dns_results_next) {
|
||||
|
||||
/*
|
||||
* We have done the dns lookup, identify the result we want
|
||||
* if any, and then complete the adoption by binding wsi to
|
||||
* socket opened on it.
|
||||
*
|
||||
* Ignore the weak assumptions about protocol driven by port
|
||||
* number and force to DGRAM / UDP since that's what this
|
||||
* function is for.
|
||||
*/
|
||||
|
||||
sock.sockfd = socket(wsi->dns_results_next->ai_family,
|
||||
SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (sock.sockfd == LWS_SOCK_INVALID)
|
||||
goto resume;
|
||||
|
||||
if (wsi->do_bind &&
|
||||
bind(sock.sockfd, wsi->dns_results_next->ai_addr,
|
||||
#if defined(_WIN32)
|
||||
(int)wsi->dns_results_next->ai_addrlen
|
||||
#else
|
||||
wsi->dns_results_next->ai_addrlen
|
||||
#endif
|
||||
) == -1) {
|
||||
lwsl_notice("%s: bind failed\n", __func__);
|
||||
goto resume;
|
||||
}
|
||||
|
||||
if (!wsi->do_bind) {
|
||||
((struct sockaddr_in *)wsi->dns_results_next->ai_addr)->
|
||||
sin_port = htons(wsi->c_port);
|
||||
|
||||
if (connect(sock.sockfd, wsi->dns_results_next->ai_addr,
|
||||
wsi->dns_results_next->ai_addrlen) == -1) {
|
||||
lwsl_err("%s: conn fd %d fam %d %s:%u failed "
|
||||
"(salen %d) errno %d\n", __func__,
|
||||
sock.sockfd,
|
||||
wsi->dns_results_next->ai_addr->sa_family,
|
||||
ads ? ads : "null", wsi->c_port,
|
||||
(int)wsi->dns_results_next->ai_addrlen,
|
||||
LWS_ERRNO);
|
||||
compatible_close(sock.sockfd);
|
||||
goto resume;
|
||||
}
|
||||
|
||||
memcpy(&wsi->udp->sa, wsi->dns_results_next->ai_addr,
|
||||
wsi->dns_results_next->ai_addrlen);
|
||||
wsi->udp->salen = wsi->dns_results_next->ai_addrlen;
|
||||
}
|
||||
|
||||
/* complete the udp socket adoption flow */
|
||||
|
||||
lws_addrinfo_clean(wsi);
|
||||
return lws_adopt_descriptor_vhost2(wsi,
|
||||
LWS_ADOPT_RAW_SOCKET_UDP, sock);
|
||||
|
||||
resume:
|
||||
wsi->dns_results_next = wsi->dns_results_next->ai_next;
|
||||
}
|
||||
|
||||
lwsl_err("%s: unable to create INET socket\n", __func__);
|
||||
lws_addrinfo_clean(wsi);
|
||||
|
||||
bail:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
|
||||
int flags, const char *protocol_name,
|
||||
struct lws *parent_wsi)
|
||||
{
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
lws_sock_file_fd_type sock;
|
||||
struct addrinfo h, *r, *rp;
|
||||
struct lws *wsi = NULL;
|
||||
char buf[16];
|
||||
int n;
|
||||
struct lws *wsi;
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
h.ai_socktype = SOCK_DGRAM;
|
||||
h.ai_protocol = IPPROTO_UDP;
|
||||
h.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
h.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif
|
||||
lwsl_info("%s: %s:%u\n", __func__, ads ? ads : "null", port);
|
||||
|
||||
lws_snprintf(buf, sizeof(buf), "%u", port);
|
||||
n = getaddrinfo(NULL, buf, &h, &r);
|
||||
if (n) {
|
||||
#if !defined(LWS_PLAT_FREERTOS)
|
||||
lwsl_info("%s: getaddrinfo error: %s\n", __func__, gai_strerror(n));
|
||||
#else
|
||||
lwsl_info("%s: getaddrinfo error: %s\n", __func__, strerror(n));
|
||||
#endif
|
||||
/* create the logical wsi without any valid fd */
|
||||
|
||||
wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_RAW_SOCKET_UDP,
|
||||
protocol_name, parent_wsi);
|
||||
if (!wsi) {
|
||||
lwsl_err("%s: udp wsi creation failed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
wsi->do_bind = !!(flags & LWS_CAUDP_BIND);
|
||||
wsi->c_port = port;
|
||||
|
||||
for (rp = r; rp; rp = rp->ai_next) {
|
||||
sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (sock.sockfd != LWS_SOCK_INVALID)
|
||||
break;
|
||||
}
|
||||
if (!rp) {
|
||||
lwsl_err("%s: unable to create INET socket\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
{
|
||||
struct addrinfo *r, h;
|
||||
char buf[16];
|
||||
int n;
|
||||
|
||||
if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr,
|
||||
#if defined(_WIN32)
|
||||
(int)rp->ai_addrlen
|
||||
#else
|
||||
rp->ai_addrlen
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
h.ai_socktype = SOCK_DGRAM;
|
||||
h.ai_protocol = IPPROTO_UDP;
|
||||
h.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
h.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif
|
||||
) == -1) {
|
||||
lwsl_err("%s: bind failed\n", __func__);
|
||||
goto bail2;
|
||||
|
||||
/* if the dns lookup is synchronous, do the whole thing now */
|
||||
lws_snprintf(buf, sizeof(buf), "%u", port);
|
||||
n = getaddrinfo(ads, buf, &h, &r);
|
||||
if (n) {
|
||||
#if !defined(LWS_PLAT_FREERTOS)
|
||||
lwsl_info("%s: getaddrinfo error: %s\n", __func__,
|
||||
gai_strerror(n));
|
||||
#else
|
||||
lwsl_info("%s: getaddrinfo error: %s\n", __func__, strerror(n));
|
||||
#endif
|
||||
freeaddrinfo(r);
|
||||
goto bail1;
|
||||
}
|
||||
/* complete it immediately after the blocking dns lookup
|
||||
* finished... free r when connect either completed or failed */
|
||||
wsi = lws_create_adopt_udp2(wsi, ads, r, 0, NULL);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
#else
|
||||
if (ads) {
|
||||
/*
|
||||
* with async dns, use the wsi as the point about which to do
|
||||
* the dns lookup and have it call the second part when it's
|
||||
* done.
|
||||
*
|
||||
* Keep a refcount on the results and free it when we connected
|
||||
* or definitively failed.
|
||||
*/
|
||||
if (lws_async_dns_query(vhost->context, 0, ads,
|
||||
LWS_ADNS_RECORD_A,
|
||||
lws_create_adopt_udp2, wsi, NULL) ==
|
||||
LADNS_RET_FAILED) {
|
||||
lwsl_err("%s: async dns failed\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
} else
|
||||
wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, NULL);
|
||||
|
||||
wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
|
||||
protocol_name, parent_wsi);
|
||||
if (!wsi)
|
||||
lwsl_err("%s: udp adoption failed\n", __func__);
|
||||
/* dns lookup is happening asynchronously */
|
||||
|
||||
return wsi;
|
||||
#endif
|
||||
|
||||
bail2:
|
||||
if (!wsi)
|
||||
compatible_close((int)sock.sockfd);
|
||||
bail1:
|
||||
freeaddrinfo(r);
|
||||
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "udp create fail");
|
||||
wsi = NULL;
|
||||
bail:
|
||||
return wsi;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
|
||||
|
|
|
@ -133,6 +133,8 @@ lws_close_trans_q_leader(struct lws_dll2 *d, void *user)
|
|||
void
|
||||
lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)
|
||||
{
|
||||
lws_addrinfo_clean(wsi);
|
||||
|
||||
if (wsi->already_did_cce)
|
||||
return;
|
||||
|
||||
|
@ -149,6 +151,22 @@ lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)
|
|||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
lws_addrinfo_clean(struct lws *wsi)
|
||||
{
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (!wsi->dns_results)
|
||||
return;
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
lws_async_dns_freeaddrinfo(wsi->dns_results);
|
||||
#else
|
||||
freeaddrinfo((struct addrinfo *)wsi->dns_results);
|
||||
#endif
|
||||
wsi->dns_results = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
__lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
|
||||
const char *caller)
|
||||
|
@ -176,6 +194,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
|
|||
|
||||
lws_free_set_NULL(wsi->cli_hostname_copy);
|
||||
|
||||
lws_addrinfo_clean(wsi);
|
||||
|
||||
/*
|
||||
* if we have wsi in our transaction queue, if we are closing we
|
||||
* must go through and close all those first
|
||||
|
@ -317,6 +337,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
|
|||
}
|
||||
|
||||
if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
|
||||
lwsi_state(wsi) == LRS_WAITING_DNS ||
|
||||
lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
|
||||
goto just_kill_connection;
|
||||
|
||||
|
@ -347,6 +368,10 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
|
|||
|
||||
just_kill_connection:
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
lws_async_dns_cancel(wsi);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
if (wsi->http.buflist_post_body)
|
||||
lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
|
||||
|
@ -371,6 +396,7 @@ just_kill_connection:
|
|||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY ||
|
||||
lwsi_state(wsi) == LRS_WAITING_DNS ||
|
||||
lwsi_state(wsi) == LRS_WAITING_CONNECT) &&
|
||||
!wsi->already_did_cce && wsi->protocol)
|
||||
lws_inform_client_conn_fail(wsi,
|
||||
|
|
|
@ -165,14 +165,6 @@ enum pmd_return {
|
|||
PMDR_FAILED = -1
|
||||
};
|
||||
|
||||
typedef union {
|
||||
#ifdef LWS_WITH_IPV6
|
||||
struct sockaddr_in6 sa6;
|
||||
#endif
|
||||
struct sockaddr_in sa4;
|
||||
} sockaddr46;
|
||||
|
||||
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
struct lws_peer {
|
||||
struct lws_peer *next;
|
||||
|
@ -284,7 +276,7 @@ typedef struct lws_dsh_obj_head {
|
|||
|
||||
typedef struct lws_dsh_obj {
|
||||
lws_dll2_t list; /* must be first */
|
||||
struct lws_dsh *dsh; /* invalid when on free list */
|
||||
struct lws_dsh *dsh; /* invalid when on free list */
|
||||
size_t size; /* invalid when on free list */
|
||||
size_t asize;
|
||||
} lws_dsh_obj_t;
|
||||
|
@ -306,6 +298,34 @@ typedef struct lws_dsh {
|
|||
*/
|
||||
} lws_dsh_t;
|
||||
|
||||
/*
|
||||
* lws_async_dns
|
||||
*/
|
||||
|
||||
typedef struct lws_async_dns {
|
||||
lws_sockaddr46 sa46; /* nameserver */
|
||||
lws_dll2_owner_t waiting_send;
|
||||
lws_dll2_owner_t waiting_resp;
|
||||
lws_dll2_owner_t cached;
|
||||
struct lws *wsi;
|
||||
time_t time_set_server;
|
||||
char dns_server_set;
|
||||
} lws_async_dns_t;
|
||||
|
||||
typedef enum {
|
||||
LADNS_CONF_SERVER_UNKNOWN = -1,
|
||||
LADNS_CONF_SERVER_SAME,
|
||||
LADNS_CONF_SERVER_CHANGED
|
||||
} lws_async_dns_server_check_t;
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
void
|
||||
lws_aysnc_dns_completed(struct lws *wsi, void *sa, size_t salen,
|
||||
lws_async_dns_retcode_t ret);
|
||||
#endif
|
||||
void
|
||||
lws_async_dns_cancel(struct lws *wsi);
|
||||
|
||||
/*
|
||||
* so we can have n connections being serviced simultaneously,
|
||||
* these things need to be isolated per-thread.
|
||||
|
@ -395,6 +415,10 @@ struct lws_context_per_thread {
|
|||
struct lws_signal_watcher w_sigint;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
lws_usec_t ust_left_poll;
|
||||
#endif
|
||||
|
||||
/* --- */
|
||||
|
||||
unsigned long count_conns;
|
||||
|
@ -448,7 +472,6 @@ struct lws_conn_stats {
|
|||
* SSL SNI -> wsi -> bind after SSL negotiation
|
||||
*/
|
||||
|
||||
|
||||
struct lws_vhost {
|
||||
#if defined(LWS_WITH_CLIENT) && defined(LWS_CLIENT_HTTP_PROXYING)
|
||||
char proxy_basic_auth_token[128];
|
||||
|
@ -545,6 +568,10 @@ struct lws_vhost {
|
|||
void
|
||||
__lws_vhost_destroy2(struct lws_vhost *vh);
|
||||
|
||||
/*
|
||||
* struct lws
|
||||
*/
|
||||
|
||||
struct lws {
|
||||
/* structs */
|
||||
|
||||
|
@ -562,11 +589,6 @@ struct lws {
|
|||
struct _lws_dbus_mode_related dbus;
|
||||
#endif
|
||||
|
||||
|
||||
const struct lws_role_ops *role_ops;
|
||||
lws_wsi_state_t wsistate;
|
||||
lws_wsi_state_t wsistate_pre_close;
|
||||
|
||||
/* lifetime members */
|
||||
|
||||
#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \
|
||||
|
@ -577,9 +599,23 @@ struct lws {
|
|||
struct lws_io_watcher w_write;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
lws_detlat_t detlat;
|
||||
#endif
|
||||
|
||||
lws_sorted_usec_list_t sul_timeout;
|
||||
lws_sorted_usec_list_t sul_hrtimer;
|
||||
|
||||
struct lws_dll2 dll_buflist; /* guys with pending rxflow */
|
||||
struct lws_dll2 same_vh_protocol;
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
struct lws_dll2 adns; /* on adns list of guys to tell result */
|
||||
lws_async_dns_cb_t adns_cb; /* callback with result */
|
||||
#endif
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
struct lws_dll2 dll_cli_active_conns;
|
||||
struct lws_dll2_owner dll2_cli_txn_queue_owner;
|
||||
struct lws_dll2 dll2_cli_txn_queue;
|
||||
#endif
|
||||
/* pointers */
|
||||
|
||||
struct lws_context *context;
|
||||
|
@ -587,14 +623,10 @@ struct lws {
|
|||
struct lws *parent; /* points to parent, if any */
|
||||
struct lws *child_list; /* points to first child */
|
||||
struct lws *sibling_list; /* subsequent children at same level */
|
||||
|
||||
const struct lws_role_ops *role_ops;
|
||||
const struct lws_protocols *protocol;
|
||||
struct lws_dll2 same_vh_protocol;
|
||||
|
||||
struct lws_sequencer *seq; /* associated sequencer if any */
|
||||
|
||||
struct lws_dll2 dll_buflist; /* guys with pending rxflow */
|
||||
|
||||
#if defined(LWS_WITH_THREADPOOL)
|
||||
struct lws_threadpool_task *tp_task;
|
||||
#endif
|
||||
|
@ -607,9 +639,8 @@ struct lws {
|
|||
#if defined(LWS_WITH_CLIENT)
|
||||
struct client_info_stash *stash;
|
||||
char *cli_hostname_copy;
|
||||
struct lws_dll2 dll_cli_active_conns;
|
||||
struct lws_dll2_owner dll2_cli_txn_queue_owner;
|
||||
struct lws_dll2 dll2_cli_txn_queue;
|
||||
const struct addrinfo *dns_results;
|
||||
const struct addrinfo *dns_results_next;
|
||||
#endif
|
||||
void *user_space;
|
||||
void *opaque_parent_data;
|
||||
|
@ -629,11 +660,8 @@ struct lws {
|
|||
uint64_t accept_start_us;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef LWS_LATENCY
|
||||
unsigned long action_start;
|
||||
unsigned long latency_start;
|
||||
#endif
|
||||
lws_wsi_state_t wsistate;
|
||||
lws_wsi_state_t wsistate_pre_close;
|
||||
|
||||
/* ints */
|
||||
#define LWS_NO_FDS_POS (-1)
|
||||
|
@ -676,8 +704,10 @@ struct lws {
|
|||
unsigned int protocol_bind_balance:1;
|
||||
unsigned int unix_skt:1;
|
||||
unsigned int close_when_buffered_out_drained:1;
|
||||
unsigned int h1_ws_proxied;
|
||||
unsigned int proxied_ws_parent;
|
||||
unsigned int h1_ws_proxied:1;
|
||||
unsigned int proxied_ws_parent:1;
|
||||
unsigned int do_bind:1;
|
||||
unsigned int oom4:1;
|
||||
|
||||
unsigned int could_have_pending:1; /* detect back-to-back writes */
|
||||
unsigned int outer_will_close:1;
|
||||
|
@ -703,9 +733,7 @@ struct lws {
|
|||
unsigned int sock_send_blocking:1;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
unsigned short c_port;
|
||||
#endif
|
||||
uint16_t c_port;
|
||||
|
||||
/* chars */
|
||||
|
||||
|
@ -993,7 +1021,7 @@ LWS_EXTERN lws_usec_t
|
|||
__lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow);
|
||||
|
||||
LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_client_connect_2(struct lws *wsi);
|
||||
lws_client_connect_2_dnsreq(struct lws *wsi);
|
||||
|
||||
LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
|
||||
|
@ -1077,6 +1105,9 @@ lws_plat_pipe_signal(struct lws *wsi);
|
|||
void
|
||||
lws_plat_pipe_close(struct lws *wsi);
|
||||
|
||||
void
|
||||
lws_addrinfo_clean(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_add_wsi_to_draining_ext_list(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
|
@ -1167,9 +1198,21 @@ lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
|
|||
extern const struct lws_protocols protocol_abs_client_raw_skt,
|
||||
protocol_abs_client_unit_test;
|
||||
|
||||
void
|
||||
__lws_reset_wsi(struct lws *wsi);
|
||||
|
||||
void
|
||||
lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len);
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
lws_async_dns_server_check_t
|
||||
lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa);
|
||||
int
|
||||
lws_async_dns_init(struct lws_context *context);
|
||||
void
|
||||
lws_async_dns_deinit(lws_async_dns_t *dns);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -443,6 +443,9 @@ lws_create_vhost(struct lws_context *context,
|
|||
#if defined(LWS_CLIENT_HTTP_PROXYING) && \
|
||||
defined(LWS_WITH_CLIENT) && defined(LWS_HAVE_GETENV)
|
||||
char *p;
|
||||
#endif
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
extern struct lws_protocols lws_async_dns_protocol;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
|
@ -558,6 +561,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
/*
|
||||
* give the vhost a unified list of protocols including:
|
||||
*
|
||||
* - internal, async_dns if enabled (first vhost only)
|
||||
* - internal, abstracted ones
|
||||
* - the ones that came from plugins
|
||||
* - his user protocols
|
||||
|
@ -594,6 +598,16 @@ lws_create_vhost(struct lws_context *context,
|
|||
vh->count_protocols++;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* 3: async dns protocol (first vhost only)
|
||||
*/
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (!context->vhost_list) {
|
||||
memcpy(&lwsp[m++], &lws_async_dns_protocol,
|
||||
sizeof(struct lws_protocols));
|
||||
vh->count_protocols++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 3: For compatibility, all protocols enabled on vhost if only
|
||||
|
@ -763,6 +777,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
goto bail1;
|
||||
}
|
||||
#endif
|
||||
n = !!context->vhost_list;
|
||||
|
||||
while (1) {
|
||||
if (!(*vh1)) {
|
||||
|
@ -772,6 +787,11 @@ lws_create_vhost(struct lws_context *context,
|
|||
vh1 = &(*vh1)->vhost_next;
|
||||
};
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (!n && lws_async_dns_init(context))
|
||||
goto bail1;
|
||||
#endif
|
||||
|
||||
/* for the case we are adding a vhost much later, after server init */
|
||||
|
||||
if (context->protocol_init_done)
|
||||
|
|
|
@ -639,6 +639,10 @@ lws_context_destroy3(struct lws_context *context)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
lws_async_dns_deinit(&context->async_dns);
|
||||
#endif
|
||||
|
||||
if (context->pt[0].fds)
|
||||
lws_free_set_NULL(context->pt[0].fds);
|
||||
#endif
|
||||
|
|
|
@ -259,34 +259,72 @@ struct lws_deferred_free
|
|||
*/
|
||||
|
||||
struct lws_context {
|
||||
time_t last_ws_ping_pong_check_s;
|
||||
lws_usec_t time_up; /* monotonic */
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
char canonical_hostname[96];
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_FILE_OPS)
|
||||
const struct lws_plat_file_ops *fops;
|
||||
struct lws_plat_file_ops fops_platform;
|
||||
#endif
|
||||
struct lws_context **pcontext_finalize;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
const struct lws_tls_ops *tls_ops;
|
||||
#if defined(LWS_WITH_ZIP_FOPS)
|
||||
struct lws_plat_file_ops fops_zip;
|
||||
#endif
|
||||
|
||||
const char *username, *groupname;
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
|
||||
struct lws_context_per_thread pt[LWS_MAX_SMP];
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
struct http2_settings set;
|
||||
#endif
|
||||
#if defined(LWS_WITH_ZIP_FOPS)
|
||||
struct lws_plat_file_ops fops_zip;
|
||||
#endif
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
struct lws_context_per_thread pt[LWS_MAX_SMP];
|
||||
|
||||
#if defined(LWS_WITH_SERVER_STATUS)
|
||||
struct lws_conn_stats conn_stats;
|
||||
#endif
|
||||
#if LWS_MAX_SMP > 1
|
||||
struct lws_mutex_refcount mr;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_LIBEV)
|
||||
struct lws_context_eventlibs_libev ev;
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
struct lws_context_eventlibs_libuv uv;
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBEVENT)
|
||||
struct lws_context_eventlibs_libevent event;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
struct lws_context_tls tls;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
lws_async_dns_t async_dns;
|
||||
#endif
|
||||
|
||||
/* pointers */
|
||||
|
||||
struct lws_vhost *vhost_list;
|
||||
struct lws_vhost *no_listener_vhost_list;
|
||||
struct lws_vhost *vhost_pending_destruction_list;
|
||||
struct lws_context **pcontext_finalize;
|
||||
const char *username, *groupname;
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
const char *server_string;
|
||||
#endif
|
||||
|
||||
struct lws_event_loop_ops *event_loop_ops;
|
||||
|
||||
#if defined(LWS_WITH_FILE_OPS)
|
||||
const struct lws_plat_file_ops *fops;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
const struct lws_tls_ops *tls_ops;
|
||||
#endif
|
||||
#if defined(LWS_WITH_PLUGINS)
|
||||
struct lws_plugin *plugin_list;
|
||||
#endif
|
||||
|
@ -297,10 +335,7 @@ struct lws_context {
|
|||
struct lws **lws_lookup;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#if LWS_MAX_SMP > 1
|
||||
struct lws_mutex_refcount mr;
|
||||
#endif
|
||||
#endif /* NETWORK */
|
||||
|
||||
#if defined(LWS_AMAZON_RTOS)
|
||||
mbedtls_entropy_context mec;
|
||||
|
@ -329,38 +364,14 @@ struct lws_context {
|
|||
#endif
|
||||
void (*eventlib_signal_cb)(void *event_lib_handle, int signum);
|
||||
|
||||
time_t last_ws_ping_pong_check_s;
|
||||
lws_usec_t time_up; /* monotonic */
|
||||
|
||||
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
|
||||
cap_value_t caps[4];
|
||||
char count_caps;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_NETWORK)
|
||||
#if defined(LWS_WITH_LIBEV)
|
||||
struct lws_context_eventlibs_libev ev;
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
struct lws_context_eventlibs_libuv uv;
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBEVENT)
|
||||
struct lws_context_eventlibs_libevent event;
|
||||
#endif
|
||||
struct lws_event_loop_ops *event_loop_ops;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK)
|
||||
struct lws_context_tls tls;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
char canonical_hostname[128];
|
||||
const char *server_string;
|
||||
#endif
|
||||
|
||||
#ifdef LWS_LATENCY
|
||||
unsigned long worst_latency;
|
||||
char worst_latency_info[256];
|
||||
#endif
|
||||
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
unsigned long time_last_state_dump;
|
||||
uint32_t last_free_heap;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* http://base64.sourceforge.net/b64.c
|
||||
*
|
||||
* with the following license:
|
||||
* already with MIT license, which is retained.
|
||||
*
|
||||
* LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
|
||||
*
|
||||
|
@ -33,9 +33,7 @@
|
|||
* Bob Trower 08/04/01 -- Create Version 0.00.00B
|
||||
*
|
||||
* I cleaned it up quite a bit to match the (linux kernel) style of the rest
|
||||
* of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws
|
||||
* since he explicitly allows sublicensing, but I give the URL above so you can
|
||||
* get the original with Bob's super-liberal terms directly if you prefer.
|
||||
* of libwebsockets
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
42
lib/plat/freertos/freertos-resolv.c
Normal file
42
lib/plat/freertos/freertos-resolv.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
lws_async_dns_server_check_t
|
||||
lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)
|
||||
{
|
||||
uint32_t ipv4;
|
||||
lws_async_dns_server_check_t s = LADNS_CONF_SERVER_CHANGED;
|
||||
|
||||
FreeRTOS_GetAddressConfiguration(NULL, NULL, NULL, &ipv4);
|
||||
|
||||
sa46->sa4.sin_family = AF_INET;
|
||||
if (sa46->sa4.sin_addr.s_addr == ipv4)
|
||||
s = LADNS_CONF_SERVER_SAME;
|
||||
|
||||
sa46->sa4.sin_addr.s_addr = ipv4;
|
||||
|
||||
return s;
|
||||
}
|
|
@ -55,6 +55,9 @@ gai_strerror(int);
|
|||
|
||||
#if defined(LWS_AMAZON_RTOS)
|
||||
#include "FreeRTOS.h"
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
#include "FreeRTOS_IP.h"
|
||||
#endif
|
||||
#include "timers.h"
|
||||
#include <esp_attr.h>
|
||||
#else
|
||||
|
|
54
lib/plat/unix/android/android-resolv.c
Normal file
54
lib/plat/unix/android/android-resolv.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
lws_async_dns_server_check_t
|
||||
lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)
|
||||
{
|
||||
char d[PROP_VALUE_MAX], *p = d;
|
||||
uint32_t ip32;
|
||||
uint8_t i[4];
|
||||
int n;
|
||||
|
||||
d[0] = '\0';
|
||||
if (__system_property_get("net.dns1", d) <= 0)
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
i[n] = atoi(d);
|
||||
p = strchr(d, '.');
|
||||
if (n != 3 && !p)
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
}
|
||||
|
||||
ip32 = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];
|
||||
n = ip32 == sa->sin_addr.s_addr;
|
||||
sa46->sa4.sin_family = AF_INET;
|
||||
sa46->sa4.sin_addr.s_addr = ip32;
|
||||
|
||||
return n ? LADNS_CONF_SERVER_SAME : LADNS_CONF_SERVER_CHANGED;
|
||||
}
|
||||
|
88
lib/plat/unix/unix-resolv.c
Normal file
88
lib/plat/unix/unix-resolv.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
lws_async_dns_server_check_t
|
||||
lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)
|
||||
{
|
||||
lws_async_dns_server_check_t s = LADNS_CONF_SERVER_CHANGED;
|
||||
char resolv[512], ads[48];
|
||||
lws_sockaddr46 sa46t;
|
||||
lws_tokenize_t ts;
|
||||
int fd, n, ns = 0;
|
||||
|
||||
/* grab the first chunk of /etc/resolv.conf */
|
||||
|
||||
fd = open("/etc/resolv.conf", LWS_O_RDONLY);
|
||||
if (fd < 0)
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
|
||||
n = read(fd, resolv, sizeof(resolv) - 1);
|
||||
close(fd);
|
||||
if (n < 0)
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
|
||||
resolv[n] = '\0';
|
||||
lws_tokenize_init(&ts, resolv, LWS_TOKENIZE_F_DOT_NONTERM |
|
||||
LWS_TOKENIZE_F_NO_FLOATS |
|
||||
LWS_TOKENIZE_F_NO_INTEGERS |
|
||||
LWS_TOKENIZE_F_MINUS_NONTERM |
|
||||
LWS_TOKENIZE_F_HASH_COMMENT);
|
||||
do {
|
||||
ts.e = lws_tokenize(&ts);
|
||||
if (ts.e != LWS_TOKZE_TOKEN) {
|
||||
ns = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ns && !strncmp("nameserver", ts.token, ts.token_len)) {
|
||||
ns = 1;
|
||||
continue;
|
||||
}
|
||||
if (!ns)
|
||||
continue;
|
||||
|
||||
/* we are a token just after the "nameserver" token */
|
||||
|
||||
ns = 0;
|
||||
if (ts.token_len > (int)sizeof(ads) - 1)
|
||||
continue;
|
||||
|
||||
memcpy(ads, ts.token, ts.token_len);
|
||||
ads[ts.token_len] = '\0';
|
||||
if (lws_sa46_parse_numeric_address(ads, &sa46t) < 0)
|
||||
continue;
|
||||
|
||||
if (!lws_sa46_compare_ads(sa46, &sa46t))
|
||||
s = LADNS_CONF_SERVER_SAME;
|
||||
|
||||
*sa46 = sa46t;
|
||||
|
||||
return s;
|
||||
|
||||
} while (ts.e > 0);
|
||||
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
}
|
73
lib/plat/windows/windows-resolv.c
Normal file
73
lib/plat/windows/windows-resolv.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Adapted from tadns 1.1, from http://adns.sourceforge.net/
|
||||
* Original license -->
|
||||
*
|
||||
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
|
||||
*
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Sergey Lyubka wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return.
|
||||
*
|
||||
* Integrated into lws, largely rewritten and relicensed (as allowed above)
|
||||
*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
lws_async_dns_server_check_t
|
||||
lws_plat_asyncdns_init(struct lws_context *context, lws_sockaddr46 *sa46)
|
||||
{
|
||||
char subkey[512], dhcpns[512], ns[512], value[128], *key =
|
||||
"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces";
|
||||
HKEY hKey, hSub;
|
||||
LONG err;
|
||||
int i, n;
|
||||
|
||||
if ((err = RegOpenKey(HKEY_LOCAL_MACHINE, key, &hKey)) != ERROR_SUCCESS) {
|
||||
lwsl_err("%s: cannot open reg key %s: %d\n", __func__, key, err);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; RegEnumKey(hKey, i, subkey, sizeof(subkey)) == ERROR_SUCCESS; i++) {
|
||||
DWORD type, len = sizeof(value);
|
||||
|
||||
if (RegOpenKey(hKey, subkey, &hSub) == ERROR_SUCCESS &&
|
||||
(RegQueryValueEx(hSub, "NameServer", 0,
|
||||
&type, value, &len) == ERROR_SUCCESS ||
|
||||
RegQueryValueEx(hSub, "DhcpNameServer", 0,
|
||||
&type, value, &len) == ERROR_SUCCESS)) {
|
||||
n = lws_sa46_parse_numeric_address(value, sa46)
|
||||
RegCloseKey(hSub);
|
||||
RegCloseKey(hKey);
|
||||
return n == 0 ? LADNS_CONF_SERVER_CHANGED :
|
||||
LADNS_CONF_SERVER_UNKNOWN;
|
||||
}
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
|
||||
return LADNS_CONF_SERVER_UNKNOWN;
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
static int
|
||||
lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
|
||||
{
|
||||
|
@ -36,7 +37,7 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
|
|||
if (wsi->ipv6) {
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_V4MAPPED;
|
||||
#endif
|
||||
} else
|
||||
|
@ -48,10 +49,11 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
|
|||
|
||||
return getaddrinfo(ads, NULL, &hints, result);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen)
|
||||
lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback,
|
||||
ssize_t plen)
|
||||
{
|
||||
#if defined(LWS_CLIENT_HTTP_PROXYING)
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
@ -155,9 +157,9 @@ send_hs:
|
|||
lwsl_info("%s: wsi %p: waiting to send hdrs (par state 0x%x)\n",
|
||||
__func__, wsi, lwsi_state(wsi_piggyback));
|
||||
} else {
|
||||
lwsl_info("%s: wsi %p: %s %s client created own conn (raw %d)\n",
|
||||
lwsl_info("%s: wsi %p: %s %s client created own conn (raw %d) vh %s\n",
|
||||
__func__, wsi, wsi->role_ops->name,
|
||||
wsi->protocol->name, rawish);
|
||||
wsi->protocol->name, rawish, wsi->vhost->name);
|
||||
|
||||
/* we are making our own connection */
|
||||
if (!rawish)
|
||||
|
@ -228,38 +230,438 @@ failed:
|
|||
}
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_2(struct lws *wsi)
|
||||
lws_client_connect_3_connect(struct lws *wsi, const char *ads,
|
||||
const struct addrinfo *result, int n, void *opaque)
|
||||
{
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
struct sockaddr_un sau;
|
||||
#endif
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
#if defined(LWS_CLIENT_HTTP_PROXYING)
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
#endif
|
||||
const char *adsin;
|
||||
ssize_t plen = 0;
|
||||
#endif
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
struct sockaddr_un sau;
|
||||
char unix_skt = 0;
|
||||
#endif
|
||||
int n, port = 0;
|
||||
const char *cce = "", *iface;
|
||||
const struct sockaddr *psa;
|
||||
const char *meth = NULL;
|
||||
struct addrinfo *result;
|
||||
const char *ads;
|
||||
sockaddr46 sa46;
|
||||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
char ipv6only = lws_check_opt(wsi->vhost->options,
|
||||
LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
|
||||
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
|
||||
struct sockaddr_in addr;
|
||||
LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
|
||||
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
|
||||
#endif
|
||||
const struct sockaddr *psa = NULL;
|
||||
const char *cce = "", *iface;
|
||||
ssize_t plen = 0;
|
||||
lws_sockaddr46 sa46;
|
||||
char ni[48];
|
||||
int m;
|
||||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
#if defined(__ANDROID__)
|
||||
ipv6only = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* async dns calls back here for everybody who cares when it gets a
|
||||
* result... but if we are piggybacking, we do not want to connect
|
||||
* ourselves
|
||||
*/
|
||||
|
||||
if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue))
|
||||
return wsi;
|
||||
#if 0
|
||||
if (!ads && !result) {
|
||||
cce = "dns resolution failed";
|
||||
if (!wsi->oom4)
|
||||
goto oom4;
|
||||
else
|
||||
goto failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We can check using getsockopt if our connect actually completed
|
||||
*/
|
||||
|
||||
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
|
||||
lws_socket_is_valid(wsi->desc.sockfd)) {
|
||||
socklen_t sl = sizeof(int);
|
||||
int e = 0;
|
||||
|
||||
/*
|
||||
* this resets SO_ERROR after reading it. If there's an error
|
||||
* condition the connect definitively failed.
|
||||
*/
|
||||
|
||||
if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
|
||||
&e, &sl)) {
|
||||
if (!e) {
|
||||
lwsl_info("%s: getsockopt check: conn OK\n",
|
||||
__func__);
|
||||
|
||||
goto conn_good;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: getsockopt says err %d\n", __func__, e);
|
||||
}
|
||||
|
||||
lwsl_debug("%s: getsockopt check: conn fail: errno %d\n",
|
||||
__func__, LWS_ERRNO);
|
||||
goto try_next_result_fds;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (*ads == '+') {
|
||||
ads++;
|
||||
memset(&sau, 0, sizeof(sau));
|
||||
sau.sun_family = AF_UNIX;
|
||||
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
|
||||
sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
|
||||
|
||||
lwsl_info("%s: Unix skt: %s\n", __func__, ads);
|
||||
|
||||
if (sau.sun_path[0] == '@')
|
||||
sau.sun_path[0] = '\0';
|
||||
|
||||
goto ads_known;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
if (n == LADNS_RET_FAILED) {
|
||||
lwsl_notice("%s: adns failed %s\n", __func__, ads);
|
||||
goto oom4;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!wsi->dns_results) {
|
||||
wsi->dns_results_next = wsi->dns_results = result;
|
||||
if (result)
|
||||
lwsl_debug("%s: result %p result->ai_next %p\n",
|
||||
__func__, result, result->ai_next);
|
||||
}
|
||||
|
||||
#if defined(LWS_CLIENT_HTTP_PROXYING) && \
|
||||
(defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
|
||||
|
||||
/* Decide what it is we need to connect to:
|
||||
*
|
||||
* Priority 1: connect to http proxy */
|
||||
|
||||
if (wsi->vhost->http.http_proxy_port) {
|
||||
plen = lws_snprintf((char *)pt->serv_buf, 256,
|
||||
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
|
||||
"User-agent: libwebsockets\x0d\x0a",
|
||||
ads, wsi->c_port);
|
||||
|
||||
if (wsi->vhost->proxy_basic_auth_token[0])
|
||||
plen += lws_snprintf((char *)pt->serv_buf + plen, 256,
|
||||
"Proxy-authorization: basic %s\x0d\x0a",
|
||||
wsi->vhost->proxy_basic_auth_token);
|
||||
|
||||
plen += lws_snprintf((char *)pt->serv_buf + plen, 5, "\x0d\x0a");
|
||||
ads = wsi->vhost->http.http_proxy_address;
|
||||
wsi->c_port = wsi->vhost->http.http_proxy_port;
|
||||
#else
|
||||
if (0) {
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
|
||||
/* Priority 2: Connect to SOCK5 Proxy */
|
||||
|
||||
} else if (wsi->vhost->socks_proxy_port) {
|
||||
if (socks_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) {
|
||||
cce = "socks msg too large";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
lwsl_client("Sending SOCKS Greeting\n");
|
||||
ads = wsi->vhost->socks_proxy_address;
|
||||
wsi->c_port = wsi->vhost->socks_proxy_port;
|
||||
#endif
|
||||
}
|
||||
|
||||
memset(&sa46, 0, sizeof(sa46));
|
||||
|
||||
if (n || !wsi->dns_results) {
|
||||
/* lws_getaddrinfo46 failed, there is no usable result */
|
||||
lwsl_notice("%s: lws_getaddrinfo46 failed %d\n",
|
||||
__func__, n);
|
||||
cce = "ipv6 lws_getaddrinfo46 failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's try connecting to each of the results in turn until one works
|
||||
* or we run out of results
|
||||
*/
|
||||
|
||||
next_result:
|
||||
|
||||
psa = (const struct sockaddr *)&sa46;
|
||||
n = sizeof(sa46);
|
||||
memset(&sa46, 0, sizeof(sa46));
|
||||
|
||||
switch (wsi->dns_results_next->ai_family) {
|
||||
case AF_INET:
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
if (ipv6only) {
|
||||
sa46.sa4.sin_family = AF_INET6;
|
||||
|
||||
/* map IPv4 to IPv6 */
|
||||
memset((char *)&sa46.sa6.sin6_addr, 0,
|
||||
sizeof(sa46.sa6.sin6_addr));
|
||||
sa46.sa6.sin6_addr.s6_addr[10] = 0xff;
|
||||
sa46.sa6.sin6_addr.s6_addr[11] = 0xff;
|
||||
memcpy(&sa46.sa6.sin6_addr.s6_addr[12],
|
||||
&((struct sockaddr_in *)
|
||||
wsi->dns_results_next->ai_addr)->sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
sa46.sa6.sin6_port = htons(wsi->c_port);
|
||||
ni[0] = '\0';
|
||||
lws_write_numeric_address(sa46.sa6.sin6_addr.s6_addr,
|
||||
16, ni, sizeof(ni));
|
||||
lwsl_info("%s: %s ipv4->ipv6 %s\n", __func__, ads, ni);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
sa46.sa4.sin_family = AF_INET;
|
||||
sa46.sa4.sin_addr.s_addr =
|
||||
((struct sockaddr_in *)wsi->dns_results_next->ai_addr)->
|
||||
sin_addr.s_addr;
|
||||
memset(&sa46.sa4.sin_zero, 0, sizeof(sa46.sa4.sin_zero));
|
||||
sa46.sa4.sin_port = htons(wsi->c_port);
|
||||
n = sizeof(struct sockaddr_in);
|
||||
lws_write_numeric_address((uint8_t *)&sa46.sa4.sin_addr.s_addr,
|
||||
4, ni, sizeof(ni));
|
||||
lwsl_info("%s: %s ipv4 %s\n", __func__, ads, ni);
|
||||
break;
|
||||
case AF_INET6:
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
if (!wsi->ipv6)
|
||||
goto try_next_result;
|
||||
sa46.sa4.sin_family = AF_INET6;
|
||||
memcpy(&sa46.sa6.sin6_addr,
|
||||
&((struct sockaddr_in6 *)wsi->dns_results_next->ai_addr)->
|
||||
sin6_addr, sizeof(struct in6_addr));
|
||||
sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)
|
||||
wsi->dns_results_next->ai_addr)->sin6_scope_id;
|
||||
sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)
|
||||
wsi->dns_results_next->ai_addr)->sin6_flowinfo;
|
||||
sa46.sa6.sin6_port = htons(wsi->c_port);
|
||||
lws_write_numeric_address((uint8_t *)&sa46.sa6.sin6_addr,
|
||||
16, ni, sizeof(ni));
|
||||
lwsl_info("%s: %s ipv6 %s\n", __func__, ads, ni);
|
||||
#else
|
||||
goto try_next_result; /* ipv4 only can't use this */
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
ads_known:
|
||||
#endif
|
||||
|
||||
/* now we decided on ipv4 or ipv6, set the port and create socket*/
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
|
||||
|
||||
if (wsi->context->event_loop_ops->check_client_connect_ok &&
|
||||
wsi->context->event_loop_ops->check_client_connect_ok(wsi)) {
|
||||
cce = "waiting for event loop watcher to close";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (wsi->unix_skt)
|
||||
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
else
|
||||
#endif
|
||||
wsi->desc.sockfd = socket(sa46.sa4.sin_family,
|
||||
SOCK_STREAM, 0);
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
|
||||
lwsl_warn("Unable to open socket\n");
|
||||
goto try_next_result;
|
||||
}
|
||||
|
||||
if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd,
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
wsi->unix_skt)) {
|
||||
#else
|
||||
0)) {
|
||||
#endif
|
||||
lwsl_err("Failed to set wsi socket options\n");
|
||||
goto try_next_result_closesock;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: %p: WAITING_CONNECT\n", __func__, wsi);
|
||||
lwsi_set_state(wsi, LRS_WAITING_CONNECT);
|
||||
|
||||
#if !defined(LWS_AMAZON_RTOS)
|
||||
if (wsi->context->event_loop_ops->accept)
|
||||
if (wsi->context->event_loop_ops->accept(wsi))
|
||||
goto try_next_result_closesock;
|
||||
#endif
|
||||
|
||||
if (__insert_wsi_socket_into_fds(wsi->context, wsi))
|
||||
goto try_next_result_closesock;
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
|
||||
goto try_next_result_fds;
|
||||
|
||||
/*
|
||||
* Past here, we can't simply free the structs as error
|
||||
* handling as oom4 does.
|
||||
*
|
||||
* We can run the whole close flow, or unpick the fds inclusion
|
||||
* and anything else we have done.
|
||||
*/
|
||||
wsi->oom4 = 1;
|
||||
if (!wsi->protocol)
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
|
||||
AWAITING_TIMEOUT);
|
||||
|
||||
if (wsi->stash)
|
||||
iface = wsi->stash->cis[CIS_IFACE];
|
||||
else
|
||||
iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
|
||||
|
||||
if (iface) {
|
||||
n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0,
|
||||
iface, wsi->ipv6);
|
||||
if (n < 0)
|
||||
goto try_next_result_fds;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (wsi->unix_skt) {
|
||||
psa = (const struct sockaddr *)&sau;
|
||||
n = sizeof(sau);
|
||||
} else
|
||||
#endif
|
||||
|
||||
if (!psa) /* coverity */
|
||||
goto try_next_result_fds;
|
||||
|
||||
/*
|
||||
* The actual connection attempt
|
||||
*/
|
||||
|
||||
m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n);
|
||||
if (m == -1) {
|
||||
|
||||
lwsl_debug("%s: connect says errno: %d\n", __func__, LWS_ERRNO);
|
||||
|
||||
if (LWS_ERRNO != LWS_EALREADY &&
|
||||
LWS_ERRNO != LWS_EINPROGRESS &&
|
||||
LWS_ERRNO != LWS_EWOULDBLOCK
|
||||
#ifdef _WIN32
|
||||
&& LWS_ERRNO != WSAEINVAL
|
||||
#endif
|
||||
) {
|
||||
#if defined(_DEBUG)
|
||||
char nads[48];
|
||||
lws_sa46_write_numeric_address(&sa46, nads, sizeof(nads));
|
||||
lwsl_info("%s: Connect failed: %s port %d\n",
|
||||
__func__, nads, wsi->c_port);
|
||||
#endif
|
||||
goto try_next_result_fds;
|
||||
}
|
||||
|
||||
if (lws_plat_check_connection_error(wsi))
|
||||
goto try_next_result_fds;
|
||||
|
||||
/*
|
||||
* must do specifically a POLLOUT poll to hear
|
||||
* about the connect completion
|
||||
*/
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
|
||||
goto try_next_result_fds;
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
conn_good:
|
||||
|
||||
lwsl_debug("%s: Connection started\n", __func__);
|
||||
|
||||
lws_addrinfo_clean(wsi);
|
||||
|
||||
if (wsi->protocol)
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
return lws_client_connect_4_established(wsi, NULL, plen);
|
||||
|
||||
|
||||
oom4:
|
||||
if (lwsi_role_client(wsi) && wsi->protocol /* && lwsi_state_est(wsi) */)
|
||||
lws_inform_client_conn_fail(wsi,(void *)cce, strlen(cce));
|
||||
|
||||
/* take care that we might be inserted in fds already */
|
||||
if (wsi->position_in_fds_table != LWS_NO_FDS_POS)
|
||||
goto failed1;
|
||||
|
||||
/*
|
||||
* We can't be an active client connection any more, if we thought
|
||||
* that was what we were going to be doing. It should be if we are
|
||||
* failing by oom4 path, we are still called by
|
||||
* lws_client_connect_via_info() and will be returning NULL to that,
|
||||
* so nobody else should have had a chance to queue on us.
|
||||
*/
|
||||
{
|
||||
struct lws_vhost *vhost = wsi->vhost;
|
||||
|
||||
lws_vhost_lock(vhost);
|
||||
__lws_free_wsi(wsi);
|
||||
lws_vhost_unlock(vhost);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
try_next_result_fds:
|
||||
wsi->oom4 = 0;
|
||||
__remove_wsi_socket_from_fds(wsi);
|
||||
|
||||
try_next_result_closesock:
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
|
||||
try_next_result:
|
||||
if (wsi->dns_results_next) {
|
||||
wsi->dns_results_next = wsi->dns_results_next->ai_next;
|
||||
if (wsi->dns_results_next)
|
||||
goto next_result;
|
||||
}
|
||||
cce = "Unable to connect";
|
||||
|
||||
//failed:
|
||||
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
|
||||
|
||||
failed1:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_2_dnsreq(struct lws *wsi)
|
||||
{
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
const char *adsin;
|
||||
#endif
|
||||
const char *cce = "", *meth = NULL, *ads;
|
||||
struct addrinfo *result = NULL;
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
struct sockaddr_in addr;
|
||||
const char *iface;
|
||||
#endif
|
||||
int n, port = 0;
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
if (!wsi->http.ah && !wsi->stash) {
|
||||
cce = "ah was NULL at cc2";
|
||||
|
@ -291,8 +693,8 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
lws_vhost_lock(wsi->vhost); /* ----------------------------------- { */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(&wsi->vhost->dll_cli_active_conns_owner)) {
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, lws_dll2_get_head(
|
||||
&wsi->vhost->dll_cli_active_conns_owner)) {
|
||||
struct lws *w = lws_container_of(d, struct lws,
|
||||
dll_cli_active_conns);
|
||||
|
||||
|
@ -351,7 +753,7 @@ lws_client_connect_2(struct lws *wsi)
|
|||
* to take over parsing the rx.
|
||||
*/
|
||||
lws_vhost_unlock(wsi->vhost); /* } ---------- */
|
||||
return lws_client_connect_3(wsi, w, plen);
|
||||
return lws_client_connect_4_established(wsi, w, 0);
|
||||
}
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
@ -407,19 +809,9 @@ create_new_conn:
|
|||
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (*ads == '+') {
|
||||
ads++;
|
||||
memset(&sau, 0, sizeof(sau));
|
||||
sau.sun_family = AF_UNIX;
|
||||
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
|
||||
sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
|
||||
|
||||
lwsl_info("%s: Unix skt: %s\n", __func__, ads);
|
||||
|
||||
if (sau.sun_path[0] == '@')
|
||||
sau.sun_path[0] = '\0';
|
||||
|
||||
unix_skt = 1;
|
||||
goto ads_known;
|
||||
wsi->unix_skt = 1;
|
||||
n = 0;
|
||||
goto next_step;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -448,17 +840,6 @@ create_new_conn:
|
|||
* Priority 1: connect to http proxy */
|
||||
|
||||
if (wsi->vhost->http.http_proxy_port) {
|
||||
plen = lws_snprintf((char *)pt->serv_buf, 256,
|
||||
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
|
||||
"User-agent: libwebsockets\x0d\x0a",
|
||||
ads, wsi->c_port);
|
||||
|
||||
if (wsi->vhost->proxy_basic_auth_token[0])
|
||||
plen += lws_snprintf((char *)pt->serv_buf + plen, 256,
|
||||
"Proxy-authorization: basic %s\x0d\x0a",
|
||||
wsi->vhost->proxy_basic_auth_token);
|
||||
|
||||
plen += lws_snprintf((char *)pt->serv_buf + plen, 5, "\x0d\x0a");
|
||||
ads = wsi->vhost->http.http_proxy_address;
|
||||
port = wsi->vhost->http.http_proxy_port;
|
||||
#else
|
||||
|
@ -470,11 +851,6 @@ create_new_conn:
|
|||
/* Priority 2: Connect to SOCK5 Proxy */
|
||||
|
||||
} else if (wsi->vhost->socks_proxy_port) {
|
||||
if (socks_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) {
|
||||
cce = "socks msg too large";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
lwsl_client("Sending SOCKS Greeting\n");
|
||||
ads = wsi->vhost->socks_proxy_address;
|
||||
port = wsi->vhost->socks_proxy_port;
|
||||
|
@ -491,271 +867,31 @@ create_new_conn:
|
|||
* prepare the actual connection
|
||||
* to whatever we decided to connect to
|
||||
*/
|
||||
lwsi_set_state(wsi, LRS_WAITING_DNS);
|
||||
|
||||
lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port);
|
||||
lwsl_warn("%s: %p: lookup %s:%u\n", __func__, wsi, ads, port);
|
||||
(void)port;
|
||||
|
||||
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
|
||||
n = lws_getaddrinfo46(wsi, ads, &result);
|
||||
memset(&sa46, 0, sizeof(sa46));
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (wsi->ipv6) {
|
||||
struct sockaddr_in6 *sa6;
|
||||
|
||||
if (n || !result) {
|
||||
/* lws_getaddrinfo46 failed, there is no usable result */
|
||||
lwsl_notice("%s: lws_getaddrinfo46 failed %d\n",
|
||||
__func__, n);
|
||||
cce = "ipv6 lws_getaddrinfo46 failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
sa6 = ((struct sockaddr_in6 *)result->ai_addr);
|
||||
sa46.sa6.sin6_family = AF_INET6;
|
||||
switch (result->ai_family) {
|
||||
case AF_INET:
|
||||
if (ipv6only)
|
||||
break;
|
||||
/* map IPv4 to IPv6 */
|
||||
memset((char *)&sa46.sa6.sin6_addr, 0,
|
||||
sizeof(sa46.sa6.sin6_addr));
|
||||
sa46.sa6.sin6_addr.s6_addr[10] = 0xff;
|
||||
sa46.sa6.sin6_addr.s6_addr[11] = 0xff;
|
||||
memcpy(&sa46.sa6.sin6_addr.s6_addr[12],
|
||||
&((struct sockaddr_in *)result->ai_addr)->sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
lwsl_notice("uplevelling AF_INET to AF_INET6\n");
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
memcpy(&sa46.sa6.sin6_addr, &sa6->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
sa46.sa6.sin6_scope_id = sa6->sin6_scope_id;
|
||||
sa46.sa6.sin6_flowinfo = sa6->sin6_flowinfo;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("Unknown address family\n");
|
||||
freeaddrinfo(result);
|
||||
cce = "unknown address family";
|
||||
goto oom4;
|
||||
}
|
||||
} else
|
||||
#endif /* use ipv6 */
|
||||
|
||||
/* use ipv4 */
|
||||
{
|
||||
void *p = NULL;
|
||||
|
||||
if (!n) {
|
||||
struct addrinfo *res = result;
|
||||
|
||||
/* pick the first AF_INET (IPv4) result */
|
||||
|
||||
while (!p && res) {
|
||||
switch (res->ai_family) {
|
||||
case AF_INET:
|
||||
p = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
||||
break;
|
||||
}
|
||||
|
||||
res = res->ai_next;
|
||||
}
|
||||
#if defined(LWS_FALLBACK_GETHOSTBYNAME)
|
||||
} else if (n == EAI_SYSTEM) {
|
||||
struct hostent *host;
|
||||
|
||||
lwsl_info("ipv4 getaddrinfo err, try gethostbyname\n");
|
||||
host = gethostbyname(ads);
|
||||
if (host) {
|
||||
p = host->h_addr;
|
||||
} else {
|
||||
lwsl_err("gethostbyname failed\n");
|
||||
cce = "gethostbyname (ipv4) failed";
|
||||
goto oom4;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
lwsl_err("getaddrinfo failed: %s: %d\n", ads, n);
|
||||
cce = "getaddrinfo failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
if (!p) {
|
||||
if (result)
|
||||
freeaddrinfo(result);
|
||||
lwsl_err("Couldn't identify address\n");
|
||||
cce = "unable to lookup address";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
sa46.sa4.sin_family = AF_INET;
|
||||
sa46.sa4.sin_addr = *((struct in_addr *)p);
|
||||
memset(&sa46.sa4.sin_zero, 0, sizeof(sa46.sa4.sin_zero));
|
||||
}
|
||||
|
||||
if (result)
|
||||
freeaddrinfo(result);
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
ads_known:
|
||||
#endif
|
||||
|
||||
/* now we decided on ipv4 or ipv6, set the port */
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
|
||||
|
||||
if (wsi->context->event_loop_ops->check_client_connect_ok &&
|
||||
wsi->context->event_loop_ops->check_client_connect_ok(wsi)) {
|
||||
cce = "waiting for event loop watcher to close";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (unix_skt) {
|
||||
wsi->unix_skt = 1;
|
||||
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (wsi->ipv6)
|
||||
wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
else
|
||||
#endif
|
||||
wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
|
||||
lwsl_warn("Unable to open socket\n");
|
||||
cce = "unable to open socket";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd,
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
unix_skt)) {
|
||||
#else
|
||||
0)) {
|
||||
/* this is either FAILED, CONTINUING, or already called connect_4 */
|
||||
|
||||
n = lws_async_dns_query(wsi->context, wsi->tsi, ads, LWS_ADNS_RECORD_A,
|
||||
lws_client_connect_3_connect, wsi, NULL);
|
||||
if (n == LADNS_RET_FAILED_WSI_CLOSED)
|
||||
return NULL;
|
||||
|
||||
if (n == LADNS_RET_FAILED)
|
||||
goto failed1;
|
||||
|
||||
return wsi;
|
||||
#endif
|
||||
lwsl_err("Failed to set wsi socket options\n");
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
cce = "set socket opts failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
lwsi_set_state(wsi, LRS_WAITING_CONNECT);
|
||||
|
||||
#if !defined(LWS_AMAZON_RTOS)
|
||||
if (wsi->context->event_loop_ops->accept)
|
||||
if (wsi->context->event_loop_ops->accept(wsi)) {
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
cce = "event loop accept failed";
|
||||
goto oom4;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (__insert_wsi_socket_into_fds(wsi->context, wsi)) {
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
cce = "insert wsi failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
compatible_close(wsi->desc.sockfd);
|
||||
cce = "change_pollfd failed";
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
/*
|
||||
* past here, we can't simply free the structs as error
|
||||
* handling as oom4 does. We have to run the whole close flow.
|
||||
*/
|
||||
|
||||
if (!wsi->protocol)
|
||||
wsi->protocol = &wsi->vhost->protocols[0];
|
||||
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
|
||||
AWAITING_TIMEOUT);
|
||||
|
||||
if (wsi->stash)
|
||||
iface = wsi->stash->cis[CIS_IFACE];
|
||||
else
|
||||
iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
|
||||
|
||||
if (iface) {
|
||||
n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0,
|
||||
iface, wsi->ipv6);
|
||||
if (n < 0) {
|
||||
cce = "unable to bind socket";
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_UNIX_SOCK)
|
||||
if (unix_skt) {
|
||||
psa = (const struct sockaddr *)&sau;
|
||||
n = sizeof(sau);
|
||||
} else
|
||||
next_step:
|
||||
#endif
|
||||
|
||||
{
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (wsi->ipv6) {
|
||||
sa46.sa6.sin6_port = htons(port);
|
||||
n = sizeof(struct sockaddr_in6);
|
||||
psa = (const struct sockaddr *)&sa46;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
sa46.sa4.sin_port = htons(port);
|
||||
n = sizeof(struct sockaddr);
|
||||
psa = (const struct sockaddr *)&sa46;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n) == -1 ||
|
||||
LWS_ERRNO == LWS_EISCONN) {
|
||||
if (LWS_ERRNO == LWS_EALREADY ||
|
||||
LWS_ERRNO == LWS_EINPROGRESS ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK
|
||||
#ifdef _WIN32
|
||||
|| LWS_ERRNO == WSAEINVAL
|
||||
#endif
|
||||
) {
|
||||
lwsl_client("nonblocking connect retry (errno = %d)\n",
|
||||
LWS_ERRNO);
|
||||
|
||||
if (lws_plat_check_connection_error(wsi)) {
|
||||
cce = "socket connect failed";
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* must do specifically a POLLOUT poll to hear
|
||||
* about the connect completion
|
||||
*/
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
|
||||
cce = "POLLOUT set failed";
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
if (LWS_ERRNO != LWS_EISCONN) {
|
||||
lwsl_notice("Connect failed errno=%d\n", LWS_ERRNO);
|
||||
cce = "connect failed";
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return lws_client_connect_3(wsi, NULL, plen);
|
||||
|
||||
return lws_client_connect_3_connect(wsi, ads, result, n, NULL);
|
||||
|
||||
oom4:
|
||||
if (lwsi_role_client(wsi) && wsi->protocol /* && lwsi_state_est(wsi) */)
|
||||
|
@ -782,16 +918,12 @@ oom4:
|
|||
|
||||
return NULL;
|
||||
|
||||
failed:
|
||||
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
|
||||
|
||||
failed1:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
|
||||
static uint8_t hnames2[] = {
|
||||
|
@ -909,7 +1041,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
|
|||
|
||||
lws_free_set_NULL(stash);
|
||||
|
||||
*pwsi = lws_client_connect_2(wsi);
|
||||
*pwsi = lws_client_connect_2_dnsreq(wsi);
|
||||
|
||||
return *pwsi;
|
||||
|
||||
|
@ -1095,7 +1227,7 @@ lws_http_client_connect_via_info2(struct lws *wsi)
|
|||
no_ah:
|
||||
wsi->context->count_wsi_allocated++;
|
||||
|
||||
return lws_client_connect_2(wsi);
|
||||
return lws_client_connect_2_dnsreq(wsi);
|
||||
|
||||
bail1:
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
|
|
|
@ -137,14 +137,14 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
|||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
case LRS_WAITING_CONNECT:
|
||||
case LRS_WAITING_DNS:
|
||||
|
||||
/*
|
||||
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
|
||||
* timeout protection set in client-handshake.c
|
||||
*/
|
||||
|
||||
if (!lws_client_connect_2(wsi)) {
|
||||
lwsl_err("%s: wsi %p: WAITING_DNS\n", __func__, wsi);
|
||||
if (!lws_client_connect_2_dnsreq(wsi)) {
|
||||
/* closed */
|
||||
lwsl_client("closed\n");
|
||||
return -1;
|
||||
|
@ -152,6 +152,15 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
|||
|
||||
/* either still pending connection, or changed mode */
|
||||
return 0;
|
||||
case LRS_WAITING_CONNECT:
|
||||
|
||||
/*
|
||||
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
|
||||
* timeout protection set in client-handshake.c
|
||||
*/
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
|
||||
break;
|
||||
|
||||
#if defined(LWS_WITH_SOCKS5)
|
||||
/* SOCKS Greeting Reply */
|
||||
|
|
|
@ -87,43 +87,44 @@ enum lwsi_state {
|
|||
/* Phase 1: pre-transport */
|
||||
|
||||
LRS_UNCONNECTED = LWSIFS_NOT_EST | 0,
|
||||
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1,
|
||||
LRS_WAITING_DNS = LWSIFS_NOT_EST | 1,
|
||||
LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 2,
|
||||
|
||||
/* Phase 2: establishing intermediaries on top of transport */
|
||||
|
||||
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2,
|
||||
LRS_WAITING_SSL = LWSIFS_NOT_EST | 3,
|
||||
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4,
|
||||
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5,
|
||||
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6,
|
||||
LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 3,
|
||||
LRS_WAITING_SSL = LWSIFS_NOT_EST | 4,
|
||||
LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 5,
|
||||
LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 6,
|
||||
LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 7,
|
||||
|
||||
/* Phase 3: establishing tls tunnel */
|
||||
|
||||
LRS_SSL_INIT = LWSIFS_NOT_EST | 7,
|
||||
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8,
|
||||
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9,
|
||||
LRS_SSL_INIT = LWSIFS_NOT_EST | 8,
|
||||
LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 9,
|
||||
LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 10,
|
||||
|
||||
/* Phase 4: connected */
|
||||
|
||||
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10,
|
||||
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11,
|
||||
LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 11,
|
||||
LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 12,
|
||||
LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST |
|
||||
LWSIFS_POCB | 12,
|
||||
LWSIFS_POCB | 13,
|
||||
|
||||
/* Phase 5: protocol logically established */
|
||||
|
||||
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14,
|
||||
LRS_DEFERRING_ACTION = LWSIFS_POCB | 15,
|
||||
LRS_IDLING = 16,
|
||||
LRS_H1C_ISSUE_HANDSHAKE = 17,
|
||||
LRS_H1C_ISSUE_HANDSHAKE2 = 18,
|
||||
LRS_ISSUE_HTTP_BODY = 19,
|
||||
LRS_ISSUING_FILE = 20,
|
||||
LRS_HEADERS = 21,
|
||||
LRS_BODY = 22,
|
||||
LRS_DISCARD_BODY = 31,
|
||||
LRS_ESTABLISHED = LWSIFS_POCB | 23,
|
||||
LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 14,
|
||||
LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 15,
|
||||
LRS_DEFERRING_ACTION = LWSIFS_POCB | 16,
|
||||
LRS_IDLING = 17,
|
||||
LRS_H1C_ISSUE_HANDSHAKE = 18,
|
||||
LRS_H1C_ISSUE_HANDSHAKE2 = 19,
|
||||
LRS_ISSUE_HTTP_BODY = 20,
|
||||
LRS_ISSUING_FILE = 21,
|
||||
LRS_HEADERS = 22,
|
||||
LRS_BODY = 23,
|
||||
LRS_DISCARD_BODY = 24,
|
||||
LRS_ESTABLISHED = LWSIFS_POCB | 25,
|
||||
/* we are established, but we have embarked on serving a single
|
||||
* transaction. Other transaction input may be pending, but we will
|
||||
* not service it while we are busy dealing with the current
|
||||
|
@ -132,19 +133,19 @@ enum lwsi_state {
|
|||
* When we complete the current transaction, we would reset our state
|
||||
* back to ESTABLISHED and start to process the next transaction.
|
||||
*/
|
||||
LRS_DOING_TRANSACTION = LWSIFS_POCB | 24,
|
||||
LRS_DOING_TRANSACTION = LWSIFS_POCB | 26,
|
||||
|
||||
/* Phase 6: finishing */
|
||||
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 25,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 26,
|
||||
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 27,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 28,
|
||||
LRS_SHUTDOWN = 29,
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 27,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 28,
|
||||
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 29,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 30,
|
||||
LRS_SHUTDOWN = 31,
|
||||
|
||||
/* Phase 7: dead */
|
||||
|
||||
LRS_DEAD_SOCKET = 30,
|
||||
LRS_DEAD_SOCKET = 32,
|
||||
|
||||
LRS_MASK = 0xffff
|
||||
};
|
||||
|
@ -333,4 +334,8 @@ int
|
|||
lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen);
|
||||
lws_client_connect_4_established(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen);
|
||||
|
||||
struct lws *
|
||||
lws_client_connect_3_connect(struct lws *wsi, const char *ads,
|
||||
const struct addrinfo *result, int n, void *opaque);
|
||||
|
|
|
@ -116,7 +116,7 @@ try_pollout:
|
|||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lwsi_state(wsi) == LRS_WAITING_CONNECT)
|
||||
lws_client_connect_3(wsi, NULL, 0);
|
||||
lws_client_connect_4_established(wsi, NULL, 0);
|
||||
#endif
|
||||
|
||||
/* one shot */
|
||||
|
|
625
lib/system/async-dns/async-dns-parse.c
Normal file
625
lib/system/async-dns/async-dns-parse.c
Normal file
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include "private-lib-async-dns.h"
|
||||
|
||||
|
||||
/* updates *dest, returns chars used from ls directly, else -1 for fail */
|
||||
|
||||
static int
|
||||
lws_adns_parse_label(const uint8_t *pkt, int len, const uint8_t *ls, int budget,
|
||||
char **dest, int dl)
|
||||
{
|
||||
const uint8_t *e = pkt + len, *ols = ls;
|
||||
char pointer = 0, first = 1;
|
||||
uint8_t ll;
|
||||
int n;
|
||||
|
||||
if (budget < 1)
|
||||
return 0;
|
||||
|
||||
/* caller must catch end of labels */
|
||||
assert(*ls);
|
||||
|
||||
again1:
|
||||
if (ls >= e)
|
||||
return -1;
|
||||
|
||||
if (((*ls) & 0xc0) == 0xc0) {
|
||||
if (budget < 2)
|
||||
return -1;
|
||||
/* pointer into message pkt to name to actually use */
|
||||
n = lws_ser_ru16be(ls) & 0x3fff;
|
||||
if (n >= len) {
|
||||
lwsl_notice("%s: illegal name pointer\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* dereference the label pointer */
|
||||
ls = pkt + n;
|
||||
|
||||
/* are we being fuzzed or messed with? */
|
||||
if (((*ls) & 0xc0) == 0xc0) {
|
||||
/* ... pointer to pointer is unreasonable */
|
||||
lwsl_notice("%s: label ptr to ptr invalid\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
pointer = 1;
|
||||
}
|
||||
|
||||
again:
|
||||
if (ls >= e)
|
||||
return -1;
|
||||
ll = *ls++;
|
||||
if (ls + ll + 4 > e || ll > budget) {
|
||||
lwsl_notice("%s: label len invalid\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ll + 2 > dl) {
|
||||
lwsl_notice("%s: qname too large\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the label content into place */
|
||||
|
||||
memcpy(*dest, ls, ll);
|
||||
(*dest)[ll] = '.';
|
||||
(*dest)[ll + 1] = '\0';
|
||||
*dest += ll + 1;
|
||||
ls += ll;
|
||||
|
||||
if (pointer) {
|
||||
if (*ls)
|
||||
goto again;
|
||||
|
||||
/*
|
||||
* special fun rule... if whole qname was a pointer label,
|
||||
* it has no 00 terminator afterwards
|
||||
*/
|
||||
if (first)
|
||||
return 2; /* we just took the 16-bit pointer */
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
first = 0;
|
||||
|
||||
if (*ls)
|
||||
goto again1;
|
||||
|
||||
ls++;
|
||||
|
||||
return ls - ols;
|
||||
}
|
||||
|
||||
typedef int (*lws_async_dns_find_t)(const char *name, void *opaque,
|
||||
uint32_t ttl, adns_query_type_t type,
|
||||
const uint8_t *payload);
|
||||
|
||||
/* locally query the response packet */
|
||||
|
||||
struct label_stack {
|
||||
char name[64];
|
||||
int enl;
|
||||
const uint8_t *p;
|
||||
};
|
||||
|
||||
/*
|
||||
* Walk the response packet, calling back to the user-provided callback for each
|
||||
* A (and AAAA if LWS_IPV6=1) record with a matching name found in there.
|
||||
*
|
||||
* Able to recurse using an explicit non-CPU stack to resolve CNAME usages
|
||||
*
|
||||
* Return -1: unexpectedly failed
|
||||
* 0: found
|
||||
* 1: didn't find anything matching
|
||||
*/
|
||||
|
||||
static int
|
||||
lws_adns_iterate(const uint8_t *pkt, int len, const char *expname,
|
||||
lws_async_dns_find_t cb, void *opaque)
|
||||
{
|
||||
const uint8_t *e = pkt + len, *p, *pay;
|
||||
struct label_stack stack[4];
|
||||
uint16_t rrtype, rrpaylen;
|
||||
int n = 0, stp = 0, ansc;
|
||||
char *sp, inq;
|
||||
uint32_t ttl;
|
||||
|
||||
lws_strncpy(stack[stp].name, expname, sizeof(stack[stp].name));
|
||||
stack[stp].enl = strlen(expname);
|
||||
|
||||
start:
|
||||
ansc = lws_ser_ru16be(pkt + DHO_NANSWERS);
|
||||
p = pkt + DHO_SIZEOF;
|
||||
inq = 1;
|
||||
|
||||
/*
|
||||
* The response also includes the query... and we have to parse it
|
||||
* so we can understand we reached the response... there's a QNAME
|
||||
* made up of labels and then 2 x 16-bit fields, for query type and
|
||||
* query class
|
||||
*/
|
||||
|
||||
resume:
|
||||
while (p + 14 < e && (inq || ansc)) {
|
||||
|
||||
if (!inq && !stp)
|
||||
ansc--;
|
||||
|
||||
/*
|
||||
* First is the name the query applies to... two main
|
||||
* formats can appear here, one is a pointer to
|
||||
* elsewhere in the message, the other separately
|
||||
* provides len / data for each dotted "label", so for
|
||||
* "warmcat.com" warmcat and com are given each with a
|
||||
* prepended length byte. Any of those may be a pointer
|
||||
* to somewhere else in the packet :-/
|
||||
*
|
||||
* Paranoia is appropriate since the name length must be
|
||||
* parsed out before the rest of the RR can be used and
|
||||
* we can be attacked with absolutely any crafted
|
||||
* content easily via UDP.
|
||||
*
|
||||
* So parse the name and additionally confirm it matches
|
||||
* what the query the TID belongs to actually asked for.
|
||||
*/
|
||||
|
||||
sp = stack[0].name;
|
||||
|
||||
/* while we have more labels */
|
||||
|
||||
n = lws_adns_parse_label(pkt, len, p, len, &sp,
|
||||
sizeof(stack[0].name) -
|
||||
lws_ptr_diff(sp, stack[0].name));
|
||||
/* includes case name won't fit */
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
p += n;
|
||||
|
||||
if (p + (inq ? 5 : 14) > e)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* p is now just after the decoded RR name, pointing at: type
|
||||
*
|
||||
* We sent class = 1 = IN query... response must match
|
||||
*/
|
||||
|
||||
if (lws_ser_ru16be(&p[2]) != 1) {
|
||||
lwsl_debug("%s: non-IN response 0x%x\n", __func__,
|
||||
lws_ser_ru16be(&p[2]));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (inq) {
|
||||
lwsl_debug("%s: reached end of inq\n", __func__);
|
||||
inq = 0;
|
||||
p += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* carefully validate the claimed RR payload length */
|
||||
|
||||
rrpaylen = lws_ser_ru16be(&p[8]);
|
||||
if (p + 10 + rrpaylen > e) { /* it may be == e */
|
||||
lwsl_notice("%s: invalid RR data length\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ttl = lws_ser_ru32be(&p[4]);
|
||||
rrtype = lws_ser_ru16be(&p[0]);
|
||||
p += 10; /* point to the payload */
|
||||
pay = p;
|
||||
|
||||
/*
|
||||
* Compare the RR names, allowing for the decoded labelname
|
||||
* to have an extra '.' at the end.
|
||||
*/
|
||||
|
||||
n = lws_ptr_diff(sp, stack[0].name);
|
||||
if (stack[0].name[n - 1] == '.')
|
||||
n--;
|
||||
|
||||
if (n < 1 || n != stack[stp].enl ||
|
||||
strcmp(stack[0].name, stack[stp].name)) {
|
||||
lwsl_debug("%s: skipping %s vs %s\n", __func__,
|
||||
stack[0].name, stack[stp].name);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's something we could be interested in...
|
||||
*
|
||||
* We can skip RRs we don't understand. But we need to deal
|
||||
* with at least these and their payloads:
|
||||
*
|
||||
* A: 4: ipv4 address
|
||||
* AAAA: 16: ipv6 address (if asked for AAAA)
|
||||
* CNAME: ?: labelized name
|
||||
*
|
||||
* If we hit a CNAME we need to try to dereference it with
|
||||
* stuff that is in the same response packet and judge it
|
||||
* from that, without losing our place here. CNAMEs may
|
||||
* point to CNAMEs to whatever depth we're willing to handle.
|
||||
*/
|
||||
|
||||
switch (rrtype) {
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
case LWS_ADNS_RECORD_AAAA:
|
||||
if (rrpaylen != 16) {
|
||||
lwsl_err("%s: unexpected rrpaylen\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
goto do_cb;
|
||||
#endif
|
||||
case LWS_ADNS_RECORD_A:
|
||||
if (rrpaylen != 4) {
|
||||
lwsl_err("%s: unexpected rrpaylen4\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
do_cb:
|
||||
#endif
|
||||
cb(stack[0].name, opaque, ttl, rrtype, p);
|
||||
break;
|
||||
|
||||
case LWS_ADNS_RECORD_CNAME:
|
||||
/*
|
||||
* The name the CNAME refers to should itself be
|
||||
* included elsewhere in the response packet.
|
||||
*
|
||||
* So switch tack, stack where to resume from and
|
||||
* search for the decoded CNAME label name definition
|
||||
* instead.
|
||||
*
|
||||
* First decode the CNAME label payload into the next
|
||||
* stack level buffer for it.
|
||||
*/
|
||||
|
||||
if (++stp == (int)LWS_ARRAY_SIZE(stack)) {
|
||||
lwsl_notice("%s: CNAMEs too deep\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
sp = stack[stp].name;
|
||||
n = lws_adns_parse_label(pkt, len, p, rrpaylen, &sp,
|
||||
sizeof(stack[stp].name) -
|
||||
lws_ptr_diff(sp, stack[stp].name));
|
||||
/* includes case name won't fit */
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
p += n;
|
||||
|
||||
if (p + 14 > e)
|
||||
return -1;
|
||||
|
||||
/* it should have exactly reached rrpaylen */
|
||||
|
||||
if (p != pay + rrpaylen) {
|
||||
lwsl_err("%s: cname name bad len\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
stack[stp].enl = lws_ptr_diff(sp, stack[stp].name);
|
||||
/* when we unstack, resume from here */
|
||||
stack[stp].p = pay + rrpaylen;
|
||||
goto start;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
skip:
|
||||
p += rrpaylen;
|
||||
}
|
||||
|
||||
if (!stp)
|
||||
return 1; /* we didn't find anything, but we didn't error */
|
||||
|
||||
/*
|
||||
* This implies there wasn't any usable definition for the
|
||||
* CNAME in the end, eg, only AAAA when we needed and A.
|
||||
*
|
||||
* Short-circuit the whole stack and resume from after the
|
||||
* original CNAME reference.
|
||||
*/
|
||||
p = stack[1].p;
|
||||
stp = 0;
|
||||
goto resume;
|
||||
}
|
||||
|
||||
int
|
||||
lws_async_dns_estimate(const char *name, void *opaque, uint32_t ttl,
|
||||
adns_query_type_t type, const uint8_t *payload)
|
||||
{
|
||||
size_t *est = (size_t *)opaque, my;
|
||||
|
||||
my = sizeof(struct addrinfo);
|
||||
if (type == LWS_ADNS_RECORD_AAAA)
|
||||
my += sizeof(struct sockaddr_in6);
|
||||
else
|
||||
my += sizeof(struct sockaddr_in);
|
||||
|
||||
*est += my;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct adstore {
|
||||
const char *name;
|
||||
struct addrinfo *pos;
|
||||
struct addrinfo *prev;
|
||||
int ctr;
|
||||
uint32_t smallest_ttl;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback for each A or AAAA record, creating getaddrinfo-compatible results
|
||||
* into the preallocated exact-sized storage.
|
||||
*/
|
||||
int
|
||||
lws_async_dns_store(const char *name, void *opaque, uint32_t ttl,
|
||||
adns_query_type_t type, const uint8_t *payload)
|
||||
{
|
||||
struct adstore *adst = (struct adstore *)opaque;
|
||||
#if defined(_DEBUG)
|
||||
char buf[48];
|
||||
#endif
|
||||
size_t i;
|
||||
|
||||
if (ttl < adst->smallest_ttl || !adst->ctr)
|
||||
adst->smallest_ttl = ttl;
|
||||
|
||||
if (adst->prev)
|
||||
adst->prev->ai_next = adst->pos;
|
||||
adst->prev = adst->pos;
|
||||
|
||||
adst->pos->ai_flags = 0;
|
||||
adst->pos->ai_family = type == LWS_ADNS_RECORD_AAAA ?
|
||||
AF_INET6 : AF_INET;
|
||||
adst->pos->ai_socktype = SOCK_STREAM;
|
||||
adst->pos->ai_protocol = IPPROTO_UDP; /* no meaning */
|
||||
adst->pos->ai_addrlen = type == LWS_ADNS_RECORD_AAAA ?
|
||||
sizeof(struct sockaddr_in6) :
|
||||
sizeof(struct sockaddr_in);
|
||||
adst->pos->ai_canonname = (char *)adst->name;
|
||||
adst->pos->ai_addr = (struct sockaddr *)&adst->pos[1];
|
||||
adst->pos->ai_next = NULL;
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
if (type == LWS_ADNS_RECORD_AAAA) {
|
||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&adst->pos[1];
|
||||
|
||||
i = sizeof(*in6);
|
||||
memset(in6, 0, i);
|
||||
in6->sin6_family = adst->pos->ai_family;
|
||||
memcpy(in6->sin6_addr.s6_addr, payload, 16);
|
||||
adst->flags |= 2;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_in *in = (struct sockaddr_in *)&adst->pos[1];
|
||||
|
||||
i = sizeof(*in);
|
||||
memset(in, 0, i);
|
||||
in->sin_family = adst->pos->ai_family;
|
||||
memcpy(&in->sin_addr.s_addr, payload, 4);
|
||||
adst->flags |= 1;
|
||||
}
|
||||
|
||||
adst->pos = (struct addrinfo *)((uint8_t *)adst->pos +
|
||||
sizeof(struct addrinfo) + i);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (lws_write_numeric_address(payload,
|
||||
type == LWS_ADNS_RECORD_AAAA ? 16 : 4,
|
||||
buf, sizeof(buf)) > 0)
|
||||
lwsl_info("%s: %d: %s: %s\n", __func__, adst->ctr,
|
||||
adst->name, buf);
|
||||
#endif
|
||||
adst->ctr++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to parse out all A or AAAA records
|
||||
*/
|
||||
|
||||
void
|
||||
lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len)
|
||||
{
|
||||
lws_adns_cache_t *c;
|
||||
struct adstore adst;
|
||||
lws_adns_q_t *q;
|
||||
const char *nm;
|
||||
size_t est;
|
||||
int n;
|
||||
|
||||
// lwsl_hexdump_notice(pkt, len);
|
||||
|
||||
/* we have to at least have the header */
|
||||
|
||||
if (len < DHO_SIZEOF)
|
||||
return;
|
||||
|
||||
/* we asked with one query, so anything else is bogus */
|
||||
|
||||
if (lws_ser_ru16be(pkt + DHO_NQUERIES) != 1)
|
||||
return;
|
||||
|
||||
/* match both A and AAAA queries if any */
|
||||
|
||||
q = lws_adns_get_query(dns, 0, &dns->waiting_resp,
|
||||
lws_ser_ru16be(pkt + DHO_TID), NULL);
|
||||
if (!q) {
|
||||
/*
|
||||
* if he's still waiting to send the second query, he's still
|
||||
* on the .waiting_send list
|
||||
*/
|
||||
q = lws_adns_get_query(dns, 0, &dns->waiting_send,
|
||||
lws_ser_ru16be(pkt + DHO_TID), NULL);
|
||||
|
||||
if (!q) {
|
||||
lwsl_notice("%s: dropping unknown query tid 0x%x\n",
|
||||
__func__, lws_ser_ru16be(pkt + DHO_TID));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* we can get dups... drop any that have already happened */
|
||||
|
||||
n = 1 << (lws_ser_ru16be(pkt + DHO_TID) & 1);
|
||||
if (q->responded & n) {
|
||||
lwsl_notice("%s: dup\n", __func__);
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
q->responded |= n;
|
||||
nm = (const char *)&q[1];
|
||||
|
||||
/*
|
||||
* First walk the packet figuring out the allocation needed for all
|
||||
* the results. Produce the following layout at c
|
||||
*
|
||||
* lws_adns_cache_t: new cache object
|
||||
* [struct addrinfo + struct sockaddr_in or _in6]: for each A or AAAA
|
||||
* char []: copy of resolved name
|
||||
*/
|
||||
|
||||
n = strlen(nm) + 1;
|
||||
|
||||
est = sizeof(lws_adns_cache_t) + n;
|
||||
if (lws_ser_ru16be(pkt + DHO_NANSWERS) &&
|
||||
lws_adns_iterate(pkt, len, nm, lws_async_dns_estimate, &est) < 0)
|
||||
goto fail_out;
|
||||
|
||||
c = lws_malloc(est, "async-dns-entry");
|
||||
if (!c) {
|
||||
lwsl_err("%s: OOM %zu\n", __func__, est);
|
||||
goto fail_out;
|
||||
}
|
||||
memset(c, 0, sizeof(*c));
|
||||
|
||||
/* place it at end, no need to care about alignment padding */
|
||||
adst.name = ((const char *)c) + est - n;
|
||||
memcpy((char *)adst.name, nm, n);
|
||||
|
||||
/*
|
||||
* Then walk the packet again, placing the objects we accounted for
|
||||
* the first time into the result allocation after the cache object
|
||||
* and copy of the name
|
||||
*/
|
||||
|
||||
adst.pos = (struct addrinfo *)&c[1];
|
||||
adst.prev = NULL;
|
||||
adst.ctr = 0;
|
||||
adst.smallest_ttl = 3600;
|
||||
adst.flags = 0;
|
||||
|
||||
/*
|
||||
* smallest_ttl applies as it is to empty results (NXDOMAIN), or is
|
||||
* set to the minimum ttl seen in all the results.
|
||||
*/
|
||||
|
||||
if (lws_ser_ru16be(pkt + DHO_NANSWERS) &&
|
||||
lws_adns_iterate(pkt, len, nm, lws_async_dns_store, &adst) < 0) {
|
||||
lws_free(c);
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
if (lws_ser_ru16be(pkt + DHO_NANSWERS)) {
|
||||
c->results = (struct addrinfo *)&c[1];
|
||||
if (q->last) /* chain the second one on */
|
||||
*q->last = c->results;
|
||||
else /* first one had no results, set first guy's c->results */
|
||||
if (q->firstcache)
|
||||
q->firstcache->results = c->results;
|
||||
}
|
||||
|
||||
if (adst.prev) /* so we know where to continue the addrinfo list */
|
||||
/* can be NULL if first resp empty */
|
||||
q->last = &adst.prev->ai_next;
|
||||
|
||||
if (q->firstcache) { /* also need to free chain when we free this guy */
|
||||
q->firstcache->chain = c;
|
||||
c->firstcache = q->firstcache;
|
||||
} else {
|
||||
|
||||
q->firstcache = c;
|
||||
|
||||
/*
|
||||
* Only register the first one into the cache...
|
||||
* Trim the oldest cache entry if necessary
|
||||
*/
|
||||
|
||||
lws_async_dns_trim_cache(dns);
|
||||
|
||||
/*
|
||||
* cache the first results object... if a second one comes,
|
||||
* we won't directly register it but will chain it on to this
|
||||
* first one and continue to addinfo ai_next linked list from
|
||||
* the first into the second
|
||||
*/
|
||||
|
||||
c->flags = adst.flags;
|
||||
lws_dll2_add_head(&c->list, &dns->cached);
|
||||
lws_sul_schedule(q->context, 0, &c->sul, sul_cb_expire,
|
||||
lws_now_usecs() +
|
||||
(adst.smallest_ttl * LWS_US_PER_SEC));
|
||||
}
|
||||
|
||||
if (q->responded != q->asked)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Now we captured everything into the new object, return the
|
||||
* addrinfo results, if any, to all interested wsi, if any...
|
||||
*/
|
||||
|
||||
lws_async_dns_complete(q, q->firstcache);
|
||||
|
||||
/*
|
||||
* the query is completely finished with
|
||||
*/
|
||||
|
||||
fail_out:
|
||||
lws_adns_q_destroy(q);
|
||||
}
|
||||
|
671
lib/system/async-dns/async-dns.c
Normal file
671
lib/system/async-dns/async-dns.c
Normal file
|
@ -0,0 +1,671 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
#include "private-lib-async-dns.h"
|
||||
|
||||
void
|
||||
lws_adns_q_destroy(lws_adns_q_t *q)
|
||||
{
|
||||
lws_dll2_remove(&q->sul.list);
|
||||
lws_dll2_remove(&q->list);
|
||||
lws_free(q);
|
||||
}
|
||||
|
||||
lws_adns_q_t *
|
||||
lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype,
|
||||
lws_dll2_owner_t *owner, uint16_t tid, const char *name)
|
||||
{
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(owner)) {
|
||||
lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
|
||||
|
||||
if (!name && (tid & 0xfffe) == (q->tid & 0xfffe))
|
||||
return q;
|
||||
|
||||
if (name && q->qtype == ((tid & 1) ? LWS_ADNS_RECORD_AAAA :
|
||||
LWS_ADNS_RECORD_A) &&
|
||||
!strcasecmp(name, (const char *)&q[1])) {
|
||||
if (owner == &dns->cached) {
|
||||
/* Keep sorted by LRU: move to the head */
|
||||
lws_dll2_remove(&q->list);
|
||||
lws_dll2_add_head(&q->list, &dns->cached);
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
lws_async_dns_drop_server(struct lws_context *context)
|
||||
{
|
||||
context->async_dns.dns_server_set = 0;
|
||||
lws_set_timeout(context->async_dns.wsi, 1, LWS_TO_KILL_ASYNC);
|
||||
context->async_dns.wsi = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c)
|
||||
{
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(&q->wsi_adns)) {
|
||||
struct lws *w = lws_container_of(d, struct lws, adns);
|
||||
|
||||
lws_dll2_remove(d);
|
||||
if (c && c->results) {
|
||||
lwsl_debug("%s: q: %p, c: %p, refcount %d -> %d\n",
|
||||
__func__, q, c, c->refcount, c->refcount + 1);
|
||||
c->refcount++;
|
||||
}
|
||||
w->adns_cb(w, (const char *)&q[1], c ? c->results : NULL, 0,
|
||||
q->opaque);
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
if (q->standalone_cb) {
|
||||
if (c && c->results) {
|
||||
lwsl_debug("%s: q: %p, c: %p, refcount %d -> %d\n",
|
||||
__func__, q, c, c->refcount, c->refcount + 1);
|
||||
c->refcount++;
|
||||
}
|
||||
|
||||
q->standalone_cb(NULL, (const char *)&q[1],
|
||||
c ? c->results : NULL, 0, q->opaque);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sul_cb_timeout(struct lws_sorted_usec_list *sul)
|
||||
{
|
||||
lws_adns_q_t *q = lws_container_of(sul, lws_adns_q_t, sul);
|
||||
|
||||
lws_async_dns_complete(q, NULL);
|
||||
lws_adns_q_destroy(q);
|
||||
|
||||
/*
|
||||
* our policy is to force reloading the dns server info if our
|
||||
* connection ever timed out, in case it or the routing state changed
|
||||
*/
|
||||
|
||||
lws_async_dns_drop_server(q->context);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_async_dns(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
uint8_t pkt[LWS_PRE + DNS_PACKET_LEN], *e = &pkt[sizeof(pkt)], *p, *pl;
|
||||
struct lws_async_dns *dns = &(lws_get_context(wsi)->async_dns);
|
||||
struct lws_dll2 *d;
|
||||
const char *name;
|
||||
lws_adns_q_t *q;
|
||||
int fd, m, n;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/* callbacks related to raw socket descriptor */
|
||||
|
||||
case LWS_CALLBACK_RAW_ADOPT:
|
||||
// lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CLOSE:
|
||||
// lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_RX:
|
||||
// lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
|
||||
//lwsl_hexdump_level(LLL_NOTICE, in, len);
|
||||
lws_adns_parse_udp(dns, in, len);
|
||||
|
||||
return 0;
|
||||
|
||||
case LWS_CALLBACK_RAW_WRITEABLE:
|
||||
|
||||
d = lws_dll2_get_head(&dns->waiting_send);
|
||||
if (!d)
|
||||
return 0;
|
||||
q = lws_container_of(d, lws_adns_q_t, list);
|
||||
name = (const char *)&q[1];
|
||||
|
||||
p = &pkt[LWS_PRE];
|
||||
memset(p, 0, DHO_SIZEOF);
|
||||
|
||||
/* we hack b0 of the tid to be 0 = A, 1 = AAAA */
|
||||
|
||||
lws_ser_wu16be(&p[DHO_TID], q->asked ? q->tid | 1 :
|
||||
q->tid);
|
||||
lws_ser_wu16be(&p[DHO_FLAGS], (1 << 8));
|
||||
lws_ser_wu16be(&p[DHO_NQUERIES], 1);
|
||||
|
||||
p += DHO_SIZEOF;
|
||||
|
||||
/* start of label-formatted qname */
|
||||
|
||||
pl = p++;
|
||||
|
||||
do {
|
||||
if (*name == '.' || !*name) {
|
||||
*pl = lws_ptr_diff(p, pl + 1);
|
||||
pl = p;
|
||||
*p++ = 0; /* also serves as terminal length */
|
||||
if (!*name++)
|
||||
break;
|
||||
} else
|
||||
*p++ = *name++;
|
||||
} while (p + 6 < e);
|
||||
|
||||
if (p + 6 >= e) {
|
||||
assert(0);
|
||||
lwsl_err("%s: name too big\n", __func__);
|
||||
goto qfail;
|
||||
}
|
||||
|
||||
lws_ser_wu16be(p, q->asked ? LWS_ADNS_RECORD_AAAA :
|
||||
LWS_ADNS_RECORD_A);
|
||||
p += 2;
|
||||
|
||||
lws_ser_wu16be(p, 1); /* IN class */
|
||||
p += 2;
|
||||
|
||||
assert(p < pkt + sizeof(pkt) - LWS_PRE);
|
||||
n = lws_ptr_diff(p, pkt + LWS_PRE);
|
||||
|
||||
fd = lws_get_socket_fd(wsi);
|
||||
if (fd < 0)
|
||||
break;
|
||||
|
||||
m = send(fd, pkt + LWS_PRE, n, 0);
|
||||
if (m != n) {
|
||||
lwsl_notice("%s: dns write failed %d %d\n", __func__,
|
||||
m, n);
|
||||
goto qfail;
|
||||
}
|
||||
|
||||
/* move us to the "waiting for response" list */
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
/* don't move to waiting resp until we sent both */
|
||||
if (q->asked) {
|
||||
q->asked |= 2;
|
||||
#endif
|
||||
lws_dll2_remove(&q->list);
|
||||
lws_dll2_add_head(&q->list, &dns->waiting_resp);
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
} else
|
||||
lws_callback_on_writable(wsi);
|
||||
#endif
|
||||
q->asked |= 1;
|
||||
|
||||
if (lws_dll2_get_head(&dns->waiting_send))
|
||||
/* more to do */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
qfail:
|
||||
lws_async_dns_complete(q, NULL);
|
||||
lws_adns_q_destroy(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_protocols lws_async_dns_protocol = {
|
||||
"lws-async-dns", callback_async_dns, 0, 0
|
||||
};
|
||||
|
||||
int
|
||||
lws_async_dns_init(struct lws_context *context)
|
||||
{
|
||||
lws_async_dns_t *dns = &context->async_dns;
|
||||
char ads[48];
|
||||
int n;
|
||||
|
||||
memset(&dns->sa46, 0, sizeof(dns->sa46));
|
||||
|
||||
n = lws_plat_asyncdns_init(context, &dns->sa46);
|
||||
if (n < 0) {
|
||||
lwsl_warn("%s: no valid dns server, retry\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
dns->sa46.sa4.sin_port = htons(53);
|
||||
lws_write_numeric_address((uint8_t *)&dns->sa46.sa4.sin_addr.s_addr, 4,
|
||||
ads, sizeof(ads));
|
||||
|
||||
context->async_dns.wsi = lws_create_adopt_udp(context->vhost_list, ads,
|
||||
53, 0, lws_async_dns_protocol.name, NULL);
|
||||
if (!dns->wsi) {
|
||||
lwsl_err("%s: foreign socket adoption failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dns->dns_server_set = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lws_adns_cache_t *
|
||||
lws_adns_get_cache(lws_async_dns_t *dns, const char *name)
|
||||
{
|
||||
lws_adns_cache_t *c;
|
||||
const char *cn;
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
lws_dll2_get_head(&dns->cached)) {
|
||||
c = lws_container_of(d, lws_adns_cache_t, list);
|
||||
cn = (const char *)&c[1];
|
||||
|
||||
if (name && !strcasecmp(name, cn)) {
|
||||
/* Keep sorted by LRU: move to the head */
|
||||
lws_dll2_remove(&c->list);
|
||||
lws_dll2_add_head(&c->list, &dns->cached);
|
||||
|
||||
return c;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
lws_adns_cache_destroy(lws_adns_cache_t *c)
|
||||
{
|
||||
lws_dll2_remove(&c->sul.list);
|
||||
lws_dll2_remove(&c->list);
|
||||
if (c->chain)
|
||||
lws_free(c->chain);
|
||||
lws_free(c);
|
||||
}
|
||||
|
||||
static int
|
||||
cache_clean(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_adns_cache_destroy(lws_container_of(d, lws_adns_cache_t, list));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sul_cb_expire(struct lws_sorted_usec_list *sul)
|
||||
{
|
||||
lws_adns_cache_t *c = lws_container_of(sul, lws_adns_cache_t, sul);
|
||||
|
||||
lws_adns_cache_destroy(c);
|
||||
}
|
||||
|
||||
void
|
||||
lws_async_dns_freeaddrinfo(const struct addrinfo *ai)
|
||||
{
|
||||
lws_adns_cache_t *c;
|
||||
|
||||
if (!ai)
|
||||
return;
|
||||
|
||||
/*
|
||||
* First query may have been empty... if second has something, we
|
||||
* fixed up the first result to point to second... but it means
|
||||
* looking backwards from ai, which is c->result, which is the second
|
||||
* packet's results, doesn't get us to the firstcache pointer.
|
||||
*
|
||||
* Adjust c to the firstcache in this case.
|
||||
*/
|
||||
|
||||
c = &((lws_adns_cache_t *)ai)[-1];
|
||||
if (c->firstcache)
|
||||
c = c->firstcache;
|
||||
|
||||
lwsl_debug("%s: c %p, %s, refcount %d -> %d\n", __func__, c,
|
||||
(c->results && c->results->ai_canonname) ?
|
||||
c->results->ai_canonname : "none",
|
||||
c->refcount, c->refcount - 1);
|
||||
|
||||
assert(c->refcount > 0);
|
||||
c->refcount--;
|
||||
}
|
||||
|
||||
void
|
||||
lws_async_dns_trim_cache(lws_async_dns_t *dns)
|
||||
{
|
||||
lws_adns_cache_t *c1;
|
||||
|
||||
if (dns->cached.count + 1< MAX_CACHE_ENTRIES)
|
||||
return;
|
||||
|
||||
c1 = lws_container_of(lws_dll2_get_tail(&dns->cached),
|
||||
lws_adns_cache_t, list);
|
||||
if (c1->refcount)
|
||||
lwsl_notice("%s: wsi %p: refcount %d on purge\n",
|
||||
__func__, c1, c1->refcount);
|
||||
else
|
||||
lws_adns_cache_destroy(c1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
clean(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_adns_q_destroy(lws_container_of(d, lws_adns_q_t, list));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_async_dns_deinit(lws_async_dns_t *dns)
|
||||
{
|
||||
lws_dll2_foreach_safe(&dns->waiting_send, NULL, clean);
|
||||
lws_dll2_foreach_safe(&dns->waiting_resp, NULL, clean);
|
||||
lws_dll2_foreach_safe(&dns->cached, NULL, cache_clean);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
cancel(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d3, d4,
|
||||
lws_dll2_get_head(&q->wsi_adns)) {
|
||||
struct lws *w = lws_container_of(d3, struct lws, adns);
|
||||
|
||||
if (user == w) {
|
||||
lws_dll2_remove(d3);
|
||||
if (!q->wsi_adns.count)
|
||||
lws_adns_q_destroy(q);
|
||||
return 1;
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d3, d4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_async_dns_cancel(struct lws *wsi)
|
||||
{
|
||||
lws_async_dns_t *dns = &wsi->context->async_dns;
|
||||
|
||||
if (!lws_dll2_foreach_safe(&dns->waiting_send, wsi, cancel))
|
||||
lws_dll2_foreach_safe(&dns->waiting_resp, wsi, cancel);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_tid(struct lws_dll2 *d, void *user)
|
||||
{
|
||||
lws_adns_q_t *q = lws_container_of(d, lws_adns_q_t, list);
|
||||
|
||||
return q->tid == (uint16_t)(long)user;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_async_dns_get_new_tid(struct lws_context *context, lws_adns_q_t *q)
|
||||
{
|
||||
lws_async_dns_t *dns = &context->async_dns;
|
||||
int budget = 10;
|
||||
|
||||
/*
|
||||
* Make the TID unpredictable, but must be unique amongst ongoing ones
|
||||
*/
|
||||
do {
|
||||
if (lws_get_random(context, &q->tid, 2) != 2)
|
||||
return -1;
|
||||
|
||||
if (lws_dll2_foreach_safe(&dns->waiting_send,
|
||||
(void *)(long)q->tid, check_tid))
|
||||
continue;
|
||||
|
||||
if (lws_dll2_foreach_safe(&dns->waiting_resp,
|
||||
(void *)(long)q->tid, check_tid))
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
|
||||
} while (budget--);
|
||||
|
||||
lwsl_err("%s: unable to get unique tid\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct temp_q {
|
||||
lws_adns_q_t tq;
|
||||
char name[48];
|
||||
};
|
||||
|
||||
lws_async_dns_retcode_t
|
||||
lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
|
||||
adns_query_type_t qtype, lws_async_dns_cb_t cb,
|
||||
struct lws *wsi, void *opaque)
|
||||
{
|
||||
lws_async_dns_t *dns = &context->async_dns;
|
||||
size_t nlen = strlen(name);
|
||||
lws_sockaddr46 *sa46;
|
||||
lws_adns_cache_t *c;
|
||||
struct addrinfo *ai;
|
||||
struct temp_q tmq;
|
||||
lws_adns_q_t *q;
|
||||
uint8_t ads[16];
|
||||
char *p;
|
||||
int m;
|
||||
|
||||
#if !defined(LWS_WITH_IPV6)
|
||||
if (qtype == LWS_ADNS_RECORD_AAAA) {
|
||||
lwsl_err("%s: ipv6 not enabled\n", __func__);
|
||||
goto failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* we magically know 'localhost' and 'localhost6' if IPv6, this is a
|
||||
* sort of canned /etc/hosts
|
||||
*/
|
||||
|
||||
if (!strcmp(name, "localhost"))
|
||||
name = "127.0.0.1";
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
if (!strcmp(name, "localhost6"))
|
||||
name = "::1";
|
||||
#endif
|
||||
|
||||
if (wsi) {
|
||||
if (!lws_dll2_is_detached(&wsi->adns)) {
|
||||
lwsl_err("%s: wsi %p already bound to query %p\n",
|
||||
__func__, wsi, wsi->adns.owner);
|
||||
goto failed;
|
||||
}
|
||||
wsi->adns_cb = cb;
|
||||
}
|
||||
|
||||
/* there's a done, cached query we can just reuse? */
|
||||
|
||||
c = lws_adns_get_cache(dns, name);
|
||||
if (c) {
|
||||
cb(wsi, name, c->results, 0, opaque);
|
||||
|
||||
return LADNS_RET_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's a 1.2.3.4 type IP address already? We don't need a dns
|
||||
* server set up to be able to create an addrinfo result for that.
|
||||
*
|
||||
* Create it as a cached object so it follows the refcount lifecycle
|
||||
* of any other result
|
||||
*/
|
||||
|
||||
m = lws_parse_numeric_address(name, ads, sizeof(ads));
|
||||
if (m == 4
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
|| m == 16
|
||||
#endif
|
||||
) {
|
||||
lws_async_dns_trim_cache(dns);
|
||||
|
||||
c = lws_zalloc(sizeof(lws_adns_cache_t) +
|
||||
sizeof(struct addrinfo) +
|
||||
sizeof(lws_sockaddr46) + nlen + 1, "adns-numip");
|
||||
if (!c)
|
||||
goto failed;
|
||||
|
||||
ai = (struct addrinfo *)&c[1];
|
||||
sa46 = (lws_sockaddr46 *)&ai[1];
|
||||
|
||||
ai->ai_socktype = SOCK_STREAM;
|
||||
memcpy(&sa46[1], name, nlen + 1);
|
||||
ai->ai_canonname = (char *)&sa46[1];
|
||||
|
||||
c->results = ai;
|
||||
memset(&tmq.tq, 0, sizeof(tmq.tq));
|
||||
tmq.tq.opaque = opaque;
|
||||
if (wsi) {
|
||||
wsi->adns_cb = cb;
|
||||
lws_dll2_add_head(&wsi->adns, &tmq.tq.wsi_adns);
|
||||
} else
|
||||
tmq.tq.standalone_cb = cb;
|
||||
lws_strncpy(tmq.name, name, sizeof(tmq.name));
|
||||
|
||||
lws_dll2_add_head(&c->list, &dns->cached);
|
||||
lws_sul_schedule(context, 0, &c->sul, sul_cb_expire,
|
||||
lws_now_usecs() + (3600ll * LWS_US_PER_SEC));
|
||||
}
|
||||
|
||||
if (m == 4) {
|
||||
ai->ai_family = sa46->sa4.sin_family = AF_INET;
|
||||
ai->ai_addrlen = sizeof(sa46->sa4);
|
||||
ai->ai_addr = (struct sockaddr *)&sa46->sa4;
|
||||
memcpy(&sa46->sa4.sin_addr, ads, m);
|
||||
|
||||
lws_async_dns_complete(&tmq.tq, c);
|
||||
|
||||
return LADNS_RET_FOUND;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
if (m == 16) {
|
||||
ai->ai_family = sa46->sa6.sin6_family = AF_INET6;
|
||||
ai->ai_addrlen = sizeof(sa46->sa6);
|
||||
ai->ai_addr = (struct sockaddr *)&sa46->sa6;
|
||||
memcpy(&sa46->sa6.sin6_addr, ads, m);
|
||||
|
||||
lws_async_dns_complete(&tmq.tq, c);
|
||||
|
||||
return LADNS_RET_FOUND;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* to try anything else we need a remote server configured...
|
||||
*/
|
||||
|
||||
if (!context->async_dns.dns_server_set &&
|
||||
lws_async_dns_init(context)) {
|
||||
lwsl_notice("%s: init failed\n", __func__);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* there's an ongoing query we can share the result of */
|
||||
|
||||
q = lws_adns_get_query(dns, qtype, &dns->waiting_send, 0, name);
|
||||
if (!q)
|
||||
q = lws_adns_get_query(dns, qtype, &dns->waiting_resp, 0, name);
|
||||
if (q) {
|
||||
lwsl_debug("%s: dns piggybacking: %d:%s\n", __func__,
|
||||
qtype, name);
|
||||
if (wsi)
|
||||
lws_dll2_add_head(&wsi->adns, &q->wsi_adns);
|
||||
|
||||
return LADNS_RET_CONTINUING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate new query / queries... this is a bit complicated because
|
||||
* multiple queries in one packet are not supported peoperly in DNS
|
||||
* itself, and there's no reliable other way to get both ipv6 and ipv4
|
||||
* (AAAA and A) responses in one hit.
|
||||
*
|
||||
* If we don't support ipv6, it's simple, we just ask for A and that's
|
||||
* it. But if we do support ipv6, we need to ask twice, once for A
|
||||
* and in a separate query, again for AAAA.
|
||||
*
|
||||
* For ipv6, A / ipv4 is routable over ipv6. So we always ask for A
|
||||
* first and then if ipv6, AAAA separately.
|
||||
*
|
||||
* The results need binding into
|
||||
*/
|
||||
|
||||
q = (lws_adns_q_t *)lws_zalloc(sizeof(*q) + nlen + 1, __func__);
|
||||
if (!q)
|
||||
goto failed;
|
||||
|
||||
if (wsi)
|
||||
lws_dll2_add_head(&wsi->adns, &q->wsi_adns);
|
||||
|
||||
q->qtype = (uint16_t)qtype;
|
||||
|
||||
if (lws_async_dns_get_new_tid(context, q))
|
||||
goto failed;
|
||||
|
||||
q->tid &= 0xfffe;
|
||||
q->context = context;
|
||||
q->tsi = tsi;
|
||||
q->opaque = opaque;
|
||||
|
||||
if (!wsi)
|
||||
q->standalone_cb = cb;
|
||||
|
||||
lws_sul_schedule(context, tsi, &q->sul, sul_cb_timeout,
|
||||
lws_now_usecs() +
|
||||
(DNS_QUERY_TIMEOUT * LWS_US_PER_SEC));
|
||||
|
||||
p = (char *)&q[1];
|
||||
while (nlen--)
|
||||
*p++ = tolower(*name++);
|
||||
*p = '\0';
|
||||
|
||||
lws_callback_on_writable(dns->wsi);
|
||||
|
||||
lws_dll2_add_head(&q->list, &dns->waiting_send);
|
||||
|
||||
lwsl_debug("%s: created new query\n", __func__);
|
||||
|
||||
return LADNS_RET_CONTINUING;
|
||||
|
||||
failed:
|
||||
cb(wsi, NULL, NULL, LADNS_RET_FAILED, opaque);
|
||||
|
||||
return LADNS_RET_FAILED;
|
||||
}
|
110
lib/system/async-dns/private-lib-async-dns.h
Normal file
110
lib/system/async-dns/private-lib-async-dns.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#define DNS_MAX 128 /* Maximum host name */
|
||||
#define DNS_PACKET_LEN 1400 /* Buffer size for DNS packet */
|
||||
#define MAX_CACHE_ENTRIES 10 /* Dont cache more than that */
|
||||
#define DNS_QUERY_TIMEOUT 30 /* Query timeout, seconds */
|
||||
|
||||
/*
|
||||
* ... when we completed a query then the query object is destroyed and a
|
||||
* cache object below is created with the results in getaddrinfo format
|
||||
* appended to the allocation
|
||||
*/
|
||||
|
||||
typedef struct lws_adns_cache {
|
||||
lws_sorted_usec_list_t sul; /* for cache TTL management */
|
||||
lws_dll2_t list;
|
||||
|
||||
struct lws_adns_cache *firstcache;
|
||||
struct lws_adns_cache *chain;
|
||||
struct addrinfo *results;
|
||||
uint8_t flags; /* b0 = has ipv4, b1 = has ipv6 */
|
||||
char refcount;
|
||||
/* name, and then result struct addrinfos overallocated here */
|
||||
} lws_adns_cache_t;
|
||||
|
||||
/*
|
||||
* these objects are used while a query is ongoing...
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
lws_sorted_usec_list_t sul; /* for query network timeout */
|
||||
lws_dll2_t list;
|
||||
|
||||
lws_dll2_owner_t wsi_adns;
|
||||
lws_async_dns_cb_t standalone_cb; /* if not associated to wsi */
|
||||
struct lws_context *context;
|
||||
void *opaque;
|
||||
struct addrinfo **last;
|
||||
|
||||
lws_adns_cache_t *firstcache;
|
||||
|
||||
lws_async_dns_retcode_t ret;
|
||||
uint16_t tid;
|
||||
uint16_t qtype;
|
||||
uint8_t tsi;
|
||||
|
||||
uint8_t asked;
|
||||
uint8_t responded;
|
||||
|
||||
/* name overallocated here */
|
||||
} lws_adns_q_t;
|
||||
|
||||
enum {
|
||||
DHO_TID,
|
||||
DHO_FLAGS = 2,
|
||||
DHO_NQUERIES = 4,
|
||||
DHO_NANSWERS = 6,
|
||||
DHO_NAUTH = 8,
|
||||
DHO_NOTHER = 10,
|
||||
|
||||
DHO_SIZEOF = 12 /* last */
|
||||
};
|
||||
|
||||
void
|
||||
lws_adns_q_destroy(lws_adns_q_t *q);
|
||||
|
||||
void
|
||||
sul_cb_expire(struct lws_sorted_usec_list *sul);
|
||||
|
||||
void
|
||||
lws_adns_cache_destroy(lws_adns_cache_t *c);
|
||||
|
||||
int
|
||||
lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c);
|
||||
|
||||
lws_adns_cache_t *
|
||||
lws_adns_get_cache(lws_async_dns_t *dns, const char *name);
|
||||
|
||||
void
|
||||
lws_adns_parse_udp(lws_async_dns_t *dns, const uint8_t *pkt, size_t len);
|
||||
|
||||
lws_adns_q_t *
|
||||
lws_adns_get_query(lws_async_dns_t *dns, adns_query_type_t qtype,
|
||||
lws_dll2_owner_t *owner, uint16_t tid, const char *name);
|
||||
|
||||
void
|
||||
lws_async_dns_trim_cache(lws_async_dns_t *dns);
|
78
minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt
Normal file
78
minimal-examples/api-tests/api-test-async-dns/CMakeLists.txt
Normal file
|
@ -0,0 +1,78 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-api-test-async-dns)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_H1 1 requirements)
|
||||
require_lws_config(LWS_WITH_CLIENT 1 requirements)
|
||||
require_lws_config(LWS_WITH_SYS_ASYNC_DNS 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
315
minimal-examples/api-tests/api-test-async-dns/main.c
Normal file
315
minimal-examples/api-tests/api-test-async-dns/main.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* lws-api-test-async-dns
|
||||
*
|
||||
* Written in 2019 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This api test confirms various kinds of async dns apis
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <signal.h>
|
||||
|
||||
static int interrupted, dtest, ok, fail, exp = 26;
|
||||
struct lws_context *context;
|
||||
|
||||
/*
|
||||
* These are used to test the apis to parse and print ipv4 / ipv6 literal
|
||||
* address strings for various cases.
|
||||
*
|
||||
* Expected error cases are not used to test the ip data -> string api.
|
||||
*/
|
||||
|
||||
static const struct ipparser_tests {
|
||||
const char *test;
|
||||
int rlen;
|
||||
const char *emit_test;
|
||||
int emit_len;
|
||||
uint8_t b[16];
|
||||
} ipt[] = {
|
||||
{ "2001:db8:85a3:0:0:8a2e:370:7334", 16,
|
||||
"2001:db8:85a3::8a2e:370:7334", 28,
|
||||
{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,
|
||||
0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },
|
||||
|
||||
{ "2001:db8:85a3::8a2e:370:7334", 16,
|
||||
"2001:db8:85a3::8a2e:370:7334", 28,
|
||||
{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00,
|
||||
0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 } },
|
||||
|
||||
{ "::1", 16, "::1", 3,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ "::", 16, "::", 2,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
{ "::ffff:192.0.2.128", 16, "::ffff:192.0.2.128", 18,
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0x02, 0x80 } },
|
||||
|
||||
{ "cats", -1, "", 0,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ "onevalid.bogus.warmcat.com", -1, "", 0,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ "1.cat.dog.com", -1, "", 0,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ ":::1", -8, "", 0,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ "0:0::0:1", 16, "::1", 3,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
|
||||
|
||||
{ "1.2.3.4", 4, "1.2.3.4", 7, { 1, 2, 3, 4 } },
|
||||
};
|
||||
|
||||
static const struct async_dns_tests {
|
||||
const char *dns_name;
|
||||
int recordtype;
|
||||
int addrlen;
|
||||
uint8_t ads[16];
|
||||
} adt[] = {
|
||||
{ "warmcat.com", LWS_ADNS_RECORD_A, 4,
|
||||
{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "libwebsockets.org", LWS_ADNS_RECORD_A, 4,
|
||||
{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "doesntexist", LWS_ADNS_RECORD_A, 0,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "localhost", LWS_ADNS_RECORD_A, 4,
|
||||
{ 127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "ipv4only.warmcat.com", LWS_ADNS_RECORD_A, 4,
|
||||
{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "onevalid.bogus.warmcat.com", LWS_ADNS_RECORD_A, 4,
|
||||
{ 46, 105, 127, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
{ "warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */
|
||||
{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
{ "ipv6only.warmcat.com", LWS_ADNS_RECORD_AAAA, 16, /* check ipv6 */
|
||||
{ 0x20, 0x01, 0x41, 0xd0, 0x00, 0x02, 0xee, 0x93,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, } },
|
||||
#endif
|
||||
};
|
||||
|
||||
static lws_sorted_usec_list_t sul;
|
||||
|
||||
struct lws *
|
||||
cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
|
||||
void *opaque);
|
||||
|
||||
static void
|
||||
next_test_cb(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
int m;
|
||||
|
||||
lwsl_notice("%s: querying %s\n", __func__, adt[dtest].dns_name);
|
||||
|
||||
m = lws_async_dns_query(context, 0,
|
||||
adt[dtest].dns_name,
|
||||
adt[dtest].recordtype, cb1, NULL,
|
||||
context);
|
||||
if (m != LADNS_RET_CONTINUING && m != LADNS_RET_FOUND) {
|
||||
lwsl_err("%s: adns 1 failed: %d\n", __func__, m);
|
||||
interrupted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct lws *
|
||||
cb1(struct lws *wsi_unused, const char *ads, const struct addrinfo *a, int n,
|
||||
void *opaque)
|
||||
{
|
||||
const struct addrinfo *ac = a;
|
||||
int ctr = 0, alen;
|
||||
uint8_t *addr;
|
||||
char buf[64];
|
||||
|
||||
dtest++;
|
||||
|
||||
if (!ac)
|
||||
lwsl_warn("%s: no results\n", __func__);
|
||||
|
||||
/* dump the results */
|
||||
|
||||
while (ac) {
|
||||
if (ac->ai_family == AF_INET) {
|
||||
addr = (uint8_t *)&(((struct sockaddr_in *)
|
||||
ac->ai_addr)->sin_addr.s_addr);
|
||||
alen = 4;
|
||||
} else {
|
||||
addr = (uint8_t *)&(((struct sockaddr_in6 *)
|
||||
ac->ai_addr)->sin6_addr.s6_addr);
|
||||
alen = 16;
|
||||
}
|
||||
strcpy(buf, "unknown");
|
||||
lws_write_numeric_address(addr, alen, buf, sizeof(buf));
|
||||
|
||||
lwsl_warn("%s: %d: %s %d %s\n", __func__, ctr++, ads, alen, buf);
|
||||
|
||||
ac = ac->ai_next;
|
||||
}
|
||||
|
||||
ac = a;
|
||||
while (ac) {
|
||||
if (ac->ai_family == AF_INET) {
|
||||
addr = (uint8_t *)&(((struct sockaddr_in *)
|
||||
ac->ai_addr)->sin_addr.s_addr);
|
||||
alen = 4;
|
||||
} else {
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
addr = (uint8_t *)&(((struct sockaddr_in6 *)
|
||||
ac->ai_addr)->sin6_addr.s6_addr);
|
||||
alen = 16;
|
||||
#else
|
||||
goto again;
|
||||
#endif
|
||||
}
|
||||
if (alen == adt[dtest - 1].addrlen &&
|
||||
!memcmp(adt[dtest - 1].ads, addr, alen)) {
|
||||
ok++;
|
||||
goto next;
|
||||
}
|
||||
#if !defined(LWS_WITH_IPV6)
|
||||
again:
|
||||
#endif
|
||||
ac = ac->ai_next;
|
||||
}
|
||||
|
||||
/* testing for NXDOMAIN? */
|
||||
|
||||
if (!a && !adt[dtest - 1].addrlen) {
|
||||
ok++;
|
||||
goto next;
|
||||
}
|
||||
|
||||
lwsl_err("%s: dns test %d: no match\n", __func__, dtest);
|
||||
fail++;
|
||||
|
||||
next:
|
||||
lws_async_dns_freeaddrinfo(a);
|
||||
if (dtest == (int)LWS_ARRAY_SIZE(adt))
|
||||
interrupted = 1;
|
||||
else
|
||||
lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
const char *p;
|
||||
|
||||
/* the normal lws init */
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS API selftest: Async DNS\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ip address parser tests */
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
|
||||
uint8_t u[16];
|
||||
int m = lws_parse_numeric_address(ipt[n].test, u, sizeof(u));
|
||||
|
||||
if (m != ipt[n].rlen) {
|
||||
lwsl_err("%s: fail %s ret %d\n",
|
||||
__func__, ipt[n].test, m);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m > 0) {
|
||||
if (memcmp(ipt[n].b, u, m)) {
|
||||
lwsl_err("%s: fail %s compare\n", __func__,
|
||||
ipt[n].test);
|
||||
lwsl_hexdump_notice(u, m);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ok++;
|
||||
}
|
||||
|
||||
/* ip address formatter tests */
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(ipt); n++) {
|
||||
char buf[64];
|
||||
int m;
|
||||
|
||||
/* don't attempt to reverse the ones that are meant to fail */
|
||||
if (ipt[n].rlen < 0)
|
||||
continue;
|
||||
|
||||
m = lws_write_numeric_address(ipt[n].b, ipt[n].rlen, buf,
|
||||
sizeof(buf));
|
||||
if (m != ipt[n].emit_len) {
|
||||
lwsl_err("%s: fail %s ret %d\n",
|
||||
__func__, ipt[n].emit_test, m);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m > 0) {
|
||||
if (strcmp(ipt[n].emit_test, buf)) {
|
||||
lwsl_err("%s: fail %s compare\n", __func__,
|
||||
ipt[n].test);
|
||||
lwsl_hexdump_notice(buf, m);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ok++;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_IPV6)
|
||||
exp -= 2;
|
||||
#endif
|
||||
|
||||
/* kick off the async dns tests */
|
||||
|
||||
lws_sul_schedule(context, 0, &sul, next_test_cb, 1);
|
||||
|
||||
/* the usual lws event loop */
|
||||
|
||||
n = 1;
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
if (fail || ok != exp)
|
||||
lwsl_user("Completed: PASS: %d / %d, FAIL: %d\n", ok, exp,
|
||||
fail);
|
||||
else
|
||||
lwsl_user("Completed: ALL PASS: %d / %d\n", ok, exp);
|
||||
|
||||
return !(ok == exp && !fail);
|
||||
}
|
24
minimal-examples/api-tests/api-test-async-dns/selftest.sh
Executable file
24
minimal-examples/api-tests/api-test-async-dns/selftest.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# $1: path to minimal example binaries...
|
||||
# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1
|
||||
# that will be ./bin from your build dir
|
||||
#
|
||||
# $2: path for logs and results. The results will go
|
||||
# in a subdir named after the directory this script
|
||||
# is in
|
||||
#
|
||||
# $3: offset for test index count
|
||||
#
|
||||
# $4: total test count
|
||||
#
|
||||
# $5: path to ./minimal-examples dir in lws
|
||||
#
|
||||
# Test return code 0: OK, 254: timed out, other: error indication
|
||||
|
||||
. $5/selftests-library.sh
|
||||
|
||||
COUNT_TESTS=1
|
||||
|
||||
dotest $1 $2 apiselftest
|
||||
exit $FAILS
|
|
@ -307,6 +307,9 @@ int main(int argc, const char **argv)
|
|||
if (lws_cmdline_option(argc, argv, "-n"))
|
||||
numbered = 1;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "--server")))
|
||||
i.address = p;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "--port")))
|
||||
i.port = atoi(p);
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ ENDMACRO()
|
|||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITH_SERVER 1 requirements)
|
||||
require_lws_config(LWS_WITH_CLIENT 1 requirements)
|
||||
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
|
|
@ -169,7 +169,7 @@ int main(int argc, const char **argv)
|
|||
/*
|
||||
* Create our own "foreign" UDP socket bound to 7681/udp
|
||||
*/
|
||||
if (!lws_create_adopt_udp(vhost, 7681, LWS_CAUDP_BIND,
|
||||
if (!lws_create_adopt_udp(vhost, NULL, 7681, LWS_CAUDP_BIND,
|
||||
protocols[0].name, NULL)) {
|
||||
lwsl_err("%s: foreign socket adoption failed\n", __func__);
|
||||
goto bail;
|
||||
|
|
Loading…
Add table
Reference in a new issue