Conversation
- Merge bind_*.go and gateway_*.go into unified netutil_*.go files - netutil_bsd.go: bindToInterface (IP_BOUND_IF) + getDefaultGateway for macOS/BSD - netutil_linux.go: bindToInterface (LocalAddr) + getDefaultGateway for Linux - netutil.go: no-op fallbacks for unsupported platforms - Simplify bindToInterface on BSD to only use iface.Index - Reduces file count from 6 to 3
- Replace 0.0.0.0/32 hack with proper -ifscope default route - SetGatewayRoute now uses 'route add -ifscope <iface> default <gateway>' - This creates a scoped default route for IP_BOUND_IF sockets - UnsetGatewayRoute updated to clean up ifscope routes properly - More logical and standards-compliant routing approach
- Better reflects the semantics: deadline extends on activity - Updated comments to clarify idle-based timeout behavior - Connection stays alive as long as there are Read/Write operations
- ConnOptions.UDPTimeout -> ConnOptions.UDPIdleTimeout - CLI flag: --udp-timeout -> --udp-idle-timeout - TOML key: udp-timeout -> udp-idle-timeout - Updated all usages across codebase and tests - Clearer naming to indicate idle-based timeout behavior
There was a problem hiding this comment.
Pull request overview
This PR refactors SpoofDPI’s proxy architecture into a unified server.Server interface, expands functionality (SOCKS5 incl. UDP ASSOCIATE + TUN UDP/TCP handling), and updates configuration/schema + docs to match the new “app/connection/udp” option model.
Changes:
- Introduces
internal/server/*implementations (HTTP, SOCKS5, TUN) and moves startup/shutdown toserver.Server. - Adds UDP support across layers: UDP desyncer, UDP packet writer/sniffer, UDP tunneling/connection pooling, and platform routing helpers.
- Migrates config structure (
general/server→app/connection, adds[udp]), updates DNS/resolver plumbing, and refreshes MkDocs user-guide navigation/content.
Reviewed changes
Copilot reviewed 85 out of 86 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| mkdocs.yml | Updates documentation nav for new option sections (App/Connection/UDP). |
| internal/system/sysproxy.go | Removes old system proxy stub (moved to per-server network config). |
| internal/system/sysproxy_linux.go | Removes old Linux proxy stub. |
| internal/system/sysproxy_darwin.go | Removes old macOS proxy implementation (replaced by PAC-based per-server setup). |
| internal/session/session.go | Updates trace ID generation and defaulting behavior. |
| internal/server/tun/udp.go | Adds UDP tunneling handler for TUN mode (pool + idle timeout + UDP desync). |
| internal/server/tun/tcp.go | Adds TCP/TLS handler for TUN mode (SNI extraction + rule matching + tunneling). |
| internal/server/tun/network.go | Adds no-op routing helpers for unsupported OSes. |
| internal/server/tun/network_linux.go | Implements Linux route/interface/policy-routing helpers for TUN. |
| internal/server/tun/network_bsd.go | Implements BSD/macOS route/interface helpers for TUN. |
| internal/server/socks5/udp_associate.go | Implements SOCKS5 UDP ASSOCIATE relay logic (client↔target UDP). |
| internal/server/socks5/server.go | Adds SOCKS5 server implementing server.Server. |
| internal/server/socks5/network.go | Adds non-darwin system proxy no-op for SOCKS5 mode. |
| internal/server/socks5/network_darwin.go | Adds macOS PAC-based system proxy setup for SOCKS5 mode. |
| internal/server/socks5/connect.go | Implements SOCKS5 CONNECT handling (TCP + TLS desync when applicable). |
| internal/server/socks5/bind.go | Implements SOCKS5 BIND handling. |
| internal/server/server.go | Introduces common server.Server interface (Start/Stop/Addr/network config). |
| internal/server/http/server.go | Refactors HTTP proxy into server.Server (Start/Stop + network config hooks). |
| internal/server/http/network.go | Adds non-darwin system proxy no-op for HTTP mode. |
| internal/server/http/network_darwin.go | Adds macOS PAC-based system proxy setup for HTTP mode. |
| internal/server/http/https.go | Refactors HTTPS CONNECT handling + tunnels via new netutil tunnel helpers. |
| internal/server/http/http.go | Refactors HTTP forwarding + tunnels via new netutil tunnel helpers. |
| internal/proxy/socks5/socks5_proxy.go | Removes legacy SOCKS5 proxy implementation (replaced by internal/server/socks5). |
| internal/proxy/proxy.go | Removes legacy proxy interface. |
| internal/proto/socks5.go | Renames SOCKS5 constants/types and expands request fields (ATYP/FQDN). |
| internal/proto/http.go | Renames ExtractDomain → ExtractHost. |
| internal/packet/udp_writer.go | Adds UDP packet injector (crafted UDP packets). |
| internal/packet/udp_sniffer.go | Adds UDP sniffer for hop/TTL estimation. |
| internal/packet/tcp_writer.go | Adjusts Ethernet header emission logic for TCP writer. |
| internal/packet/tcp_sniffer.go | Updates TCP sniffer: cache keys, local filtering, linktype-aware BPF. |
| internal/packet/sniffer.go | Updates sniffer interface + adds shared hop estimation/local-IP helpers. |
| internal/packet/network_detector.go | Improves gateway/interface detection + adds periodic probing. |
| internal/packet/LICENSE | Removes vendored license file from internal/packet. |
| internal/packet/handle_linux.go | Improves Linux LinkType detection (Ethernet vs Raw vs SLL). |
| internal/netutil/pac.go | Adds local PAC server helper for macOS network proxy configuration. |
| internal/netutil/netutil.go | Adds non-linux/darwin/freebsd stubs for interface binding + gateway detection. |
| internal/netutil/netutil_linux.go | Adds Linux interface-binding + default-gateway detection. |
| internal/netutil/netutil_bsd.go | Adds BSD/macOS interface-binding + default-gateway detection. |
| internal/netutil/dial.go | Refactors DialFastest to accept a Destination (addrs/port/timeout/iface). |
| internal/netutil/conn.go | Refactors tunneling helpers (directional results + WaitAndLogTunnel + conn wrappers). |
| internal/netutil/conn_pool.go | Adds UDP connection pool (LRU + idle timeout cleanup). |
| internal/netutil/addr.go | Introduces Destination + adds subnet selection + default iface/gateway utilities. |
| internal/matcher/matcher.go | Switches pointer helpers to lo and updates priority comparison. |
| internal/matcher/matcher_test.go | Updates tests to use lo pointers. |
| internal/matcher/domain.go | Updates defaults for rule fields using lo. |
| internal/matcher/domain_test.go | Updates tests to use lo pointers. |
| internal/matcher/addr.go | Updates defaults for rule fields using lo. |
| internal/matcher/addr_test.go | Updates tests to use lo pointers. |
| internal/logging/logging.go | Adjusts trace-id logging behavior + renames joined-error logging helpers. |
| internal/dns/udp.go | Adds conn timeout wiring into UDP resolver and renames defaults. |
| internal/dns/system.go | Renames System resolver type + switches to LookupIP and net.IP slices. |
| internal/dns/route.go | Updates routing resolver to net.IP + improves logging and error handling. |
| internal/dns/resolver.go | Switches RecordSet to []net.IP and joins resolver errors via errors.Join. |
| internal/dns/https.go | Refactors DoH to POST + http2 transport + retry handling. |
| internal/dns/cache.go | Updates cache logging granularity/fields. |
| internal/dns/addrselect/addrselect.go | Refactors RFC6724 sorter to []net.IP (drops Zone handling). |
| internal/desync/udp.go | Adds UDP desyncer (fake packet injection based on hop/TTL). |
| internal/config/validate.go | Updates validator enums for new app/connection/segment option sets. |
| internal/config/validate_test.go | Adds/updates validation tests (incl. log-level coverage). |
| internal/config/toml.go | Switches pointer helper to lo. |
| internal/config/toml_test.go | Updates TOML fixtures to new [app]/[connection]/[udp] schema. |
| internal/config/segment_test.go | Adds placeholder segment test file. |
| internal/config/parse.go | Adds parsing for app mode + segment-from + custom split mode. |
| internal/config/config.go | Restructures config model and defaulting; adds UDP + pcap enablement checks. |
| internal/config/config_test.go | Updates tests for config reshape + UDP defaults. |
| internal/config/cli_test.go | Updates CLI tests for renamed flags/options and UDP flags. |
| internal/cache/ttl_cache.go | Adds Range and Size helpers. |
| internal/cache/lru_cache.go | Adds Range/Delete/Size helpers. |
| internal/cache/cache.go | Extends cache interface with Delete/Range/Size. |
| cmd/spoofdpi/main_test.go | Updates main tests for new config shape and server creation. |
| go.mod | Adds dependencies (water, x/net, gvisor, lo, etc.) and bumps x/* versions. |
| go.sum | Updates dependency checksums accordingly. |
| docs/user-guide/udp.md | Adds UDP configuration documentation. |
| docs/user-guide/server.md | Removes old server configuration doc (replaced by app/connection). |
| docs/user-guide/policy.md | Removes policy auto section; reframes template behavior. |
| docs/user-guide/overview.md | Updates overview for new option categories and examples. |
| docs/user-guide/https.md | Documents custom split-mode and new defaults. |
| docs/user-guide/connection.md | Adds connection timeout + fake TTL documentation. |
| docs/user-guide/app.md | Replaces “General” page with “App” page and new flag names. |
internal/netutil/conn_pool.go
Outdated
| func (p *ConnPool) Add(key string, rawConn net.Conn) *PooledConn { | ||
| p.mu.Lock() | ||
| defer p.mu.Unlock() | ||
|
|
||
| // Evict if capacity is reached | ||
| if p.ll.Len() >= p.capacity { | ||
| p.evictOldest() | ||
| } | ||
|
|
||
| now := time.Now() | ||
| expiredAt := now.Add(p.timeout) | ||
|
|
||
| wrapper := &PooledConn{ | ||
| Conn: rawConn, | ||
| pool: p, | ||
| key: key, | ||
| timeout: p.timeout, | ||
| expiredAt: expiredAt, | ||
| } | ||
|
|
||
| _ = rawConn.SetDeadline(expiredAt) | ||
|
|
||
| elem := p.ll.PushFront(&connEntry{key: key, conn: wrapper}) | ||
| p.cache[key] = elem | ||
|
|
||
| return wrapper |
| rawConn, err := netutil.DialFastest(ctx, "udp", dst) | ||
| if err != nil { | ||
| logger.Warn().Err(err).Str("addr", targetAddrStr).Msg("failed to dial udp target") | ||
| continue | ||
| } | ||
|
|
||
| // Add to pool (pool handles LRU eviction and deadline) | ||
| conn := h.pool.Add(key, rawConn) | ||
|
|
||
| // Apply UDP options from rule if matched | ||
| udpOpts := h.defaultUDPOpts.Clone() | ||
| if rule != nil && rule.UDP != nil { | ||
| udpOpts = udpOpts.Merge(rule.UDP) | ||
| } | ||
|
|
||
| // Send fake packets before real payload (UDP desync) | ||
| if h.desyncer != nil { | ||
| _, _ = h.desyncer.Desync(ctx, lNewConn, conn.Conn, udpOpts) | ||
| } | ||
|
|
||
| // Start a goroutine to read from the target and forward to the client | ||
| go func(targetConn *netutil.PooledConn, clientAddr *net.UDPAddr) { | ||
| respBuf := make([]byte, 65535) | ||
| for { | ||
| n, _, err := targetConn.Conn.(*net.UDPConn).ReadFromUDP(respBuf) | ||
| if err != nil { | ||
| // Connection closed or network issues | ||
| return | ||
| } | ||
|
|
||
| // Inbound: Target -> Proxy -> Client | ||
| // Wrap with SOCKS5 Header | ||
| remoteAddr := targetConn.Conn.(*net.UDPConn).RemoteAddr().(*net.UDPAddr) | ||
| header := createUDPHeaderFromAddr(remoteAddr) | ||
| response := append(header, respBuf[:n]...) | ||
|
|
||
| if _, err := lNewConn.WriteToUDP(response, clientAddr); err != nil { | ||
| // If we can't write back to the client, it might be gone or network issue. | ||
| // Exit this goroutine to avoid busy looping. | ||
| logger.Warn().Err(err).Msg("failed to write udp to client") | ||
| return | ||
| } | ||
| } | ||
| }(conn, clientAddr) |
| } else { | ||
| // Check IP Version == 4 at the base offset | ||
| // Load byte at baseOffset, mask 0xF0, check if 0x40 | ||
| instructions = append( | ||
| instructions, | ||
| // BPFInstruction{Op: 0x30, Jt: 0, Jf: 0, K: baseOffset}, // Ldb [baseOffset] | ||
| BPFInstruction{Op: 0x54, Jt: 0, Jf: 0, K: 0xf0}, // And 0xf0 | ||
| BPFInstruction{ | ||
| Op: 0x15, | ||
| Jt: 0, | ||
| Jf: 8, | ||
| K: 0x40, | ||
| }, // Jeq 0x40, True, False(Skip to End) | ||
| ) |
| packetLayers, err = uw.createIPv6Layers( | ||
| srcMAC, | ||
| dstMAC, | ||
| srcUDP.IP, | ||
| srcUDP.IP, | ||
| srcPort, | ||
| dstPort, | ||
| ttl, | ||
| ) |
| // Configure HTTP/2 transport explicitly | ||
| if err := http2.ConfigureTransport(tr); err != nil { | ||
| // Log error instead of panic if strict http2 is not required, otherwise panic | ||
| panic(fmt.Sprintf("failed to configure http2: %v", err)) | ||
| } |
| for _, addr := range addrs { | ||
| if ipnet, ok := addr.(*net.IPNet); ok { | ||
| // Match IP version: use IPv4 source for IPv4 target, IPv6 for IPv6 | ||
| if targetIP.To4() != nil && ipnet.IP.To4() != nil && !ipnet.IP.IsLoopback() { | ||
| dialer.LocalAddr = &net.TCPAddr{IP: ipnet.IP} | ||
| return nil | ||
| } else if targetIP.To4() == nil && ipnet.IP.To4() == nil && !ipnet.IP.IsLoopback() { | ||
| dialer.LocalAddr = &net.TCPAddr{IP: ipnet.IP} | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Pull request overview
Adds new runtime modes (SOCKS5 and TUN) and refactors networking/config plumbing to support UDP handling, system network configuration, and packet-based desync features across modes.
Changes:
- Introduces a unified
server.Serverinterface and implements SOCKS5 proxy mode plus TUN TCP/UDP handlers. - Refactors config layout into
[app],[connection], and[udp], updates CLI/tests/docs accordingly, and modernizes DNS/connection dialing aroundnetutil.Destination. - Expands packet tooling (UDP sniffer/writer, routing helpers, session cache, tunnel accounting) to support UDP desync and TUN routing.
Reviewed changes
Copilot reviewed 90 out of 91 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| mkdocs.yml | Updates docs navigation for new option sections (App/Connection/UDP). |
| internal/system/sysproxy.go | Removes legacy system proxy stubs. |
| internal/system/sysproxy_linux.go | Removes legacy Linux system proxy stub. |
| internal/system/sysproxy_darwin.go | Removes legacy macOS system proxy implementation (moved per-server). |
| internal/session/session.go | Adjusts trace ID generation/lookup implementation. |
| internal/server/tun/udp.go | Adds UDP handler for TUN mode with rule-based options and tunneling. |
| internal/server/tun/tcp.go | Adds TCP(+TLS sniff/SNI match) handler for TUN mode. |
| internal/server/tun/network.go | Adds unsupported-platform no-op routing/address helpers. |
| internal/server/tun/network_linux.go | Adds Linux route/interface/gateway policy routing helpers. |
| internal/server/tun/network_bsd.go | Adds macOS/FreeBSD route/interface/gateway helpers. |
| internal/server/socks5/udp_associate.go | Implements SOCKS5 UDP ASSOCIATE relay with NAT session pooling + UDP desync. |
| internal/server/socks5/server.go | Implements SOCKS5 server (accept loop, negotiation, command dispatch, net config). |
| internal/server/socks5/network.go | Adds non-darwin no-op system proxy hooks for SOCKS5 mode. |
| internal/server/socks5/network_darwin.go | Adds macOS PAC-based system proxy setup for SOCKS5 mode. |
| internal/server/socks5/connect.go | Implements SOCKS5 CONNECT handling with TLS-aware desync path. |
| internal/server/socks5/bind.go | Implements SOCKS5 BIND handling. |
| internal/server/server.go | Introduces new core Server interface. |
| internal/server/http/server.go | Migrates HTTP proxy to server.Server and new config layout; updates dialing/matching. |
| internal/server/http/network.go | Adds non-darwin no-op system proxy hooks for HTTP mode. |
| internal/server/http/network_darwin.go | Adds macOS PAC-based system proxy setup for HTTP mode. |
| internal/server/http/https.go | Refactors HTTPS handler to use new destination/conn options and tunnel aggregation. |
| internal/server/http/http.go | Refactors HTTP handler to use new destination type and shared tunnel aggregation. |
| internal/proxy/proxy.go | Removes legacy ProxyServer interface. |
| internal/proto/socks5.go | Expands SOCKS5 protocol parsing/types/constants. |
| internal/proto/http.go | Renames ExtractDomain to ExtractHost. |
| internal/packet/udp_writer.go | Adds UDP packet crafting/injection support. |
| internal/packet/udp_sniffer.go | Adds UDP packet sniffing + hop/TTL estimation updates. |
| internal/packet/tcp_writer.go | Adjusts Ethernet header emission for IPv4 crafted packets. |
| internal/packet/sniffer.go | Updates sniffer interface and adds hop estimation/local-IP helpers. |
| internal/packet/network_detector.go | Refactors network detection/probing and default interface selection. |
| internal/packet/LICENSE | Removes embedded Apache 2.0 license file from packet dir. |
| internal/packet/handle_linux.go | Improves Linux link-type detection (SLL vs Ethernet vs raw IP). |
| internal/netutil/session_cache.go | Adds NAT-keyed session cache with LRU + idle/cleanup behavior. |
| internal/netutil/route.go | Adds safe subnet selection and default iface/gateway discovery helpers. |
| internal/netutil/route_unsupported.go | Adds unsupported-platform stubs for routing helpers. |
| internal/netutil/route_linux.go | Adds Linux bind-to-interface and default gateway discovery. |
| internal/netutil/route_bsd.go | Adds BSD bind-to-interface and default gateway discovery. |
| internal/netutil/pac.go | Adds local PAC HTTP server helper. |
| internal/netutil/netutil.go | Adds IPv4-mapped detection helper used by key types. |
| internal/netutil/key.go | Adds zero-allocation NAT/IP cache keys. |
| internal/netutil/key_benchmark_test.go | Adds benchmark for NAT key allocation changes. |
| internal/netutil/dst.go | Introduces Destination struct and updates destination validation. |
| internal/netutil/dial.go | Refactors dialing to accept Destination (timeouts + iface binding). |
| internal/netutil/conn.go | Refactors tunneling to report transfer results + adds buffered/idle-timeout conn wrappers. |
| internal/matcher/matcher.go | Replaces internal ptr helper usage with lo in priority comparisons. |
| internal/matcher/matcher_test.go | Updates matcher tests to use lo pointers. |
| internal/matcher/domain.go | Updates defaulting logic to use lo pointers. |
| internal/matcher/domain_test.go | Updates domain matcher tests to use lo pointers. |
| internal/matcher/addr.go | Updates defaulting logic to use lo pointers. |
| internal/matcher/addr_test.go | Updates addr matcher tests to use lo pointers. |
| internal/logging/logging.go | Ensures trace_id is always logged; renames unwrapped error helpers. |
| internal/dns/udp.go | Adds conn-timeout integration into UDP resolver. |
| internal/dns/system.go | Renames system resolver + switches to LookupIP returning net.IP. |
| internal/dns/route.go | Refactors routing resolver to net.IP and improves logging/metrics. |
| internal/dns/resolver.go | Switches DNS record set to net.IP and joins multi-errors via errors.Join. |
| internal/dns/https.go | Refactors DoH transport (HTTP/2) and changes exchange to POST with retries. |
| internal/dns/cache.go | Updates cache resolver to generic cache interface and new method names. |
| internal/dns/addrselect/addrselect.go | Refactors RFC6724 selection to operate on net.IP. |
| internal/desync/udp.go | Adds UDP desyncer implementation using writer/sniffer TTL data. |
| internal/config/validate.go | Updates validators for new app/connection/udp option enums. |
| internal/config/validate_test.go | Updates tests and adds log-level validator test. |
| internal/config/toml.go | Switches ptr helper to lo.ToPtr. |
| internal/config/toml_test.go | Updates config parsing tests to new app/connection/udp structure. |
| internal/config/segment_test.go | Adds placeholder test file for segment package/tests. |
| internal/config/parse.go | Adds parsing for app mode, custom HTTPS split mode, and segment-from type. |
| internal/config/config.go | Major config schema refactor (App/Conn/UDP) + updates pcap-enable logic and defaults. |
| internal/config/config_test.go | Updates config tests for schema refactor and UDP defaults. |
| internal/config/cli_test.go | Updates CLI tests for new flags/options and SOCKS5 default port behavior. |
| internal/cache/ttl_cache.go | Refactors TTL cache to be generic and expands API surface. |
| internal/cache/ttl_cache_benchmark_test.go | Adds benchmark comparing string vs struct keys. |
| internal/cache/lru_cache.go | Refactors LRU cache to be generic and expands API surface. |
| internal/cache/cache.go | Updates cache interface to generic Fetch/Store/Evict/ForEach/Size. |
| go.sum | Updates dependency checksums and adds new deps. |
| go.mod | Adds new dependencies (lo, water, x/net, gvisor) and bumps x/* deps. |
| docs/user-guide/udp.md | Adds UDP configuration documentation. |
| docs/user-guide/server.md | Removes legacy server options documentation (superseded by app/connection). |
| docs/user-guide/policy.md | Removes policy.auto docs and reframes template behavior. |
| docs/user-guide/overview.md | Updates overview to reflect new option sections and examples. |
| docs/user-guide/https.md | Updates HTTPS docs (chunk default, fake-packet format, custom segments). |
| docs/user-guide/connection.md | Adds connection (timeouts/default-fake-ttl) documentation. |
| docs/user-guide/app.md | Replaces “General” docs with “App” docs (mode/network-config/etc.). |
| cmd/spoofdpi/main_test.go | Updates main tests for new config schema and server creation. |
| // Configure HTTP/2 transport explicitly | ||
| if err := http2.ConfigureTransport(tr); err != nil { | ||
| // Log error instead of panic if strict http2 is not required, otherwise panic | ||
| panic(fmt.Sprintf("failed to configure http2: %v", err)) | ||
| } |
| done := make(chan struct{}) | ||
| go func() { | ||
| _, _ = io.Copy(io.Discard, lConn) // Block until TCP closes | ||
| close(done) | ||
| }() |
| // Initial Client Identification | ||
| if clientAddr == nil { | ||
| clientAddr = addr | ||
| } | ||
|
|
||
| // Only accept packets from the client that established the association | ||
| if !addr.IP.Equal(clientAddr.IP) || addr.Port != clientAddr.Port { | ||
| continue | ||
| } |
internal/packet/udp_writer.go
Outdated
| eth := &layers.Ethernet{ | ||
| SrcMAC: srcMAC, | ||
| DstMAC: dstMAC, | ||
| EthernetType: layers.EthernetTypeIPv6, | ||
| } | ||
| packetLayers = append(packetLayers, eth) | ||
|
|
internal/packet/tcp_writer.go
Outdated
| if srcMAC != nil { | ||
| eth := &layers.Ethernet{ | ||
| SrcMAC: srcMAC, | ||
| DstMAC: dstMAC, | ||
| EthernetType: layers.EthernetTypeIPv4, | ||
| } | ||
| packetLayers = append(packetLayers, eth) | ||
| } |
internal/server/socks5/server.go
Outdated
| func (p *SOCKS5Proxy) Stop() error { | ||
| if p.listener != nil { | ||
| return p.listener.Close() | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Pull request overview
This PR expands SpoofDPI to support additional operating modes (SOCKS5 and TUN), along with related networking/routing setup and documentation updates.
Changes:
- Add TUN-mode TCP/UDP handlers and OS-specific route/interface configuration helpers.
- Update session trace ID generation/handling.
- Update docs navigation to reflect the new configuration sections/pages.
Reviewed changes
Copilot reviewed 90 out of 91 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| mkdocs.yml | Updates the documentation nav to new “App/Connection/UDP” sections. |
| internal/system/sysproxy.go | Removes legacy non-darwin/non-linux system proxy stub. |
| internal/system/sysproxy_linux.go | Removes legacy Linux system proxy stub implementation. |
| internal/system/sysproxy_darwin.go | Removes legacy macOS system proxy implementation. |
| internal/session/session.go | Changes trace ID generation and default trace ID fallback behavior. |
| internal/server/tun/tcp.go | Introduces TUN TCP handler with TLS SNI extraction + rule matching + tunneling. |
| internal/server/tun/udp.go | Introduces TUN UDP handler with rule application + tunneling. |
| internal/server/tun/network.go | Adds no-op route/interface helpers for unsupported OS builds. |
| internal/server/tun/network_linux.go | Adds Linux routing/policy-routing and interface address configuration via ip. |
| internal/server/tun/network_bsd.go | Adds darwin/freebsd route/interface configuration helpers. |
| internal/cache/lru_cache.go | Updates LRU cache behavior used by connection/session caching. |
| internal/netutil/session_cache.go | Updates session cache storage/eviction behavior for connection reuse. |
| internal/server/socks5/udp_associate.go | Adds/updates SOCKS5 UDP ASSOCIATE handling. |
| internal/packet/tcp_writer.go | Adjusts crafted TCP packet layer building (Ethernet encapsulation logic). |
Comments suppressed due to low confidence (1)
internal/cache/lru_cache.go:114
- LRUCache.Store overwrites an existing entry's value without calling onInvalidate for the old value. This can leak resources when values own file descriptors/sockets (e.g., SessionCache stores *IdleTimeoutConn and relies on onInvalidate to close underlying conns). Consider capturing the old value and invoking onInvalidate (or explicitly evicting first) before replacing entry.value.
if ok {
entry := element.Value.(*lruEntry[K])
entry.value = value
c.list.MoveToFront(element)
return true
LRUCache previously overwrote existing values without notification when storing a duplicate key. To prevent potential resource leaks, the onInvalidate callback is now invoked prior to storing the new value, ensuring the previous entry is properly cleaned up.
There was a problem hiding this comment.
Pull request overview
This PR adds new operating modes (SOCKS5 proxy and TUN transparent mode) and refactors the runtime/config/networking layers to support TCP+UDP handling, per-mode system network configuration, and UDP desync/packet-crafting capabilities.
Changes:
- Introduces
server.Serverabstraction and adds SOCKS5 + TUN servers/handlers (including UDP tunneling/desync). - Refactors configuration schema into
app,connection, andudpsections; updates CLI parsing and docs accordingly. - Extends packet/netutil/cache infrastructure (routing helpers, conn registry, generic caches, PAC server support).
Reviewed changes
Copilot reviewed 88 out of 89 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| mkdocs.yml | Updates docs navigation for new config sections. |
| internal/system/sysproxy.go | Removes prior system proxy implementation (moved to per-server network config). |
| internal/system/sysproxy_linux.go | Removes Linux no-op/placeholder system proxy implementation. |
| internal/system/sysproxy_darwin.go | Removes previous macOS system proxy implementation (replaced by PAC-based per-mode setup). |
| internal/session/session.go | Removes unsafe trace-id generation and adjusts trace-id fallback behavior. |
| internal/server/tun/udp.go | Adds UDP handler for TUN mode with rule merge + idle timeout + tunneling. |
| internal/server/tun/tcp.go | Adds TCP/TLS handler for TUN mode (SNI extraction + rule matching + desync + tunneling). |
| internal/server/tun/network.go | Adds unsupported-platform stubs for TUN route/interface operations. |
| internal/server/tun/network_linux.go | Implements Linux route/interface/policy-routing helpers for TUN. |
| internal/server/tun/network_bsd.go | Implements darwin/freebsd route/interface helpers for TUN. |
| internal/server/socks5/server.go | Adds SOCKS5 server with CONNECT/BIND/UDP_ASSOCIATE support and rule matching. |
| internal/server/socks5/network.go | Adds non-darwin no-op system proxy setup for SOCKS5 mode. |
| internal/server/socks5/network_darwin.go | Adds PAC-based system proxy setup for SOCKS5 mode on macOS. |
| internal/server/socks5/connect.go | Implements SOCKS5 CONNECT handling (TLS detection + desync + tunneling). |
| internal/server/socks5/bind.go | Implements SOCKS5 BIND handling with bidirectional tunneling. |
| internal/server/server.go | Introduces shared server.Server interface (Listen/SetNetworkConfig/etc.). |
| internal/server/http/server.go | Refactors HTTP proxy to implement server.Server and to use new config types. |
| internal/server/http/network.go | Adds non-darwin no-op system proxy setup for HTTP mode. |
| internal/server/http/network_darwin.go | Adds PAC-based system proxy setup for HTTP mode on macOS. |
| internal/server/http/https.go | Refactors HTTPS CONNECT handling + tunnel aggregation + uses new conn options. |
| internal/server/http/http.go | Refactors plain HTTP tunneling to new tunnel result aggregation. |
| internal/proxy/socks5/socks5_proxy.go | Removes old SOCKS5 proxy implementation (replaced by internal/server/socks5). |
| internal/proxy/proxy.go | Removes old proxy interface (superseded by internal/server/server.go). |
| internal/proto/socks5.go | Updates SOCKS5 constants/struct layout and parsing fields (ATYP/FQDN/etc.). |
| internal/proto/http.go | Renames ExtractDomain to ExtractHost. |
| internal/packet/udp_writer.go | Adds UDP packet crafting/injection support. |
| internal/packet/udp_sniffer.go | Adds UDP TTL/hop-count sniffer + BPF filter generation. |
| internal/packet/tcp_writer.go | Makes Ethernet header optional depending on link type/MAC availability. |
| internal/packet/sniffer.go | Updates sniffer interface/types and adds hop-estimation helpers. |
| internal/packet/network_detector.go | Improves gateway/interface detection and adds periodic probing. |
| internal/packet/LICENSE | Removes embedded license file from packet directory. |
| internal/packet/handle_linux.go | Improves Linux link-type detection (Ethernet vs Raw vs SLL). |
| internal/netutil/route.go | Adds route utilities (safe subnet + default iface/gateway discovery). |
| internal/netutil/route_unsupported.go | Adds unsupported-platform stubs for routing helpers. |
| internal/netutil/route_linux.go | Adds Linux gateway parsing + interface binding support. |
| internal/netutil/route_bsd.go | Adds BSD/macOS gateway parsing + IP_BOUND_IF binding support. |
| internal/netutil/pac.go | Adds PAC server helper used by macOS system proxy setup. |
| internal/netutil/netutil.go | Adds helper for IPv4-mapped IPv6 detection. |
| internal/netutil/key.go | Adds zero-allocation cache keys (NATKey/IPKey). |
| internal/netutil/dst.go | Introduces Destination struct and updates destination validation types. |
| internal/netutil/dial.go | Refactors dialing to accept *Destination and support interface binding. |
| internal/netutil/conn.go | Refactors tunnel copy + introduces aggregated transfer results and idle-timeout conn. |
| internal/netutil/conn_registry.go | Adds LRU+idle-timeout UDP connection registry/pool. |
| internal/matcher/matcher.go | Switches pointer helpers to lo and keeps priority comparison logic. |
| internal/matcher/matcher_test.go | Updates tests for lo pointer helpers. |
| internal/matcher/domain.go | Updates defaults to lo pointer helpers. |
| internal/matcher/domain_test.go | Updates tests for lo pointer helpers. |
| internal/matcher/addr.go | Updates defaults to lo pointer helpers. |
| internal/matcher/addr_test.go | Updates tests for lo pointer helpers. |
| internal/logging/logging.go | Adjusts trace-id logging behavior and renames unwrapped-error helpers. |
| internal/dns/udp.go | Wires DNS timeouts via connection options. |
| internal/dns/system.go | Refactors system resolver to use LookupIP and []net.IP. |
| internal/dns/route.go | Refactors routing resolver to new DNS/IP types and adds timing logs. |
| internal/dns/resolver.go | Switches DNS record sets to []net.IP and joins errors. |
| internal/dns/https.go | Refactors DoH transport (timeouts/http2) and changes request style + retry logic. |
| internal/dns/cache.go | Migrates DNS cache to generic cache interface and updates logging/ops. |
| internal/dns/addrselect/addrselect.go | Migrates address selection helpers to []net.IP. |
| internal/desync/udp.go | Adds UDP desync logic (fake packet injection) using UDP sniffer/writer. |
| internal/config/validate.go | Updates validation enums for new config schema. |
| internal/config/validate_test.go | Updates validation tests and adds log-level tests. |
| internal/config/toml.go | Switches pointer helper to lo. |
| internal/config/toml_test.go | Updates TOML schema tests for new config sections/options. |
| internal/config/segment_test.go | Adds placeholder test file for segment-related package. |
| internal/config/parse.go | Adds parsing for new enums (app mode, custom split mode, segment-from). |
| internal/config/config.go | Refactors config structure (App/Conn/UDP) and PCAP enablement logic. |
| internal/config/config_test.go | Updates tests for new config schema/UDP defaults. |
| internal/config/cli_test.go | Updates CLI tests for new flags/config layout. |
| internal/cache/ttl_cache.go | Makes TTL cache generic and expands cache interface support. |
| internal/cache/lru_cache.go | Makes LRU cache generic, adds eviction hooks and richer interface methods. |
| internal/cache/cache.go | Updates cache interface to be generic and adds iteration/eviction APIs. |
| go.sum | Updates dependency checksums for added/updated modules. |
| go.mod | Adds new dependencies for new modes/features (lo, water, x/net, gvisor, etc.). |
| docs/user-guide/udp.md | Adds UDP configuration documentation. |
| docs/user-guide/server.md | Removes old server configuration doc (replaced by app/connection docs). |
| docs/user-guide/policy.md | Updates policy docs to reflect template-only approach. |
| docs/user-guide/overview.md | Updates docs overview for new config categories and examples. |
| docs/user-guide/https.md | Documents new defaults and custom segment plans. |
| docs/user-guide/connection.md | Adds connection timeouts + default-fake-ttl documentation. |
| docs/user-guide/app.md | Replaces general config doc with app-mode/listen-addr/network auto-config docs. |
| cmd/spoofdpi/main_test.go | Updates main tests for new config schema and server creation. |
| pacURL, l, err := netutil.RunPACServer(pacContent) | ||
| if err != nil { | ||
| return fmt.Errorf("error creating pac server: %w", err) | ||
| } | ||
| pacListener = l |
There was a problem hiding this comment.
SetSystemProxy assigns to the package-level pacListener without closing any existing listener first. If SetSystemProxy is invoked more than once in-process, the previous PAC server listener can be leaked. Consider closing an existing pacListener (if non-nil) before starting a new PAC server, or make the operation idempotent.
internal/server/socks5/connect.go
Outdated
| // 3. Send Success Response | ||
| err = proto.SOCKS5SuccessResponse().Bind(net.IPv4zero).Port(0).Write(lConn) | ||
| if err != nil { | ||
| logger.Error().Err(err).Msg("failed to write socks5 success reply") | ||
| return err | ||
| } | ||
|
|
||
| // logger := logging.WithLocalScope(ctx, h.logger, "connect(tcp)") | ||
| dst.Timeout = *connOpts.TCPTimeout | ||
|
|
||
| rConn, err := netutil.DialFastest(ctx, "tcp", dst) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer netutil.CloseConns(rConn) | ||
|
|
There was a problem hiding this comment.
SOCKS5 CONNECT sends a success reply to the client before confirming the upstream TCP dial succeeded. If DialFastest fails, the client has already received a success response and may hang or mis-handle the connection. Consider dialing first (or at least verifying dst.Addrs is non-empty and the dial is likely to succeed), then send SOCKS5SuccessResponse only after the upstream connection is established; otherwise send a failure response.
internal/netutil/pac.go
Outdated
| server := &http.Server{ | ||
| Handler: mux, | ||
| } | ||
|
|
||
| go func() { | ||
| _ = server.Serve(listener) | ||
| }() | ||
|
|
||
| addr := listener.Addr().(*net.TCPAddr) | ||
| url := fmt.Sprintf("http://127.0.0.1:%d/proxy.pac", addr.Port) | ||
|
|
||
| return url, listener, nil |
There was a problem hiding this comment.
RunPACServer starts an http.Server in a goroutine but only returns the net.Listener to callers. Closing the listener does not guarantee active connections and the Serve goroutine are fully terminated, which can leak goroutines/resources across repeated enable/disable cycles. Consider returning a cleanup function (or the *http.Server) so callers can invoke server.Close()/Shutdown in addition to closing the listener.
| go func(rConn *netutil.IdleTimeoutConn, lAddr *net.UDPAddr, remoteAddr *net.UDPAddr) { | ||
| respBuf := make([]byte, 65535) | ||
| for { | ||
| // Read via IdleTimeoutConn so each inbound packet extends the deadline. | ||
| n, err := rConn.Read(respBuf) | ||
| if err != nil { | ||
| // Connection closed or network issues | ||
| return | ||
| } | ||
|
|
||
| // Inbound: Target -> Proxy -> Client | ||
| // Wrap with SOCKS5 Header | ||
| header := createUDPHeaderFromAddr(remoteAddr) | ||
| response := append(header, respBuf[:n]...) | ||
|
|
||
| if _, err := lUDPConn.WriteToUDP(response, lAddr); err != nil { | ||
| // If we can't write back to the client, it might be gone or network issue. | ||
| // Exit this goroutine to avoid busy looping. | ||
| logger.Warn().Err(err).Msg("failed to write udp to client") | ||
| return | ||
| } | ||
| } |
There was a problem hiding this comment.
UDP ASSOCIATE spawns a per-session goroutine that returns on read error without closing/evicting the cached connection. This can leave dead connections in the pool until periodic cleanup, and repeated failures can accumulate idle entries. Consider explicitly closing the IdleTimeoutConn (or evicting the NAT key) when the read loop exits due to error to release resources promptly.
No description provided.