1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

client: rfc6724 dns results sorting

RFC6724 defines an ipv6-centric DNS result sorting algorithm, that
takes route and source address route information for the results
given by the DNS resolution, and sorts them in order of preferability,
which defines the order they should be tried in.

If LWS_WITH_NETLINK, then lws takes care about collecting and monitoring
the interface, route and source address information, and uses it to
perform the RFC6724 sorting to re-sort the DNS before trying to make
the connections.
This commit is contained in:
Andy Green 2020-09-20 09:14:46 +01:00
parent d38d1176ca
commit 9eb4c4fac2
24 changed files with 1066 additions and 193 deletions

View file

@ -416,3 +416,7 @@ if (WIN32 AND NOT LWS_EXT_PTHREAD_LIBRARIES)
set(LWS_WITH_SYS_SMD 0)
endif()
if (LWS_WITH_RFC6724 AND (NOT LWS_WITH_CLIENT OR NOT LWS_IPV6))
message(FATAL_ERROR "RFC6724 requires CLIENT and LWS_IPV6")
endif()

View file

@ -115,6 +115,11 @@ option(LWS_WITH_HTTP_BASIC_AUTH "Support Basic Auth" ON)
option(LWS_WITH_HTTP_UNCOMMON_HEADERS "Include less common http header support" ON)
option(LWS_WITH_SYS_STATE "lws_system state support" ON)
option(LWS_WITH_SYS_SMD "Lws System Message Distribution" ON)
if (LWS_IPV6)
option(LWS_WITH_RFC6724 "Enable RFC6724 DNS result sorting" ON)
else()
option(LWS_WITH_RFC6724 "Enable RFC6724 DNS result sorting" OFF)
endif()
#
# Secure Streams

View file

@ -166,6 +166,7 @@
#cmakedefine LWS_WITH_POLARSSL
#cmakedefine LWS_WITH_POLL
#cmakedefine LWS_WITH_RANGES
#cmakedefine LWS_WITH_RFC6724
#cmakedefine LWS_WITH_SECURE_STREAMS
#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM
#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API

View file

