class io.SshChannel

Overview

This class provides high-level asynchronous interface for client-side SSH connections. More…

import "io_ssh.jncx"

class SshChannel {
    // fields

    io.SshEvents readonly volatile m_activeEvents;
    std.Error const* readonly volatile m_ioError;
    bool readonly m_isOpen;

    // properties

    io.SocketAddress const property m_address;
    io.SocketAddress const property m_peerAddress;
    size_t autoget property m_readBlockSize;
    size_t autoget property m_readBufferSize;
    size_t autoget property m_writeBufferSize;
    io.SocketOptions autoget property m_options;

    // construction

    construct();
    destruct();

    // methods

    bool errorcode open(io.AddressFamily family);
    bool errorcode open(io.SocketAddress const* address);
    void close();

    bool errorcode connect(
        io.SocketAddress const* address,
        string_t userName,
        void const* privateKey,
        size_t privateKeySize,
        string_t password,
        string_t channelType = "session",
        string_t processType = "shell",
        string_t ptyType = "xterm",
        uint_t ptyWidth = 80,
        uint_t ptyHeight = 24
    );

    bool errorcode connect(io.SshConnectParams const* params);

    bool errorcode authenticate(
        string_t userName,
        void const* privateKey,
        size_t privateKeySize,
        string_t password
    );

    bool errorcode resizePty(
        uint_t width,
        uint_t height
    );

    size_t errorcode read(
        void* p,
        size_t size
    );

    size_t errorcode write(
        void const* p,
        size_t size
    );

    long errorcode wait(
        io.SshEvents eventMask,
        void function* handler(io.SshEvents triggeredEvents)
    );

    bool errorcode cancelWait(long handle);

    io.SshEvents blockingWait(
        io.SshEvents eventMask,
        uint_t timeout = -1
    );

    import io.SshChannel.SshEvents async asyncWait(io.SshEvents eventMask);

    // aliases

    alias dispose = close;
};

Detailed Documentation

This class provides high-level asynchronous interface for client-side SSH connections.

Under the hood io.SshChannel uses libssh2 library.

A typical sequence of steps when establishing an SSH connection looks like this:

  • Call open method to open underlying socket;

  • Call connect method to initate connection to SSH server;

  • Assign IO event handler with wait method. You would probably also want to schedule your event handler to be run in particular environment(e.g., in a specific thread) and partially apply some syncrhonization ID to discard late events;

  • If io.SshEvents.SshAuthenticateError event is fired, you can re-authenticate using authenticate method;

  • Wait until io.SshEvents.SshConnectCompleted or io.SshEvents.IoError event;

  • When io.SshEvents.IncomingData event is fired, read from the file using read method;

  • Write to the file stream using write method;

  • Suspend writing if the return value of write is less than requested;

  • Resume writing on io.SshEvents.WriteBufferReady event;

  • Close the SSH channel when no longer needed with close method.

Sample code:

class MyDialog {
    // ...

    io.SshChannel m_ssh;
    uint_t m_syncId;

    construct();

    void open(
        string_t portName,
        uint_t baudRate
    );

    void close();
    void waitSshEvents();

    void onWriteButtonClicked();

    void onSshEvent(
        uint_t syncId,
        io.SshEvents triggeredEvents
    );
}

MyDialog.connect(
    string_t addrString,
    string_t userName,
    string_t password
) {
    io.SocketAddress sockAddr;
    sockAddr.parse(addrString);

    m_ssh.open();
    m_ssh.connect(sockAddr, userName, password);

    waitSshEvents();

catch:
    // handle the error...
}

MyDialog.close() {
    m_serial.close();
    m_syncId++; // events past this point will be discarded as 'late'
}

MyDialog.waitSshEvent() {
    io.SshEvents eventMask =
        io.SshEvents.IoError |
        io.SshEvents.SshAuthenticateError |
        io.SshEvents.IncomingData;

    if (!(m_ssh.m_activeEvents & io.SshEvents.SshConnectCompleted))
        eventMask |= io.SshEvents.SshConnectCompleted;

    m_ssh.wait(eventMask, onPcapEvent ~(++m_syncId) @
        g_mainThreadScheduler);
}

MyDialog.onWriteButtonClicked() {
    static char data[] = "a quick brown fox jumps over the lazy dog";
    m_ssh.write(data, sizeof(data));
}

