diff --git a/.circleci/config.yml b/.circleci/config.yml index 42eba7471..42f5e6f07 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,8 @@ jobs: - checkout - run: name: Set up ssh & known_hosts - command: sudo /etc/init.d/ssh start; rm -f ~/.ssh/id_rsa*; ssh-keygen -q -N "" -f ~/.ssh/id_rsa; sudo rm -f ~/.ssh/authorized_keys; cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys; rm -f ~/.ssh/known_hosts; ssh -o "StrictHostKeyChecking no" localhost ls + command: sudo sed -i -e 's/#ListenAddress/ListenAddress/' /etc/ssh/sshd_config; sudo sed -i -e 's/AddressFamily inet/AddressFamily any/' /etc/ssh/sshd_config; sudo /etc/init.d/ssh restart; rm -f ~/.ssh/id_rsa*; ssh-keygen -q -N "" -f ~/.ssh/id_rsa; sudo rm -f ~/.ssh/authorized_keys; cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys; rm -f ~/.ssh/known_hosts; ssh -o "StrictHostKeyChecking no" localhost ls; ssh -o "StrictHostKeyChecking no" 0:0:0:0:0:0:0:1 ls + - run: name: Init submodules command: if [ $CIRCLE_BRANCH != "release" ]; then git submodule update --init --recursive; fi @@ -31,6 +32,12 @@ jobs: - run: name: Connect new -> old command: sudo ../root_version/build/etserver --daemon; sudo cp ../root_version/build/etterminal /usr/bin/etterminal; sleep 1; build/et -c "ls" localhost --logtostdout --verbose=9 + - run: + name: Connect new -> old (ipv6) + command: build/et -c "ls" 0:0:0:0:0:0:0:1 --logtostdout --verbose=9 + - run: + name: Connect new -> old (ipv6 with port in host) + command: build/et -c "ls" 0:0:0:0:0:0:0:1:2022 --logtostdout --verbose=9 - run: name: Kill server command: sudo pkill etserver diff --git a/src/base/SubprocessToString.cpp b/src/base/SubprocessToString.cpp index cb948e856..ff3785895 100644 --- a/src/base/SubprocessToString.cpp +++ b/src/base/SubprocessToString.cpp @@ -2,7 +2,7 @@ namespace et { #ifdef WIN32 -#define BUFSIZE 4096 +#define BUFSIZE 4096 void ErrorExit(PTSTR); @@ -14,13 +14,13 @@ namespace et { HANDLE g_hChildStd_OUT_Wr = NULL; - // Set the bInheritHandle flag so pipe handles are inherited. + // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; - // Create a pipe for the child process's STDOUT. + // Create a pipe for the child process's STDOUT. if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) ErrorExit(TEXT("StdoutRd CreatePipe")); @@ -30,12 +30,12 @@ namespace et { if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) ErrorExit(TEXT("Stdout SetHandleInformation")); - // Create a pipe for the child process's STDIN. + // Create a pipe for the child process's STDIN. if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) ErrorExit(TEXT("Stdin CreatePipe")); - // Ensure the write handle to the pipe for STDIN is not inherited. + // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) ErrorExit(TEXT("Stdin SetHandleInformation")); @@ -50,11 +50,11 @@ namespace et { STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; - // Set up members of the PROCESS_INFORMATION structure. + // Set up members of the PROCESS_INFORMATION structure. ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); - // Set up members of the STARTUPINFO structure. + // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); @@ -64,30 +64,30 @@ namespace et { siStartInfo.hStdInput = g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; - // Create the child process. + // Create the child process. std::wstring_convert> converter; std::wstring wide = converter.from_bytes(localCommand); bSuccess = CreateProcess(NULL, - &(wide[0]), // command line - NULL, // process security attributes - NULL, // primary thread security attributes - TRUE, // handles are inherited - 0, // creation flags - NULL, // use parent's environment - NULL, // use parent's current directory - &siStartInfo, // STARTUPINFO pointer - &piProcInfo); // receives PROCESS_INFORMATION - - // If an error occurs, exit the application. + &(wide[0]), // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. if (!bSuccess) ErrorExit(TEXT("CreateProcess")); else { // Close handles to the child process and its primary thread. // Some applications might keep these handles to monitor the status - // of the child process, for example. + // of the child process, for example. CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); @@ -99,10 +99,10 @@ namespace et { CloseHandle(g_hChildStd_IN_Rd); } - // Read from pipe that is the standard output for child process. + // Read from pipe that is the standard output for child process. // Read output from the child process's pipe for STDOUT - // and write to the parent process's pipe for STDOUT. - // Stop when there is no more data. + // and write to the parent process's pipe for STDOUT. + // Stop when there is no more data. DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; bSuccess = FALSE; @@ -117,20 +117,20 @@ namespace et { childOutput += string((const char*)chBuf, (size_t)dwRead); } - // The remaining open handles are cleaned up when this process terminates. - // To avoid resource leaks in a larger application, close handles explicitly. + // The remaining open handles are cleaned up when this process terminates. + // To avoid resource leaks in a larger application, close handles explicitly. return childOutput; } -#include +#include #include -#include +#include #include void ErrorExit(PTSTR lpszFunction) - // Format a readable error message, display a message box, + // Format a readable error message, display a message box, // and exit from the application. { LPVOID lpMsgBuf; @@ -169,9 +169,9 @@ namespace et { } pid_t pid = fork(); - if (!pid) { - // start etserver daemon on dst. - dup2(link_client[1], 1); + if (pid == 0) { + // child process + dup2(link_client[1], STDOUT_FILENO); close(link_client[0]); close(link_client[1]); @@ -181,21 +181,17 @@ namespace et { argsArray[a + 1] = strdup(args[a].c_str()); } argsArray[args.size() + 1] = NULL; - // run the command in interactive mode execvp(command.c_str(), argsArray); - LOG(INFO) << "execl error"; + LOG(INFO) << "execvp error"; for (int a = 0; a <= args.size(); a++) { free(argsArray[a]); } delete[] argsArray; exit(1); } - else if (pid < 0) { - LOG(INFO) << "Failed to fork"; - exit(1); - } - else { + else if (pid > 0) { + // parent process close(link_client[1]); wait(NULL); string sshBuffer; @@ -208,7 +204,11 @@ namespace et { } return sshBuffer; } + else { + LOG(INFO) << "Failed to fork"; + exit(1); + } } #endif -} \ No newline at end of file +} diff --git a/src/terminal/PsuedoUserTerminal.hpp b/src/terminal/PsuedoUserTerminal.hpp index a6b84669e..1a7e95f8b 100644 --- a/src/terminal/PsuedoUserTerminal.hpp +++ b/src/terminal/PsuedoUserTerminal.hpp @@ -31,14 +31,15 @@ class PsuedoUserTerminal : public UserTerminal { switch (pid) { case -1: FATAL_FAIL(pid); + break; case 0: { close(routerFd); runTerminal(); + // only get here if execl fails so a break is not needed since we exit exit(0); } default: { // parent - break; } } diff --git a/src/terminal/SshSetupHandler.cpp b/src/terminal/SshSetupHandler.cpp index e062a445f..dba3bbdcd 100644 --- a/src/terminal/SshSetupHandler.cpp +++ b/src/terminal/SshSetupHandler.cpp @@ -3,21 +3,25 @@ #include "SubprocessToString.hpp" namespace et { +const string SshSetupHandler::ETTERMINAL_BIN = "etterminal"; + string genCommand(const string& passkey, const string& id, const string& clientTerm, const string& user, bool kill, - const string& command_prefix, const string& options) { - string SSH_SCRIPT_PREFIX; + const string& etterminal_path, const string& options) { + string ssh_script_prefix; + string etterminal_bin = + etterminal_path.empty() ? SshSetupHandler::ETTERMINAL_BIN : etterminal_path; - string COMMAND = "echo '" + id + "/" + passkey + "_" + clientTerm + "\n' | " + - command_prefix + " etterminal " + options; + string command = "echo '" + id + "/" + passkey + "_" + clientTerm + "' | " + + etterminal_bin + " " + options; // Kill old ET sessions of the user if (kill) { - SSH_SCRIPT_PREFIX = - "pkill etterminal -u " + user + "; sleep 0.5; " + SSH_SCRIPT_PREFIX; + ssh_script_prefix = + "pkill etterminal -u " + user + "; sleep 0.5; " + ssh_script_prefix; } - return SSH_SCRIPT_PREFIX + COMMAND; + return ssh_script_prefix + command; } string SshSetupHandler::SetupSsh(const string& user, const string& host, diff --git a/src/terminal/SshSetupHandler.hpp b/src/terminal/SshSetupHandler.hpp index feb9361df..ca74c6809 100644 --- a/src/terminal/SshSetupHandler.hpp +++ b/src/terminal/SshSetupHandler.hpp @@ -9,9 +9,10 @@ class SshSetupHandler { static string SetupSsh(const string &user, const string &host, const string &host_alias, int port, const string &jumphost, int jport, bool kill, - int vlevel, const string &cmd_prefix, + int vlevel, const string &etterminal_path, const string &serverFifo, const std::vector& ssh_options); + static const string ETTERMINAL_BIN; }; } // namespace et #endif // __ET_SSH_SETUP_HANDLER__ diff --git a/src/terminal/TerminalClientMain.cpp b/src/terminal/TerminalClientMain.cpp index 8890efdb1..38979c8ea 100644 --- a/src/terminal/TerminalClientMain.cpp +++ b/src/terminal/TerminalClientMain.cpp @@ -51,7 +51,8 @@ int main(int argc, char** argv) { cxxopts::value()->default_value("2022")) // ("c,command", "Run command on connect", cxxopts::value()) // - ("prefix", "Add prefix when launching etterminal on server side", + ("terminal-path", "Path to etterminal on server side. " + "Use if etterminal is not on the system path.", cxxopts::value()) // ("t,tunnel", "Tunnel: Array of source:destination ports or " @@ -69,8 +70,8 @@ int main(int argc, char** argv) { ("x,kill-other-sessions", "kill all old sessions belonging to the user") // ("macserver", - "Set when connecting to an OS/X server. Sets " - "--prefix=/usr/local/bin/etterminal") // + "Set when connecting to an macOS server. Sets " + "--terminal-path=/usr/local/bin/etterminal") // ("v,verbose", "Enable verbose logging", cxxopts::value()->default_value("0")) // ("k,keepalive", "Client keepalive duration in seconds", @@ -144,18 +145,29 @@ int main(int argc, char** argv) { CLOG(INFO, "stdout") << options.help({}) << endl; exit(0); } - string arg = result["host"].as(); - if (arg.find('@') != string::npos) { - int i = arg.find('@'); - username = arg.substr(0, i); - arg = arg.substr(i + 1); + string host_arg = result["host"].as(); + if (host_arg.find('@') != string::npos) { + int i = host_arg.find('@'); + username = host_arg.substr(0, i); + host_arg = host_arg.substr(i + 1); } - if (arg.find(':') != string::npos) { - int i = arg.find(':'); - destinationPort = stoi(arg.substr(i + 1)); - arg = arg.substr(0, i); + + if (host_arg.find(':') != string::npos) { + int colon_count = std::count(host_arg.begin(), host_arg.end(), ':'); + if (colon_count == 1 || colon_count == 8) { + // ipv4 or hostname or ipv6 with port specified + int port_colon_pos = host_arg.rfind(':'); + destinationPort = stoi(host_arg.substr(port_colon_pos + 1)); + host_arg = host_arg.substr(0, port_colon_pos); + } else if (colon_count == 7) { + // ipv6 without port specified + } else { + CLOG(INFO, "stdout") << "Invalid host positional arg: " + << result["host"].as() << endl; + exit(1); + } } - destinationHost = arg; + destinationHost = host_arg; string jumphost = result.count("jumphost") ? result["jumphost"].as() : ""; @@ -254,17 +266,17 @@ int main(int argc, char** argv) { if (result.count("ssh-option")) { ssh_options = result["ssh-option"].as>(); } - string prefix = ""; + string etterminal_path = ""; if (result.count("macserver") > 0) { - prefix = "/usr/local/bin/etterminal"; + etterminal_path = "/usr/local/bin/etterminal"; } - if (result.count("prefix")) { - prefix = result["prefix"].as(); + if (result.count("etterminal_path")) { + etterminal_path = result["terminal-path"].as(); } string idpasskeypair = SshSetupHandler::SetupSsh( username, destinationHost, host_alias, destinationPort, jumphost, jport, - result.count("x") > 0, result["verbose"].as(), prefix, serverFifo, - ssh_options); + result.count("x") > 0, result["verbose"].as(), etterminal_path, + serverFifo, ssh_options); string id = "", passkey = ""; // Trim whitespace diff --git a/src/terminal/TerminalMain.cpp b/src/terminal/TerminalMain.cpp index 3c8c29297..699de0e4f 100644 --- a/src/terminal/TerminalMain.cpp +++ b/src/terminal/TerminalMain.cpp @@ -13,12 +13,6 @@ using namespace et; -void setDaemonLogFile(string idpasskey, string daemonType) { - string first_idpass_chars = idpasskey.substr(0, 10); - string logFile = string(GetTempDirectory() + "etterminal_") + daemonType + - "_" + first_idpass_chars; -} - int main(int argc, char** argv) { // Setup easylogging configurations el::Configurations defaultConf = LogHandler::setupLogHandler(&argc, &argv); @@ -30,20 +24,20 @@ int main(int argc, char** argv) { ::signal(SIGINT, et::InterruptSignalHandler); // Parse command line arguments - cxxopts::Options options("et", "Remote shell for the busy and impatient"); + cxxopts::Options options("etterminal", "User terminal for Eternal Terminal."); try { - options.positional_help("[user@]hostname[:port]").show_positional_help(); options.allow_unrecognised_options(); options.add_options() // ("h,help", "Print help") // ("idpasskey", - "If set, uses IPC to send a client id/key to the server daemon", + "If set, uses IPC to send a client id/key to the server daemon. " + "Alternatively, pass in via stdin.", cxxopts::value()->default_value("")) // ("idpasskeyfile", "If set, uses IPC to send a client id/key to the server daemon from a " - "file", + "file. Alternatively, pass in via stdin.", cxxopts::value()->default_value("")) // ("jump", "If set, forward all packets between client and dst terminal") // @@ -51,6 +45,7 @@ int main(int argc, char** argv) { cxxopts::value()->default_value("")) // ("dstport", "Must be set if jump is set to true", cxxopts::value()->default_value("2022")) // + // Not used by etterminal but easylogging uses this flag under the hood ("v,verbose", "Enable verbose logging", cxxopts::value()->default_value("0")) // ("logtostdout", "Write log to stdout") // @@ -60,8 +55,6 @@ int main(int argc, char** argv) { cxxopts::value()->default_value("")) // ; - options.parse_positional({"host", "positional"}); - auto result = options.parse(argc, argv); if (result.count("help")) { CLOG(INFO, "stdout") << options.help({}) << endl; @@ -151,8 +144,6 @@ int main(int argc, char** argv) { string id = split(idpasskey, '/')[0]; string username = string(ssh_get_local_username()); if (result.count("jump")) { - setDaemonLogFile(idpasskey, "jumphost"); - // etserver with --jump cannot write to the default log file(root) LogHandler::setupLogFile( &defaultConf, @@ -183,8 +174,6 @@ int main(int argc, char** argv) { return 0; } - setDaemonLogFile(idpasskey, "terminal"); - // etserver with --idpasskey cannot write to the default log file(root) LogHandler::setupLogFile( &defaultConf,