Update Files
This commit is contained in:
121
Kinc/Sources/kinc/libs/core-net/client/client.c
Normal file
121
Kinc/Sources/kinc/libs/core-net/client/client.c
Normal 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
|
155
Kinc/Sources/kinc/libs/core-net/client/conmon.c
Normal file
155
Kinc/Sources/kinc/libs/core-net/client/conmon.c
Normal 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;
|
||||
}
|
560
Kinc/Sources/kinc/libs/core-net/client/connect.c
Normal file
560
Kinc/Sources/kinc/libs/core-net/client/connect.c
Normal 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;
|
||||
}
|
395
Kinc/Sources/kinc/libs/core-net/client/connect2.c
Normal file
395
Kinc/Sources/kinc/libs/core-net/client/connect2.c
Normal 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
|
||||
}
|
808
Kinc/Sources/kinc/libs/core-net/client/connect3.c
Normal file
808
Kinc/Sources/kinc/libs/core-net/client/connect3.c
Normal 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;
|
||||
}
|
338
Kinc/Sources/kinc/libs/core-net/client/connect4.c
Normal file
338
Kinc/Sources/kinc/libs/core-net/client/connect4.c
Normal 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;
|
||||
}
|
778
Kinc/Sources/kinc/libs/core-net/client/sort-dns.c
Normal file
778
Kinc/Sources/kinc/libs/core-net/client/sort-dns.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user