aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/conch/endpoints.py
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/conch/endpoints.py
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/conch/endpoints.py')
-rw-r--r--contrib/python/Twisted/py2/twisted/conch/endpoints.py872
1 files changed, 872 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/conch/endpoints.py b/contrib/python/Twisted/py2/twisted/conch/endpoints.py
new file mode 100644
index 0000000000..2e19d0870f
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/conch/endpoints.py
@@ -0,0 +1,872 @@
+# -*- test-case-name: twisted.conch.test.test_endpoints -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Endpoint implementations of various SSH interactions.
+"""
+
+__all__ = [
+ 'AuthenticationFailed', 'SSHCommandAddress', 'SSHCommandClientEndpoint']
+
+from struct import unpack
+from os.path import expanduser
+
+import signal
+
+from zope.interface import Interface, implementer
+
+from twisted.logger import Logger
+from twisted.python.compat import nativeString, networkString
+from twisted.python.filepath import FilePath
+from twisted.python.failure import Failure
+from twisted.internet.error import ConnectionDone, ProcessTerminated
+from twisted.internet.interfaces import IStreamClientEndpoint
+from twisted.internet.protocol import Factory
+from twisted.internet.defer import Deferred, succeed, CancelledError
+from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol
+
+from twisted.conch.ssh.keys import Key
+from twisted.conch.ssh.common import getNS, NS
+from twisted.conch.ssh.transport import SSHClientTransport
+from twisted.conch.ssh.connection import SSHConnection
+from twisted.conch.ssh.userauth import SSHUserAuthClient
+from twisted.conch.ssh.channel import SSHChannel
+from twisted.conch.client.knownhosts import ConsoleUI, KnownHostsFile
+from twisted.conch.client.agent import SSHAgentClient
+from twisted.conch.client.default import _KNOWN_HOSTS
+
+
+class AuthenticationFailed(Exception):
+ """
+ An SSH session could not be established because authentication was not
+ successful.
+ """
+
+
+
+# This should be public. See #6541.
+class _ISSHConnectionCreator(Interface):
+ """
+ An L{_ISSHConnectionCreator} knows how to create SSH connections somehow.
+ """
+ def secureConnection():
+ """
+ Return a new, connected, secured, but not yet authenticated instance of
+ L{twisted.conch.ssh.transport.SSHServerTransport} or
+ L{twisted.conch.ssh.transport.SSHClientTransport}.
+ """
+
+
+ def cleanupConnection(connection, immediate):
+ """
+ Perform cleanup necessary for a connection object previously returned
+ from this creator's C{secureConnection} method.
+
+ @param connection: An L{twisted.conch.ssh.transport.SSHServerTransport}
+ or L{twisted.conch.ssh.transport.SSHClientTransport} returned by a
+ previous call to C{secureConnection}. It is no longer needed by
+ the caller of that method and may be closed or otherwise cleaned up
+ as necessary.
+
+ @param immediate: If C{True} don't wait for any network communication,
+ just close the connection immediately and as aggressively as
+ necessary.
+ """
+
+
+
+class SSHCommandAddress(object):
+ """
+ An L{SSHCommandAddress} instance represents the address of an SSH server, a
+ username which was used to authenticate with that server, and a command
+ which was run there.
+
+ @ivar server: See L{__init__}
+ @ivar username: See L{__init__}
+ @ivar command: See L{__init__}
+ """
+ def __init__(self, server, username, command):
+ """
+ @param server: The address of the SSH server on which the command is
+ running.
+ @type server: L{IAddress} provider
+
+ @param username: An authentication username which was used to
+ authenticate against the server at the given address.
+ @type username: L{bytes}
+
+ @param command: A command which was run in a session channel on the
+ server at the given address.
+ @type command: L{bytes}
+ """
+ self.server = server
+ self.username = username
+ self.command = command
+
+
+
+class _CommandChannel(SSHChannel):
+ """
+ A L{_CommandChannel} executes a command in a session channel and connects
+ its input and output to an L{IProtocol} provider.
+
+ @ivar _creator: See L{__init__}
+ @ivar _command: See L{__init__}
+ @ivar _protocolFactory: See L{__init__}
+ @ivar _commandConnected: See L{__init__}
+ @ivar _protocol: An L{IProtocol} provider created using C{_protocolFactory}
+ which is hooked up to the running command's input and output streams.
+ """
+ name = b'session'
+ _log = Logger()
+
+ def __init__(self, creator, command, protocolFactory, commandConnected):
+ """
+ @param creator: The L{_ISSHConnectionCreator} provider which was used
+ to get the connection which this channel exists on.
+ @type creator: L{_ISSHConnectionCreator} provider
+
+ @param command: The command to be executed.
+ @type command: L{bytes}
+
+ @param protocolFactory: A client factory to use to build a L{IProtocol}
+ provider to use to associate with the running command.
+
+ @param commandConnected: A L{Deferred} to use to signal that execution
+ of the command has failed or that it has succeeded and the command
+ is now running.
+ @type commandConnected: L{Deferred}
+ """
+ SSHChannel.__init__(self)
+ self._creator = creator
+ self._command = command
+ self._protocolFactory = protocolFactory
+ self._commandConnected = commandConnected
+ self._reason = None
+
+
+ def openFailed(self, reason):
+ """
+ When the request to open a new channel to run this command in fails,
+ fire the C{commandConnected} deferred with a failure indicating that.
+ """
+ self._commandConnected.errback(reason)
+
+
+ def channelOpen(self, ignored):
+ """
+ When the request to open a new channel to run this command in succeeds,
+ issue an C{"exec"} request to run the command.
+ """
+ command = self.conn.sendRequest(
+ self, b'exec', NS(self._command), wantReply=True)
+ command.addCallbacks(self._execSuccess, self._execFailure)
+
+
+ def _execFailure(self, reason):
+ """
+ When the request to execute the command in this channel fails, fire the
+ C{commandConnected} deferred with a failure indicating this.
+
+ @param reason: The cause of the command execution failure.
+ @type reason: L{Failure}
+ """
+ self._commandConnected.errback(reason)
+
+
+ def _execSuccess(self, ignored):
+ """
+ When the request to execute the command in this channel succeeds, use
+ C{protocolFactory} to build a protocol to handle the command's input
+ and output and connect the protocol to a transport representing those
+ streams.
+
+ Also fire C{commandConnected} with the created protocol after it is
+ connected to its transport.
+
+ @param ignored: The (ignored) result of the execute request
+ """
+ self._protocol = self._protocolFactory.buildProtocol(
+ SSHCommandAddress(
+ self.conn.transport.transport.getPeer(),
+ self.conn.transport.creator.username,
+ self.conn.transport.creator.command))
+ self._protocol.makeConnection(self)
+ self._commandConnected.callback(self._protocol)
+
+
+ def dataReceived(self, data):
+ """
+ When the command's stdout data arrives over the channel, deliver it to
+ the protocol instance.
+
+ @param data: The bytes from the command's stdout.
+ @type data: L{bytes}
+ """
+ self._protocol.dataReceived(data)
+
+
+ def request_exit_status(self, data):
+ """
+ When the server sends the command's exit status, record it for later
+ delivery to the protocol.
+
+ @param data: The network-order four byte representation of the exit
+ status of the command.
+ @type data: L{bytes}
+ """
+ (status,) = unpack('>L', data)
+ if status != 0:
+ self._reason = ProcessTerminated(status, None, None)
+
+
+ def request_exit_signal(self, data):
+ """
+ When the server sends the command's exit status, record it for later
+ delivery to the protocol.
+
+ @param data: The network-order four byte representation of the exit
+ signal of the command.
+ @type data: L{bytes}
+ """
+ shortSignalName, data = getNS(data)
+ coreDumped, data = bool(ord(data[0:1])), data[1:]
+ errorMessage, data = getNS(data)
+ languageTag, data = getNS(data)
+ signalName = "SIG%s" % (nativeString(shortSignalName),)
+ signalID = getattr(signal, signalName, -1)
+ self._log.info(
+ "Process exited with signal {shortSignalName!r};"
+ " core dumped: {coreDumped};"
+ " error message: {errorMessage};"
+ " language: {languageTag!r}",
+ shortSignalName=shortSignalName,
+ coreDumped=coreDumped,
+ errorMessage=errorMessage.decode('utf-8'),
+ languageTag=languageTag,
+ )
+ self._reason = ProcessTerminated(None, signalID, None)
+
+
+ def closed(self):
+ """
+ When the channel closes, deliver disconnection notification to the
+ protocol.
+ """
+ self._creator.cleanupConnection(self.conn, False)
+ if self._reason is None:
+ reason = ConnectionDone("ssh channel closed")
+ else:
+ reason = self._reason
+ self._protocol.connectionLost(Failure(reason))
+
+
+
+class _ConnectionReady(SSHConnection):
+ """
+ L{_ConnectionReady} is an L{SSHConnection} (an SSH service) which only
+ propagates the I{serviceStarted} event to a L{Deferred} to be handled
+ elsewhere.
+ """
+ def __init__(self, ready):
+ """
+ @param ready: A L{Deferred} which should be fired when
+ I{serviceStarted} happens.
+ """
+ SSHConnection.__init__(self)
+ self._ready = ready
+
+
+ def serviceStarted(self):
+ """
+ When the SSH I{connection} I{service} this object represents is ready
+ to be used, fire the C{connectionReady} L{Deferred} to publish that
+ event to some other interested party.
+
+ """
+ self._ready.callback(self)
+ del self._ready
+
+
+
+class _UserAuth(SSHUserAuthClient):
+ """
+ L{_UserAuth} implements the client part of SSH user authentication in the
+ convenient way a user might expect if they are familiar with the
+ interactive I{ssh} command line client.
+
+ L{_UserAuth} supports key-based authentication, password-based
+ authentication, and delegating authentication to an agent.
+ """
+ password = None
+ keys = None
+ agent = None
+
+ def getPublicKey(self):
+ """
+ Retrieve the next public key object to offer to the server, possibly
+ delegating to an authentication agent if there is one.
+
+ @return: The public part of a key pair that could be used to
+ authenticate with the server, or L{None} if there are no more
+ public keys to try.
+ @rtype: L{twisted.conch.ssh.keys.Key} or L{None}
+ """
+ if self.agent is not None:
+ return self.agent.getPublicKey()
+
+ if self.keys:
+ self.key = self.keys.pop(0)
+ else:
+ self.key = None
+ return self.key.public()
+
+
+ def signData(self, publicKey, signData):
+ """
+ Extend the base signing behavior by using an SSH agent to sign the
+ data, if one is available.
+
+ @type publicKey: L{Key}
+ @type signData: L{str}
+ """
+ if self.agent is not None:
+ return self.agent.signData(publicKey.blob(), signData)
+ else:
+ return SSHUserAuthClient.signData(self, publicKey, signData)
+
+
+ def getPrivateKey(self):
+ """
+ Get the private part of a key pair to use for authentication. The key
+ corresponds to the public part most recently returned from
+ C{getPublicKey}.
+
+ @return: A L{Deferred} which fires with the private key.
+ @rtype: L{Deferred}
+ """
+ return succeed(self.key)
+
+
+ def getPassword(self):
+ """
+ Get the password to use for authentication.
+
+ @return: A L{Deferred} which fires with the password, or L{None} if the
+ password was not specified.
+ """
+ if self.password is None:
+ return
+ return succeed(self.password)
+
+
+ def ssh_USERAUTH_SUCCESS(self, packet):
+ """
+ Handle user authentication success in the normal way, but also make a
+ note of the state change on the L{_CommandTransport}.
+ """
+ self.transport._state = b'CHANNELLING'
+ return SSHUserAuthClient.ssh_USERAUTH_SUCCESS(self, packet)
+
+
+ def connectToAgent(self, endpoint):
+ """
+ Set up a connection to the authentication agent and trigger its
+ initialization.
+
+ @param endpoint: An endpoint which can be used to connect to the
+ authentication agent.
+ @type endpoint: L{IStreamClientEndpoint} provider
+
+ @return: A L{Deferred} which fires when the agent connection is ready
+ for use.
+ """
+ factory = Factory()
+ factory.protocol = SSHAgentClient
+ d = endpoint.connect(factory)
+ def connected(agent):
+ self.agent = agent
+ return agent.getPublicKeys()
+ d.addCallback(connected)
+ return d
+
+
+ def loseAgentConnection(self):
+ """
+ Disconnect the agent.
+ """
+ if self.agent is None:
+ return
+ self.agent.transport.loseConnection()
+
+
+
+class _CommandTransport(SSHClientTransport):
+ """
+ L{_CommandTransport} is an SSH client I{transport} which includes a host
+ key verification step before it will proceed to secure the connection.
+
+ L{_CommandTransport} also knows how to set up a connection to an
+ authentication agent if it is told where it can connect to one.
+
+ @ivar _userauth: The L{_UserAuth} instance which is in charge of the
+ overall authentication process or L{None} if the SSH connection has not
+ reach yet the C{user-auth} service.
+ @type _userauth: L{_UserAuth}
+ """
+ # STARTING -> SECURING -> AUTHENTICATING -> CHANNELLING -> RUNNING
+ _state = b'STARTING'
+
+ _hostKeyFailure = None
+
+ _userauth = None
+
+
+ def __init__(self, creator):
+ """
+ @param creator: The L{_NewConnectionHelper} that created this
+ connection.
+
+ @type creator: L{_NewConnectionHelper}.
+ """
+ self.connectionReady = Deferred(
+ lambda d: self.transport.abortConnection())
+ # Clear the reference to that deferred to help the garbage collector
+ # and to signal to other parts of this implementation (in particular
+ # connectionLost) that it has already been fired and does not need to
+ # be fired again.
+ def readyFired(result):
+ self.connectionReady = None
+ return result
+ self.connectionReady.addBoth(readyFired)
+ self.creator = creator
+
+
+ def verifyHostKey(self, hostKey, fingerprint):
+ """
+ Ask the L{KnownHostsFile} provider available on the factory which
+ created this protocol this protocol to verify the given host key.
+
+ @return: A L{Deferred} which fires with the result of
+ L{KnownHostsFile.verifyHostKey}.
+ """
+ hostname = self.creator.hostname
+ ip = networkString(self.transport.getPeer().host)
+
+ self._state = b'SECURING'
+ d = self.creator.knownHosts.verifyHostKey(
+ self.creator.ui, hostname, ip, Key.fromString(hostKey))
+ d.addErrback(self._saveHostKeyFailure)
+ return d
+
+
+ def _saveHostKeyFailure(self, reason):
+ """
+ When host key verification fails, record the reason for the failure in
+ order to fire a L{Deferred} with it later.
+
+ @param reason: The cause of the host key verification failure.
+ @type reason: L{Failure}
+
+ @return: C{reason}
+ @rtype: L{Failure}
+ """
+ self._hostKeyFailure = reason
+ return reason
+
+
+ def connectionSecure(self):
+ """
+ When the connection is secure, start the authentication process.
+ """
+ self._state = b'AUTHENTICATING'
+
+ command = _ConnectionReady(self.connectionReady)
+
+ self._userauth = _UserAuth(self.creator.username, command)
+ self._userauth.password = self.creator.password
+ if self.creator.keys:
+ self._userauth.keys = list(self.creator.keys)
+
+ if self.creator.agentEndpoint is not None:
+ d = self._userauth.connectToAgent(self.creator.agentEndpoint)
+ else:
+ d = succeed(None)
+
+ def maybeGotAgent(ignored):
+ self.requestService(self._userauth)
+ d.addBoth(maybeGotAgent)
+
+
+ def connectionLost(self, reason):
+ """
+ When the underlying connection to the SSH server is lost, if there were
+ any connection setup errors, propagate them. Also, clean up the
+ connection to the ssh agent if one was created.
+ """
+ if self._userauth:
+ self._userauth.loseAgentConnection()
+
+ if self._state == b'RUNNING' or self.connectionReady is None:
+ return
+ if self._state == b'SECURING' and self._hostKeyFailure is not None:
+ reason = self._hostKeyFailure
+ elif self._state == b'AUTHENTICATING':
+ reason = Failure(
+ AuthenticationFailed("Connection lost while authenticating"))
+ self.connectionReady.errback(reason)
+
+
+
+@implementer(IStreamClientEndpoint)
+class SSHCommandClientEndpoint(object):
+ """
+ L{SSHCommandClientEndpoint} exposes the command-executing functionality of
+ SSH servers.
+
+ L{SSHCommandClientEndpoint} can set up a new SSH connection, authenticate
+ it in any one of a number of different ways (keys, passwords, agents),
+ launch a command over that connection and then associate its input and
+ output with a protocol.
+
+ It can also re-use an existing, already-authenticated SSH connection
+ (perhaps one which already has some SSH channels being used for other
+ purposes). In this case it creates a new SSH channel to use to execute the
+ command. Notably this means it supports multiplexing several different
+ command invocations over a single SSH connection.
+ """
+
+ def __init__(self, creator, command):
+ """
+ @param creator: An L{_ISSHConnectionCreator} provider which will be
+ used to set up the SSH connection which will be used to run a
+ command.
+ @type creator: L{_ISSHConnectionCreator} provider
+
+ @param command: The command line to execute on the SSH server. This
+ byte string is interpreted by a shell on the SSH server, so it may
+ have a value like C{"ls /"}. Take care when trying to run a
+ command like C{"/Volumes/My Stuff/a-program"} - spaces (and other
+ special bytes) may require escaping.
+ @type command: L{bytes}
+
+ """
+ self._creator = creator
+ self._command = command
+
+
+ @classmethod
+ def newConnection(cls, reactor, command, username, hostname, port=None,
+ keys=None, password=None, agentEndpoint=None,
+ knownHosts=None, ui=None):
+ """
+ Create and return a new endpoint which will try to create a new
+ connection to an SSH server and run a command over it. It will also
+ close the connection if there are problems leading up to the command
+ being executed, after the command finishes, or if the connection
+ L{Deferred} is cancelled.
+
+ @param reactor: The reactor to use to establish the connection.
+ @type reactor: L{IReactorTCP} provider
+
+ @param command: See L{__init__}'s C{command} argument.
+
+ @param username: The username with which to authenticate to the SSH
+ server.
+ @type username: L{bytes}
+
+ @param hostname: The hostname of the SSH server.
+ @type hostname: L{bytes}
+
+ @param port: The port number of the SSH server. By default, the
+ standard SSH port number is used.
+ @type port: L{int}
+
+ @param keys: Private keys with which to authenticate to the SSH server,
+ if key authentication is to be attempted (otherwise L{None}).
+ @type keys: L{list} of L{Key}
+
+ @param password: The password with which to authenticate to the SSH
+ server, if password authentication is to be attempted (otherwise
+ L{None}).
+ @type password: L{bytes} or L{None}
+
+ @param agentEndpoint: An L{IStreamClientEndpoint} provider which may be
+ used to connect to an SSH agent, if one is to be used to help with
+ authentication.
+ @type agentEndpoint: L{IStreamClientEndpoint} provider
+
+ @param knownHosts: The currently known host keys, used to check the
+ host key presented by the server we actually connect to.
+ @type knownHosts: L{KnownHostsFile}
+
+ @param ui: An object for interacting with users to make decisions about
+ whether to accept the server host keys. If L{None}, a L{ConsoleUI}
+ connected to /dev/tty will be used; if /dev/tty is unavailable, an
+ object which answers C{b"no"} to all prompts will be used.
+ @type ui: L{None} or L{ConsoleUI}
+
+ @return: A new instance of C{cls} (probably
+ L{SSHCommandClientEndpoint}).
+ """
+ helper = _NewConnectionHelper(
+ reactor, hostname, port, command, username, keys, password,
+ agentEndpoint, knownHosts, ui)
+ return cls(helper, command)
+
+
+ @classmethod
+ def existingConnection(cls, connection, command):
+ """
+ Create and return a new endpoint which will try to open a new channel
+ on an existing SSH connection and run a command over it. It will
+ B{not} close the connection if there is a problem executing the command
+ or after the command finishes.
+
+ @param connection: An existing connection to an SSH server.
+ @type connection: L{SSHConnection}
+
+ @param command: See L{SSHCommandClientEndpoint.newConnection}'s
+ C{command} parameter.
+ @type command: L{bytes}
+
+ @return: A new instance of C{cls} (probably
+ L{SSHCommandClientEndpoint}).
+ """
+ helper = _ExistingConnectionHelper(connection)
+ return cls(helper, command)
+
+
+ def connect(self, protocolFactory):
+ """
+ Set up an SSH connection, use a channel from that connection to launch
+ a command, and hook the stdin and stdout of that command up as a
+ transport for a protocol created by the given factory.
+
+ @param protocolFactory: A L{Factory} to use to create the protocol
+ which will be connected to the stdin and stdout of the command on
+ the SSH server.
+
+ @return: A L{Deferred} which will fire with an error if the connection
+ cannot be set up for any reason or with the protocol instance
+ created by C{protocolFactory} once it has been connected to the
+ command.
+ """
+ d = self._creator.secureConnection()
+ d.addCallback(self._executeCommand, protocolFactory)
+ return d
+
+
+ def _executeCommand(self, connection, protocolFactory):
+ """
+ Given a secured SSH connection, try to execute a command in a new
+ channel created on it and associate the result with a protocol from the
+ given factory.
+
+ @param connection: See L{SSHCommandClientEndpoint.existingConnection}'s
+ C{connection} parameter.
+
+ @param protocolFactory: See L{SSHCommandClientEndpoint.connect}'s
+ C{protocolFactory} parameter.
+
+ @return: See L{SSHCommandClientEndpoint.connect}'s return value.
+ """
+ commandConnected = Deferred()
+
+ def disconnectOnFailure(passthrough):
+ # Close the connection immediately in case of cancellation, since
+ # that implies user wants it gone immediately (e.g. a timeout):
+ immediate = passthrough.check(CancelledError)
+ self._creator.cleanupConnection(connection, immediate)
+ return passthrough
+ commandConnected.addErrback(disconnectOnFailure)
+
+ channel = _CommandChannel(
+ self._creator, self._command, protocolFactory, commandConnected)
+ connection.openChannel(channel)
+ return commandConnected
+
+
+
+class _ReadFile(object):
+ """
+ A weakly file-like object which can be used with L{KnownHostsFile} to
+ respond in the negative to all prompts for decisions.
+ """
+ def __init__(self, contents):
+ """
+ @param contents: L{bytes} which will be returned from every C{readline}
+ call.
+ """
+ self._contents = contents
+
+
+ def write(self, data):
+ """
+ No-op.
+
+ @param data: ignored
+ """
+
+
+ def readline(self, count=-1):
+ """
+ Always give back the byte string that this L{_ReadFile} was initialized
+ with.
+
+ @param count: ignored
+
+ @return: A fixed byte-string.
+ @rtype: L{bytes}
+ """
+ return self._contents
+
+
+ def close(self):
+ """
+ No-op.
+ """
+
+
+
+@implementer(_ISSHConnectionCreator)
+class _NewConnectionHelper(object):
+ """
+ L{_NewConnectionHelper} implements L{_ISSHConnectionCreator} by
+ establishing a brand new SSH connection, securing it, and authenticating.
+ """
+ _KNOWN_HOSTS = _KNOWN_HOSTS
+ port = 22
+
+ def __init__(self, reactor, hostname, port, command, username, keys,
+ password, agentEndpoint, knownHosts, ui,
+ tty=FilePath(b"/dev/tty")):
+ """
+ @param tty: The path of the tty device to use in case C{ui} is L{None}.
+ @type tty: L{FilePath}
+
+ @see: L{SSHCommandClientEndpoint.newConnection}
+ """
+ self.reactor = reactor
+ self.hostname = hostname
+ if port is not None:
+ self.port = port
+ self.command = command
+ self.username = username
+ self.keys = keys
+ self.password = password
+ self.agentEndpoint = agentEndpoint
+ if knownHosts is None:
+ knownHosts = self._knownHosts()
+ self.knownHosts = knownHosts
+
+ if ui is None:
+ ui = ConsoleUI(self._opener)
+ self.ui = ui
+ self.tty = tty
+
+
+ def _opener(self):
+ """
+ Open the tty if possible, otherwise give back a file-like object from
+ which C{b"no"} can be read.
+
+ For use as the opener argument to L{ConsoleUI}.
+ """
+ try:
+ return self.tty.open("rb+")
+ except:
+ # Give back a file-like object from which can be read a byte string
+ # that KnownHostsFile recognizes as rejecting some option (b"no").
+ return _ReadFile(b"no")
+
+
+ @classmethod
+ def _knownHosts(cls):
+ """
+
+ @return: A L{KnownHostsFile} instance pointed at the user's personal
+ I{known hosts} file.
+ @type: L{KnownHostsFile}
+ """
+ return KnownHostsFile.fromPath(FilePath(expanduser(cls._KNOWN_HOSTS)))
+
+
+ def secureConnection(self):
+ """
+ Create and return a new SSH connection which has been secured and on
+ which authentication has already happened.
+
+ @return: A L{Deferred} which fires with the ready-to-use connection or
+ with a failure if something prevents the connection from being
+ setup, secured, or authenticated.
+ """
+ protocol = _CommandTransport(self)
+ ready = protocol.connectionReady
+
+ sshClient = TCP4ClientEndpoint(
+ self.reactor, nativeString(self.hostname), self.port)
+
+ d = connectProtocol(sshClient, protocol)
+ d.addCallback(lambda ignored: ready)
+ return d
+
+
+ def cleanupConnection(self, connection, immediate):
+ """
+ Clean up the connection by closing it. The command running on the
+ endpoint has ended so the connection is no longer needed.
+
+ @param connection: The L{SSHConnection} to close.
+ @type connection: L{SSHConnection}
+
+ @param immediate: Whether to close connection immediately.
+ @type immediate: L{bool}.
+ """
+ if immediate:
+ # We're assuming the underlying connection is an ITCPTransport,
+ # which is what the current implementation is restricted to:
+ connection.transport.transport.abortConnection()
+ else:
+ connection.transport.loseConnection()
+
+
+
+@implementer(_ISSHConnectionCreator)
+class _ExistingConnectionHelper(object):
+ """
+ L{_ExistingConnectionHelper} implements L{_ISSHConnectionCreator} by
+ handing out an existing SSH connection which is supplied to its
+ initializer.
+ """
+
+ def __init__(self, connection):
+ """
+ @param connection: See L{SSHCommandClientEndpoint.existingConnection}'s
+ C{connection} parameter.
+ """
+ self.connection = connection
+
+
+ def secureConnection(self):
+ """
+
+ @return: A L{Deferred} that fires synchronously with the
+ already-established connection object.
+ """
+ return succeed(self.connection)
+
+
+ def cleanupConnection(self, connection, immediate):
+ """
+ Do not do any cleanup on the connection. Leave that responsibility to
+ whatever code created it in the first place.
+
+ @param connection: The L{SSHConnection} which will not be modified in
+ any way.
+ @type connection: L{SSHConnection}
+
+ @param immediate: An argument which will be ignored.
+ @type immediate: L{bool}.
+ """