Skip to content

SwarmBuilder::with_websocket hardcodes dns::tokio::Transport::system, bypassing with_dns_config (breaks on Android) #6529

Description

@yexiyue

Summary

The tokio flavour of SwarmBuilder::with_websocket unconditionally wraps its inner TCP transport in a system-config DNS transport:

impl_websocket_builder!(
"tokio",
super::provider::Tokio,
// Note this is an unnecessary await for Tokio Websocket (i.e. tokio dns) in order to be
// consistent with above AsyncStd construction.
futures::future::ready(libp2p_dns::tokio::Transport::system(
libp2p_tcp::tokio::Transport::new(libp2p_tcp::Config::default())
)),
rw_stream_sink::RwStreamSink<libp2p_websocket::BytesConnection<libp2p_tcp::tokio::TcpStream>>
);

This ignores any resolver configuration supplied earlier via with_dns_config. On targets where the system resolver is unavailable, the websocket phase fails even though the user explicitly configured a custom resolver one phase earlier.

Concrete case (Android): hickory-resolver 0.26's system_conf reads the system DNS via JNI (ndk-context). In host environments that never initialize the ndk context — e.g. React Native / uniffi mobile apps, where no android-activity-style glue runs — read_system_conf() fails with android context was not initialized. Switching the DNS phase to with_dns_config(custom_cfg, opts) fixes that call site, but with_websocket then constructs a second, system-config resolver and fails with the byte-for-byte identical error. That makes it genuinely confusing to debug: it looks exactly like the bug you just fixed.

Expected behavior

One of:

  • with_websocket reuses the resolver configuration established in the DNS phase (or skips DNS-wrapping entirely when the outer transport is already DNS-wrapped);
  • a variant like with_websocket_dns_config(cfg, opts, ...) exists so the resolver can be supplied;
  • at minimum, the with_websocket docs mention the hidden Transport::system call and its platform requirements.

Actual behavior

with_websocket always calls libp2p_dns::tokio::Transport::system and returns WebsocketError(Dns(...)) on Android RN hosts, regardless of with_dns_config.

Workaround

On Android we skip the websocket transport via the WebsocketPhase::with_relay_client shortcut (which calls without_websocket() internally). Acceptable when websocket has no consumers on that platform, but it forks the builder chain per target and silently removes /ws//wss dial capability.

Version

  • master @ 93c5059 (libp2p/src/builder/phase/websocket.rs:123)
  • also present in libp2p 0.56.0 (same line in the impl_websocket_builder! expansion)
  • hickory-resolver 0.26.1

Would you like to work on fixing this bug?

Happy to send a PR once maintainers agree on the preferred direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions