ddn.api.net.ssh
DDN SSH API.
This module defines the public contracts for SSH-2 client connections: the transport abstraction the protocol engine runs on, non-blocking progress and result types, typed errors, session lifecycle and driving, host-key verification, asynchronous signing for public-key authentication, and multiplexed channels with flow-control visibility.
Implementations are provided by separate packages (for example ddn-net-ssh); consumers of channels - such as an SFTP client - depend only on the interfaces defined here.
The API is non-blocking throughout: every potentially-blocking operation reports SshProgress, and the caller re-drives the owning session when the reported wait is satisfied.
Types 43
Result codes for raw transport operations.
Mirrors the convention used by the other ddn.api.net transports, declared locally so this module does not depend on the TLS API.
Progress indicator for non-blocking SSH operations.
Every potentially-blocking operation returns or reports an SshProgress value instead of blocking. The caller checks the result and re-drives the owning session when the corresponding wait is satisfiable.
Typed error information for SSH operations.
Used by SshResult, operations, channels, and sessions to report failures without exceptions. Error reporting is per operation or per channel; a session-level error only exists for conditions that make the whole connection unusable.
SshErrorType typeThe error categorystring messageHuman-readable error messageushort reasonCodeSSH disconnect reason code when the error carries one (RFC 4250 assigned numbers), zero otherwise.SshErrorTypeCategories of SSH errors.Result of an SSH read or write operation.
Combines a byte count with progress information, optional typed error details, and an end-of-stream flag.
Result conventions:
- Positive
bytes: number of bytes transferred. progress == DONEwithbytes == 0andeof == trueunambiguously means end of stream. Idle reads with nothing available never reportDONE; they reportNEED_READorWAIT.- A zero-length buffer is a no-op that returns immediately with
DONEandbytes == 0; it is distinct from both an idle read and end of stream. - Bytes reported as accepted by a write must never be resubmitted
by the caller; the sender tracks the remainder.
ptrdiff_t bytesBytes transferred (positive), or 0 on EOF/error/idleSshProgress progressProgress indicatorSshError errorError details (valid when progress == ERROR)bool eofTrue when the peer signalled end of stream for this directionbool done() const pure nothrow @safe @nogcReturns: true if the operation completed (successfully or EOF).bool wouldBlock() const pure nothrow @safe @nogcReturns: true if the caller should retry after session transport readiness.bool waiting() const pure nothrow @safe @nogcReturns: true if the caller should retry after the non-transport wait reported by the owning session was satisfied.Ordered byte transport underlying an SSH connection.
Implementations wrap a socket, a pipe, an in-memory buffer, or any other ordered stream. The SSH protocol engine performs all of its network interaction through this interface; it never opens sockets by itself, which keeps it embeddable in any event loop and testable on memory transports.
Return Value Convention:
- Positive value: number of bytes read/written.
- Zero (0): clean end-of-file, peer closed the connection.
- Negative value: an SshIoResult code indicating
error or would-block.
Lifecycle states of an SSH session.
A session moves forward through these states as SshSession.pump is driven; it never moves backward except to FAILED or DISCONNECTED, which are terminal.
Kinds of waits an SSH session can report to its driver.
What the driver of a session should wait for when pumping cannot progress without external action.
For SshWaitKind.IO the flags select the readiness events of the session transport supplied by the application. Agent or signer I/O is application-owned and surfaces as SshWaitKind.DECISION; the application queries its signer for the concrete readiness it needs.
SshWaitKind kindThe kind of wait currently blocking progressbool needReadSession transport readability is required (valid for IO)bool needWriteSession transport writability is required (valid for IO)bool hasDeadlineWhether deadline carries a meaningful absolute time. Valid for every wait kind: an IO or DECISION wait may also carry a timeout (keepalive, rekey, or handshake deadline) after which the driver must...MonoTime deadlineAbsolute monotonic deadline the driver should sleep until. Valid only when hasDeadline is true; for every other case the value is unspecified and must not be used.Algorithm preference lists for session negotiation.
Lists are ordered from most to least preferred. Cipher and MAC preferences apply to both directions. MAC preferences are used only when the selected cipher requires a separate MAC.
Names are SSH wire names (for example curve25519-sha256); unknown peer names are parsed safely and reported through negotiation results and diagnostics rather than crashing the session.
string[] kexPreferred key exchange methods, most preferred firststring[] hostKeyAccepted host key / signature algorithms, most preferred firststring[] cipherPreferred bulk ciphers (both directions), most preferred firststring[] macPreferred MACs for non-AEAD ciphers; may be emptystring[] compressionAccepted compression algorithms; the initial profile is none onlySshAlgorithmPreferences recommended() static pure @safeReturns: the recommended qualified preferences: the side-channel-qualified initial profile first, then the interoperable Phase 12 alternatives whose secret-bearing paths remain qualified only as ex...Algorithms actually negotiated for a session.
Directional entries reflect the independent per-direction negotiation of the SSH transport protocol.
string kexNegotiated key exchange methodstring hostKeyNegotiated host key / signature algorithmstring cipherC2SNegotiated client-to-server cipherstring cipherS2CNegotiated server-to-client cipherstring macC2SNegotiated client-to-server MAC (empty for AEAD ciphers)string macS2CNegotiated server-to-client MAC (empty for AEAD ciphers)string compressionC2SNegotiated client-to-server compressionstring compressionS2CNegotiated server-to-client compressionAn SSH-2 client connection driven by explicit pumping.
Ownership and threading rules:
- The session owns all protocol state for the connection and
advances all channels, key exchange, rekey, authentication operations, and control messages when pumped.
- Sessions are single-threaded and non-reentrant: callbacks
invoked during pump must not call back into the session.
- The session references - but does not own - the supplied
SshTransport; the application keeps the transport alive until the session reaches a terminal state.
Driving model:
- Call pump whenever any wait reported by
waitState is satisfied, or to make initial progress.
pumpperforms pending non-blocking I/O on the sessiontransport and returns the aggregate progress; when it cannot proceed,
waitStatedescribes what the driver should wait for (transport readiness, an application decision, or a timer deadline).- A blocked channel write may require reading the session
transport (window adjustment), not transport writability; the session accounts for this when reporting waits.
Global request replies arrive in request order without identifiers, so the session issues them strictly one reply at a time.
A remote TCP/IP forward request (tcpip-forward).
ushort boundPort() @property const;Returns: the bound port allocated by the server. When port 0 was requested this is the allocated port; otherwise the requested port. Zero while the request is pending or refused.Identity of an incoming forwarded TCP/IP channel (RFC 4254 7.2).
string boundAddressAddress the server accepted the TCP connection onushort boundPortPort the server accepted the TCP connection on (the bound port)string originatorAddressAddress of the connecting TCP peerushort originatorPortPort of the connecting TCP peerDecision states for an incoming forwarded channel.
The caller's decision for an incoming forwarded channel.
SshForwardAcceptStatus statusThe decision state; defaults to refuseCaller-controlled bind/accept policy for remote TCP forwarding.
The handler decides every incoming forwarded channel; the session never accepts one on its own. While decideForwardedChannel reports PENDING the request stays queued and is asked again on later pump calls; the queue is bounded and overflow is refused.
SshForwardAcceptResult decideForwardedChannel(
scope const SshForwardedChannelInfo info, SshChannel channel)Decides whether to accept an incoming forwarded TCP/IP channel.SshError sessionError() @property const;Returns: the session-fatal error once the state is FAILED. Per-operation and per-channel errors are reported on the affected operation or channel instead.void setAlgorithmPreferences(scope const SshAlgorithmPreferences prefs)Sets algorithm preferences. Legal only before the handshake starts; later calls are rejected with an SshError.CAPABILITY-style exception in the implementation.void setClock(MonoTime delegate() clock)Sets the monotonic clock used for keepalive, rekey, and handshake deadlines. Tests inject a deterministic clock; the default is the wall-clock MonoTime.currTime.void setKeepAlive(Duration interval)Enables or disables keepalive messages.SshProgress pump()Advances all session state machines.SshWaitState waitState() @property const;Returns: what the driver should wait for while pump cannot progress without external action.void requestRekey()Requests a key re-exchange at the next legal opportunity. The request is asynchronous; rekeying reflects when it starts.string peerIdentification() @property const;Returns: the peer's SSH software identification string (for example OpenSSH_10.0), once version exchange completed.SshNegotiatedAlgorithms negotiated() @property const;Returns: the algorithms negotiated for this session, once algorithm negotiation completed.void setHostKeyVerifier(SshHostKeyVerifier verifier)Installs the host-key verifier consulted during the initial key exchange and on every rekey. No credentials are transmitted and no signer is invoked until the host key has been accepted.SshAuthOperation discoverAuthenticationMethods()Discovers the authentication methods currently accepted by the server.SshAuthOperation authenticatePublicKey(SshSigner signer)Starts (or resumes) public-key authentication with an application-owned signer.SshAuthOperation authenticatePassword(SshPasswordProvider provider)Starts (or resumes) password authentication with an application-owned credential provider.SshAuthOperation authenticateKeyboardInteractive(
SshKeyboardInteractiveProvider provider)Starts (or resumes) keyboard-interactive authentication with an application-owned, UI-neutral challenge provider.SshChannelOpenOperation openSessionChannel(
scope const SshChannelOpenOptions options = SshChannelOpenOptions.init)Opens a new session channel for exec, shell, or subsystem requests.SshChannelOpenOperation openDirectTcpIpChannel(string host, ushort port,
string originatorAddress, ushort originatorPort,
scope const SshChannelOpenOptions options = SshChannelOpenOptions
.init)Opens a direct TCP/IP channel to a destination reachable from the server.void setForwardedChannelHandler(SshForwardedChannelHandler handler)Registers the policy for incoming forwarded TCP/IP channels.SshRemoteForwardOperation requestRemoteForward(string bindAddress,
ushort bindPort)Requests a remote TCP/IP listener (tcpip-forward).SshGlobalRequestOperation cancelRemoteForward(string bindAddress,
ushort bindPort)Cancels a remote TCP/IP listener (cancel-tcpip-forward).void disconnect(ushort reason, string description)Disconnects the session.Information about a server host key offered for verification.
string algorithmHost key algorithm name (for example ssh-ed25519)const(ubyte)[] blobRaw host key blob as transmittedstring fingerprintSha256Unpadded base64 SHA-256 fingerprint of blobVerdict of an application host-key policy.
Application-supplied host trust policy.
The session consults the verifier during the initial key exchange and on every rekey, always before any credential or signing request. Implementations must not block; interactions that need user input return SshVerdict.PENDING and answer on a later poll.
SshVerdict verifyHostKey(scope const SshHostKeyInfo info)Asks for a verdict on the offered host key.A public key offered for public-key authentication.
string algorithmKey format name as used in the key blob (for example ssh-rsa)const(ubyte)[] blobRaw public key blobstring commentOptional key commentResult of an asynchronous signing request.
SshProgress progressDONE with a signature, WAIT to be asked again, or ERRORSshError errorError details (valid when progress == ERROR)const(ubyte)[] signatureSignature blob (valid when progress == DONE)string signatureAlgorithmSignature algorithm of signature (valid when DONE)Application-owned signer for public-key authentication.
Implementations wrap an in-memory private key or - asynchronously - an ssh-agent. Signatures cover the exact session-bound authentication request data supplied by the session, so a signature can never be replayed against another session.
Implementations must not block. An agent-backed signer reports SshProgress.WAIT while its own transport is pending; the application drives that transport independently of the session.
SshSignResult sign(scope const(ubyte)[] requestData,
string signatureAlgorithm)Signs the given request data with the requested signature algorithm, continuing a pending operation when called again.Outcome of polling a password provider.
Result of polling an SshPasswordProvider.
SshPasswordStatus statusPoll outcomestring passwordThe password (valid when status == PROVIDED)Application-owned password source.
Implementations must not block; user interaction returns SshPasswordStatus.PENDING until the answer is available. Password-change-required responses from the server are surfaced as a distinct authentication error; the provider is never re-polled indefinitely for them.
SshPasswordResult password()Returns: The current password state; poll again after PENDING.One RFC 4256 keyboard-interactive prompt.
string textText presented to the userbool echoWhether the user's response may be echoed while enteredOne bounded RFC 4256 challenge round from the server.
A zero-length prompts array is valid: providers still receive the round so they can surface instruction, then return an empty answer array.
string nameServer-supplied challenge namestring instructionServer-supplied instructionsSshKeyboardInteractivePrompt[] promptsPrompts in wire orderOutcome of polling a keyboard-interactive provider.
Result of polling an SshKeyboardInteractiveProvider.
SshKeyboardInteractiveStatus statusPoll outcomestring[] answersResponses in prompt order (valid when status == ANSWERED)Application-owned keyboard-interactive challenge provider.
Implementations own all UI decisions and must not block. The SSH library never logs answers and discards provider results immediately after serializing one response. Providers should likewise avoid retaining answers after returning them.
SshKeyboardInteractiveResult respond(
SshKeyboardInteractiveChallenge challenge)Polls for answers to one challenge round.SSH user authentication methods, used for server capability discovery.
A user authentication operation driven by session pumping.
The handle is stable for the lifetime of the attempt: resuming or retrying the same operation never duplicates protocol requests. A partialSuccess result means the server accepted one factor and requires another; start the next method with a new authenticate call.
SshProgress progress() @property const;Returns: current progress of the attempt; WAIT means the session reported a decision or signer wait.bool succeeded() @property const;Returns: whether authentication succeeded overall; when false and done, consult error or continue after partialSuccess.bool partialSuccess() @property const;Returns: whether the server accepted this factor and wants another.SshAuthMethod[] availableMethods() @property const;Returns: authentication methods the server advertised, discovered during this attempt.string banner() @property const;Returns: the bounded authentication banner, when the server sent one.bool passwordChangeRequired() @property const;Returns: whether the server rejected this attempt because the account requires a password change. Password changes are not supported, so such attempts fail; this flag distinguishes the outcome from...void cancel()Cancels the attempt. Subsequent progress reports a SshError.CANCELLED-typed error.Channel types offered by this API.
Flow-control parameters of a channel as requested by the client.
uint initialWindowInitial receive window the client grants the peeruint maximumPacketSizeMaximum packet size the peer may send in one channel messageAn in-flight channel-open operation driven by session pumping.
SshChannel channel() @propertyReturns: The confirmed channel, or null while the operation is pending or has failed.void cancel()Cancels the attempt; if the channel was already confirmed, it is closed instead.Channel request types carryable by SshChannel.request.
Terminal-mode opcodes for pseudo-terminal allocation (RFC 4254 section 8).
Each opcode takes one uint32 argument; boolean modes use 0/1. The encoded mode stream ends with TTY_OP_END, which the implementation appends automatically.
One terminal-mode pair of a pseudo-terminal request (RFC 4254 section 8).
ubyte opcodeMode opcode; must be in the defined range 1 to 159uint valueMode argument; boolean modes use 0 or 1A request on a session channel.
Field applicability follows SshChannelRequestType; unused fields are ignored. Requests are issued in order; a reply-bearing request is not overtaken by later requests on the same channel because SSH ties replies to channels, not requests.
SshChannelRequestType typeThe kind of requestbool wantReplyWhether to ask the peer for an explicit replystring commandCommand string (EXEC)string subsystemSubsystem name (SUBSYSTEM)string terminalTerminal type name (PTY)string envNameEnvironment variable name (ENV)string envValueEnvironment variable value (ENV)string signalNameSignal name without the SIG prefix, for example TERM (SIGNAL)uint terminalWidthTerminal width in columns (PTY, WINDOW_CHANGE)uint terminalHeightTerminal height in rows (PTY, WINDOW_CHANGE)uint pixelWidthTerminal width in pixels (PTY, WINDOW_CHANGE)uint pixelHeightTerminal height in pixels (PTY, WINDOW_CHANGE)SshTerminalMode[] terminalModesTerminal modes (PTY), encoded as opcode/argument pairs per RFC 4254 section 8; the implementation appends the terminating TTY_OP_END. An empty list sends no modes.An in-flight channel request driven by session pumping.
SshError error() @property const;Returns: the failure details when the request failed or was rejected.bool accepted() @property const;Returns: Whether the peer accepted the request; meaningful once done and only when a reply was requested.void cancel()Cancels the attempt; delivery that already happened is not undone.Exit information of a session channel, when the peer reported it.
bool presentWhether any exit information was receivedint statusExit status (valid when present and isSignal is false)string signalNameSignal name without the SIG prefix (exit-signal variant)bool coreDumpedWhether the command died from a signalbool isSignal() const pure nothrow @safe @nogcReturns: true when the exit information describes a signal.An opened SSH channel.
Data conventions follow SshResult: reads return standard or extended data separately, idle reads report NEED_READ or WAIT, end-of-stream is DONE with the eof flag set, and a zero-length buffer is an immediate no-op. Bytes accepted by write must never be resubmitted.
Flow control: the SSH implementation owns protocol window accounting and grants receive credit as the application consumes buffered data. Consumers observe backpressure through canWrite and pendingWriteBytes; they never manage window adjustments themselves.
Ownership: the channel belongs to its session; closing the session closes all channels.
SshResult readExtended(ubyte[] buffer)Reads extended data (stderr) into the buffer. Extended data shares the channel receive window with standard data; draining or discarding it is required to keep receiving (see discardExtendedData).SshResult write(const(ubyte)[] data)Writes data to the channel. May accept fewer bytes than requested (partial write); the accepted prefix is transmitted by the session and must not be resubmitted by the caller.bool canWrite() @property const;Returns: whether write would immediately accept at least one byte (send window and queue headroom available).size_t pendingWriteBytes() @property const;Returns: the number of bytes accepted by write but not yet confirmed flushed to the session transport.ulong sendWindowRemaining() @property const;Returns: the remaining send window in bytes (diagnostic; backpressure is observed through canWrite).uint peerMaximumPacketSize() @property const;Returns: the maximum channel packet size the peer declared.SshChannelRequestOperation request(scope const SshChannelRequest request)Issues a request on this channel.void sendEof()Signals end-of-file for our direction (half-close). Buffered peer data remains readable; only our writing direction ends.void close()Closes the channel. The close handshake completes through session pumping; closed reflects completion. Pending operations complete with cancellation errors or their outcomes.SshError channelError() @property const;Returns: the channel-fatal error, when this channel failed independently of the session.SshExitStatus exitStatus() @property const;Returns: exit information when the peer reported it for this session channel.void discardExtendedData(bool discard)Selects the extended-data policy.