class Net::SSH::Transport::Session
SSH session via Net::SSH.start.
but will instead be created for you automatically when you create a new
never be instantiated directly (unless you really know what you’re about),
implements basic message exchanging and protocol initialization. It will
The transport layer represents the lowest level of the SSH protocol, and
def close
def close socket.cleanup socket.close end
def closed?
def closed? socket.closed? end
def configure_client(options = {})
options. This is typically used to define the cipher, compression, and
Configure's the packet stream's client state with the given set of
def configure_client(options = {}) socket.client.set(options) end
def configure_server(options = {})
options. This is typically used to define the cipher, compression, and
Configure's the packet stream's server state with the given set of
def configure_server(options = {}) socket.server.set(options) end
def enqueue_message(message)
Enqueues the given message, such that it will be sent at the earliest
def enqueue_message(message) socket.enqueue_packet(message) end
def hint(which, value = true)
Sets a new hint for the packet stream, which the packet stream may use
def hint(which, value = true) socket.hints[which] = value end
def host_as_string
Returns the host (and possibly IP address) in a format compatible with
def host_as_string @host_as_string ||= begin string = "#{host}" string = "[#{string}]:#{port}" if port != DEFAULT_PORT peer_ip = socket.peer_ip if peer_ip != Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP && peer_ip != host string2 = peer_ip string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT string << "," << string2 end string end end
def host_keys
def host_keys @host_keys ||= begin known_hosts = options.fetch(:known_hosts, KnownHosts) known_hosts.search_for(options[:host_key_alias] || host_as_string, options) end end
def initialize(host, options = {})
the initial key exchange completes, leaving you with a ready-to-use
Instantiates a new transport layer abstraction. This will block until
def initialize(host, options = {}) self.logger = options[:logger] @host = host @port = options[:port] || DEFAULT_PORT @bind_address = options[:bind_address] || nil @options = options @socket = if (factory = options[:proxy]) debug { "establishing connection to #{@host}:#{@port} through proxy" } factory.open(@host, @port, options) else debug { "establishing connection to #{@host}:#{@port}" } Socket.tcp(@host, @port, @bind_address, nil, connect_timeout: options[:timeout]) end @socket.extend(PacketStream) @socket.logger = @logger debug { "connection established" } @queue = [] @host_key_verifier = select_host_key_verifier(options[:verify_host_key]) @server_version = ServerVersion.new(socket, logger, options[:timeout]) @algorithms = Algorithms.new(self, options) @algorithms.start wait { algorithms.initialized? } rescue Errno::ETIMEDOUT raise Net::SSH::ConnectionTimeout end
def next_message
Blocks until a new packet is available to be read, and returns that
def next_message poll_message(:block) end
def peer
Returns a hash of information about the peer (remote) side of the socket,
def peer @peer ||= { ip: socket.peer_ip, port: @port.to_i, host: @host, canonized: host_as_string } end
def poll_message(mode = :nonblock, consume_queue = true)
is not in process, and consume_queue is true, packets will be first
received, it will be enqueued and otherwise ignored. When a key-exchange
If a key-exchange is in process and a disallowed packet type is
be returned.
DEBUG, and KEXINIT) are handled silently by this method, and will never
available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED,
waiting to be read. Otherwise, this will block until a packet is
default), this will not block and will return nil if there are no packets
Tries to read the next packet from the socket. If mode is :nonblock (the
def poll_message(mode = :nonblock, consume_queue = true) loop do return @queue.shift if consume_queue && @queue.any? && algorithms.allow?(@queue.first) packet = socket.next_packet(mode, options[:timeout]) return nil if packet.nil? case packet.type when DISCONNECT raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})" when IGNORE debug { "IGNORE packet received: #{packet[:data].inspect}" } when UNIMPLEMENTED lwarn { "UNIMPLEMENTED: #{packet[:number]}" } when DEBUG send(packet[:always_display] ? :fatal : :debug) { packet[:message] } when KEXINIT algorithms.accept_kexinit(packet) else return packet if algorithms.allow?(packet) push(packet) end end end
def push(packet)
#poll_message will return packets from the queue in the order they
Adds the given packet to the packet queue. If the queue is non-empty,
def push(packet) @queue.push(packet) end
def rekey!
If a rekey is already pending, this returns immediately, having no
Requests a rekey operation, and blocks until the operation completes.
def rekey! if !algorithms.pending? algorithms.rekey! wait { algorithms.initialized? } end end
def rekey_as_needed
rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?)
Returns immediately if a rekey is already in process. Otherwise, if a
def rekey_as_needed return if algorithms.pending? socket.if_needs_rekey? { rekey! } end
def select_host_key_verifier(verifier)
Values false, true, and :very were deprecated in
it is returned directly. Otherwise, an exception is raised.
If the argument happens to respond to :verify and :verify_signature,
- :always (secure)
- :accept_new (insecure)
- :accept_new_or_local_tunnel (insecure)
- :never (very insecure)
a verifier, like `::Net::SSH::Verifiers::Never`.
Usually, the argument is a symbol like `:never` which corresponds to
the parameter.
Instantiates a new host-key verification class, based on the value of
def select_host_key_verifier(verifier) case verifier when false Kernel.warn('verify_host_key: false is deprecated, use :never') Net::SSH::Verifiers::Never.new when :never then Net::SSH::Verifiers::Never.new when true Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel') Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new when :accept_new_or_local_tunnel, nil then Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new when :very Kernel.warn('verify_host_key: :very is deprecated, use :accept_new') Net::SSH::Verifiers::AcceptNew.new when :accept_new then Net::SSH::Verifiers::AcceptNew.new when :secure then Kernel.warn('verify_host_key: :secure is deprecated, use :always') Net::SSH::Verifiers::Always.new when :always then Net::SSH::Verifiers::Always.new else if verifier.respond_to?(:verify) if verifier.respond_to?(:verify_signature) verifier else Kernel.warn("Warning: verifier without :verify_signature is deprecated") CompatibleVerifier.new(verifier) end else raise( ArgumentError, "Invalid argument to :verify_host_key (or deprecated " \ ":paranoid): #{verifier.inspect}" ) end end end
def send_message(message)
Sends the given message via the packet stream, blocking until the
def send_message(message) socket.send_packet(message) end
def service_request(service)
Returns a new service_request packet for the given service name, ready
def service_request(service) Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service) end
def shutdown!
when the connection needs to close but you don't know the status of the
never be done, but it might be necessary (in a rescue clause, for instance,
Performs a "hard" shutdown of the connection. In general, this should
def shutdown! error { "forcing connection closed" } socket.close end
def wait
this just waits long enough to see if there are any pending packets. Any
Waits (blocks) until the given block returns true. If no block is given,
def wait loop do break if block_given? && yield message = poll_message(:nonblock, false) push(message) if message break if !block_given? end end