MyDialog.onSshEvent(
    uint_t syncId,
    io.SshEvents triggeredEvents
) {
    if (syncId != m_syncId) // late event
        return;

    if (triggeredEvents & io.SshEvents.IoError) {
        string_t errorString = m_ssh.m_ioError.m_description;
        // ...
    }

    if (triggeredEvents & io.SshEvents.SshAuthenticateError) {
        // show some kind of login dialog ...
        // ...
        // ... then re-authenticate:

        m_ssh.authenticate(userName, password);
    }

    if (triggeredEvents & io.SshEvents.SshConnectCompleted) {
        // SSH channel established
    }

    if (triggeredEvents & io.SshEvents.IncomingData) {
        char buffer[256];
        size_t size = m_ssh.read(buffer, sizeof(buffer));
        // ...
    }

    waitSshEvents(); // restart wait
}

See also:

io.SshEvents, io.Socket, io.SocketAddress, io.HostNameResolver

Fields

bool readonly m_isOpen

Holds the open status for the socket, i.e. true if opened; false otherwise.

Properties

io.SocketAddress const property m_address

This property is used for getting the local address assigned to this socket. If the address has not been assigned yet, returns empty address.

io.SocketAddress const property m_peerAddress

This property is used for getting the remote address of the peer this socket is connected to. If the connection has not been established yet, returns empty address.

Methods

bool errorcode open(io.AddressFamily family)

Opens a TCP socket this connection will use for transport.

Returns true on success. If a new socket could not be opened, IO error supplied by operating system is set and then the function returns false [1].

bool errorcode open(io.SocketAddress const* address)

Opens a TCP socket this connection will use for transport.

Returns true on success. If a new socket could not be opened, IO error supplied by operating system is set and then the function returns false [1].

void close()

Closes SSH channel and underlying TCP socket; does nothing if connection is not and not being established.

Sometimes it may be convenient to use disposable pattern to ensure timely invokation of close [2].

bool errorcode connect(
    io.SocketAddress const* address,
    string_t userName,
    void const* privateKey,
    size_t privateKeySize,
    string_t password,
    string_t channelType = "session",
    string_t processType = "shell",
    string_t ptyType = "xterm",
    uint_t ptyWidth = 80,
    uint_t ptyHeight = 24
)

Initiates a connection request to address.

The meanings of the arguments for this function are as follows:

  • address specifes the remote SSH server to connect to;

  • userName and password specify user credential used to login to this SSH server;

  • channelType specifies the type of the channel to establish (typically, session; can also be direct-tcpip, tcpip-forward, etc);

  • processType specifies what kind of process to run on the server side of this SSH channel(typically, shell; can also be exec, subsystem etc);

  • ptyType specifies what type of pseudo terminal kind of process to run on the server side of this SSH channel(typically, xterm; can also be ansi, vanilla etc);

  • ptyWidth and ptyHeight specify the size of the pseudo-terminal;

  • isSync specifies whether this is a synchronous connect.

Returns true on success. If a connection could not be initiated, corresponding error is set and then the function returns false [1].

bool errorcode authenticate(
    string_t userName,
    void const* privateKey,
    size_t privateKeySize,
    string_t password
)

Sends a request to SSH server to re-authenticate on AuthError event.

Returns true on success. If re-authenticate request could not be sent, returns false [1].

bool errorcode resizePty(
    uint_t width,
    uint_t height
)

Sends a request to SSH server to resize pseudo-terminal to width x height. If isSync is true then this method does not return until the remote server completes the request.

Returns true on success. If pseudo-terminal could not be resized, error returned by the remote server is set and then the function returns false [1].

size_t errorcode read(
    void* p,
    size_t size
)

Attempts to receive up to size bytes from the SSH socket into the buffer pointed to by p.

Returns the actual amount of bytes read on success. If read operation is unsuccessful, IO error supplied by the operating system is set and then the function returns -1 [1].

Normally you would call this function from within your event handler for IncomingData event. If this function is called when there is no incoming data, it blocks until either the data arrives, or the socket is closed.

size_t errorcode write(
    void const* p,
    size_t size
)

Attempts to send size bytes from the buffer pointed to by p over the established SSH channel.

Returns the actual amount of bytes written on success. If write operation is unsuccessful, IO error supplied by the operating system is set and then the function returns -1 [1].

If the return value shows less bytes than specified by size argument, you should suspend further transmission until the file stream is ready to accept more data. When this happens, WriteBufferReady event is fired and transmission could be resumed.

Aliases

alias dispose = close

Effectively makes io.SshChannel a disposable class [2].


Footnotes