This commit is contained in:
2026-07-04 20:24:20 -03:00
parent 4866f0cf10
commit ea15f1bfa1
10 changed files with 1059 additions and 190 deletions
+300
View File
@@ -115,6 +115,66 @@ func TestVLESSOverTCP(t *testing.T) {
}
}
func vlessDNSSinkHeader(id [16]byte, cmd byte, host net.IP, port uint16) []byte {
var b bytes.Buffer
b.WriteByte(0)
b.Write(id[:])
b.WriteByte(0)
b.WriteByte(cmd)
b.WriteByte(byte(port >> 8))
b.WriteByte(byte(port))
if ip4 := host.To4(); ip4 != nil {
b.WriteByte(atypIPv4)
b.Write(ip4)
} else {
b.WriteByte(atypIPv6)
b.Write(host.To16())
}
return b.Bytes()
}
func TestVLESSDNSSinkTargetFastIgnored(t *testing.T) {
_, port, id, stop := newTestInbound(t, "tcp", "")
defer stop()
conn, err := net.Dial("tcp", net.JoinHostPort("127.0.0.1", itoa(port)))
if err != nil {
t.Fatalf("dial inbound: %v", err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(2 * time.Second))
if _, err := conn.Write(vlessDNSSinkHeader(id, vlessCmdTCP, net.IPv4(0, 0, 0, 0), 0)); err != nil {
t.Fatalf("write sink header: %v", err)
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
t.Fatalf("read VLESS sink response: %v", err)
}
if resp[0] != 0 || resp[1] != 0 {
t.Fatalf("bad VLESS sink response: %v", resp)
}
buf := make([]byte, 1)
if _, err := conn.Read(buf); err == nil {
t.Fatalf("sink target should close immediately after response")
}
}
func TestNativeDNSSinkTargetDetection(t *testing.T) {
for _, host := range []string{"0.0.0.0", "::", "[::]", "::%lo"} {
if !isNativeDNSSinkTarget(host) {
t.Fatalf("%q should be detected as DNS sink", host)
}
}
for _, host := range []string{"", "127.0.0.1", "1.1.1.1", "example.com"} {
if isNativeDNSSinkTarget(host) {
t.Fatalf("%q should not be detected as DNS sink", host)
}
}
}
func TestVLESSRejectsUnknownUUID(t *testing.T) {
echoPort, stopEcho := startEchoServer(t)
defer stopEcho()
@@ -578,3 +638,243 @@ func TestNativeLocalAddrForIPv6Tunnel(t *testing.T) {
t.Fatalf("must not bind IPv4 source to IPv6 target: %#v", local)
}
}
func startUDPEchoServer(t *testing.T) (int, func()) {
t.Helper()
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatalf("udp echo listen: %v", err)
}
go func() {
buf := make([]byte, 64*1024)
for {
n, addr, err := pc.ReadFrom(buf)
if err != nil {
return
}
_, _ = pc.WriteTo(buf[:n], addr)
}
}()
return pc.LocalAddr().(*net.UDPAddr).Port, func() { pc.Close() }
}
func vlessMuxHeader(id [16]byte) []byte {
var b bytes.Buffer
b.WriteByte(0)
b.Write(id[:])
b.WriteByte(0)
b.WriteByte(vlessCmdMux)
return b.Bytes()
}
func buildMuxUDPFrame(sessionID uint16, host string, port int, payload []byte) []byte {
meta := []byte{byte(sessionID >> 8), byte(sessionID), nativeMuxStatusNew, nativeMuxOptionData, nativeMuxNetworkUDP}
meta = appendNativeMuxAddressPort(meta, host, uint16(port))
var out bytes.Buffer
binary.Write(&out, binary.BigEndian, uint16(len(meta)))
out.Write(meta)
binary.Write(&out, binary.BigEndian, uint16(len(payload)))
out.Write(payload)
return out.Bytes()
}
func buildMuxTCPFrame(sessionID uint16, host string, port int, payload []byte) []byte {
meta := []byte{byte(sessionID >> 8), byte(sessionID), nativeMuxStatusNew, nativeMuxOptionData, nativeMuxNetworkTCP}
meta = appendNativeMuxAddressPort(meta, host, uint16(port))
var out bytes.Buffer
binary.Write(&out, binary.BigEndian, uint16(len(meta)))
out.Write(meta)
binary.Write(&out, binary.BigEndian, uint16(len(payload)))
out.Write(payload)
return out.Bytes()
}
func buildMuxXUDPFrame(sessionID uint16, host string, port int, payload []byte, gid [8]byte) []byte {
meta := []byte{byte(sessionID >> 8), byte(sessionID), nativeMuxStatusNew, nativeMuxOptionData, nativeMuxNetworkUDP}
meta = appendNativeMuxAddressPort(meta, host, uint16(port))
meta = append(meta, gid[:]...)
var out bytes.Buffer
binary.Write(&out, binary.BigEndian, uint16(len(meta)))
out.Write(meta)
// Official Mux.Cool/XUDP carries GlobalID in the outer New metadata. The
// following packet block is the UDP datagram itself, not another XUDP metadata
// stream.
binary.Write(&out, binary.BigEndian, uint16(len(payload)))
out.Write(payload)
return out.Bytes()
}
func TestVLESSMuxUDPDoesNotStall(t *testing.T) {
udpPort, stopUDP := startUDPEchoServer(t)
defer stopUDP()
_, port, id, stop := newTestInbound(t, "tcp", "")
defer stop()
conn, err := net.Dial("tcp", net.JoinHostPort("127.0.0.1", itoa(port)))
if err != nil {
t.Fatalf("dial inbound: %v", err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
if _, err := conn.Write(vlessMuxHeader(id)); err != nil {
t.Fatalf("write mux header: %v", err)
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
t.Fatalf("read mux response header: %v", err)
}
if resp[0] != 0 || resp[1] != 0 {
t.Fatalf("bad mux vless response: %v", resp)
}
want := []byte("quic-over-mux")
if _, err := conn.Write(buildMuxUDPFrame(7, "127.0.0.1", udpPort, want)); err != nil {
t.Fatalf("write mux udp frame: %v", err)
}
meta, err := readNativeMuxMetadata(conn)
if err != nil {
t.Fatalf("read mux response meta: %v", err)
}
if meta.sessionID != 7 || meta.status != nativeMuxStatusKeep || meta.option&nativeMuxOptionData == 0 {
t.Fatalf("bad mux response metadata: %#v", meta)
}
got, err := readNativeMuxDataBlock(conn)
if err != nil {
t.Fatalf("read mux response payload: %v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("mux udp echo mismatch: got %q want %q", got, want)
}
}
func TestVLESSMuxXUDPDoesNotStall(t *testing.T) {
udpPort, stopUDP := startUDPEchoServer(t)
defer stopUDP()
_, port, id, stop := newTestInbound(t, "tcp", "")
defer stop()
conn, err := net.Dial("tcp", net.JoinHostPort("127.0.0.1", itoa(port)))
if err != nil {
t.Fatalf("dial inbound: %v", err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
if _, err := conn.Write(vlessMuxHeader(id)); err != nil {
t.Fatalf("write mux header: %v", err)
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
t.Fatalf("read mux response header: %v", err)
}
want := []byte("quic-over-xudp")
gid := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
if _, err := conn.Write(buildMuxXUDPFrame(9, "127.0.0.1", udpPort, want, gid)); err != nil {
t.Fatalf("write mux xudp frame: %v", err)
}
meta, err := readNativeMuxMetadata(conn)
if err != nil {
t.Fatalf("read mux xudp response meta: %v", err)
}
if meta.sessionID != 9 || meta.status != nativeMuxStatusKeep || meta.option&nativeMuxOptionData == 0 {
t.Fatalf("bad mux xudp response metadata: %#v", meta)
}
if meta.host != "127.0.0.1" || int(meta.port) != udpPort {
t.Fatalf("xudp response did not preserve UDP endpoint: %#v", meta)
}
got, err := readNativeMuxDataBlock(conn)
if err != nil {
t.Fatalf("read xudp response payload: %v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("mux xudp echo mismatch: got %q want %q", got, want)
}
}
func TestVLESSMuxXUDPPayloadLookingLikeMetadataDoesNotStall(t *testing.T) {
udpPort, stopUDP := startUDPEchoServer(t)
defer stopUDP()
_, port, id, stop := newTestInbound(t, "tcp", "")
defer stop()
conn, err := net.Dial("tcp", net.JoinHostPort("127.0.0.1", itoa(port)))
if err != nil {
t.Fatalf("dial inbound: %v", err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
if _, err := conn.Write(vlessMuxHeader(id)); err != nil {
t.Fatalf("write mux header: %v", err)
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
t.Fatalf("read mux response header: %v", err)
}
// This payload intentionally looks like old inner-XUDP metadata. Mux.Cool
// packet mode must still forward it as one UDP datagram and must not block
// waiting for another fake payload block.
want := []byte{0, 0, 2, 1, 'q', 'u', 'i', 'c'}
gid := [8]byte{8, 7, 6, 5, 4, 3, 2, 1}
if _, err := conn.Write(buildMuxXUDPFrame(10, "127.0.0.1", udpPort, want, gid)); err != nil {
t.Fatalf("write mux xudp frame: %v", err)
}
meta, err := readNativeMuxMetadata(conn)
if err != nil {
t.Fatalf("read mux xudp response meta: %v", err)
}
if meta.sessionID != 10 || meta.status != nativeMuxStatusKeep || meta.option&nativeMuxOptionData == 0 {
t.Fatalf("bad mux xudp response metadata: %#v", meta)
}
got, err := readNativeMuxDataBlock(conn)
if err != nil {
t.Fatalf("read xudp response payload: %v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("mux xudp metadata-looking payload changed: got %q want %q", got, want)
}
}
func TestVLESSMuxTCPDoesNotStall(t *testing.T) {
tcpPort, stopTCP := startEchoServer(t)
defer stopTCP()
_, port, id, stop := newTestInbound(t, "tcp", "")
defer stop()
conn, err := net.Dial("tcp", net.JoinHostPort("127.0.0.1", itoa(port)))
if err != nil {
t.Fatalf("dial inbound: %v", err)
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
if _, err := conn.Write(vlessMuxHeader(id)); err != nil {
t.Fatalf("write mux header: %v", err)
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
t.Fatalf("read mux response header: %v", err)
}
want := []byte("tcp-over-mux")
if _, err := conn.Write(buildMuxTCPFrame(11, "127.0.0.1", tcpPort, want)); err != nil {
t.Fatalf("write mux tcp frame: %v", err)
}
meta, err := readNativeMuxMetadata(conn)
if err != nil {
t.Fatalf("read mux tcp response meta: %v", err)
}
if meta.sessionID != 11 || meta.status != nativeMuxStatusKeep || meta.option&nativeMuxOptionData == 0 {
t.Fatalf("bad mux tcp response metadata: %#v", meta)
}
got, err := readNativeMuxDataBlock(conn)
if err != nil {
t.Fatalf("read mux tcp response payload: %v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("mux tcp echo mismatch: got %q want %q", got, want)
}
}