mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-17 21:33:43 +08:00
121 lines
2.9 KiB
Go
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()
|
|
}
|
|
}
|