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,43 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
# Provides various functionality code of a UDP Connection.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# class DummyConnection < EventMachine::Connection
|
7
|
+
# include ShadowsocksRuby::Connections::UDP::ClientConnection
|
8
|
+
# end
|
9
|
+
# # some how get a DummyConnection object
|
10
|
+
# # dummy_connection = ...
|
11
|
+
# # dummy_connection.plexer_protocol.udp_process_client will be called looply
|
12
|
+
module UDP
|
13
|
+
# (see TCP::ClientConnection)
|
14
|
+
module ClientConnection
|
15
|
+
include ShadowsocksRuby::Connections::Connection
|
16
|
+
include ShadowsocksRuby::Connections::ServerConnection
|
17
|
+
|
18
|
+
# (see TCP::ClientConnection#initialize)
|
19
|
+
def initialize protocol_stack, params, backend_protocol_stack, backend_params
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_first_packet
|
24
|
+
address_bin = packet_protocol.udp_receive_from_client(-1)
|
25
|
+
create_plexer(@params[:host], @params[:port], RemoteServerConnection)
|
26
|
+
plexer.packet_protocol.udp_send_to_remoteserver address_bin
|
27
|
+
class << self
|
28
|
+
alias process_hook process_other_packet
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is Called by process loop
|
33
|
+
alias process_hook process_first_packet
|
34
|
+
|
35
|
+
def process_other_packet
|
36
|
+
data = packet_protocol.udp_receive_from_client(-1)
|
37
|
+
plexer.packet_protocol.udp_send_to_remoteserver(data)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
module UDP
|
4
|
+
# (see TCP::ClientConnection)
|
5
|
+
module DestinationConnection
|
6
|
+
include ShadowsocksRuby::Connections::Connection
|
7
|
+
include ShadowsocksRuby::Connections::BackendConnection
|
8
|
+
|
9
|
+
# (see TCP::ClientConnection#process_hook)
|
10
|
+
def process_hook
|
11
|
+
data = packet_protocol.udp_receive_from_destination(-1)
|
12
|
+
plexer.packet_protocol.udp_send_to_localbackend(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
module UDP
|
4
|
+
# (see TCP::ClientConnection)
|
5
|
+
module LocalBackendConnection
|
6
|
+
include ShadowsocksRuby::Connections::Connection
|
7
|
+
include ShadowsocksRuby::Connections::ServerConnection
|
8
|
+
|
9
|
+
def process_first_packet
|
10
|
+
address_bin = packet_protocol.udp_receive_from_localbackend(-1)
|
11
|
+
host, port = Util::parse_address_bin(address_bin)
|
12
|
+
create_plexer(host, port, DestinationConnection)
|
13
|
+
class << self
|
14
|
+
alias process_hook process_other_packet
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# (see TCP::ClientConnection#process_hook)
|
19
|
+
alias process_hook process_first_packet
|
20
|
+
|
21
|
+
def process_other_packet
|
22
|
+
data = packet_protocol.udp_receive_from_localbackend(-1)
|
23
|
+
plexer.packet_protocol.udp_send_to_destination(data)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
module UDP
|
4
|
+
# (see TCP::ClientConnection)
|
5
|
+
module RemoteServerConnection
|
6
|
+
include ShadowsocksRuby::Connections::Connection
|
7
|
+
include ShadowsocksRuby::Connections::BackendConnection
|
8
|
+
|
9
|
+
# (see TCP::ClientConnection#process_hook)
|
10
|
+
def process_hook
|
11
|
+
data = packet_protocol.udp_receive_from_remoteserver(-1)
|
12
|
+
plexer.packet_protocol.udp_send_to_client(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
|
4
|
+
# To be used with any cipher methods with an IV, like {Cipher::OpenSSL},
|
5
|
+
# {Cipher::RbNaCl} and {Cipher::RC4_MD5}.
|
6
|
+
class IvCipherProtocol
|
7
|
+
include DummyHelper
|
8
|
+
include BufferHelper
|
9
|
+
|
10
|
+
attr_accessor :next_protocol
|
11
|
+
|
12
|
+
# @param [Hash] configuration parameters
|
13
|
+
# @option params [Cipher::OpenSSL, Cipher::RbNaCl, Cipher::RC4_MD5] :cipher a cipher object with IV and a key, +required+
|
14
|
+
def initialize params = {}
|
15
|
+
@cipher = params[:cipher] or raise ProtocolError, "params[:cipher] is required"
|
16
|
+
@buffer =""
|
17
|
+
end
|
18
|
+
|
19
|
+
def tcp_receive_from_remoteserver_first_packet n
|
20
|
+
iv_len = @cipher.iv_len
|
21
|
+
@recv_iv = async_recv iv_len
|
22
|
+
class << self
|
23
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
|
24
|
+
end
|
25
|
+
tcp_receive_from_remoteserver_other_packet n
|
26
|
+
end
|
27
|
+
|
28
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_first_packet
|
29
|
+
|
30
|
+
def tcp_receive_from_remoteserver_other_packet n
|
31
|
+
@buffer << @cipher.decrypt(async_recv(-1), @recv_iv)
|
32
|
+
tcp_receive_from_remoteserver_other_packet_helper n
|
33
|
+
end
|
34
|
+
|
35
|
+
def tcp_send_to_remoteserver_first_packet data
|
36
|
+
send_first_packet_process data
|
37
|
+
class << self
|
38
|
+
alias tcp_send_to_remoteserver send_other_packet
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_first_packet
|
43
|
+
|
44
|
+
def tcp_receive_from_localbackend_first_packet n
|
45
|
+
iv_len = @cipher.iv_len
|
46
|
+
@recv_iv = async_recv iv_len
|
47
|
+
class << self
|
48
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
49
|
+
end
|
50
|
+
tcp_receive_from_localbackend_other_packet n
|
51
|
+
end
|
52
|
+
|
53
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
54
|
+
|
55
|
+
def tcp_receive_from_localbackend_other_packet n
|
56
|
+
@buffer << @cipher.decrypt(async_recv(-1), @recv_iv)
|
57
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
58
|
+
end
|
59
|
+
|
60
|
+
def tcp_send_to_localbackend_first_packet data
|
61
|
+
send_first_packet_process data
|
62
|
+
class << self
|
63
|
+
alias tcp_send_to_localbackend send_other_packet
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
alias tcp_send_to_localbackend tcp_send_to_localbackend_first_packet
|
68
|
+
|
69
|
+
|
70
|
+
def send_first_packet_process data
|
71
|
+
@send_iv = @cipher.random_iv
|
72
|
+
send_data @send_iv + @cipher.encrypt(data, @send_iv)
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_other_packet data
|
76
|
+
send_data @cipher.encrypt(data, @send_iv)
|
77
|
+
end
|
78
|
+
|
79
|
+
alias tcp_receive_from_client raise_me
|
80
|
+
alias tcp_send_to_client raise_me
|
81
|
+
#alias tcp_receive_from_remoteserver raise_me
|
82
|
+
#alias tcp_send_to_remoteserver raise_me
|
83
|
+
#alias tcp_receive_from_localbackend raise_me
|
84
|
+
#alias tcp_send_to_localbackend raise_me
|
85
|
+
alias tcp_receive_from_destination raise_me
|
86
|
+
alias tcp_send_to_destination raise_me
|
87
|
+
|
88
|
+
alias udp_receive_from_client raise_me
|
89
|
+
alias udp_send_to_client raise_me
|
90
|
+
alias udp_receive_from_remoteserver raise_me
|
91
|
+
alias udp_send_to_remoteserver raise_me
|
92
|
+
alias udp_receive_from_localbackend raise_me
|
93
|
+
alias udp_send_to_localbackend raise_me
|
94
|
+
alias udp_receive_from_destination raise_me
|
95
|
+
alias udp_send_to_destination raise_me
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
|
4
|
+
# To be used with protocols without an IV, like {Cipher::Table}
|
5
|
+
class NoIvCipherProtocol
|
6
|
+
include DummyHelper
|
7
|
+
|
8
|
+
attr_accessor :next_protocol
|
9
|
+
|
10
|
+
# @param [Hash] configuration parameters
|
11
|
+
# @option params [Cipher::Table] :cipher a cipher object without IV, +required+
|
12
|
+
def initialize params = {}
|
13
|
+
@cipher = params[:cipher] or raise ProtocolError, "params[:cipher] is required"
|
14
|
+
end
|
15
|
+
|
16
|
+
def tcp_receive_from_remoteserver n
|
17
|
+
@cipher.decrypt(async_recv n)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tcp_send_to_remoteserver data
|
21
|
+
send_data(@cipher.encrypt data)
|
22
|
+
end
|
23
|
+
|
24
|
+
def tcp_receive_from_localbackend n
|
25
|
+
@cipher.decrypt(async_recv n)
|
26
|
+
end
|
27
|
+
|
28
|
+
def tcp_send_to_localbackend data
|
29
|
+
send_data(@cipher.encrypt data)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
alias tcp_receive_from_client raise_me
|
35
|
+
alias tcp_send_to_client raise_me
|
36
|
+
#alias tcp_receive_from_remoteserver raise_me
|
37
|
+
#alias tcp_send_to_remoteserver raise_me
|
38
|
+
#alias tcp_receive_from_localbackend raise_me
|
39
|
+
#alias tcp_send_to_localbackend raise_me
|
40
|
+
alias tcp_receive_from_destination raise_me
|
41
|
+
alias tcp_send_to_destination raise_me
|
42
|
+
|
43
|
+
alias udp_receive_from_client raise_me
|
44
|
+
alias udp_send_to_client raise_me
|
45
|
+
alias udp_receive_from_remoteserver raise_me
|
46
|
+
alias udp_send_to_remoteserver raise_me
|
47
|
+
alias udp_receive_from_localbackend raise_me
|
48
|
+
alias udp_send_to_localbackend raise_me
|
49
|
+
alias udp_receive_from_destination raise_me
|
50
|
+
alias udp_send_to_destination raise_me
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
# Origin shadowsocks protocols with One Time Authenticate
|
4
|
+
#
|
5
|
+
# specification: https://shadowsocks.org/en/spec/protocol.html
|
6
|
+
class VerifySha1Protocol
|
7
|
+
include DummyHelper
|
8
|
+
include BufferHelper
|
9
|
+
|
10
|
+
attr_accessor :next_protocol
|
11
|
+
|
12
|
+
ATYP_IPV4 = 1
|
13
|
+
ATYP_DOMAIN = 3
|
14
|
+
ATYP_IPV6 = 4
|
15
|
+
|
16
|
+
# @param [Hash] configuration parameters
|
17
|
+
# @option params [Cipher::OpenSSL, Cipher::RbNaCl, Cipher::RC4_MD5] :cipher a cipher object with IV and a key, +required+
|
18
|
+
# @option params [Boolean] :compatible compatibility with origin mode, default _true_
|
19
|
+
def initialize params = {}
|
20
|
+
@params = {:compatible => true}.merge(params)
|
21
|
+
@cipher = @params[:cipher] or raise ProtocolError, "params[:cipher] is required"
|
22
|
+
raise ProtocolError, "cipher object mush have an IV and a key" if @cipher.iv_len == 0 || @cipher.key == nil
|
23
|
+
@buffer = ""
|
24
|
+
@counter = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def tcp_receive_from_remoteserver_first_packet n
|
28
|
+
@recv_iv = async_recv(@cipher.iv_len)
|
29
|
+
class << self
|
30
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
|
31
|
+
end
|
32
|
+
tcp_receive_from_remoteserver_other_packet n
|
33
|
+
end
|
34
|
+
|
35
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_first_packet
|
36
|
+
|
37
|
+
def tcp_receive_from_remoteserver_other_packet n
|
38
|
+
@buffer << @cipher.decrypt(async_recv(-1), @recv_iv)
|
39
|
+
tcp_receive_from_remoteserver_other_packet_helper n
|
40
|
+
end
|
41
|
+
|
42
|
+
def tcp_send_to_remoteserver_first_packet data
|
43
|
+
data[0] = [0x10 | data.unpack("C").first].pack("C") # set ota flag
|
44
|
+
@send_iv = @cipher.random_iv
|
45
|
+
hmac = ShadowsocksRuby::Cipher.hmac_sha1_digest(@send_iv + @cipher.key, data)
|
46
|
+
send_data @send_iv + @cipher.encrypt(data + hmac, @send_iv)
|
47
|
+
class << self
|
48
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_other_packet
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_first_packet
|
53
|
+
|
54
|
+
def tcp_send_to_remoteserver_other_packet data
|
55
|
+
data = @cipher.encrypt(data, @send_iv)
|
56
|
+
hmac = ShadowsocksRuby::Cipher.hmac_sha1_digest(@send_iv + [@counter].pack("n"), data)
|
57
|
+
send_data [data.length].pack("n") << hmac << data
|
58
|
+
@counter += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def tcp_receive_from_localbackend_first_packet n
|
62
|
+
class << self
|
63
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
64
|
+
end
|
65
|
+
data = async_recv(-1) # first packet
|
66
|
+
@recv_iv = data.slice!(0, @cipher.iv_len)
|
67
|
+
data = @cipher.decrypt(data, @recv_iv)
|
68
|
+
@atyp = data.unpack("C")[0]
|
69
|
+
if @atyp & 0x10 == 0x10 # OTA mode
|
70
|
+
hmac = data[-10, 10]
|
71
|
+
raise PharseError, "hmac_sha1 is not correct" \
|
72
|
+
unless ShadowsocksRuby::Cipher.hmac_sha1_digest(@recv_iv + @cipher.key, data[0 ... -10]) == hmac
|
73
|
+
data[0] = [0x0F & @atyp].pack("C") # clear ota flag
|
74
|
+
@buffer << data[0 ... -10]
|
75
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
76
|
+
else # origin mode
|
77
|
+
if @params[:compatible] == false
|
78
|
+
raise PharseError, "invalid OTA first packet in strict OTA mode"
|
79
|
+
end
|
80
|
+
@buffer << data
|
81
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
87
|
+
|
88
|
+
def tcp_receive_from_localbackend_other_packet n
|
89
|
+
if @atyp & 0x10 == 0x10 # OTA mode
|
90
|
+
len = async_recv(2).unpack("n").first
|
91
|
+
hmac = async_recv(10)
|
92
|
+
data = async_recv(len)
|
93
|
+
raise PharseError, "hmac_sha1 is not correct" \
|
94
|
+
unless ShadowsocksRuby::Cipher.hmac_sha1_digest(@recv_iv + [@counter].pack("n"), data) == hmac
|
95
|
+
@buffer << @cipher.decrypt(data, @recv_iv)
|
96
|
+
@counter += 1
|
97
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
98
|
+
else # origin mode
|
99
|
+
@buffer << @cipher.decrypt(async_recv(-1), @recv_iv)
|
100
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def tcp_send_to_localbackend_first_packet data
|
105
|
+
@send_iv = @cipher.random_iv
|
106
|
+
send_data @send_iv + @cipher.encrypt(data, @send_iv)
|
107
|
+
class << self
|
108
|
+
alias tcp_send_to_localbackend tcp_send_to_localbackend_other_packet
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
alias tcp_send_to_localbackend tcp_send_to_localbackend_first_packet
|
113
|
+
|
114
|
+
def tcp_send_to_localbackend_other_packet data
|
115
|
+
send_data @cipher.encrypt(data, @send_iv)
|
116
|
+
end
|
117
|
+
|
118
|
+
alias tcp_receive_from_client raise_me
|
119
|
+
alias tcp_send_to_client raise_me
|
120
|
+
#alias tcp_receive_from_remoteserver raise_me
|
121
|
+
#alias tcp_send_to_remoteserver raise_me
|
122
|
+
#alias tcp_receive_from_localbackend raise_me
|
123
|
+
#alias tcp_send_to_localbackend raise_me
|
124
|
+
alias tcp_receive_from_destination raise_me
|
125
|
+
alias tcp_send_to_destination raise_me
|
126
|
+
|
127
|
+
alias udp_receive_from_client raise_me
|
128
|
+
alias udp_send_to_client raise_me
|
129
|
+
alias udp_receive_from_remoteserver raise_me
|
130
|
+
alias udp_send_to_remoteserver raise_me
|
131
|
+
alias udp_receive_from_localbackend raise_me
|
132
|
+
alias udp_send_to_localbackend raise_me
|
133
|
+
alias udp_receive_from_destination raise_me
|
134
|
+
alias udp_send_to_destination raise_me
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Protocols
|
3
|
+
# Http Simple Obfuscation Protocol
|
4
|
+
#
|
5
|
+
# Specification:
|
6
|
+
# * https://github.com/shadowsocksr/shadowsocksr/blob/manyuser/shadowsocks/obfsplugin/http_simple.py
|
7
|
+
# * https://github.com/shadowsocksr/obfsplugin/blob/master/c/http_simple.c
|
8
|
+
class HttpSimpleProtocol
|
9
|
+
include DummyHelper
|
10
|
+
include BufferHelper
|
11
|
+
|
12
|
+
attr_accessor :next_protocol
|
13
|
+
|
14
|
+
USER_AGENTS = ["Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
|
15
|
+
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0",
|
16
|
+
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
|
17
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36",
|
18
|
+
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
|
19
|
+
"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
|
20
|
+
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
|
21
|
+
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
|
22
|
+
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
|
23
|
+
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
|
24
|
+
"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
25
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"]
|
26
|
+
|
27
|
+
# @param [Hash] configuration parameters
|
28
|
+
# @option params [String] :host shadowsocks server address, required by remoteserver protocol
|
29
|
+
# @option params [String] :port shadowsocks server port, required by remoteserver protocol
|
30
|
+
# @option params [Boolean] :compatible compatibility with origin mode, default _true_
|
31
|
+
# @option params [String] :obfs_param obfs param, optional
|
32
|
+
def initialize params = {}
|
33
|
+
@params = {:compatible => true }.merge(params)
|
34
|
+
@buffer = ''
|
35
|
+
end
|
36
|
+
|
37
|
+
def tcp_receive_from_remoteserver_first_packet n
|
38
|
+
class << self
|
39
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
|
40
|
+
end
|
41
|
+
|
42
|
+
async_recv_until("\r\n\r\n")
|
43
|
+
async_recv n
|
44
|
+
end
|
45
|
+
|
46
|
+
alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_first_packet
|
47
|
+
|
48
|
+
def tcp_receive_from_remoteserver_other_packet n
|
49
|
+
async_recv n
|
50
|
+
end
|
51
|
+
|
52
|
+
def tcp_send_to_remoteserver_first_packet data
|
53
|
+
if data.length > 30 + 64
|
54
|
+
headlen = 30 + Random.rand(64 + 1)
|
55
|
+
else
|
56
|
+
headlen = data.length
|
57
|
+
end
|
58
|
+
headdata = data.slice!(0, headlen)
|
59
|
+
port = ''
|
60
|
+
raise ProtocolError, "No :port params" if @params[:port] == nil
|
61
|
+
if @params[:port] != 80
|
62
|
+
port = ':' << @params[:port].to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
body = nil
|
66
|
+
hosts = @params[:obfs_param] || @params[:host]
|
67
|
+
raise ProtocolError, "No :host or :obfs_param parameters" if hosts == nil
|
68
|
+
if hosts.include?('#')
|
69
|
+
hosts, body = hosts.split('#')
|
70
|
+
body = body.gsub(/\n/,"\r\n")
|
71
|
+
body = body.gsub(/\\n/,"\r\n")
|
72
|
+
end
|
73
|
+
hosts = hosts.split(",")
|
74
|
+
host = hosts[Random.rand(hosts.length)]
|
75
|
+
|
76
|
+
http_head = "GET /" << headdata.unpack("H*")[0].gsub(/../,'%\0') << " HTTP/1.1\r\n"
|
77
|
+
http_head << "Host: " << host << port << "\r\n"
|
78
|
+
|
79
|
+
if body != nil
|
80
|
+
http_head << body << "\r\n\r\n"
|
81
|
+
else
|
82
|
+
http_head << "User-Agent: " + USER_AGENTS[Random.rand(USER_AGENTS.length)] << "\r\n"
|
83
|
+
http_head << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n"
|
84
|
+
end
|
85
|
+
send_data(http_head << data)
|
86
|
+
class << self
|
87
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_other_packet
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
alias tcp_send_to_remoteserver tcp_send_to_remoteserver_first_packet
|
93
|
+
|
94
|
+
def tcp_send_to_remoteserver_other_packet data
|
95
|
+
send_data data
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def tcp_receive_from_localbackend_first_packet n
|
100
|
+
data = async_recv 10
|
101
|
+
if (data =~ /^GET|^POST/) == nil
|
102
|
+
if @params[:compatible]
|
103
|
+
@buffer << data << async_recv(n - data.length)
|
104
|
+
else
|
105
|
+
raise PharseError, "not a valid http_simple first packet in strict mode"
|
106
|
+
end
|
107
|
+
else
|
108
|
+
buf = ""
|
109
|
+
buf << data << async_recv_until("\r\n\r\n")
|
110
|
+
host = get_host_from_http_header(buf)
|
111
|
+
if host != nil && @params[:obfs_param] != nil
|
112
|
+
hosts = @params[:obfs_param]
|
113
|
+
if hosts.include?('#')
|
114
|
+
hosts, body = hosts.split('#')
|
115
|
+
body = body.gsub(/\n/,"\r\n")
|
116
|
+
body = body.gsub(/\\n/,"\r\n")
|
117
|
+
end
|
118
|
+
hosts = hosts.split(",")
|
119
|
+
if !hosts.include?(host)
|
120
|
+
raise PharseError, "request host not in :obfs_param"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
ret_buf = get_data_from_http_header(buf)
|
124
|
+
raise PharseError, "not a valid request" if ret_buf.length < 4
|
125
|
+
@buffer << ret_buf
|
126
|
+
end
|
127
|
+
|
128
|
+
class << self
|
129
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
|
130
|
+
end
|
131
|
+
tcp_receive_from_localbackend_other_packet_helper n
|
132
|
+
end
|
133
|
+
|
134
|
+
alias tcp_receive_from_localbackend tcp_receive_from_localbackend_first_packet
|
135
|
+
|
136
|
+
def tcp_receive_from_localbackend_other_packet n
|
137
|
+
async_recv(n)
|
138
|
+
end
|
139
|
+
|
140
|
+
def tcp_send_to_localbackend_first_packet data
|
141
|
+
class << self
|
142
|
+
alias tcp_send_to_localbackend tcp_send_to_localbackend_other_packet
|
143
|
+
end
|
144
|
+
|
145
|
+
header = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: "
|
146
|
+
header << Time.now.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
147
|
+
header << "\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n"
|
148
|
+
send_data header << data
|
149
|
+
end
|
150
|
+
|
151
|
+
alias tcp_send_to_localbackend tcp_send_to_localbackend_first_packet
|
152
|
+
|
153
|
+
def tcp_send_to_localbackend_other_packet data
|
154
|
+
send_data data
|
155
|
+
end
|
156
|
+
|
157
|
+
# helpers
|
158
|
+
def get_data_from_http_header buf
|
159
|
+
[buf.split("\r\n")[0][/(%..)+/].gsub(/%/,'')].pack("H*")
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_host_from_http_header buf
|
163
|
+
buf[/^Host: (.+):/,1]
|
164
|
+
end
|
165
|
+
|
166
|
+
def async_recv_until str
|
167
|
+
@next_protocol.async_recv_until str
|
168
|
+
end
|
169
|
+
|
170
|
+
alias tcp_receive_from_client raise_me
|
171
|
+
alias tcp_send_to_client raise_me
|
172
|
+
#alias tcp_receive_from_remoteserver raise_me
|
173
|
+
#alias tcp_send_to_remoteserver raise_me
|
174
|
+
#alias tcp_receive_from_localbackend raise_me
|
175
|
+
#alias tcp_send_to_localbackend raise_me
|
176
|
+
alias tcp_receive_from_destination raise_me
|
177
|
+
alias tcp_send_to_destination raise_me
|
178
|
+
|
179
|
+
alias udp_receive_from_client raise_me
|
180
|
+
alias udp_send_to_client raise_me
|
181
|
+
alias udp_receive_from_remoteserver raise_me
|
182
|
+
alias udp_send_to_remoteserver raise_me
|
183
|
+
alias udp_receive_from_localbackend raise_me
|
184
|
+
alias udp_send_to_localbackend raise_me
|
185
|
+
alias udp_receive_from_destination raise_me
|
186
|
+
alias udp_send_to_destination raise_me
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|