forked from YiQiu1984/lightsocks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.go
160 lines (144 loc) · 4.6 KB
/
server.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package lightsocks
import (
"encoding/binary"
"net"
)
type LsServer struct {
Cipher *cipher
ListenAddr *net.TCPAddr
}
// 新建一个服务端
// 服务端的职责是:
// 1. 监听来自本地代理客户端的请求
// 2. 解密本地代理客户端请求的数据,解析 SOCKS5 协议,连接用户浏览器真正想要连接的远程服务器
// 3. 转发用户浏览器真正想要连接的远程服务器返回的数据的加密后的内容到本地代理客户端
func NewLsServer(password string, listenAddr string) (*LsServer, error) {
bsPassword, err := parsePassword(password)
if err != nil {
return nil, err
}
structListenAddr, err := net.ResolveTCPAddr("tcp", listenAddr)
if err != nil {
return nil, err
}
return &LsServer{
Cipher: newCipher(bsPassword),
ListenAddr: structListenAddr,
}, nil
}
// 运行服务端并且监听来自本地代理客户端的请求
func (lsServer *LsServer) Listen(didListen func(listenAddr net.Addr)) error {
return ListenSecureTCP(lsServer.ListenAddr, lsServer.Cipher, lsServer.handleConn, didListen)
}
// 解 SOCKS5 协议
// https://www.ietf.org/rfc/rfc1928.txt
func (lsServer *LsServer) handleConn(localConn *SecureTCPConn) {
defer localConn.Close()
buf := make([]byte, 256)
/**
The localConn connects to the dstServer, and sends a ver
identifier/method selection message:
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1 to 255 |
+----+----------+----------+
The VER field is set to X'05' for this ver of the protocol. The
NMETHODS field contains the number of method identifier octets that
appear in the METHODS field.
*/
// 第一个字段VER代表Socks的版本,Socks5默认为0x05,其固定长度为1个字节
_, err := localConn.DecodeRead(buf)
// 只支持版本5
if err != nil || buf[0] != 0x05 {
return
}
/**
The dstServer selects from one of the methods given in METHODS, and
sends a METHOD selection message:
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
*/
// 不需要验证,直接验证通过
localConn.EncodeWrite([]byte{0x05, 0x00})
/**
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
*/
// 获取真正的远程服务的地址
n, err := localConn.DecodeRead(buf)
// n 最短的长度为7 情况为 ATYP=3 DST.ADDR占用1字节 值为0x0
if err != nil || n < 7 {
return
}
// CMD代表客户端请求的类型,值长度也是1个字节,有三种类型
// CONNECT X'01'
if buf[1] != 0x01 {
// 目前只支持 CONNECT
return
}
var dIP []byte
// aType 代表请求的远程服务器地址类型,值长度1个字节,有三种类型
switch buf[3] {
case 0x01:
// IP V4 address: X'01'
dIP = buf[4 : 4+net.IPv4len]
case 0x03:
// DOMAINNAME: X'03'
ipAddr, err := net.ResolveIPAddr("ip", string(buf[5:n-2]))
if err != nil {
return
}
dIP = ipAddr.IP
case 0x04:
// IP V6 address: X'04'
dIP = buf[4 : 4+net.IPv6len]
default:
return
}
dPort := buf[n-2:]
dstAddr := &net.TCPAddr{
IP: dIP,
Port: int(binary.BigEndian.Uint16(dPort)),
}
// 连接真正的远程服务
dstServer, err := net.DialTCP("tcp", nil, dstAddr)
if err != nil {
return
} else {
defer dstServer.Close()
// Conn被关闭时直接清除所有数据 不管没有发送的数据
dstServer.SetLinger(0)
// 响应客户端连接成功
/**
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
*/
// 响应客户端连接成功
localConn.EncodeWrite([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
// 进行转发
// 从 localUser 读取数据发送到 dstServer
go func() {
err := localConn.DecodeCopy(dstServer)
if err != nil {
// 在 copy 的过程中可能会存在网络超时等 error 被 return,只要有一个发生了错误就退出本次工作
localConn.Close()
dstServer.Close()
}
}()
// 从 dstServer 读取数据发送到 localUser,这里因为处在翻墙阶段出现网络错误的概率更大
(&SecureTCPConn{
Cipher: localConn.Cipher,
ReadWriteCloser: dstServer,
}).EncodeCopy(localConn)
}