kerryb-net-ssh-socks 0.0.3
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.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +16 -0
- data/README.md +28 -0
- data/Rakefile +2 -0
- data/lib/net/ssh/socks.rb +146 -0
- data/net-ssh-socks.gemspec +23 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Net::SSH::Socks
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
Net::SSH::Socks is a library for programmatically creating a SOCKS proxy server that tunnels through SSH. Similar to Net::SSH::Service::Forward#local except the host is dynamic (determined by the client application, such as a browser).
|
6
|
+
|
7
|
+
Useful for securing traffic over SSH or getting in/out of a firewall.
|
8
|
+
|
9
|
+
## Use cases
|
10
|
+
|
11
|
+
* If your company blocks certain sites/resources but allows SSH, then you can tunnel out to a machine outside the firewall.
|
12
|
+
* If you need to access sites/resources inside a firewall, then you can tunnel into a machine inside the firewall.
|
13
|
+
* Encrypt your traffic to protect yourself from things like Firesheep on public wifi.
|
14
|
+
|
15
|
+
## Synopsis
|
16
|
+
|
17
|
+
require 'net/ssh/socks'
|
18
|
+
|
19
|
+
Net::SSH.start('host', 'user') do |ssh|
|
20
|
+
ssh.forward.socks(8080)
|
21
|
+
ssh.loop { true }
|
22
|
+
end
|
23
|
+
|
24
|
+
Now, configure your browser to use a SOCKS proxy at localhost:8080
|
25
|
+
|
26
|
+
## Install
|
27
|
+
|
28
|
+
gem install net-ssh-socks
|
data/Rakefile
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
module Net
|
4
|
+
module SSH
|
5
|
+
class Socks
|
6
|
+
VERSION = "0.0.3"
|
7
|
+
|
8
|
+
METHOD_NO_AUTH = 0
|
9
|
+
CMD_CONNECT = 1
|
10
|
+
REP_SUCCESS = 0
|
11
|
+
RESERVED = 0
|
12
|
+
ATYP_IPV4 = 1
|
13
|
+
ATYP_DOMAIN = 3
|
14
|
+
ATYP_IPV6 = 4
|
15
|
+
SOCKS4 = 4
|
16
|
+
SOCKS4_SUCCESS = [0, 0x5a, 0, 0, 0, 0, 0, 0].pack("C*")
|
17
|
+
SOCKS5 = 5
|
18
|
+
|
19
|
+
# client is an open socket
|
20
|
+
def initialize(client)
|
21
|
+
@client = client
|
22
|
+
end
|
23
|
+
|
24
|
+
# Communicates with a client application as described by the SOCKS 5
|
25
|
+
# specification: http://tools.ietf.org/html/rfc1928 and
|
26
|
+
# http://en.wikipedia.org/wiki/SOCKS
|
27
|
+
# returns the host and port requested by the client
|
28
|
+
def client_handshake
|
29
|
+
version = @client.recv(1).unpack("C*").first
|
30
|
+
case version
|
31
|
+
when SOCKS4 then client_handshake_v4
|
32
|
+
when SOCKS5 then client_handshake_v5
|
33
|
+
else
|
34
|
+
raise "SOCKS version not supported: #{version.inspect}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def client_handshake_v4
|
39
|
+
command = @client.recv(1)
|
40
|
+
port = @client.recv(2).unpack("n").first
|
41
|
+
ip_addr = @client.recv(4).unpack("C*").join('.')
|
42
|
+
username = @client.recv(1024) # read the rest of the stream
|
43
|
+
|
44
|
+
@client.send SOCKS4_SUCCESS, 0
|
45
|
+
[ip_addr, port]
|
46
|
+
end
|
47
|
+
|
48
|
+
def client_handshake_v5
|
49
|
+
nmethods, *methods = @client.recv(8).unpack("C*")
|
50
|
+
|
51
|
+
if methods.include?(METHOD_NO_AUTH)
|
52
|
+
packet = [SOCKS5, METHOD_NO_AUTH].pack("C*")
|
53
|
+
@client.send packet, 0
|
54
|
+
else
|
55
|
+
@client.close
|
56
|
+
raise 'Unsupported authentication method. Only "No Authentication" is supported'
|
57
|
+
end
|
58
|
+
|
59
|
+
version, command, reserved, address_type, *destination = @client.recv(256).unpack("C*")
|
60
|
+
|
61
|
+
packet = ([SOCKS5, REP_SUCCESS, RESERVED, address_type] + destination).pack("C*")
|
62
|
+
@client.send packet, 0
|
63
|
+
|
64
|
+
remote_host, remote_port = case address_type
|
65
|
+
when ATYP_IPV4
|
66
|
+
host = destination[0..-3].join('.')
|
67
|
+
port = destination[-2..-1].pack('C*').unpack('n')
|
68
|
+
[host, port]
|
69
|
+
when ATYP_DOMAIN
|
70
|
+
host = destination[1..-3].pack('C*')
|
71
|
+
port = destination[-2..-1].pack('C*').unpack('n')
|
72
|
+
[host, port]
|
73
|
+
when ATYP_IPV6
|
74
|
+
@client.close
|
75
|
+
raise 'Unsupported address type. Only "IPv4" is supported'
|
76
|
+
else
|
77
|
+
raise "Unknown address_type: #{address_type}"
|
78
|
+
end
|
79
|
+
|
80
|
+
[remote_host, remote_port]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Net::SSH::Connection::Session
|
87
|
+
def on_close(&block)
|
88
|
+
@on_close = block
|
89
|
+
end
|
90
|
+
|
91
|
+
alias_method :close_without_callbacks, :close
|
92
|
+
def close
|
93
|
+
close_without_callbacks
|
94
|
+
@on_close.call if @on_close
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Net::SSH::Service::Forward
|
99
|
+
# Starts listening for connections on the local host, and forwards them
|
100
|
+
# to the specified remote host/port via the SSH connection. This method
|
101
|
+
# accepts either one or two arguments. When two arguments are given,
|
102
|
+
# they are:
|
103
|
+
#
|
104
|
+
# * the local address to bind to
|
105
|
+
# * the local port to listen on
|
106
|
+
#
|
107
|
+
# If one argument is given, it is as if the local bind address is
|
108
|
+
# "127.0.0.1", and the rest are applied as above.
|
109
|
+
#
|
110
|
+
# ssh.forward.socks(8080)
|
111
|
+
# ssh.forward.socks("0.0.0.0", 8080)
|
112
|
+
def socks(*args)
|
113
|
+
if args.length < 1 || args.length > 2
|
114
|
+
raise ArgumentError, "expected 1 or 2 parameters, got #{args.length}"
|
115
|
+
end
|
116
|
+
|
117
|
+
bind_address = "127.0.0.1"
|
118
|
+
bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/
|
119
|
+
local_port = args.shift.to_i
|
120
|
+
info { "socks on #{bind_address} port #{local_port}" }
|
121
|
+
|
122
|
+
socks_server = TCPServer.new(bind_address, local_port)
|
123
|
+
session.listen_to(socks_server) do |server|
|
124
|
+
client = server.accept
|
125
|
+
|
126
|
+
socks = Net::SSH::Socks.new(client)
|
127
|
+
remote_host, remote_port = socks.client_handshake
|
128
|
+
info { "connection requested on #{remote_host} port #{remote_port}" }
|
129
|
+
|
130
|
+
channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, :long, local_port) do |channel|
|
131
|
+
info { "direct channel established" }
|
132
|
+
prepare_client(client, channel, :local)
|
133
|
+
end
|
134
|
+
|
135
|
+
channel.on_open_failed do |ch, code, description|
|
136
|
+
error { "could not establish direct channel: #{description} (#{code})" }
|
137
|
+
client.close
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
session.on_close do
|
142
|
+
debug { "cleaning up socks server" }
|
143
|
+
socks_server.close
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'net/ssh/socks'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "kerryb-net-ssh-socks"
|
7
|
+
s.version = Net::SSH::Socks::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Mike Enriquez"]
|
10
|
+
s.email = ["mike@edgecase.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{An extension to Net::SSH that adds dynamic port forwarding through a SOCKS proxy}
|
13
|
+
s.description = %q{Net::SSH::Socks is a library for programmatically creating a SOCKS proxy. Similar to Net::SSH::Service::Forward#local except the host is dynamic (determined by the client application, such as a browser).}
|
14
|
+
|
15
|
+
s.rubyforge_project = "net-ssh-socks"
|
16
|
+
|
17
|
+
s.add_dependency "net-ssh", "~> 2.0"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kerryb-net-ssh-socks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.3
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Enriquez
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-02 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: net-ssh
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "2.0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description: Net::SSH::Socks is a library for programmatically creating a SOCKS proxy. Similar to Net::SSH::Service::Forward#local except the host is dynamic (determined by the client application, such as a browser).
|
28
|
+
email:
|
29
|
+
- mike@edgecase.com
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files: []
|
35
|
+
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- lib/net/ssh/socks.rb
|
43
|
+
- net-ssh-socks.gemspec
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: ""
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: net-ssh-socks
|
68
|
+
rubygems_version: 1.6.2
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: An extension to Net::SSH that adds dynamic port forwarding through a SOCKS proxy
|
72
|
+
test_files: []
|
73
|
+
|