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