Skip to content

feat: UPnP port forwarding + peer proxy lifecycle#391

Open
myleshorton wants to merge 5 commits intomainfrom
afisk/port-forwarding
Open

feat: UPnP port forwarding + peer proxy lifecycle#391
myleshorton wants to merge 5 commits intomainfrom
afisk/port-forwarding

Conversation

@myleshorton
Copy link
Copy Markdown
Contributor

@myleshorton myleshorton commented Mar 31, 2026

Summary

Enables uncensored Lantern users to act as full samizdat proxy servers on residential IPs, directly accessible to censored users.

Port forwarding (portforward/portforward.go)

  • UPnP IGD v2/v1 port mapping with retry logic (10 attempts, random ports 10000-60000)
  • 1-hour lease with automatic renewal at 50% interval
  • ExternalIP() discovery via multiple providers (ipify, ifconfig.me, icanhazip)
  • Clean unmapping on shutdown

Peer proxy lifecycle (vpn/peer.go)

  • PeerProxy.Start(): Map UPnP port → discover external IP → register with API → start sing-box with server config → start heartbeat
  • PeerProxy.Stop(): Deregister → stop sing-box → unmap port
  • API generates all credentials (X25519, short ID, TLS cert, masquerade domain) and returns complete sing-box config
  • Heartbeat every 5 minutes keeps route alive; missed heartbeat auto-deprecates

Why

Residential IPs fall outside the GFW's monitoring scope (~2% of IPs, datacenter ranges only). See full analysis.

Server-side companion PR

getlantern/lantern-cloud#2553 — peer registration API

Test plan

  • UPnP port mapping on a home router
  • External IP discovery returns correct public IP
  • Full lifecycle: start → register → heartbeat → stop → deregister
  • sing-box starts with API-generated config and accepts samizdat connections

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 31, 2026 19:36
Copy link
Copy Markdown
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

Introduces a new portforward Go package to enable UPnP IGD-based TCP port forwarding, supporting the “Share My Connection” peer proxy feature by making a desktop peer reachable from the public internet.

Changes:

  • Added Forwarder/Mapping implementation for UPnP IGDv2 with IGDv1 fallback, plus lease renewal and unmap support.
  • Implemented random external port selection within a configured range with retries.
  • Added basic unit tests for LocalIP, port selection, and no-op behaviors.

Reviewed changes

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

File Description
portforward/portforward.go New UPnP IGD port mapping + renewal/unmapping implementation and LocalIP helper.
portforward/portforward_test.go New unit tests covering LocalIP, randomPort range, and Forwarder no-op behavior.

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

Comment thread portforward/portforward.go
Comment thread portforward/portforward.go Outdated
Comment thread portforward/portforward.go Outdated
Comment thread portforward/portforward.go Outdated
Comment thread portforward/portforward.go Outdated
Comment thread portforward/portforward_test.go
Comment thread portforward/portforward.go
Comment thread portforward/portforward.go
myleshorton and others added 2 commits April 11, 2026 05:51
Enables "Share My Connection" by opening ports on the local router via
UPnP IGD (v2 with v1 fallback). Random port selection with retry,
lease renewal at 50% interval, and graceful cleanup on unmap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add PeerProxy type that manages the full lifecycle of a residential
peer proxy:
1. Maps port via UPnP (existing portforward module)
2. Discovers external IP via public API services
3. Registers with lantern-cloud API (POST /v1/peer/register)
4. Receives and starts sing-box with server config from API
5. Heartbeats every 5 minutes to keep route alive
6. Clean shutdown: deregister + stop sing-box + unmap port

Also adds ExternalIP() to portforward package for public IP discovery
using multiple providers (ipify, ifconfig.me, icanhazip) with fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@myleshorton myleshorton force-pushed the afisk/port-forwarding branch from a71372c to 5a6ffa8 Compare April 11, 2026 11:53
@myleshorton myleshorton changed the title Add UPnP port forwarding for peer proxy sharing feat: UPnP port forwarding + peer proxy lifecycle Apr 11, 2026
myleshorton and others added 2 commits April 11, 2026 06:44
- Fix goroutine leak: capture stopC in local var for renewal goroutine
- Store description in Mapping, reuse in renewMapping
- Fix docstring: random port selection, not requested port
- Iterate all discovered IGD clients, not just first
- Handle randomPort error, fix inclusive range
- Add Description field to Mapping struct

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /peer/start  — Start peer proxy (async: UPnP + API registration)
POST /peer/stop   — Stop peer proxy (deregister + cleanup)
GET  /peer/status  — Return { active: bool }

The PeerController interface decouples IPC from the peer proxy
implementation. PeerProxy in vpn/peer.go satisfies it. The controller
is registered via Server.SetPeerController() — if not set, endpoints
return 501 Not Implemented.

Client-side functions: StartPeerProxy(), StopPeerProxy(), GetPeerStatus()
follow the existing sendRequest[T]() pattern.

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

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


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

Comment thread vpn/ipc/peer.go Outdated
Comment thread vpn/ipc/server.go
Comment thread vpn/peer.go
Comment thread vpn/peer.go
Comment thread portforward/portforward_test.go Outdated
Comment thread vpn/peer.go
Comment thread vpn/ipc/peer.go
- Use service context (not request context) for async peer start
- Check deregister HTTP response status
- Remove unused ctx param from startSingbox
- Add auth token (X-Lantern-Pro-Token) to all API requests
- Fix test assertion for inclusive port range
- Fix comment on SetPeerController about status behavior

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