pagoda-tunnel 0.1.0
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 +4 -0
- data/Gemfile +4 -0
- data/Rakefile +16 -0
- data/lib/pagoda-tunnel.rb +157 -0
- data/pagoda-tunnel.gemspec +31 -0
- data/spec/tunnel_spec.rb +34 -0
- metadata +74 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
desc "Run all specs"
|
5
|
+
RSpec::Core::RakeTask.new('spec') do |t|
|
6
|
+
t.rspec_opts = ['--colour --format documentation']
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Create tag v#{Pagoda::Tunnel::VERSION}"
|
10
|
+
task :tag do
|
11
|
+
|
12
|
+
puts "tagging version v#{Pagoda::Tunnel::VERSION}"
|
13
|
+
`git tag -a v#{Pagoda::Tunnel::VERSION} -m "Version #{Pagoda::Tunnel::VERSION}"`
|
14
|
+
`git push origin --tags`
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'socket'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Pagoda
|
6
|
+
class Tunnel
|
7
|
+
VERSION = "0.1.0"
|
8
|
+
# Your code goes here...
|
9
|
+
|
10
|
+
def initialize(type, user, pass, app, instance, port = 3306)
|
11
|
+
@type = type
|
12
|
+
@user = user
|
13
|
+
@pass = pass
|
14
|
+
@app = app
|
15
|
+
@instance = instance
|
16
|
+
@port = port
|
17
|
+
end
|
18
|
+
|
19
|
+
def port_available?(ip, port)
|
20
|
+
begin
|
21
|
+
Timeout::timeout(1) do
|
22
|
+
begin
|
23
|
+
s = TCPSocket.new(ip, port)
|
24
|
+
s.close
|
25
|
+
return false
|
26
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Timeout::Error
|
31
|
+
end
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
def next_available_port(start_port)
|
36
|
+
until port_available?("0.0.0.0", start_port)
|
37
|
+
# puts "port #{start_port} was not available"
|
38
|
+
start_port += 1
|
39
|
+
end
|
40
|
+
start_port
|
41
|
+
end
|
42
|
+
|
43
|
+
def start
|
44
|
+
|
45
|
+
[:INT, :TERM].each do |sig|
|
46
|
+
Signal.trap(sig) do
|
47
|
+
puts "Tunnel Closed."
|
48
|
+
puts "-----------------------------------------------"
|
49
|
+
puts
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
remote_host = "tunnel.pagodabox.com" # switch to tunnel.pagodabox.com
|
55
|
+
remote_port = 443
|
56
|
+
|
57
|
+
max_threads = 20
|
58
|
+
threads = []
|
59
|
+
|
60
|
+
chunk = 4096*4096
|
61
|
+
|
62
|
+
# puts "start TCP server"
|
63
|
+
# puts "+> Opening Tunnel"
|
64
|
+
@port = next_available_port(@port)
|
65
|
+
retrys = 0
|
66
|
+
begin
|
67
|
+
proxy_server = TCPServer.new('0.0.0.0', @port)
|
68
|
+
rescue Exception => e
|
69
|
+
@port += 1
|
70
|
+
retry if retrys < 4
|
71
|
+
# puts "unable to connect to #{@port}. The algorithm is broken"
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
|
75
|
+
puts
|
76
|
+
puts "Tunnel Established! Accepting connections on :"
|
77
|
+
puts "-----------------------------------------------"
|
78
|
+
puts
|
79
|
+
puts "HOST : 127.0.0.1 (or localhost)"
|
80
|
+
puts "PORT : #{@port}"
|
81
|
+
puts "USER : (found in pagodabox dashboard)"
|
82
|
+
puts "PASS : (found in pagodabox dashboard)"
|
83
|
+
puts
|
84
|
+
puts "-----------------------------------------------"
|
85
|
+
puts "(note : ctrl-c To close this tunnel)"
|
86
|
+
|
87
|
+
loop do
|
88
|
+
|
89
|
+
# puts "start a new thread for every client connection"
|
90
|
+
threads << Thread.new(proxy_server.accept) do |client_socket|
|
91
|
+
begin
|
92
|
+
# puts "client connection"
|
93
|
+
begin
|
94
|
+
server_socket = TCPSocket.new(remote_host, remote_port)
|
95
|
+
ssl_context = OpenSSL::SSL::SSLContext.new()
|
96
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(server_socket, ssl_context)
|
97
|
+
ssl_socket.sync_close = true
|
98
|
+
ssl_socket.connect
|
99
|
+
rescue Errno::ECONNREFUSED
|
100
|
+
# puts "connection refused"
|
101
|
+
client_socket.close
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
|
105
|
+
# puts "authenticate"
|
106
|
+
if ssl_socket.readpartial(chunk) == "auth"
|
107
|
+
# puts "authentication"
|
108
|
+
# puts "auth=#{@type}:#{@user}:#{@pass}:#{@app}:#{@instance}"
|
109
|
+
ssl_socket.write "auth=#{@type}:#{@user}:#{@pass}:#{@app}:#{@instance}"
|
110
|
+
if ssl_socket.readpartial(chunk) == "success"
|
111
|
+
# puts "successful connection"
|
112
|
+
else
|
113
|
+
# puts "failed connection"
|
114
|
+
end
|
115
|
+
else
|
116
|
+
# puts "danger will robbinson! abort!"
|
117
|
+
end
|
118
|
+
|
119
|
+
loop do
|
120
|
+
# puts "wait for data on either socket"
|
121
|
+
(ready_sockets, dummy, dummy) = IO.select([client_socket, ssl_socket])
|
122
|
+
|
123
|
+
# puts "full duplex connection until data stream ends"
|
124
|
+
begin
|
125
|
+
ready_sockets.each do |socket|
|
126
|
+
data = socket.readpartial(chunk)
|
127
|
+
if socket == client_socket
|
128
|
+
# puts "SERVER <== CLIENT"
|
129
|
+
ssl_socket.write data
|
130
|
+
ssl_socket.flush
|
131
|
+
else
|
132
|
+
# puts "SERVER ==> CLIENT"
|
133
|
+
client_socket.write data
|
134
|
+
client_socket.flush
|
135
|
+
end
|
136
|
+
end
|
137
|
+
rescue EOFError
|
138
|
+
break
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
rescue StandardError => error
|
143
|
+
end
|
144
|
+
client_socket.close rescue StandardError
|
145
|
+
ssl_socket.close rescue StandardError
|
146
|
+
end
|
147
|
+
|
148
|
+
# puts "clean up the dead threads, and wait until we have available threads"
|
149
|
+
threads = threads.select { |thread| thread.alive? ? true : (thread.join; false) }
|
150
|
+
while threads.size >= max_threads
|
151
|
+
sleep 1
|
152
|
+
threads = threads.select { |thread| thread.alive? ? true : (thread.join; false) }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pagoda-tunnel"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "pagoda-tunnel"
|
7
|
+
s.version = Pagoda::Tunnel::VERSION
|
8
|
+
s.authors = ["Lyon"]
|
9
|
+
s.email = ["lyon@delorum.com"]
|
10
|
+
s.homepage = "http://www.pagodabox.com"
|
11
|
+
s.summary = %q{Pagodabox Tunnel gem}
|
12
|
+
s.description = %q{Pagodabox container tunnel. Allows users to tunnel into pagodabox db's.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "pagoda-tunnel"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# Development
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
# s.add_development_dependency "simplecov"
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
# Production
|
28
|
+
s.add_dependency "rest-client"
|
29
|
+
|
30
|
+
|
31
|
+
end
|
data/spec/tunnel_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
require 'pagoda-tunnel'
|
3
|
+
|
4
|
+
describe Pagoda::Tunnel do
|
5
|
+
|
6
|
+
it "can create a connection" do
|
7
|
+
tunnel = Pagoda::Tunnel.new(nil,nil,nil,nil,nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "cannot find an open port" do
|
11
|
+
tunnel = Pagoda::Tunnel.new(nil,nil,nil,nil,nil)
|
12
|
+
tunnel.port_available?("0.0.0.0", 3309).should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "finds an open port" do
|
16
|
+
sock = TCPServer.new("0.0.0.0", 40000)
|
17
|
+
tunnel = Pagoda::Tunnel.new(nil,nil,nil,nil,nil)
|
18
|
+
tunnel.port_available?("0.0.0.0", 40000).should == false
|
19
|
+
sock.close
|
20
|
+
end
|
21
|
+
|
22
|
+
it "searches until it finds an available port" do
|
23
|
+
tunnel = Pagoda::Tunnel.new(nil,nil,nil,nil,nil)
|
24
|
+
tunnel.should_receive(:port_available?).with("0.0.0.0", 40000).and_return false
|
25
|
+
tunnel.should_receive(:port_available?).with("0.0.0.0", 40001).and_return false
|
26
|
+
tunnel.should_receive(:port_available?).with("0.0.0.0", 40002).and_return false
|
27
|
+
tunnel.should_receive(:port_available?).with("0.0.0.0", 40003).and_return true
|
28
|
+
STDOUT.stub(:puts)
|
29
|
+
tunnel.next_available_port(40000).should == 40003
|
30
|
+
end
|
31
|
+
|
32
|
+
it "needs more testing"
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pagoda-tunnel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lyon
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &2156757880 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156757880
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rest-client
|
27
|
+
requirement: &2156757260 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2156757260
|
36
|
+
description: Pagodabox container tunnel. Allows users to tunnel into pagodabox db's.
|
37
|
+
email:
|
38
|
+
- lyon@delorum.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Rakefile
|
46
|
+
- lib/pagoda-tunnel.rb
|
47
|
+
- pagoda-tunnel.gemspec
|
48
|
+
- spec/tunnel_spec.rb
|
49
|
+
homepage: http://www.pagodabox.com
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: pagoda-tunnel
|
69
|
+
rubygems_version: 1.8.10
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Pagodabox Tunnel gem
|
73
|
+
test_files:
|
74
|
+
- spec/tunnel_spec.rb
|