Files
VoCat/internal/proxy/probe_test.go
T

121 lines
2.9 KiB
Go

package proxy
import (
"context"
"io"
"net"
"testing"
"time"
)
func TestProbeSOCKS5RequiresRealUDPExchange(t *testing.T) {
address, stop := startProbeSOCKS5Server(t, false)
defer stop()
result, err := probeSOCKS5(
context.Background(),
address,
"",
"",
250*time.Millisecond,
"192.0.2.53:53",
"example.test",
)
if err == nil {
t.Fatal("Probe unexpectedly succeeded when the relay dropped UDP data")
}
if !result.UDPAssociateOK {
t.Fatal("UDP ASSOCIATE should have succeeded")
}
if result.UDPExchangeOK {
t.Fatal("UDP exchange should not be reported as successful")
}
if result.Diagnosis != "udp_no_roundtrip" {
t.Fatalf("Diagnosis = %q, want udp_no_roundtrip", result.Diagnosis)
}
}
func TestProbeSOCKS5ReportsRealUDPDNSRoundTrip(t *testing.T) {
address, stop := startProbeSOCKS5Server(t, true)
defer stop()
result, err := probeSOCKS5(
context.Background(),
address,
"",
"",
time.Second,
"192.0.2.53:53",
"example.test",
)
if err != nil {
t.Fatalf("Probe returned error: %v", err)
}
if !result.HandshakeOK || !result.UDPAssociateOK || !result.UDPExchangeOK {
t.Fatalf("Probe evidence incomplete: %+v", result)
}
if result.Diagnosis != "ready" {
t.Fatalf("Diagnosis = %q, want ready", result.Diagnosis)
}
if result.DNSName != "example.test" || result.DNSServer != "192.0.2.53:53" {
t.Fatalf("Unexpected DNS evidence: %+v", result)
}
}
func startProbeSOCKS5Server(t *testing.T, echoDNS bool) (string, func()) {
t.Helper()
udpConnection, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
if err != nil {
t.Fatalf("ListenUDP: %v", err)
}
tcpListener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
udpConnection.Close()
t.Fatalf("Listen: %v", err)
}
if echoDNS {
go func() {
buffer := make([]byte, 2048)
count, sender, readErr := udpConnection.ReadFromUDP(buffer)
if readErr != nil || count < 22 {
return
}
// The test target is IPv4, so the SOCKS5 UDP header is ten bytes.
buffer[12] = 0x81
buffer[13] = 0x80
_, _ = udpConnection.WriteToUDP(buffer[:count], sender)
}()
}
go func() {
connection, acceptErr := tcpListener.Accept()
if acceptErr != nil {
return
}
defer connection.Close()
greeting := make([]byte, 3)
if _, readErr := io.ReadFull(connection, greeting); readErr != nil {
return
}
if _, writeErr := connection.Write([]byte{5, 0}); writeErr != nil {
return
}
associate := make([]byte, 10)
if _, readErr := io.ReadFull(connection, associate); readErr != nil {
return
}
udpPort := udpConnection.LocalAddr().(*net.UDPAddr).Port
response := []byte{5, 0, 0, 1, 127, 0, 0, 1, byte(udpPort >> 8), byte(udpPort)}
if _, writeErr := connection.Write(response); writeErr != nil {
return
}
_, _ = io.Copy(io.Discard, connection)
}()
return tcpListener.Addr().String(), func() {
_ = tcpListener.Close()
_ = udpConnection.Close()
}
}