Multi-Hop SSH Tunneling for Chilkat v9.5.0.55

The ability to multi-hop SSH tunnels is available for testing in the Chilkat v9.5.0.55 pre-release.

The typical schemes for multiple hop SSH look like this:

(for running a remote shell or commands on SSH_server_B)
application => SSH_server_A => SSH_server_B

(for connecting to a remote host:port, via Socket with TCP or TLS)
application(TCP_or_TLS) => SSH_server_A => SSH_server_B => destHost:destPort

(IMAP)
application => SSH_server_A => SSH_server_B => IMAP_server

(SMTP)
application => SSH_server_A => SSH_server_B => SMTP_server

(POP3)
application => SSH_server_A => SSH_server_B => POP3_server

(SFTP)
application => SSH_server_A => SFTP_server_B

(SCP)
application => SSH_server_A => SSH_server_B

(HTTP -- dynamic port forwarding)
application => SshTunnel_bg_thread(SOCKS) => SSH_server_A => SSH_server_B => destHost:destPort

It is technically possible to chain any number of hops together, although performance would get worse for each additional hop.

application => SSH_server_A => SSH_server_B => ... => SSH_server_N => destHost:destPort

The new v9.5.0.55 methods to achieve multi-hop SSH are:

  • Ssh.ConnectThroughSsh
  • SFtp.ConnectThroughSsh
  • SshTunnel.ConnectThroughSsh
  • Imap.UseSsh
  • Mailman.UseSsh
  • Socket.UseSsh

See the online reference documentation at Chilkat reference documentation.

See these examples:

 

SSH/SFTP Specifications (RFC)

SSH

  • RFC 4250 The Secure Shell (SSH) Protocol Assigned Numbers
  • RFC 4251 The Secure Shell (SSH) Protocol Architecture
  • RFC 4252 The Secure Shell (SSH) Authentication Protocol
  • RFC 4253 The Secure Shell (SSH) Transport Layer Protocol
  • RFC 4254 The Secure Shell (SSH) Connection Protocol
  • RFC 4255 Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
  • RFC 4256 Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)
  • RFC 4335 The Secure Shell (SSH) Session Channel Break Extension
  • RFC 4344 The Secure Shell (SSH) Transport Layer Encryption Modes
  • RFC 4419 Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol
  • RFC 4462 Generic Security Service Application Program Interface (GSS-API) Authentication and Key Exchange for the Secure Shell (SSH) Protocol
  • RFC 4716 The Secure Shell (SSH) Public Key File Format
  • RFC 4819 Secure Shell Public Key Subsystem

SFTP

SSH SendReqExec — Interactive Commands such as “more”

Commands that assume an interactive user at a shell prompt should not be passed to SendRequestExec.  For example, the “more” command assumes there is a shell and that the user will press RETURN when another screenful of text is wanted.  It would be more appropriate to use the Unix/Linux “cat” command w/ SendRequestExec.

To run interactive commands, one should instead start a remote shell by (1) starting a pseudo-terminal by calling SendReqPty and then (2) starting a shell by calling SendReqShell. See this example:

ASP: SSH — Running Commands that Prompt for Additional Input, such as “su”
SQL Server: SSH — Running Commands that Prompt for Additional Input, such as “su”
C#: SSH — Running Commands that Prompt for Additional Input, such as “su”
Delphi: SSH — Running Commands that Prompt for Additional Input, such as “su”
Visual FoxPro: SSH — Running Commands that Prompt for Additional Input, such as “su”
PHP: SSH — Running Commands that Prompt for Additional Input, such as “su”
VB.NET: SSH — Running Commands that Prompt for Additional Input, such as “su”
Visual Basic: SSH — Running Commands that Prompt for Additional Input, such as “su”
VBScript: SSH — Running Commands that Prompt for Additional Input, such as “su”

SSH SendReqExec — Commands with No Output

