diff options
-rw-r--r-- | lib/net/ssh/proxy/command.rb | 75 | ||||
-rw-r--r-- | lib/net/ssh/transport/packet_stream.rb | 11 |
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. |