Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,121 @@
/*
* 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.
*/
#include "private-lib-core.h"
#if defined(LWS_CLIENT_HTTP_PROXYING)
int
lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
{
char authstring[96];
int brackets = 0;
char *p;
if (!proxy)
return -1;
/* we have to deal with a possible redundant leading http:// */
if (!strncmp(proxy, "http://", 7))
proxy += 7;
p = strrchr(proxy, '@');
if (p) { /* auth is around */
if (lws_ptr_diff_size_t(p, proxy) > sizeof(authstring) - 1)
goto auth_too_long;
lws_strncpy(authstring, proxy, lws_ptr_diff_size_t(p, proxy) + 1);
// null termination not needed on input
if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy),
vhost->proxy_basic_auth_token,
sizeof vhost->proxy_basic_auth_token) < 0)
goto auth_too_long;
lwsl_vhost_info(vhost, " Proxy auth in use");
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
proxy = p + 1;
#endif
} else
vhost->proxy_basic_auth_token[0] = '\0';
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
#if defined(LWS_WITH_IPV6)
/*
* isolating the address / port is complicated by IPv6 overloading
* the meaning of : in the address. The convention to solve it is to
* put [] around the ipv6 address part, eg, "[::1]:443". This must be
* parsed to "::1" as the address and the port as 443.
*
* IPv4 addresses like myproxy:443 continue to be parsed as normal.
*/
if (proxy[0] == '[')
brackets = 1;
#endif
lws_strncpy(vhost->http.http_proxy_address, proxy + brackets,
sizeof(vhost->http.http_proxy_address));
p = vhost->http.http_proxy_address;
#if defined(LWS_WITH_IPV6)
if (brackets) {
/* original is IPv6 format "[::1]:443" */
p = strchr(vhost->http.http_proxy_address, ']');
if (!p) {
lwsl_vhost_err(vhost, "malformed proxy '%s'", proxy);
return -1;
}
*p++ = '\0';
}
#endif
p = strchr(p, ':');
if (!p && !vhost->http.http_proxy_port) {
lwsl_vhost_err(vhost, "http_proxy needs to be ads:port");
return -1;
}
if (p) {
*p = '\0';
vhost->http.http_proxy_port = (unsigned int)atoi(p + 1);
}
lwsl_vhost_info(vhost, " Proxy %s:%u", vhost->http.http_proxy_address,
vhost->http.http_proxy_port);
#endif
return 0;
auth_too_long:
lwsl_vhost_err(vhost, "proxy auth too long");
return -1;
}
#endif

View File

@ -0,0 +1,155 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2019 - 2021 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.
*
* Client Connection Latency and DNS reporting
*/
/*
* We want to allocate copies for and append DNS results that we don't already
* have. We take this approach because a) we may be getting duplicated results
* from multiple DNS servers, and b) we may be getting results stacatto over
* time.
*
* We capture DNS results from either getaddrinfo or ASYNC_DNS the same here,
* before they are sorted and filtered.
*
* Because this is relatively expensive, we only do it on client wsi that
* explicitly indicated that they want it with the LCCSCF_CONMON flag.
*/
#include <private-lib-core.h>
int
lws_conmon_append_copy_new_dns_results(struct lws *wsi,
const struct addrinfo *cai)
{
if (!(wsi->flags & LCCSCF_CONMON))
return 0;
/*
* Let's go through the incoming guys, seeing if we already have them,
* or if we want to take a copy
*/
while (cai) {
struct addrinfo *ai = wsi->conmon.dns_results_copy;
char skip = 0;
/* do we already have this guy? */
while (ai) {
if (ai->ai_family != cai->ai_family &&
ai->ai_addrlen != cai->ai_addrlen &&
ai->ai_protocol != cai->ai_protocol &&
ai->ai_socktype != cai->ai_socktype &&
/* either ipv4 or v6 address must match */
((ai->ai_family == AF_INET &&
((struct sockaddr_in *)ai->ai_addr)->
sin_addr.s_addr ==
((struct sockaddr_in *)cai->ai_addr)->
sin_addr.s_addr)
#if defined(LWS_WITH_IPV6)
||
(ai->ai_family == AF_INET6 &&
!memcmp(((struct sockaddr_in6 *)ai->ai_addr)->
sin6_addr.s6_addr,
((struct sockaddr_in6 *)cai->ai_addr)->
sin6_addr.s6_addr, 16))
#endif
)) {
/* yes, we already got a copy then */
skip = 1;
break;
}
ai = ai->ai_next;
}
if (!skip) {
/*
* No we don't already have a copy of this one, let's
* allocate and append it then
*/
size_t al = sizeof(struct addrinfo) +
(size_t)cai->ai_addrlen;
size_t cl = cai->ai_canonname ?
strlen(cai->ai_canonname) + 1 : 0;
ai = lws_malloc(al + cl + 1, __func__);
if (!ai) {
lwsl_wsi_warn(wsi, "OOM");
return 1;
}
*ai = *cai;
ai->ai_addr = (struct sockaddr *)&ai[1];
memcpy(ai->ai_addr, cai->ai_addr, (size_t)cai->ai_addrlen);
if (cl) {
ai->ai_canonname = ((char *)ai->ai_addr) +
cai->ai_addrlen;
memcpy(ai->ai_canonname, cai->ai_canonname,
cl);
ai->ai_canonname[cl] = '\0';
}
ai->ai_next = wsi->conmon.dns_results_copy;
wsi->conmon.dns_results_copy = ai;
}
cai = cai->ai_next;
}
return 0;
}
void
lws_conmon_addrinfo_destroy(struct addrinfo *ai)
{
while (ai) {
struct addrinfo *ai1 = ai->ai_next;
lws_free(ai);
ai = ai1;
}
}
void
lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest)
{
memcpy(dest, &wsi->conmon, sizeof(*dest));
dest->peer46 = wsi->sa46_peer;
/* wsi no longer has to free it... */
wsi->conmon.dns_results_copy = NULL;
wsi->perf_done = 1;
}
void
lws_conmon_release(struct lws_conmon *conmon)
{
if (!conmon)
return;
lws_conmon_addrinfo_destroy(conmon->dns_results_copy);
conmon->dns_results_copy = NULL;
}

View File

