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,125 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module ShadowsocksRuby
|
4
|
+
module Cli
|
5
|
+
class SslocalRunner
|
6
|
+
def initialize(argv = ARGV, stdin = $stdin, stdout = $stdout, stderr = $stderr, kernel = Kernel)
|
7
|
+
@argv = argv
|
8
|
+
$kernel = kernel
|
9
|
+
$stdin = stdin
|
10
|
+
$stdout = stdout
|
11
|
+
$stderr = stderr
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute!
|
15
|
+
# here sets default options
|
16
|
+
options = {
|
17
|
+
:port => 8388,
|
18
|
+
:local_addr => '0.0.0.0',
|
19
|
+
:local_port => 1080,
|
20
|
+
:packet_name => 'origin',
|
21
|
+
:cipher_name => 'aes-256-cfb',
|
22
|
+
:timeout => 300
|
23
|
+
}
|
24
|
+
lazy_default = {
|
25
|
+
:config => 'config.json',
|
26
|
+
:obfs_name => 'http_simple'
|
27
|
+
}
|
28
|
+
|
29
|
+
version = ShadowsocksRuby::VERSION
|
30
|
+
config_help = "path to config file (lazy default: #{lazy_default[:config]})"
|
31
|
+
server_addr_help = "server address"
|
32
|
+
server_port_help = "server port (default: #{options[:port]})"
|
33
|
+
local_addr_help = "local binding address (default: #{options[:local_addr]})"
|
34
|
+
local_port_help = "local port (default: default: #{options[:local_port]})"
|
35
|
+
password_help = "password"
|
36
|
+
packet_protocol_help = "packet protocol (default: #{options[:packet_name]})"
|
37
|
+
packet_param_help = "packet protocol parameters"
|
38
|
+
cipher_help = "cipher protocol (default: #{options[:cipher_name]})"
|
39
|
+
obfs_protocol_help = "obfuscate protocol (lazy default: #{lazy_default[:obfs_name]})"
|
40
|
+
obfs_param_help = "obfuscate protocol parameters"
|
41
|
+
timeout_help = "timeout in seconds, default: #{options[:timeout]}"
|
42
|
+
fast_open_help = "use TCP_FASTOPEN, requires Linux 3.7+"
|
43
|
+
einhorn_help = "Use Einhorn socket manager"
|
44
|
+
|
45
|
+
help_help = "display help message"
|
46
|
+
version_help = "show version information"
|
47
|
+
verbose_help = "verbose mode"
|
48
|
+
quiet_help = "quiet mode, only show warnings/errors"
|
49
|
+
get_help = "run #{File.basename($0)} -h to get help"
|
50
|
+
|
51
|
+
option_parser = OptionParser.new do |op|
|
52
|
+
|
53
|
+
op.banner = "A SOCKS like tunnel proxy that helps you bypass firewalls."
|
54
|
+
op.separator ""
|
55
|
+
op.separator "Usage: #{File.basename($0)} [options]"
|
56
|
+
op.separator ""
|
57
|
+
|
58
|
+
op.separator "Proxy options:"
|
59
|
+
op.on("-c", "--config [CONFIG]", config_help) { |value| options[:config] = value }
|
60
|
+
op.on("-s", "--server SERVER_ADDR", server_addr_help) { |value| options[:server] = value }
|
61
|
+
op.on("-p", "--port SERVER_PORT", server_port_help) { |value| options[:port] = value.to_i }
|
62
|
+
op.on("-b", "--bind_addr LOCAL_ADDR", local_addr_help) { |value| options[:local_addr] = value }
|
63
|
+
op.on("-l", "--local_port LOCAL_PORT", local_port_help) { |value| options[:local_port] = value.to_i }
|
64
|
+
op.on("-k", "--password PASSWORD", password_help) { |value| options[:password] = value }
|
65
|
+
op.on("-O", "--packet-protocol NAME", packet_protocol_help) { |value| options[:packet_name] = value }
|
66
|
+
op.on("-G", "--packet-param PARAM", packet_param_help) { |value| options[:packet_param] = value }
|
67
|
+
op.on("-m", "--cipher-protocol NAME", cipher_help) { |value| options[:cipher_name] = value }
|
68
|
+
op.on("-o", "--obfs-protocol [NAME]", obfs_protocol_help) { |value| options[:obfs_name] = value }
|
69
|
+
op.on("-g", "--obfs-param PARAM", obfs_param_help) { |value| options[:obfs_param] = value }
|
70
|
+
op.on("-t", "--timeout TIMEOUT", timeout_help) { |value| options[:timeout] = value.to_i }
|
71
|
+
op.on( "--fast-open", fast_open_help) { |value| options[:tcp_fast_open] = value }
|
72
|
+
op.on('-E', '--einhorn', einhorn_help) { |value| @options[:einhorn] = true }
|
73
|
+
op.separator ""
|
74
|
+
|
75
|
+
op.separator "Common options:"
|
76
|
+
op.on("-h", "--help", help_help) { puts op.to_s; $kernel.exit }
|
77
|
+
op.on("-v", "--vv", verbose_help) { options[:verbose] = true }
|
78
|
+
op.on("-q", "--qq", quiet_help) { options[:quiet] = true }
|
79
|
+
op.on( "--version", version_help) { puts version; $kernel.exit }
|
80
|
+
op.separator ""
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
option_parser.parse!(@argv)
|
86
|
+
rescue OptionParser::MissingArgument => e
|
87
|
+
$kernel.abort("#{e.message}\n#{get_help}")
|
88
|
+
end
|
89
|
+
|
90
|
+
if options.include?(:config)
|
91
|
+
options[:config] = 'config.json' if options[:config] == nil
|
92
|
+
config_options = {}
|
93
|
+
begin
|
94
|
+
open(options[:config], 'r') do |f|
|
95
|
+
config_options = JSON.load(f, nil, {:symbolize_names => true})
|
96
|
+
end
|
97
|
+
rescue Errno::ENOENT
|
98
|
+
$kernel.abort("#{options[:config]} doesn't exists")
|
99
|
+
rescue JSON::ParserError
|
100
|
+
$kernel.abort("#{options[:config]} parse error")
|
101
|
+
end
|
102
|
+
options = config_options.merge(options)
|
103
|
+
end
|
104
|
+
|
105
|
+
if options.include?(:obfs_name)
|
106
|
+
options[:obfs_name] = lazy_default[:obfs_name] if options[:obfs_name] == nil
|
107
|
+
end
|
108
|
+
|
109
|
+
if !options.include?(:password)
|
110
|
+
$kernel.abort("--password is required\n#{get_help}")
|
111
|
+
end
|
112
|
+
|
113
|
+
if !options.include?(:server)
|
114
|
+
$kernel.abort("--server is required\n#{get_help}")
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
options[:__client] = true
|
119
|
+
|
120
|
+
App.options = options
|
121
|
+
App.instance.run!
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module ShadowsocksRuby
|
4
|
+
module Cli
|
5
|
+
class SsserverRunner
|
6
|
+
def initialize(argv = ARGV, stdin = $stdin, stdout = $stdout, stderr = $stderr, kernel = Kernel)
|
7
|
+
@argv = argv
|
8
|
+
$kernel = kernel
|
9
|
+
$stdin = stdin
|
10
|
+
$stdout = stdout
|
11
|
+
$stderr = stderr
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute!
|
15
|
+
# here sets default options
|
16
|
+
options = {
|
17
|
+
:server => '0.0.0.0',
|
18
|
+
:port => 8388,
|
19
|
+
:packet_name => 'origin',
|
20
|
+
:cipher_name => 'aes-256-cfb',
|
21
|
+
:timeout => 300
|
22
|
+
}
|
23
|
+
lazy_default = {
|
24
|
+
:config => 'config.json',
|
25
|
+
:obfs_name => 'http_simple'
|
26
|
+
}
|
27
|
+
|
28
|
+
version = ShadowsocksRuby::VERSION
|
29
|
+
config_help = "path to config file (lazy default: #{lazy_default[:config]})"
|
30
|
+
server_addr_help = "server address (default: #{options[:server]})"
|
31
|
+
server_port_help = "server port (default: #{options[:port]})"
|
32
|
+
password_help = "password"
|
33
|
+
packet_protocol_help = "packet protocol (default: #{options[:packet_name]})"
|
34
|
+
packet_param_help = "packet protocol parameters"
|
35
|
+
cipher_help = "cipher protocol (default: #{options[:cipher_name]})"
|
36
|
+
obfs_protocol_help = "obfuscate protocol (lazy default: #{lazy_default[:obfs_name]})"
|
37
|
+
obfs_param_help = "obfuscate protocol parameters"
|
38
|
+
timeout_help = "timeout in seconds, default: #{options[:timeout]}"
|
39
|
+
fast_open_help = "use TCP_FASTOPEN, requires Linux 3.7+"
|
40
|
+
einhorn_help = "Use Einhorn socket manager"
|
41
|
+
|
42
|
+
help_help = "display help message"
|
43
|
+
version_help = "show version information"
|
44
|
+
verbose_help = "verbose mode"
|
45
|
+
quiet_help = "quiet mode, only show warnings/errors"
|
46
|
+
get_help = "run #{File.basename($0)} -h to get help"
|
47
|
+
|
48
|
+
option_parser = OptionParser.new do |op|
|
49
|
+
|
50
|
+
op.banner = "A SOCKS like tunnel proxy that helps you bypass firewalls."
|
51
|
+
op.separator ""
|
52
|
+
op.separator "Usage: #{File.basename($0)} [options]"
|
53
|
+
op.separator ""
|
54
|
+
|
55
|
+
op.separator "Proxy options:"
|
56
|
+
op.on("-c", "--config [CONFIG]", config_help) { |value| options[:config] = value }
|
57
|
+
op.on("-s", "--server SERVER_ADDR", server_addr_help) { |value| options[:server] = value }
|
58
|
+
op.on("-p", "--port SERVER_PORT", server_port_help) { |value| options[:port] = value.to_i }
|
59
|
+
op.on("-k", "--password PASSWORD", password_help) { |value| options[:password] = value }
|
60
|
+
op.on("-O", "--packet-protocol NAME", packet_protocol_help) { |value| options[:packet_name] = value }
|
61
|
+
op.on("-G", "--packet-param PARAM", packet_param_help) { |value| options[:packet_param] = value }
|
62
|
+
op.on("-m", "--cipher-protocol NAME", cipher_help) { |value| options[:cipher_name] = value }
|
63
|
+
op.on("-o", "--obfs-protocol [NAME]", obfs_protocol_help) { |value| options[:obfs_name] = value }
|
64
|
+
op.on("-g", "--obfs-param PARAM", obfs_param_help) { |value| options[:obfs_param] = value }
|
65
|
+
op.on("-t", "--timeout TIMEOUT", timeout_help) { |value| options[:timeout] = value.to_i }
|
66
|
+
op.on( "--fast-open", fast_open_help) { |value| options[:tcp_fast_open] = value }
|
67
|
+
op.on('-E', '--einhorn', einhorn_help) { |value| @options[:einhorn] = true }
|
68
|
+
op.separator ""
|
69
|
+
|
70
|
+
op.separator "Common options:"
|
71
|
+
op.on("-h", "--help", help_help) { puts op.to_s; $kernel.exit }
|
72
|
+
op.on("-v", "--vv", verbose_help) { options[:verbose] = true }
|
73
|
+
op.on("-q", "--qq", quiet_help) { options[:quiet] = true }
|
74
|
+
op.on( "--version", version_help) { puts version; $kernel.exit }
|
75
|
+
op.separator ""
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
begin
|
80
|
+
option_parser.parse!(@argv)
|
81
|
+
rescue OptionParser::MissingArgument => e
|
82
|
+
$kernel.abort("#{e.message}\n#{get_help}")
|
83
|
+
end
|
84
|
+
|
85
|
+
if options.include?(:config)
|
86
|
+
options[:config] = lazy_default[:config] if options[:config] == nil
|
87
|
+
config_options = {}
|
88
|
+
begin
|
89
|
+
open(options[:config], 'r') do |f|
|
90
|
+
config_options = JSON.load(f, nil, {:symbolize_names => true})
|
91
|
+
end
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
$kernel.abort("#{options[:config]} doesn't exists")
|
94
|
+
rescue JSON::ParserError
|
95
|
+
$kernel.abort("#{options[:config]} parse error")
|
96
|
+
end
|
97
|
+
options = config_options.merge(options)
|
98
|
+
end
|
99
|
+
|
100
|
+
if options.include?(:obfs_name)
|
101
|
+
options[:obfs_name] = lazy_default[:obfs_name] if options[:obfs_name] == nil
|
102
|
+
end
|
103
|
+
|
104
|
+
if !options.include?(:password)
|
105
|
+
$kernel.abort("password is required\n#{get_help}")
|
106
|
+
end
|
107
|
+
|
108
|
+
options[:__server] = true
|
109
|
+
|
110
|
+
App.options = options
|
111
|
+
App.instance.run!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
# Mixed-in code to provide functionality to a BackendConnection
|
4
|
+
# ,whose peer is a upstream peer, like a Destinatiion or a RemoteServer.
|
5
|
+
module BackendConnection
|
6
|
+
|
7
|
+
# Packet Protocol
|
8
|
+
#
|
9
|
+
# A Strategy Pattern for replacing protocol algorithm
|
10
|
+
#
|
11
|
+
# all read from {BackendConnection}'s {packet_protocol} is just data,
|
12
|
+
#
|
13
|
+
# the first send to {packet_protocol} chould be address_bin (for a {RemoteServerConnection})
|
14
|
+
# or data (for a {DestinationConnection}),
|
15
|
+
# other send to {packet_protocol} should be just data
|
16
|
+
#
|
17
|
+
# @return [ShadowsocksRuby::Protocols::SomePacketProtocol]
|
18
|
+
# @see ShadowsocksRuby::Protocols
|
19
|
+
attr_accessor :packet_protocol
|
20
|
+
|
21
|
+
# (see ServerConnection#params)
|
22
|
+
attr_reader :params
|
23
|
+
|
24
|
+
# If clild class override initialize, make sure to call super
|
25
|
+
#
|
26
|
+
# @param [Protocols::ProtocolStack] protocol_stack
|
27
|
+
# @param [Hash] params
|
28
|
+
# @return [Connection_Object]
|
29
|
+
def initialize protocol_stack, params
|
30
|
+
super()
|
31
|
+
|
32
|
+
@packet_protocol = protocol_stack.build!(self)
|
33
|
+
|
34
|
+
@params = params
|
35
|
+
@connected = EM::DefaultDeferrable.new
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def connection_completed
|
40
|
+
@connected.succeed
|
41
|
+
end
|
42
|
+
|
43
|
+
# Buffer data until the connection to the backend server
|
44
|
+
# is established and is ready for use
|
45
|
+
def send_data data
|
46
|
+
@connected.callback { super data }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'fiber'
|
2
|
+
require 'ipaddr'
|
3
|
+
|
4
|
+
module ShadowsocksRuby
|
5
|
+
|
6
|
+
# This module contains various functionality code to be mixed-in
|
7
|
+
# with EventMachine::Connection
|
8
|
+
# when Connection object is instantiated.
|
9
|
+
#
|
10
|
+
# There are 4 kinds of connection: client, local backend, remote server and destination. Which are demonstrated below:
|
11
|
+
#
|
12
|
+
# ------------------------------------------- -------------------------------------------------
|
13
|
+
# | | | |
|
14
|
+
# Client <---> |ClientConnection -- RemoteServerConnecton| <---> |LocalBackendConnection -- DestinationConnection| <---> Destination
|
15
|
+
# net | Shadowsocks Client | net | Shadowsocks Server | net
|
16
|
+
# ------------------------------------------- -------------------------------------------------
|
17
|
+
#
|
18
|
+
#
|
19
|
+
module Connections
|
20
|
+
# Mixed-in code to provide fiber enabled asynchronously receive
|
21
|
+
# function and pressure controled +send_data+ to EventMachine::Connection
|
22
|
+
#
|
23
|
+
# User code should define +process_hook+ which hopefully implement a state machine .
|
24
|
+
#
|
25
|
+
# *Note:* User code should not override +post_init+ and +receive_data+, it is by design.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# class DummyConnection < EventMachine::Connection
|
29
|
+
# include ShadowsocksRuby::Connections::Connection
|
30
|
+
# def process_hook
|
31
|
+
# @i ||= 0
|
32
|
+
# @i += 1
|
33
|
+
# puts "I'm now in a fiber enabled context: #{@fiber}"
|
34
|
+
# Fiber.yield if @i >= 3
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
module Connection
|
39
|
+
# 512K, used to pause plexer when plexer.get_outbound_data_size > this value
|
40
|
+
PressureLevel = 524288
|
41
|
+
|
42
|
+
# It is where to relay peer's traffic to
|
43
|
+
# For a server connection, plexer is backend connection.
|
44
|
+
# For a backend connection, plexer is server connection.
|
45
|
+
# @return [Connection]
|
46
|
+
attr_accessor :plexer
|
47
|
+
|
48
|
+
# you can set logger in test code
|
49
|
+
attr_writer :logger
|
50
|
+
|
51
|
+
# get the logger object, the defautl logger is App.instance.logger
|
52
|
+
def logger
|
53
|
+
@logger ||= App.instance.logger
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# send_data with pressure control
|
58
|
+
# @param[String] data Data to send asynchronously
|
59
|
+
def send_data data
|
60
|
+
pressure_control
|
61
|
+
super data
|
62
|
+
end
|
63
|
+
|
64
|
+
# Call process_hook, which should be defined in user code
|
65
|
+
# @private
|
66
|
+
def process
|
67
|
+
process_hook
|
68
|
+
end
|
69
|
+
|
70
|
+
# Initialize a fiber context and enter the process loop
|
71
|
+
# normally, a child class should not override post_init, it is by design
|
72
|
+
# @private
|
73
|
+
def post_init
|
74
|
+
@buffer = String.new('', encoding: Encoding::ASCII_8BIT)
|
75
|
+
@fiber = Fiber.new do
|
76
|
+
# poor man's state machine
|
77
|
+
while true
|
78
|
+
process
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@fiber.resume
|
82
|
+
end
|
83
|
+
|
84
|
+
def peer
|
85
|
+
@peer ||=
|
86
|
+
begin
|
87
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
88
|
+
"#{ip}:#{port}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Asynchronously receive n bytes from @buffer
|
93
|
+
# @param [Integer] n Bytes to receive, if n = -1 returns all data in @buffer
|
94
|
+
# @return [String] Returned n bytes data
|
95
|
+
def async_recv n
|
96
|
+
# wait n bytes
|
97
|
+
if n == -1 && @buffer.size == 0 || @buffer.size < n
|
98
|
+
@wait_length = n
|
99
|
+
Fiber.yield
|
100
|
+
end
|
101
|
+
# read n bytes from buffer
|
102
|
+
if n == -1
|
103
|
+
s, @buffer = @buffer, String.new('', encoding: Encoding::ASCII_8BIT)
|
104
|
+
return s
|
105
|
+
else
|
106
|
+
return @buffer.slice!(0, n)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Asynchronously receive data until str (eg: "\\r\\n\r\\n") appears.
|
111
|
+
# @param [String] str Desired endding str
|
112
|
+
# @raise BufferOversizeError raise if cannot find str in first 65536 bytes (64K bytes)of @buffer,
|
113
|
+
# enough for a HTTP request head.
|
114
|
+
# @return [String] Returned data, with str at end
|
115
|
+
def async_recv_until str
|
116
|
+
# wait for str
|
117
|
+
pos = @buffer =~ Regexp.new(str)
|
118
|
+
while pos == nil
|
119
|
+
@wait_length = -1
|
120
|
+
Fiber.yield
|
121
|
+
pos = @buffer =~ Regexp.new(str)
|
122
|
+
raise BufferOversizeError, "oversized async_recv_until read" if @buffer.size > 65536
|
123
|
+
end
|
124
|
+
# read until str from buffer
|
125
|
+
return @buffer.slice!(0, pos + str.length)
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Provide fiber enabled data receiving, should be always be called
|
130
|
+
# in a fiber context.
|
131
|
+
#
|
132
|
+
# Normally, client class should not call receive_data directlly,
|
133
|
+
# instead should call async_recv or async_recv_until
|
134
|
+
#
|
135
|
+
# @param [String] data
|
136
|
+
# @raise OutOfFiberConextError
|
137
|
+
#
|
138
|
+
# @private
|
139
|
+
def receive_data data
|
140
|
+
if @fiber.alive?
|
141
|
+
@buffer << data
|
142
|
+
if @wait_length == -1 || @buffer.size >= @wait_length
|
143
|
+
@fiber.resume
|
144
|
+
end
|
145
|
+
else
|
146
|
+
raise OutOfFiberContextError, "should not go here"
|
147
|
+
end
|
148
|
+
rescue MyErrorModule => e
|
149
|
+
logger.info {e.message}
|
150
|
+
close_connection
|
151
|
+
rescue Exception => e
|
152
|
+
logger.info {e.class.to_s + " " + e.message + e.backtrace.join("\n")}
|
153
|
+
close_connection
|
154
|
+
end
|
155
|
+
|
156
|
+
# if peer receving data is too slow, pause plexer sending data to me
|
157
|
+
# ,prevent memery usage to be too high
|
158
|
+
# @private
|
159
|
+
def pressure_control
|
160
|
+
@plexer ||= nil
|
161
|
+
if @plexer != nil
|
162
|
+
if get_outbound_data_size >= PressureLevel
|
163
|
+
@plexer.pause unless @plexer.paused?
|
164
|
+
EventMachine.next_tick self.method(:pressure_control)
|
165
|
+
else
|
166
|
+
@plexer.resume if @plexer.paused?
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Close plexer first if it exists
|
172
|
+
def unbind
|
173
|
+
@plexer ||= nil
|
174
|
+
@plexer.close_connection_after_writing if @plexer != nil
|
175
|
+
end
|
176
|
+
|
177
|
+
alias tcp_receive_from_client async_recv
|
178
|
+
alias tcp_receive_from_remoteserver async_recv
|
179
|
+
alias tcp_receive_from_localbackend async_recv
|
180
|
+
alias tcp_receive_from_destination async_recv
|
181
|
+
alias tcp_send_to_client send_data
|
182
|
+
alias tcp_send_to_remoteserver send_data
|
183
|
+
alias tcp_send_to_localbackend send_data
|
184
|
+
alias tcp_send_to_destination send_data
|
185
|
+
alias udp_receive_from_client async_recv
|
186
|
+
alias udp_receive_from_remoteserver async_recv
|
187
|
+
alias udp_receive_from_localbackend async_recv
|
188
|
+
alias udp_receive_from_destination async_recv
|
189
|
+
alias udp_send_to_client send_data
|
190
|
+
alias udp_send_to_remoteserver send_data
|
191
|
+
alias udp_send_to_localbackend send_data
|
192
|
+
alias udp_send_to_destination send_data
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
|
4
|
+
# Mixed-in code to provide functionality to a ServerConnection
|
5
|
+
# ,whose peer is a downstream peer, like a Client or a LocalBackend.
|
6
|
+
module ServerConnection
|
7
|
+
|
8
|
+
# Packet Protocol
|
9
|
+
#
|
10
|
+
# A Strategy Pattern for replacing protocol algorithm
|
11
|
+
#
|
12
|
+
# the first read from {ServerConnection}'s {packet_protocol} is an address_bin,
|
13
|
+
# other read from {packet_protocol} is just data
|
14
|
+
#
|
15
|
+
# all send to {packet_protocol} should be just data
|
16
|
+
#
|
17
|
+
# @return [ShadowsocksRuby::Protocols::SomePacketProtocol]
|
18
|
+
# @see ShadowsocksRuby::Protocols
|
19
|
+
attr_accessor :packet_protocol
|
20
|
+
|
21
|
+
# Params
|
22
|
+
# @return [Hash]
|
23
|
+
attr_reader :params
|
24
|
+
|
25
|
+
# If clild class override initialize, make sure to call super
|
26
|
+
#
|
27
|
+
# @param [Protocols::ProtocolStack] protocol_stack
|
28
|
+
# @param [Hash] params
|
29
|
+
# @param [Protocols::ProtocolStack] backend_protocol_stack
|
30
|
+
# @param [Hash] backend_params
|
31
|
+
# @return [Connection_Object]
|
32
|
+
def initialize protocol_stack, params, backend_protocol_stack, backend_params
|
33
|
+
super()
|
34
|
+
|
35
|
+
@packet_protocol = protocol_stack.build!(self)
|
36
|
+
|
37
|
+
@params = params
|
38
|
+
|
39
|
+
@backend_protocol_stack = backend_protocol_stack
|
40
|
+
@backend_params = backend_params
|
41
|
+
end
|
42
|
+
|
43
|
+
def post_init
|
44
|
+
logger.info {"Accepted #{peer}"}
|
45
|
+
super()
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create a plexer -- a backend connection
|
49
|
+
# @param [String] host
|
50
|
+
# @param [Integer] port
|
51
|
+
# @param [Class] backend_klass
|
52
|
+
# @return [Connection_Object]
|
53
|
+
def create_plexer(host, port, backend_klass)
|
54
|
+
@plexer = EventMachine.connect host, port, backend_klass, @backend_protocol_stack, @backend_params
|
55
|
+
@plexer.plexer = self
|
56
|
+
@plexer
|
57
|
+
rescue EventMachine::ConnectionError => e
|
58
|
+
raise ConnectionError, e.message + " when connect to #{host}:#{port}"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
# Provides various functionality code of a TCP Connection.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# class DummyConnection < EventMachine::Connection
|
7
|
+
# include ShadowsocksRuby::Connections::TCP::ClientConnection
|
8
|
+
# end
|
9
|
+
# # some how get a DummyConnection object
|
10
|
+
# # dummy_connection = ...
|
11
|
+
# # dummy_connection.plexer_protocol.tcp_process_client will be called looply
|
12
|
+
module TCP
|
13
|
+
# Mixed-in with an EventMachine::Connection Object to use this.
|
14
|
+
module ClientConnection
|
15
|
+
include ShadowsocksRuby::Connections::Connection
|
16
|
+
include ShadowsocksRuby::Connections::ServerConnection
|
17
|
+
# (see ServerConnection#initialize)
|
18
|
+
# @option params [String] :host shadowsocks server address, required
|
19
|
+
# @option params [Integer] :port shadowsocks server port, required
|
20
|
+
def initialize protocol_stack, params, backend_protocol_stack, backend_params
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_first_packet
|
25
|
+
address_bin = packet_protocol.tcp_receive_from_client(-1)
|
26
|
+
create_plexer(@params[:host], @params[:port], RemoteServerConnection)
|
27
|
+
plexer.packet_protocol.tcp_send_to_remoteserver address_bin
|
28
|
+
class << self
|
29
|
+
alias process_hook process_other_packet
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is Called by process loop
|
34
|
+
alias process_hook process_first_packet
|
35
|
+
|
36
|
+
def process_other_packet
|
37
|
+
data = packet_protocol.tcp_receive_from_client(-1)
|
38
|
+
plexer.packet_protocol.tcp_send_to_remoteserver(data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
module TCP
|
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
|
+
#plexer.packet_protocol.tcp_send_to_localbackend(packet_protocol.tcp_receive_from_destination(-1))
|
12
|
+
data = packet_protocol.tcp_receive_from_destination(-1)
|
13
|
+
plexer.packet_protocol.tcp_send_to_localbackend(data)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ShadowsocksRuby
|
2
|
+
module Connections
|
3
|
+
module TCP
|
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.tcp_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.tcp_receive_from_localbackend(-1)
|
23
|
+
plexer.packet_protocol.tcp_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 TCP
|
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.tcp_receive_from_remoteserver(-1)
|
12
|
+
plexer.packet_protocol.tcp_send_to_client(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|