Add flexible connection string support to wsssht

- Support connection string format: [service://]clientid[@wssshd-host][:wssshd-port]
- Command line options take precedence over connection string values
- Update usage message and man page with new syntax
- Add comprehensive documentation for connection string format
- Maintain backward compatibility with existing option-based usage
parent c0a6998c
...@@ -10,10 +10,11 @@ wsssht \- WebSocket SSH Tunnel Setup Tool ...@@ -10,10 +10,11 @@ wsssht \- WebSocket SSH Tunnel Setup Tool
[\fB\-\-wssshd\-port\fR \fIPORT\fR] [\fB\-\-wssshd\-port\fR \fIPORT\fR]
[\fB\-\-interval\fR \fISEC\fR] [\fB\-\-interval\fR \fISEC\fR]
[\fB\-\-debug\fR] [\fB\-\-debug\fR]
[\fB\-\-tunnel\fR \fITYPES\fR] [\fB\-\-tunnel\fR \fITRANSPORT\fR]
[\fB\-\-tunnel\-control\fR \fITYPES\fR] [\fB\-\-tunnel\-control\fR \fITYPES\fR]
[\fB\-\-service\fR \fISERVICE\fR] [\fB\-\-service\fR \fISERVICE\fR]
[\fB\-\-help\fR] [\fB\-\-help\fR]
[\fIservice://\fR]\fIclientid\fR[\fI@wssshd-host\fR][\fI:wssshd-port\fR]
.SH DESCRIPTION .SH DESCRIPTION
.B wsssht .B wsssht
is a WebSocket SSH tunnel setup tool that establishes secure tunnels through WebSocket connections without automatically executing SSH/SCP commands. It provides connection information for manual use with any TCP client. is a WebSocket SSH tunnel setup tool that establishes secure tunnels through WebSocket connections without automatically executing SSH/SCP commands. It provides connection information for manual use with any TCP client.
...@@ -58,19 +59,41 @@ Service type (default: ssh) ...@@ -58,19 +59,41 @@ Service type (default: ssh)
.TP .TP
.BR \-\-help .BR \-\-help
Display help message and exit Display help message and exit
.SH CONNECTION STRING FORMAT
The optional connection string argument supports the following format:
.RS
.IP \fB[service://]clientid[@wssshd-host][:wssshd-port]\fR
.RE
.PP
Where:
.RS
.IP \fBservice\fR
Optional service prefix (e.g., ssh://, rdp://)
.IP \fBclientid\fR
Required client identifier
.IP \fBwssshd-host\fR
Optional wssshd server hostname
.IP \fBwssshd-port\fR
Optional wssshd server port
.RE
.PP
Command line options take precedence over values parsed from the connection string.
.SH EXAMPLES .SH EXAMPLES
.TP .TP
Basic tunnel setup: Basic tunnel setup with options:
.B wsssht \-\-clientid myclient \-\-wssshd\-host mbetter.nexlab.net \-\-wssshd\-port 9898 .B wsssht \-\-clientid myclient \-\-wssshd\-host mbetter.nexlab.net
.TP
Connection string format:
.B wsssht myclient@mbetter.nexlab.net:9898
.TP .TP
Specify local port: With service prefix:
.B wsssht \-\-tunnel\-port 2222 \-\-clientid myclient \-\-wssshd\-host mbetter.nexlab.net .B wsssht ssh://myclient@mbetter.nexlab.net
.TP .TP
Use specific transport: Mixed options and connection string:
.B wsssht \-\-tunnel websocket \-\-clientid myclient \-\-wssshd\-host mbetter.nexlab.net .B wsssht \-\-tunnel websocket myclient@mbetter.nexlab.net
.TP .TP
Debug mode: Debug mode:
.B wsssht \-\-debug \-\-clientid myclient \-\-wssshd\-host mbetter.nexlab.net .B wsssht \-\-debug myclient@mbetter.nexlab.net
.SH MANUAL CONNECTION .SH MANUAL CONNECTION
Once the tunnel is established, Once the tunnel is established,
.B wsssht .B wsssht
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
void print_usage(const char *program_name) { void print_usage(const char *program_name) {
fprintf(stderr, "Usage: %s [options]\n", program_name); fprintf(stderr, "Usage: %s [options] [service://]clientid[@wssshd-host][:wssshd-port]\n", program_name);
fprintf(stderr, "WebSocket SSH Tunnel - Setup WebSocket tunnels for manual connections\n\n"); fprintf(stderr, "WebSocket SSH Tunnel - Setup WebSocket tunnels for manual connections\n\n");
fprintf(stderr, "Protect the dolls!\n\n"); fprintf(stderr, "Protect the dolls!\n\n");
fprintf(stderr, "Options:\n"); fprintf(stderr, "Options:\n");
...@@ -57,16 +57,62 @@ void print_usage(const char *program_name) { ...@@ -57,16 +57,62 @@ void print_usage(const char *program_name) {
fprintf(stderr, " --service SERVICE Service type (default: ssh)\n"); fprintf(stderr, " --service SERVICE Service type (default: ssh)\n");
fprintf(stderr, " --help Show this help\n"); fprintf(stderr, " --help Show this help\n");
fprintf(stderr, "\nExamples:\n"); fprintf(stderr, "\nExamples:\n");
fprintf(stderr, " %s --clientid myclient --wssshd-host mbetter.nexlab.net --wssshd-port 9898\n", program_name); fprintf(stderr, " %s --clientid myclient --wssshd-host mbetter.nexlab.net\n", program_name);
fprintf(stderr, " %s --tunnel-port 2222 --clientid myclient --wssshd-host mbetter.nexlab.net\n", program_name); fprintf(stderr, " %s myclient@mbetter.nexlab.net:9898\n", program_name);
fprintf(stderr, " %s --tunnel websocket --debug --clientid myclient --wssshd-host mbetter.nexlab.net\n", program_name); fprintf(stderr, " %s ssh://myclient@mbetter.nexlab.net\n", program_name);
fprintf(stderr, " %s --tunnel websocket myclient@mbetter.nexlab.net\n", program_name);
fprintf(stderr, "\nDonations:\n"); fprintf(stderr, "\nDonations:\n");
fprintf(stderr, " BTC: bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx\n"); fprintf(stderr, " BTC: bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx\n");
fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n"); fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n");
} }
int parse_connection_string(const char *conn_str, char **service, char **client_id, char **wssshd_host, int *wssshd_port) {
char *str = strdup(conn_str);
if (!str) return 0;
// Check for service prefix (e.g., "ssh://client@host:port")
char *service_end = strstr(str, "://");
if (service_end) {
*service_end = '\0';
*service = strdup(str);
str = service_end + 3;
}
// Find @ separator for client@host
char *at_pos = strchr(str, '@');
if (!at_pos) {
// No @ found, treat whole string as client_id
*client_id = strdup(str);
free(str);
return 1;
}
// Split client_id and host part
*at_pos = '\0';
*client_id = strdup(str);
// Parse host and port
char *host_part = at_pos + 1;
char *colon_pos = strchr(host_part, ':');
if (colon_pos) {
// Has port
*colon_pos = '\0';
*wssshd_host = strdup(host_part);
*wssshd_port = atoi(colon_pos + 1);
} else {
// No port
*wssshd_host = strdup(host_part);
}
free(str);
return 1;
}
int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_argc, char ***remaining_argv) { int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_argc, char ***remaining_argv) {
// Parse wsssht options - no positional arguments expected // Parse wsssht options and optional connection string
int target_start = -1;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--clientid") == 0 && i + 1 < argc) { if (strcmp(argv[i], "--clientid") == 0 && i + 1 < argc) {
if (config->client_id) free(config->client_id); if (config->client_id) free(config->client_id);
...@@ -106,17 +152,71 @@ int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_ar ...@@ -106,17 +152,71 @@ int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_ar
} else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
print_usage(argv[0]); print_usage(argv[0]);
return 0; return 0;
} else { } else if (argv[i][0] == '-') {
// Unknown option or unexpected argument // Unknown option
fprintf(stderr, "Error: Unknown option or unexpected argument: %s\n", argv[i]); fprintf(stderr, "Error: Unknown option: %s\n", argv[i]);
print_usage(argv[0]); print_usage(argv[0]);
return 0; return 0;
} else {
// Non-option argument - should be connection string
if (target_start == -1) {
target_start = i;
} else {
// Multiple non-option arguments not allowed
fprintf(stderr, "Error: Multiple connection strings not allowed\n");
print_usage(argv[0]);
return 0;
}
}
}
// Handle connection string if present
if (target_start != -1) {
char *service = NULL;
char *client_id = NULL;
char *wssshd_host = NULL;
int wssshd_port = 9898;
if (!parse_connection_string(argv[target_start], &service, &client_id, &wssshd_host, &wssshd_port)) {
fprintf(stderr, "Error: Invalid connection string format\n");
free(service);
free(client_id);
free(wssshd_host);
return 0;
}
// Set values only if not already set by command line options
if (!config->service && service) {
config->service = service;
} else {
free(service);
}
if (!config->client_id && client_id) {
config->client_id = client_id;
} else {
free(client_id);
}
if (!config->wssshd_host && wssshd_host) {
config->wssshd_host = wssshd_host;
} else {
free(wssshd_host);
}
if (config->wssshd_port == 9898 && wssshd_port != 9898) {
config->wssshd_port = wssshd_port;
} }
} }
// No remaining arguments expected // Return remaining arguments (should be none)
*remaining_argc = 0; if (target_start != -1) {
*remaining_argv = NULL; *remaining_argc = argc - target_start - 1;
*remaining_argv = &argv[target_start + 1];
} else {
*remaining_argc = 0;
*remaining_argv = NULL;
}
return 1; return 1;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment