shadowsocks_ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/.yardopts +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +160 -0
- data/Rakefile +14 -0
- data/bin/aruba +17 -0
- data/bin/bundler +17 -0
- data/bin/console +14 -0
- data/bin/cucumber +17 -0
- data/bin/einhorn +17 -0
- data/bin/einhornsh +17 -0
- data/bin/htmldiff +17 -0
- data/bin/ldiff +17 -0
- data/bin/lrucache_server +16 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/setup +8 -0
- data/bin/yard +17 -0
- data/bin/yardoc +17 -0
- data/bin/yri +17 -0
- data/config.example.json +6 -0
- data/exe/sslocal-ruby +4 -0
- data/exe/ssserver-ruby +4 -0
- data/lib/shadowsocks_ruby/app.rb +236 -0
- data/lib/shadowsocks_ruby/cipher/cipher.rb +112 -0
- data/lib/shadowsocks_ruby/cipher/openssl.rb +81 -0
- data/lib/shadowsocks_ruby/cipher/rbnacl.rb +64 -0
- data/lib/shadowsocks_ruby/cipher/rc4_md5.rb +54 -0
- data/lib/shadowsocks_ruby/cipher/table.rb +65 -0
- data/lib/shadowsocks_ruby/cli/sslocal_runner.rb +125 -0
- data/lib/shadowsocks_ruby/cli/ssserver_runner.rb +115 -0
- data/lib/shadowsocks_ruby/connections/backend_connection.rb +50 -0
- data/lib/shadowsocks_ruby/connections/connection.rb +195 -0
- data/lib/shadowsocks_ruby/connections/server_connection.rb +63 -0
- data/lib/shadowsocks_ruby/connections/tcp/client_connection.rb +43 -0
- data/lib/shadowsocks_ruby/connections/tcp/destination_connection.rb +19 -0
- data/lib/shadowsocks_ruby/connections/tcp/localbackend_connection.rb +29 -0
- data/lib/shadowsocks_ruby/connections/tcp/remoteserver_connection.rb +18 -0
- data/lib/shadowsocks_ruby/connections/udp/client_connection.rb +43 -0
- data/lib/shadowsocks_ruby/connections/udp/destination_connection.rb +18 -0
- data/lib/shadowsocks_ruby/connections/udp/localbackend_connection.rb +29 -0
- data/lib/shadowsocks_ruby/connections/udp/remoteserver_connection.rb +18 -0
- data/lib/shadowsocks_ruby/protocols/cipher/iv_cipher.rb +99 -0
- data/lib/shadowsocks_ruby/protocols/cipher/no_iv_cipher.rb +53 -0
- data/lib/shadowsocks_ruby/protocols/cipher/verify_sha1.rb +138 -0
- data/lib/shadowsocks_ruby/protocols/obfs/http_simple.rb +189 -0
- data/lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb +342 -0
- data/lib/shadowsocks_ruby/protocols/packet/plain.rb +51 -0
- data/lib/shadowsocks_ruby/protocols/packet/shadowsocks.rb +88 -0
- data/lib/shadowsocks_ruby/protocols/packet/socks5.rb +162 -0
- data/lib/shadowsocks_ruby/protocols/protocol.rb +164 -0
- data/lib/shadowsocks_ruby/protocols/protocol_stack.rb +117 -0
- data/lib/shadowsocks_ruby/util.rb +52 -0
- data/lib/shadowsocks_ruby/version.rb +3 -0
- data/lib/shadowsocks_ruby.rb +86 -0
- data/shadowsocks_ruby.gemspec +48 -0
- metadata +290 -0
@@ -0,0 +1,342 @@
|
|
1
|
+
require 'lrucache'
|
2
|
+
|
3
|
+
module ShadowsocksRuby
|
4
|
+
module Protocols
|
5
|
+
# TLS 1.2 Obfuscation Protocol
|
6
|
+
#
|
7
|
+
# Specification:
|
8
|
+
# * https://github.com/shadowsocksr/shadowsocksr/blob/manyuser/shadowsocks/obfsplugin/obfs_tls.py
|
9
|
+
# * https://github.com/shadowsocksr/obfsplugin/blob/master/c/tls1.2_ticket.c
|
10
|
+
#
|
11
|
+
# TLS 1.2 Reference:
|
12
|
+
# * https://en.wikipedia.org/wiki/Transport_Layer_Security
|
13
|
+
# * https://tools.ietf.org/html/rfc5246
|
14
|
+
# * https://tools.ietf.org/html/rfc5077
|
15
|
+
# * https://tools.ietf.org/html/rfc6066
|
16
|
+
class TlsTicketProtocol
|
17
|
+
include DummyHelper
|
18
|
+
include BufferHelper
|
19
|
+
|
20
|
+
VERSION_SSL_3_0 = [3, 0]
|
21
|
+
VERSION_TLS_1_0 = [3, 1]
|
22
|
+
VERSION_TLS_1_1 = [3, 2]
|
23
|
+
VERSION_TLS_1_2 = [3, 3]
|
24
|
+
|
25
|
+
#Content types
|
26
|
+
CTYPE_ChangeCipherSpec = 0x14
|
27
|
+
CTYPE_Alert = 0x15
|
28
|
+
CTYPE_Handshake = 0x16
|
29
|
+
CTYPE_Application = 0x17
|
30
|
+
CTYPE_Heartbeat = 0x18
|
31
|
+
|
32
|
+
#Message types
|
33
|
+
MTYPE_HelloRequest = 0
|
34
|
+
MTYPE_ClientHello = 1
|
35
|
+
MTYPE_ServerHello = 2
|
36
|
+
MTYPE_NewSessionTicket = 4
|
37
|
+
MTYPE_Certificate = 11
|
38
|
+
MTYPE_ServerKeyExchange = 12
|
39
|
+
MTYPE_CertificateRequest = 13
|
40
|
+
MTYPE_ServerHelloDone = 14
|
41
|
+
MTYPE_CertificateVerify = 15
|
42
|
+
MTYPE_ClientKeyExchange = 16
|
43
|
+
MTYPE_Finished = 20
|
44
|
+
|
45
|
+
attr_accessor :next_protocol
|
46
|
+
|
47
|
+
# @param [Hash] configuration parameters
|
48
|
+
# @option params [String] :host shadowsocks server address, required by remoteserver protocol
|
49
|
+
# @option params [String] :key key, required by both remoteserver and localbackend protocol
|
50
|
+
# @option params [Boolean] :compatible compatibility with origin mode, default _true_
|
51
|
+
# @option params [String] :obfs_param obfs param, optional
|
52
|
+
# @option params [LRUCache] :lrucache lrucache, optional, it intened to be a lrucache Proxy if provided
|
53
|
+
def initialize params = {}
|
54
|
+
@params = {:compatible => true}.merge(params)
|
55
|
+
@buffer = ''
|
56
|
+
@client_id = Random.new.bytes(32)
|
57
|
+
@max_time_dif = 60 * 60 * 24 # time dif (second) setting
|
58
|
+
@startup_time = Time.now.to_i - 60 * 30
|
59
|
+
@client_data = @params[:lrucache] || LRUCache.new(:ttl => 60 * 5)
|
60
|
+
end
|
61
|
+
|
62
|
+
def tcp_send_to_remoteserver_first_packet data
|
63
|
+
send_client_change_cipherspec_and_finish data
|
64
|
+
|
65
|
+
class << self
|
66
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_other_packet
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_first_packet
|
72
|
+
|
73
|
+
# TLS 1.2 Application Pharse
|
74
|
+
def tcp_send_to_remoteserver_other_packet data
|
75
|
+
send_data_application_pharse data
|
76
|
+
end
|
77
|
+
|
78
|
+
def tcp_receive_from_remoteserver_first_packet n
|
79
|
+
send_client_hello
|
80
|
+
recv_server_hello
|
81
|
+
class << self
|
82
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
|
83
|
+
end
|
84
|
+
tcp_receive_from_remoteserver_other_packet n
|
85
|
+
end
|
86
|
+
|
87
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_first_packet
|
88
|
+
|
89
|
+
# TLS 1.2 Application Pharse
|
90
|
+
def tcp_receive_from_remoteserver_other_packet n
|
91
|
+
head = async_recv 3
|
92
|
+
if head != [CTYPE_Application, *VERSION_TLS_1_2].pack("C3")
|
93
|
+
raise PharseError, "client_decode appdata error"
|
94
|
+
end
|
95
|
+
size = async_recv(2).unpack("n")[0]
|
96
|
+
@buffer << async_recv(size)
|
97
|
+
|
98
|
+
tcp_receive_from_remoteserver_other_packet_helper n
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
def tcp_receive_from_localbackend_first_packet n
|
104
|
+
class << self
|
105
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
106
|
+
end
|
107
|
+
recv_client_hello
|
108
|
+
@no_effect ||= nil
|
109
|
+
if !@no_effect
|
110
|
+
send_server_hello
|
111
|
+
recv_client_change_cipherspec_and_finish
|
112
|
+
tcp_receive_from_localbackend_other_packet n
|
113
|
+
else
|
114
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
119
|
+
|
120
|
+
# TLS 1.2 Application Pharse
|
121
|
+
def tcp_receive_from_localbackend_other_packet n
|
122
|
+
@no_effect ||= nil
|
123
|
+
if !@no_effect
|
124
|
+
head = async_recv 3
|
125
|
+
if head != [CTYPE_Application, *VERSION_TLS_1_2].pack("C3")
|
126
|
+
raise PharseError, "server_decode appdata error"
|
127
|
+
end
|
128
|
+
size = async_recv(2).unpack("n")[0]
|
129
|
+
@buffer << async_recv(size)
|
130
|
+
end
|
131
|
+
|
132
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
133
|
+
end
|
134
|
+
|
135
|
+
def tcp_send_to_localbackend data
|
136
|
+
@no_effect ||= nil
|
137
|
+
if !@no_effect
|
138
|
+
send_data_application_pharse data
|
139
|
+
else
|
140
|
+
send_data data
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# helpers
|
145
|
+
def get_random
|
146
|
+
verifyid = [Time.now.to_i].pack("N") << Random.new.bytes(18)
|
147
|
+
hello = ""
|
148
|
+
hello << verifyid # Random part 1
|
149
|
+
hello << ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, verifyid) # Random part 2
|
150
|
+
end
|
151
|
+
|
152
|
+
def send_client_hello
|
153
|
+
client_hello = ""
|
154
|
+
|
155
|
+
client_hello << [*VERSION_TLS_1_2].pack("C2") # ProtocolVersion
|
156
|
+
client_hello << get_random # Random len 32
|
157
|
+
client_hello << [32].pack("C") << @client_id # SessionID
|
158
|
+
client_hello << Util.hex2bin("001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a") # CipherSuite
|
159
|
+
client_hello << Util.hex2bin("0100") # CompressionMethod
|
160
|
+
|
161
|
+
ext = Util.hex2bin("ff01000100") # Extension 1 (type ff01 + len 0001 + data 00 )
|
162
|
+
|
163
|
+
hosts = @params[:obfs_param] || @params[:host]
|
164
|
+
if (hosts == nil or hosts == "")
|
165
|
+
raise PharseError, "No :host or :obfs_param parameters"
|
166
|
+
end
|
167
|
+
if (("0".."9").include? hosts[-1])
|
168
|
+
hosts = ""
|
169
|
+
end
|
170
|
+
hosts = hosts.split(",")
|
171
|
+
if hosts.length != 0
|
172
|
+
host = hosts[Random.rand(hosts.length)]
|
173
|
+
else
|
174
|
+
host = ""
|
175
|
+
end
|
176
|
+
ext << make_ext_sni(host) # Extension 2
|
177
|
+
ext << Util.hex2bin("00170000") # Extension 3 (type 0017 + len 0000)
|
178
|
+
ext << Util.hex2bin("002300d0") << Random.new.bytes(208) # ticket, Extension 4 (type 0023 + len 00d0 + data)
|
179
|
+
ext << Util.hex2bin("000d001600140601060305010503040104030301030302010203") # Extension 5 (type 000d + len 0016 + data)
|
180
|
+
ext << Util.hex2bin("000500050100000000") # Extension 6 (type 0005 + len 0005 + data)
|
181
|
+
ext << Util.hex2bin("00120000") # Extension 7 (type 0012 + len 0000)
|
182
|
+
ext << Util.hex2bin("75500000") # Extension 8 (type 7550 + len 0000)
|
183
|
+
ext << Util.hex2bin("000b00020100") # Extension 9 (type 000b + len 0002 + data)
|
184
|
+
ext << Util.hex2bin("000a0006000400170018") # Extension 10 (type 000a + len 0006 + data)
|
185
|
+
|
186
|
+
client_hello << [ext.length].pack("n") << ext # Extension List
|
187
|
+
|
188
|
+
client_handshake_message = [MTYPE_ClientHello, 0, client_hello.length].pack("CCn") << client_hello
|
189
|
+
handshake_message = [CTYPE_Handshake,*VERSION_TLS_1_0, client_handshake_message.length].pack("C3n") << client_handshake_message
|
190
|
+
|
191
|
+
send_data(handshake_message)
|
192
|
+
end
|
193
|
+
|
194
|
+
def send_client_change_cipherspec_and_finish data
|
195
|
+
buf = ""
|
196
|
+
buf << [CTYPE_ChangeCipherSpec, *VERSION_TLS_1_2, 0, 1, 1].pack("C*")
|
197
|
+
buf << [CTYPE_Handshake, *VERSION_TLS_1_2, 32].pack("C3n") << Random.new.bytes(22)
|
198
|
+
buf << ShadowsocksRuby::Cipher::hmac_sha1_digest(@params[:key] + @client_id, buf)
|
199
|
+
buf << [CTYPE_Application, *VERSION_TLS_1_2, data.length].pack("C3n") << data
|
200
|
+
send_data buf
|
201
|
+
end
|
202
|
+
|
203
|
+
def recv_server_hello
|
204
|
+
data = async_recv(129) # ServerHello 76 + ServerChangeSipherSpec 6 + Finished 37
|
205
|
+
verify = data[11 ... 33]
|
206
|
+
if ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, verify) != data[33 ... 43]
|
207
|
+
raise PharseError, "client_decode data error"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def recv_client_hello
|
212
|
+
data = async_recv 3
|
213
|
+
if data != [CTYPE_Handshake, *VERSION_TLS_1_0].pack("C3")
|
214
|
+
if @params[:compatible]
|
215
|
+
@buffer = data
|
216
|
+
@no_effect = true
|
217
|
+
return
|
218
|
+
else
|
219
|
+
raise PharseError, "decode error"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
len_client_handshake_message = async_recv(2).unpack("n")[0]
|
224
|
+
client_handshake_message = async_recv(len_client_handshake_message)
|
225
|
+
|
226
|
+
if (client_handshake_message.slice!(0, 2) != [MTYPE_ClientHello, 0].pack("C2"))
|
227
|
+
raise PharseError, "tls_auth not client hello message"
|
228
|
+
end
|
229
|
+
|
230
|
+
len_client_hello = client_handshake_message.slice!(0, 2).unpack("n")[0]
|
231
|
+
client_hello = client_handshake_message
|
232
|
+
|
233
|
+
if (len_client_hello != client_hello.length )
|
234
|
+
raise PharseError, "tls_auth wrong message size"
|
235
|
+
end
|
236
|
+
|
237
|
+
if (client_hello.slice!(0,2) != [*VERSION_TLS_1_2].pack("C2"))
|
238
|
+
raise PharseError, "tls_auth wrong tls version"
|
239
|
+
end
|
240
|
+
|
241
|
+
verifyid = client_hello.slice!(0, 32)
|
242
|
+
|
243
|
+
len_sessionid = client_hello.slice!(0,1).unpack("C")[0]
|
244
|
+
if (len_sessionid < 32)
|
245
|
+
raise PharseError, "tls_auth wrong sessionid_len"
|
246
|
+
end
|
247
|
+
|
248
|
+
sessionid = client_hello.slice!(0, len_sessionid)
|
249
|
+
@client_id = sessionid
|
250
|
+
|
251
|
+
sha1 = ShadowsocksRuby::Cipher::hmac_sha1_digest(@params[:key] + sessionid, verifyid[0, 22])
|
252
|
+
utc_time = Time.at(verifyid[0, 4].unpack("N")[0])
|
253
|
+
time_dif = Time.now.to_i - utc_time.to_i
|
254
|
+
|
255
|
+
#if @params[:obfs_param] != nil
|
256
|
+
# @max_time_dif = @params[:obfs_param].to_i
|
257
|
+
#end
|
258
|
+
if @max_time_dif > 0 && (time_dif.abs > @max_time_dif or utc_time.to_i - @startup_time < - @max_time_dif / 2)
|
259
|
+
raise PharseError, "tls_auth wrong time"
|
260
|
+
end
|
261
|
+
|
262
|
+
if sha1 != verifyid[22 .. -1]
|
263
|
+
raise PharseError, "tls_auth wrong sha1"
|
264
|
+
end
|
265
|
+
|
266
|
+
if @client_data[verifyid[0, 22]]
|
267
|
+
raise PharseError, "replay attack detect, id = #{Util.bin2hex(verifyid)}"
|
268
|
+
end
|
269
|
+
@client_data[verifyid[0, 22]] = sessionid
|
270
|
+
end
|
271
|
+
|
272
|
+
def send_server_hello
|
273
|
+
data = [*VERSION_TLS_1_2].pack("C2")
|
274
|
+
data << get_random
|
275
|
+
data << Util.hex2bin("20") # len 32 in decimal
|
276
|
+
data << @client_id
|
277
|
+
data << Util.hex2bin("c02f000005ff01000100")
|
278
|
+
data = Util.hex2bin("0200") << [data.length].pack("n") << data
|
279
|
+
data = Util.hex2bin("160303") << [data.length].pack("n") << data # ServerHello len 86 (11 + 32 + 1 + 32 + 10)
|
280
|
+
data << Util.hex2bin("14") << [*VERSION_TLS_1_2].pack("C2") << Util.hex2bin("000101") # ChangeCipherSpec len (6)
|
281
|
+
data << Util.hex2bin("16") << [*VERSION_TLS_1_2].pack("C2") << Util.hex2bin("0020") << Random.new.bytes(22)
|
282
|
+
data << ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, data) # Finished len(37)
|
283
|
+
send_data data # len 129
|
284
|
+
end
|
285
|
+
|
286
|
+
def recv_client_change_cipherspec_and_finish
|
287
|
+
data = async_recv 43
|
288
|
+
if data[0, 6] != [CTYPE_ChangeCipherSpec, *VERSION_TLS_1_2, 0, 1, 1].pack("C*") # ChangeCipherSpec
|
289
|
+
raise PharseError, "server_decode data error"
|
290
|
+
end
|
291
|
+
if data[6, 5] != [CTYPE_Handshake, *VERSION_TLS_1_2, 32].pack("C3n") # Finished
|
292
|
+
raise PharseError, "server_decode data error"
|
293
|
+
end
|
294
|
+
if ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, data[0, 33]) != data[33, 10]
|
295
|
+
raise PharseError, "server_decode data error"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def send_data_application_pharse data
|
300
|
+
buf = ""
|
301
|
+
while data.length > 2048
|
302
|
+
size = [Random.rand(65535) % 4096 + 100, data.length].min
|
303
|
+
buf << [CTYPE_Application, *VERSION_TLS_1_2, size].pack("C3n") << data.slice!(0, size)
|
304
|
+
end
|
305
|
+
if data.length > 0
|
306
|
+
buf << [CTYPE_Application, *VERSION_TLS_1_2, data.length].pack("C3n") << data
|
307
|
+
end
|
308
|
+
send_data buf
|
309
|
+
end
|
310
|
+
|
311
|
+
def make_ext_sni host
|
312
|
+
name_type = 0 #host_name
|
313
|
+
server_name = [name_type, host.length].pack("Cn") << host
|
314
|
+
server_name_list = [server_name.length].pack("n") << server_name
|
315
|
+
|
316
|
+
type = Util.hex2bin("0000")
|
317
|
+
data = [server_name_list.length].pack("n") << server_name_list
|
318
|
+
|
319
|
+
return type << data
|
320
|
+
end
|
321
|
+
|
322
|
+
alias tcp_receive_from_client raise_me
|
323
|
+
alias tcp_send_to_client raise_me
|
324
|
+
#alias tcp_receive_from_remoteserver raise_me
|
325
|
+
#alias tcp_send_to_remoteserver raise_me
|
326
|
+
#alias tcp_receive_from_localbackend raise_me
|
327
|
+
#alias tcp_send_to_localbackend raise_me
|
328
|
+
alias tcp_receive_from_destination raise_me
|
329
|
+
alias tcp_send_to_destination raise_me
|
330
|
+
|
331
|
+
alias udp_receive_from_client raise_me
|
332
|
+
alias udp_send_to_client raise_me
|
333
|
+
alias udp_receive_from_remoteserver raise_me
|
334
|
+
alias udp_send_to_remoteserver raise_me
|
335
|
+
alias udp_receive_from_localbackend raise_me
|
336
|
+
alias udp_send_to_localbackend raise_me
|
337
|
+
alias udp_receive_from_destination raise_me
|
338
|
+
alias udp_send_to_destination raise_me
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
# Relay data from peer to plexer without any process.
|
4
|
+
# This is a packet protocol, so no need to implement @buffer
|
5
|
+
class PlainProtocol
|
6
|
+
include DummyHelper
|
7
|
+
|
8
|
+
attr_accessor :next_protocol
|
9
|
+
|
10
|
+
# @param [Hash] configuration parameters
|
11
|
+
def initialize params = {}
|
12
|
+
@params = {}.merge(params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def tcp_receive_from_destination n
|
16
|
+
async_recv n
|
17
|
+
end
|
18
|
+
|
19
|
+
def tcp_send_to_destination data
|
20
|
+
send_data data
|
21
|
+
end
|
22
|
+
|
23
|
+
def udp_receive_from_destination n
|
24
|
+
async_recv n
|
25
|
+
end
|
26
|
+
|
27
|
+
def udp_send_to_destination data
|
28
|
+
send_data data
|
29
|
+
end
|
30
|
+
|
31
|
+
alias tcp_receive_from_client raise_me
|
32
|
+
alias tcp_send_to_client raise_me
|
33
|
+
alias tcp_receive_from_remoteserver raise_me
|
34
|
+
alias tcp_send_to_remoteserver raise_me
|
35
|
+
alias tcp_receive_from_localbackend raise_me
|
36
|
+
alias tcp_send_to_localbackend raise_me
|
37
|
+
#alias tcp_receive_from_destination raise_me
|
38
|
+
#alias tcp_send_to_destination raise_me
|
39
|
+
|
40
|
+
alias udp_receive_from_client raise_me
|
41
|
+
alias udp_send_to_client raise_me
|
42
|
+
alias udp_receive_from_remoteserver raise_me
|
43
|
+
alias udp_send_to_remoteserver raise_me
|
44
|
+
alias udp_receive_from_localbackend raise_me
|
45
|
+
alias udp_send_to_localbackend raise_me
|
46
|
+
#alias udp_receive_from_destination raise_me
|
47
|
+
#alias udp_send_to_destination raise_me
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
# Shadowsocks packet protocol for both origin shadowsocks protocol and OTA shadowsocks protocol.
|
4
|
+
#
|
5
|
+
# specification:
|
6
|
+
# * https://shadowsocks.org/en/spec/protocol.html
|
7
|
+
# * https://shadowsocks.org/en/spec/one-time-auth.html
|
8
|
+
#
|
9
|
+
# This is a packet protocol, so no need to implement @buffer
|
10
|
+
class ShadowsocksProtocol
|
11
|
+
include DummyHelper
|
12
|
+
|
13
|
+
attr_accessor :next_protocol
|
14
|
+
|
15
|
+
ATYP_IPV4 = 1
|
16
|
+
ATYP_DOMAIN = 3
|
17
|
+
ATYP_IPV6 = 4
|
18
|
+
|
19
|
+
# @param [Hash] configuration parameters
|
20
|
+
def initialize params = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def tcp_receive_from_localbackend_first_packet n
|
24
|
+
buf = ""
|
25
|
+
s = async_recv(1)
|
26
|
+
buf << s
|
27
|
+
address_type = s.unpack("C").first
|
28
|
+
case address_type
|
29
|
+
when ATYP_IPV4
|
30
|
+
buf << async_recv(4)
|
31
|
+
when ATYP_IPV6
|
32
|
+
buf << async_recv(16)
|
33
|
+
when ATYP_DOMAIN
|
34
|
+
buf << (s = async_recv(1))
|
35
|
+
domain_len = s.unpack("C").first
|
36
|
+
buf << async_recv(domain_len)
|
37
|
+
buf << async_recv(2) # port
|
38
|
+
else
|
39
|
+
raise PharseError, "unknown address_type: #{address_type}"
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
44
|
+
end
|
45
|
+
# first packet is special:
|
46
|
+
# ATYP + Destination Address + Destination Port
|
47
|
+
buf
|
48
|
+
end
|
49
|
+
|
50
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
51
|
+
|
52
|
+
def tcp_receive_from_localbackend_other_packet n
|
53
|
+
async_recv(n)
|
54
|
+
end
|
55
|
+
|
56
|
+
def tcp_send_to_localbackend data
|
57
|
+
send_data data
|
58
|
+
end
|
59
|
+
|
60
|
+
def tcp_receive_from_remoteserver n
|
61
|
+
async_recv(n)
|
62
|
+
end
|
63
|
+
|
64
|
+
def tcp_send_to_remoteserver data
|
65
|
+
send_data data
|
66
|
+
end
|
67
|
+
|
68
|
+
alias tcp_receive_from_client raise_me
|
69
|
+
alias tcp_send_to_client raise_me
|
70
|
+
#alias tcp_receive_from_remoteserver raise_me
|
71
|
+
#alias tcp_send_to_remoteserver raise_me
|
72
|
+
#alias tcp_receive_from_localbackend raise_me
|
73
|
+
#alias tcp_send_to_localbackend raise_me
|
74
|
+
alias tcp_receive_from_destination raise_me
|
75
|
+
alias tcp_send_to_destination raise_me
|
76
|
+
|
77
|
+
alias udp_receive_from_client raise_me
|
78
|
+
alias udp_send_to_client raise_me
|
79
|
+
alias udp_receive_from_remoteserver raise_me
|
80
|
+
alias udp_send_to_remoteserver raise_me
|
81
|
+
alias udp_receive_from_localbackend raise_me
|
82
|
+
alias udp_send_to_localbackend raise_me
|
83
|
+
alias udp_receive_from_destination raise_me
|
84
|
+
alias udp_send_to_destination raise_me
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module ShadowsocksRuby
|
4
|
+
module Protocols
|
5
|
+
# SOCKS 5 protocol
|
6
|
+
#
|
7
|
+
# specification :
|
8
|
+
# * http://tools.ieft.org/html/rfc1928
|
9
|
+
# * http://en.wikipedia.org/wiki/SOCKS
|
10
|
+
# @note Now only implemented the server side protocol, not the client side protocol.
|
11
|
+
class Socks5Protocol
|
12
|
+
include DummyHelper
|
13
|
+
|
14
|
+
attr_accessor :next_protocol
|
15
|
+
|
16
|
+
METHOD_NO_AUTH = 0
|
17
|
+
CMD_CONNECT = 1
|
18
|
+
REP_SUCCESS = 0
|
19
|
+
RESERVED = 0
|
20
|
+
ATYP_IPV4 = 1
|
21
|
+
ATYP_DOMAIN = 3
|
22
|
+
ATYP_IPV6 = 4
|
23
|
+
SOCKS5 = 5
|
24
|
+
|
25
|
+
# @param [Hash] configuration parameters
|
26
|
+
def initialize params = {}
|
27
|
+
@params = {}.merge(params)
|
28
|
+
end
|
29
|
+
|
30
|
+
def tcp_receive_from_client_first_packet n
|
31
|
+
# check version
|
32
|
+
version = async_recv(1).unpack("C").first
|
33
|
+
if version != SOCKS5
|
34
|
+
raise PharseError, "SOCKS version not supported: #{version.inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# client handshake v5
|
38
|
+
nmethods = async_recv(1).unpack("C").first
|
39
|
+
*methods = async_recv(nmethods).unpack("C*")
|
40
|
+
if methods.include?(METHOD_NO_AUTH)
|
41
|
+
packet = [SOCKS5, METHOD_NO_AUTH].pack("C*")
|
42
|
+
send_data packet
|
43
|
+
else
|
44
|
+
raise PharseError, 'Unsupported authentication method. Only "No Authentication" is supported'
|
45
|
+
end
|
46
|
+
|
47
|
+
version, command, reserved = async_recv(3).unpack("C3")
|
48
|
+
buf =''
|
49
|
+
buf << (s = async_recv(1))
|
50
|
+
address_type = s.unpack("C").first
|
51
|
+
case address_type
|
52
|
+
when ATYP_IPV4
|
53
|
+
buf << async_recv(4)
|
54
|
+
when ATYP_IPV6
|
55
|
+
buf << async_recv(16)
|
56
|
+
when ATYP_DOMAIN
|
57
|
+
buf << (s = async_recv(1))
|
58
|
+
domain_len = s.unpack("C").first
|
59
|
+
buf << async_recv(domain_len)
|
60
|
+
buf << async_recv(2) # port
|
61
|
+
else
|
62
|
+
raise PharseError, "unknown address_type: #{address_type}"
|
63
|
+
end
|
64
|
+
|
65
|
+
packet = ([SOCKS5, REP_SUCCESS, RESERVED, 1, 0, 0, 0, 0, 0]).pack("C8n")
|
66
|
+
send_data packet
|
67
|
+
class << self
|
68
|
+
alias tcp_receive_from_client tcp_receive_from_client_other_packet
|
69
|
+
end
|
70
|
+
|
71
|
+
# first packet is special:
|
72
|
+
# ATYP + Destination Address + Destination Port
|
73
|
+
buf
|
74
|
+
end
|
75
|
+
|
76
|
+
alias tcp_receive_from_client tcp_receive_from_client_first_packet
|
77
|
+
|
78
|
+
def tcp_receive_from_client_other_packet n
|
79
|
+
async_recv(n)
|
80
|
+
end
|
81
|
+
|
82
|
+
def tcp_send_to_client data
|
83
|
+
send_data data
|
84
|
+
end
|
85
|
+
|
86
|
+
def tcp_receive_from_localbackend_first_packet n
|
87
|
+
# check version
|
88
|
+
version = async_recv(1).unpack("C").first
|
89
|
+
if version != SOCKS5
|
90
|
+
raise PharseError, "SOCKS version not supported: #{version.inspect}"
|
91
|
+
end
|
92
|
+
|
93
|
+
# client handshake v5
|
94
|
+
nmethods = async_recv(1).unpack("C").first
|
95
|
+
*methods = async_recv(nmethods).unpack("C*")
|
96
|
+
if methods.include?(METHOD_NO_AUTH)
|
97
|
+
packet = [SOCKS5, METHOD_NO_AUTH].pack("C*")
|
98
|
+
send_data packet
|
99
|
+
else
|
100
|
+
raise PharseError, 'Unsupported authentication method. Only "No Authentication" is supported'
|
101
|
+
end
|
102
|
+
|
103
|
+
version, command, reserved = async_recv(3).unpack("C3")
|
104
|
+
buf =''
|
105
|
+
buf << (s = async_recv(1))
|
106
|
+
address_type = s.unpack("C").first
|
107
|
+
case address_type
|
108
|
+
when ATYP_IPV4
|
109
|
+
buf << async_recv(4)
|
110
|
+
when ATYP_IPV6
|
111
|
+
buf << async_recv(16)
|
112
|
+
when ATYP_DOMAIN
|
113
|
+
buf << (s = async_recv(1))
|
114
|
+
domain_len = s.unpack("C").first
|
115
|
+
buf << async_recv(domain_len)
|
116
|
+
buf << async_recv(2) # port
|
117
|
+
else
|
118
|
+
raise PharseError, "unknown address_type: #{address_type}"
|
119
|
+
end
|
120
|
+
|
121
|
+
packet = ([SOCKS5, REP_SUCCESS, RESERVED, 1, 0, 0, 0, 0, 0]).pack("C8n")
|
122
|
+
send_data packet
|
123
|
+
|
124
|
+
class << self
|
125
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
126
|
+
end
|
127
|
+
|
128
|
+
# first packet is special:
|
129
|
+
# ATYP + Destination Address + Destination Port
|
130
|
+
buf
|
131
|
+
end
|
132
|
+
|
133
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
134
|
+
|
135
|
+
def tcp_receive_from_localbackend_other_packet n
|
136
|
+
async_recv(n)
|
137
|
+
end
|
138
|
+
|
139
|
+
def tcp_send_to_localbackend data
|
140
|
+
send_data data
|
141
|
+
end
|
142
|
+
|
143
|
+
#alias tcp_receive_from_client raise_me
|
144
|
+
#alias tcp_send_to_client raise_me
|
145
|
+
alias tcp_receive_from_remoteserver raise_me
|
146
|
+
alias tcp_send_to_remoteserver raise_me
|
147
|
+
#alias tcp_receive_from_localbackend raise_me
|
148
|
+
#alias tcp_send_to_localbackend raise_me
|
149
|
+
alias tcp_receive_from_destination raise_me
|
150
|
+
alias tcp_send_to_destination raise_me
|
151
|
+
|
152
|
+
alias udp_receive_from_client raise_me
|
153
|
+
alias udp_send_to_client raise_me
|
154
|
+
alias udp_receive_from_remoteserver raise_me
|
155
|
+
alias udp_send_to_remoteserver raise_me
|
156
|
+
alias udp_receive_from_localbackend raise_me
|
157
|
+
alias udp_send_to_localbackend raise_me
|
158
|
+
alias udp_receive_from_destination raise_me
|
159
|
+
alias udp_send_to_destination raise_me
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|