feat: UPnP port forwarding + peer proxy lifecycle#391
Open
myleshorton wants to merge 5 commits intomainfrom
Open
feat: UPnP port forwarding + peer proxy lifecycle#391myleshorton wants to merge 5 commits intomainfrom
myleshorton wants to merge 5 commits intomainfrom
Conversation
Contributor
There was a problem hiding this comment.
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/Mappingimplementation 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.
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>
a71372c to
5a6ffa8
Compare
- 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>
Contributor
There was a problem hiding this comment.
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.
3 tasks
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)ExternalIP()discovery via multiple providers (ipify, ifconfig.me, icanhazip)Peer proxy lifecycle (
vpn/peer.go)PeerProxy.Start(): Map UPnP port → discover external IP → register with API → start sing-box with server config → start heartbeatPeerProxy.Stop(): Deregister → stop sing-box → unmap portWhy
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
🤖 Generated with Claude Code