shadowsocks_ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|