Skip to content

Commit cd19e9b

Browse files
Parse LocalForward from SSH config (#697)
* Parse LocalForward from SSH config * review
1 parent 581c48a commit cd19e9b

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

src/terminal/ParseConfigFile.hpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <fstream>
1010
#include <iostream>
1111
#include <string>
12+
#include <utility>
13+
#include <vector>
1214

1315
/* This is needed for a standard getpwuid_r on opensolaris */
1416
#define _POSIX_PTHREAD_SEMANTICS
@@ -84,6 +86,7 @@ enum ssh_config_opcode_e {
8486
SOC_PROXYJUMP,
8587
SOC_FORWARDAGENT,
8688
SOC_IDENTITYAGENT,
89+
SOC_LOCALFORWARD,
8790
SOC_END /* Keep this one last in the list */
8891
};
8992

@@ -121,7 +124,8 @@ enum ssh_options_e {
121124
SSH_OPTIONS_HMAC_S_C,
122125
SSH_OPTIONS_PROXYJUMP,
123126
SSH_OPTIONS_FORWARDAGENT,
124-
SSH_OPTIONS_IDENTITYAGENT
127+
SSH_OPTIONS_IDENTITYAGENT,
128+
SSH_OPTIONS_LOCALFORWARD
125129
};
126130

127131
struct Options {
@@ -141,6 +145,7 @@ struct Options {
141145
int gss_delegate_creds;
142146
int forward_agent;
143147
char *identity_agent;
148+
vector<pair<int, int>> local_forwards;
144149
};
145150

146151
struct ssh_config_keyword_table_s {
@@ -166,6 +171,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
166171
{"proxyjump", SOC_PROXYJUMP},
167172
{"forwardagent", SOC_FORWARDAGENT},
168173
{"identityagent", SOC_IDENTITYAGENT},
174+
{"localforward", SOC_LOCALFORWARD},
169175
{NULL, SOC_UNSUPPORTED}};
170176

171177
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
@@ -1036,6 +1042,36 @@ int ssh_options_set(struct Options *options, enum ssh_options_e type,
10361042
}
10371043
}
10381044
break;
1045+
case SSH_OPTIONS_LOCALFORWARD:
1046+
v = static_cast<const char *>(value);
1047+
if (v == NULL || v[0] == '\0') {
1048+
CLOG(INFO, "stdout") << "invalid error" << endl;
1049+
return -1;
1050+
} else {
1051+
char *forward_entry = strdup(v);
1052+
if (forward_entry == NULL) {
1053+
CLOG(INFO, "stdout") << "error" << endl;
1054+
return -1;
1055+
}
1056+
1057+
// Format is [bind_address:]port host:hostport
1058+
char *local_port_str = strtok(forward_entry, " ");
1059+
char *remote_part = strtok(NULL, " ");
1060+
1061+
// TODO: Support bind_address before the local port.
1062+
if (local_port_str && remote_part) {
1063+
int local_port = atoi(local_port_str);
1064+
char *colon_pos = strrchr(remote_part, ':');
1065+
if (colon_pos) {
1066+
int remote_port = atoi(colon_pos + 1);
1067+
options->local_forwards.push_back(
1068+
make_pair(local_port, remote_port));
1069+
}
1070+
}
1071+
1072+
SAFE_FREE(forward_entry);
1073+
}
1074+
break;
10391075

10401076
default:
10411077
CLOG(INFO, "stdout") << "Unknown ssh option" << endl;
@@ -1233,7 +1269,8 @@ static int ssh_config_parse_line(const char *targethost,
12331269

12341270
opcode = ssh_config_get_opcode(keyword);
12351271
if (*parsing == 1 && opcode != SOC_HOST && opcode != SOC_MATCH &&
1236-
opcode != SOC_UNSUPPORTED && opcode != SOC_INCLUDE) {
1272+
opcode != SOC_UNSUPPORTED && opcode != SOC_INCLUDE &&
1273+
opcode != SOC_LOCALFORWARD) {
12371274
if (seen[opcode] != 0) {
12381275
SAFE_FREE(x);
12391276
return 0;
@@ -1401,6 +1438,17 @@ static int ssh_config_parse_line(const char *targethost,
14011438
SAFE_FREE(filename);
14021439
}
14031440
break;
1441+
case SOC_LOCALFORWARD:
1442+
p = ssh_config_get_str_tok(&s, NULL);
1443+
if (p && *parsing) {
1444+
const char *remote_part = ssh_config_get_str_tok(&s, NULL);
1445+
if (remote_part) {
1446+
char forward_str[1024];
1447+
snprintf(forward_str, sizeof(forward_str), "%s %s", p, remote_part);
1448+
ssh_options_set(options, SSH_OPTIONS_LOCALFORWARD, forward_str);
1449+
}
1450+
}
1451+
break;
14041452
case SOC_UNSUPPORTED:
14051453
LOG(INFO) << "unsupported config line: " << string(line) << ", ignored";
14061454
break;

src/terminal/TerminalClient.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ TerminalClient::TerminalClient(
3030
auto pfsresponse =
3131
portForwardHandler->createSource(pfsr, nullptr, -1, -1);
3232
if (pfsresponse.has_error()) {
33-
throw std::runtime_error(pfsresponse.error());
33+
LOG(WARNING) << "Failed to establish port forward "
34+
<< pfsr.source().port() << ":"
35+
<< pfsr.destination().port() << " - "
36+
<< pfsresponse.error();
37+
continue;
3438
}
3539
#endif
3640
}

src/terminal/TerminalClientMain.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ int main(int argc, char** argv) {
7575
NULL, // gss_client_identity
7676
0, // gss_delegate_creds
7777
0, // forward_agent
78-
NULL // identity_agent
78+
NULL, // identity_agent
79+
{} // local_forwards (empty vector)
7980
};
8081

8182
// Parse command line arguments
@@ -380,6 +381,19 @@ int main(int argc, char** argv) {
380381
extractSingleOptionWithDefault<string>(result, options, "tunnel", "");
381382
string r_tunnel_arg = extractSingleOptionWithDefault<string>(
382383
result, options, "reversetunnel", "");
384+
385+
for (const auto& localForward : sshConfigOptions.local_forwards) {
386+
string tunnelEntry =
387+
to_string(localForward.first) + ":" + to_string(localForward.second);
388+
LOG(INFO) << "Adding tunnel from SSH config LocalForward: "
389+
<< tunnelEntry;
390+
if (tunnel_arg.empty()) {
391+
tunnel_arg = tunnelEntry;
392+
} else {
393+
tunnel_arg += "," + tunnelEntry;
394+
}
395+
}
396+
383397
TerminalClient terminalClient(clientSocket, clientPipeSocket,
384398
socketEndpoint, id, passkey, console,
385399
is_jumphost, tunnel_arg, r_tunnel_arg,

0 commit comments

Comments
 (0)