Represents a low-level SSH session, at the transport protocol level. This handles the algorithm negotiation and key exchange for any SSH connection.
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 ] |
[W] | algorithm_negotiator | |
[R] | algorithms | the collection of algorithms currently being used |
[W] | ciphers | |
[W] | compressors | |
[W] | decompressors | |
[W] | default_port | |
[W] | hmacs | |
[W] | kexs | |
[W] | logger | |
[W] | packet_receiver | |
[W] | packet_sender | |
[R] | session_id | the unique session identifier |
[W] | socket_factory | |
[W] | version_negotiator |
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.
[ show 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: @port = options[ :port ] || @default_port 85: @socket = timeout( options[:timeout] || 0 ) do 86: ( options[:proxy] || @socket_factory ).open( host, @port ) 87: end 88: 89: @packet_sender.socket = @socket 90: @packet_receiver.socket = @socket 91: 92: @kex_info = { 93: :client_version_string => self.class.version, 94: :server_version_string => 95: @version_negotiator.negotiate( @socket, self.class.version ) } 96: 97: @options = options 98: kexinit 99: end
Returns the version string of this client.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 59 59: def self.version 60: "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}" 61: end
Returns the name of the client’s host, as reported by the socket.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 102 102: def client_name 103: return @hostname if defined? @hostname 104: 105: sockaddr = @socket.getsockname 106: begin 107: @hostname = 108: Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first 109: rescue 110: begin 111: @hostname = Socket.getnameinfo( sockaddr ).first 112: rescue 113: begin 114: @hostname = Socket.gethostbyname( Socket.gethostname ).first 115: rescue 116: @logger.error "the client ipaddr/name could not be determined" 117: end 118: end 119: end 120: 121: return @hostname 122: end
Closes the connection.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 137 137: def close 138: # TODO: send a DISCONNECT message to the server to close gracefully 139: @socket.close 140: 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.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 315 315: def ping! 316: send_message [IGNORE, 4, "ping"].pack("cNA4") 317: 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.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 309 309: def reader_ready? 310: IO.select([@socket],nil,nil,0) != nil 311: end
Sends the given payload, using the currently configured OutgoingPacketStream.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 298 298: def send_message( message ) 299: if @logger.debug? 300: @logger.debug "sending message >>#{message.to_s.inspect}<<" 301: end 302: 303: @packet_sender.send message 304: 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.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 232 232: def wait_for_message 233: buffer = type = nil 234: 235: if @saved_message 236: type, buffer = @saved_message 237: @logger.debug "returning saved message: #{type}" if @logger.debug? 238: @saved_message = nil 239: else 240: loop do 241: if @logger.debug? 242: @logger.debug "waiting for packet from server..." 243: end 244: 245: buffer = @packet_receiver.get 246: next unless buffer 247: 248: type = buffer.read_byte 249: @logger.debug "got packet of type #{type}" if @logger.debug? 250: 251: case type 252: when DISCONNECT 253: reason_code = buffer.read_long 254: description = buffer.read_string 255: language = buffer.read_string 256: raise Net::SSH::Transport::Disconnect, 257: "disconnected: #{description} (#{reason_code})" 258: 259: when IGNORE 260: # do nothing 261: @logger.info "received IGNORE message " + 262: "(#{buffer.read_string.inspect})" if @logger.debug? 263: 264: when DEBUG 265: # do nothing 266: @logger.info "received DEBUG message" if @logger.debug? 267: always_display = buffer.read_bool 268: message = buffer.read_string 269: language = buffer.read_string 270: if always_display 271: @logger.warn "#{message} (#{language})" if @logger.warn? 272: else 273: @logger.debug "#{message} (#{language})" if @logger.debug? 274: end 275: 276: when KEXINIT 277: # unless we're already doing a key-exchange, do key 278: # re-exchange 279: if !@doing_kexinit 280: @logger.info "re-key requested" if @logger.info? 281: @saved_message = [ type, buffer ] 282: kexinit 283: else 284: break 285: end 286: 287: else 288: break 289: end 290: end 291: end 292: 293: return type, buffer 294: end