/
protocol.go
140 lines (129 loc) · 2.8 KB
/
protocol.go
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package socks
import (
"encoding/binary"
"io"
"net"
"strconv"
"github.com/pkg/errors"
)
const nextProto = "h3-23"
// client connect
// type can be 0x00(IPv4), 0x01(IPv6), 0x02(FQDN)
//
// host size = 4 (type = IPv4)
// host size = 16 (type = IPv6)
// host size = 1 + FQDN size (type = FQDN)
// +-----+-------+------+--------+
// | pwd | type | host | port |
// +-----+-------+------+--------+
// | var | uint8 | var | uint16 |
// +-----+-------+------+--------+
const (
typeSize = 1
fqdnSize = 1
portSize = 2
respSize = 1
)
const (
typeIPv4 uint8 = iota + 1
typeIPv6
typeFQDN
)
const (
respOK uint8 = iota + 1
respInvalidPWD
respInvalidHost
respConnectFailed
)
// type + host + port
func packHostData(host string, port uint16) ([]byte, error) {
var hostData []byte
ip := net.ParseIP(host)
if ip != nil {
ip4 := ip.To4()
if ip4 != nil { // IPv4
hostData = make([]byte, typeSize+net.IPv4len)
hostData[0] = typeIPv4
copy(hostData[typeSize:], ip4)
} else { // IPv6
ip6 := ip.To16()
if ip6 != nil {
hostData = make([]byte, typeSize+net.IPv6len)
hostData[0] = typeIPv6
copy(hostData[typeSize:], ip6)
} else {
return nil, errors.New("unknown host type")
}
}
} else { // FQDN
if len(host) > 255 {
return nil, errors.New("FQDN too long")
}
h := []byte(host)
l := len(h)
hostData = make([]byte, typeSize+fqdnSize+l)
hostData[0] = typeFQDN
hostData[1] = byte(l)
copy(hostData[typeSize+fqdnSize:], h)
}
// set port
portData := make([]byte, portSize)
binary.BigEndian.PutUint16(portData, port)
return append(hostData, portData...), nil
}
func unpackHostData(u io.Reader) (string, error) {
typ := make([]byte, typeSize)
_, err := u.Read(typ)
if err != nil {
return "", err
}
var host string
switch typ[0] {
case typeIPv4:
ip := make([]byte, net.IPv4len)
_, err = io.ReadFull(u, ip)
if err != nil {
return "", err
}
host = net.IP(ip).String()
case typeIPv6:
ip := make([]byte, net.IPv6len)
_, err = io.ReadFull(u, ip)
if err != nil {
return "", err
}
host = net.IP(ip).String()
case typeFQDN:
fqdnLen := make([]byte, fqdnSize)
_, err = u.Read(fqdnLen)
if err != nil {
return "", err
}
fqdn := make([]byte, int(fqdnLen[0]))
_, err = io.ReadFull(u, fqdn)
if err != nil {
return "", err
}
host = string(fqdn)
default:
return "", errors.New("invalid type")
}
port := make([]byte, portSize)
_, err = io.ReadFull(u, port)
if err != nil {
return "", err
}
portStr := strconv.Itoa(int(binary.BigEndian.Uint16(port)))
return net.JoinHostPort(host, portStr), nil
}
type Response uint8
func (r Response) Error() string {
switch uint8(r) {
case respInvalidPWD:
return "invalid password"
case respConnectFailed:
return "failed to connect target"
default:
return "unknown error"
}
}