Skip to content

Commit

Permalink
ipv6 abbreviated address support
Browse files Browse the repository at this point in the history
Help output:

```
$> ./et --help
Remote shell for the busy and impatient
Usage:
  et [OPTION...] [user@]host[:port]

  Note that 'host' can be a hostname or ipv4 address with or without a port
  or an ipv6 address. If the ipv6 address is abbreviated with :: then it must
  be specfied without a port (use -p,--port).

  -h, --help                 Print help
      --version              Print version
  -u, --username             Username
      --host arg             Remote host name
  ...
```
Tests:

```
$> ./et ::1 -p 8080 --macserver
$> ./et 0:0:0:0:0:0:0:1 -p 8080 --macserver
$> ./et 0:0:0:0:0:0:0:1:8080 --macserver
$> ./et ::1 -p 8080 --macserver # fails because defaults to port 2022
```
Fixes #537
  • Loading branch information
jshort committed Oct 13, 2022
1 parent c1d54e7 commit 3feafb1
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 36 deletions.
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ jobs:
- 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: Connect new -> old (ipv6 abbreviated)
command: build/et -c "ls" ::1 --logtostdout --verbose=9
- run:
name: Connect new -> old (ipv6 abbreviated with port arg)
command: build/et -c "ls" ::1 --port 2022 --logtostdout --verbose=9
- run:
name: Kill server
command: sudo pkill etserver
Expand Down
96 changes: 60 additions & 36 deletions src/terminal/TerminalClientMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,60 +38,65 @@ int main(int argc, char** argv) {
// Parse command line arguments
cxxopts::Options options("et", "Remote shell for the busy and impatient");
try {
options.positional_help("[user@]hostname[:port]").show_positional_help();
options.allow_unrecognised_options();

options.add_options() //
("h,help", "Print help") //
("version", "Print version") //
("u,username", "Username") //
options.positional_help("");
options.custom_help("[OPTION...] [user@]host[:port]\n\n"
" Note that 'host' can be a hostname or ipv4 address with or without a port\n"
" or an ipv6 address. If the ipv6 address is abbreviated with :: then it must\n"
" be specfied without a port (use -p,--port)."
);

options.add_options()
("h,help", "Print help")
("version", "Print version")
("u,username", "Username")
("host", "Remote host name",
cxxopts::value<std::string>()) //
("p,port", "Remote machine port",
cxxopts::value<int>()->default_value("2022")) //
cxxopts::value<std::string>())
("p,port", "Remote machine etserver port",
cxxopts::value<int>()->default_value("2022"))
("c,command", "Run command on connect",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("terminal-path", "Path to etterminal on server side. "
"Use if etterminal is not on the system path.",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("t,tunnel",
"Tunnel: Array of source:destination ports or "
"srcStart-srcEnd:dstStart-dstEnd (inclusive) port ranges (e.g. "
"10080:80,10443:443, 10090-10092:8000-8002)",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("r,reversetunnel",
"Reverse Tunnel: Array of source:destination ports or "
"srcStart-srcEnd:dstStart-dstEnd (inclusive) port ranges",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("jumphost", "jumphost between localhost and destination",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("jport", "Jumphost machine port",
cxxopts::value<int>()->default_value("2022")) //
cxxopts::value<int>()->default_value("2022"))
("x,kill-other-sessions",
"kill all old sessions belonging to the user") //
"kill all old sessions belonging to the user")
("macserver",
"Set when connecting to an macOS server. Sets "
"--terminal-path=/usr/local/bin/etterminal") //
"--terminal-path=/usr/local/bin/etterminal")
("v,verbose", "Enable verbose logging",
cxxopts::value<int>()->default_value("0")) //
cxxopts::value<int>()->default_value("0"))
("k,keepalive", "Client keepalive duration in seconds",
cxxopts::value<int>()) //
("logtostdout", "Write log to stdout") //
("silent", "Disable logging") //
("N,no-terminal", "Do not create a terminal") //
("f,forward-ssh-agent", "Forward ssh-agent socket") //
cxxopts::value<int>())
("logtostdout", "Write log to stdout")
("silent", "Disable logging")
("N,no-terminal", "Do not create a terminal")
("f,forward-ssh-agent", "Forward ssh-agent socket")
("ssh-socket", "The ssh-agent socket to forward",
cxxopts::value<std::string>()) //
cxxopts::value<std::string>())
("telemetry",
"Allow et to anonymously send errors to guide future improvements",
cxxopts::value<bool>()->default_value("true")) //
cxxopts::value<bool>()->default_value("true"))
("serverfifo",
"If set, communicate to etserver on the matching fifo name", //
cxxopts::value<std::string>()->default_value("")) //
"If set, communicate to etserver on the matching fifo name",
cxxopts::value<std::string>()->default_value(""))
("ssh-option", "Options to pass down to `ssh -o`",
cxxopts::value<std::vector<std::string>>());

options.parse_positional({"host", "positional"});
options.parse_positional({"host"});

auto result = options.parse(argc, argv);
if (result.count("help")) {
Expand Down Expand Up @@ -154,24 +159,43 @@ int main(int argc, char** argv) {

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
if (colon_count == 1) {
// ipv4 or hostname 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);
// maybe ipv6 (colon_count >= 2)
if(host_arg.find("::") != string::npos) {
// ipv6 with double colon zero abbreviation and no port
// leave host_arg as is
} else {
if (colon_count == 7) {
// ipv6, fully expanded, without port
} else if (colon_count == 8) {
// ipv6, fully expanded, with port
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 {
CLOG(INFO, "stdout") << "Invalid host positional arg: "
<< result["host"].as<std::string>() << endl;
exit(1);
}
}
}
// CLOG(INFO, "stdout") << "Invalid host positional arg: "
// << result["host"].as<std::string>() << endl;
// exit(1);
}
destinationHost = host_arg;
// host_alias is used for the initiating ssh call, if sshd runs on a port
// other than 22, either configure your .ssh/config with an alias with an
// overridden port or pass --ssh-option Port=<sshd_port>
string host_alias = destinationHost;

string jumphost =
result.count("jumphost") ? result["jumphost"].as<string>() : "";
string host_alias = destinationHost;
int keepaliveDuration = result.count("keepalive") ? result["keepalive"].as<int>() : MAX_CLIENT_KEEP_ALIVE_DURATION;
if (keepaliveDuration < 1 || keepaliveDuration > MAX_CLIENT_KEEP_ALIVE_DURATION) {
CLOG(INFO, "stdout") << "Keep-alive duration must between 1 and " << MAX_CLIENT_KEEP_ALIVE_DURATION << " seconds" << endl;
Expand Down

0 comments on commit 3feafb1

Please sign in to comment.