Sample code for 30+ languages & platforms
PureBasic

TCP or TLS over Multiple Hop SSH to Remote Server

See more Socket/SSL/TLS Examples

Demonstrates how to use the Chilkat Socket API to connect to a remote server (using TCP or TLS) tunneled through mulitple-hop SSH. The scheme looks like this:
Application => ServerSSH1 => ServerSSH2 => DestinationServer

The ConnectThroughSsh and UseSsh methods are added in Chilkat version 9.5.0.55 to accomplish this task.

Chilkat PureBasic Downloads

PureBasic
IncludeFile "CkSsh.pb"
IncludeFile "CkSocket.pb"

Procedure ChilkatExample()

    success.i = 0

    ; This example requires the Chilkat API to have been previously unlocked.
    ; See Global Unlock Sample for sample code.

    ssh1.i = CkSsh::ckCreate()
    If ssh1.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    ; Connect directly to the 1st SSH server.
    success = CkSsh::ckConnect(ssh1,"serverssh1.com",22)
    If success <> 1
        Debug CkSsh::ckLastErrorText(ssh1)
        CkSsh::ckDispose(ssh1)
        ProcedureReturn
    EndIf

    ; Authenticate using login/password:
    success = CkSsh::ckAuthenticatePw(ssh1,"ssh1Login","ssh1Password")
    If success <> 1
        Debug CkSsh::ckLastErrorText(ssh1)
        CkSsh::ckDispose(ssh1)
        ProcedureReturn
    EndIf

    ; Connect through the 1st SSH connection to reach a 2nd SSH server.
    ; Note: Any number of SSH connections may be simultaneously tunneled through a single
    ; existing SSH connection.
    ssh2.i = CkSsh::ckCreate()
    If ssh2.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    success = CkSsh::ckConnectThroughSsh(ssh2,ssh1,"serverssh2.com",22)
    If success <> 1
        Debug CkSsh::ckLastErrorText(ssh2)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        ProcedureReturn
    EndIf

    ; Authenticate with ssh2...
    success = CkSsh::ckAuthenticatePw(ssh2,"ssh2Login","ssh2Password")
    If success <> 1
        Debug CkSsh::ckLastErrorText(ssh2)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        ProcedureReturn
    EndIf

    socket.i = CkSocket::ckCreate()
    If socket.i = 0
        Debug "Failed to create object."
        ProcedureReturn
    EndIf

    ; Tell the socket object to connect to our destination server though the ssh2 tunnel (which itself is routed through ssh1).
    ; The connection looks like this:  ApplicationSocket(TLS) => ServerSSH1 => ServerSSH2 => DestinationServer
    success = CkSocket::ckUseSsh(socket,ssh2)
    If success <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    ; Connect using TLS to www.chilkatsoft.com
    ; We could also tunnel a bare TCP connection by specifying port 80 with useTls = 0.
    useTls.i = 1
    maxWaitMillisec.i = 20000
    success = CkSocket::ckConnect(socket,"www.chilkatsoft.com",443,useTls,maxWaitMillisec)
    If success <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    ; Once the multiple hop SSH tunneled connection is setup,  the socket programming
    ; is identical to the normal case where we have a direct connection.

    ; Tell the socket object that all text is to be sent in the utf-8 encoding,
    ; and the text received is assumed to be utf-8.
    CkSocket::setCkStringCharset(socket, "utf-8")

    ; Send an HTTP HEAD request:
    success = CkSocket::ckSendString(socket,"HEAD / HTTP/1.1" + Chr(13) + Chr(10) + "Host: www.chilkatsoft.com" + Chr(13) + Chr(10) + Chr(13) + Chr(10))
    If success <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    ; Wait a maximum of 4 seconds while no data is forthcoming:
    CkSocket::setCkMaxReadIdleMs(socket, 4000)

    ; Get the 1st response line, which should be "HTTP/1.1 200 OK"
    responseStatusLine.s = CkSocket::ckReceiveToCRLF(socket)
    If CkSocket::ckLastMethodSuccess(socket) <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    Debug "StatusLine: " + responseStatusLine

    ; Now get the 1st line of the response header:
    responseHeaderLine.s = CkSocket::ckReceiveToCRLF(socket)
    If CkSocket::ckLastMethodSuccess(socket) <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    Debug "HeaderLine: " + responseHeaderLine

    ; Now read the remainder of the response header by reading until a double CRLF is seen:
    remainderOfHeader.s = CkSocket::ckReceiveUntilMatch(socket,Chr(13) + Chr(10) + Chr(13) + Chr(10))
    If CkSocket::ckLastMethodSuccess(socket) <> 1
        Debug CkSocket::ckLastErrorText(socket)
        CkSsh::ckDispose(ssh1)
        CkSsh::ckDispose(ssh2)
        CkSocket::ckDispose(socket)
        ProcedureReturn
    EndIf

    Debug "Remainder: " + remainderOfHeader

    ; Close the connection with the server.  This closes the tunnel through ssh2.
    ; Wait a max of 20 seconds (20000 millsec)
    success = CkSocket::ckClose(socket,20000)

    ; Close the connection with ssh2.  (This closes the the tunnel through ssh1.)
    ; The connection with ssh1 is still alive, and may be used for more connections.
    CkSsh::ckDisconnect(ssh2)

    CkSsh::ckDisconnect(ssh1)


    CkSsh::ckDispose(ssh1)
    CkSsh::ckDispose(ssh2)
    CkSocket::ckDispose(socket)


    ProcedureReturn
EndProcedure