Skip to content

Commit

Permalink
Support for ipv6 addresses (with or without port specified) (#536)
Browse files Browse the repository at this point in the history
* Support for ipv6 addresses (with or without port specified)

Currently et does not support passing an ipv6 address as the host
positional argument is assumed to be an ipv4 address or hostname with a
single colon with the port after it and the rest is ignored, eg:

```
$ et 2620:10d:c083:1418:14ac:6bbc:4737:56ee                                                                                                                                     15:19:52  10.07.22  100% █
Could not reach the ET server: 2620:10
```

This change will support ipv4 address or hostname with port specified or
ipv6 address with or without port specified.  Currently, if a port is
specified in the positional arg, it overwrites what was specified
(explicitly or by default) with the -p,--port option. I think this is
backwards and may offer a PR to invert this logic.

* etterminal option/description cleanup plus other housecleaning.

No-op changes:
* TerminalMain: change binary name/description
* TerminalMain: get rid of Daemon log file (does nothing now)
* SubprocessToString: reorganize forking logic and remove irrelevant
  comments and cleanup trailing whitespace
* PsuedoUserTerminal: breaks in forking switch statement

* Rename et --prefix option to --terminal-path.

The prefix option to the terminal client is misleading as it isn't a
prefix (or search path) and is treated as an absolute path to the
etterminal executable.  This renames it as such and removes the
superfluous positional arg in the command string building for the ssh
command (i.e. /usr/local/bin/etterminal etterminal -v 0)
  • Loading branch information
jshort committed Oct 12, 2022
1 parent ccc3435 commit c1d54e7
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 84 deletions.
9 changes: 8 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
78 changes: 39 additions & 39 deletions src/base/SubprocessToString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace et {
#ifdef WIN32
#define BUFSIZE 4096
#define BUFSIZE 4096

void ErrorExit(PTSTR);

Expand All @@ -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"));
Expand All @@ -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"));
Expand All @@ -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));
Expand All @@ -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<std::codecvt_utf8_utf16<wchar_t>> 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);
Expand All @@ -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;
Expand All @@ -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 <windows.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdio.h>
#include <strsafe.h>

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;
Expand Down Expand Up @@ -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]);

Expand All @@ -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;
Expand All @@ -208,7 +204,11 @@ namespace et {
}
return sshBuffer;
}
else {
LOG(INFO) << "Failed to fork";
exit(1);
}
}
#endif

}
}
3 changes: 2 additions & 1 deletion src/terminal/PsuedoUserTerminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
18 changes: 11 additions & 7 deletions src/terminal/SshSetupHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion src/terminal/SshSetupHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>& ssh_options);
static const string ETTERMINAL_BIN;
};
} // namespace et
#endif // __ET_SSH_SETUP_HANDLER__
50 changes: 31 additions & 19 deletions src/terminal/TerminalClientMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ int main(int argc, char** argv) {
cxxopts::value<int>()->default_value("2022")) //
("c,command", "Run command on connect",
cxxopts::value<std::string>()) //
("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<std::string>()) //
("t,tunnel",
"Tunnel: Array of source:destination ports or "
Expand All @@ -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<int>()->default_value("0")) //
("k,keepalive", "Client keepalive duration in seconds",
Expand Down Expand Up @@ -144,18 +145,29 @@ int main(int argc, char** argv) {
CLOG(INFO, "stdout") << options.help({}) << endl;
exit(0);
}
string arg = result["host"].as<std::string>();
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<std::string>();
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<std::string>() << endl;
exit(1);
}
}
destinationHost = arg;
destinationHost = host_arg;

string jumphost =
result.count("jumphost") ? result["jumphost"].as<string>() : "";
Expand Down Expand Up @@ -254,17 +266,17 @@ int main(int argc, char** argv) {
if (result.count("ssh-option")) {
ssh_options = result["ssh-option"].as<std::vector<string>>();
}
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<string>();
if (result.count("etterminal_path")) {
etterminal_path = result["terminal-path"].as<string>();
}
string idpasskeypair = SshSetupHandler::SetupSsh(
username, destinationHost, host_alias, destinationPort, jumphost, jport,
result.count("x") > 0, result["verbose"].as<int>(), prefix, serverFifo,
ssh_options);
result.count("x") > 0, result["verbose"].as<int>(), etterminal_path,
serverFifo, ssh_options);

string id = "", passkey = "";
// Trim whitespace
Expand Down
Loading

0 comments on commit c1d54e7

Please sign in to comment.