When a command is passed to SendReqExec that produces no output, such as “echo 1 > test.txt”, then do not try to read the channel (such as by calling ChannelReadAndPoll) because no data will be forthcoming and the channel read will timeout (as expected). The correct sequence of method calls would be to:

  1. Call SendReqExec to execute the command on the remote server.
  2. Call ChannelSendClose to initiate the closing of the channel.
  3. Call ChannelReceiveToClose to wait until the server closes the channel. In this case, no actual output data is returned because the command passed to SendReqExec produces no output.  This method is called to cleanly wait for the server’s “close” message.

    SSH / SFTP – Too much time between connect and authentication

    The Solution:

    Issue solved.   The problem was, that we stepped through the code and because of that too much time elapsed between connect and authentication.  As we ran the program without breakpoints it worked.

    The Problem:

    The AuthenticatePw method failed and the LastErrorText contained this information:

    ChilkatLog:
       AuthenticatePw:
         DllDate: Jan 31 2010
         UnlockPrefix: ***
         Username: ***
         Component: .NET 2.0
         SshVersion: SSH-2.0-XFB.Gateway Windows
         SftpVersion: 0
         login: ***
         sendMessage:
           msgName: SERVICE_REQUEST
           unpaddedLength: 22
           remainder: 6
           paddingLen: 10
           totalSize: 32
         SentServiceReq: ssh-userauth
         numBytesRequested: 16
         Connection closed by server.
         Failed to read data on SSH connection.
         Failed to read packet from SSH server.
         Error reading service accept.
         Socket connection lost.
         Failed.
    

    Very simple C# SSH Shell Console Terminal

    Here’s an example that demonstrates a rough start to creating a C# console SSH shell terminal (where the user can type commands and output from the remote command echos to the console:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    
    namespace SshTerminalConsole
    {
        class Program
        {
            static void Main(string[] args)
            {
                Chilkat.Ssh ssh = new Chilkat.Ssh();
                ssh.UnlockComponent("Test");
    
                //  Hostname may be an IP address or hostname:
                string hostname = "192.168.1.117";
                int port = 22;
    
                Console.WriteLine("Connecting...");
    
                //ssh.KeepSessionLog = true;
                bool success = ssh.Connect(hostname, port);
                if (success != true)
                {
                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                    // Read so we can see the error before the console closes.
                    string x = Console.ReadLine();      
                    return;
                }
    
                //  When reading, if no additional data arrives for more than
                //  5 seconds, then abort:
                ssh.IdleTimeoutMs = 5000;
    
                Console.WriteLine("Authenticating...");
    
                //  SSH Server Authentication
                //  If there is no login/password required, you must still call
                //  AuthenticatePw and use any values for login/password.
                success = ssh.AuthenticatePw("chilkat", "***");
                if (success != true)
                {
                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                    // Read so we can see the error before the console closes.
                    string x = Console.ReadLine();
                    return;
                }
    
                Console.WriteLine("Opening Channel...");
    
                //  Open a session channel.
                int channelNum = ssh.OpenSessionChannel();
                if (channelNum < 0)
                {
                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                    // Read so we can see the error before the console closes.
                    string x = Console.ReadLine();
                    return;
                }
    
                //  Request a pseudo-terminal
                string termType;
                termType = "dumb";
                int widthInChars;
                widthInChars = 120;
                int heightInChars;
                heightInChars = 40;
                int pixWidth;
                pixWidth = 0;
                int pixHeight;
                pixHeight = 0;
                success = ssh.SendReqPty(channelNum, termType, widthInChars, heightInChars, pixWidth, pixHeight);
                if (success != true)
                {
                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                    // Read so we can see the error before the console closes.
                    string x = Console.ReadLine();
                    return;
                }
    
                Console.WriteLine("Starting a shell...");
    
                //  Start a shell on the channel:
                success = ssh.SendReqShell(channelNum);
                if (success != true)
                {
                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                    // Read so we can see the error before the console closes.
                    string x = Console.ReadLine();
                    return;
                }
    
                // Loop to read from the SSH channel, output to the console, and read keyboard input from the console.
                StringBuilder sb = new StringBuilder();
                while (true)
                {
                    if (Console.KeyAvailable)
                    {
                        ConsoleKeyInfo key = Console.ReadKey(true);
                        Console.Write(key.KeyChar);
    
                        switch (key.Key)
                        {
                            case ConsoleKey.Enter:
                                Console.WriteLine("");
    
                                sb.Append("\n");
                                success = ssh.ChannelSendString(channelNum, sb.ToString(), "ansi");
                                if (success != true)
                                {
                                    Console.WriteLine(ssh.LastErrorText + "\r\n");
                                    // Read so we can see the error before the console closes.
                                    string x = Console.ReadLine();
                                    return;
                                }
    
                                sb.Length = 0;
                                break;
                            default:
                                //Console.Write(key.KeyChar);
                                sb.Append(key.KeyChar);
                                break;
                        }
    
                    }
    
                    // Now check for incoming data from the SSH channel.
                    int retval = ssh.ChannelPoll(channelNum, 10);
                    if (retval == -1)
                    {
                        Console.Write(ssh.LastErrorText);
                        Console.WriteLine("");
                        // Read so we can see the error before the console closes.
                        string x = Console.ReadLine();
                        return;
                    }
                    if (retval > 0)
                    {
                        Console.Write(ssh.GetReceivedText(channelNum, "ansi"));
                    }
                    else
                    {
                        // If data arrived, loop around and get more immediately.
                        // Otherwise wait 20ms.
                        System.Threading.Thread.Sleep(20);
                    }
    
                }  
    
            }
        }
    }
    

    Converting a PuTTY Private Key (.ppk) to OpenSSH (.pem)

    ASP: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    SQL Server: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    C#: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    C++: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    MFC: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    C: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Delphi: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Visual FoxPro: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Java: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Perl: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    PHP: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Python: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Ruby: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    VB.NET: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    Visual Basic: Convert PuTTY Private Key (ppk) to OpenSSH (pem)
    VBScript: Convert PuTTY Private Key (ppk) to OpenSSH (pem)