diff --git a/go.mod b/go.mod index a9f898a..28ae09e 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ module github.com/puzed/wrapguard -go 1.23.0 +go 1.24.0 toolchain go1.24.2 require ( github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 + golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb ) require ( - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) diff --git a/go.sum b/go.sum index ac04e80..b1889a2 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,18 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= +golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= +gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c h1:m/r7OM+Y2Ty1sgBQ7Qb27VgIMBW8ZZhT4gLnUyDIhzI= +gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= diff --git a/logger_test.go b/logger_test.go index a2cfb56..c74ae88 100644 --- a/logger_test.go +++ b/logger_test.go @@ -122,7 +122,7 @@ func TestLogger_Log(t *testing.T) { var buf bytes.Buffer logger := NewLogger(tt.loggerLevel, &buf) - logger.log(tt.logLevel, tt.message) + logger.log(tt.logLevel, "%s", tt.message) output := buf.String() if tt.shouldOutput { @@ -226,9 +226,9 @@ func TestLogger_JSONMarshaling(t *testing.T) { logger := NewLogger(LogLevelInfo, &buf) // Test with special characters that need JSON escaping - message := `test "quoted" message with \backslash and + message := `test "quoted" message with \backslash and newline` - logger.Infof(message) + logger.Infof("%s", message) output := buf.String() var entry LogEntry @@ -378,7 +378,7 @@ func TestLogger_LongMessage(t *testing.T) { // Create a very long message longMessage := strings.Repeat("a", 10000) - logger.Infof(longMessage) + logger.Infof("%s", longMessage) output := buf.String() var entry LogEntry diff --git a/tunnel.go b/tunnel.go index 2c04230..0cf6408 100644 --- a/tunnel.go +++ b/tunnel.go @@ -59,16 +59,22 @@ func NewMemoryTUN(name string, mtu int) *MemoryTUN { func (m *MemoryTUN) File() *os.File { return nil } -func (m *MemoryTUN) Read(buf []byte, offset int) (int, error) { +func (m *MemoryTUN) Read(bufs [][]byte, sizes []int, offset int) (int, error) { packet, ok := <-m.inbound if !ok { return 0, fmt.Errorf("TUN closed") } - copy(buf[offset:], packet) - return len(packet), nil + + // Read a single packet into the first buffer + if len(bufs) > 0 { + n := copy(bufs[0][offset:], packet) + sizes[0] = n + return 1, nil + } + return 0, nil } -func (m *MemoryTUN) Write(buf []byte, offset int) (int, error) { +func (m *MemoryTUN) Write(bufs [][]byte, offset int) (int, error) { m.mutex.RLock() if m.closed { m.mutex.RUnlock() @@ -76,27 +82,34 @@ func (m *MemoryTUN) Write(buf []byte, offset int) (int, error) { } m.mutex.RUnlock() - packet := make([]byte, len(buf)-offset) - copy(packet, buf[offset:]) + // Write all packets in the batch + written := 0 + for _, buf := range bufs { + packet := make([]byte, len(buf)-offset) + copy(packet, buf[offset:]) - // Handle incoming packets from WireGuard - if m.tunnel != nil { - go m.tunnel.handleIncomingPacket(packet) - } + // Handle incoming packets from WireGuard + if m.tunnel != nil { + go m.tunnel.handleIncomingPacket(packet) + } - select { - case m.outbound <- packet: - default: - // Drop if full + select { + case m.outbound <- packet: + written++ + default: + // Drop if full + break + } } - return len(packet), nil + return written, nil } func (m *MemoryTUN) Flush() error { return nil } func (m *MemoryTUN) MTU() (int, error) { return m.mtu, nil } func (m *MemoryTUN) Name() (string, error) { return m.name, nil } func (m *MemoryTUN) Events() <-chan tun.Event { return m.events } +func (m *MemoryTUN) BatchSize() int { return 1 } func (m *MemoryTUN) Close() error { m.mutex.Lock() diff --git a/tunnel_test.go b/tunnel_test.go index 8fc1fbc..7097d27 100644 --- a/tunnel_test.go +++ b/tunnel_test.go @@ -103,17 +103,23 @@ func TestMemoryTUN_ReadWrite(t *testing.T) { // Read data buf := make([]byte, 1500) - n, err := tun.Read(buf, 0) + bufs := [][]byte{buf} + sizes := make([]int, 1) + n, err := tun.Read(bufs, sizes, 0) if err != nil { t.Errorf("Read() returned error: %v", err) } - if n != len(testData) { - t.Errorf("expected to read %d bytes, got %d", len(testData), n) + if n != 1 { + t.Errorf("expected to read 1 packet, got %d", n) + } + + if sizes[0] != len(testData) { + t.Errorf("expected packet size %d bytes, got %d", len(testData), sizes[0]) } - if string(buf[:n]) != string(testData) { - t.Errorf("expected data %q, got %q", string(testData), string(buf[:n])) + if string(buf[:sizes[0]]) != string(testData) { + t.Errorf("expected data %q, got %q", string(testData), string(buf[:sizes[0]])) } } @@ -124,13 +130,14 @@ func TestMemoryTUN_WriteToOutbound(t *testing.T) { testData := []byte("outbound packet data") // Write to TUN (simulating WireGuard writing) - n, err := tun.Write(testData, 0) + bufs := [][]byte{testData} + n, err := tun.Write(bufs, 0) if err != nil { t.Errorf("Write() returned error: %v", err) } - if n != len(testData) { - t.Errorf("expected to write %d bytes, got %d", len(testData), n) + if n != 1 { + t.Errorf("expected to write 1 packet, got %d", n) } // Check if data appeared in outbound channel @@ -159,13 +166,15 @@ func TestMemoryTUN_Close(t *testing.T) { // Test that Read returns error after close buf := make([]byte, 100) - _, err = tun.Read(buf, 0) + bufs := [][]byte{buf} + sizes := make([]int, 1) + _, err = tun.Read(bufs, sizes, 0) if err == nil { t.Error("Read() should return error after close") } // Test that Write returns error after close - _, err = tun.Write([]byte("test"), 0) + _, err = tun.Write([][]byte{[]byte("test")}, 0) if err == nil { t.Error("Write() should return error after close") } @@ -248,7 +257,9 @@ func TestTunnel_DialWireGuard(t *testing.T) { router: NewRoutingEngine(config), } - ctx := context.Background() + // Use a timeout context to prevent hanging on connection attempts + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() // Test dialing known WireGuard IPs (fallback mode) tests := []struct {