-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathrequest.go
More file actions
97 lines (90 loc) · 2.32 KB
/
Copy pathrequest.go
File metadata and controls
97 lines (90 loc) · 2.32 KB
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
package snell
import (
"io"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
type Request struct {
Command byte
ClientID []byte
Destination M.Socksaddr
}
func (r Request) Write(buffer *buf.Buffer) error {
if len(r.ClientID) > 255 {
return E.New("snell: client id too long")
}
_, err := buffer.Write([]byte{RequestVersion, r.Command, byte(len(r.ClientID))})
if err != nil {
return err
}
if len(r.ClientID) > 0 {
_, err = buffer.Write(r.ClientID)
if err != nil {
return err
}
}
switch r.Command {
case CommandConnect, CommandConnectV2:
return WriteConnectAddress(buffer, r.Destination)
case CommandPing, CommandUDP:
return nil
default:
return E.Extend(ErrUnsupportedCommand, r.Command)
}
}
func (r Request) Len() int {
requestLen := 3 + len(r.ClientID)
switch r.Command {
case CommandConnect, CommandConnectV2:
return requestLen + 1 + len(r.Destination.AddrString()) + 2
default:
return requestLen
}
}
func ReadRequest(reader io.Reader) (Request, error) {
prefix := make([]byte, 3)
_, err := io.ReadFull(reader, prefix)
if err != nil {
return Request{}, err
}
if prefix[0] != RequestVersion {
return Request{}, E.Extend(ErrBadVersion, "request version ", prefix[0])
}
clientIDLen := int(prefix[2])
request := Request{Command: prefix[1]}
if clientIDLen > 0 {
request.ClientID = make([]byte, clientIDLen)
_, err = io.ReadFull(reader, request.ClientID)
if err != nil {
return Request{}, err
}
}
switch request.Command {
case CommandConnect, CommandConnectV2:
request.Destination, err = ReadConnectAddress(reader)
if err != nil {
return Request{}, err
}
case CommandPing, CommandUDP:
default:
return Request{}, E.Extend(ErrUnsupportedCommand, request.Command)
}
return request, nil
}
func ReadServerError(record *buf.Buffer) error {
errorCode, err := record.ReadByte()
if err != nil {
return err
}
message, err := M.ReadSockString(record)
if err != nil {
return err
}
// Surge 6.7.0 (11520): SNConnectorV4::socket:didReadData: rejects server-error replies unless the frame is
// exactly code, message length, and message bytes.
if !record.IsEmpty() {
return E.New("snell: server error reply has trailing data")
}
return E.New("snell: server error ", errorCode, ": ", message)
}