package vless
import (
"bytes"
"encoding/binary"
"net"
"testing"
)
func buildRequest(t *testing.T, uuid [16]byte, addons []byte, cmd byte, port uint16, addrType byte, addr []byte) []byte {
t.Helper()
var buf bytes.Buffer
buf.WriteByte(Version) // version
buf.Write(uuid[:]) // UUID
if addons == nil {
buf.WriteByte(0) // addons length
} else {
buf.WriteByte(byte(len(addons)))
buf.Write(addons)
}
buf.WriteByte(cmd) // command
var portBuf [2]byte
binary.BigEndian.PutUint16(portBuf[:], port)
buf.Write(portBuf[:]) // port
buf.WriteByte(addrType) // address type
buf.Write(addr) // address
return buf.Bytes()
}
func TestParseRequest_TCPIPv4(t *testing.T) {
uuid := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
ip := net.ParseIP("192.168.1.1").To4()
data := buildRequest(t, uuid, nil, CmdTCP, 8080, AddrIPv4, ip)
req, err := ParseRequest(bytes.NewReader(data))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if req.UUID != uuid {
t.Errorf("UUID mismatch: got %v, want %v", req.UUID, uuid)
}
if req.Command != CmdTCP {
t.Errorf("Command mismatch: got %d, want %d", req.Command, CmdTCP)
}
if req.Port != 8080 {
t.Errorf("Port mismatch: got %d, want 8080", req.Port)
}
if req.Address != "192.168.1.1" {
t.Errorf("Address mismatch: got %q, want %q", req.Address, "192.168.1.1")
}
if req.Target() != "192.168.1.1:8080" {
t.Errorf("Target mismatch: got %q", req.Target())
}
}
func TestParseRequest_UDPDomain(t *testing.T) {
uuid := [16]byte{0xaa, 0xbb, 0xcc, 0xdd}
domain := "example.com"
addrBuf := append([]byte{byte(len(domain))}, []byte(domain)...)
data := buildRequest(t, uuid, nil, CmdUDP, 53, AddrDomain, addrBuf)
req, err := ParseRequest(bytes.NewReader(data))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if req.Command != CmdUDP {
t.Errorf("Command mismatch: got %d, want %d", req.Command, CmdUDP)
}
if req.Port != 53 {
t.Errorf("Port mismatch: got %d, want 53", req.Port)
}
if req.Address != "example.com" {
t.Errorf("Address mismatch: got %q, want %q", req.Address, "example.com")
}
}
func TestParseRequest_TCPIPv6(t *testing.T) {
uuid := [16]byte{0xff}
ip := net.ParseIP("2001:db8::1").To16()
data := buildRequest(t, uuid, nil, CmdTCP, 443, AddrIPv6, ip)
req, err := ParseRequest(bytes.NewReader(data))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if req.Address != "2001:db8::1" {
t.Errorf("Address mismatch: got %q, want %q", req.Address, "2001:db8::1")
}
}
func TestParseRequest_WithAddons(t *testing.T) {
uuid := [16]byte{0x01}
addons := []byte{0x0a, 0x0b, 0x0c}
ip := net.ParseIP("10.0.0.1").To4()
data := buildRequest(t, uuid, addons, CmdTCP, 80, AddrIPv4, ip)
req, err := ParseRequest(bytes.NewReader(data))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(req.Addons) != 3 {
t.Errorf("Addons length mismatch: got %d, want 3", len(req.Addons))
}
if req.Address != "10.0.0.1" {
t.Errorf("Address mismatch: got %q", req.Address)
}
}
func TestParseRequest_BadVersion(t *testing.T) {
data := []byte{0x01} // version 1, invalid
_, err := ParseRequest(bytes.NewReader(data))
if err == nil {
t.Fatal("expected error for bad version")
}
}
func TestParseRequest_Truncated(t *testing.T) {
// Only version + partial UUID.
data := []byte{0x00, 0x01, 0x02}
_, err := ParseRequest(bytes.NewReader(data))
if err == nil {
t.Fatal("expected error for truncated input")
}
}
func TestParseRequest_BadCommand(t *testing.T) {
uuid := [16]byte{}
ip := net.ParseIP("1.2.3.4").To4()
data := buildRequest(t, uuid, nil, 0x03, 80, AddrIPv4, ip) // cmd=Mux, unsupported
_, err := ParseRequest(bytes.NewReader(data))
if err == nil {
t.Fatal("expected error for unsupported command")
}
}
func TestParseRequest_BadAddrType(t *testing.T) {
uuid := [16]byte{}
// Build manually with bad address type.
var buf bytes.Buffer
buf.WriteByte(Version)
buf.Write(uuid[:])
buf.WriteByte(0) // no addons
buf.WriteByte(0x01) // TCP
var portBuf [2]byte
binary.BigEndian.PutUint16(portBuf[:], 80)
buf.Write(portBuf[:])
buf.WriteByte(0xFF) // bad address type
_, err := ParseRequest(bytes.NewReader(buf.Bytes()))
if err == nil {
t.Fatal("expected error for bad address type")
}
}
func TestWriteResponse(t *testing.T) {
var buf bytes.Buffer
if err := WriteResponse(&buf); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if buf.Len() != 2 {
t.Fatalf("response length mismatch: got %d, want 2", buf.Len())
}
if buf.Bytes()[0] != 0x00 || buf.Bytes()[1] != 0x00 {
t.Errorf("response bytes mismatch: got %v, want [0x00, 0x00]", buf.Bytes())
}
}