Question:
How can I debug SSH tunnel? (CkSshTunnel in C++)?
The best approach is to begin with something very simple -- and step-by-step add functionality until you arrive at the final objective. Starting with the entire ball-of-wax and trying to determine the problems in a complex system will typically get you nowhere. It's better to begin with simplicity, get it working, and then add complexity bit by bit.
This guide will use C++ for the programming language, but the API is identical (except for syntax and naming conventions) in all programming languages supported by Chilkat.
The first, most basic task is to setup the CkSshTunnel for debug logging, specify the property settings, and then start a background thread for accepting incoming connections. That's it. After starting the background thread, shut it down and exit the test program. No actual connections will be tested in the very first step. The point here is to (1) make sure the background thread can be started and stoppped, and (2) make sure the log files can be created. Your program must specify log file paths such that you have the filesystem permissions to create the files.
Make sure your C++ program is compiled for multi-threaded (do NOT use a single-thread runtime).
Here is the source code for this test:
// This tests the bare minimum: // It starts a background thread to begin accepting incoming connections. // It then stops the background thread, and stops all tunnels. // (However, in this example, no tunnels are actually created, so there aren't // actually any tunnels to be stopped.) void sshTunnelTest0(void) { CkSshTunnel sshTunnel;// For debugging, indicate where log files are to be created. sshTunnel.put_DebugLogFilePath("c:/aaworkarea/sshTunnelDebugLog.txt"); sshTunnel.put_TunnelThreadSessionLogPath("c:/aaworkarea/tunnelThreads.txt"); sshTunnel.put_AcceptThreadSessionLogPath("c:/aaworkarea/acceptThread.txt"); bool success; success = sshTunnel.UnlockComponent("30-day trial"); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // Indicate the final destination host:port. // This is the host:port that the SSH server will connect to. sshTunnel.put_DestPort(80); sshTunnel.put_DestHostname("www.chilkatsoft.com"); // Provide information about the location of the SSH server, // and the authentication to be used with it. This is the // login information for the SSH user account to be used. sshTunnel.put_SshHostname("192.168.1.109"); sshTunnel.put_SshPort(22); sshTunnel.put_SshLogin("mySshLogin"); sshTunnel.put_SshPassword("mySshPassword"); // Start accepting connections in a background thread. // The SSH tunnels are autonomously run in a background // thread. There is one background thread for accepting // connections, and another for managing the tunnel pool. long listenPort; listenPort = 3316; success = sshTunnel.BeginAccepting(listenPort); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // Do nothing -- this test is simply to verify that the background thread // for accepting incoming connections can be created and stopped. // Stop the background thread that accepts new connections: success = sshTunnel.StopAccepting(); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // If any background tunnels are still in existence (and managed // by a single SSH tunnel pool background thread), stop them... // In this example, there are no existing tunnels, but call it anyway... long maxWaitMs; maxWaitMs = 1000; success = sshTunnel.StopAllTunnels(maxWaitMs); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } printf("ALL OK!\n"); }
Step #2 -- This is the same as the initial sanity test, except a single SSH tunnel is created, and then shutdown. No data is sent or received over the tunnel.
// The next test is the same as the sshTunnelTest0, except we // initiate a connection to the SSH tunnel. Our background thread is waiting // for incoming connections on port 3316, and our foreground thread will connect to it. // Upon connecting, the background thread connects to the SSH server to create an // SSH tunnel. (The SSH server connects to the DestHostname:DestPort) // This is a bi-direction connection where data sent to localhost:3316 // will be forwarded over the SSH tunnel to the SSH server, and then the server // will forward the data to DestHostname:DestPort.// For now, let's just establish the tunnel. Don't send any data. // Make sure the tunnel can be created and shutdown. void sshTunnelTest1(void) {
CkSshTunnel sshTunnel; sshTunnel.put_DebugLogFilePath("c:/aaworkarea/sshTunnelDebugLog.txt"); sshTunnel.put_TunnelThreadSessionLogPath("c:/aaworkarea/tunnelThreads.txt"); sshTunnel.put_AcceptThreadSessionLogPath("c:/aaworkarea/acceptThread.txt"); bool success; success = sshTunnel.UnlockComponent("30-day trial"); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } sshTunnel.put_DestPort(80); sshTunnel.put_DestHostname("www.chilkatsoft.com"); // Provide information about the location of the SSH server, // and the authentication to be used with it. sshTunnel.put_SshHostname("192.168.1.109"); sshTunnel.put_SshPort(22); sshTunnel.put_SshLogin("mySshLogin"); sshTunnel.put_SshPassword("mySshPassword"); // Start accepting connections in a background thread. // The SSH tunnels are autonomously run in a background // thread. There is one background thread for accepting // connections, and another for managing the tunnel pool. long listenPort; listenPort = 3316; success = sshTunnel.BeginAccepting(listenPort); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // You don't actually have to use CkSocket here. You can use // any TCP socket programming API, including the standard system socket system calls.. CkSocket sock; success = sock.UnlockComponent("30-day trial"); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } // Establish the tunnel. After this program completes, examine the logs // to see the activity... success = sock.Connect("localhost",3316,false,2000); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } // Give it some time to actually connect before aborting/stopping everything. // This causes the foreground thread to sleep for 5 seconds. // (This test was done on MS Windows, where the Sleep function is part of the MS Platform SDK.) Sleep(5000); // Stop the background thread that accepts new connections: success = sshTunnel.StopAccepting(); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // If any background tunnels are still in existence (and managed // by a single SSH tunnel pool background thread), stop them... long maxWaitMs; maxWaitMs = 1000; success = sshTunnel.StopAllTunnels(maxWaitMs); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } printf("ALL OK!\n"); }
The final test is to establish an SSH tunnel, and then send/receive data.
// This is the same as the sshTunnelTest1, except we // will now send data over the tunnel.// We'll send an HTTP HEAD request through the SSH tunnel to an HTTP server, // and then read the response... void sshTunnelTest2(void) {
CkSshTunnel sshTunnel; sshTunnel.put_DebugLogFilePath("c:/aaworkarea/sshTunnelDebugLog.txt"); sshTunnel.put_TunnelThreadSessionLogPath("c:/aaworkarea/tunnelThreads.txt"); sshTunnel.put_AcceptThreadSessionLogPath("c:/aaworkarea/acceptThread.txt"); bool success; success = sshTunnel.UnlockComponent("30-day trial"); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } sshTunnel.put_DestPort(80); sshTunnel.put_DestHostname("www.chilkatsoft.com"); sshTunnel.put_SshHostname("192.168.1.109"); sshTunnel.put_SshPort(22); sshTunnel.put_SshLogin("mySshLogin"); sshTunnel.put_SshPassword("mySshPassword"); // Start accepting connections in a background thread. // The SSH tunnels are autonomously run in a background // thread. There is one background thread for accepting // connections, and another for managing the tunnel pool. long listenPort; listenPort = 3316; success = sshTunnel.BeginAccepting(listenPort); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // You don't actually have to use CkSocket here. You can use // any TCP socket programming API, including the standard system socket system calls.. CkSocket sock; success = sock.UnlockComponent("30-day trial"); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } // Establish the tunnel. After this program completes, examine the logs // to see the activity... success = sock.Connect("localhost",3316,false,2000); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } success = sock.SendString("HEAD / HTTP/1.1\r\nHost:www.chilkatsoft.com\r\n\r\n"); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } CkString strResponse; success = sock.ReceiveUntilMatch("\r\n\r\n",strResponse); if (success != true) { printf("%s\n",sock.lastErrorText()); return; } printf("HTTP Response: %s\n",strResponse.getString()); // Stop the background thread that accepts new connections: success = sshTunnel.StopAccepting(); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } // If any background tunnels are still in existence (and managed // by a single SSH tunnel pool background thread), stop them... long maxWaitMs; maxWaitMs = 1000; success = sshTunnel.StopAllTunnels(maxWaitMs); if (success != true) { printf("%s\n",sshTunnel.lastErrorText()); return; } printf("ALL OK!\n"); }