socks_tunnel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dddadc3f63d978799189f6fa955e3f810ad77a2e
4
+ data.tar.gz: ef0d4d891892564a69d559d791d436274a488708
5
+ SHA512:
6
+ metadata.gz: 0bc01a03e50e7b585a7b9a6756df027b9589d4c29c1d3a18f370c0149d3f94d9959ba5d62983b25196b711135c917627907a00bfea61979112705f18f250cd2f
7
+ data.tar.gz: 02e8978e9b80d7d04c67dee7769bfad33e2dee49843e6bbd43cb4f62fb992852a1baf5c6a706963a0077d4b2ee85240c154cd51353c503e24a7b61c680a93b47
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in socks_tunnel.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Weihu Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # SocksTunnel
2
+
3
+ Establish secure tunnel via Socks 5.
4
+
5
+ ## Installation
6
+
7
+ $ gem install socks_tunnel
8
+
9
+ ## Usage
10
+
11
+ Start local server
12
+
13
+ socks_tunnel local --password=PASSWORD --port=PORT --remote-addr=REMOTE_ADDR
14
+
15
+ Options
16
+
17
+ <table>
18
+ <tr>
19
+ <td>--port</td><td>The port number to listen.</td><td>Required</td>
20
+ </tr>
21
+ <tr>
22
+ <td>--password</td><td>The password for generating encryption key and iv.</td><td>Required</td>
23
+ </tr>
24
+ <tr>
25
+ <td>--remote-addr</td><td>The address of remote server. (host:port)</td><td>Required</td>
26
+ </tr>
27
+ <tr>
28
+ <td>--host</td><td>When running local server on a different machine, use this to set the IP.</td><td>Optional</td>
29
+ </tr>
30
+ <tr>
31
+ <td>--salt</td><td>The salt for generating encryption key and iv.</td><td>Optional</td>
32
+ </tr>
33
+ </table>
34
+
35
+ Start remote server
36
+
37
+ socks_tunnel remote --password=PASSWORD --port=PORT
38
+
39
+ Options
40
+
41
+ <table>
42
+ <tr>
43
+ <td>--port</td><td>The port number to listen.</td><td>Required</td>
44
+ </tr>
45
+ <tr>
46
+ <td>--password</td><td>The password for generating encryption key and iv.</td><td>Required</td>
47
+ </tr>
48
+ <tr>
49
+ <td>--salt</td><td>The salt for generating encryption key and iv.</td><td>Optional</td>
50
+ </tr>
51
+ </table>
52
+
53
+ *Password* and *Salt* used in both local and remote server should be the same.
54
+
55
+ ## Example
56
+
57
+ Test on local machine
58
+
59
+ # Start local server
60
+ socks_tunnel local --password=mypassword --port=8081 --remote-addr=127.0.0.1:8082
61
+ # Start remote server
62
+ socks_tunnel remote --password=mypassword --port=8082
63
+
64
+ # Use curl to test
65
+ curl --socks5 localhost:8081 https://github.com/
66
+
67
+ Run *Remote server* on a real server
68
+
69
+ # Start local server
70
+ socks_tunnel local --password=mypassword --port=8081 --remote-addr=[SERVER_IP]:8082
71
+ # Start remote server
72
+ socks_tunnel remote --password=mypassword --port=8082
73
+
74
+ # Use curl to test
75
+ curl --socks5 localhost:8081 https://github.com/
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/socks_tunnel ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "socks_tunnel/cli"
5
+
6
+ SocksTunnel::CLI.start(ARGV)
@@ -0,0 +1,10 @@
1
+ require "eventmachine"
2
+ require "socks_tunnel/version"
3
+ require "socks_tunnel/buffer"
4
+ require "socks_tunnel/coder"
5
+ require "socks_tunnel/config"
6
+
7
+ module SocksTunnel
8
+ autoload :Local, "socks_tunnel/local"
9
+ autoload :Remote, "socks_tunnel/remote"
10
+ end
@@ -0,0 +1,33 @@
1
+ module SocksTunnel
2
+ class Buffer
3
+ def initialize
4
+ @buffer_data = ''
5
+ end
6
+
7
+ def <<(data)
8
+ @buffer_data << data
9
+ end
10
+
11
+ def each
12
+ loop do
13
+ fore, rest = @buffer_data.split(Config.delimiter, 2)
14
+ break unless rest
15
+ yield fore
16
+ @buffer_data = rest
17
+ end
18
+ end
19
+
20
+ def shift
21
+ fore, rest = @buffer_data.split(Config.delimiter, 2)
22
+ if rest
23
+ @buffer_data = rest
24
+ return fore
25
+ end
26
+ nil
27
+ end
28
+
29
+ def clear
30
+ @buffer_data = ''
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ require "thor"
2
+ require "socks_tunnel"
3
+
4
+ module SocksTunnel
5
+ class CLI < Thor
6
+ desc "local", "Start local server"
7
+ option :port, required: true
8
+ option :password, required: true
9
+ option :remote_addr, required: true
10
+ option :host
11
+ def local
12
+ Config.from(options)
13
+ Local.run
14
+ end
15
+
16
+ desc "remote", "Start remote server"
17
+ option :port, required: true
18
+ option :password, required: true
19
+ def remote
20
+ Config.from(options)
21
+ Remote.run
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ require "openssl"
2
+
3
+ module SocksTunnel
4
+ class Coder
5
+ def initialize
6
+ cipher = OpenSSL::Cipher.new(Config.cipher)
7
+ key_iv = OpenSSL::PKCS5.pbkdf2_hmac_sha1(Config.password, Config.salt, 2000, cipher.key_len + cipher.iv_len)
8
+ @key = key_iv[0, cipher.key_len]
9
+ @iv = key_iv[cipher.key_len, cipher.iv_len]
10
+
11
+ @encoder = OpenSSL::Cipher.new(Config.cipher)
12
+ @encoder.encrypt
13
+ @encoder.key = @key
14
+
15
+ @decoder = OpenSSL::Cipher.new(Config.cipher)
16
+ @decoder.decrypt
17
+ @decoder.key = @key
18
+ end
19
+
20
+ def encode(data)
21
+ @encoder.iv = @iv
22
+ @encoder.update(data) + @encoder.final
23
+ end
24
+
25
+ def decode(data)
26
+ @decoder.iv = @iv
27
+ @decoder.update(data) + @decoder.final
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ module SocksTunnel
2
+ class Config
3
+ class << self
4
+ attr_accessor :local_server_host, :local_server_port,
5
+ :remote_server_host, :remote_server_port, :password, :salt
6
+
7
+ def from(options)
8
+ if options[:remote_addr]
9
+ @local_server_port = options[:port]
10
+ @local_server_host = options[:host] if options[:host]
11
+ @remote_server_host, @remote_server_port = options[:remote_addr].split(':')
12
+ else
13
+ @remote_server_port = options[:port]
14
+ end
15
+ @password = options[:password]
16
+ @salt = options[:salt] if options[:salt]
17
+ end
18
+
19
+ def local_server_host
20
+ @local_server_host ||= '127.0.0.1'
21
+ end
22
+
23
+ def salt
24
+ @salt ||= "V\x11\x97\xA6r\xEF[\xFE"
25
+ end
26
+
27
+ def delimiter
28
+ @delimiter ||= '===SOCKSTUNNEL==='
29
+ end
30
+
31
+ def cipher
32
+ @cipher ||= 'AES-256-CBC'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,155 @@
1
+ require "ipaddr"
2
+
3
+ module SocksTunnel
4
+ class Local
5
+ def self.run
6
+ EventMachine.run do
7
+ puts "Start local server at #{Config.local_server_host}:#{Config.local_server_port}"
8
+ EventMachine.start_server(Config.local_server_host, Config.local_server_port, Server)
9
+ end
10
+ end
11
+
12
+ class Connection < EventMachine::Connection
13
+ attr_accessor :server
14
+
15
+ def post_init
16
+ @coder = Coder.new
17
+ @buffer = Buffer.new
18
+ end
19
+
20
+ def send_encoded_data(data)
21
+ return if data.nil? || data.empty?
22
+ send_data(@coder.encode(data))
23
+ send_data(Config.delimiter)
24
+ end
25
+
26
+ def receive_data(data)
27
+ return if data.nil? || data.empty?
28
+ @buffer << data
29
+ @buffer.each do |segment|
30
+ server.send_data(@coder.decode(segment))
31
+ end
32
+ end
33
+
34
+ def unbind
35
+ server.close_connection_after_writing
36
+ end
37
+ end
38
+
39
+ module Server
40
+ def post_init
41
+ @fiber = Fiber.new do
42
+ greeting
43
+ loop { do_command }
44
+ end
45
+ end
46
+
47
+ def receive_data(data)
48
+ if @connection
49
+ @connection.send_encoded_data(data.to_s)
50
+ else
51
+ @data = data
52
+ @fiber = nil if @fiber.resume
53
+ end
54
+ end
55
+
56
+ def unbind
57
+ @connection.close_connection if @connection
58
+ end
59
+
60
+ private
61
+
62
+ # IN
63
+ # +----+----------+----------+
64
+ # |VER | NMETHODS | METHODS |
65
+ # +----+----------+----------+
66
+ # | 1 | 1 | 1 to 255 |
67
+ # +----+----------+----------+
68
+ #
69
+ # OUT
70
+ # +----+--------+
71
+ # |VER | METHOD |
72
+ # +----+--------+
73
+ # | 1 | 1 |
74
+ # +----+--------+
75
+ def greeting
76
+ ver = @data.unpack("C").first
77
+ clear_data
78
+ if ver == 5
79
+ send_data "\x05\x00" # NO AUTHENTICATION REQUIRED
80
+ else
81
+ send_data "\x05\xFF" # NO ACCEPTABLE METHODS
82
+ end
83
+ Fiber.yield
84
+ end
85
+
86
+ # IN
87
+ # +----+-----+-------+------+----------+----------+
88
+ # |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
89
+ # +----+-----+-------+------+----------+----------+
90
+ # | 1 | 1 | X'00' | 1 | Variable | 2 |
91
+ # +----+-----+-------+------+----------+----------+
92
+ #
93
+ # OUT
94
+ # see the defination of reply_data
95
+ def do_command
96
+ _, cmd, _, atype, addr_length = @data.unpack("C5")
97
+ header_length = 0
98
+
99
+ case atype
100
+ when 1, 4 # 1: ipv4, 4 bytes / 4: ipv6, 16 bytes
101
+ ip_length = 4 * atype
102
+ host = IPAddr.ntop @data[4, ip_length]
103
+ port = @data[4 + ip_length, 2].unpack('S>').first
104
+ header_length = ip_length + 6
105
+ when 3 # domain name
106
+ host = @data[5, addr_length]
107
+ port = @data[5 + addr_length, 2].unpack('S>').first
108
+ header_length = addr_length + 7
109
+ else
110
+ panic :address_type_not_supported
111
+ end
112
+
113
+ case cmd
114
+ when 1
115
+ send_data reply_data(:success)
116
+ @connection = EventMachine.connect(Config.remote_server_host, Config.remote_server_port, Connection)
117
+ @connection.server = self
118
+ @connection.send_encoded_data("#{host}:#{port}")
119
+ @connection.send_encoded_data(@data[header_length, -1])
120
+ clear_data
121
+ Fiber.yield
122
+ when 2, 3 # bind, udp
123
+ panic :command_not_supported
124
+ else
125
+ panic :command_not_supported
126
+ end
127
+ end
128
+
129
+ # +----+-----+-------+------+----------+----------+
130
+ # |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
131
+ # +----+-----+-------+------+----------+----------+
132
+ # | 1 | 1 | X'00' | 1 | Variable | 2 |
133
+ # +----+-----+-------+------+----------+----------+
134
+ def reply_data(type)
135
+ @replies_hash ||= begin
136
+ {
137
+ success: 0,
138
+ command_not_supported: 7,
139
+ address_type_not_supported: 8,
140
+ }.map { |k, v| [k, ("\x05#{[v].pack('C')}\x00\x01\x00\x00\x00\x00\x00\x00")] }.to_h
141
+ end
142
+ @replies_hash[type]
143
+ end
144
+
145
+ def clear_data
146
+ @data = nil
147
+ end
148
+
149
+ def panic(reply_type)
150
+ send_data reply_data(reply_type)
151
+ Fiber.yield true
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,64 @@
1
+ module SocksTunnel
2
+ class Remote
3
+ def self.run
4
+ EventMachine.run do
5
+ puts "Start remote server at 0.0.0.0:#{Config.remote_server_port}"
6
+ EventMachine.start_server('0.0.0.0', Config.remote_server_port, Server)
7
+ end
8
+ end
9
+
10
+ class Connection < EventMachine::Connection
11
+ attr_accessor :server
12
+
13
+ def receive_data(data)
14
+ @server.send_encoded_data(data)
15
+ end
16
+
17
+ def unbind
18
+ @server.close_connection_after_writing
19
+ end
20
+ end
21
+
22
+ class Server < EventMachine::Connection
23
+ def post_init
24
+ @coder = Coder.new
25
+ @buffer = Buffer.new
26
+ end
27
+
28
+ def send_encoded_data(data)
29
+ return if data.nil? || data.empty?
30
+ send_data(@coder.encode(data))
31
+ send_data(Config.delimiter)
32
+ end
33
+
34
+ def receive_data(data)
35
+ if @connection
36
+ @buffer << data
37
+ @buffer.each do |segment|
38
+ @connection.send_data(@coder.decode(segment))
39
+ end
40
+ else
41
+ @buffer << data
42
+ addr = @buffer.shift
43
+ if addr
44
+ addr = @coder.decode(addr)
45
+ host, port = addr.split(':')
46
+ port = (port.nil? || port.empty?) ? 80 : port.to_i
47
+ @connection = EventMachine.connect(host, port, Connection)
48
+ @connection.server = self
49
+ @buffer.each do |segment|
50
+ @connection.send_data(@coder.decode(segment))
51
+ end
52
+ end
53
+ end
54
+ rescue
55
+ @connection.close_connection if @connection
56
+ close_connection
57
+ end
58
+
59
+ def unbind
60
+ @connection.close_connection if @connection
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module SocksTunnel
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'socks_tunnel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "socks_tunnel"
8
+ spec.version = SocksTunnel::VERSION
9
+ spec.authors = ["Weihu Chen"]
10
+ spec.email = ["cctiger36@gmail.com"]
11
+
12
+ spec.summary = %q{Establish secure tunnel via Socks 5.}
13
+ spec.description = %q{Establish secure tunnel via Socks 5.}
14
+ spec.homepage = "https://github.com/cctiger36/socks_tunnel"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "eventmachine", "> 1.2"
23
+ spec.add_dependency "thor", "> 0.19"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.12"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: socks_tunnel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Weihu Chen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-07 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.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: Establish secure tunnel via Socks 5.
84
+ email:
85
+ - cctiger36@gmail.com
86
+ executables:
87
+ - socks_tunnel
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/socks_tunnel
99
+ - lib/socks_tunnel.rb
100
+ - lib/socks_tunnel/buffer.rb
101
+ - lib/socks_tunnel/cli.rb
102
+ - lib/socks_tunnel/coder.rb
103
+ - lib/socks_tunnel/config.rb
104
+ - lib/socks_tunnel/local.rb
105
+ - lib/socks_tunnel/remote.rb
106
+ - lib/socks_tunnel/version.rb
107
+ - socks_tunnel.gemspec
108
+ homepage: https://github.com/cctiger36/socks_tunnel
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.5.1
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Establish secure tunnel via Socks 5.
132
+ test_files: []