Skip to content

client: sort-dns: skip AAAA results when ipv6 is disable#3628

Open
zzblydia wants to merge 100 commits into
warmcat:mainfrom
zzblydia:main_20260709_DISABLE_IPV6
Open

client: sort-dns: skip AAAA results when ipv6 is disable#3628
zzblydia wants to merge 100 commits into
warmcat:mainfrom
zzblydia:main_20260709_DISABLE_IPV6

Conversation

@zzblydia

@zzblydia zzblydia commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

While debugging this I noticed the dns side and the client connection aren't really tied together:

with async dns the queries go out on the resolver's own wsi, get piggybacked between consumers and end up in a context-owned cache.
But LWS_SERVER_OPTION_DISABLE_IPV6 is a context / vhost option. So instead of teaching the shared resolver about it, I'm honouring it where a particular client wsi actually picks up the results — in lws_sort_dns(), which builds the candidate list for both the async and the blocking path.

wsi->ipv6 is already 0 in all the cases that matter here: option set on the context or the vhost, or the client bound to an IPv4 source address. Without this a "disabled" wsi can still connect over ipv6, eg via a cache entry some dual-stack consumer created earlier, and then things like lws_get_peer_addresses() read the peer sockaddr as sockaddr_in and get garbage.

The resolver still asks for both A and AAAA — on purpose. The blocking path does the same thing (getaddrinfo with PF_UNSPEC, glibc sends both), and a shared query might be serving a dual-stack consumer at the same time anyway.
Both paths gather everything and the per-wsi choice happens at the same place.

Verification

I had tested on a dual-stack server within a local network.
Environment: Ubuntu 24.04 x86_64, LWS_WITH_IPV6=ON, LWS_WITH_SYS_ASYNC_DNS=ON.

  • Case 1: DISABLE_IPV6 NOT set
    • Behavior: The client wsi get two ip( ipv4 and ipv6) and firstly connects to the server via IPv4(RFC6724 default policy table).
  • Case 2: DISABLE_IPV6 enabled
    • Behavior: The client successfully ignores the cached AAAA/IPv6 results and connects via IPv4.

vikramdattu and others added 30 commits June 9, 2026 15:36
The mbedTLS 4 migration handled the generic (non-FreeRTOS) include
branch but missed the FreeRTOS paths, so an ESP-IDF v6 / mbedTLS 4
build hits removed-header errors:

- private-lib-tls.h includes <mbedtls/aes.h> and <mbedtls/gcm.h>
  unconditionally in the FreeRTOS branch; both were removed in
  mbedTLS 4. Gate behind !LWS_HAVE_MBEDTLS_V4, mirroring the generic
  branch.
- freertos-sockets.c includes the legacy <mbedtls/net.h> (removed in
  mbedTLS 4) when LWS_HAVE_MBEDTLS_NET_SOCKETS is unset, which it
  always is on FreeRTOS since the probe doesn't run there. Include
  <mbedtls/net_sockets.h> on mbedTLS 4: it still carries
  mbedtls_net_context and the MBEDTLS_ERR_NET_* codes used by the BIO
  callbacks even when MBEDTLS_NET_C is off.

Verified on a real IDF v6 / mbedTLS 4 build.
The m==16 (IPv6) numeric-literal path used ai/sa46 without deriving
them from the cache entry c, unlike the m==4 (IPv4) path just above.
When the cache entry already existed (the allocation block was not
entered), ai/sa46 were uninitialised, which GCC flags as
-Werror=maybe-uninitialized with IPv6 enabled. Mirror the IPv4 path.
Subject: [PATCH] freertos: don't leave SPAWN (and its siginfo_t dep) enabled

LWS_WITH_STUB defaults ON and implies LWS_WITH_SPAWN in
CMakeLists-implied-options.txt before the LWS_PLAT_FREERTOS block
disables STUB, so SPAWN stays on for FreeRTOS. There is no spawn support
on FreeRTOS, and the spawn API's siginfo_t typedef fails to build with
newer newlib (ESP-IDF v6). Force SPAWN off in the FreeRTOS block.
The OpenSSL-compat wrapper selects net_sockets.h over the v4-removed
net.h, and the MBEDTLS_SSL_NEW_SESSION_TICKET enum, via the
LWS_HAVE_MBEDTLS_NET_SOCKETS / LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET
probes. Those don't run on FreeRTOS, so on an mbedTLS 4 build there the
v3 branch is compiled and breaks. Also accept LWS_HAVE_MBEDTLS_V4 so the
v4 path is taken wherever that's set.
IDF v6 switched to picolibc, whose errno is thread-local. lws forces
LWIP_PROVIDE_ERRNO and declares its own `extern int errno`, both
non-TLS, so objects fail to link against picolibc's TLS errno
("TLS definition ... mismatches non-TLS reference"). On mbedTLS 4
(a proxy for IDF v6) skip LWIP_PROVIDE_ERRNO and include <errno.h>
instead. Older IDF (newlib) is unchanged.
If lws_client_connect_via_info() never fully completes (e.g., DNS not resolvable, but glibc
is waiting?), the ah allocated during the h1 connect sequence is still attached to the wsi,
even though it is "pre-natal".

While we're in this state, before the connection failure is handled, if the user ends up calling
lws_context_destroy() e.g., in a libuv foreign loop, the ah pool is freed first.  However,
the wsi still has a dangling pointer to the ah.  Then in a later part of the context destroy sequence,
lws_pt_destroy() loops through the pre-natal wsis, and eventually accesses and dereferences the
dangling wsi->http.ah pointer.

In _lws_destroy_ah(), detach the wsi->http.ah pointer when we free it.
This seems safest, since _lws_destroy_ah() is called in a handful of contexts.
…u list is

 empty

