Class: Net::SSH::Proxy::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/net/ssh/proxy/command.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command_line_template) ⇒ Command

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.



30
31
32
33
# File 'lib/net/ssh/proxy/command.rb', line 30

def initialize(command_line_template)
  @command_line_template = command_line_template
  @command_line = nil
end

Instance Attribute Details

#command_lineObject (readonly)

The command line for the session



24
25
26
# File 'lib/net/ssh/proxy/command.rb', line 24

def command_line
  @command_line
end

#command_line_templateObject (readonly)

The command line template



21
22
23
# File 'lib/net/ssh/proxy/command.rb', line 21

def command_line_template
  @command_line_template
end

Instance Method Details

#open(host, port, connection_options = nil) ⇒ Object

Return a new socket connected to the given host and port via the proxy that was requested when the socket factory was instantiated.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/net/ssh/proxy/command.rb', line 37

def open(host, port, connection_options = nil)
  command_line = @command_line_template.gsub(/%(.)/) {
    case $1
    when 'h'
      host
    when 'p'
      port.to_s
    when 'r'
      remote_user = connection_options && connection_options[:remote_user]
      if remote_user
        remote_user
      else
        raise ArgumentError, "remote user name not available"
      end
    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 "command failed"
      end
    else
      raise "command timed out"
    end
  rescue => e
    raise ConnectError, "#{e}: #{command_line}"
  end
  @command_line = command_line
  if Gem.win_platform?
    # read_nonblock and write_nonblock are not available on Windows
    # pipe. Use sysread and syswrite as a replacement works.
    def io.send(data, flag)
      syswrite(data)
    end

    def io.recv(size)
      sysread(size)
    end
  else
    def io.send(data, flag)
      begin
        result = write_nonblock(data)
      rescue IO::WaitWritable, Errno::EINTR
        IO.select(nil, [self])
        retry
      end
      result
    end

    def io.recv(size)
      begin
        result = read_nonblock(size)
      rescue IO::WaitReadable, Errno::EINTR
        timeout_in_seconds = 20
        if IO.select([self], nil, [self], timeout_in_seconds) == nil
          raise "Unexpected spurious read wakeup"
        end
        retry
      end
      result
    end
  end
  io
end