@ -86,27 +86,38 @@ typedef union {
typedef union {
#if defined(LWS_WITH_IPV6)
struct sockaddr_in6 sa6;
#else
#if defined(LWS_ESP_PLATFORM)
uint8_t _pad_sa6[28];
#endif
#endif
struct sockaddr_in sa4;
} lws_sockaddr46;
#define sa46_sockaddr(_sa46) ((struct sockaddr *)(_sa46))
#define sa46_address(_sa46) ((uint8_t *)((_sa46)->sa4.sin_family == AF_INET ? \
&_sa46->sa4.sin_addr : &_sa46->sa6.sin6_addr ))
#define sa46_address_len(_sa46) ((_sa46)->sa4.sin_family == AF_INET ? 4 : 16)
#define sa46_socklen(_sa46) ((_sa46)->sa4.sin_family == AF_INET ? \
#if defined(LWS_WITH_IPV6)
#define sa46_socklen(_sa46) (socklen_t)((_sa46)->sa4.sin_family == AF_INET ? \
sizeof(struct sockaddr_in) : \
sizeof(struct sockaddr_in6))
#define sa46_sockport(_sa46, _sp) { if ((_sa46)->sa4.sin_family == AF_INET) \
(_sa46)->sa4.sin_port = (_sp); else \
(_sa46)->sa6.sin6_port = (_sp); }
#define sa46_address(_sa46) ((uint8_t *)((_sa46)->sa4.sin_family == AF_INET ? \
&_sa46->sa4.sin_addr : &_sa46->sa6.sin6_addr ))
#else
#define sa46_socklen(_sa46) (socklen_t)sizeof(struct sockaddr_in)
#define sa46_sockport(_sa46, _sp) (_sa46)->sa4.sin_port = (_sp)
#define sa46_address(_sa46) (uint8_t *)&_sa46->sa4.sin_addr
#endif
#define sa46_address_len(_sa46) ((_sa46)->sa4.sin_family == AF_INET ? 4 : 16)
#if defined(LWS_WITH_UDP)
struct lws_udp {
lws_sockaddr46 sa46;
lws_sockaddr46 sa46_pending;
};
#endif
/**

View file

@ -43,8 +43,7 @@ typedef enum {
struct addrinfo;
typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads,
const struct addrinfo *result, int n,
void *opaque);
const struct addrinfo *result, int n, void *opaque);
/**
* lws_async_dns_query() - perform a dns lookup using async dns

View file

@ -245,6 +245,11 @@ LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i));
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted_priv(lws_dll2_t *d, lws_dll2_owner_t *own, void *priv,
int (*compare3)(void *priv, const lws_dll2_t *d,
const lws_dll2_t *i));
LWS_VISIBLE LWS_EXTERN void *
_lws_dll2_search_sz_pl(lws_dll2_owner_t *own, const char *name, size_t namelen,
size_t dll2_ofs, size_t ptr_ofs);

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2020 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
@ -67,6 +67,13 @@ typedef struct lws_route {
uint8_t source_ads:1;
} lws_route_t;
/*
* We reuse the route object as the dns sort granule, so there's only one
* struct needs to know all the gnarly ipv6 details
*/
typedef lws_route_t lws_dns_sort_t;
/**
* lws_canonical_hostname() - returns this host's hostname
*

View file

@ -73,6 +73,7 @@ if (LWS_WITH_CLIENT)
core-net/client/connect2.c
core-net/client/connect3.c
core-net/client/connect4.c
core-net/client/sort-dns.c
)
endif()

View file

@ -610,18 +610,21 @@ bail:
#if defined(LWS_WITH_UDP)
#if defined(LWS_WITH_CLIENT)
/*
* This is the ASYNC_DNS callback target for udp client, it's analogous to
* connect3()
*/
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;
int bc = 1;
int bc = 1, m;
assert(wsi);
if (!wsi->dns_results)
wsi->dns_results_next = wsi->dns_results = r;
if (ads && (n < 0 || !r)) {
/*
* DNS lookup failed: there are no usable results. Fail the
@ -629,16 +632,28 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads,
*/
lwsl_notice("%s: bad: n %d, r %p\n", __func__, n, r);
/*
* We didn't get a callback on a cache item and bump the
* refcount. So don't let the cleanup continue to think it
* needs to decrement any refcount.
*/
wsi->dns_results_next = wsi->dns_results = NULL;
goto bail;
}
while (wsi->dns_results_next) {
m = lws_sort_dns(wsi, r);
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lws_async_dns_freeaddrinfo(&r);
#else
freeaddrinfo((struct addrinfo *)r);
#endif
if (m)
goto bail;
while (lws_dll2_get_head(&wsi->dns_sorted_list)) {
lws_dns_sort_t *s = lws_container_of(
lws_dll2_get_head(&wsi->dns_sorted_list),
lws_dns_sort_t, list);
/*
* Remove it from the head, but don't free it yet... we are
* taking responsibility to free it
*/
lws_dll2_remove(&s->list);
/*
* We have done the dns lookup, identify the result we want
@ -651,30 +666,35 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads,
*/
#if !defined(__linux__)
/* PF_PACKET is linux-only */
sock.sockfd = socket(wsi->dns_results_next->ai_family,
sock.sockfd = socket(s->dest.sa4.sin_family,
SOCK_DGRAM, IPPROTO_UDP);
#else
/* PF_PACKET is linux-only */
sock.sockfd = socket(wsi->pf_packet ? PF_PACKET :
wsi->dns_results_next->ai_family,
s->dest.sa4.sin_family,
SOCK_DGRAM, wsi->pf_packet ?
htons(0x800) : IPPROTO_UDP);
#endif
if (sock.sockfd == LWS_SOCK_INVALID)
goto resume;
((struct sockaddr_in *)wsi->dns_results_next->ai_addr)->sin_port =
htons(wsi->c_port);
/* ipv6 udp!!! */
if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&bc,
sizeof(bc)) < 0)
if (s->af == AF_INET)
s->dest.sa4.sin_port = htons(wsi->c_port);
#if defined(LWS_WITH_IPV6)
else
s->dest.sa6.sin6_port = htons(wsi->c_port);
#endif
if (setsockopt(sock.sockfd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&bc, sizeof(bc)) < 0)
lwsl_err("%s: failed to set reuse\n", __func__);
if (wsi->do_broadcast &&
setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST, (const char *)&bc,
sizeof(bc)) < 0)
lwsl_err("%s: failed to set broadcast\n",
__func__);
setsockopt(sock.sockfd, SOL_SOCKET, SO_BROADCAST,
(const char *)&bc, sizeof(bc)) < 0)
lwsl_err("%s: failed to set broadcast\n", __func__);
/* Bind the udp socket to a particular network interface */
@ -683,52 +703,53 @@ lws_create_adopt_udp2(struct lws *wsi, const char *ads,
goto resume;
if (wsi->do_bind &&
bind(sock.sockfd, wsi->dns_results_next->ai_addr,
bind(sock.sockfd, sa46_sockaddr(&s->dest),
#if defined(_WIN32)
(int)wsi->dns_results_next->ai_addrlen
(int)sa46_socklen(&s->dest)
#else
sizeof(struct sockaddr)//wsi->dns_results_next->ai_addrlen
sizeof(struct sockaddr)
#endif
) == -1) {
) == -1) {
lwsl_err("%s: bind failed\n", __func__);
goto resume;
}
if (!wsi->do_bind && !wsi->pf_packet) {
#if !defined(__APPLE__)
if (connect(sock.sockfd, wsi->dns_results_next->ai_addr,
(socklen_t)wsi->dns_results_next->ai_addrlen) == -1) {
if (connect(sock.sockfd, sa46_sockaddr(&s->dest),
sa46_socklen(&s->dest)) == -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,
"errno %d\n", __func__, sock.sockfd,
s->dest.sa4.sin_family,
ads ? ads : "null", wsi->c_port,
(int)wsi->dns_results_next->ai_addrlen,
LWS_ERRNO);
compatible_close(sock.sockfd);
goto resume;
}
#endif
memcpy(&wsi->udp->sa46,
wsi->dns_results_next->ai_addr,
wsi->dns_results_next->ai_addrlen);
}
if (wsi->udp)
wsi->udp->sa46 = s->dest;
wsi->sa46_peer = s->dest;
/* we connected: complete the udp socket adoption flow */
lws_free(s);
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;
lws_free(s);
}
lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO);
lws_addrinfo_clean(wsi);
bail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail");
/* caller must close */
return NULL;
}
@ -747,12 +768,15 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
/* create the logical wsi without any valid fd */
wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_RAW_SOCKET_UDP,
wsi = lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET | LWS_ADOPT_RAW_SOCKET_UDP,
protocol_name, parent_wsi, opaque);
if (!wsi) {
lwsl_err("%s: udp wsi creation failed\n", __func__);
goto bail;
}
// lwsl_notice("%s: role %s\n", __func__, wsi->role_ops->name);
wsi->do_bind = !!(flags & LWS_CAUDP_BIND);
wsi->do_broadcast = !!(flags & LWS_CAUDP_BROADCAST);
wsi->pf_packet = !!(flags & LWS_CAUDP_PF_PACKET);
@ -814,8 +838,9 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
*/
n = lws_async_dns_query(vhost->context, 0, ads,
LWS_ADNS_RECORD_A,
lws_create_adopt_udp2, wsi, (void *)ifname);
lwsl_debug("%s: dns query returned %d\n", __func__, n);
lws_create_adopt_udp2, wsi,
(void *)ifname);
// lwsl_notice("%s: dns query returned %d\n", __func__, n);
if (n == LADNS_RET_FAILED) {
lwsl_err("%s: async dns failed\n", __func__);
wsi = NULL;
@ -832,7 +857,7 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
/* dns lookup is happening asynchronously */
lwsl_debug("%s: returning wsi %p\n", __func__, wsi);
// lwsl_notice("%s: returning wsi %p\n", __func__, wsi);
return wsi;
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)

View file

@ -285,10 +285,13 @@ solo:
wsi->detlat.earliest_write_req_pre_write = lws_now_usecs();
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
if (wsi->dns_results)
n = 0;
else
n = 0;
if (!wsi->dns_sorted_list.count) {
/*
* blocking dns resolution
*/
n = lws_getaddrinfo46(wsi, ads, &result);
}
#else
lwsi_set_state(wsi, LRS_WAITING_DNS);
/* this is either FAILED, CONTINUING, or already called connect_4 */

View file