lws_cache_item_evict_lru() silently does nothing when the lru list is
empty, but the eviction loop in lws_cache_heap_write() only checks the
footprint / item count limits.  If the limits still appear exceeded
after the cache has been fully emptied (eg, a single item larger than
max_footprint, or a stale current_footprint inherited from the
pre-existing accounting bug), the loop condition never changes and the
service thread spins forever inside the write, never returning to the
event loop.

Signed-off-by: minicx <minicx@disroot.org>
lws frees wsi->stash early in lws_http_client_connect_via_info2()
when LWS_WITH_SOCKS5 is enabled (even without an actual SOCKS5 proxy).
lws_cookie_write_nsc() handles this with a fallback to
lws_hdr_simple_ptr(), but lws_cookie_attach_cookies() did not,
returning -1 immediately when stash was NULL.

This caused cookie jar lookups to silently fail on builds with
LWS_WITH_SOCKS5 enabled (e.g. txiki.js on Linux), while working on
those without it (e.g. Android builds).

Add the same header-based fallback to lws_cookie_attach_cookies(),
matching the existing pattern in lws_cookie_write_nsc().
Signed-off-by: stropee <simon@sirocha.fr>
… in iface field on FreeRTOS/LwIP

Signed-off-by: stropee <simon@sirocha.fr>
On Windows, send() expects int for the third argument (len), but
socks5-client.c was casting the ssize_t value to (size_t).  MSVC
at /W3 warns C4267 about the implicit narrowing from size_t (64-bit)
to int (32-bit), and with /WX the warning becomes a fatal error
(C2220).

Fix the two send() sites by adding an explicit (int) cast guarded
with #if defined(WIN32), matching the existing pattern used in
output.c.

AG: use helper and fixup similar cases

Ref: txiki.js CI build (error C2220 at socks5-client.c:239,64)
We leave lws_buflist as it is since it is a public api.

We introduce an alternative solution lws_buflist2.  It offers a similar
api to lws_buflist, but it is based on lws_dll2_owner.

 - its footprint in the using struct is an lws_buflist2_owner.
 - allocations are tracked using lws_dll2... that solves problems that
   lws_buflist has having to iterate to find the tail, or total size, it
   already always tracks the tail and allocated size.
 - You can set the sanity limit size per-owner (lws_buflist is fixed)
ws: ss: hexdump before assert
Integrate OpenHiTLS as a new TLS backend option for libwebsockets,
providing an alternative to OpenSSL, mbedTLS, and other TLS libraries.

Core changes:
- CMake detection and build configuration for OpenHiTLS
- Complete TLS client and server implementation
- X.509 certificate operations (parse, verify, load)
- Crypto primitives: AES, RSA, EC, Hash operations
- TLS 1.2 and TLS 1.3 support with session management
- SNI, ALPN, and keylog callback support
- BSL_UIO wrapper layer for OpenHiTLS I/O abstraction

Technical details:
- New cmake/FindOpenHITLS.cmake for library detection
- Backend-specific code in lib/tls/openhitls/
- Integration with lws TLS abstraction layer
- Support for both memory and file-based certificates
- Error mapping between OpenHiTLS and lws error codes
rops_perform_user_POLLOUT_h2() ends by calling
lws_wsi_mux_action_pending_writeable_reqs(), which decides POLLOUT purely
from child streams' requested_POLLOUT and clears POLLOUT when no child
wants it.  It ignores the network connection's own queued protocol sends
(wsi->h2.h2n->pps), e.g. a connection- or stream-level WINDOW_UPDATE that
grants the peer receive-window credit.  When such a pps is queued but no
child stream currently wants POLLOUT, POLLOUT is cleared and the pps is
never flushed, so the peer never gets the credit and the transfer stalls.

This is easy to hit on the client with a bodyless request (e.g. a plain
GET): the request stream goes HALF_CLOSED_LOCAL as soon as the headers are
sent with END_STREAM, so no child wants POLLOUT, while the netconn still
needs to emit the WINDOW_UPDATE that opens the receive window.  The
response headers arrive but the body stalls at the flow-control window
(the stream window, or 65535 at the connection level) until the peer
resets the stream.

Fix: keep POLLOUT asserted while the netconn still has pps pending, before
falling through to the child-only reconciler.

Observed only with the mbedtls TLS backend; the byte-identical code under
GnuTLS/OpenSSL happens to flush via different read/POLLOUT interleaving,
which is likely why this has gone unnoticed.
@zzblydia zzblydia changed the title close # 3625 ws client: sort-dns: skip AAAA results when ipv6 is disa… close #3625 ws client: sort-dns: skip AAAA results when ipv6 is disa… Jul 9, 2026
@zzblydia zzblydia changed the title close #3625 ws client: sort-dns: skip AAAA results when ipv6 is disa… client: sort-dns: skip AAAA results when ipv6 is disa… Jul 9, 2026
@zzblydia zzblydia changed the title client: sort-dns: skip AAAA results when ipv6 is disa… client: sort-dns: skip AAAA results when ipv6 is disable Jul 9, 2026
@lws-team
lws-team force-pushed the main branch 3 times, most recently from aacd499 to 71cc256 Compare July 9, 2026 08:15
@sonarqubecloud

Copy link
Copy Markdown

@zzblydia

Copy link
Copy Markdown
Contributor Author

Code reviewed and comments updated.

    /*
	 * lws_sort_dns() filters out IPv6 results if wsi->ipv6 == 0, pick up
	 * the vhost / context ipv6 policy
	 */

@lws-team
lws-team force-pushed the main branch 14 times, most recently from 33db3e8 to 85ca262 Compare July 17, 2026 04:12
@lws-team
lws-team force-pushed the main branch 2 times, most recently from f3a9f48 to 774c64a Compare July 19, 2026 04:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants