shadowsocks_ruby 0.1.0

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