@ -0,0 +1,560 @@
/*
* 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.
*/
#include "private-lib-core.h"
static const uint8_t hnames[] = {
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
_WSI_TOKEN_CLIENT_URI,
_WSI_TOKEN_CLIENT_HOST,
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
_WSI_TOKEN_CLIENT_METHOD,
_WSI_TOKEN_CLIENT_IFACE,
_WSI_TOKEN_CLIENT_ALPN
};
struct lws *
lws_http_client_connect_via_info2(struct lws *wsi)
{
struct client_info_stash *stash = wsi->stash;
int n;
lwsl_wsi_debug(wsi, "stash %p", stash);
if (!stash)
return wsi;
wsi->a.opaque_user_data = wsi->stash->opaque_user_data;
if (stash->cis[CIS_METHOD] && (!strcmp(stash->cis[CIS_METHOD], "RAW") ||
!strcmp(stash->cis[CIS_METHOD], "MQTT")))
goto no_ah;
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so into a temp
* allocated stash
*/
for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames); n++)
if (hnames[n] && stash->cis[n] &&
lws_hdr_simple_create(wsi, hnames[n], stash->cis[n]))
goto bail;
#if defined(LWS_WITH_SOCKS5)
if (!wsi->a.vhost->socks_proxy_port)
lws_free_set_NULL(wsi->stash);
#endif
no_ah:
return lws_client_connect_2_dnsreq(wsi);
bail:
#if defined(LWS_WITH_SOCKS5)
if (!wsi->a.vhost->socks_proxy_port)
lws_free_set_NULL(wsi->stash);
#endif
lws_free_set_NULL(wsi->stash);
return NULL;
}
int
lws_client_stash_create(struct lws *wsi, const char **cisin)
{
size_t size;
char *pc;
int n;
size = sizeof(*wsi->stash) + 1;
/*
* Let's overallocate the stash object with space for all the args
* in one hit.
*/
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n])
size += strlen(cisin[n]) + 1;
if (wsi->stash)
lws_free_set_NULL(wsi->stash);
wsi->stash = lws_malloc(size, "client stash");
if (!wsi->stash)
return 1;
/* all the pointers default to NULL, but no need to zero the args */
memset(wsi->stash, 0, sizeof(*wsi->stash));
pc = (char *)&wsi->stash[1];
for (n = 0; n < CIS_COUNT; n++)
if (cisin[n]) {
size_t mm;
wsi->stash->cis[n] = pc;
if (n == CIS_PATH && cisin[n][0] != '/')
*pc++ = '/';
mm = strlen(cisin[n]) + 1;
memcpy(pc, cisin[n], mm);
pc += mm;
}
return 0;
}
struct lws *
lws_client_connect_via_info(const struct lws_client_connect_info *i)
{
const char *local = i->protocol;
struct lws *wsi, *safe = NULL;
const struct lws_protocols *p;
const char *cisin[CIS_COUNT];
char buf_localport[8];
struct lws_vhost *vh;
int tsi;
if (i->context->requested_stop_internal_loops)
return NULL;
if (!i->context->protocol_init_done)
if (lws_protocol_init(i->context))
return NULL;
/*
* If we have .local_protocol_name, use it to select the local protocol
* handler to bind to. Otherwise use .protocol if http[s].
*/
if (i->local_protocol_name)
local = i->local_protocol_name;
lws_context_lock(i->context, __func__);
/*
* PHASE 1: if SMP, find out the tsi related to current service thread
*/
tsi = lws_pthread_self_to_tsi(i->context);
assert(tsi >= 0);
/* PHASE 2: create a bare wsi */
wsi = __lws_wsi_create_with_role(i->context, tsi, NULL, i->log_cx);
lws_context_unlock(i->context);
if (wsi == NULL)
return NULL;
vh = i->vhost;
if (!vh) {
#if defined(LWS_WITH_TLS_JIT_TRUST)
if (lws_tls_jit_trust_vhost_bind(i->context, i->address, &vh))
#endif
{
vh = lws_get_vhost_by_name(i->context, "default");
if (!vh) {
vh = i->context->vhost_list;
if (!vh) { /* coverity */
lwsl_cx_err(i->context, "no vhost");
goto bail;
}
if (!strcmp(vh->name, "system"))
vh = vh->vhost_next;
}
}
}
#if defined(LWS_WITH_SECURE_STREAMS)
/* any of these imply we are a client wsi bound to an SS, which
* implies our opaque user ptr is the ss (or sspc if PROXY_LINK) handle
*/
wsi->for_ss = !!(i->ssl_connection & (LCCSCF_SECSTREAM_CLIENT | LCCSCF_SECSTREAM_PROXY_LINK | LCCSCF_SECSTREAM_PROXY_ONWARD));
wsi->client_bound_sspc = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK); /* so wsi close understands need to remove sspc ptr to wsi */
wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD);
#endif
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
wsi->fic.name = "wsi";
if (i->fic.fi_owner.count)
/*
* This moves all the lws_fi_t from i->fi to the vhost fi,
* leaving it empty
*/
lws_fi_import(&wsi->fic, &i->fic);
lws_fi_inherit_copy(&wsi->fic, &i->context->fic, "wsi", i->fi_wsi_name);
if (lws_fi(&wsi->fic, "createfail"))
goto bail;
#if defined(LWS_WITH_SECURE_STREAMS)
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
if (wsi->client_bound_sspc) {
lws_sspc_handle_t *fih = (lws_sspc_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
if (wsi->for_ss) {
lws_ss_handle_t *fih = (lws_ss_handle_t *)i->opaque_user_data;
lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL);
}
#endif
#endif
lws_wsi_fault_timedclose(wsi);
/*
* Until we exit, we can report connection failure directly to the
* caller without needing to call through to protocol CONNECTION_ERROR.
*/
wsi->client_suppress_CONNECTION_ERROR = 1;
if (i->keep_warm_secs)
wsi->keep_warm_secs = i->keep_warm_secs;
else
wsi->keep_warm_secs = 5;
wsi->flags = i->ssl_connection;
wsi->c_pri = i->priority;
if (i->retry_and_idle_policy)
wsi->retry_policy = i->retry_and_idle_policy;
else
wsi->retry_policy = &i->context->default_retry;
if (i->ssl_connection & LCCSCF_WAKE_SUSPEND__VALIDITY)
wsi->conn_validity_wakesuspend = 1;
lws_vhost_bind_wsi(vh, wsi);
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
/* additionally inerit from vhost we bound to */
lws_fi_inherit_copy(&wsi->fic, &vh->fic, "wsi", i->fi_wsi_name);
#endif
if (!wsi->a.vhost) {
lwsl_wsi_err(wsi, "No vhost in the context");
goto bail;
}
/*
* PHASE 3: Choose an initial role for the wsi and do role-specific init
*
* Note the initial role may not reflect the final role, eg,
* we may want ws, but first we have to go through h1 to get that
*/
if (lws_role_call_client_bind(wsi, i) < 0) {
lwsl_wsi_err(wsi, "unable to bind to role");
goto bail;
}
lwsl_wsi_info(wsi, "role binding to %s", wsi->role_ops->name);
/*
* PHASE 4: fill up the wsi with stuff from the connect_info as far as
* it can go. It's uncertain because not only is our connection
* going to complete asynchronously, we might have bound to h1 and not
* even be able to get ahold of an ah immediately.
*/
wsi->user_space = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
wsi->position_in_fds_table = LWS_NO_FDS_POS;
wsi->ocport = wsi->c_port = (uint16_t)(unsigned int)i->port;
wsi->sys_tls_client_cert = i->sys_tls_client_cert;
#if defined(LWS_ROLE_H2)
wsi->txc.manual_initial_tx_credit =
(int32_t)i->manual_initial_tx_credit;
#endif
wsi->a.protocol = &wsi->a.vhost->protocols[0];
wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
wsi->client_no_follow_redirect = !!(i->ssl_connection &
LCCSCF_HTTP_NO_FOLLOW_REDIRECT);
/*
* PHASE 5: handle external user_space now, generic alloc is done in
* role finalization
*/
if (i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
if (local) {
lwsl_wsi_info(wsi, "vh %s protocol binding to %s\n",
wsi->a.vhost->name, local);
p = lws_vhost_name_to_protocol(wsi->a.vhost, local);
if (p)
lws_bind_protocol(wsi, p, __func__);
else
lwsl_wsi_info(wsi, "unknown protocol %s", local);
lwsl_wsi_info(wsi, "%s: %s %s entry",
lws_wsi_tag(wsi), wsi->role_ops->name,
wsi->a.protocol ? wsi->a.protocol->name : "none");
}
/*
* PHASE 5: handle external user_space now, generic alloc is done in
* role finalization
*/
if (!wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
#if defined(LWS_WITH_TLS)
wsi->tls.use_ssl = (unsigned int)i->ssl_connection;
#else
if (i->ssl_connection & LCCSCF_USE_SSL) {
lwsl_wsi_err(wsi, "lws not configured for tls");
goto bail;
}
#endif
/*
* PHASE 6: stash the things from connect_info that we can't process
* right now, eg, if http binding, without an ah. If h1 and no ah, we
* will go on the ah waiting list and process those things later (after
* the connect_info and maybe the things pointed to have gone out of
* scope)
*
* However these things are stashed in a generic way at this point,
* with no relationship to http or ah
*/
cisin[CIS_ADDRESS] = i->address;
cisin[CIS_PATH] = i->path;
cisin[CIS_HOST] = i->host;
cisin[CIS_ORIGIN] = i->origin;
cisin[CIS_PROTOCOL] = i->protocol;
cisin[CIS_METHOD] = i->method;
cisin[CIS_IFACE] = i->iface;
lws_snprintf(buf_localport, sizeof(buf_localport), "%u", i->local_port);
cisin[CIS_LOCALPORT] = buf_localport;
cisin[CIS_ALPN] = i->alpn;
cisin[CIS_USERNAME] = i->auth_username;
cisin[CIS_PASSWORD] = i->auth_password;
if (lws_client_stash_create(wsi, cisin))
goto bail;
#if defined(LWS_WITH_TLS)
if (i->alpn)
lws_strncpy(wsi->alpn, i->alpn, sizeof(wsi->alpn));
#endif
wsi->a.opaque_user_data = wsi->stash->opaque_user_data =
i->opaque_user_data;
#if defined(LWS_WITH_SECURE_STREAMS)
if (wsi->for_ss) {
/* it's related to ss... the options are
*
* LCCSCF_SECSTREAM_PROXY_LINK : client SSPC link to proxy
* LCCSCF_SECSTREAM_PROXY_ONWARD: proxy's onward connection
*/
__lws_lc_tag(i->context, &i->context->lcg[
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK ? LWSLCG_WSI_SSP_CLIENT :
#if defined(LWS_WITH_SERVER)
(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD ? LWSLCG_WSI_SSP_ONWARD :
#endif
LWSLCG_WSI_CLIENT
#if defined(LWS_WITH_SERVER)
)
#endif
],
#else
LWSLCG_WSI_CLIENT],
#endif
&wsi->lc, "%s/%s/%s/(%s)", i->method ? i->method : "WS",
wsi->role_ops->name, i->address,
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
wsi->client_bound_sspc ?
lws_sspc_tag((lws_sspc_handle_t *)i->opaque_user_data) :
#endif
lws_ss_tag(((lws_ss_handle_t *)i->opaque_user_data)));
} else
#endif
__lws_lc_tag(i->context, &i->context->lcg[LWSLCG_WSI_CLIENT], &wsi->lc,
"%s/%s/%s/%s", i->method ? i->method : "WS",
wsi->role_ops->name ? wsi->role_ops->name : "novh", vh->name, i->address);
lws_metrics_tag_wsi_add(wsi, "vh", wsi->a.vhost->name);
/*
* at this point user callbacks like
* LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to
* know the parent... eg for proxying we can grab extra headers from
* the parent's incoming ah and add them to the child client handshake
*/
if (i->parent_wsi) {
lwsl_wsi_info(wsi, "created as child %s",
lws_wsi_tag(i->parent_wsi));
wsi->parent = i->parent_wsi;
safe = wsi->sibling_list = i->parent_wsi->child_list;
i->parent_wsi->child_list = wsi;
}
/*
* PHASE 7: Do any role-specific finalization processing. We can still
* see important info things via wsi->stash
*/
if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_client_bind)) {
int n = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_client_bind).
client_bind(wsi, NULL);
if (n && i->parent_wsi)
/* unpick from parent */
i->parent_wsi->child_list = safe;
if (n < 0)
/* we didn't survive, wsi is freed */
goto bail2;
if (n)
/* something else failed, wsi needs freeing */
goto bail;
}
/* let the caller's optional wsi storage have the wsi we created */
if (i->pwsi)
*i->pwsi = wsi;
if (!wsi->a.protocol)
/* we must have one protocol or another bound by this point */
goto bail;
/* PHASE 8: notify protocol with role-specific connected callback */
/* raw socket per se doesn't want this... raw socket proxy wants it... */
if (wsi->role_ops != &role_ops_raw_skt ||
(i->local_protocol_name &&
!strcmp(i->local_protocol_name, "raw-proxy"))) {
lwsl_wsi_debug(wsi, "adoption cb %d to %s %s",
wsi->role_ops->adoption_cb[0],
wsi->role_ops->name, wsi->a.protocol->name);
wsi->a.protocol->callback(wsi, wsi->role_ops->adoption_cb[0],
wsi->user_space, NULL, 0);
}
#if defined(LWS_WITH_HUBBUB)
if (i->uri_replace_to)
wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb,
i->uri_replace_from,
i->uri_replace_to);
#endif
if (i->method && (!strcmp(i->method, "RAW") // ||
// !strcmp(i->method, "MQTT")
)) {
/*
* Not for MQTT here, since we don't know if we will
* pipeline it or not...
*/
#if defined(LWS_WITH_TLS)
wsi->tls.ssl = NULL;
if (wsi->role_ops != &role_ops_raw_skt && (wsi->tls.use_ssl & LCCSCF_USE_SSL)) {
const char *cce = NULL;
switch (
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
lws_client_create_tls(wsi, &cce, 1)
#else
lws_client_create_tls(wsi, &cce, 0)
#endif
) {
case 1:
return wsi;
case 0:
break;
default:
goto bail3;
}
}
#endif
/* fallthru */
wsi = lws_http_client_connect_via_info2(wsi);
}
if (wsi)
/*
* If it subsequently fails, report CONNECTION_ERROR,
* because we're going to return a non-error return now.
*/
wsi->client_suppress_CONNECTION_ERROR = 0;
return wsi;
#if defined(LWS_WITH_TLS)
bail3:
lwsl_wsi_info(wsi, "tls start fail");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "tls start fail");
if (i->pwsi)
*i->pwsi = NULL;
return NULL;
#endif
bail:
#if defined(LWS_WITH_TLS)
if (wsi->tls.ssl)
lws_tls_restrict_return(wsi);
#endif
lws_free_set_NULL(wsi->stash);
lws_fi_destroy(&wsi->fic);
lws_free(wsi);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
bail2:
#endif
if (i->pwsi)
*i->pwsi = NULL;
return NULL;
}

