Add new wsssh application - SSH wrapper with ProxyCommand support

- Create wsssh.h header file with configuration structures and function declarations
- Implement wsssh.c with comprehensive SSH wrapper functionality:
  * Parse command line arguments (--help, --clientid, --wssshd-host, --wssshd-port, --debug, --tunnel, --tunnel-control)
  * Parse target string in format: user[@clientid[.wssshd-host[:sshstring]]]
  * Build ProxyCommand using wsssht --pipe with appropriate options
  * Find wsssht in PATH or same directory as wsssh
  * Construct and execute SSH command with ProxyCommand
  * Debug mode shows constructed command without executing
- Add wsssh to build system (configure.sh and Makefile)
- Update debian/control to include wsssh in package description
- Create comprehensive man page (man/wsssh.1) with usage examples
- Tested functionality with various command line options

wsssh provides a convenient wrapper around SSH that automatically sets up
WebSocket tunneling through wsssht, making it easy to use SSH with WebSocket
relays without manual ProxyCommand configuration.
parent 66a58d82
...@@ -58,12 +58,12 @@ LDFLAGS = $(shell pkg-config --libs openssl) ...@@ -58,12 +58,12 @@ LDFLAGS = $(shell pkg-config --libs openssl)
# Source files # Source files
LIB_SRCS = libwsssht/wssshlib.c libwsssht/websocket.c libwsssht/wssh_ssl.c libwsssht/tunnel.c libwsssht/utils.c libwsssht/modes.c libwsssht/threads.c LIB_SRCS = libwsssht/wssshlib.c libwsssht/websocket.c libwsssht/wssh_ssl.c libwsssht/tunnel.c libwsssht/utils.c libwsssht/modes.c libwsssht/threads.c
LIB_OBJS = $(LIB_SRCS:.c=.o) LIB_OBJS = $(LIB_SRCS:.c=.o)
SRCS = wssshc.c wsssht.c SRCS = wssshc.c wsssht.c wsssh.c
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)
TARGETS = wssshc wsssht TARGETS = wssshc wsssht wsssh
# Man pages # Man pages
MANPAGES = man/wssshc.1 man/wsssht.1 MANPAGES = man/wssshc.1 man/wsssht.1 man/wsssh.1
# Default target # Default target
all: $(TARGETS) all: $(TARGETS)
...@@ -75,6 +75,9 @@ wssshc: wssshc.o libwsssht/wssshlib.o libwsssht/websocket.o libwsssht/wssh_ssl.o ...@@ -75,6 +75,9 @@ wssshc: wssshc.o libwsssht/wssshlib.o libwsssht/websocket.o libwsssht/wssh_ssl.o
wsssht: wsssht.o $(LIB_OBJS) wsssht: wsssht.o $(LIB_OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
wsssh: wsssh.o
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# Object files # Object files
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
...@@ -103,12 +106,16 @@ uninstall: ...@@ -103,12 +106,16 @@ uninstall:
# Remove from both possible locations # Remove from both possible locations
rm -f $(DESTDIR)/usr/local/bin/wssshc rm -f $(DESTDIR)/usr/local/bin/wssshc
rm -f $(DESTDIR)/usr/local/bin/wsssht rm -f $(DESTDIR)/usr/local/bin/wsssht
rm -f $(DESTDIR)/usr/local/bin/wsssh
rm -f $(DESTDIR)/usr/local/share/man/man1/wssshc.1 rm -f $(DESTDIR)/usr/local/share/man/man1/wssshc.1
rm -f $(DESTDIR)/usr/local/share/man/man1/wsssht.1 rm -f $(DESTDIR)/usr/local/share/man/man1/wsssht.1
rm -f $(DESTDIR)/usr/local/share/man/man1/wsssh.1
rm -f $(DESTDIR)/usr/bin/wssshc rm -f $(DESTDIR)/usr/bin/wssshc
rm -f $(DESTDIR)/usr/bin/wsssht rm -f $(DESTDIR)/usr/bin/wsssht
rm -f $(DESTDIR)/usr/bin/wsssh
rm -f $(DESTDIR)/usr/share/man/man1/wssshc.1 rm -f $(DESTDIR)/usr/share/man/man1/wssshc.1
rm -f $(DESTDIR)/usr/share/man/man1/wsssht.1 rm -f $(DESTDIR)/usr/share/man/man1/wsssht.1
rm -f $(DESTDIR)/usr/share/man/man1/wsssh.1
.PHONY: all clean install uninstall .PHONY: all clean install uninstall
EOF EOF
......
...@@ -16,4 +16,5 @@ Description: WebSocket SSH Tools - C implementation ...@@ -16,4 +16,5 @@ Description: WebSocket SSH Tools - C implementation
route SSH/SCP traffic through registered client machines. route SSH/SCP traffic through registered client machines.
. .
This package contains the C implementation of the WebSocket SSH tools: This package contains the C implementation of the WebSocket SSH tools:
wssshc (client registration) and wsssht (tunnel setup tool with pipe mode). wssshc (client registration), wsssht (tunnel setup tool with pipe mode),
\ No newline at end of file and wsssh (SSH wrapper with ProxyCommand support).
\ No newline at end of file
.TH WSSH 1 "September 2024" "wsssh 1.0" "WebSocket SSH Tools"
.SH NAME
wsssh \- SSH wrapper with WebSocket ProxyCommand support
.SH SYNOPSIS
.B wsssh
[\fB\-\-help\fR] [\fB\-\-clientid\fR \fIclient_id\fR] [\fB\-\-wssshd\-host\fR \fIhost\fR]
[\fB\-\-wssshd\-port\fR \fIport\fR] [\fB\-\-debug\fR] [\fB\-\-tunnel\fR \fItransport\fR]
[\fB\-\-tunnel\-control\fR \fItransport\fR] [\fIuser\fR][\fB@\fR[\fIclientid\fR][\fB.\fR[\fIwssshd\-host\fR]][\fB:\fR[\fIsshstring\fR]]]
[\fIssh_options\fR...]
.SH DESCRIPTION
.B wsssh
is an SSH wrapper that automatically configures SSH to use WebSocket tunnels through
.B wsssht
with ProxyCommand. It parses the target specification and constructs the appropriate
SSH command with ProxyCommand to establish secure connections through WebSocket relays.
.SH OPTIONS
.TP
.B \-\-help
Show help message and exit.
.TP
.B \-\-clientid \fIclient_id\fR
Specify the client ID of the registered wssshc endpoint.
.TP
.B \-\-wssshd\-host \fIhost\fR
Specify the wssshd relay host.
.TP
.B \-\-wssshd\-port \fIport\fR
Specify the wssshd relay websocket port (default: 9898).
.TP
.B \-\-debug
Enable debug output. When debug is enabled, the SSH command is displayed but not executed.
.TP
.B \-\-tunnel \fItransport\fR
Select data channel transport (comma-separated or 'any').
.TP
.B \-\-tunnel\-control \fItransport\fR
Select control channel transport (comma-separated or 'any').
.SH TARGET FORMAT
The target specification follows the format:
.sp
\fIuser\fR[\fB@\fR[\fIclientid\fR][\fB.\fR[\fIwssshd\-host\fR]][\fB:\fR[\fIsshstring\fR]]]
.sp
Where:
.RS
.TP
\fIuser\fR
SSH username
.TP
\fIclientid\fR
Client ID of the registered wssshc endpoint
.TP
\fIwssshd\-host\fR
wssshd relay hostname
.TP
\fIsshstring\fR
Additional SSH connection string (e.g., port number)
.RE
.SH EXAMPLES
.TP
Connect to myclient:
.B wsssh user@myclient
.TP
Connect with specific relay host:
.B wsssh user@myclient.server.com
.TP
Connect with custom port:
.B wsssh user@myclient.server.com:2222
.TP
Enable debug output:
.B wsssh \-\-debug user@myclient.server.com
.TP
Specify transport:
.B wsssh \-\-tunnel websocket user@myclient.server.com
.TP
Pass additional SSH options:
.B wsssh user@myclient.server.com -p 2222 -o StrictHostKeyChecking=no
.SH ENVIRONMENT
.B wsssh
requires
.B wsssht
to be available either in PATH or in the same directory as wsssh.
.SH SEE ALSO
.BR wsssht (1),
.BR wssshc (1),
.BR ssh (1)
.SH AUTHOR
Written by Stefy Lanza <stefy@nexlab.net> and SexHack.me
.SH COPYRIGHT
Copyright \(co 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
.br
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
.SH BUGS
Report bugs to <stefy@nexlab.net>
\ No newline at end of file
/*
* WebSocket SSH (wsssh) - SSH Wrapper with WebSocket ProxyCommand
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "wsssh.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <limits.h>
#include <libgen.h>
void print_wsssh_usage(const char *program_name) {
fprintf(stderr, "Usage: %s [options] <user>[@clientid[.wssshd-host[:sshstring]]] [ssh_options...]\n", program_name);
fprintf(stderr, "WebSocket SSH Wrapper - SSH through WebSocket tunnels\n\n");
fprintf(stderr, "Protect the dolls!\n\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " --help Show this help\n");
fprintf(stderr, " --clientid ID Client ID of the registered wssshc endpoint\n");
fprintf(stderr, " --wssshd-host HOST wssshd relay host\n");
fprintf(stderr, " --wssshd-port PORT wssshd relay websocket port (default: 9898)\n");
fprintf(stderr, " --debug Enable debug output\n");
fprintf(stderr, " --tunnel TRANSPORT Select data channel transport (comma-separated or 'any')\n");
fprintf(stderr, " --tunnel-control TRANSPORT Select control channel transport (comma-separated or 'any')\n");
fprintf(stderr, "\nTarget format:\n");
fprintf(stderr, " user[@clientid[.wssshd-host[:sshstring]]]\n");
fprintf(stderr, "\nExamples:\n");
fprintf(stderr, " %s user@myclient\n", program_name);
fprintf(stderr, " %s user@myclient.server.com\n", program_name);
fprintf(stderr, " %s --debug user@myclient.server.com:22\n", program_name);
fprintf(stderr, " %s --tunnel websocket user@myclient.server.com -p 2222\n", program_name);
fprintf(stderr, "\nDonations:\n");
fprintf(stderr, " BTC: bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx\n");
fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n");
}
int parse_wsssh_args(int argc, char *argv[], wsssh_config_t *config) {
// Initialize config with defaults
memset(config, 0, sizeof(wsssh_config_t));
config->wssshd_port = 9898;
// Parse options
int remaining_start = 1;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
print_wsssh_usage(argv[0]);
return 0;
} else if (strcmp(argv[i], "--clientid") == 0 && i + 1 < argc) {
config->client_id = strdup(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "--wssshd-host") == 0 && i + 1 < argc) {
config->wssshd_host = strdup(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "--wssshd-port") == 0 && i + 1 < argc) {
config->wssshd_port = atoi(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "--debug") == 0) {
config->debug = 1;
} else if (strcmp(argv[i], "--tunnel") == 0 && i + 1 < argc) {
config->tunnel = strdup(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "--tunnel-control") == 0 && i + 1 < argc) {
config->tunnel_control = strdup(argv[i + 1]);
i++;
} else if (argv[i][0] == '-') {
// Unknown option, treat as SSH option
remaining_start = i;
break;
} else {
// First non-option argument is the target
remaining_start = i;
break;
}
}
// Store remaining arguments
config->remaining_argc = argc - remaining_start;
if (config->remaining_argc > 0) {
config->remaining_argv = &argv[remaining_start];
}
return 1;
}
int parse_target_string(const char *target, wsssh_config_t *config) {
if (!target) return 0;
char *target_copy = strdup(target);
if (!target_copy) return 0;
// Parse user@host format
char *at_pos = strchr(target_copy, '@');
if (at_pos) {
*at_pos = '\0';
config->user = strdup(target_copy);
// Parse the part after @
char *host_part = at_pos + 1;
// Check for :port
char *colon_pos = strchr(host_part, ':');
if (colon_pos) {
*colon_pos = '\0';
config->ssh_string = strdup(colon_pos + 1);
}
// Check for .domain
char *dot_pos = strchr(host_part, '.');
if (dot_pos) {
*dot_pos = '\0';
config->client_id = strdup(host_part);
config->wssshd_host = strdup(dot_pos + 1);
} else {
config->client_id = strdup(host_part);
}
} else {
// No @, just user/host
config->user = strdup(target_copy);
}
free(target_copy);
return 1;
}
char *find_wsssht_path() {
// First check if wsssht is in PATH
if (system("which wsssht > /dev/null 2>&1") == 0) {
return strdup("wsssht");
}
// If not in PATH, check in the same directory as wsssh
char wsssh_path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", wsssh_path, sizeof(wsssh_path) - 1);
if (len != -1) {
wsssh_path[len] = '\0';
char *dir_end = strrchr(wsssh_path, '/');
if (dir_end) {
*dir_end = '\0';
char wsssht_full_path[PATH_MAX];
snprintf(wsssht_full_path, sizeof(wsssht_full_path), "%s/wsssht", wsssh_path);
if (access(wsssht_full_path, X_OK) == 0) {
return strdup(wsssht_full_path);
}
}
}
return NULL; // wsssht not found
}
char *build_proxy_command(wsssh_config_t *config) {
char *wsssht_path = find_wsssht_path();
if (!wsssht_path) {
fprintf(stderr, "Error: wsssht not found in PATH or in the same directory as wsssh\n");
fprintf(stderr, "Please install wsssht to use wsssh\n");
return NULL;
}
char *cmd = malloc(2048);
if (!cmd) {
free(wsssht_path);
return NULL;
}
// Start with wsssht path --pipe
sprintf(cmd, "%s --pipe", wsssht_path);
free(wsssht_path);
// Add debug if specified
if (config->debug) {
strcat(cmd, " --debug");
}
// Add tunnel if specified
if (config->tunnel) {
strcat(cmd, " --tunnel ");
strcat(cmd, config->tunnel);
}
// Add tunnel-control if specified
if (config->tunnel_control) {
strcat(cmd, " --tunnel-control ");
strcat(cmd, config->tunnel_control);
}
// Add wssshd-port if not default
if (config->wssshd_port != 9898) {
char port_str[32];
sprintf(port_str, " --wssshd-port %d", config->wssshd_port);
strcat(cmd, port_str);
}
// Add client_id
if (config->client_id) {
strcat(cmd, " ssh://");
strcat(cmd, config->client_id);
// Add wssshd-host if specified
if (config->wssshd_host) {
strcat(cmd, "@");
strcat(cmd, config->wssshd_host);
}
}
return cmd;
}
char *build_ssh_command(wsssh_config_t *config, const char *proxy_command) {
if (!config->user || !config->client_id) {
return NULL;
}
char *cmd = malloc(4096);
if (!cmd) return NULL;
// Start with ssh -o ProxyCommand="..."
sprintf(cmd, "ssh -o ProxyCommand=\"%s\"", proxy_command);
// Add user@client_id
strcat(cmd, " ");
strcat(cmd, config->user);
strcat(cmd, "@");
strcat(cmd, config->client_id);
// Add ssh_string if specified
if (config->ssh_string) {
strcat(cmd, ":");
strcat(cmd, config->ssh_string);
}
// Add remaining arguments
for (int i = 1; i < config->remaining_argc; i++) {
strcat(cmd, " ");
// Escape quotes in arguments
if (strchr(config->remaining_argv[i], '"')) {
strcat(cmd, "'");
strcat(cmd, config->remaining_argv[i]);
strcat(cmd, "'");
} else {
strcat(cmd, config->remaining_argv[i]);
}
}
return cmd;
}
int execute_ssh_command(const char *ssh_command, int debug) {
if (!ssh_command) return 1;
if (debug) {
printf("[DEBUG] SSH command: %s\n", ssh_command);
} else {
printf("Executing: %s\n", ssh_command);
}
// Execute the command
int result = system(ssh_command);
if (result == -1) {
perror("system");
return 1;
}
return WEXITSTATUS(result);
}
int main(int argc, char *argv[]) {
wsssh_config_t config;
// Parse arguments
if (!parse_wsssh_args(argc, argv, &config)) {
return 0; // Help was printed
}
// Need at least one argument (the target)
if (config.remaining_argc == 0) {
fprintf(stderr, "Error: No target specified\n");
print_wsssh_usage(argv[0]);
return 1;
}
// Parse the target string
if (!parse_target_string(config.remaining_argv[0], &config)) {
fprintf(stderr, "Error: Invalid target format\n");
return 1;
}
// Build proxy command
char *proxy_command = build_proxy_command(&config);
if (!proxy_command) {
fprintf(stderr, "Error: Failed to build proxy command\n");
return 1;
}
// Build SSH command
char *ssh_command = build_ssh_command(&config, proxy_command);
if (!ssh_command) {
fprintf(stderr, "Error: Failed to build SSH command\n");
free(proxy_command);
return 1;
}
// Execute SSH command
int result;
if (config.debug) {
printf("[DEBUG] Would execute: %s\n", ssh_command);
result = 0; // Success for debug mode
} else {
result = execute_ssh_command(ssh_command, config.debug);
}
// Cleanup
free(proxy_command);
free(ssh_command);
free(config.client_id);
free(config.wssshd_host);
free(config.tunnel);
free(config.tunnel_control);
free(config.user);
free(config.ssh_string);
return result;
}
\ No newline at end of file
/*
* WebSocket SSH (wsssh) - SSH Wrapper with WebSocket ProxyCommand
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef WSSH_H
#define WSSH_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
// Configuration structure for wsssh
typedef struct {
char *client_id;
char *wssshd_host;
int wssshd_port;
int debug;
char *tunnel;
char *tunnel_control;
char *user;
char *target_host;
char *ssh_string;
int remaining_argc;
char **remaining_argv;
} wsssh_config_t;
// Function declarations
void print_wsssh_usage(const char *program_name);
int parse_wsssh_args(int argc, char *argv[], wsssh_config_t *config);
int parse_target_string(const char *target, wsssh_config_t *config);
char *build_proxy_command(wsssh_config_t *config);
char *build_ssh_command(wsssh_config_t *config, const char *proxy_command);
int execute_ssh_command(const char *ssh_command, int debug);
#endif // WSSH_H
\ No newline at end of file
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