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,164 @@
1
+ module ShadowsocksRuby
2
+ # Protocols are layered. Each layer know nothing about other layers.
3
+ # (data) | TOP: HTTP, FTP, STMP ... ^ (data)
4
+ # Pack | layer 3 protocol packet | Unpack
5
+ # Encrypt | layer 2 protocol cipher | Decrypt
6
+ # Obfuscate | layer 1 protocol obfuscate | DeObfuscate
7
+ # (opaque) V Bottom: TCP/UDP | (opaque)
8
+ #
9
+ #
10
+ # === Protocols
11
+ # There are 16 hooks can be called on protocols, which are:
12
+ # * tcp_receive_from_client
13
+ # * tcp_send_to_client
14
+ # * tcp_receive_from_remoteserver
15
+ # * tcp_send_to_remoteserver
16
+ # * tcp_receive_from_localbackend
17
+ # * tcp_send_to_localbackend
18
+ # * tcp_receive_from_destination
19
+ # * tcp_send_to_destination
20
+ # * udp_receive_from_client
21
+ # * udp_send_to_client
22
+ # * udp_receive_from_remoteserver
23
+ # * udp_send_to_remoteserver
24
+ # * udp_receive_from_localbackend
25
+ # * udp_send_to_localbackend
26
+ # * udp_receive_from_destination
27
+ # * udp_send_to_destination
28
+ #
29
+ # Each receive_from hook's job is to return data of exact length required by upper level protocol.
30
+ #
31
+ # Each send_to hook's job is to get the send work done, with the data it received from the upper level protocol.
32
+ #
33
+ # Each hook can call next layer protocols's corresponding receive_from and send_to hook,
34
+ #
35
+ # The bottom layer calls +Connection+'s corresponding receive_from and send_to hook.
36
+ #
37
+ # The +Connection+ then map corresponding receive_from and send_to hook to real data transfer
38
+ # methods +async_recv+ and +send_data+.
39
+ #
40
+ #
41
+ # @see Connections
42
+ # @see ProtocolStack
43
+ module Protocols
44
+
45
+ # This module include helper methods for a Packet/Cipher/Obfs Protocol.
46
+ #
47
+ # To simplify boring long method name writing (eg: tcp_receive_from_client, tcp_send_to_client),
48
+ # two Adapter method are introduced and could be untilized:
49
+ # +#async_recv+ and +#send_data+,
50
+ # they will be adapted to long method names (eg: tcp_receive_from_client, tcp_send_to_client) at runtime.
51
+ #
52
+ # This helper module implement dummy {#async_recv} and {#send_data}, do nothing but just raise,
53
+ # in order to make things clear.
54
+ #
55
+ # To use it, use +include DummyHelper+ to include it into your protocol implementation.
56
+ module DummyHelper
57
+
58
+ # Receive n bytes of data
59
+ # @param[Integer] n length of data to receive
60
+ # @raise ProtocolError
61
+ # @return [String] Should return real data on runtime
62
+ def async_recv n
63
+ raise ProtocolError, "async_recv must be set before use"
64
+ end
65
+
66
+ # Send data
67
+ # @param[String] data data to send
68
+ # @raise ProtocolError
69
+ def send_data data
70
+ raise ProtocolError, "send_data must be set before use"
71
+ end
72
+
73
+ # If user code don't want to implement a method, it can call this to raise Error.
74
+ # @raise UnimplementError
75
+ def raise_me *args
76
+ raise UnimplementError, "Some day may implement this: " + caller[0][/`.*'/][1..-2]
77
+ end
78
+ end
79
+
80
+ # This module include helper methods for deal with @buffer.
81
+ #
82
+ module BufferHelper
83
+ def tcp_receive_from_remoteserver_other_packet_helper n
84
+ if n == -1
85
+ return @buffer.slice!(0, @buffer.length)
86
+ elsif n < @buffer.length
87
+ class << self
88
+ alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_in_buffer
89
+ end
90
+ return @buffer.slice!(0, n)
91
+ else
92
+ if n == @buffer.length
93
+ return @buffer.slice!(0, n)
94
+ else
95
+ data = async_recv(n - @buffer.length)
96
+ return @buffer.slice!(0, n) << data
97
+ end
98
+ end
99
+ end
100
+
101
+ def tcp_receive_from_remoteserver_in_buffer n
102
+ if n == -1
103
+ class << self
104
+ alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
105
+ end
106
+ return @buffer.slice!(0, @buffer.length)
107
+ elsif n < @buffer.length
108
+ return @buffer.slice!(0, n)
109
+ else
110
+ class << self
111
+ alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
112
+ end
113
+ if n == @buffer.length
114
+ return @buffer.slice!(0, n)
115
+ else
116
+ data = async_recv(n - @buffer.length)
117
+ return @buffer.slice!(0, n) << data
118
+ end
119
+ end
120
+ end
121
+
122
+ def tcp_receive_from_localbackend_other_packet_helper n
123
+ if n == -1
124
+ return @buffer.slice!(0, @buffer.length)
125
+ elsif n < @buffer.length
126
+ class << self
127
+ alias tcp_receive_from_localbackend tcp_receive_from_localbackend_in_buffer
128
+ end
129
+ return @buffer.slice!(0, n)
130
+ else
131
+ if n == @buffer.length
132
+ return @buffer.slice!(0, n)
133
+ else
134
+ data = async_recv(n - @buffer.length)
135
+ return @buffer.slice!(0, n) << data
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ def tcp_receive_from_localbackend_in_buffer n
142
+ if n == -1
143
+ class << self
144
+ alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
145
+ end
146
+ return @buffer.slice!(0, @buffer.length)
147
+ elsif n < @buffer.length
148
+ return @buffer.slice!(0, n)
149
+ else
150
+ class << self
151
+ alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
152
+ end
153
+ if n == @buffer.length
154
+ return @buffer.slice!(0, n)
155
+ else
156
+ data = async_recv(n - @buffer.length)
157
+ return @buffer.slice!(0, n) << data
158
+ end
159
+ end
160
+ end
161
+
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,117 @@
1
+ require "to_camel_case"
2
+
3
+ module ShadowsocksRuby
4
+ module Protocols
5
+
6
+ # Factory class for build protocol stacks
7
+ #
8
+ # @see Protocols
9
+ class ProtocolStack
10
+
11
+ # @example This is what a cfg_ary Array look like
12
+ # [
13
+ # ["some_packet_protocol_name"], # a packet protocol is required
14
+ # ["some_cipher_protocol_name", {:cipher => "some_cipher_object"}], # a cipher protocol is optional
15
+ # ["some_obfs_protocol_name", {:obfs_params => "..."}] # a obfs protocol is optional
16
+ # ]
17
+ #
18
+ # @param [Array] cfg_ary
19
+ # @param [String] cipher_name
20
+ # @param [password] password
21
+ def initialize cfg_ary, cipher_name, password
22
+ @cfg_ary = cfg_ary.map do |protocol_name, param|
23
+ protocol_class = protocol_name.to_camel_case + "Protocol"
24
+ protocol_class = Protocols.const_get(protocol_class)
25
+ [protocol_class, param ||= {}]
26
+ end
27
+ @cipher_name = cipher_name
28
+ @password = password
29
+ end
30
+
31
+ # Factory method for build a protocol stack
32
+ #
33
+ # @param [EventMachine::Connection] conn
34
+ # @return top protocol (packet protocol) in the stack
35
+ def build! conn
36
+ cipher = nil
37
+
38
+ protocols = @cfg_ary.map do | klass, params|
39
+ case klass.to_s
40
+ when Protocols::PlainProtocol.to_s, Protocols::Socks5Protocol.to_s, \
41
+ Protocols::ShadowsocksProtocol.to_s, Protocols::HttpSimpleProtocol.to_s
42
+ # do nothing, these are known protocols don't need cipher
43
+ when Protocols::TlsTicketProtocol.to_s
44
+ # this protocol need a key
45
+ params = {:key => (cipher ||= Cipher.build(@cipher_name, @password)).key}.merge(params)
46
+ else
47
+ params = {:cipher => (cipher ||= Cipher.build(@cipher_name, @password))}.merge(params)
48
+ end
49
+ klass.new(params)
50
+ end
51
+
52
+ protocols.each_cons(2) do | p, p1 |
53
+ p.next_protocol = p1
54
+ end
55
+
56
+ protocols.last.tap do |p|
57
+ p.next_protocol = conn
58
+ end
59
+
60
+ protocols.each do |p|
61
+ case
62
+ when conn.is_a?(ShadowsocksRuby::Connections::TCP::ClientConnection)
63
+ class << p
64
+ extend Forwardable
65
+ def_delegator :@next_protocol, :tcp_receive_from_client, :async_recv
66
+ def_delegator :@next_protocol, :tcp_send_to_client, :send_data
67
+ end
68
+ when conn.is_a?(ShadowsocksRuby::Connections::TCP::RemoteServerConnection)
69
+ class << p
70
+ extend Forwardable
71
+ def_delegator :@next_protocol, :tcp_receive_from_remoteserver, :async_recv
72
+ def_delegator :@next_protocol, :tcp_send_to_remoteserver, :send_data
73
+ end
74
+ when conn.is_a?(ShadowsocksRuby::Connections::TCP::LocalBackendConnection)
75
+ class << p
76
+ extend Forwardable
77
+ def_delegator :@next_protocol, :tcp_receive_from_localbackend, :async_recv
78
+ def_delegator :@next_protocol, :tcp_send_to_localbackend, :send_data
79
+ end
80
+ when conn.is_a?(ShadowsocksRuby::Connections::TCP::DestinationConnection)
81
+ class << p
82
+ extend Forwardable
83
+ def_delegator :@next_protocol, :tcp_receive_from_destination, :async_recv
84
+ def_delegator :@next_protocol, :tcp_send_to_destination, :send_data
85
+ end
86
+ when conn.is_a?(ShadowsocksRuby::Connections::UDP::ClientConnection)
87
+ class << p
88
+ extend Forwardable
89
+ def_delegator :@next_protocol, :udp_receive_from_client, :async_recv
90
+ def_delegator :@next_protocol, :udp_send_to_client, :send_data
91
+ end
92
+ when conn.is_a?(ShadowsocksRuby::Connections::UDP::RemoteServerConnection)
93
+ class << p
94
+ extend Forwardable
95
+ def_delegator :@next_protocol, :udp_receive_from_remoteserver, :async_recv
96
+ def_delegator :@next_protocol, :udp_send_to_remoteserver, :send_data
97
+ end
98
+ when conn.is_a?(ShadowsocksRuby::Connections::UDP::LocalBackendConnection)
99
+ class << p
100
+ extend Forwardable
101
+ def_delegator :@next_protocol, :udp_receive_from_localbackend, :async_recv
102
+ def_delegator :@next_protocol, :udp_send_to_localbackend, :send_data
103
+ end
104
+ when conn.is_a?(ShadowsocksRuby::Connections::UDP::DestinationConnection)
105
+ class << p
106
+ extend Forwardable
107
+ def_delegator :@next_protocol, :udp_receive_from_destination, :async_recv
108
+ def_delegator :@next_protocol, :udp_send_to_destination, :send_data
109
+ end
110
+ end
111
+ end
112
+
113
+ protocols.first
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,52 @@
1
+ module ShadowsocksRuby
2
+ # Various utility functions
3
+ module Util
4
+ module_function
5
+
6
+ ATYP_IPV4 = 1
7
+ ATYP_DOMAIN = 3
8
+ ATYP_IPV6 = 4
9
+
10
+ # Hex encodes a message
11
+ #
12
+ # @param [String] bytes The bytes to encode
13
+ #
14
+ # @return [String] Tasty, tasty hexadecimal
15
+ def bin2hex(bytes)
16
+ bytes.to_s.unpack("H*").first
17
+ end
18
+
19
+ # Hex decodes a message
20
+ #
21
+ # @param [String] hex hex to decode.
22
+ #
23
+ # @return [String] crisp and clean bytes
24
+ def hex2bin(hex)
25
+ [hex.to_s].pack("H*")
26
+ end
27
+
28
+ # Parse address bytes
29
+ # @param [String] bytes The bytes to parse
30
+ # @return [Array<String, Integer>] Return Host, Port
31
+ def parse_address_bin(bytes)
32
+ address_type = bytes.slice!(0, 1).unpack("C").first
33
+ case address_type
34
+ when ATYP_IPV4
35
+ host = IPAddr.ntop bytes.slice!(0, 4)
36
+ port = bytes.slice!(0, 2).unpack('n').first
37
+ [host, port]
38
+ when ATYP_IPV6
39
+ host = IPAddr.ntop bytes.slice!(0, 16)
40
+ port = bytes.slice!(0, 2).unpack('n').first
41
+ [host, port]
42
+ when ATYP_DOMAIN
43
+ domain_len = bytes.slice!(0, 1).unpack("C").first
44
+ host = bytes.slice!(0, domain_len)
45
+ port = bytes.slice!(0, 2).unpack('n').first
46
+ [host, port]
47
+ else
48
+ raise PharseError, "unknown address_type: #{address_type}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module ShadowsocksRuby
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,86 @@
1
+ require 'logger'
2
+ require 'eventmachine'
3
+ require 'forwardable'
4
+
5
+ require "shadowsocks_ruby/version"
6
+ require "shadowsocks_ruby/util"
7
+ require "shadowsocks_ruby/cipher/cipher"
8
+ require "shadowsocks_ruby/cipher/openssl"
9
+
10
+ %w[ openssl rbnacl rc4_md5 table ].each do |file|
11
+ require "shadowsocks_ruby/cipher/#{file}"
12
+ end
13
+
14
+ require "shadowsocks_ruby/protocols/protocol"
15
+ require "shadowsocks_ruby/protocols/protocol_stack"
16
+
17
+ # autoload protocols in these directories
18
+ %w[ packet cipher obfs ].each do |dir|
19
+ Dir[File.join(File.dirname(__FILE__) ,"shadowsocks_ruby", "protocols", dir , "*.rb")].each do |file|
20
+ file = File.basename(file,".rb")
21
+ require "shadowsocks_ruby/protocols/#{dir}/#{file}"
22
+ end
23
+ end
24
+
25
+ require "shadowsocks_ruby/connections/connection"
26
+ require "shadowsocks_ruby/connections/server_connection"
27
+ require "shadowsocks_ruby/connections/backend_connection"
28
+
29
+ %w[ client_connection remoteserver_connection localbackend_connection destination_connection ].each do |file|
30
+ require "shadowsocks_ruby/connections/tcp/#{file}"
31
+ end
32
+
33
+ %w[ client_connection remoteserver_connection localbackend_connection destination_connection ].each do |file|
34
+ require "shadowsocks_ruby/connections/udp/#{file}"
35
+ end
36
+
37
+ require "shadowsocks_ruby/app"
38
+
39
+ require "shadowsocks_ruby/cli/ssserver_runner"
40
+ require "shadowsocks_ruby/cli/sslocal_runner"
41
+
42
+ module ShadowsocksRuby
43
+ # This indicate a known failure by Shadowsock package.
44
+ # rescue this module will rescue all Shadowsocks known failure.
45
+ module MyErrorModule; end
46
+ # This indicates a failure in the App Class.
47
+ class AppError < ArgumentError
48
+ include MyErrorModule
49
+ end
50
+
51
+ # This indicates a failure in the Cipher Class.
52
+ class CipherError < ArgumentError
53
+ include MyErrorModule
54
+ end
55
+
56
+ # This indicates a failure in the Protocol Class.
57
+ class ProtocolError < ArgumentError
58
+ include MyErrorModule
59
+ end
60
+
61
+ # This indicates a failure in the Protocol pharse.
62
+ class PharseError < StandardError
63
+ include MyErrorModule
64
+ end
65
+
66
+ # This indicates a failure in Connection. A function is called out of fiber context.
67
+ class OutOfFiberContextError < StandardError
68
+ include MyErrorModule
69
+ end
70
+
71
+ # This indicates a failure in Connection
72
+ class ConnectionError < StandardError
73
+ include MyErrorModule
74
+ end
75
+
76
+ # This indicates a failure in Connection. The Buffer is oversized.
77
+ class BufferOversizeError < StandardError
78
+ include MyErrorModule
79
+ end
80
+
81
+ # This indicates something is unimplemented
82
+ class UnimplementError < StandardError
83
+ include MyErrorModule
84
+ end
85
+
86
+ end
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "shadowsocks_ruby/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "shadowsocks_ruby"
8
+ spec.version = ShadowsocksRuby::VERSION
9
+ spec.author = "zhenkyle"
10
+ spec.email = "zhenkyle@gmail.com"
11
+
12
+ spec.summary = %q{A flexible platform for writing tunnel proxy to help you bypass firewalls.}
13
+ spec.description = %q{ShadowsocksRuby is a flexible platform for writing [shadowsocks](https://github.com/shadowsocks/shadowsocks) like tunnel proxy to help you bypass firewalls.}
14
+ spec.homepage = "https://github.com/zhenkyle/shadowsocks_ruby"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ spec.metadata["yard.run"] = "yri" # use "yard" to build full HTML docs.
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.13"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ spec.add_development_dependency "evented-spec", "~> 1.0.0.beta2"
38
+ spec.add_development_dependency "em-http-request", "~> 1.1.5"
39
+ spec.add_development_dependency('aruba', '~> 0.14.2')
40
+ spec.add_development_dependency('yard', '~> 0.9.8')
41
+
42
+ spec.add_runtime_dependency "eventmachine", "~> 1.2.1"
43
+ spec.add_runtime_dependency "rbnacl-libsodium", "~> 1.0.11"
44
+ spec.add_runtime_dependency "openssl", "~> 2.0.2"
45
+ spec.add_runtime_dependency "lrucache", "~> 0.1.4"
46
+ spec.add_runtime_dependency "to_camel_case", "~>1.0.0"
47
+ spec.add_runtime_dependency 'einhorn'
48
+ end