class ESSH::Transport::Session

The transport layer represents the lowest level of the SSH protocol, and implements basic message exchanging and protocol initialization. It will never be instantiated directly (unless you really know what you're about), but will instead be created for you automatically when you create a new SSH session via Net::SSH.start.

Constants

DEFAULT_PORT

The standard port for the SSH protocol.

ServerVersion

Attributes

algorithms[R]

The Algorithms instance used to perform key exchanges.

host[R]

The host to connect to, as given to the constructor.

host_key_verifier[R]

The host-key verifier object used to verify host keys, to ensure that the connection is not being spoofed.

options[R]

The hash of options that were given to the object at initialization.

port[R]

The port number to connect to, as given in the options to the constructor. If no port number was given, this will default to DEFAULT_PORT.

reactor[R]

The event loop that this SSH session is running on

server_version[R]

The ServerVersion instance that encapsulates the negotiated protocol version.

socket[R]

The underlying socket object being used to communicate with the remote host.

Public Class Methods

new(host, **options) click to toggle source

Instantiates a new transport layer abstraction. This will block until the initial key exchange completes, leaving you with a ready-to-use transport session.

# File lib/evented-ssh/transport/session.rb, line 63
def initialize(host, **options)
    self.logger = options[:logger]

    @reactor = ::Libuv.reactor

    @host = host
    @port = options[:port] || DEFAULT_PORT
    @bind_address = options[:bind_address] || '0.0.0.0'
    @options = options

    debug { "establishing connection to #{@host}:#{@port}" }

    actual_host = if IPAddress.valid?(@host)
        @host
    else
        @reactor.lookup(@host)[0][0]
    end

    @socket = PacketStream.new(self, **options)
    @socket.connect(actual_host, @port)

    debug { "connection established" }

    @host_key_verifier = select_host_key_verifier(options[:paranoid])
    @algorithms = Algorithms.new(self, options)
    @server_version = ServerVersion.new
    @socket.algorithms = @algorithms

    socket.direct_write "#{::Net::SSH::Transport::ServerVersion::PROTO_VERSION}\r\n"
    socket.start_read

    @algorithms.ready # Wait for this to complete
end

Public Instance Methods

close() click to toggle source

Cleans up (see PacketStream#cleanup) and closes the underlying socket.

# File lib/evented-ssh/transport/session.rb, line 129
def close
    info { "closing connection" }
    socket.shutdown
end
closed?() click to toggle source

Returns true if the underlying socket has been closed.

# File lib/evented-ssh/transport/session.rb, line 124
def closed?
    socket.closed?
end
configure_client(options={}) click to toggle source

Configure's the packet stream's client state with the given set of options. This is typically used to define the cipher, compression, and hmac algorithms to use when sending packets to the server.

# File lib/evented-ssh/transport/session.rb, line 206
def configure_client(options={})
    socket.client.set(options)
end
configure_server(options={}) click to toggle source

Configure's the packet stream's server state with the given set of options. This is typically used to define the cipher, compression, and hmac algorithms to use when reading packets from the server.

# File lib/evented-ssh/transport/session.rb, line 213
def configure_server(options={})
    socket.server.set(options)
end
enqueue_message(message) click to toggle source

Enqueues the given message, such that it will be sent at the earliest opportunity. This does not block, but returns immediately.

# File lib/evented-ssh/transport/session.rb, line 199
def enqueue_message(message)
    socket.enqueue_packet(message)
end
hint(which, value=true) click to toggle source

Sets a new hint for the packet stream, which the packet stream may use to change its behavior. (See PacketStream#hints).

# File lib/evented-ssh/transport/session.rb, line 219
def hint(which, value=true)
    socket.hints[which] = value
end
host_as_string() click to toggle source

Returns the host (and possibly IP address) in a format compatible with SSH known-host files.

# File lib/evented-ssh/transport/session.rb, line 106
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 != host
            string2 = peer_ip
            string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
            string << "," << string2
        end

        string
    end
end
host_keys() click to toggle source
# File lib/evented-ssh/transport/session.rb, line 97
def host_keys
    @host_keys ||= begin
        known_hosts = options.fetch(:known_hosts, ::Net::SSH::KnownHosts)
        known_hosts.search_for(options[:host_key_alias] || host_as_string, options)
    end
end
next_message() click to toggle source

Blocks until a new packet is available to be read, and returns that packet. See poll_message.

# File lib/evented-ssh/transport/session.rb, line 175
def next_message
    socket.get_packet
end
peer() click to toggle source

Returns a hash of information about the peer (remote) side of the socket, including :ip, :port, :host, and :canonized (see host_as_string).

# File lib/evented-ssh/transport/session.rb, line 169
def peer
    @peer ||= { ip: socket.peer_ip, port: @port.to_i, host: @host, canonized: host_as_string }
end
poll_message() click to toggle source
# File lib/evented-ssh/transport/session.rb, line 179
def poll_message
    socket.get_packet
end
push(packet) click to toggle source

Adds the given packet to the packet queue. If the queue is non-empty, poll_message will return packets from the queue in the order they were received.

# File lib/evented-ssh/transport/session.rb, line 186
def push(packet)
    socket.queue_packet(packet)
    process_waiting
end
rekey!() click to toggle source

Requests a rekey operation, and blocks until the operation completes. If a rekey is already pending, this returns immediately, having no effect.

# File lib/evented-ssh/transport/session.rb, line 152
def rekey!
    if !algorithms.pending?
        algorithms.rekey!
        @algorithms.pending?&.promise&.value # Wait for this to complete
    end
end
rekey_as_needed() click to toggle source

Returns immediately if a rekey is already in process. Otherwise, if a rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?) one is performed, causing this method to block until it completes.

# File lib/evented-ssh/transport/session.rb, line 162
def rekey_as_needed
    return if algorithms.pending?
    socket.if_needs_rekey? { rekey! }
end
send_message(message) click to toggle source

Sends the given message via the packet stream, blocking until the entire message has been sent.

# File lib/evented-ssh/transport/session.rb, line 193
def send_message(message)
    socket.enqueue_packet(message)
end
service_request(service) click to toggle source

Returns a new service_request packet for the given service name, ready for sending to the server.

# File lib/evented-ssh/transport/session.rb, line 145
def service_request(service)
    ::Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service)
end
shutdown!() click to toggle source

Performs a “hard” shutdown of the connection. In general, this should never be done, but it might be necessary (in a rescue clause, for instance, when the connection needs to close but you don't know the status of the underlying protocol's state).

# File lib/evented-ssh/transport/session.rb, line 138
def shutdown!
    error { "forcing connection closed" }
    socket.close
end

Private Instance Methods

select_host_key_verifier(paranoid) click to toggle source

Instantiates a new host-key verification class, based on the value of the parameter. When true or nil, the default Lenient verifier is returned. If it is false, the Null verifier is returned, and if it is :very, the Strict verifier is returned. If it is :secure, the even more strict Secure verifier is returned. If the argument happens to respond to :verify, it is returned directly. Otherwise, an exception is raised.

# File lib/evented-ssh/transport/session.rb, line 232
def select_host_key_verifier(paranoid)
    case paranoid
    when true, nil
        ::Net::SSH::Verifiers::Lenient.new
    when false
        ::Net::SSH::Verifiers::Null.new
    when :very
        ::Net::SSH::Verifiers::Strict.new
    when :secure
        ::Net::SSH::Verifiers::Secure.new
    else
        if paranoid.respond_to?(:verify)
            paranoid
        else
            raise ArgumentError, "argument to :paranoid is not valid: #{paranoid.inspect}"
        end
    end
end