Class Net::SSH::Transport::Session
In: lib/net/ssh/transport/session.rb
Parent: Object

Represents a low-level SSH session, at the transport protocol level. This handles the algorithm negotiation and key exchange for any SSH connection.

Methods

Included Modules

Constants

Constants

NAME = "Ruby/Net::SSH"   The name that Net::SSH reports for itself
PROTOCOL = "SSH-2.0"   The SSH protocol supported by Net::SSH.
VALID_OPTIONS = [ :port, :host_key, :kex, :encryption, :hmac, :compression, :languages, :compression_level, :proxy, :timeout ]

Attributes

algorithm_negotiator  [W] 
algorithms  [R]  the collection of algorithms currently being used
ciphers  [W] 
compressors  [W] 
decompressors  [W] 
default_port  [W] 
hmacs  [W] 
kexs  [W] 
logger  [W] 
packet_receiver  [W] 
packet_sender  [W] 
session_id  [R]  the unique session identifier
socket_factory  [W] 
version_negotiator  [W] 

Public Class methods

Create a new connection to the given host. This will negotiate the algorithms to use and exchange the keys. A block must be given. The uninitialized self will be passed to the block, so that dependencies may be injected.

[Source]

     # File lib/net/ssh/transport/session.rb, line 71
 71:         def initialize( host, options={} )
 72:           @saved_message = nil
 73:           @session_id = nil
 74: 
 75:           yield self
 76: 
 77:           invalid_options = options.keys - VALID_OPTIONS
 78: 
 79:           unless invalid_options.empty?
 80:             raise ArgumentError,
 81:               "invalid option(s) to #{self.class}: #{invalid_options.inspect}"
 82:           end
 83: 
 84:           @logger.debug "connecting" if @logger.debug?
 85: 
 86:           @port = options[ :port ] || @default_port
 87:           @socket = timeout( options[:timeout] || 0 ) do
 88:             ( options[:proxy] || @socket_factory ).open( host, @port )
 89:           end
 90: 
 91:           @packet_sender.socket = @socket
 92:           @packet_receiver.socket = @socket
 93: 
 94:           @kex_info = {
 95:             :client_version_string => self.class.version,
 96:             :server_version_string =>
 97:               @version_negotiator.negotiate( @socket, self.class.version ) }
 98: 
 99:           @options = options
100:           kexinit
101:         end

Returns the version string of this client.

[Source]

    # File lib/net/ssh/transport/session.rb, line 59
59:         def self.version
60:           "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}"
61:         end

Public Instance methods

Returns the name of the client’s host, as reported by the socket.

[Source]

     # File lib/net/ssh/transport/session.rb, line 104
104:         def client_name
105:           return @hostname if defined? @hostname
106: 
107:           sockaddr = @socket.getsockname
108:           begin
109:             @hostname =
110:               Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first
111:           rescue
112:             begin
113:               @hostname = Socket.getnameinfo( sockaddr ).first
114:             rescue
115:               begin
116:                 @hostname = Socket.gethostbyname( Socket.gethostname ).first
117:               rescue
118:                 @logger.error "the client ipaddr/name could not be determined"
119:               end
120:             end
121:           end
122: 
123:           return @hostname
124:         end

Closes the connection.

[Source]

     # File lib/net/ssh/transport/session.rb, line 139
139:         def close
140:           # TODO: send a DISCONNECT message to the server to close gracefully
141:           @socket.close
142:         end

Sends an IGNORE packet to the server, as a way to ping the connection and make sure the server knows the client is still active.

[Source]

     # File lib/net/ssh/transport/session.rb, line 317
317:         def ping!
318:           send_message [IGNORE, 4, "ping"].pack("cNA4")
319:         end

Returns true if there are bytes to be read on the socket. Note that this only means there is an encrypted packet ready to be read, not that there is data available to any particular SSH channel.

[Source]

     # File lib/net/ssh/transport/session.rb, line 311
311:         def reader_ready?
312:           IO.select([@socket],nil,nil,0) != nil
313:         end

Sends the given payload, using the currently configured OutgoingPacketStream.

[Source]

     # File lib/net/ssh/transport/session.rb, line 300
300:         def send_message( message )
301:           if @logger.debug?
302:             @logger.debug "sending message >>#{message.to_s.inspect}<<"
303:           end
304: 
305:           @packet_sender.send message
306:         end

Waits for the next message from the server, handling common requests like DISCONNECT, IGNORE, DEBUG, and KEXINIT in the background. The next message is returned as a [ type, buffer ] tuple, where the buffer is a Net::SSH::Util::ReaderBuffer.

[Source]

     # File lib/net/ssh/transport/session.rb, line 234
234:         def wait_for_message
235:           buffer = type = nil
236: 
237:           if @saved_message
238:             type, buffer = @saved_message
239:             @logger.debug "returning saved message: #{type}" if @logger.debug?
240:             @saved_message = nil
241:           else
242:             loop do
243:               if @logger.debug?
244:                 @logger.debug "waiting for packet from server..."
245:               end
246: 
247:               buffer = @packet_receiver.get
248:               next unless buffer
249: 
250:               type = buffer.read_byte
251:               @logger.debug "got packet of type #{type}" if @logger.debug?
252: 
253:               case type
254:                 when DISCONNECT
255:                   reason_code = buffer.read_long
256:                   description = buffer.read_string
257:                   language = buffer.read_string
258:                   raise Net::SSH::Transport::Disconnect,
259:                     "disconnected: #{description} (#{reason_code})"
260: 
261:                 when IGNORE
262:                   # do nothing
263:                   @logger.info "received IGNORE message " +
264:                     "(#{buffer.read_string.inspect})" if @logger.debug?
265: 
266:                 when DEBUG
267:                   # do nothing
268:                   @logger.info "received DEBUG message" if @logger.debug?
269:                   always_display = buffer.read_bool
270:                   message = buffer.read_string
271:                   language = buffer.read_string
272:                   if always_display
273:                     @logger.warn "#{message} (#{language})" if @logger.warn?
274:                   else
275:                     @logger.debug "#{message} (#{language})" if @logger.debug?
276:                   end
277: 
278:                 when KEXINIT
279:                   # unless we're already doing a key-exchange, do key
280:                   # re-exchange
281:                   if !@doing_kexinit
282:                     @logger.info "re-key requested" if @logger.info?
283:                     @saved_message = [ type, buffer ]
284:                     kexinit
285:                   else
286:                     break
287:                   end
288: 
289:                 else
290:                   break
291:               end
292:             end
293:           end
294: 
295:           return type, buffer
296:         end

[Validate]