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 ] |
[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 69 69: def initialize( host, options={} ) 70: @saved_message = nil 71: @session_id = nil 72: 73: yield self 74: 75: invalid_options = options.keys - VALID_OPTIONS 76: 77: unless invalid_options.empty? 78: raise ArgumentError, 79: "invalid option(s) to #{self.class}: #{invalid_options.inspect}" 80: end 81: 82: @port = options[ :port ] || @default_port 83: @socket = ( options[:proxy] || @socket_factory ).open( host, @port ) 84: 85: @packet_sender.socket = @socket 86: @packet_receiver.socket = @socket 87: 88: @kex_info = { 89: :client_version_string => self.class.version, 90: :server_version_string => 91: @version_negotiator.negotiate( @socket, self.class.version ) } 92: 93: @options = options 94: kexinit 95: end
Returns the version string of this client.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 58 58: def self.version 59: "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}" 60: end
Returns the name of the client’s host, as reported by the socket.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 98 98: def client_name 99: return @hostname if defined? @hostname 100: 101: sockaddr = @socket.getsockname 102: begin 103: @hostname = 104: Socket.getnameinfo( sockaddr, Socket::NI_NAMEREQD ).first 105: rescue 106: begin 107: @hostname = Socket.getnameinfo( sockaddr ).first 108: rescue 109: begin 110: @hostname = Socket.gethostbyname( Socket.gethostname ).first 111: rescue 112: @logger.error "the client ipaddr/name could not be determined" 113: end 114: end 115: end 116: 117: return @hostname 118: end
Closes the connection.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 133 133: def close 134: # TODO: send a DISCONNECT message to the server to close gracefully 135: @socket.close 136: 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 305 305: def reader_ready? 306: IO.select([@socket],nil,nil,0) != nil 307: end
Sends the given payload, using the currently configured OutgoingPacketStream.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 294 294: def send_message( message ) 295: if @logger.debug? 296: @logger.debug "sending message >>#{message.to_s.inspect}<<" 297: end 298: 299: @packet_sender.send message 300: 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 228 228: def wait_for_message 229: buffer = type = nil 230: 231: if @saved_message 232: type, buffer = @saved_message 233: @logger.debug "returning saved message: #{type}" if @logger.debug? 234: @saved_message = nil 235: else 236: loop do 237: if @logger.debug? 238: @logger.debug "waiting for packet from server..." 239: end 240: 241: buffer = @packet_receiver.get 242: next unless buffer 243: 244: type = buffer.read_byte 245: @logger.debug "got packet of type #{type}" if @logger.debug? 246: 247: case type 248: when DISCONNECT 249: reason_code = buffer.read_long 250: description = buffer.read_string 251: language = buffer.read_string 252: raise Net::SSH::Transport::Disconnect, 253: "disconnected: #{description} (#{reason_code})" 254: 255: when IGNORE 256: # do nothing 257: @logger.info "received IGNORE message " + 258: "(#{buffer.read_string.inspect})" if @logger.debug? 259: 260: when DEBUG 261: # do nothing 262: @logger.info "received DEBUG message" if @logger.debug? 263: always_display = buffer.read_bool 264: message = buffer.read_string 265: language = buffer.read_string 266: if always_display 267: @logger.warn "#{message} (#{language})" if @logger.warn? 268: else 269: @logger.debug "#{message} (#{language})" if @logger.debug? 270: end 271: 272: when KEXINIT 273: # unless we're already doing a key-exchange, do key 274: # re-exchange 275: if !@doing_kexinit 276: @logger.info "re-key requested" if @logger.info? 277: @saved_message = [ type, buffer ] 278: kexinit 279: else 280: break 281: end 282: 283: else 284: break 285: end 286: end 287: end 288: 289: return type, buffer 290: end