Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ipv6 abbreviated address support #539

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
93 changes: 57 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>()) //
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all these trailing // as I'm not sure why there are there. It looks like they are supposed to be \ like when you extend a shell command in a script but they are not necessary in this context.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are to avoid clang-format from putting multiple options per line

("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,40 @@ 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);
}
}
}
}
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