The Forward::Driver class manages ports that have been forwarded. It will open a socket on each local port and listen for local connections, forwarding those connections over an SSH channel to the other end.

Methods
Attributes
[R] direct_channel_count The number of direct (local-to-remote) channels that have been opened.
[R] open_direct_channel_count The number of direct (local-to-remote) channels that are currently open.
Public Class methods
new( connection, buffers, log, handlers )

Create a new Driver instance.

    # File lib/net/ssh/service/forward/driver.rb, line 39
39:           def initialize( connection, buffers, log, handlers )
40:             @connection = connection
41:             @buffers = buffers
42:             @log = log
43:             @handlers = handlers
44: 
45:             @local_forwards = Hash.new
46:             @remote_forwards = Hash.new
47:             @direct_channel_count = 0
48:             @open_direct_channel_count = 0
49: 
50:             @connection.add_channel_open_handler(
51:               "forwarded-tcpip", &method(:do_open_channel) )
52:           end
Public Instance methods
active_locals()

Return an array of the active forwarded local connections. Each element of the array is another array containing the local port, and the remote host and port of the connection.

     # File lib/net/ssh/service/forward/driver.rb, line 229
229:           def active_locals
230:             @local_forwards.keys
231:           end
active_remotes()

Return an array of the active forwarded remote connections. Each element of the array is an integer representing the port number of the remote host that is being forwarded to the local client.

     # File lib/net/ssh/service/forward/driver.rb, line 236
236:           def active_remotes
237:             @remote_forwards.keys
238:           end
cancel_local( local_port, bind_address="127.0.0.1" )

Cease forwarding connections from the given local port (and interface). The parameters must match those given to a prior call to local. Existing forwarded connections are not affected by this call, but no more connections will be accepted on the local port.

     # File lib/net/ssh/service/forward/driver.rb, line 245
245:           def cancel_local( local_port, bind_address="127.0.0.1" )
246:             key = [ local_port.to_i, bind_address ]
247: 
248:             forward = @local_forwards[ key ]
249:             @local_forwards.delete key
250: 
251:             forward[ :socket ].close
252:             forward[ :thread ].terminate
253: 
254:             true
255:           end
cancel_remote( remote_port, remote_host="127.0.0.1" )

Cease forwarding connections from the given remote port. The remote_host parameter must match the corresponding parameter that was passed to remote. Existing forwarded connections are not affected by this call, but no more connections will be forwarded from the remote host to the local host via that port.

     # File lib/net/ssh/service/forward/driver.rb, line 262
262:           def cancel_remote( remote_port, remote_host="127.0.0.1" )
263:             writer = @buffers.writer
264:             writer.write_string remote_host
265:             writer.write_long remote_port.to_i
266: 
267:             @connection.global_request( "cancel-tcpip-forward", writer
268:             ) do |success, response|
269:               if success
270:                 @remote_forwards.delete remote_port
271:               else
272:                 raise Net::SSH::Exception,
273:                   "could not cancel remote forward request on " +
274:                   "(#{remote_port},#{remote_host})"
275:               end
276:             end
277:           end
direct_channel( local_port, remote_host, remote_port, handler, *data )

Open a direct "tcpip" channel to the remote machine, which will then forward the connection to the given remote-host and remote-port. The connection will appear to have come from the given port on the local machine.

The handler object may respond to the following messages, in order to respond to requests over the channel:

  • on_receive( channel, data ): when data is received over the channel (from the remote machine), this method will be invoked.
  • on_eof( channel ): when the remote machine will no longer send data, this method will be invoked. The client may continue to send data over the channel, however.
  • on_close( channel ): when the channel has been closed and is no longer valid for passing data, this method will be invoked.
  • confirm( channel, local_port, remote_host, remote_port, *data ): when the channel has been opened and the remote machine has confirmed it, this method will be invoked. The data parameters are the same parameters as were passed to the direct_channel method.
  • process( channel ): invoked after the channel is confirmed, to process the channel. It is invoked in a new Thread.

Only the process method is required—the others will only be invoked if the handler responds to them.

     # File lib/net/ssh/service/forward/driver.rb, line 79
 79:           def direct_channel( local_port, remote_host, remote_port,
 80:             handler, *data )
 81:           # begin
 82:             writer = @buffers.writer
 83:             writer.write_string remote_host
 84:             writer.write_long remote_port.to_i
 85:             writer.write_string "127.0.0.1"
 86:             writer.write_long local_port.to_i
 87: 
 88:             @direct_channel_count += 1
 89:             @open_direct_channel_count += 1
 90: 
 91:             if @log.debug?
 92:               @log.debug "opening direct channel for " +
 93:                 "#{local_port}:#{remote_host}:#{remote_port}"
 94:             end
 95: 
 96:             c = @connection.open_channel( 'direct-tcpip', writer ) do |channel|
 97:               if @log.debug?
 98:                 @log.debug "direct channel confirmed for " +
 99:                   "#{local_port}:#{remote_host}:#{remote_port}"
