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
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
[W] algorithm_negotiator
[R] algorithms the collection of algorithms currently being used
[W] ciphers
[W] compressors
[W] decompressors
[W] default_port
[W] hmacs
[R] host the hostname that was requested
[W] kexs
[W] logger
[W] packet_receiver
[W] packet_sender
[R] port the port that was requested
[R] session_id the unique session identifier
[W] socket_factory
[W] version_negotiator
Public Class methods
new( host, options={} ) {|self| ...}

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.

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

Returns the version string of this client.

    # File lib/net/ssh/transport/session.rb, line 65
65:         def self.version
66:           "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}"
67:         end
Public Instance methods
client_name()

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

     # File lib/net/ssh/transport/session.rb, line 120
120:         def client_name
121:           return @hostname if defined? @hostname
122: 
123:           sockaddr = @socket.getsockname
124:           begin
125:             @hostname =
126:               Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first
127:           rescue
128:             begin
129:               @hostname = Socket.getnameinfo( sockaddr ).first
130:             rescue
131:               begin
132:                 @hostname = Socket.gethostbyname( Socket.gethostname ).first
133:               rescue
134:                 @logger.error "the client ipaddr/name could not be determined"
135:               end
136:             end
137:           end
138: 
139:           return @hostname
140:         end
close()

Closes the connection.

     # File lib/net/ssh/transport/session.rb, line 155
155:         def close
156:           # TODO: send a DISCONNECT message to the server to close gracefully
157:           @socket.close
158:         end
peer()

Returns info about the remote peer

     # File lib/net/ssh/transport/session.rb, line 111
111:         def peer
112:           @peer ||= begin
113:             addr = @socket.getpeername
114:             ip_address = Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
115:             { :ip => ip_address, :port => @port.to_i, :host => @host }
116:           end
117:         end
ping!()

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.

     # File lib/net/ssh/transport/session.rb, line 333
333:         def ping!
334:           send_message [IGNORE, 4, "ping"].pack("cNA4")
335:         end
reader_ready?()

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.

     # File lib/net/ssh/transport/session.rb, line 327
327:         def reader_ready?
328:           IO.select([@socket],nil,nil,0) != nil
329:         end
send_message( message )

Sends the given payload, using the currently configured OutgoingPacketStream.

     # File lib/net/ssh/transport/session.rb, line 316
316:         def send_message( message )
317:           if @logger.debug?
318:             @logger.debug "sending message >>#{message.to_s.inspect}<<"
319:           end
320: 
321:           @packet_sender.send message
322:         end
wait_for_message()

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.

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