Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
)
26 changes: 16 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
8 changes: 4 additions & 4 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
43 changes: 28 additions & 15 deletions tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,44 +59,57 @@ 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()
return 0, fmt.Errorf("TUN closed")
}
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()
Expand Down
33 changes: 22 additions & 11 deletions tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]]))
}
}

Expand All @@ -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
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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 {
Expand Down
Loading