Skip to content

Register direct transport for unbounded signaling#375

Open
myleshorton wants to merge 4 commits intomainfrom
afisk/direct-transport-for-unbounded
Open

Register direct transport for unbounded signaling#375
myleshorton wants to merge 4 commits intomainfrom
afisk/direct-transport-for-unbounded

Conversation

@myleshorton
Copy link
Contributor

Summary

  • Adds lazyDirectTransport — an http.RoundTripper backed by sing-box's direct outbound dialer
  • Registers it in the sing-box context before service creation so unbounded can retrieve it via adapter.DirectTransportFromContext
  • Resolves it after NewServiceWithContext returns (when the direct outbound exists)

How it works

tunnel.init()
  │
  ├─ Create lazyDirectTransport{}
  ├─ Register in ctx via ContextWithDirectTransport(ctx, transport)
  │
  ├─ libbox.NewServiceWithContext(ctx, ...)
  │    └─ Unbounded outbound constructed, calls DirectTransportFromContext(ctx)
  │       └─ Gets reference to lazyDirectTransport (not yet resolved)
  │
  ├─ directTransport.Resolve(ctx)
  │    └─ Looks up "direct" outbound → wraps its DialContext in http.Transport
  │
  └─ lbService.Start() / PostStart()
       └─ Unbounded makes HTTP calls → lazyDirectTransport delegates to direct outbound
          └─ Socket protection applied automatically by platform

Platform bypass mechanisms (via direct outbound)

Platform Socket protection
Desktop daemon Traffic doesn't enter TUN (daemon owns the device)
Android VpnService.protect(fd)
iOS/macOS IP_BOUND_IF / IPV6_BOUND_IF
Linux SO_BINDTOIFINDEX

Dependencies

Test plan

  • Build passes after lantern-box dependency is updated
  • Verify direct outbound lookup succeeds at tunnel init time
  • Integration test: unbounded signaling traffic bypasses tunnel

🤖 Generated with Claude Code

Add lazyDirectTransport, an http.RoundTripper that wraps sing-box's
direct outbound dialer. It is registered in the context before the
sing-box service is created (so unbounded can retrieve it during
construction) and resolved after (once the direct outbound exists).

The direct outbound's dialer carries platform-specific socket protection
(VpnService.protect on Android, IP_BOUND_IF on iOS/macOS, etc.),
ensuring that unbounded's signaling traffic bypasses the VPN tunnel on
all platforms.

Depends on getlantern/lantern-box#200

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 24, 2026 22:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a “lazy” direct http.RoundTripper that can be registered into the sing-box context early (so outbounds like unbounded can capture it during construction) and then resolved after libbox.NewServiceWithContext returns by wiring it to the sing-box direct outbound dialer.

Changes:

  • Register a lazyDirectTransport in the tunnel context before creating the libbox service.
  • Resolve lazyDirectTransport after service creation by looking up the direct outbound and building an http.Transport that dials via it.
  • Add new vpn/direct_transport.go implementing the lazy transport.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
vpn/tunnel.go Registers and resolves the lazy direct transport around NewServiceWithContext.
vpn/direct_transport.go Implements an http.RoundTripper that defers to a resolved inner transport built from the direct outbound dialer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

myleshorton and others added 3 commits March 24, 2026 17:04
… Resolve, add tests

- Copy inner/resolved under read lock, release before I/O (avoids blocking Resolve)
- Clone http.DefaultTransport instead of bare http.Transport (preserves timeouts, HTTP/2)
- Make Resolve failure fatal in tunnel init (fail fast, not at runtime)
- Add unit tests for unresolved, resolved, no-outbound-manager, and concurrent cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants