Class Net::SSH::Service::Forward

  1. lib/net/ssh/service/forward.rb
Parent: Object

This class implements various port forwarding services for use by Net::SSH clients. The Forward class should never need to be instantiated directly; instead, it should be accessed via the singleton instance returned by Connection::Session#forward:

ssh.forward.local(1234, "www.capify.org", 80)

Included modules

  1. Loggable

Attributes

session [R] The underlying connection service instance that the port-forwarding services employ.

Public class methods

new (session)

Instantiates a new Forward service instance atop the given connection service session. This will register new channel open handlers to handle the specialized channels that the SSH port forwarding protocols employ.

[show source]
    # File lib/net/ssh/service/forward.rb, line 24
24:     def initialize(session)
25:       @session = session
26:       self.logger = session.logger
27:       @remote_forwarded_ports = {}
28:       @local_forwarded_ports = {}
29:       @agent_forwarded = false
30: 
31:       session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip))
32:       session.on_open_channel('auth-agent', &method(:auth_agent_channel))
33:       session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel))
34:     end

Public instance methods

active_locals ()

Returns a list of all active locally forwarded ports. The returned value is an array of arrays, where each element is a two-element tuple consisting of the local port and bind address corresponding to the forwarding port.

[show source]
     # File lib/net/ssh/service/forward.rb, line 101
101:     def active_locals
102:       @local_forwarded_ports.keys
103:     end
active_remotes ()

Returns all active forwarded remote ports. The returned value is an array of two-element tuples, where the first element is the port on the remote host and the second is the bind address.

[show source]
     # File lib/net/ssh/service/forward.rb, line 159
159:     def active_remotes
160:       @remote_forwarded_ports.keys
161:     end
agent (channel)

Enables SSH agent forwarding on the given channel. The forwarded agent will remain active even after the channel closes—the channel is only used as the transport for enabling the forwarded connection. You should never need to call this directly—it is called automatically the first time a session channel is opened, when the connection was created with :forward_agent set to true:

Net::SSH.start("remote.host", "me", :forwrd_agent => true) do |ssh|
  ssh.open_channel do |ch|
    # agent will be automatically forwarded by this point
  end
  ssh.loop
end
[show source]
     # File lib/net/ssh/service/forward.rb, line 176
176:     def agent(channel)
177:       return if @agent_forwarded
178:       @agent_forwarded = true
179: 
180:       channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success|
181:         if success
182:           debug { "authentication agent forwarding is active" }
183:         else
184:           achannel.send_channel_request("auth-agent-req") do |a2channel, success2|
185:             if success2
186:               debug { "authentication agent forwarding is active" }
187:             else
188:               error { "could not establish forwarding of authentication agent" }
189:             end
190:           end
191:         end
192:       end
193:     end
cancel_local (port, bind_address="127.0.0.1")

Terminates an active local forwarded port. If no such forwarded port exists, this will raise an exception. Otherwise, the forwarded connection is terminated.

ssh.forward.cancel_local(1234)
ssh.forward.cancel_local(1234, "0.0.0.0")
[show source]
    # File lib/net/ssh/service/forward.rb, line 90
90:     def cancel_local(port, bind_address="127.0.0.1")
91:       socket = @local_forwarded_ports.delete([port, bind_address])
92:       socket.shutdown rescue nil
93:       socket.close rescue nil
94:       session.stop_listening_to(socket)
95:     end
cancel_remote (port, host="127.0.0.1")

Requests that a remote forwarded port be cancelled. The remote forwarded port on the remote host, bound to the given address on the remote host, will be terminated, but not immediately. This method returns immediately after queueing the request to be sent to the server. If for some reason the port cannot be cancelled, an exception will be raised (asynchronously).

If you want to know when the connection has been cancelled, it will no longer be present in the active_remotes list. If you want to block until the port is no longer active, you could do something like this:

ssh.forward.cancel_remote(1234, "0.0.0.0")
ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
[show source]
     # File lib/net/ssh/service/forward.rb, line 146
146:     def cancel_remote(port, host="127.0.0.1")
147:       session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response|
148:         if success
149:           @remote_forwarded_ports.delete([port, host])
150:         else
151:           raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}"
152:         end
153:       end
154:     end
local (*args)

Starts listening for connections on the local host, and forwards them to the specified remote host/port via the SSH connection. This method accepts either three or four arguments. When four arguments are given, they are:

  • the local address to bind to
  • the local port to listen on
  • the remote host to forward connections to
  • the port on the remote host to connect to

If three arguments are given, it is as if the local bind address is “127.0.0.1“, and the rest are applied as above.

ssh.forward.local(1234, "www.capify.org", 80)
ssh.forward.local("0.0.0.0", 1234, "www.capify.org", 80)
[show source]
    # File lib/net/ssh/service/forward.rb, line 51
51:     def local(*args)
52:       if args.length < 3 || args.length > 4
53:         raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}"
54:       end
55: 
56:       bind_address = "127.0.0.1"
57:       bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/
58: 
59:       local_port = args.shift.to_i
60:       remote_host = args.shift
61:       remote_port = args.shift.to_i
62: 
63:       socket = TCPServer.new(bind_address, local_port)
64: 
65:       @local_forwarded_ports[[local_port, bind_address]] = socket
66: 
67:       session.listen_to(socket) do |server|
68:         client = server.accept
69:         debug { "received connection on #{bind_address}:#{local_port}" }
70: 
71:         channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, :long, local_port) do |achannel|
72:           achannel.info { "direct channel established" }
73:         end
74: 
75:         prepare_client(client, channel, :local)
76:   
77:         channel.on_open_failed do |ch, code, description|
78:           channel.error { "could not establish direct channel: #{description} (#{code})" }
79:           channel[:socket].close
80:         end
81:       end
82:     end
remote (port, host, remote_port, remote_host="127.0.0.1")

Requests that all connections on the given remote-port be forwarded via the local host to the given port/host. The last argument describes the bind address on the remote host, and defaults to 127.0.0.1.

This method will return immediately, but the port will not actually be forwarded immediately. If the remote server is not able to begin the listener for this request, an exception will be raised asynchronously.

If you want to know when the connection is active, it will show up in the active_remotes list. If you want to block until the port is active, you could do something like this:

ssh.forward.remote(80, "www.google.com", 1234, "0.0.0.0")
ssh.loop { !ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
[show source]
     # File lib/net/ssh/service/forward.rb, line 119
119:     def remote(port, host, remote_port, remote_host="127.0.0.1")
120:       session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
121:         if success
122:           debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
123:           @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
124:         else
125:           error { "remote forwarding request failed" }
126:           raise Net::SSH::Exception, "remote forwarding request failed"
127:         end
128:       end
129:     end
remote_to (port, host, remote_port, remote_host="127.0.0.1")

Alias for remote