Reverted timeout-based solution and restored the unified reader architecture:
- Each peer connection continuously reads and queues to dataChan
- Single unifiedReader() dispatcher matches data with read requests
- No blocking - all connections monitored simultaneously
- Addresses @RPRX's request for unified reader instead of timeout
Architecture benefits:
- True concurrent reading from all peer connections
- Clean separation between reading and dispatching
- No timeout delays or retry loops
- Scalable to any number of peers
Tests pass.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Replaced complex unified reader with minimal changes:
- Add 50ms read deadline to prevent readers from blocking indefinitely
- Use core.ToBackgroundDetachedContext for connection independence
- Clear deadline after read completes
- Allows multiple peer readers to timeout and retry instead of blocking
This is much simpler than the unified reader architecture while still
solving the core blocking issue. When a reader times out, it returns
and another reader can try, allowing all peers to work.
Tests pass.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
- Use named constant for dataChan buffer size
- Add bounds checking to prevent panic if n > len(data)
- Only send valid data portion (buf[:n]) to dataChan
- Use sync.Once to prevent double-close panic in Close()
- Add comment explaining data loss risk (acceptable for UDP-like behavior)
All tests pass, no security vulnerabilities found.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Replaced the competing reader goroutines with a unified reading architecture:
- Each peer connection continuously reads into a shared data channel
- A single dispatcher goroutine matches received data with read requests
- Eliminates blocking issues - all connections are monitored simultaneously
- No more race conditions between peer readers
This addresses @RPRX's suggestion to "统一 read 后再分给指定的 peer reader"
(unified read then distribute to specified peer readers).
Architecture:
- connectTo() registers connection and starts a dedicated reader per connection
- Each connection reader continuously reads and sends to dataChan
- unifiedReader() dispatcher waits for data, then matches with pending requests
- All peers can receive simultaneously without any blocking
Tests pass successfully.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Use core.ToBackgroundDetachedContext to prevent all peer connections from
being cancelled when the original request context is cancelled. This ensures
peer connections remain independent and stable.
Tests pass, no security issues found.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
The issue was that with only 1 worker (default), when a peer's reader goroutine
blocked waiting for data, it prevented other peers from receiving packets.
Simple solution: Automatically set workers to the number of peers if not
explicitly configured. This allows each peer to have its own worker thread
for concurrent packet reception.
- Reverted complex architectural changes
- Added simple logic to set workers = len(peers) when NumWorkers not set
- Much simpler and easier to understand than previous approach
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Only clear reserved bytes if read was successful (err == nil). This prevents
processing invalid data when conn.Read() returns an error.
Code review feedback addressed.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
The root cause was architectural: each peer connection created a goroutine
that competed for the same readQueue. When a goroutine grabbed a read request
but its connection had no data, it would block, preventing other peers from
receiving packets. This caused the "only one peer works at a time" behavior.
Solution: Redesigned the packet flow:
- Each peer connection now continuously reads from its socket and sends
packets to a shared packetQueue
- A dispatcher goroutine matches readQueue requests (from WireGuard) with
packets from packetQueue
- This allows all peer connections to work simultaneously without blocking
Changes:
- Added packetQueue channel and receivedPacket struct to buffer packets
- Modified Open() to start a dispatcher goroutine
- Rewrote connectTo() to continuously read and queue packets
- Each peer connection now operates independently
Tests pass. This architectural fix addresses the fundamental issue with
multi-peer WireGuard support.
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
The issue was in client.go, not server.go. When WireGuard is used as an
outbound with multiple peers, all peers were sharing the same context from
the first connection. This caused all subsequent peer connections to be
associated with the first connection's session ID, leading to routing failures.
The fix uses core.ToBackgroundDetachedContext() to create an independent
context for the netBindClient, allowing each peer connection to work
independently with its own session context.
- Reverted incorrect changes to server.go
- Fixed client.go to use detached context for the bind
- Tests pass successfully
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Add mutex protection to server.go to prevent race condition when multiple
peers connect simultaneously. The shared routingInfo field was being
overwritten by concurrent Process() calls, causing connections to fail.
- Add sync.RWMutex to protect access to routing info
- Only update routing info if not already set or dispatcher changed
- Use local copy of routing info in forwardConnection to avoid races
- Existing tests pass
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
* Proxy: Implement tun raw network interface inbound support for Linux
* Proxy: Tun. Include "android" as build condition for build of tun_default implementation
* Proxy: Tun. Add .Close() cleanup calls to Handler.Init() where needed
* Proxy: Add Tun for Android
* Proxy: Tun. Implement Windows support
---------
Co-authored-by: yuhan6665 <1588741+yuhan6665@users.noreply.github.com>
* Wireguard: Decouple server endpoint DNS from address option
Previously, Wireguard server endpoint's domain resolution was incorrectly constrained by the local `address` option. For example, `ForceIPv6v4` might fail to resolve AAAA records for the server domain if no IPv6 was explicitly configured in the `address` option.
This commit decouples the server endpoint's domain resolution from the local `address` configuration. It ensures the Wireguard server address is resolved independently, allowing its `domainStrategy` to function correctly without being limited by the client's local network or `address` settings.
* Delete code instead of commenting it out