100:               end
101: 
102:               if handler.respond_to?( :on_receive )
103:                 channel.on_data( &handler.method(:on_receive) )
104:               end
105: 
106:               if handler.respond_to?( :on_eof )
107:                 channel.on_eof( &handler.method(:on_eof) )
108:               end
109: 
110:               channel.on_close do |ch|
111:                 @open_direct_channel_count -= 1
112:                 handler.on_close( ch ) if handler.respond_to?( :on_close )
113:               end
114: 
115:               if handler.respond_to?( :confirm )
116:                 handler.confirm( channel, local_port, remote_host,
117:                   remote_port, *data )
118:               end
119: 
120:               if handler.respond_to?( :process )
121:                 Thread.new { handler.process( channel ) }
122:               end
123:             end
124: 
125:             c.on_confirm_failed do |channel, code,desc,lang|
126:               raise Net::SSH::Exception, "could not open direct channel for " +
127:                 "#{local_port}:#{remote_host}:#{remote_port} (#{code}, #{desc})"
128:             end
129: 
130:             nil
131:           end
do_open_channel( connection, channel, data )

open a new channel as requested by the server

     # File lib/net/ssh/service/forward/driver.rb, line 280
280:           def do_open_channel( connection, channel, data )
281:             connected_address = data.read_string
282:             connected_port = data.read_long
283:             originator_address = data.read_string
284:             originator_port = data.read_long
285: 
286:             forward_data = @remote_forwards[ connected_port ]
287:             unless forward_data
288:               raise Net::SSH::Exception,
289:                 "recieved invalid channel-open command for a port forward " +
290:                 "that was never requested"
291:             end
292: 
293:             handler = forward_data[:handler]
294: 
295:             forward_data[:channel] = channel
296: 
297:             if handler.respond_to?( :on_open )
298:               handler.on_open( channel,
299:                                connected_address, connected_port,
300:                                originator_address, originator_port )
301:             end
302: 
303:             channel.on_data( &handler.method(:on_receive) )
304: 
305:             if handler.respond_to?( :on_close )
306:               channel.on_close( &handler.method(:on_close) )
307:             end
308: 
309:             if handler.respond_to?( :on_eof )
310:               channel.on_eof( &handler.method(:on_eof) )
311:             end
312:           end
local( *args )

Forward connections on the given local port, to the given remote host and remote port.

This method will return immediately, forwarding the connections asynchronously.

     # File lib/net/ssh/service/forward/driver.rb, line 138
138:           def local( *args )
139:             if args.length < 3 || args.length > 4
140:               raise ArgumentError,
141:                 "expected 3 or 4 parameters, got #{args.length+1}"
142:             end
143: 
144:             bind_address = "127.0.0.1"
145:             bind_address = args.shift if args.first.is_a? String
146: 
147:             local_port = args.shift.to_i
148:             remote_host = args.shift
149:             remote_port = args.shift
150: 
151:             Thread.new do
152:               begin
153:                 key = [ local_port.to_i, bind_address ]
154: 
155:                 if @log.debug?
156:                   @log.debug "starting local forwarding server on " +
157:                     key.inspect
158:                 end
159: 
160:                 socket = TCPServer.new( bind_address, local_port )
161: 
162:                 @local_forwards[ key ] = { :thread => Thread.current,
163:                                            :socket => socket }
164: 
165:                 if @log.debug?
166:                   @log.debug "listening for connections on #{key.inspect}"
167:                 end
168: 
169:                 while ( client = socket.accept )
170:                   @log.debug "#{key.inspect} received connection" if @log.debug?
171: 
172:                   direct_channel( local_port,
173:                                   remote_host,
174:                                   remote_port,
175:                                   @handlers[:local].call( client ) )
176:                 end
177: 
178:               rescue Exception => e
179:                 @log.error "error forwarding local connection: " +
180:                   "#{e.class} (#{e.message})\n   " +
181:                   e.backtrace.join( "\n  " )
182:               end
183:             end
184:           end
remote( handler, remote_port, remote_host="127.0.0.1" )

Initiate forwarding of the given remote port on the connected host. Forwarded packets will be passed to the given block as they are recieved. The remote-host represents the address that should be bound on the remote host, and defaults to ‘127.0.0.1’.

     # File lib/net/ssh/service/forward/driver.rb, line 190
190:           def remote( handler, remote_port, remote_host="127.0.0.1" )
191:             if @remote_forwards[ remote_port ]
192:               raise Net::SSH::Exception, "#{remote_port} is already forwarded"
193:             end
194: 
195:             writer = @buffers.writer
196:             writer.write_string remote_host
197:             writer.write_long remote_port.to_i
198: 
199:             @connection.global_request( "tcpip-forward", writer
200:             ) do |success, response|
201:               if success
202:                 remote_port = response.read_long if remote_port == 0
203:                 @remote_forwards[ remote_port ] = { :port => remote_port,
204:                                                     :handler => handler }
205:                 handler.setup( remote_port ) if handler.respond_to?( :setup )
206:               else
207:                 msg = "remote port #{remote_port} could not be forwarded " +
208:                       "to local host"
209:                 if handler.respond_to?( :error )
210:                   handler.error( msg )
211:                 else
212:                   raise Net::SSH::Exception, msg
213:                 end
214:               end
215:             end
216:           end
remote_to( port, host, remote_port, remote_host="127.0.0.1" )

A convenience method for setting up a forwarded connection from the given port on the remote host, to the given host and port (local).

     # File lib/net/ssh/service/forward/driver.rb, line 220
220:           def remote_to( port, host, remote_port, remote_host="127.0.0.1" )
221:           # begin
222:             remote( @handlers[:remote].call( port, host ), remote_port,
223:               remote_host )
224:           end

[Validate]