View File

@ -0,0 +1,395 @@
/*
* 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.
*/
#include "private-lib-core.h"
#if !defined(WIN32)
#include <netdb.h>
#endif
#ifndef AI_V4MAPPED
#define AI_V4MAPPED 0
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
static int
lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result)
{
lws_metrics_caliper_declare(cal, wsi->a.context->mt_conn_dns);
struct addrinfo hints;
#if defined(LWS_WITH_SYS_METRICS)
char buckname[32];
#endif
int n;
memset(&hints, 0, sizeof(hints));
*result = NULL;
hints.ai_socktype = SOCK_STREAM;
#ifdef LWS_WITH_IPV6
if (wsi->ipv6) {
#if !defined(__ANDROID__)
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_V4MAPPED;
#endif
} else
#endif
{
hints.ai_family = PF_UNSPEC;
}
#if defined(LWS_WITH_CONMON)
wsi->conmon_datum = lws_now_usecs();
#endif
wsi->dns_reachability = 0;
if (lws_fi(&wsi->fic, "dnsfail"))
n = EAI_FAIL;
else
n = getaddrinfo(ads, NULL, &hints, result);
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_dns = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
/*
* Which EAI_* are available and the meanings are highly platform-
* dependent, even different linux distros differ.
*/
if (0
#if defined(EAI_SYSTEM)
|| n == EAI_SYSTEM
#endif
#if defined(EAI_NODATA)
|| n == EAI_NODATA
#endif
#if defined(EAI_FAIL)
|| n == EAI_FAIL
#endif
#if defined(EAI_AGAIN)
|| n == EAI_AGAIN
#endif
) {
wsi->dns_reachability = 1;
lws_metrics_caliper_report(cal, METRES_NOGO);
#if defined(LWS_WITH_SYS_METRICS)
lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n);
lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
#endif
#if defined(LWS_WITH_CONMON)
wsi->conmon.dns_disposition = LWSCONMON_DNS_SERVER_UNREACHABLE;
#endif
#if 0
lwsl_wsi_debug(wsi, "asking to recheck CPD in 1s");
lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC);
#endif
}
lwsl_wsi_info(wsi, "getaddrinfo '%s' says %d", ads, n);
#if defined(LWS_WITH_SYS_METRICS)
if (n < 0) {
lws_snprintf(buckname, sizeof(buckname), "dns=\"nores %d\"", n);
lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname);
}
#endif
#if defined(LWS_WITH_CONMON)
wsi->conmon.dns_disposition = n < 0 ? LWSCONMON_DNS_NO_RESULT :
LWSCONMON_DNS_OK;
#endif
lws_metrics_caliper_report(cal, n >= 0 ? METRES_GO : METRES_NOGO);
return n;
}
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS) && defined(EAI_NONAME)
static const char * const dns_nxdomain = "DNS NXDOMAIN";
#endif
struct lws *
lws_client_connect_2_dnsreq(struct lws *wsi)
{
struct addrinfo *result = NULL;
const char *meth = NULL;
#if defined(LWS_WITH_IPV6)
struct sockaddr_in addr;
const char *iface;
#endif
const char *adsin;
int n, port = 0;
struct lws *w;
if (lwsi_state(wsi) == LRS_WAITING_DNS ||
lwsi_state(wsi) == LRS_WAITING_CONNECT) {
lwsl_wsi_info(wsi, "LRS_WAITING_DNS / CONNECT");
return wsi;
}
/*
* clients who will create their own fresh connection keep a copy of
* the hostname they originally connected to, in case other connections
* want to use it too
*/
if (!wsi->cli_hostname_copy) {
const char *pa = lws_wsi_client_stash_item(wsi, CIS_HOST,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
if (pa)
wsi->cli_hostname_copy = lws_strdup(pa);
}
/*
* The first job is figure out if we want to pipeline on or just join
* an existing "active connection" to the same place
*/
meth = lws_wsi_client_stash_item(wsi, CIS_METHOD,
_WSI_TOKEN_CLIENT_METHOD);
/* consult active connections to find out disposition */
adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
/* we only pipeline connections that said it was okay */
if (!wsi->client_pipeline) {
lwsl_wsi_debug(wsi, "new conn on no pipeline flag");
goto solo;
}
if (wsi->keepalive_rejected) {
lwsl_notice("defeating pipelining due to no "
"keepalive on server\n");
goto solo;
}
/* only pipeline things we associate with being a stream */
if (meth && !_lws_is_http_method(meth) && strcmp(meth, "RAW") &&
strcmp(meth, "UDP") && strcmp(meth, "MQTT"))
goto solo;
if (!adsin)
/*
* This cannot happen since user code must provide the client
* address to get this far, it's here to satisfy Coverity
*/
return NULL;
switch (lws_vhost_active_conns(wsi, &w, adsin)) {
case ACTIVE_CONNS_SOLO:
break;
case ACTIVE_CONNS_MUXED:
lwsl_wsi_notice(wsi, "ACTIVE_CONNS_MUXED");
if (lwsi_role_h2(wsi)) {
if (wsi->a.protocol->callback(wsi,
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
wsi->user_space, NULL, 0))
goto failed1;
//lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
//lwsi_set_state(w, LRS_ESTABLISHED);
lws_callback_on_writable(wsi);
}
return wsi;
case ACTIVE_CONNS_QUEUED:
lwsl_wsi_debug(wsi, "ACTIVE_CONNS_QUEUED st 0x%x: ",
lwsi_state(wsi));
if (lwsi_state(wsi) == LRS_UNCONNECTED) {
if (lwsi_role_h2(w))
lwsi_set_state(wsi,
LRS_H2_WAITING_TO_SEND_HEADERS);
else
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
}
return lws_client_connect_4_established(wsi, w, 0);
}
solo:
/*
* If we made our own connection, and we're doing a method that can
* take a pipeline, we are an "active client connection".
*
* Add ourselves to the vhost list of those so that others can
* piggyback on our transaction queue
*/
if (meth && (!strcmp(meth, "RAW") || _lws_is_http_method(meth) ||
!strcmp(meth, "MQTT")) &&
lws_dll2_is_detached(&wsi->dll2_cli_txn_queue) &&
lws_dll2_is_detached(&wsi->dll_cli_active_conns)) {
lws_context_lock(wsi->a.context, __func__);
lws_vhost_lock(wsi->a.vhost);
lwsl_wsi_info(wsi, "adding as active conn");
/* caution... we will have to unpick this on oom4 path */
lws_dll2_add_head(&wsi->dll_cli_active_conns,
&wsi->a.vhost->dll_cli_active_conns_owner);
lws_vhost_unlock(wsi->a.vhost);
lws_context_unlock(wsi->a.context);
}
/*
* Since address must be given at client creation, should not be
* possible, but necessary to satisfy coverity
*/
if (!adsin)
return NULL;
#if defined(LWS_WITH_UNIX_SOCK)
/*
* unix socket destination?
*/
if (*adsin == '+') {
wsi->unix_skt = 1;
n = 0;
goto next_step;
}
#endif
/*
* start off allowing ipv6 on connection if vhost allows it
*/
wsi->ipv6 = LWS_IPV6_ENABLED(wsi->a.vhost);
#ifdef LWS_WITH_IPV6
if (wsi->stash)
iface = wsi->stash->cis[CIS_IFACE];
else
iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (wsi->ipv6 && iface &&
inet_pton(AF_INET, iface, &addr.sin_addr) == 1) {
lwsl_wsi_notice(wsi, "client connection forced to IPv4");
wsi->ipv6 = 0;
}
#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) {
adsin = wsi->a.vhost->http.http_proxy_address;
port = (int)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) {
lwsl_wsi_client(wsi, "Sending SOCKS Greeting");
adsin = wsi->a.vhost->socks_proxy_address;
port = (int)wsi->a.vhost->socks_proxy_port;
#endif
} else {
/* Priority 3: Connect directly */
/* ads already set */
port = wsi->c_port;
}
/*
* prepare the actual connection
* to whatever we decided to connect to
*/
lwsi_set_state(wsi, LRS_WAITING_DNS);
lwsl_wsi_info(wsi, "lookup %s:%u", adsin, port);
wsi->conn_port = (uint16_t)port;
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
n = 0;
if (!wsi->dns_sorted_list.count) {
/*
* blocking dns resolution
*/
n = lws_getaddrinfo46(wsi, adsin, &result);
#if defined(EAI_NONAME)
if (n == EAI_NONAME) {
/*
* The DNS server responded with NXDOMAIN... even
* though this is still in the client creation call,
* we need to make a CCE, otherwise there won't be
* any user indication of what went wrong
*/
wsi->client_suppress_CONNECTION_ERROR = 0;
lws_inform_client_conn_fail(wsi, (void *)dns_nxdomain,
strlen(dns_nxdomain));
goto failed1;
}
#endif
}
#else
/* this is either FAILED, CONTINUING, or already called connect_4 */
if (lws_fi(&wsi->fic, "dnsfail"))
return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL);
else
n = lws_async_dns_query(wsi->a.context, wsi->tsi, adsin,
LWS_ADNS_RECORD_A, lws_client_connect_3_connect,
wsi, NULL, NULL);
if (n == LADNS_RET_FAILED_WSI_CLOSED)
return NULL;
if (n == LADNS_RET_FAILED)
goto failed1;
return wsi;
#endif
#if defined(LWS_WITH_UNIX_SOCK)
next_step:
#endif
return lws_client_connect_3_connect(wsi, adsin, result, n, NULL);
//#if defined(LWS_WITH_SYS_ASYNC_DNS)
failed1:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2");
return NULL;
//#endif
}