@ -108,21 +108,36 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
{
#if defined(LWS_WITH_UNIX_SOCK)
struct sockaddr_un sau;
#endif
#ifdef LWS_WITH_IPV6
char ipv6only = lws_check_opt(wsi->a.vhost->options,
LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY |
LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
#endif
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
const struct sockaddr *psa = NULL;
uint16_t port = wsi->c_port;
const char *cce, *iface;
lws_sockaddr46 sa46;
lws_dns_sort_t *curr;
ssize_t plen = 0;
lws_dll2_t *d;
int m;
if (n == LWS_CONNECT_COMPLETION_GOOD)
/*
* If we come here with result set, we need to convert getaddrinfo
* results to a lws_dns_sort_t list one time and free the results.
*
* We use this pattern because ASYNC_DNS will callback here with the
* results when it gets them (and may come here more than once, eg, for
* AAAA then A or vice-versa)
*/
if (result) {
lws_sort_dns(wsi, result);
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lws_async_dns_freeaddrinfo(&result);
#else
freeaddrinfo((struct addrinfo *)result);
#endif
result = NULL;
}
if (n == LWS_CONNECT_COMPLETION_GOOD)
goto conn_good;
#if defined(LWS_WITH_IPV6) && defined(__ANDROID__)
@ -138,6 +153,16 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
if (!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue))
return wsi;
if (n && /* calling back with a problem */
!wsi->dns_sorted_list.count && /* there's no results */
!lws_socket_is_valid(wsi->desc.sockfd) && /* no attempt ongoing */
!wsi->speculative_connect_owner.count /* no spec attempt */ ) {
lwsl_notice("%s: dns lookup failed %d\n", __func__, n);
cce = "dns lookup failed";
goto oom4;
}
/*
* We come back here again when we think the connect() may have
* completed one way or the other, we can't proceed until we know we
@ -147,7 +172,8 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
lws_socket_is_valid(wsi->desc.sockfd)) {
if (!result && !wsi->sul_connect_timeout.list.owner)
if (!wsi->dns_sorted_list.count &&
!wsi->sul_connect_timeout.list.owner)
/* no dns results and no ongoing timeout for one */
goto connect_to;
@ -169,7 +195,7 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
#if defined(LWS_WITH_UNIX_SOCK)
if (ads && *ads == '+') {
ads++;
memset(&sa46, 0, sizeof(sa46));
memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer));
memset(&sau, 0, sizeof(sau));
sau.sun_family = AF_UNIX;
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
@ -195,13 +221,6 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
}
#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_WITH_DETAILED_LATENCY)
if (lwsi_state(wsi) == LRS_WAITING_DNS &&
wsi->a.context->detailed_latency_cb) {
@ -214,105 +233,47 @@ lws_client_connect_3_connect(struct lws *wsi, const char *ads,
wsi->detlat.earliest_write_req_pre_write = lws_now_usecs();
}
#endif
#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->a.vhost->http.http_proxy_port) {
port = wsi->a.vhost->http.http_proxy_port;
#else
if (0) {
#endif
#if defined(LWS_WITH_SOCKS5)
/*
* Priority 2: Connect to SOCK5 Proxy
*/
} else if (wsi->a.vhost->socks_proxy_port) {
if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_GREETING, &plen)) {
cce = "socks msg too large";
goto oom4;
}
lwsl_client("Sending SOCKS Greeting\n");
ads = wsi->a.vhost->socks_proxy_address;
port = wsi->a.vhost->socks_proxy_port;
#endif
}
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 directly connecting to each of the results in turn until
* one works, or we run out of results...
*
* We have a sorted dll2 list with the head one most preferable
*/
next_dns_result:
if (!wsi->dns_sorted_list.count)
goto failed1;
/*
* Make a possibly 4->6 adapted copy of the next dns result in sa46
* Copy the wsi head sorted dns result into the wsi->sa46_peer, and
* remove and free the original from the sorted list
*/
psa = (const struct sockaddr *)&sa46;
n = sizeof(struct sockaddr_in6);
memset(&sa46, 0, sizeof(sa46));
d = lws_dll2_get_head(&wsi->dns_sorted_list);
curr = lws_container_of(d, lws_dns_sort_t, list);
switch (wsi->dns_results_next->ai_family) {
case AF_INET:
#if defined(LWS_WITH_IPV6)
if (ipv6only) {
lws_sa46_4to6(&sa46, &((struct sockaddr_in *)
wsi->dns_results_next->ai_addr)->sin_addr,
port);
break;
}
lws_dll2_remove(&curr->list);
wsi->sa46_peer = curr->dest;
#if defined(LWS_WITH_NETLINK)
wsi->peer_route_uidx = curr->uidx;
lwsl_info("%s: peer_route_uidx %d\n", __func__, wsi->peer_route_uidx);
#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;
sa46.sa4.sin_port = htons(port);
n = sizeof(struct sockaddr_in);
break;
case AF_INET6:
#if defined(LWS_WITH_IPV6)
if (!wsi->ipv6)
goto try_next_dns_result;
lws_sa46_copy_address(&sa46, &((struct sockaddr_in6 *)
wsi->dns_results_next->ai_addr)->
sin6_addr, AF_INET6);
lws_free(curr);
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(port);
#else
goto try_next_dns_result; /* ipv4 only can't use this */
#endif
break;
}
sa46_sockport(&wsi->sa46_peer, htons(port));
psa = sa46_sockaddr(&wsi->sa46_peer);
n = sa46_socklen(&wsi->sa46_peer);
#if defined(LWS_WITH_UNIX_SOCK)
ads_known:
#endif
/*
* Now we prepared sa46, if not already connecting, create the related
* Now we prepared psa, if not already connecting, create the related
* socket and add to the fds
*/
@ -330,7 +291,7 @@ ads_known:
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
else
#endif
wsi->desc.sockfd = socket(sa46.sa4.sin_family,
wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family,
SOCK_STREAM, 0);
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
@ -457,12 +418,19 @@ ads_known:
*/
#if defined(_DEBUG)
#if defined(LWS_WITH_UNIX_SOCK)
if (!wsi->unix_skt) {
#endif
char nads[48];
lws_sa46_write_numeric_address(&sa46, nads,
lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
sizeof(nads));
lwsl_info("%s: Connect failed: %s port %d\n", __func__,
nads, port);
#if defined(LWS_WITH_UNIX_SOCK)
}
#endif
#endif
goto try_next_dns_result_fds;
@ -506,7 +474,6 @@ conn_good:
*/
lws_sul_cancel(&wsi->sul_connect_timeout);
lwsl_info("%s: Connection started %p\n", __func__, wsi->dns_results);
#if defined(LWS_WITH_DETAILED_LATENCY)
if (wsi->a.context->detailed_latency_cb) {
@ -585,11 +552,9 @@ try_next_dns_result_closesock:
try_next_dns_result:
lws_sul_cancel(&wsi->sul_connect_timeout);
if (wsi->dns_results_next) {
wsi->dns_results_next = wsi->dns_results_next->ai_next;
if (wsi->dns_results_next)
goto next_dns_result;
}
if (lws_dll2_get_head(&wsi->dns_sorted_list))
goto next_dns_result;
lws_addrinfo_clean(wsi);
cce = "Unable to connect";
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));

View file

