Skip to content

Conversation

@aannaannyaaa
Copy link

Fix Chrome WebTransport DNS port-scanning penalty

Fixes #3286

Problem

Chrome has an anti-port-scanning mechanism that penalizes cancelled WebTransport requests. When a DNS-based multiaddr is dialed and cancelled before DNS resolution completes, Chrome stores the penalty against an empty string key instead of a specific IP address.

This causes ALL future DNS-based WebTransport dials to be penalized, not just dials to that specific host.

Solution

This PR adds DNS pre-resolution for WebTransport multiaddrs in Chrome only:

  1. Detects Chrome browser via user agent (isChrome())
  2. Detects DNS components in multiaddrs (hasDNSComponent())
  3. Adds async boundary before creating WebTransport session
  4. Allows Chrome's DNS resolver to complete before dial
  5. Ensures penalties are applied per-IP, not globally

Key Changes

  • Added isChrome() function to detect Chrome/Chromium browsers
  • Added hasDNSComponent() to detect DNS-based multiaddrs
  • Added resolveMultiaddrDNS() to create async boundary for Chrome
  • Refactored dial() to pre-resolve DNS before dialing
  • Added dialSingleAddress() private method for cleaner separation
  • Added comprehensive tests for Chrome detection and DNS handling

How It Works

The async boundary (await setTimeout(0)) ensures we yield to the event loop, giving Chrome's internal DNS resolver time to complete before the WebTransport session is created:

  • Before: dial() → immediately create WebTransport → DNS not resolved → cancellation → penalty to ""
  • After: dial() → async boundary → create WebTransport → DNS resolved → cancellation → penalty to specific IP

Test Results

22/22 tests passing in browser environment
22/22 tests passing in webworker environment
All new Chrome DNS tests passing (11 new tests)
No regressions in existing tests

New Tests Added

  • Chrome detection tests (4 tests)
  • DNS component detection tests (5 tests)
  • DNS multiaddr handling tests (2 tests)

Browser Compatibility

  • Chrome/Chromium: DNS pre-resolution active (fixes issue)
  • Firefox/Safari/Edge: No change in behavior
  • Node.js: No change in behavior

Breaking Changes

None. This is a backward-compatible fix that only affects Chrome browsers with DNS-based multiaddrs.

image

@aannaannyaaa aannaannyaaa requested a review from a team as a code owner November 21, 2025 19:24
@tabcat
Copy link
Contributor

tabcat commented Nov 23, 2025

Hi @aannaannyaaa !
Thanks for opening this, and thanks for the very helpful PR description.

There are a few issues I've spotted.

The first has to do with simple linting issues like trailing spaces and unused variables.
The workflows haven't been enabled for this PR so it's possible it could fail other checks that are in place. If you want to test those you can check what the ci workflow does and run those scripts locally. I'll ping someone soon about enabling the workflows soon.

The second issue is more structural. I don't see where the dns address in the ma is actually being turned into an ip before its handed to the WebTransport construction which would be doing the dns resolution. From reading the original issue I believe the implied action was to use the dns service from libp2p or a new instance of @multiformats/dns to resolve the ip address, and hand that to the WebTransport construction.

From a glance it looks like the best place to put that logic would be directly before the WebTransport construction.
https://example.com:4000 -> https://123.123.123.123:4000
There shouldn't be a need to fallback to the dns host if that connection fails either. So removing the dialSingleAddress and dial loop would be best.

The isChrome method you've added looks good.

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.

Resolve DNS portions of WebTransport addresses before dialing

2 participants