package vless
import (
"encoding/binary"
"fmt"
"io"
"net"
)
const (
Version = 0
CmdTCP byte = 0x01
CmdUDP byte = 0x02
AddrIPv4 byte = 0x01
AddrDomain byte = 0x02
AddrIPv6 byte = 0x03
)
// Request represents a parsed VLESS request header.
type Request struct {
UUID [16]byte
Command byte
Port uint16
Address string
Addons []byte
}
// Target returns the address:port string for dialing.
func (r *Request) Target() string {
return net.JoinHostPort(r.Address, fmt.Sprintf("%d", r.Port))
}
// ParseRequest reads a VLESS request header from r.
func ParseRequest(r io.Reader) (*Request, error) {
// Version (1 byte).
var ver [1]byte
if _, err := io.ReadFull(r, ver[:]); err != nil {
return nil, fmt.Errorf("reading version: %w", err)
}
if ver[0] != Version {
return nil, fmt.Errorf("unsupported VLESS version: %d", ver[0])
}
// UUID (16 bytes).
var req Request
if _, err := io.ReadFull(r, req.UUID[:]); err != nil {
return nil, fmt.Errorf("reading UUID: %w", err)
}
// Addons length (1 byte) + addons.
var addonsLen [1]byte
if _, err := io.ReadFull(r, addonsLen[:]); err != nil {
return nil, fmt.Errorf("reading addons length: %w", err)
}
if addonsLen[0] > 0 {
req.Addons = make([]byte, addonsLen[0])
if _, err := io.ReadFull(r, req.Addons); err != nil {
return nil, fmt.Errorf("reading addons: %w", err)
}
}
// Command (1 byte).
var cmd [1]byte
if _, err := io.ReadFull(r, cmd[:]); err != nil {
return nil, fmt.Errorf("reading command: %w", err)
}
req.Command = cmd[0]
if req.Command != CmdTCP && req.Command != CmdUDP {
return nil, fmt.Errorf("unsupported command: 0x%02x", req.Command)
}
// Port (2 bytes, big-endian).
var portBuf [2]byte
if _, err := io.ReadFull(r, portBuf[:]); err != nil {
return nil, fmt.Errorf("reading port: %w", err)
}
req.Port = binary.BigEndian.Uint16(portBuf[:])
// Address type (1 byte) + address.
var addrType [1]byte
if _, err := io.ReadFull(r, addrType[:]); err != nil {
return nil, fmt.Errorf("reading address type: %w", err)
}
switch addrType[0] {
case AddrIPv4:
var ip [4]byte
if _, err := io.ReadFull(r, ip[:]); err != nil {
return nil, fmt.Errorf("reading IPv4 address: %w", err)
}
req.Address = net.IP(ip[:]).String()
case AddrDomain:
var domainLen [1]byte
if _, err := io.ReadFull(r, domainLen[:]); err != nil {
return nil, fmt.Errorf("reading domain length: %w", err)
}
domain := make([]byte, domainLen[0])
if _, err := io.ReadFull(r, domain); err != nil {
return nil, fmt.Errorf("reading domain: %w", err)
}
req.Address = string(domain)
case AddrIPv6:
var ip [16]byte
if _, err := io.ReadFull(r, ip[:]); err != nil {
return nil, fmt.Errorf("reading IPv6 address: %w", err)
}
req.Address = net.IP(ip[:]).String()
default:
return nil, fmt.Errorf("unsupported address type: 0x%02x", addrType[0])
}
return &req, nil
}
// WriteResponse writes the VLESS response header (version 0, no addons).
func WriteResponse(w io.Writer) error {
_, err := w.Write([]byte{Version, 0x00})
return err
}