View File

@ -0,0 +1,808 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 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"
#if defined(WIN32)
/*
* Windows doesn't offer a Posix connect() event... we use a sul
* to check the connection status periodically while a connection
* is ongoing.
*
* Leaving this to POLLOUT to retry which is the way for Posix
* platforms instead on win32 causes event-loop busywaiting
* so for win32 we manage the retry interval directly with the sul.
*/
void
lws_client_win32_conn_async_check(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
win32_sul_connect_async_check);
lwsl_wsi_debug(wsi, "checking ongoing connection attempt");
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
}
#endif
void
lws_client_conn_wait_timeout(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
sul_connect_timeout);
/*
* This is used to constrain the time we're willing to wait for a
* connection before giving up on it and retrying.
*/
lwsl_wsi_info(wsi, "connect wait timeout has fired");
lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL);
}
void
lws_client_dns_retry_timeout(lws_sorted_usec_list_t *sul)
{
struct lws *wsi = lws_container_of(sul, struct lws,
sul_connect_timeout);
/*
* This limits the amount of dns lookups we will try before
* giving up and failing... it reuses sul_connect_timeout, which
* isn't officially used until we connected somewhere.
*/
lwsl_wsi_info(wsi, "dns retry");
if (!lws_client_connect_2_dnsreq(wsi))
lwsl_wsi_notice(wsi, "DNS lookup failed");
}
/*
* Figure out if an ongoing connect() has arrived at a final disposition or not
*
* We can check using getsockopt if our connect actually completed.
* Posix connect() allows nonblocking to redo the connect to
* find out if it succeeded.
*/
typedef enum {
LCCCR_CONNECTED = 1,
LCCCR_CONTINUE = 0,
LCCCR_FAILED = -1,
} lcccr_t;
static lcccr_t
lws_client_connect_check(struct lws *wsi, int *real_errno)
{
#if !defined(LWS_WITH_NO_LOGS)
char t16[16];
#endif
int en = 0;
#if !defined(WIN32)
int e;
socklen_t sl = sizeof(e);
#endif
(void)en;
/*
* This resets SO_ERROR after reading it. If there's an error
* condition, the connect definitively failed.
*/
#if !defined(WIN32)
if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, &e, &sl)) {
en = LWS_ERRNO;
if (!e) {
lwsl_wsi_debug(wsi, "getsockopt: conn OK errno %s",
lws_errno_describe(en, t16, sizeof(t16)));
return LCCCR_CONNECTED;
}
lwsl_wsi_notice(wsi, "getsockopt fd %d says %s", wsi->desc.sockfd,
lws_errno_describe(e, t16, sizeof(t16)));
*real_errno = e;
return LCCCR_FAILED;
}
#else
fd_set write_set, except_set;
struct timeval tv;
int ret;
FD_ZERO(&write_set);
FD_ZERO(&except_set);
FD_SET(wsi->desc.sockfd, &write_set);
FD_SET(wsi->desc.sockfd, &except_set);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select((int)wsi->desc.sockfd + 1, NULL, &write_set, &except_set, &tv);
if (FD_ISSET(wsi->desc.sockfd, &write_set)) {
/* actually connected */
lwsl_wsi_debug(wsi, "select write fd set, conn OK");
return LCCCR_CONNECTED;
}
if (FD_ISSET(wsi->desc.sockfd, &except_set)) {
/* Failed to connect */
lwsl_wsi_notice(wsi, "connect failed, select exception fd set");
return LCCCR_FAILED;
}
if (!ret) {
lwsl_wsi_debug(wsi, "select timeout");
return LCCCR_CONTINUE;
}
en = LWS_ERRNO;
#endif
lwsl_wsi_notice(wsi, "connection check FAILED: %s",
lws_errno_describe(*real_errno || en, t16, sizeof(t16)));
return LCCCR_FAILED;
}
/*
* We come here to fire off a connect, and to check its disposition later.
*
* If it did not complete before the individual attempt timeout, we will try to
* connect again with the next dns result.
*/
struct lws *
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
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
const char *cce = "Unable to connect", *iface, *local_port;
const struct sockaddr *psa = NULL;
uint16_t port = wsi->conn_port;
char dcce[48], t16[16];
lws_dns_sort_t *curr;
ssize_t plen = 0;
lws_dll2_t *d;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
int cfail;
#endif
int m, af = 0, en;
/*
* 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_sul_cancel(&wsi->sul_connect_timeout);
#if defined(LWS_WITH_CONMON)
/* append a copy from before the sorting */
lws_conmon_append_copy_new_dns_results(wsi, result);
#endif
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 defined(LWS_WITH_UNIX_SOCK)
memset(&sau, 0, sizeof(sau));
#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 (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_wsi_notice(wsi, "dns lookup failed %d", n);
/*
* DNS lookup itself failed... let's try again until we
* timeout
*/
lwsi_set_state(wsi, LRS_UNCONNECTED);
lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_connect_timeout,
lws_client_dns_retry_timeout,
LWS_USEC_PER_SEC);
return wsi;
// 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
* actually connected.
*/
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
lws_socket_is_valid(wsi->desc.sockfd)) {
if (!wsi->dns_sorted_list.count &&
!wsi->sul_connect_timeout.list.owner)
/* no dns results and no ongoing timeout for one */
goto connect_to;
/*
* If the connection failed, the OS-level errno may be
* something like EINPROGRESS rather than the actual problem
* that prevented a connection. This value will represent the
* “real” problem that we should report to the caller.
*/
int real_errno = 0;
switch (lws_client_connect_check(wsi, &real_errno)) {
case LCCCR_CONNECTED:
/*
* Oh, it has happened...
*/
goto conn_good;
case LCCCR_CONTINUE:
#if defined(WIN32)
lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check,
lws_client_win32_conn_async_check,
wsi->a.context->win32_connect_check_interval_usec);
#endif
return NULL;
default:
if (!real_errno)
real_errno = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce), "conn fail: %s",
lws_errno_describe(real_errno, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_debug(wsi, "%s", dcce);
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
goto try_next_dns_result_fds;
}
}
#if defined(LWS_WITH_UNIX_SOCK)
if (ads && *ads == '+') {
ads++;
memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer));
af = sau.sun_family = AF_UNIX;
strncpy(sau.sun_path, ads, sizeof(sau.sun_path));
sau.sun_path[sizeof(sau.sun_path) - 1] = '\0';
lwsl_wsi_info(wsi, "Unix skt: %s", 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_wsi_notice(wsi, "adns failed %s", ads);
/*
* Caller that is giving us LADNS_RET_FAILED will deal
* with cleanup
*/
return NULL;
}
#endif
/*
* 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:
cce = "Unable to connect";
if (!wsi->dns_sorted_list.count)
goto failed1;
/*
* Copy the wsi head sorted dns result into the wsi->sa46_peer, and
* remove and free the original from the sorted list
*/
d = lws_dll2_get_head(&wsi->dns_sorted_list);
curr = lws_container_of(d, lws_dns_sort_t, list);
lws_dll2_remove(&curr->list);
wsi->sa46_peer = curr->dest;
#if defined(LWS_WITH_NETLINK)
wsi->peer_route_uidx = curr->uidx;
lwsl_wsi_info(wsi, "peer_route_uidx %d", wsi->peer_route_uidx);
#endif
lws_free(curr);
sa46_sockport(&wsi->sa46_peer, htons(port));
psa = sa46_sockaddr(&wsi->sa46_peer);
n = (int)sa46_socklen(&wsi->sa46_peer);
#if defined(LWS_WITH_UNIX_SOCK)
ads_known:
#endif
/*
* Now we prepared psa, if not already connecting, create the related
* socket and add to the fds
*/
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
if (wsi->a.context->event_loop_ops->check_client_connect_ok &&
wsi->a.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)
af = 0;
if (wsi->unix_skt) {
af = AF_UNIX;
wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
}
else
#endif
{
af = wsi->sa46_peer.sa4.sin_family;
wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family,
SOCK_STREAM, 0);
}
if (!lws_socket_is_valid(wsi->desc.sockfd)) {
en = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce),
"conn fail: skt creation: %s",
lws_errno_describe(en, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result;
}
if (lws_plat_set_socket_options(wsi->a.vhost, wsi->desc.sockfd,
#if defined(LWS_WITH_UNIX_SOCK)
wsi->unix_skt)) {
#else
0)) {
#endif
en = LWS_ERRNO;
lws_snprintf(dcce, sizeof(dcce),
"conn fail: skt options: %s",
lws_errno_describe(en, t16, sizeof(t16)));
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result_closesock;
}
/* apply requested socket options */
if (lws_plat_set_socket_options_ip(wsi->desc.sockfd,
wsi->c_pri, wsi->flags))
lwsl_wsi_warn(wsi, "unable to set ip options");
lwsl_wsi_debug(wsi, "WAITING_CONNECT");
lwsi_set_state(wsi, LRS_WAITING_CONNECT);
if (wsi->a.context->event_loop_ops->sock_accept)
if (wsi->a.context->event_loop_ops->sock_accept(wsi)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: sock accept");
cce = dcce;
lwsl_wsi_warn(wsi, "%s", dcce);
goto try_next_dns_result_closesock;
}
lws_pt_lock(pt, __func__);
if (__insert_wsi_socket_into_fds(wsi->a.context, wsi)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: insert fd");
cce = dcce;
lws_pt_unlock(pt);
goto try_next_dns_result_closesock;
}
lws_pt_unlock(pt);
/*
* The fd + wsi combination is entered into the wsi tables
* at this point, with a pollfd
*
* 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.
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: change pollfd");
cce = dcce;
goto try_next_dns_result_fds;
}
if (!wsi->a.protocol)
wsi->a.protocol = &wsi->a.vhost->protocols[0];
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
wsi->a.vhost->connect_timeout_secs);
iface = lws_wsi_client_stash_item(wsi, CIS_IFACE,
_WSI_TOKEN_CLIENT_IFACE);
local_port = lws_wsi_client_stash_item(wsi, CIS_LOCALPORT,
_WSI_TOKEN_CLIENT_LOCALPORT);
if ((iface && *iface) || (local_port && atoi(local_port))) {
m = lws_socket_bind(wsi->a.vhost, wsi, wsi->desc.sockfd,
(local_port ? atoi(local_port) : 0), iface, af);
if (m < 0) {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: socket bind");
cce = dcce;
goto try_next_dns_result_fds;
}
}
}
#if defined(LWS_WITH_UNIX_SOCK)
if (wsi->unix_skt) {
psa = (const struct sockaddr *)&sau;
if (sau.sun_path[0])
n = (int)(sizeof(uint16_t) + strlen(sau.sun_path));
else
n = (int)(sizeof(uint16_t) +
strlen(&sau.sun_path[1]) + 1);
} else
#endif
if (!psa) /* coverity */
goto try_next_dns_result_fds;
/*
* The actual connection attempt
*/
#if defined(LWS_ESP_PLATFORM)
errno = 0;
#endif
/* grab a copy for peer tracking */
#if defined(LWS_WITH_UNIX_SOCK)
if (!wsi->unix_skt)
#endif
memmove(&wsi->sa46_peer, psa, (unsigned int)n);
/*
* Finally, make the actual connection attempt
*/
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->cal_conn.mt) {
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
}
lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tcp);
#endif
wsi->socket_is_permanently_unusable = 0;
if (lws_fi(&wsi->fic, "conn_cb_rej") ||
user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
LWS_CALLBACK_CONNECTING, wsi->user_space,
(void *)(intptr_t)wsi->desc.sockfd, 0)) {
lwsl_wsi_info(wsi, "CONNECTION CB closed");
goto failed1;
}
{
char buf[64];
lws_sa46_write_numeric_address((lws_sockaddr46 *)psa, buf, sizeof(buf));
lwsl_wsi_notice(wsi, "trying %s", buf);
}
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
cfail = lws_fi(&wsi->fic, "connfail");
if (cfail)
m = -1;
else
#endif
m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa,
(socklen_t)n);
#if defined(LWS_WITH_CONMON)
wsi->conmon_datum = lws_now_usecs();
wsi->conmon.ciu_sockconn = 0;
#endif
if (m == -1) {
/*
* Since we're nonblocking, connect not having completed is not
* necessarily indicating any problem... we have to look at
* either errno or the socket to understand if we actually
* failed already...
*/
int errno_copy = LWS_ERRNO;
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
if (cfail)
/* fake an abnormal, fatal situation */
errno_copy = 999;
#endif
lwsl_wsi_debug(wsi, "connect: fd %d, %s",
wsi->desc.sockfd,
lws_errno_describe(errno_copy, t16, sizeof(t16)));
if (errno_copy &&
errno_copy != LWS_EALREADY &&
errno_copy != LWS_EINPROGRESS &&
errno_copy != LWS_EWOULDBLOCK
#ifdef _WIN32
&& errno_copy != WSAEINVAL
&& errno_copy != WSAEISCONN
#endif
) {
/*
* The connect() failed immediately...
*/
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
#if defined(_DEBUG)
#if defined(LWS_WITH_UNIX_SOCK)
if (!wsi->unix_skt) {
#endif
char nads[48];
lws_sa46_write_numeric_address(&wsi->sa46_peer, nads,
sizeof(nads));
lws_snprintf(dcce, sizeof(dcce),
"conn fail: %s: %s:%d",
lws_errno_describe(errno_copy, t16, sizeof(t16)),
nads, port);
cce = dcce;
wsi->sa46_peer.sa4.sin_family = 0;
lwsl_wsi_info(wsi, "%s", cce);
#if defined(LWS_WITH_UNIX_SOCK)
} else {
lws_snprintf(dcce, sizeof(dcce),
"conn fail: %s: UDS %s",
lws_errno_describe(errno_copy, t16, sizeof(t16)), ads);
cce = dcce;
}
#endif
#endif
goto try_next_dns_result_fds;
}
#if defined(WIN32)
if (lws_plat_check_connection_error(wsi))
goto try_next_dns_result_fds;
if (errno_copy == WSAEISCONN)
goto conn_good;
#endif
/*
* The connection attempt is ongoing asynchronously... let's set
* a specialized timeout for this connect attempt completion, it
* uses wsi->sul_connect_timeout just for this purpose
*/
lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_connect_timeout,
lws_client_conn_wait_timeout,
wsi->a.context->timeout_secs *
LWS_USEC_PER_SEC);
#if defined(WIN32)
/*
* Windows is not properly POSIX, we have to manually schedule a
* callback to poll checking its status
*/
lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check,
lws_client_win32_conn_async_check,
wsi->a.context->win32_connect_check_interval_usec
);
#else
/*
* POSIX platforms must do specifically a POLLOUT poll to hear
* about the connect completion as a POLLOUT event
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
goto try_next_dns_result_fds;
#endif
return wsi;
}
conn_good:
/*
* The connection has happened
*/
#if defined(LWS_WITH_CONMON)
wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t)
(lws_now_usecs() - wsi->conmon_datum);
#endif
#if !defined(LWS_PLAT_OPTEE)
{
socklen_t salen = sizeof(wsi->sa46_local);
#if defined(_DEBUG)
char buf[64];
#endif
if (getsockname((int)wsi->desc.sockfd,
(struct sockaddr *)&wsi->sa46_local,
&salen) == -1) {
en = LWS_ERRNO;
lwsl_warn("getsockname: %s\n", lws_errno_describe(en, t16, sizeof(t16)));
}
#if defined(_DEBUG)
#if defined(LWS_WITH_UNIX_SOCK)
if (wsi->unix_skt)
buf[0] = '\0';
else
#endif
lws_sa46_write_numeric_address(&wsi->sa46_local, buf, sizeof(buf));
lwsl_wsi_info(wsi, "source ads %s", buf);
#endif
}
#endif
lws_sul_cancel(&wsi->sul_connect_timeout);
#if defined(WIN32)
lws_sul_cancel(&wsi->win32_sul_connect_async_check);
#endif
lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
lws_addrinfo_clean(wsi);
if (wsi->a.protocol)
wsi->a.protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
wsi->user_space, NULL, 0);
lwsl_wsi_debug(wsi, "going into connect_4");
return lws_client_connect_4_established(wsi, NULL, plen);
oom4:
/*
* We get here if we're trying to clean up a connection attempt that
* didn't make it as far as getting inserted into the wsi / fd tables
*/
if (lwsi_role_client(wsi) && wsi->a.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)
/* do the full wsi close flow */
goto failed1;
lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
/*
* 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->a.vhost;
lws_sockfd_type sfd = wsi->desc.sockfd;
//lws_vhost_lock(vhost);
__lws_free_wsi(wsi); /* acquires vhost lock in wsi reset */
//lws_vhost_unlock(vhost);
sanity_assert_no_wsi_traces(vhost->context, wsi);
sanity_assert_no_sockfd_traces(vhost->context, sfd);
}
return NULL;
connect_to:
/*
* It looks like the sul_connect_timeout fired
*/
lwsl_wsi_info(wsi, "abandoning connect due to timeout");
try_next_dns_result_fds:
lws_pt_lock(pt, __func__);
__remove_wsi_socket_from_fds(wsi);
lws_pt_unlock(pt);
try_next_dns_result_closesock:
/*
* We are killing the socket but leaving
*/
compatible_close(wsi->desc.sockfd);
wsi->desc.sockfd = LWS_SOCK_INVALID;
try_next_dns_result:
lws_sul_cancel(&wsi->sul_connect_timeout);
#if defined(WIN32)
lws_sul_cancel(&wsi->win32_sul_connect_async_check);
#endif
if (lws_dll2_get_head(&wsi->dns_sorted_list))
goto next_dns_result;
lws_addrinfo_clean(wsi);
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
failed1:
lws_sul_cancel(&wsi->sul_connect_timeout);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect3");
return NULL;
}

