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 | |
| [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 |
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 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
Returns the version string of this client.
[ show source ]
# File lib/net/ssh/transport/session.rb, line 65
65: def self.version
66: "#{PROTOCOL}-#{NAME}_#{Net::SSH::Version::STRING}"
67: end
Returns the name of the client‘s host, as reported by the socket.
[ show source ]
# 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
Closes the connection.
[ show source ]
# 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
Returns info about the remote peer
[ show source ]
# 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
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 333
333: def ping!
334: send_message [IGNORE, 4, "ping"].pack("cNA4")
335: 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 327
327: def reader_ready?
328: IO.select([@socket],nil,nil,0) != nil
329: end
Sends the given payload, using the currently configured OutgoingPacketStream.
[ show source ]
# 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
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 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