From 6f90bf351281e45df72a2d00387220576e1dba06 Mon Sep 17 00:00:00 2001 From: Fang-Pin Chang Date: Tue, 6 Dec 2016 08:33:55 -0800 Subject: [PATCH 1/5] 1. Add .ruby-gemset file for working with RVM. 2. Ignore *.gem files. 3. Add open() variant with 4 parameters that calls the @session.forward.local variant with 4 parameters as well. --- .gitignore | 2 ++ .ruby-gemset | 1 + lib/net/ssh/gateway.rb | 22 +++++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .ruby-gemset diff --git a/.gitignore b/.gitignore index e166318..b7ff631 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ /tmp/ *.swp .DS_Store + +*.gem diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000..3376840 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +net-ssh-gateway diff --git a/lib/net/ssh/gateway.rb b/lib/net/ssh/gateway.rb index 557c479..07f7632 100644 --- a/lib/net/ssh/gateway.rb +++ b/lib/net/ssh/gateway.rb @@ -101,12 +101,32 @@ def shutdown! # # If +local_port+ is not specified, the next available port will be used. def open(host, port, local_port=nil) + self.open(host, port, '127.0.0.1', local_port) + end + + # Opens a new port on the local host and forwards it to the given host/port + # via the gateway host. If a block is given, the newly allocated port + # number will be yielded to the block, and the port automatically closed + # (see #close) when the block finishes. Otherwise, the port number will be + # returned, and the caller is responsible for closing the port (#close). + # + # gateway.open('host', 80) do |port| + # # ... + # end + # + # port = gateway.open('host', 80) + # # ... + # gateway.close(port) + # + # The +local+host+ parameter specifies which IP address to bind to locally. + # If +local_port+ is not specified, the next available port will be used. + def open(host, port, local_host, local_port=nil) ensure_open! actual_local_port = local_port || next_port @session_mutex.synchronize do - @session.forward.local(actual_local_port, host, port) + @session.forward.local(local_host, actual_local_port, host, port) end if block_given? From 473d59b73439bdb1b1e08f199e21f18e660cb3b5 Mon Sep 17 00:00:00 2001 From: Fang-Pin Chang Date: Wed, 7 Dec 2016 21:28:40 -0800 Subject: [PATCH 2/5] 1. Move #open3() and #open4() to be private methods. 2. Update comments. --- lib/net/ssh/gateway.rb | 114 +++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/lib/net/ssh/gateway.rb b/lib/net/ssh/gateway.rb index 07f7632..52f0948 100644 --- a/lib/net/ssh/gateway.rb +++ b/lib/net/ssh/gateway.rb @@ -99,48 +99,23 @@ def shutdown! # # ... # gateway.close(port) # - # If +local_port+ is not specified, the next available port will be used. - def open(host, port, local_port=nil) - self.open(host, port, '127.0.0.1', local_port) - end - - # Opens a new port on the local host and forwards it to the given host/port - # via the gateway host. If a block is given, the newly allocated port - # number will be yielded to the block, and the port automatically closed - # (see #close) when the block finishes. Otherwise, the port number will be - # returned, and the caller is responsible for closing the port (#close). - # - # gateway.open('host', 80) do |port| - # # ... - # end - # - # port = gateway.open('host', 80) - # # ... - # gateway.close(port) + # This function takes variable arguments. See comments in the case statement + # for details. # - # The +local+host+ parameter specifies which IP address to bind to locally. + # The +local_host+ parameter specifies which network interface to bind to locally, + # and defaults to "127.0.0.1" if omitted. # If +local_port+ is not specified, the next available port will be used. - def open(host, port, local_host, local_port=nil) - ensure_open! - - actual_local_port = local_port || next_port - - @session_mutex.synchronize do - @session.forward.local(local_host, actual_local_port, host, port) - end - - if block_given? - begin - yield actual_local_port - ensure - close(actual_local_port) - end + def open(*args) + case args.size + when 2 # open(host, port) + open3(*args[0,2], nil) + when 3 # open(host, port, local_port) + open3(*args) + when 4 # open(host, port, local_host, local_port) + open4(*args) else - return actual_local_port + raise ArgumentError, "Expecting 2..4 arguments, but got #{args.size} instead." end - rescue Errno::EADDRINUSE - raise if local_port # if a local port was explicitly requested, bubble the error up - retry end # Cancels port-forwarding over an open port that was previously opened via @@ -213,4 +188,67 @@ def next_port port end end + + # Opens a new port on the local host and forwards it to the given host/port + # via the gateway host. If a block is given, the newly allocated port + # number will be yielded to the block, and the port automatically closed + # (see #close) when the block finishes. Otherwise, the port number will be + # returned, and the caller is responsible for closing the port (#close). + # + # gateway.open('host', 80) do |port| + # # ... + # end + # + # port = gateway.open('host', 80) + # # ... + # gateway.close(port) + # + # Unlike open4(), this method always binds to the loopback network + # interface # of "127.0.0.1". + # + # If +local_port+ is not specified, the next available port will be used. + def open3(host, port, local_port=nil) + open4(host, port, "127.0.0.1", local_port) + end + + # Opens a new port on the local host and forwards it to the given host/port + # via the gateway host. If a block is given, the newly allocated port + # number will be yielded to the block, and the port automatically closed + # (see #close) when the block finishes. Otherwise, the port number will be + # returned, and the caller is responsible for closing the port (#close). + # + # gateway.open('host', 80) do |port| + # # ... + # end + # + # port = gateway.open('host', 80) + # # ... + # gateway.close(port) + # + # Unlike open3(), this method specifies which network interface to bind to + # locally via the +local_host+ parameter. + # + # If +local_port+ is not specified, the next available port will be used. + def open4(host, port, local_host, local_port=nil) + ensure_open! + + actual_local_port = local_port || next_port + + @session_mutex.synchronize do + @session.forward.local(local_host, actual_local_port, host, port) + end + + if block_given? + begin + yield actual_local_port + ensure + close(actual_local_port) + end + else + return actual_local_port + end + rescue Errno::EADDRINUSE + raise if local_port # if a local port was explicitly requested, bubble the error up + retry + end end From dbef24a0a0c3f6a5bd769fc5cb53f9b1a616d097 Mon Sep 17 00:00:00 2001 From: Fang-Pin Chang Date: Wed, 7 Dec 2016 21:29:09 -0800 Subject: [PATCH 3/5] Update tests. --- .gitignore | 1 + net-ssh-gateway.gemspec | 2 ++ test/net/ssh/gateway_test.rb | 18 ++++++++++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index b7ff631..288ddfd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ *.swp .DS_Store +.byebug_history *.gem diff --git a/net-ssh-gateway.gemspec b/net-ssh-gateway.gemspec index 1861be7..b5ffccf 100644 --- a/net-ssh-gateway.gemspec +++ b/net-ssh-gateway.gemspec @@ -23,6 +23,8 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "minitest", "~> 5.8.4" spec.add_development_dependency "mocha" + spec.add_development_dependency "echoe" + spec.add_development_dependency "byebug" spec.add_runtime_dependency "net-ssh", ">= 2.6.5" end diff --git a/test/net/ssh/gateway_test.rb b/test/net/ssh/gateway_test.rb index e58c077..0540331 100644 --- a/test/net/ssh/gateway_test.rb +++ b/test/net/ssh/gateway_test.rb @@ -20,19 +20,25 @@ def test_shutdown_without_any_open_connections_should_terminate_session def test_open_should_start_local_ports_at_65535 gateway_session, gateway = new_gateway assert_equal 65535, gateway.open("app1", 22) - assert_equal [65535, "app1", 22], gateway_session.forward.active_locals[65535] + assert_equal ["127.0.0.1", 65535, "app1", 22], gateway_session.forward.active_locals[65535] end def test_open_should_decrement_port_and_retry_if_ports_are_in_use gateway_session, gateway = new_gateway(:reserved => lambda { |n| n > 65000 }) assert_equal 65000, gateway.open("app1", 22) - assert_equal [65000, "app1", 22], gateway_session.forward.active_locals[65000] + assert_equal ["127.0.0.1", 65000, "app1", 22], gateway_session.forward.active_locals[65000] end def test_open_with_explicit_local_port_should_use_that_port gateway_session, gateway = new_gateway assert_equal 8181, gateway.open("app1", 22, 8181) - assert_equal [8181, "app1", 22], gateway_session.forward.active_locals[8181] + assert_equal ["127.0.0.1", 8181, "app1", 22], gateway_session.forward.active_locals[8181] + end + + def test_open_with_explicit_local_host_and_port_should_use_that_port + gateway_session, gateway = new_gateway + assert_equal 8181, gateway.open("app1", 22, "1.2.3.4", 8181) + assert_equal ["1.2.3.4", 8181, "app1", 22], gateway_session.forward.active_locals[8181] end def test_ssh_should_return_connection_when_no_block_is_given @@ -40,7 +46,7 @@ def test_ssh_should_return_connection_when_no_block_is_given expect_connect_to("127.0.0.1", "user", :port => 65535).returns(result = mock("session")) newsess = gateway.ssh("app1", "user") assert_equal result, newsess - assert_equal [65535, "app1", 22], gateway_session.forward.active_locals[65535] + assert_equal ["127.0.0.1", 65535, "app1", 22], gateway_session.forward.active_locals[65535] end def test_ssh_with_block_should_yield_session_and_then_close_port @@ -91,9 +97,9 @@ def cancel_local(port) @active_locals.delete(port) end - def local(lport, host, rport) + def local(lhost, lport, host, rport) raise Errno::EADDRINUSE if @options[:reserved] && @options[:reserved][lport] - @active_locals[lport] = [lport, host, rport] + @active_locals[lport] = [lhost, lport, host, rport] end end From 26d6165c245a16437c50971366a9f9b852fc4114 Mon Sep 17 00:00:00 2001 From: Fang-Pin Chang Date: Mon, 12 Dec 2016 09:36:19 -0800 Subject: [PATCH 4/5] Add support for the optional second parameter when closing a forwarded port. --- lib/net/ssh/gateway.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/net/ssh/gateway.rb b/lib/net/ssh/gateway.rb index 52f0948..ab49662 100644 --- a/lib/net/ssh/gateway.rb +++ b/lib/net/ssh/gateway.rb @@ -120,11 +120,11 @@ def open(*args) # Cancels port-forwarding over an open port that was previously opened via # #open. - def close(port) + def close(port, bind_address="127.0.0.1") ensure_open! @session_mutex.synchronize do - @session.forward.cancel_local(port) + @session.forward.cancel_local(port, bind_address) end end From 1c8e0e336ce26515d4f3b631940fa202010881cb Mon Sep 17 00:00:00 2001 From: Fang-Pin Chang Date: Mon, 12 Dec 2016 09:47:09 -0800 Subject: [PATCH 5/5] Version bump to 2.1.0. --- lib/net/ssh/gateway/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/net/ssh/gateway/version.rb b/lib/net/ssh/gateway/version.rb index d1f0b63..7f57a80 100644 --- a/lib/net/ssh/gateway/version.rb +++ b/lib/net/ssh/gateway/version.rb @@ -1,7 +1,7 @@ module Net module SSH class Gateway - VERSION = "2.0.0" + VERSION = "2.1.0" end end end