View File

@ -0,0 +1,338 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 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"
struct lws *
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->a.context->pt[(int)wsi->tsi];
#endif
const char *meth;
struct lws_pollfd pfd;
const char *cce = "";
int n, m, rawish = 0;
meth = lws_wsi_client_stash_item(wsi, CIS_METHOD,
_WSI_TOKEN_CLIENT_METHOD);
if (meth && (!strcmp(meth, "RAW")
#if defined(LWS_ROLE_MQTT)
|| !strcmp(meth, "MQTT")
#endif
))
rawish = 1;
if (wsi_piggyback)
goto send_hs;
#if defined(LWS_CLIENT_HTTP_PROXYING)
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/* we are connected to server, or proxy */
/* http proxy */
if (wsi->a.vhost->http.http_proxy_port) {
const char *cpa;
cpa = lws_wsi_client_stash_item(wsi, CIS_ADDRESS,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
if (!cpa)
goto failed;
lwsl_wsi_info(wsi, "going via proxy");
plen = lws_snprintf((char *)pt->serv_buf, 256,
"CONNECT %s:%u HTTP/1.1\x0d\x0a"
"Host: %s:%u\x0d\x0a"
"User-agent: lws\x0d\x0a", cpa, wsi->ocport,
cpa, wsi->ocport);
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
if (wsi->a.vhost->proxy_basic_auth_token[0])
plen += lws_snprintf((char *)pt->serv_buf + plen, 256,
"Proxy-authorization: basic %s\x0d\x0a",
wsi->a.vhost->proxy_basic_auth_token);
#endif
plen += lws_snprintf((char *)pt->serv_buf + plen, 5,
"\x0d\x0a");
/* lwsl_hexdump_notice(pt->serv_buf, plen); */
/*
* OK from now on we talk via the proxy, so connect to that
*/
if (wsi->stash)
wsi->stash->cis[CIS_ADDRESS] =
wsi->a.vhost->http.http_proxy_address;
else
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->a.vhost->http.http_proxy_address))
goto failed;
wsi->c_port = (uint16_t)wsi->a.vhost->http.http_proxy_port;
n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf,
(unsigned int)plen,
MSG_NOSIGNAL);
if (n < 0) {
lwsl_wsi_debug(wsi, "ERROR writing to proxy socket");
cce = "proxy write failed";
goto failed;
}
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
(int)wsi->a.context->timeout_secs);
wsi->conn_port = wsi->c_port;
lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
return wsi;
}
#endif
#endif
/* coverity */
if (!wsi->a.protocol)
return NULL;
#if defined(LWS_WITH_SOCKS5)
if (lwsi_state(wsi) != LRS_ESTABLISHED)
switch (lws_socks5c_greet(wsi, &cce)) {
case -1:
goto failed;
case 1:
return wsi;
default:
break;
}
#endif
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
send_hs:
if (wsi_piggyback &&
!lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) {
/*
* We are pipelining on an already-established connection...
* we can skip tls establishment.
*
* Set these queued guys to a state where they won't actually
* send their headers until we decide later.
*/
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
/*
* we can't send our headers directly, because they have to
* be sent when the parent is writeable. The parent will check
* for anybody on his client transaction queue that is in
* LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
*
* If we are trying to do this too early, before the network
* connection has written his own headers, then it will just
* wait in the queue until it's possible to send them.
*/
lws_callback_on_writable(wsi_piggyback);
lwsl_wsi_info(wsi, "waiting to send hdrs (par state 0x%x)",
lwsi_state(wsi_piggyback));
} else {
lwsl_wsi_info(wsi, "%s %s client created own conn "
"(raw %d) vh %s st 0x%x",
wsi->role_ops->name, wsi->a.protocol->name, rawish,
wsi->a.vhost->name, lwsi_state(wsi));
/* we are making our own connection */
if (!rawish
#if defined(LWS_WITH_TLS)
// && (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || wsi->tls.ssl)
#endif
) {
if (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2)
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
} else {
/* for a method = "RAW" connection, this makes us
* established */
#if defined(LWS_WITH_TLS)// && !defined(LWS_WITH_MBEDTLS)
/* we have connected if we got here */
if (lwsi_state(wsi) == LRS_WAITING_CONNECT &&
(wsi->tls.use_ssl & LCCSCF_USE_SSL)) {
int result;
//lwsi_set_state(wsi, LRS_WAITING_SSL);
/*
* We can retry this... just cook the SSL BIO
* the first time
*/
result = lws_client_create_tls(wsi, &cce, 1);
switch (result) {
case CCTLS_RETURN_DONE:
break;
case CCTLS_RETURN_RETRY:
lwsl_wsi_debug(wsi, "create_tls RETRY");
return wsi;
default:
lwsl_wsi_debug(wsi, "create_tls FAIL");
goto failed;
}
/*
* We succeeded to negotiate a new client tls
* tunnel. If it's h2 alpn, we have arranged
* to send the h2 prefix and set our state to
* LRS_H2_WAITING_TO_SEND_HEADERS already.
*/
lwsl_wsi_notice(wsi, "tls established st 0x%x, "
"client_h2_alpn %d", lwsi_state(wsi),
wsi->client_h2_alpn);
if (lwsi_state(wsi) !=
LRS_H2_WAITING_TO_SEND_HEADERS)
lwsi_set_state(wsi,
LRS_H1C_ISSUE_HANDSHAKE2);
lws_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
(int)wsi->a.context->timeout_secs);
#if 0
/* ensure pollin enabled */
if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
lwsl_wsi_notice(wsi,
"unable to set POLLIN");
#endif
goto provoke_service;
}
#endif
/* clear his established timeout */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
m = wsi->role_ops->adoption_cb[0];
if (m) {
n = user_callback_handle_rxflow(
wsi->a.protocol->callback, wsi,
(enum lws_callback_reasons)m,
wsi->user_space, NULL, 0);
if (n < 0) {
lwsl_wsi_info(wsi, "RAW_PROXY_CLI_ADOPT err");
goto failed;
}
}
/* service.c pollout processing wants this */
wsi->hdr_parsing_completed = 1;
#if defined(LWS_ROLE_MQTT)
if (meth && !strcmp(meth, "MQTT")) {
#if defined(LWS_WITH_TLS)
if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
lwsi_set_state(wsi, LRS_WAITING_SSL);
return wsi;
}
#endif
lwsl_wsi_info(wsi, "settings LRS_MQTTC_IDLE");
lwsi_set_state(wsi, LRS_MQTTC_IDLE);
/*
* provoke service to issue the CONNECT
* directly.
*/
lws_set_timeout(wsi,
PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
(int)wsi->a.context->timeout_secs);
assert(lws_socket_is_valid(wsi->desc.sockfd));
pfd.fd = wsi->desc.sockfd;
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLOUT;
lwsl_wsi_info(wsi, "going to service fd");
n = lws_service_fd_tsi(wsi->a.context, &pfd, wsi->tsi);
if (n < 0) {
cce = "first service failed";
goto failed;
}
if (n)
/* returns 1 on fail after close wsi */
return NULL;
return wsi;
}
#endif
lwsl_wsi_info(wsi, "setting ESTABLISHED");
lwsi_set_state(wsi, LRS_ESTABLISHED);
return wsi;
}
/*
* provoke service to issue the handshake directly.
*
* we need to do it this way because in the proxy case, this is
* the next state and executed only if and when we get a good
* proxy response inside the state machine... but notice in
* SSL case this may not have sent anything yet with 0 return,
* and won't until many retries from main loop. To stop that
* becoming endless, cover with a timeout.
*/
#if defined(LWS_WITH_TLS) //&& !defined(LWS_WITH_MBEDTLS)
provoke_service:
#endif
lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
(int)wsi->a.context->timeout_secs);
assert(lws_socket_is_valid(wsi->desc.sockfd));
pfd.fd = wsi->desc.sockfd;
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLIN;
n = lws_service_fd_tsi(wsi->a.context, &pfd, wsi->tsi);
if (n < 0) {
cce = "first service failed";
goto failed;
}
if (n) /* returns 1 on failure after closing wsi */
return NULL;
}
#endif
return wsi;
failed:
lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect4");
return NULL;
}

View File

@ -0,0 +1,778 @@
/*
* 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;
memset(score, 0, sizeof(*score));
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->dest));
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
*/
if (!da->source)
return SAS_PREFER_B;
if (!db->source)
return SAS_PREFER_A;
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_wsi_notice(wsi, "empty");
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_wsi_info(wsi, "%d: (%d)%s, gw (%d)%s, idi: %d, "
"lbl: %d, prec: %d", 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_wsi_info(wsi, "sort_dns: %p", 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))
goto next;
ds = lws_zalloc(sizeof(*ds), __func__);
if (!ds)
return 1;
memcpy(&ds->dest, ai->ai_addr, (size_t)ai->ai_addrlen);
ds->dest.sa4.sin_family = (sa_family_t)ai->ai_family;
lws_sa46_write_numeric_address(&ds->dest, afip, sizeof(afip));
lwsl_wsi_info(wsi, "unsorted entry (af %d) %s",
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->context->routing_table.count) {
estr = _lws_route_est_outgoing(pt, &ds->dest);
if (!estr) {
lws_free(ds);
lwsl_wsi_notice(wsi, "%s has no route out\n",
afip);
/*
* 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_wsi_info(wsi, "promoting v4->v6");
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->context->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->context->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;
}