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,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
|