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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/.yardopts +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +10 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +160 -0
  10. data/Rakefile +14 -0
  11. data/bin/aruba +17 -0
  12. data/bin/bundler +17 -0
  13. data/bin/console +14 -0
  14. data/bin/cucumber +17 -0
  15. data/bin/einhorn +17 -0
  16. data/bin/einhornsh +17 -0
  17. data/bin/htmldiff +17 -0
  18. data/bin/ldiff +17 -0
  19. data/bin/lrucache_server +16 -0
  20. data/bin/rake +17 -0
  21. data/bin/rspec +17 -0
  22. data/bin/setup +8 -0
  23. data/bin/yard +17 -0
  24. data/bin/yardoc +17 -0
  25. data/bin/yri +17 -0
  26. data/config.example.json +6 -0
  27. data/exe/sslocal-ruby +4 -0
  28. data/exe/ssserver-ruby +4 -0
  29. data/lib/shadowsocks_ruby/app.rb +236 -0
  30. data/lib/shadowsocks_ruby/cipher/cipher.rb +112 -0
  31. data/lib/shadowsocks_ruby/cipher/openssl.rb +81 -0
  32. data/lib/shadowsocks_ruby/cipher/rbnacl.rb +64 -0
  33. data/lib/shadowsocks_ruby/cipher/rc4_md5.rb +54 -0
  34. data/lib/shadowsocks_ruby/cipher/table.rb +65 -0
  35. data/lib/shadowsocks_ruby/cli/sslocal_runner.rb +125 -0
  36. data/lib/shadowsocks_ruby/cli/ssserver_runner.rb +115 -0
  37. data/lib/shadowsocks_ruby/connections/backend_connection.rb +50 -0
  38. data/lib/shadowsocks_ruby/connections/connection.rb +195 -0
  39. data/lib/shadowsocks_ruby/connections/server_connection.rb +63 -0
  40. data/lib/shadowsocks_ruby/connections/tcp/client_connection.rb +43 -0
  41. data/lib/shadowsocks_ruby/connections/tcp/destination_connection.rb +19 -0
  42. data/lib/shadowsocks_ruby/connections/tcp/localbackend_connection.rb +29 -0
  43. data/lib/shadowsocks_ruby/connections/tcp/remoteserver_connection.rb +18 -0
  44. data/lib/shadowsocks_ruby/connections/udp/client_connection.rb +43 -0
  45. data/lib/shadowsocks_ruby/connections/udp/destination_connection.rb +18 -0
  46. data/lib/shadowsocks_ruby/connections/udp/localbackend_connection.rb +29 -0
  47. data/lib/shadowsocks_ruby/connections/udp/remoteserver_connection.rb +18 -0
  48. data/lib/shadowsocks_ruby/protocols/cipher/iv_cipher.rb +99 -0
  49. data/lib/shadowsocks_ruby/protocols/cipher/no_iv_cipher.rb +53 -0
  50. data/lib/shadowsocks_ruby/protocols/cipher/verify_sha1.rb +138 -0
  51. data/lib/shadowsocks_ruby/protocols/obfs/http_simple.rb +189 -0
  52. data/lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb +342 -0
  53. data/lib/shadowsocks_ruby/protocols/packet/plain.rb +51 -0
  54. data/lib/shadowsocks_ruby/protocols/packet/shadowsocks.rb +88 -0
  55. data/lib/shadowsocks_ruby/protocols/packet/socks5.rb +162 -0
  56. data/lib/shadowsocks_ruby/protocols/protocol.rb +164 -0
  57. data/lib/shadowsocks_ruby/protocols/protocol_stack.rb +117 -0
  58. data/lib/shadowsocks_ruby/util.rb +52 -0
  59. data/lib/shadowsocks_ruby/version.rb +3 -0
  60. data/lib/shadowsocks_ruby.rb +86 -0
  61. data/shadowsocks_ruby.gemspec +48 -0
  62. 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