@ -0,0 +1,774 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 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.
*
*
* Either the libc getaddrinfo() or ASYNC_DNS provides a chain of addrinfo,
* we use lws_sort_dns() to convert it to an lws_dll2 of lws_dns_sort_t, after
* which the addrinfo results are freed.
*
* If the system has no routing table info (from, eg, NETLINK), then that's
* it the sorted results are bound to the wsi and used.
*
* If the system has routing table info, we study the routing table and the
* DNS results in order to sort the lws_dns_sort_t result linked-list into
* most desirable at the head, and strip results we can't see a way to route.
*/
#include "private-lib-core.h"
#if defined(__linux__)
#include <linux/if_addr.h>
#endif
#if defined(__FreeBSD__)
#include <net/if.h>
#include <netinet6/in6_var.h>
#endif
#if defined(LWS_WITH_IPV6) && defined(LWS_WITH_NETLINK)
/*
* RFC6724 default policy table
*
* Prefix Precedence Label
* ::1/128 50 0
* ::/0 40 1
* ::ffff:0:0/96 35 4 (override prec to 100 to prefer ipv4)
* 2002::/16 30 2
* 2001::/32 5 5
* fc00::/7 3 13
* ::/96 1 3
* fec0::/10 1 11
* 3ffe::/16 1 12
*
* implemented using offsets into a combined 40-byte table below
*/
static const uint8_t ma[] = {
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
/* 28 */ 0x20, 0x02,
/* 30 */ 0x20, 0x01, 0x00, 0x00,
/* 34 */ 0xfc, 0x00,
/* 36 */ 0xfe, 0xc0,
/* 38 */ 0x3f, 0xfe
};
static const uint8_t frac[] = {
0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
};
/* 9 x 4 byte = 36 byte policy index table */
static const struct score_policy {
uint8_t ma_ofs;
uint8_t prefix;
lws_dns_score_t score;
} rfc6724_policy[] = {
{ 0, 128, { 50, 0 } }, /* ::1/128 */
{ 0, 0, { 40, 1 } }, /* ::0 */
#if 1
/* favour ipv6 as a general policy */
{ 16, 96, { 35, 4 } }, /* ::ffff:0:0/96 */
#else
/* favour ipv4 as a general policy */
{ 16, 96, { 100, 4 } }, /* ::ffff:0:0/96 */
#endif
{ 28, 16, { 30, 2 } }, /* 2002::/16 */
{ 30, 32, { 5, 5 } }, /* 2001::/32 */
{ 34, 7, { 3, 13 } }, /* fc00::/7 */
{ 0, 96, { 1, 3 } }, /* ::/96 */
{ 36, 10, { 1, 11 } }, /* fec0::/10 */
{ 38, 16, { 1, 12 } }, /* 3ffe::/16 */
};
static int
lws_ipv6_prefix_match_len(const struct sockaddr_in6 *a,
const struct sockaddr_in6 *b)
{
const uint8_t *ads_a = (uint8_t *)&a->sin6_addr,
*ads_b = (uint8_t *)&b->sin6_addr;
int n = 0, match = 0;
for (n = 0; n < 16; n++) {
if (ads_a[n] == ads_b[n])
match += 8;
else
break;
}
if (match != 128) {
int m;
for (m = 1; m < 8; m++) {
if ((ads_a[n] & frac[m]) == (ads_b[n] & frac[m]))
match++;
else
break;
}
}
return match;
}
static int
lws_ipv6_unicast_scope(const struct sockaddr_in6 *sa)
{
uint64_t *u;
u = (uint64_t *)&sa->sin6_addr;
if (*u == 0xfe80000000000000ull)
return 2; /* link-local */
return 0xe;
}
static int
lws_sort_dns_scope(lws_sockaddr46 *sa46)
{
if (sa46->sa4.sin_family == AF_INET) {
uint8_t *p = (uint8_t *)&sa46->sa4.sin_addr;
/* RFC6724 3.2 */
if (p[0] == 127 || (p[0] == 169 && p[1] == 254))
return 2; /* link-local */
return 0xe; /* global */
}
return lws_ipv6_unicast_scope(&sa46->sa6);
}
static int
lws_sort_dns_classify(lws_sockaddr46 *sa46, lws_dns_score_t *score)
{
const struct score_policy *pol = rfc6724_policy;
const uint8_t *p, *po;
lws_sockaddr46 s;
int n, m;
if (sa46->sa4.sin_family == AF_INET) {
memset(&s, 0, sizeof(s));
s.sa6.sin6_family = AF_INET6;
lws_4to6((uint8_t *)s.sa6.sin6_addr.s6_addr,
(const uint8_t *)&sa46->sa4.sin_addr);
/* use the v6 version of the v4 address */
sa46 = &s;
}
for (n = 0; n < (int)LWS_ARRAY_SIZE(rfc6724_policy); n++) {
po = (uint8_t *)&sa46->sa6.sin6_addr.s6_addr;
p = &ma[pol->ma_ofs];
for (m = 0; m < pol->prefix >> 3; m++)
if (*p++ != *po++)
goto next;
if ((pol->prefix & 7) && (*p & frac[pol->prefix & 7]) !=
(*po & frac[pol->prefix & 7]))
goto next;
*score = pol->score;
return 0;
next:
pol++;
}
return 1;
}
enum {
SAS_PREFER_A = 1,
SAS_SAME = 0,
SAS_PREFER_B = -1
};
/* ifa is laid out with types for ipv4, if it's AF_INET6 case to sockaddr_in6 */
#define to_v6_sa(x) ((struct sockaddr_in6 *)x)
#define to_sa46_sa(x) ((lws_sockaddr46 *)x)
/*
* The source address selection algorithm produces as output a single
* source address for use with a given destination address. This
* algorithm only applies to IPv6 destination addresses, not IPv4
* addresses.
*
* This implements RFC6724 Section 5.
*
* Either or both sa and sb can be dest or gateway routes
*/
static int
lws_sort_dns_scomp(struct lws_context_per_thread *pt, const lws_route_t *sa,
const lws_route_t *sb, const struct sockaddr_in6 *dst)
{
const struct sockaddr_in6 *sa6 = to_v6_sa(&sa->dest),
*sb6 = to_v6_sa(&sb->dest);
lws_dns_score_t scorea, scoreb, scoredst;
int scopea, scopeb, scoped, mla, mlb;
lws_route_t *rd;
if (!sa->dest.sa4.sin_family)
sa6 = to_v6_sa(&sa->gateway);
if (!sb->dest.sa4.sin_family)
sb6 = to_v6_sa(&sb->gateway);
/*
* We shouldn't come here unless sa and sb both have AF_INET6 addresses
*/
assert(sa6->sin6_family == AF_INET6);
assert(sb6->sin6_family == AF_INET6);
/*
* Rule 1: Prefer same address.
* If SA = D, then prefer SA. Similarly, if SB = D, then prefer SB.
*/
if (!memcmp(&sa6->sin6_addr, &dst->sin6_addr, 16))
return SAS_PREFER_A;
if (!memcmp(&sb6->sin6_addr, &dst->sin6_addr, 16))
return SAS_PREFER_B;
/*
* Rule 2: Prefer appropriate scope.
* If Scope(SA) < Scope(SB): If Scope(SA) < Scope(D), then prefer SB
* and otherwise prefer SA.
*
* Similarly, if Scope(SB) < Scope(SA): If Scope(SB) < Scope(D), then
* prefer SA and otherwise prefer SB.
*/
scopea = lws_sort_dns_scope(to_sa46_sa(sa6));
scopeb = lws_sort_dns_scope(to_sa46_sa(sb6));
scoped = lws_sort_dns_scope(to_sa46_sa(dst));
if (scopea < scopeb)
return scopea < scoped ? SAS_PREFER_B : SAS_PREFER_A;
if (scopeb < scopea)
return scopeb < scoped ? SAS_PREFER_A : SAS_PREFER_B;
/*
* Rule 3: Avoid deprecated addresses.
* If one of the two source addresses is "preferred" and one of them
* is "deprecated" (in the RFC 4862 sense), then prefer the one that
* is "preferred".
*/
if (!(sa->ifa_flags & IFA_F_DEPRECATED) &&
(sb->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_A;
if ( (sa->ifa_flags & IFA_F_DEPRECATED) &&
!(sb->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_B;
/*
* Rule 4: Prefer home addresses.
* If SA is simultaneously a home address and care-of address and SB is
* not, then prefer SA. Similarly, if SB is simultaneously a home
* address and care-of address and SA is not, then prefer SB. If SA is
* just a home address and SB is just a care-of address, then prefer SA.
* Similarly, if SB is just a home address and SA is just a care-of
* address, then prefer SB.
*
* !!! not sure how to determine if care-of address
*/
if ( (sa->ifa_flags & IFA_F_HOMEADDRESS) &&
!(sb->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_A;
if (!(sa->ifa_flags & IFA_F_HOMEADDRESS) &&
(sb->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_B;
/*
* Rule 5: Prefer outgoing interface.
* If SA is assigned to the interface that will be used to send to D
* and SB is assigned to a different interface, then prefer SA.
* Similarly, if SB is assigned to the interface that will be used
* to send to D and SA is assigned to a different interface, then
* prefer SB.
*/
rd = _lws_route_est_outgoing(pt, (lws_sockaddr46 *)dst);
if (rd) {
if (rd->if_idx == sa->if_idx)
return SAS_PREFER_A;
if (rd->if_idx == sb->if_idx)
return SAS_PREFER_B;
}
/*
* Rule 6: Prefer matching label.
* If Label(SA) = Label(D) and Label(SB) <> Label(D), then prefer SA.
* Similarly, if Label(SB) = Label(D) and Label(SA) <> Label(D), then
* prefer SB.
*/
lws_sort_dns_classify(to_sa46_sa(sa6), &scorea);
lws_sort_dns_classify(to_sa46_sa(sb6), &scoreb);
lws_sort_dns_classify(to_sa46_sa(dst), &scoredst);
if (scorea.label == scoredst.label && scoreb.label != scoredst.label)
return SAS_PREFER_A;
if (scoreb.label == scoredst.label && scorea.label != scoredst.label)
return SAS_PREFER_B;
/*
* Rule 7: Prefer temporary addresses.
* If SA is a temporary address and SB is a public address, then
* prefer SA. Similarly, if SB is a temporary address and SA is a
* public address, then prefer SB.
*/
if ( (sa->ifa_flags & IFA_F_TEMPORARY) &&
!(sb->ifa_flags & IFA_F_TEMPORARY))
return SAS_PREFER_A;
if (!(sa->ifa_flags & IFA_F_TEMPORARY) &&
(sb->ifa_flags & IFA_F_TEMPORARY))
return SAS_PREFER_B;
/*
* Rule 8: Use longest matching prefix.
* If CommonPrefixLen(SA, D) > CommonPrefixLen(SB, D), then prefer SA.
* Similarly, if CommonPrefixLen(SB, D) > CommonPrefixLen(SA, D), then
* prefer SB.
*/
mla = lws_ipv6_prefix_match_len(sa6, dst);
mlb = lws_ipv6_prefix_match_len(sb6, dst);
if (mla > mlb)
return SAS_PREFER_A;
return SAS_SAME;
}
/*
* Given two possible source addresses and the destination address, we attempt
* to pick which one is "better".
*
* This implements RFC6724 Section 6.
*/
static int
lws_sort_dns_dcomp(const lws_dns_sort_t *da, const lws_dns_sort_t *db)
{
int scopea, scopeb, scope_srca, scope_srcb, cpla, cplb;
const uint8_t *da_ads = (const uint8_t *)&da->dest.sa6.sin6_addr,
*db_ads = (const uint8_t *)&db->dest.sa6.sin6_addr;
lws_dns_score_t score_srca, score_srcb;
/*
* Rule 1: Avoid unusable destinations
*
* We already strip destinations with no usable source
*/
/*
* Rule 2: Prefer matching scope
*
* If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
* then prefer DA. Similarly, if Scope(DA) <> Scope(Source(DA)) and
* Scope(DB) = Scope(Source(DB)), then prefer DB.
*/
scopea = lws_ipv6_unicast_scope(to_v6_sa(&da->dest));
scopeb = lws_ipv6_unicast_scope(to_v6_sa(&db));
scope_srca = lws_ipv6_unicast_scope(to_v6_sa(&da->source));
scope_srcb = lws_ipv6_unicast_scope(to_v6_sa(&db->source));
if (scopea == scope_srca && scopeb != scope_srcb)
return SAS_PREFER_A;
if (scopea != scope_srca && scopeb == scope_srcb)
return SAS_PREFER_B;
#if defined(IFA_F_DEPRECATED)
/*
* Rule 3: Avoid deprecated addresses.
*
* If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
* Similarly, if Source(DA) is not deprecated and Source(DB) is
* deprecated, then prefer DA.
*/
if (!(da->ifa_flags & IFA_F_DEPRECATED) &&
(db->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_A;
if ( (da->ifa_flags & IFA_F_DEPRECATED) &&
!(db->ifa_flags & IFA_F_DEPRECATED))
return SAS_PREFER_B;
#endif
/*
* Rule 4: Prefer home addresses.
*
* If Source(DA) is simultaneously a home address and care-of address
* and Source(DB) is not, then prefer DA. Similarly, if Source(DB) is
* simultaneously a home address and care-of address and Source(DA) is
* not, then prefer DB.
*
* If Source(DA) is just a home address and Source(DB) is just a care-of
* address, then prefer DA. Similarly, if Source(DA) is just a care-of
* address and Source(DB) is just a home address, then prefer DB.
*
* !!! not sure how to determine if care-of address
*/
if ( (da->ifa_flags & IFA_F_HOMEADDRESS) &&
!(db->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_A;
if (!(da->ifa_flags & IFA_F_HOMEADDRESS) &&
(db->ifa_flags & IFA_F_HOMEADDRESS))
return SAS_PREFER_B;
/*
* Rule 5: Prefer matching label.
*
* If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
* then prefer DA. Similarly, if Label(Source(DA)) <> Label(DA) and
* Label(Source(DB)) = Label(DB), then prefer DB
*/
lws_sort_dns_classify(&da->source->dest, &score_srca);
lws_sort_dns_classify(&db->source->dest, &score_srcb);
if (score_srca.label == da->score.label &&
score_srcb.label != db->score.label)
return SAS_PREFER_A;
if (score_srca.label != da->score.label &&
score_srcb.label == db->score.label)
return SAS_PREFER_B;
/*
* Rule 6: Prefer higher precedence.
*
* If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if
* Precedence(DA) < Precedence(DB), then prefer DB.
*/
if (da->score.precedence > db->score.precedence)
return SAS_PREFER_A;
if (da->score.precedence < db->score.precedence)
return SAS_PREFER_B;
/*
* Rule 7: Prefer native transport.
* If DA is reached via an encapsulating transition mechanism (e.g.,
* IPv6 in IPv4) and DB is not, then prefer DB. Similarly, if DB is
* reached via encapsulation and DA is not, then prefer DA.
*/
if (!memcmp(&ma[16], da_ads, 12) && memcmp(&ma[16], db_ads, 12))
return SAS_PREFER_B;
if (memcmp(&ma[16], da_ads, 12) && !memcmp(&ma[16], db_ads, 12))
return SAS_PREFER_A;
/*
* Rule 8: Prefer smaller scope.
* If Scope(DA) < Scope(DB), then prefer DA. Similarly, if Scope(DA) >
* Scope(DB), then prefer DB.
*/
if (scopea < scopeb)
return SAS_PREFER_A;
if (scopea > scopeb)
return SAS_PREFER_B;
/*
* Rule 9: Use longest matching prefix.
* When DA and DB belong to the same address family (both are IPv6 or
* both are IPv4): If CommonPrefixLen(Source(DA), DA) >
* CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if
* CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
* then prefer DB.
*/
cpla = lws_ipv6_prefix_match_len(&da->source->dest.sa6, &da->dest.sa6);
cplb = lws_ipv6_prefix_match_len(&db->source->dest.sa6, &db->dest.sa6);
if (cpla > cplb)
return SAS_PREFER_A;
if (cpla < cplb)
return SAS_PREFER_B;
/*
* Rule 10: Otherwise, leave the order unchanged.
*/
return SAS_SAME;
}
static int
lws_sort_dns_compare(const lws_dll2_t *a, const lws_dll2_t *b)
{
const lws_dns_sort_t *sa = lws_container_of(a, lws_dns_sort_t, list),
*sb = lws_container_of(b, lws_dns_sort_t, list);
return lws_sort_dns_dcomp(sa, sb);
}
#endif /* ipv6 + netlink */
#if defined(_DEBUG)
static void
lws_sort_dns_dump(struct lws *wsi)
{
int n = 1;
(void)n; /* nologs */
if (!lws_dll2_get_head(&wsi->dns_sorted_list))
lwsl_notice("%s: empty\n", __func__);
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&wsi->dns_sorted_list)) {
lws_dns_sort_t *s = lws_container_of(d, lws_dns_sort_t, list);
char dest[48], gw[48];
lws_sa46_write_numeric_address(&s->dest, dest, sizeof(dest));
lws_sa46_write_numeric_address(&s->gateway, gw, sizeof(gw));
lwsl_notice("%s: %d: (%d)%s, gw (%d)%s, idi: %d, "
"lbl: %d, prec: %d\n",
__func__, n++, s->dest.sa4.sin_family, dest,
s->gateway.sa4.sin_family, gw,
s->if_idx, s->score.label, s->score.precedence);
} lws_end_foreach_dll(d);
}
#endif
int
lws_sort_dns(struct lws *wsi, const struct addrinfo *result)
{
#if defined(LWS_WITH_NETLINK)
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
#endif
const struct addrinfo *ai = result;
lwsl_info("%s: sort_dns: %p\n", __func__, result);
/*
* We're going to take the dns results and produce our own linked-list
* of them, if we can sorted into descending preferability order, and
* possibly filtered.
*
* First let's just convert the addrinfo list into our expanded
* lws_dns_sort_t list, we can discard the addrinfo list then
*/
while (ai) {
#if defined(LWS_WITH_NETLINK) || \
(defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6))
lws_route_t
#if defined(LWS_WITH_NETLINK)
*estr = NULL
#endif
#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6)
, *bestsrc = NULL
#endif
;
#endif
lws_dns_sort_t *ds;
char afip[48];
/*
* Only transfer address families we can cope with
*/
if ((int)ai->ai_addrlen > (int)sizeof(lws_sockaddr46) ||
(ai->ai_family != AF_INET && ai->ai_family != AF_INET6)) {
lwsl_info("%s: skip %d %d %d\n", __func__,
ai->ai_family, (int)ai->ai_addrlen,
(int)sizeof(lws_sockaddr46));
goto next;
}
ds = lws_zalloc(sizeof(*ds), __func__);
if (!ds)
return 1;
memcpy(&ds->dest, ai->ai_addr, ai->ai_addrlen);
ds->dest.sa4.sin_family = ai->ai_family;
lws_sa46_write_numeric_address(&ds->dest, afip, sizeof(afip));
lwsl_info("%s: unsorted entry (af %d) %s\n", __func__,
ds->dest.sa4.sin_family, afip);
#if defined(LWS_WITH_NETLINK)
/*
* Let's assess this DNS result in terms of route
* selection, eg, if no usable net route or gateway for it,
* we don't have a way to use it if we listed it
*/
if (pt->routing_table.count) {
estr = _lws_route_est_outgoing(pt, &ds->dest);
if (!estr) {
lws_free(ds);
lwsl_notice("%s: no route out\n", __func__);
/*
* There's no outbound route for this, it's
* unusable, so don't add it to the list
*/
goto next;
}
ds->if_idx = estr->if_idx;
ds->uidx = estr->uidx;
/*
* ...evidently, there's a way for it to go out...
*/
}
#endif
#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6)
/*
* These sorting rules only apply to ipv6. If we have ipv4
* dest and estimate we will use an ipv4 source address to
* route it, then skip this.
*
* However if we have ipv4 dest and estimate we will use an
* ipv6 source address to route it, because of ipv6-only
* egress, then promote it to ipv6 and sort it
*/
if (ds->dest.sa4.sin_family == AF_INET) {
if (!estr ||
estr->dest.sa4.sin_family == AF_INET ||
estr->gateway.sa4.sin_family == AF_INET)
/*
* No estimated route, or v4 estimated route,
* just add it to sorted list
*/
goto just_add;
/*
* v4 dest on estimated v6 source ads route, because
* eg, there's no active v4 source ads just ipv6...
* promote v4 -> v6 address using ::ffff:xx:yy
*/
lwsl_info("%s: promoting v4->v6\n", __func__);
lws_sa46_4to6(&ds->dest,
(uint8_t *)&ds->dest.sa4.sin_addr, 0);
}
/* first, classify this destination ads */
lws_sort_dns_classify(&ds->dest, &ds->score);
/*
* RFC6724 Section 5: Source Address Selection
*
* Go through the source options choosing the best for this
* destination... this can only operate on ipv6 destination
* address
*/
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&pt->routing_table)) {
lws_route_t *r = lws_container_of(d, lws_route_t, list);
/* gateway routes are skipped here */
if (ds->dest.sa6.sin6_family == AF_INET6 &&
r->dest.sa4.sin_family == AF_INET6 && (!bestsrc ||
lws_sort_dns_scomp(pt, bestsrc, r, &ds->dest.sa6) ==
SAS_PREFER_B))
bestsrc = r;
} lws_end_foreach_dll(d);
/* bestsrc is the best source route, or NULL if none */
if (!bestsrc && pt->routing_table.count) {
/* drop it, no usable source route */
lws_free(ds);
goto next;
}
just_add:
if (!bestsrc) {
lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list);
goto next;
}
ds->source = bestsrc;
/*
* RFC6724 Section 6: Destination Address Selection
*
* Insert the destination into the list at a position reflecting
* its preferability, so the head entry is the most preferred
*/
lws_dll2_add_sorted(&ds->list, &wsi->dns_sorted_list,
lws_sort_dns_compare);
#else
/*
* We don't have the routing table + source address details in
* order to sort the DNS results... simply make entries in the
* order of the addrinfo results
*/
lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list);
#endif
next:
ai = ai->ai_next;
}
//lwsl_notice("%s: sorted table: %d\n", __func__,
// wsi->dns_sorted_list.count);
#if defined(_DEBUG)
lws_sort_dns_dump(wsi);
#endif
return !wsi->dns_sorted_list.count;
}

View file

@ -240,15 +240,17 @@ void
lws_addrinfo_clean(struct lws *wsi)
{
#if defined(LWS_WITH_CLIENT)
if (!wsi->dns_results)
return;
struct lws_dll2 *d = lws_dll2_get_head(&wsi->dns_sorted_list), *d1;
#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;
while (d) {
lws_dns_sort_t *r = lws_container_of(d, lws_dns_sort_t, list);
d1 = d->next;
lws_dll2_remove(d);
lws_free(r);
d = d1;
}
#endif
}
@ -276,6 +278,11 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
context = wsi->a.context;
pt = &context->pt[(int)wsi->tsi];
#if defined(LWS_WITH_SYS_ASYNC_DNS)
if (wsi == context->async_dns.wsi)
context->async_dns.wsi = NULL;
#endif
lws_pt_assert_lock_held(pt);
lws_stats_bump(pt, LWSSTATS_C_API_CLOSE, 1);

View file

@ -879,13 +879,14 @@ lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b)
void
lws_4to6(uint8_t *v6addr, const uint8_t *v4addr)
{
memset(v6addr, 0, 10);
v6addr[10] = v6addr[11] = 0xff;
v6addr[12] = v4addr[0];
v6addr[13] = v4addr[1];
v6addr[14] = v4addr[2];
v6addr[15] = v4addr[3];
memset(v6addr, 0, 10);
v6addr[10] = v6addr[11] = 0xff;
}
#if defined(LWS_WITH_IPV6)

View file

@ -362,6 +362,7 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
goto post_send;
}
}
if (lws_has_buffered_out(wsi))
n = sendto(wsi->desc.sockfd, (const char *)buf,
len, 0, sa46_sockaddr(&wsi->udp->sa46_pending),

View file

@ -719,6 +719,13 @@ struct lws {
struct lws_dll2 dll_cli_active_conns;
struct lws_dll2 dll2_cli_txn_queue;
struct lws_dll2_owner dll2_cli_txn_queue_owner;
lws_dll2_t speculative_list;
lws_dll2_owner_t speculative_connect_owner;
/* wsis: additional connection candidates */
lws_dll2_owner_t dns_sorted_list;
/* lws_dns_sort_t: dns results wrapped and sorted in a linked-list...
* deleted as they are tried, list empty == everything tried */
#endif
lws_sockaddr46 sa46_peer;
@ -746,8 +753,6 @@ struct lws {
#if defined(LWS_WITH_CLIENT)
struct client_info_stash *stash;
char *cli_hostname_copy;
const struct addrinfo *dns_results;
const struct addrinfo *dns_results_next;
#endif
void *user_space;
void *opaque_parent_data;
@ -1396,6 +1401,9 @@ lws_route_t *
_lws_route_est_outgoing(struct lws_context_per_thread *pt,
const lws_sockaddr46 *dest);
int
lws_sort_dns(struct lws *wsi, const struct addrinfo *result);
int
lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len);
@ -1536,6 +1544,10 @@ lws_sul_nonmonotonic_adjust(struct lws_context *ctx, int64_t step_us);
void
lws_netdev_instance_remove_destroy(struct lws_netdev_instance *ni);
int
lws_score_dns_results(struct lws_context *ctx,
const struct addrinfo **result);
#if defined(LWS_WITH_SYS_SMD)
int
lws_netdev_smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp,

View file

@ -72,11 +72,16 @@ _lws_routing_table_dump(struct lws_context_per_thread *pt)
*
* It's OK if the route uidx wraps, we explicitly confirm nobody else is using
* the uidx before assigning one to a new route.
*
* We won't use uidx 0, so it can be understood to mean the uidx was never set.
*/
lws_route_uidx_t
_lws_route_get_uidx(struct lws_context_per_thread *pt)
{
if (!pt->route_uidx)
pt->route_uidx++;
while (1) {
char again = 0;
@ -89,6 +94,8 @@ _lws_route_get_uidx(struct lws_context_per_thread *pt)
if (rou->uidx == pt->route_uidx) {
/* if so, bump and restart the check */
pt->route_uidx++;
if (!pt->route_uidx)
pt->route_uidx++;
again = 1;
}
} lws_end_foreach_dll(d);
@ -236,10 +243,6 @@ _lws_route_check_wsi(struct lws *wsi)
return !_lws_route_est_outgoing(pt, &wsi->sa46_peer);
}
/*
* priority_deleted_route should be -1 if no deleted route
*/
int
_lws_route_pt_close_unroutable(struct lws_context_per_thread *pt)
{
@ -271,6 +274,11 @@ _lws_route_pt_close_route_users(struct lws_context_per_thread *pt,
struct lws *wsi;
unsigned int n;
if (!uidx)
return 0;
lwsl_info("%s: closing users of route %d\n", __func__, uidx);
for (n = 0; n < pt->fds_count; n++) {
wsi = wsi_from_fd(pt->context, pt->fds[n].fd);
if (!wsi)

View file

@ -184,6 +184,8 @@ lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)
#if defined(LWS_ROLE_RAW_FILE)
lwsl_notice("%s: falling back to raw file role bind\n", __func__);
/* fall back to raw file role if, eg, h1 not configured */
if (role_ops_raw_file.adoption_bind &&

View file

@ -183,6 +183,31 @@ lws_dll2_owner_clear(struct lws_dll2_owner *d)
d->count = 0;
}
void
lws_dll2_add_sorted_priv(lws_dll2_t *d, lws_dll2_owner_t *own, void *priv,
int (*compare3)(void *priv, const lws_dll2_t *d,
const lws_dll2_t *i))
{
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
lws_dll2_get_head(own)) {
assert(p != d);
if (compare3(priv, p, d) >= 0) {
/* drop us in before this guy */
lws_dll2_add_before(d, p);
return;
}
} lws_end_foreach_dll_safe(p, tp);
/*
* Either nobody on the list yet to compare him to, or he's the
* furthest away timeout... stick him at the tail end
*/
lws_dll2_add_tail(d, own);
}
void
lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i))
@ -195,8 +220,6 @@ lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
/* drop us in before this guy */
lws_dll2_add_before(d, p);
// lws_dll2_describe(own, "post-insert");
return;
}
} lws_end_foreach_dll_safe(p, tp);

View file

@ -270,7 +270,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
* the connection has definitively failed... but
* do we have more DNS entries to try?
*/
if (wsi_from_fd(context, pfd->fd)->dns_results_next) {
if (wsi_from_fd(context, pfd->fd)->dns_sorted_list.count) {
lws_sul_schedule(context, 0,
&wsi_from_fd(context, pfd->fd)->
sul_connect_timeout,

View file

@ -348,8 +348,9 @@ int
lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot);
struct lws *
lws_client_connect_4_established(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);
const struct addrinfo *result, int n, void *opaque);

View file

@ -234,17 +234,24 @@ fail:
static int
rops_adoption_bind_raw_skt(struct lws *wsi, int type, const char *vh_prot_name)
{
// lwsl_notice("%s: bind type %d\n", __func__, type);
/* no http but socket... must be raw skt */
if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
(type & _LWS_ADOPT_FINISH))
((type & _LWS_ADOPT_FINISH) && (!(type & LWS_ADOPT_FLAG_UDP))))
return 0; /* no match */
#if defined(LWS_WITH_UDP)
if (type & LWS_ADOPT_FLAG_UDP)
if ((type & LWS_ADOPT_FLAG_UDP) && !wsi->udp) {
/*
* these can be >128 bytes, so just alloc for UDP
*/
wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
if (!wsi->udp)
return 0;
memset(wsi->udp, 0, sizeof(*wsi->udp));
}
#endif
lws_role_transition(wsi, 0, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT :

View file

@ -25,7 +25,7 @@
#include "private-lib-core.h"
#include "private-lib-async-dns.h"
static const uint32_t botable[] = { 500, 1000, 1250, 5000
static const uint32_t botable[] = { 300, 500, 700, 1250, 5000
/* in case everything just dog slow */ };
static const lws_retry_bo_t retry_policy = {
botable, LWS_ARRAY_SIZE(botable), LWS_ARRAY_SIZE(botable),
@ -88,8 +88,13 @@ lws_async_dns_complete(lws_adns_q_t *q, lws_adns_cache_t *c)
__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);
if (w->adns_cb(w, (const char *)&q[1], c ? c->results : NULL, 0,
q->opaque) == NULL)
lwsl_notice("%s: failed\n", __func__);
// lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,
// "adopt udp2 fail");
lws_set_timeout(w, NO_PENDING_TIMEOUT, 0);
} lws_end_foreach_dll_safe(d, d1);
if (q->standalone_cb) {
@ -111,8 +116,6 @@ lws_async_dns_sul_cb_retry(struct lws_sorted_usec_list *sul)
{
lws_adns_q_t *q = lws_container_of(sul, lws_adns_q_t, sul);
// lwsl_notice("%s\n", __func__);
lws_callback_on_writable(q->dns->wsi);
}
@ -203,8 +206,7 @@ lws_async_dns_writeable(struct lws *wsi, lws_adns_q_t *q)
goto qfail;
}
lws_ser_wu16be(p, which ? LWS_ADNS_RECORD_AAAA :
LWS_ADNS_RECORD_A);
lws_ser_wu16be(p, which ? LWS_ADNS_RECORD_AAAA : LWS_ADNS_RECORD_A);
p += 2;
lws_ser_wu16be(p, 1); /* IN class */
@ -212,6 +214,7 @@ lws_async_dns_writeable(struct lws *wsi, lws_adns_q_t *q)
assert(p < pkt + sizeof(pkt) - LWS_PRE);
n = lws_ptr_diff(p, pkt + LWS_PRE);
m = lws_write(wsi, pkt + LWS_PRE, n, 0);
if (m != n) {
lwsl_notice("%s: dns write failed %d %d errno %d\n", __func__,
@ -267,7 +270,6 @@ callback_async_dns(struct lws *wsi, enum lws_callback_reasons reason,
break;
case LWS_CALLBACK_RAW_WRITEABLE:
// lwsl_notice("%s: WRITABLE\n", __func__);
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
dns->waiting.head) {
@ -298,6 +300,9 @@ lws_async_dns_init(struct lws_context *context)
char ads[48];
int n;
if (dns->wsi)
return 0;
if (!context->vhost_list) { /* coverity... system vhost always present */
lwsl_err("%s: no system vhost\n", __func__);
return 1;
@ -327,14 +332,16 @@ ok:
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,
NULL, NULL, &retry_policy);
dns->wsi = lws_create_adopt_udp(context->vhost_list, ads, 53, 0,
lws_async_dns_protocol.name, NULL,
NULL, NULL, &retry_policy);
if (!dns->wsi) {
lwsl_err("%s: foreign socket adoption failed\n", __func__);
return 1;
}
context->async_dns.wsi->udp->sa46 = dns->sa46;
dns->dns_server_set = 1;
return 0;
@ -588,7 +595,7 @@ lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
}
/*
* It's a 1.2.3.4 type IP address already? We don't need a dns
* It's a 1.2.3.4 or ::1 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
@ -743,11 +750,12 @@ lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
lws_dll2_add_head(&q->list, &dns->waiting);
lwsl_debug("%s: created new query\n", __func__);
lwsl_info("%s: created new query\n", __func__);
return LADNS_RET_CONTINUING;
failed:
lwsl_notice("%s: failed\n", __func__);
cb(wsi, NULL, NULL, LADNS_RET_FAILED, opaque);
return LADNS_RET_FAILED;

View file

@ -180,6 +180,9 @@ int main(int argc, const char **argv)
if ((p = lws_cmdline_option(argc, argv, "-p")))
port = atoi(p);
if (lws_cmdline_option(argc, argv, "-n"))
ssl_connection &= ~LCCSCF_USE_SSL;
if (lws_cmdline_option(argc, argv, "-j"))
ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;