shadowsocks 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/Gemfile +3 -0
- data/README.md +20 -0
- data/Rakefile +5 -0
- data/bin/ss-local +19 -0
- data/bin/ss-server +19 -0
- data/config.json +7 -0
- data/ext/encrypt/encrypt.c +11 -0
- data/ext/encrypt/extconf.rb +3 -0
- data/lib/shadowsocks.rb +9 -0
- data/lib/shadowsocks/cli.rb +51 -0
- data/lib/shadowsocks/config.rb +50 -0
- data/lib/shadowsocks/listener.rb +58 -0
- data/lib/shadowsocks/local.rb +79 -0
- data/lib/shadowsocks/server.rb +62 -0
- data/lib/shadowsocks/table.rb +84 -0
- data/lib/shadowsocks/tunnel.rb +19 -0
- data/lib/shadowsocks/version.rb +3 -0
- data/shadowsocks.gemspec +25 -0
- data/tasks/compile.rake +9 -0
- data/tasks/dev.rake +7 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 75e7362c5a7e492c1a72ce66fe087442c2196867
|
4
|
+
data.tar.gz: 0f9abb4370f9185c79ec822f62cce07b17bb43c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0fb909540f9566f272e19db7eb289abd03106d94c6cf4862dd4af63aab55a047730c087024b74982959868acf4a41bc0cf98fbba89612e9f54624288da0c8dde
|
7
|
+
data.tar.gz: 5083e49dd9fed51a947b94ad0e4868f55237a6fb4d7d3a9aa2b3497e5ec69a4999fb4442fbb4ef67a0014cdcb5ac50c7e29a499f6f11f2f50bbe6795e85fcb97
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
shadowsocks-ruby
|
2
|
+
================
|
3
|
+
|
4
|
+
a Ruby EventMachine port of shadowsocks-nodejs (not stable yet). use shadowsocks-nodejs instead
|
5
|
+
|
6
|
+
install
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
gem install eventmachine
|
10
|
+
gem install ffi
|
11
|
+
rake
|
12
|
+
```
|
13
|
+
|
14
|
+
server side
|
15
|
+
|
16
|
+
``` ruby
|
17
|
+
nohup ruby server.rb > log &
|
18
|
+
```
|
19
|
+
|
20
|
+
then enjoy!
|
data/Rakefile
ADDED
data/bin/ss-local
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'shadowsocks'
|
7
|
+
require 'shadowsocks/cli'
|
8
|
+
require 'shadowsocks/config'
|
9
|
+
|
10
|
+
begin
|
11
|
+
config = Shadowsocks::Config.new ARGV
|
12
|
+
cli = Shadowsocks::Cli.new({side: :local, config: config})
|
13
|
+
cli.run
|
14
|
+
rescue => e
|
15
|
+
raise e if $DEBUG
|
16
|
+
STDERR.puts e.message
|
17
|
+
STDERR.puts e.backtrace.join("\n")
|
18
|
+
exit 1
|
19
|
+
end
|
data/bin/ss-server
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'shadowsocks'
|
7
|
+
require 'shadowsocks/cli'
|
8
|
+
require 'shadowsocks/config'
|
9
|
+
|
10
|
+
begin
|
11
|
+
config = Shadowsocks::Config.new ARGV
|
12
|
+
cli = Shadowsocks::Cli.new({side: :server, config: config})
|
13
|
+
cli.run
|
14
|
+
rescue => e
|
15
|
+
raise e if $DEBUG
|
16
|
+
STDERR.puts e.message
|
17
|
+
STDERR.puts e.backtrace.join("\n")
|
18
|
+
exit 1
|
19
|
+
end
|
data/config.json
ADDED
data/lib/shadowsocks.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
require File.expand_path('../version', __FILE__)
|
5
|
+
|
6
|
+
module Shadowsocks
|
7
|
+
class Cli
|
8
|
+
include ::Shadowsocks::Table
|
9
|
+
|
10
|
+
attr_accessor :side, :args, :config, :table
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@side = options[:side]
|
14
|
+
@config = options[:config]
|
15
|
+
@table = get_table(config.password)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
case side
|
20
|
+
when :local
|
21
|
+
EventMachine::run {
|
22
|
+
Signal.trap("INT") { EventMachine.stop }
|
23
|
+
Signal.trap("TERM") { EventMachine.stop }
|
24
|
+
|
25
|
+
puts "*** Local side is up, port:#{config.local_port}"
|
26
|
+
puts "*** Hit Ctrl+c to stop"
|
27
|
+
EventMachine::start_server "0.0.0.0", config.local_port, Shadowsocks::Local::LocalListener, &method(:initialize_connection)
|
28
|
+
}
|
29
|
+
when :server
|
30
|
+
EventMachine::run {
|
31
|
+
Signal.trap("INT") { EventMachine.stop }
|
32
|
+
Signal.trap("TERM") { EventMachine.stop }
|
33
|
+
|
34
|
+
puts "*** Server side is up, port:#{config.server_port}"
|
35
|
+
puts "*** Hit Ctrl+c to stop"
|
36
|
+
|
37
|
+
EventMachine::start_server "0.0.0.0", config.server_port, Shadowsocks::Server::ServerListener, &method(:initialize_connection)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def initialize_connection connection
|
45
|
+
connection.config = @config
|
46
|
+
connection.table = @table
|
47
|
+
connection.pending_connect_timeout = @config.timeout
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Shadowsocks
|
4
|
+
class Config
|
5
|
+
attr_reader :args, :server, :server_port, :local_port, :password, :timeout, :config_path
|
6
|
+
|
7
|
+
def initialize(_args)
|
8
|
+
@args = _args || []
|
9
|
+
|
10
|
+
parse_args
|
11
|
+
read_config
|
12
|
+
end
|
13
|
+
|
14
|
+
def read_config
|
15
|
+
@config_path = File.expand_path('../..', File.dirname(__FILE__)) + '/config.json' unless @config_file
|
16
|
+
cfg_file = File.open @config_path
|
17
|
+
json = JSON.parse cfg_file.read
|
18
|
+
cfg_file.close
|
19
|
+
|
20
|
+
@server = json["server"] if @server.nil?
|
21
|
+
@password = json["password"] if @password.nil?
|
22
|
+
@server_port = json["server_port"].to_i if @server_port.nil?
|
23
|
+
@local_port = json["local_port"].to_i if @local_port.nil?
|
24
|
+
@timeout = json["timeout"].to_i if @timeout.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parse_args
|
30
|
+
opt_parser = OptionParser.new do |opts|
|
31
|
+
opts.banner = "Usage: ss-server [options]"
|
32
|
+
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "Specific options:"
|
35
|
+
|
36
|
+
opts.on("-s", "--server ADDR", "Remote server, IP address or domain") { |c| @server = c }
|
37
|
+
opts.on("-k", "--password PASSWORD", "Password, should be same in client and server sides") { |c| @password = c }
|
38
|
+
opts.on("-c", "--config PATH", "config.json path") { |c| @config_path = c }
|
39
|
+
opts.on("-p", "--port PORT", Integer, "Remote server port") { |c| @server_port = c }
|
40
|
+
opts.on("-l", "--local_port PORT", Integer,"Local client port") { |c| @local_port = c }
|
41
|
+
opts.on("-t", "--timeout NUMBER", Integer, "connection timeout") { |c| @timeout = c }
|
42
|
+
|
43
|
+
opts.on_tail("-v", "--version", "Show shadowsocks gem version") { puts Shadowsocks::VERSION; exit }
|
44
|
+
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
|
45
|
+
end
|
46
|
+
|
47
|
+
opt_parser.parse!(args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Shadowsocks
|
2
|
+
class Listener < EventMachine::Connection
|
3
|
+
include ::Shadowsocks::Table
|
4
|
+
|
5
|
+
attr_accessor :stage, :remote_addr, :remote_port, :addr_to_send, :cached_pieces,
|
6
|
+
:header_length, :connector, :config, :table
|
7
|
+
|
8
|
+
def receive_data data
|
9
|
+
data_handler data
|
10
|
+
end
|
11
|
+
|
12
|
+
def post_init
|
13
|
+
@stage = 0
|
14
|
+
@cached_pieces = []
|
15
|
+
puts "A client has connected..."
|
16
|
+
end
|
17
|
+
|
18
|
+
def unbind
|
19
|
+
puts "A client has left..."
|
20
|
+
connection_cleanup
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def connection_cleanup
|
26
|
+
connector.close_connection if connector
|
27
|
+
self.close_connection
|
28
|
+
end
|
29
|
+
|
30
|
+
def resolve_addrtype data
|
31
|
+
case @addrtype
|
32
|
+
when "\x01"
|
33
|
+
ip_address data
|
34
|
+
when "\x03"
|
35
|
+
domain_address data
|
36
|
+
else
|
37
|
+
warn "unsupported addrtype: " + @addrtype.unpack('c')[0].to_s
|
38
|
+
connection_cleanup
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def domain_address data
|
43
|
+
@remote_addr = data[2, @addr_len]
|
44
|
+
@remote_port = data[2 + @addr_len, 2].unpack('s>')[0]
|
45
|
+
@header_length = 2 + @addr_len + 2
|
46
|
+
end
|
47
|
+
|
48
|
+
def ip_address data
|
49
|
+
@remote_addr = inet_ntoa data[1..4]
|
50
|
+
@remote_port = data[5, 2].unpack('s>')[0]
|
51
|
+
@header_length = 7
|
52
|
+
end
|
53
|
+
|
54
|
+
def inet_ntoa n
|
55
|
+
n.unpack("C*").join "."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Shadowsocks
|
2
|
+
module Local
|
3
|
+
class ServerConnector < ::Shadowsocks::Tunnel
|
4
|
+
def post_init
|
5
|
+
p "connecting #{server.remote_addr[3..-1]} via #{server.config.server}"
|
6
|
+
addr_to_send = server.addr_to_send.clone
|
7
|
+
|
8
|
+
send_data encrypt(table[:encrypt_table], addr_to_send)
|
9
|
+
server.cached_pieces.each { |piece| send_data encrypt(table[:encrypt_table], piece) }
|
10
|
+
server.cached_pieces = []
|
11
|
+
|
12
|
+
server.stage = 5
|
13
|
+
end
|
14
|
+
|
15
|
+
def receive_data data
|
16
|
+
server.send_data encrypt(table[:decrypt_table], data)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class LocalListener < ::Shadowsocks::Listener
|
21
|
+
private
|
22
|
+
|
23
|
+
def data_handler data
|
24
|
+
case stage
|
25
|
+
when 0
|
26
|
+
send_data "\x05\x00"
|
27
|
+
@stage = 1
|
28
|
+
when 1
|
29
|
+
fireup_tunnel data
|
30
|
+
when 4
|
31
|
+
cached_pieces.push data
|
32
|
+
when 5
|
33
|
+
connector.send_data(encrypt(table[:encrypt_table], data)) and return
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def fireup_tunnel(data)
|
38
|
+
begin
|
39
|
+
unless data[1] == "\x01"
|
40
|
+
send_data "\x05\x07\x00\x01"
|
41
|
+
connection_cleanup and return
|
42
|
+
end
|
43
|
+
|
44
|
+
@addr_to_send = data[3]
|
45
|
+
|
46
|
+
resolve_addrtype data
|
47
|
+
|
48
|
+
send_data "\x05\x00\x00\x01\x00\x00\x00\x00" + [config.server_port].pack('s>')
|
49
|
+
|
50
|
+
@stage = 4
|
51
|
+
@connector = EventMachine.connect config.server, config.server_port, ServerConnector, self, @table
|
52
|
+
|
53
|
+
if data.size > header_length
|
54
|
+
cached_pieces.push data[header_length, data.size]
|
55
|
+
end
|
56
|
+
rescue Exception => e
|
57
|
+
warn e
|
58
|
+
connection_cleanup
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def resolve_addrtype data
|
63
|
+
@addrtype = data[3]
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
def domain_address data
|
68
|
+
@addr_len = data[4].unpack('c')[0]
|
69
|
+
@addr_to_send += data[4..5 + @addr_len + 2]
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
def ip_address data
|
74
|
+
@addr_to_send += data[4..9]
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Shadowsocks
|
2
|
+
module Server
|
3
|
+
class RequestConnector < ::Shadowsocks::Tunnel
|
4
|
+
def post_init
|
5
|
+
p "connecting #{server.remote_addr} via #{server.config.server}"
|
6
|
+
|
7
|
+
server.cached_pieces.each { |piece| send_data piece }
|
8
|
+
server.cached_pieces = nil
|
9
|
+
|
10
|
+
server.stage = 5
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data data
|
14
|
+
server.send_data encrypt(table[:encrypt_table], data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ServerListener < ::Shadowsocks::Listener
|
19
|
+
private
|
20
|
+
|
21
|
+
def data_handler data
|
22
|
+
data = encrypt table[:decrypt_table], data
|
23
|
+
case stage
|
24
|
+
when 0
|
25
|
+
fireup_tunnel data
|
26
|
+
when 4
|
27
|
+
cached_pieces.push data
|
28
|
+
when 5
|
29
|
+
connector.send_data(data) and return
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def fireup_tunnel data
|
34
|
+
begin
|
35
|
+
resolve_addrtype data
|
36
|
+
|
37
|
+
@stage = 4
|
38
|
+
|
39
|
+
if data.size > header_length
|
40
|
+
cached_pieces.push data[header_length, data.size]
|
41
|
+
end
|
42
|
+
|
43
|
+
@connector = EventMachine.connect @remote_addr, @remote_port, RequestConnector, self, table
|
44
|
+
rescue Exception => e
|
45
|
+
warn e
|
46
|
+
connection_cleanup
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def resolve_addrtype data
|
51
|
+
@addrtype = data[0]
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def domain_address data
|
56
|
+
@addr_len = data[1].unpack('c')[0]
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "digest"
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Ext
|
5
|
+
def self.binary_path
|
6
|
+
path = ''
|
7
|
+
%w( so bundle dll ).each do |ext|
|
8
|
+
path = File.expand_path('..', File.dirname(__FILE__)) + "/encrypt.#{ext}"
|
9
|
+
break if File.exists? path
|
10
|
+
end
|
11
|
+
|
12
|
+
path
|
13
|
+
end
|
14
|
+
|
15
|
+
extend FFI::Library
|
16
|
+
ffi_lib binary_path
|
17
|
+
attach_function "encrypt", [:pointer, :pointer, :int], :pointer
|
18
|
+
end
|
19
|
+
|
20
|
+
module Shadowsocks
|
21
|
+
module Table
|
22
|
+
include Ext
|
23
|
+
|
24
|
+
def get_table key
|
25
|
+
table = Array.new(256, 0)
|
26
|
+
decrypt_table = Array.new(256, 0)
|
27
|
+
|
28
|
+
a = Digest::MD5.digest(key).unpack('Q<')[0]
|
29
|
+
i = 0
|
30
|
+
|
31
|
+
while i < 256
|
32
|
+
table[i] = i
|
33
|
+
i += 1
|
34
|
+
end
|
35
|
+
i = 1
|
36
|
+
|
37
|
+
while i < 1024
|
38
|
+
table = merge_sort(table, lambda { |x, y|
|
39
|
+
a % (x + i) - a % (y + i)
|
40
|
+
})
|
41
|
+
i += 1
|
42
|
+
end
|
43
|
+
i = 0
|
44
|
+
while i < 256
|
45
|
+
decrypt_table[table[i]] = i
|
46
|
+
i += 1
|
47
|
+
end
|
48
|
+
{ encrypt_table: table, decrypt_table: decrypt_table }
|
49
|
+
end
|
50
|
+
|
51
|
+
def encrypt (table, buf)
|
52
|
+
table_ptr = FFI::MemoryPointer.new(:int, table.length)
|
53
|
+
table_ptr.put_array_of_int32(0, table)
|
54
|
+
|
55
|
+
buf_ptr = FFI::MemoryPointer.new(:string, buf.length)
|
56
|
+
buf_ptr.put_bytes(0, buf)
|
57
|
+
|
58
|
+
r = Ext.encrypt(table_ptr, buf_ptr, buf.length).get_bytes(0, buf.length)
|
59
|
+
table_ptr.free
|
60
|
+
buf_ptr.free
|
61
|
+
r
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge (left, right, comparison)
|
65
|
+
result = []
|
66
|
+
while (left.length > 0) and (right.length > 0)
|
67
|
+
if comparison.call(left[0], right[0]) <= 0
|
68
|
+
result.push left.shift()
|
69
|
+
else
|
70
|
+
result.push right.shift()
|
71
|
+
end
|
72
|
+
end
|
73
|
+
result.push left.shift() while left.length > 0
|
74
|
+
result.push right.shift() while right.length > 0
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def merge_sort (array, comparison)
|
79
|
+
return array if array.size < 2
|
80
|
+
middle = (array.size / 2).ceil
|
81
|
+
merge(merge_sort(array.slice(0, middle), comparison), merge_sort(array.slice(middle .. array.size), comparison), comparison)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Shadowsocks
|
2
|
+
class Tunnel < EventMachine::Connection
|
3
|
+
attr_accessor :server, :table
|
4
|
+
|
5
|
+
def initialize server, table
|
6
|
+
@server = server
|
7
|
+
@table = table
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def unbind
|
12
|
+
server.close_connection_after_writing
|
13
|
+
end
|
14
|
+
|
15
|
+
def encrypt table, data
|
16
|
+
server.encrypt table, data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/shadowsocks.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('../lib/shadowsocks/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'shadowsocks'
|
5
|
+
s.version = Shadowsocks::VERSION
|
6
|
+
s.date = '2013-09-26'
|
7
|
+
s.summary = "ruby version of shadowsocks"
|
8
|
+
s.description = "Fuck GFW"
|
9
|
+
s.authors = ["Sen"]
|
10
|
+
s.email = 'sen9ob@gmail.com'
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = `git ls-files -- test/{functional,unit}/*`.split("\n")
|
13
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
14
|
+
s.homepage = 'http://rubygems.org/gems/shadowsocks'
|
15
|
+
s.license = 'MIT'
|
16
|
+
s.extensions = %w[ext/encrypt/extconf.rb]
|
17
|
+
|
18
|
+
s.add_dependency "eventmachine", "~> 1.0.3"
|
19
|
+
s.add_dependency "json", "~> 1.8.0"
|
20
|
+
s.add_dependency "ffi", "~> 1.9.0"
|
21
|
+
|
22
|
+
s.add_development_dependency "rake-compiler", "~> 0.9.1"
|
23
|
+
s.add_development_dependency "mocha", "~> 0.14.0"
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
end
|
data/tasks/compile.rake
ADDED
data/tasks/dev.rake
ADDED
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shadowsocks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: eventmachine
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.8.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ffi
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.9.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.9.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake-compiler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.9.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.14.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.14.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Fuck GFW
|
98
|
+
email: sen9ob@gmail.com
|
99
|
+
executables:
|
100
|
+
- ss-local
|
101
|
+
- ss-server
|
102
|
+
extensions:
|
103
|
+
- ext/encrypt/extconf.rb
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- .gitignore
|
107
|
+
- Gemfile
|
108
|
+
- Gemfile.lock
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/ss-local
|
112
|
+
- bin/ss-server
|
113
|
+
- config.json
|
114
|
+
- ext/encrypt/encrypt.c
|
115
|
+
- ext/encrypt/extconf.rb
|
116
|
+
- lib/shadowsocks.rb
|
117
|
+
- lib/shadowsocks/cli.rb
|
118
|
+
- lib/shadowsocks/config.rb
|
119
|
+
- lib/shadowsocks/listener.rb
|
120
|
+
- lib/shadowsocks/local.rb
|
121
|
+
- lib/shadowsocks/server.rb
|
122
|
+
- lib/shadowsocks/table.rb
|
123
|
+
- lib/shadowsocks/tunnel.rb
|
124
|
+
- lib/shadowsocks/version.rb
|
125
|
+
- shadowsocks.gemspec
|
126
|
+
- tasks/compile.rake
|
127
|
+
- tasks/dev.rake
|
128
|
+
homepage: http://rubygems.org/gems/shadowsocks
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.0.3
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: ruby version of shadowsocks
|
152
|
+
test_files: []
|