~bigbes/shroud

ref: 3804bcf465a4eb00d1f03720316d0a176b0b97fb shroud/internal/vless/protocol.go -rw-r--r-- 3.0 KiB
3804bcf4 — Eugene Blikh feat(vless): add VLESS+REALITY transport support 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
}