summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/net/ssh/proxy/command.rb75
-rw-r--r--lib/net/ssh/transport/packet_stream.rb11
2 files changed, 82 insertions, 4 deletions
diff --git a/lib/net/ssh/proxy/command.rb b/lib/net/ssh/proxy/command.rb
new file mode 100644
index 0000000..c22ad01
--- /dev/null
+++ b/lib/net/ssh/proxy/command.rb
@@ -0,0 +1,75 @@
+require 'socket'
+require 'net/ssh/proxy/errors'
+require 'net/ssh/ruby_compat'
+
+module Net; module SSH; module Proxy
+
+ # An implementation of a command proxy. To use it, instantiate it,
+ # then pass the instantiated object via the :proxy key to
+ # Net::SSH.start:
+ #
+ # require 'net/ssh/proxy/command'
+ #
+ # proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p')
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
+ # ...
+ # end
+ class Command
+
+ # The command line template
+ attr_reader :command_line_template
+
+ # The command line for the session
+ attr_reader :command_line
+
+ # Create a new socket factory that tunnels via a command executed
+ # with the user's shell, which is composed from the given command
+ # template. In the command template, `%h' will be substituted by
+ # the host name to connect and `%p' by the port.
+ def initialize(command_line_template)
+ @command_line_template = command_line_template
+ @command_line = nil
+ end
+
+ # Return a new socket connected to the given host and port via the
+ # proxy that was requested when the socket factory was instantiated.
+ def open(host, port)
+ command_line = @command_line_template.gsub(/%(.)/) {
+ case $1
+ when 'h'
+ host
+ when 'p'
+ port.to_s
+ when '%'
+ '%'
+ else
+ raise ArgumentError, "unknown key: #{$1}"
+ end
+ }
+ begin
+ io = IO.popen(command_line, "r+")
+ if result = Net::SSH::Compat.io_select([io], nil, [io], 60)
+ if result.last.any?
+ raise ConnectError, "command failed: #{command_line}"
+ end
+ else
+ raise ConnectError, "command timed out: #{command_line}"
+ end
+ rescue => e
+ raise ConnectError, "#{e}: #{command_line}"
+ end
+ @command_line = command_line
+ class << io
+ def send(data, flag)
+ write_nonblock(data)
+ end
+
+ def recv(size)
+ read_nonblock(size)
+ end
+ end
+ io
+ end
+ end
+
+end; end; end
diff --git a/lib/net/ssh/transport/packet_stream.rb b/lib/net/ssh/transport/packet_stream.rb
index 8404c56..41cae0d 100644
--- a/lib/net/ssh/transport/packet_stream.rb
+++ b/lib/net/ssh/transport/packet_stream.rb
@@ -59,10 +59,13 @@ module Net; module SSH; module Transport
# The IP address of the peer (remote) end of the socket, as reported by
# the socket.
def peer_ip
- @peer_ip ||= begin
- addr = getpeername
- Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
- end
+ @peer_ip ||=
+ if respond_to?(:getpeername)
+ addr = getpeername
+ Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
+ else
+ "<no hostip for proxy command>"
+ end
end
# Returns true if the IO is available for reading, and false otherwise.