Back to FlowSsh Documentation

Client

[C]

struct FlowSshC_Client {};

Creation
---------
FlowSshC_Client* __cdecl FlowSshC_Client_Create();

[C++]

class Client : public IRefCountable, public NoCopy
{
public:
  Client();
..
}

[C#]

public class Client : IDisposable
{
  public Client();
..
}

Members

Creation, Destruction

  • Create - Create a new client object.
  • [C/Cpp] AddRef - Increment reference count.
  • [C/Cpp] Release - Decrement reference count.

Initialization

Connection and User Authentication

Proxy Settings

Algorithms

Options

  • SetOptions - Enable or disable key re-exchange and keep-alive.

Port Forwarding

[C] Handler Registration

[Cpp/Net] Overrides and Events

[Net] Disposing

  • IsDisposed - Is the object disposed?
  • Dispose - Release all resources used by the object. Also initiates a disconnect.

Remarks

The client interface enables you to establish a secure connection with an SSH server. Every connected client represents a single encrypted TCP connection with the server. Each connection can then host several channels that perform various SSH tasks. In FlowSshC, clients are represented by an opaque FlowSshC_Client structure. In FlowSshCpp/Net they are managed by the Client class. Handlers are called only for active clients. A client is said to be active if it is either connected or connecting.

Before you establish a connection you have to:

  • Set an application name with [Client]SetAppName.
  • Specify a host name or IP of your SSH server and (optionally) a port number.
  • Specify a user name and (optionally) a password or a keypair which authenticates you to the server.
  • Register/implement at least the OnHostKey handler. Without this handler server (host-key) verification fails and the connection is aborted.
  • (optionally) Register/implement other handlers. Usually you will also have an OnBanner, OnPasswordChange, and an OnFurtherAuth handler (in [C] the last two are registered with SetUserAuthHandler).
  • (optionally) Set proxy settings.

User Authentication. By default:

  • Host and user name are empty strings, and port is set to 22.
  • Password and keypair are NULL/null - not set.
  • DNS names are resolved locally (ProxyOptions: resolveLocally = true).

Authentication procedure

  • If neither a password nor a keypair is set, the 'none' authentication method will be attempted.
  • If both password and keypair are set, 'public-key' authentication will be performed first.
  • If a password is set and the 'password' authentication method either fails or is not available, then password through 'keyboard-interactive' will be attempted. Conditions for a successful password through 'keyboard-interactive' authentication are as follows:
    • The kbdi-info request must contain exactly one prompt.
    • One of the kbdi-info request strings must contain a case-insensitive "password" substring.
  • If further authentication is required, the [Client]OnFurtherAuth handler (in [C] UserAuth) will be invoked. If this handler is not registered/implemented, or if it returns false, the client will disconnect with a user authentication failure.
  • If a password change is requested during 'password' authentication, the [Client]OnPasswordChange (in [C] UserAuth) will be invoked. If this handler is not registered/implemented, if it returns false, or if the server rejects the new password, the original 'password' authentication attempt will fail.

Proxy Behavior. By default:

  • The use of a proxy server is disabled (ProxyType = [ProxyType]None).
  • The proxy host is an empty string and the proxy port is set to 1080.
  • DNS names are resolved locally (ProxyOptions: resolveLocally = true), not by the proxy server.

SOCKSv4 supports DNS names starting with the SOCKSv4a extension. Therefore, if your SOCKSv4 proxy does not support the SOCKSv4a extension, DNS names must be resolved locally (the default). SOCKSv4 (including SOCKSv4a) does not support authentication.

Algorithms. By default:

  • All key exchange algorithms are enabled and ordered by their declaration order.
  • Both host-key algorithms are enabled and "ssh-rsa" takes precedence.
  • All encryption algorithms except "none" are enabled and ordered by their declaration order.
  • All MAC algorithms except "none" are enabled and ordered by their declaration order.
  • Zlib compression is disabled while "none" is enabled. Note that compression may deteriorate transfer rates in modern high-bandwidth environments. Compression will be used only if both the client and server have enabled it.

Changing algorithm options has no effect on active clients past the initial key exchange. You will normally not need to alter any of these options. If you do, it should be only for a clear reason.

Port Forwarding. Forwarding instructions will fail immediately if the client is not connected or connecting, and if the client is connecting they will be delayed until the connect request completes.

Examples

The examples below demonstrate how to establish a connection with the server using FlowSshC, FlowSshCpp, and FlowSshNet, respectively. For the sake of simplicity only three basic handlers are implemented:

  • A global error handler. It must always be registered before using the library.
  • A host-key handler. When a connection with the server is being established, this handler authenticates the server.
  • A progress handler which tracks the connection progress. It also tells us if the connection was established or not.

In a real world scenario you will mostly also implement the other available handlers (at least some of them).

While [Client]Connect returns immediately, without blocking, both the host-key and progress handler are called asynchronously and very likely after [Client]Connect itself returns. In these examples, OnHostKey verifies the public-key of the server to ensure we are connecting to the right server. It is our code inside this handler that decides if the connection should succeed or fail. The progress handler on the other side merely informs us about the progress of the connection being established, and about the final success or failure.

Connect to an SSH server with FlowSshC

C

#include <stdio.h>
#include "FlowSshC.h"

// OnError handler

void OnError(void* handlerData, unsigned int flags, wchar_t const* desc)
{
  wprintf(L"Fatal error: %s\n", desc ? desc : L"<no error description>");
  // For the sake of simplicity, no cleanup is performed here.
  // The lack of cleanup may cause debugger to report memory leaks.
  exit(1);
}

// OnHostKey handler - authenticates the server

bool OnHostKey(void* handlerData, FlowSshC_PublicKey* publicKey)
{
  BSTR md5 = FlowSshC_PublicKey_GetMD5(publicKey);
  BSTR bb = FlowSshC_PublicKey_GetBubbleBabble(publicKey);
  
  wprintf(L"Received the following host key:\n"
    L"MD5 Fingerprint: %s\n"
    L"Bubble-Babble: %s\n",
    md5, bb);

  SysFreeString(md5);
  SysFreeString(bb);

  wprintf(L"Accept the key (yes/no)? ");

  wchar_t input[10];
  if (_getws_s(input, 9))
    return _wcsicmp(input, L"yes") == 0;
  else return false;
}

// OnProgressConnect handler

struct ProgressConnectData
{
  bool m_connected;
  HANDLE m_doneEvent;
};

void OnProgressConnect(void* handlerData, unsigned int taskState, unsigned int taskSpecificStep, wchar_t const* auxInfo)
{
  ProgressConnectData* data = (ProgressConnectData*)handlerData;
  
  if (taskState == FlowSshC_TaskState::Completed)
  {
    data->m_connected = true;
    SetEvent(data->m_doneEvent);
  }
  else if (taskState == FlowSshC_TaskState::Failed)
  {
    data->m_connected = false;
    SetEvent(data->m_doneEvent);
    
    // We could also log the taskSpecificStep here.
    // It contains a FlowSshC_ConnectStep enumeration.
  }
}

// ConnectToServer

bool ConnectToServer(FlowSshC_Client* client, wchar_t const* host, int port, wchar_t const* userName, wchar_t const* userPwd)
{
  ProgressConnectData data = { FALSE, CreateEvent(0, true, false, 0)};
  
  FlowSshC_Client_SetHost(client, host);
  FlowSshC_Client_SetPort(client, port);
  
  FlowSshC_Client_SetUserName(client, userName);
  FlowSshC_Client_SetPassword(client, userPwd);
  
  FlowSshC_Client_SetHostKeyHandler(client, OnHostKey, 0);
  FlowSshC_Client_Connect(client, OnProgressConnect, &data);
  
  WaitForSingleObject(data.m_doneEvent, INFINITE);
  CloseHandle(data.m_doneEvent);
  return data.m_connected;
}

// wmain

int wmain(int argc, wchar_t const* argv[])
{
  wchar_t const* appName = L"my-app 1.0";
  wchar_t const* host = L"127.0.0.1";
  unsigned int port = 22;
  wchar_t const* userName = L"my-name";
  wchar_t const* userPwd = L"my-pwd";
  
  FlowSshC_Client* client = NULL;
  
  FlowSshC_Initialize(OnError, 0);
  // FlowSshC_SetActCode(L"Enter Your Activation Code Here");
  
  client = FlowSshC_Client_Create();
  FlowSshC_Client_SetAppName(client, appName);
  
  if (ConnectToServer(client, host, port, userName, userPwd))
  {
    // client successfully connected so let's do something with it
    // ..
  }
  
  FlowSshC_Client_Release(client);
  FlowSshC_Shutdown();
}

Connect to an SSH server with FlowSshCpp

C++

#include <iostream>
#include "FlowSshCpp.h"

using namespace std;
using namespace FlowSshCpp;

// MyErrorHandler

class MyErrorHandler : public ErrorHandler
{
private:
  // This object must be created on the heap.
  ~MyErrorHandler() {}

protected:
  virtual void OnExceptionInHandler(bool fatal, wchar_t const* desc)
  {
    wcout << (fatal ? L"Error [fatal]: " : L"Error: ");
    wcout << (desc ? desc : L"<no error description>") << endl;
    // For the sake of simplicity, no cleanup is performed here.
    // The lack of cleanup may cause debugger to report memory leaks.
    exit(1);
  }
};

// MyClient

class MyClient : public Client
{
private:
  // This object must be created on the heap.
  ~MyClient() {}

protected:
  // OnHostKey handler - authenticates the server
  bool OnHostKey(RefPtr<PublicKey> publicKey)
  {
    wcout << L"Received the following host key:" << endl;
    wcout << L" MD5 Fingerprint: " << publicKey->GetMD5() << endl;
    wcout << L" Bubble-Babble: " << publicKey->GetBubbleBabble() << endl;
    wcout << L"Accept the key (yes/no)? ";
    
    std::wstring input;
      wcin >> input;
    
    if (input == L"yes")
      return true;
    
    // host key rejected
    return false;
  }
};

// wmain

int wmain(int argc, wchar_t const* argv[])
{
  wchar_t const* appName = L"my-app 1.0";
  wchar_t const* host = L"127.0.0.1";
  unsigned int port = 22;
  wchar_t const* userName = L"my-name";
  wchar_t const* userPwd = L"my-pwd";
  
  try
  {
    // Initialize FlowSsh and register an ErrorHandler for uncaught exceptions in user-defined handlers.
    // Example: If there is an uncaught exception in MyClient::OnHostKey,
    //          then this is reported in MyErrorHandler::OnExceptionInHandler.
    // Set your activation code to remove the "Welcome to FlowSsh evaluation!".
    
    Initializer init(new MyErrorHandler());
    // init.SetActCode(L"Enter Your Activation Code Here");
    
    // create and connect client
    
    RefPtr<MyClient> client(new MyClient);
    client->SetAppName(appName);
    client->SetHost(host);
    client->SetPort(port);
    client->SetUserName(userName);
    client->SetPassword(userPwd);
    
    RefPtr<ProgressEvent> progress(new ProgressEvent);
    client->Connect(progress);
    progress->WaitDone();
    
    if (progress->Success())
    {
      // client successfully connected so let's do something with it
      // ..
    }
  }
  catch (Exception const& e)
  {
    wcout << e.What() << endl;
    return 1;
  }
  return 0;
}

Connect to an SSH server with FlowSshNet

C#

using System;
using System.Text;

using Bitvise;
using Bitvise.FlowSshNet;

namespace SshSample
{
  // MyClient
  
  public class MyClient : Client
  {
    public MyClient()
    {
      this.OnHostKey += new HostKeyEventHandler(this.OnMyHostKey);
    }
    
    // OnHostKey handler - authenticates the server
    private bool OnMyHostKey(object sender, PublicKey publicKey)
    {
      Console.WriteLine("Received the following host key:");
      Console.WriteLine(" MD5 Fingerprint: {0}", publicKey.GetMD5());
      Console.WriteLine(" Bubble-Babble: {0}", publicKey.GetBubbleBabble());
      Console.Write("Accept the key (yes/no)? ");
      
      string input = Console.ReadLine();
      if (input == "yes")
        return true;
      
      return false; // host key rejected
    }
  }
  
  class Program
  {
    static void OnUncaughtExceptionInEvent(object sender, bool fatal, System.Exception e)
    {
      Console.WriteLine((fatal ? "Error [fatal]: " : "Error: ") + e.ToString());
      // For the sake of simplicity, no cleanup is performed here.
      // The lack of cleanup may cause debugger to report memory leaks.
      System.Environment.Exit(1);
    }
    
    static int Main(string[] args)
    {
      try
      {
        string appName = "my-app 1.0";
        string host = "127.0.0.1";
        uint port = 22;
        string userName = "my-name";
        string userPwd = "my-pwd";
        
        // Register delegate for uncaught exceptions in user-defined events.
        // Example: If there is an uncaught exception in MyClient.OnMyHostKey,
        //          then this is reported in OnUncaughtExceptionInEvent.
        // Set your activation code to remove the "Welcome to FlowSsh evaluation!".
        
        SshNet.OnExceptionInEvent += new ExceptionInEventEventHandler(OnUncaughtExceptionInEvent);
        // SshNet.SetActCode("Enter Your Activation Code Here");
        
        // create and connect client
        
        MyClient client = new MyClient();
        client.SetAppName(appName);
        client.SetHost(host);
        client.SetPort(port);
        client.SetUserName(userName);
        client.SetPassword(userPwd);
        
        ProgressHandler progress = new ProgressHandler();
        client.Connect(progress);
        progress.WaitDone();
        
        if (progress.Success)
        {
          // client successfully connected so let's do something with it
          // ..
        }
      }
      catch (System.Exception e) // inclusive Bitvise.FlowSshNet.Exception's
      {
        Console.WriteLine(e.Message);
        return 1;
      }
      finally
      {
        SshNet.Shutdown();
      }
      return 0;
    } // Main
  } // Program
}