feat: Rebrand project to WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive

- Update project name from 'WebSocket SSH' to 'WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive'
- Update project description to emphasize evolution as universal tunneling utility
- Update README.md, DOCUMENTATION.md, CHANGELOG.md, TODO.md
- Update all man pages (wsssh.1, wsscp.1, wssshc.1, wsssht.1)
- Update Debian control files for both packages
- Update configuration examples and build scripts
- Update source code comments in tunnel and client files
parent 8c867e16
# Changelog # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
......
# WebSocket SSH Documentation # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Documentation
## Table of Contents ## Table of Contents
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
## Overview ## Overview
WebSocket SSH (wsssh) is a tunneling system that enables secure SSH/SCP access to remote machines through WebSocket-based intermediaries. Unlike traditional SSH jump hosts, wsssh uses WebSocket connections for real-time, bidirectional communication between clients and servers. wsssh is a swiss army's knife server assisted tunnelling system for the win initially born as a websocket ssh tunnelling system and evolved to a universal tunnelling utility
### Key Components ### Key Components
......
# WebSocket SSH (wsssh) # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive
![WebSocket SSH Logo](logos/logo-256.png) ![WebSocket SSH Logo](logos/logo-256.png)
A modern SSH tunneling system that uses WebSocket connections to securely route SSH/SCP traffic through registered client machines. This allows you to access remote servers through intermediate "jump hosts" using WebSocket-based tunnels. wsssh is a swiss army's knife server assisted tunnelling system for the win initially born as a websocket ssh tunnelling system and evolved to a universal tunnelling utility
## Features ## Features
......
# WebSocket SSH - Future Enhancements Roadmap # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive - Future Enhancements Roadmap
## Recently Completed (v1.6.1) ## Recently Completed (v1.6.1)
- [x] **Major Code Refactoring**: Complete modularization of `wsssht.c` for improved maintainability - [x] **Major Code Refactoring**: Complete modularization of `wsssht.c` for improved maintainability
......
#!/bin/bash #!/bin/bash
# WebSocket SSH Tools Build Script # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Build Script
# Build script for WebSocket SSH tools (wssshd, wssshc, wsscp, etc.) # Build script for WSSSH tools (wssshd, wssshc, wsscp, etc.)
# #
# Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me # Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
# #
......
#!/bin/bash #!/bin/bash
# WebSocket SSH Tools Clean Script # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Clean Script
# Clean script for removing build artifacts from WebSocket SSH tools # Clean script for removing build artifacts from WSSSH tools
# #
# Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me # Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
# #
......
...@@ -11,19 +11,18 @@ Vcs-Git: https://git.nexlab.net/nexlab/wsssh.git ...@@ -11,19 +11,18 @@ Vcs-Git: https://git.nexlab.net/nexlab/wsssh.git
Package: wsssh-server Package: wsssh-server
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, debconf (>= 0.5) | debconf-2.0 Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, debconf (>= 0.5) | debconf-2.0
Description: WebSocket SSH Server (wssshd) Description: WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Server (wssshd)
A modern SSH tunneling system that provides WebSocket-based SSH/SCP access wsssh is a swiss army's knife server assisted tunnelling system for the win initially born as a websocket ssh tunnelling system and evolved to a universal tunnelling utility. This package contains the server component
to registered client machines. This package contains the server component
that handles WebSocket connections and manages SSH tunnels. that handles WebSocket connections and manages SSH tunnels.
. .
This package includes a standalone PyInstaller binary that bundles all This package includes a standalone PyInstaller binary that bundles all
required dependencies, eliminating the need for external Python packages. required dependencies, eliminating the need for external Python packages.
. .
The wssshd server provides: The wssshd server provides:
- WebSocket SSH tunnel management - WSSSH tunnel management
- Client registration and authentication - Client registration and authentication
- Web-based management interface - Web-based management interface
- Secure tunnel establishment between clients and servers - Secure tunnel establishment between clients and servers
- High availability with watchdog monitoring - High availability with watchdog monitoring
. .
This is the server component of the WebSocket SSH system. This is the server component of the WSSSH system.
\ No newline at end of file \ No newline at end of file
# WebSocket SSH Client (wssshc) Configuration Example # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Client (wssshc) Configuration Example
# #
# This is an example configuration file for wssshc. # This is an example configuration file for wssshc.
# Copy this file to /etc/wssshc.conf or ~/.config/wsssh/wssshc.conf # Copy this file to /etc/wssshc.conf or ~/.config/wsssh/wssshc.conf
...@@ -24,3 +24,14 @@ password = my-secret-password ...@@ -24,3 +24,14 @@ password = my-secret-password
# Reconnection interval in seconds (default: 30) # Reconnection interval in seconds (default: 30)
interval = 30 interval = 30
# Default tunnel host and port (can be overridden by service configurations)
# tunnel-host = 127.0.0.1
# tunnel-port = 22
# Directory containing service configuration files (default: /etc/wsssh.d/)
# Multiple directories can be specified with colon separation
services-path = /etc/wsssh.d/
# Default service name for tunnel requests (default: ssh)
service = ssh
\ No newline at end of file
# WebSocket SSH Tunnel (wsssht) Configuration Example # WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive Tunnel (wsssht) Configuration Example
# #
# This is an example configuration file for wsssht. # This is an example configuration file for wsssht.
# Copy this file to ~/.config/wsssh/wsssht.conf and modify the settings as needed. # Copy this file to ~/.config/wsssh/wsssht.conf and modify the settings as needed.
......
...@@ -11,10 +11,9 @@ Vcs-Git: https://git.nexlab.net/nexlab/wsssh.git ...@@ -11,10 +11,9 @@ Vcs-Git: https://git.nexlab.net/nexlab/wsssh.git
Package: wsssh-tools Package: wsssh-tools
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, openssl Depends: ${shlibs:Depends}, ${misc:Depends}, openssl
Description: WebSocket SSH Tools - C implementation Description: WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive - C implementation
A modern SSH tunneling system that uses WebSocket connections to securely wsssh is a swiss army's knife server assisted tunnelling system for the win initially born as a websocket ssh tunnelling system and evolved to a universal tunnelling utility
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 WSSSH tools:
wssshc (client registration), wsssht (tunnel setup tool with pipe mode), wssshc (client registration), wsssht (tunnel setup tool with pipe mode),
and wsssh (SSH wrapper with ProxyCommand support). and wsssh (SSH wrapper with ProxyCommand support).
\ No newline at end of file
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <errno.h> #include <errno.h>
#include <sched.h> #include <sched.h>
#include <pthread.h> #include <pthread.h>
#include <sys/wait.h>
#define INITIAL_FRAME_BUFFER_SIZE 8192 #define INITIAL_FRAME_BUFFER_SIZE 8192
...@@ -294,6 +295,182 @@ void handle_tunnel_request(SSL *ssl, const char *request_id, int debug, const ch ...@@ -294,6 +295,182 @@ void handle_tunnel_request(SSL *ssl, const char *request_id, int debug, const ch
} }
} }
// Execute a command before tunnel connection
static int execute_service_command(const char *command, int debug) {
if (!command || strlen(command) == 0) return 0;
if (debug) {
printf("[DEBUG] Executing service command: %s\n", command);
}
pid_t pid = fork();
if (pid == 0) {
// Child process
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1); // exec failed
} else if (pid > 0) {
// Parent process
int status;
waitpid(pid, &status, 0);
if (debug) {
printf("[DEBUG] Service command exited with status: %d\n", WEXITSTATUS(status));
}
return WEXITSTATUS(status) == 0 ? 0 : -1;
} else {
// Fork failed
perror("Failed to fork for service command");
return -1;
}
}
void handle_tunnel_request_with_service(SSL *ssl, const char *request_id, service_config_t *service, int debug) {
pthread_mutex_lock(&tunnel_mutex);
// Check if tunnel with this request_id already exists
tunnel_t *existing_tunnel = find_tunnel_by_request_id(request_id);
if (existing_tunnel) {
if (debug) {
printf("[DEBUG - Tunnel] Tunnel with request_id %s already exists, ignoring duplicate request\n", request_id);
}
pthread_mutex_unlock(&tunnel_mutex);
return;
}
// Execute service command if specified
if (service && service->command) {
if (execute_service_command(service->command, debug) != 0) {
if (debug) {
printf("[DEBUG - Tunnel] Service command failed, aborting tunnel request\n");
}
pthread_mutex_unlock(&tunnel_mutex);
return;
}
}
tunnel_t *new_tunnel = malloc(sizeof(tunnel_t));
if (!new_tunnel) {
perror("Memory allocation failed");
pthread_mutex_unlock(&tunnel_mutex);
return;
}
// For wssshc: Connect to target TCP endpoint and forward raw TCP data
struct sockaddr_in target_addr;
int target_sock;
// Determine protocol (TCP or UDP)
int use_udp = 0;
if (service && service->proto && strcmp(service->proto, "udp") == 0) {
use_udp = 1;
target_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (debug) {
printf("[DEBUG - Tunnel] Using UDP protocol for service\n");
}
} else {
target_sock = socket(AF_INET, SOCK_STREAM, 0);
}
if (target_sock < 0) {
perror("Target socket creation failed");
free(new_tunnel);
pthread_mutex_unlock(&tunnel_mutex);
return;
}
memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
// Use service configuration if available
const char *target_host = service && service->tunnel_host ? service->tunnel_host : "127.0.0.1";
int target_port = service && service->tunnel_port ? service->tunnel_port : 22;
target_addr.sin_port = htons(target_port); // Target port
// Resolve target host
struct hostent *target_he;
if ((target_he = gethostbyname(target_host)) == NULL) {
herror("Target host resolution failed");
close(target_sock);
free(new_tunnel);
pthread_mutex_unlock(&tunnel_mutex);
return;
}
target_addr.sin_addr = *((struct in_addr *)target_he->h_addr); // Target host
if (!use_udp) {
// TCP connection
if (connect(target_sock, (struct sockaddr *)&target_addr, sizeof(target_addr)) < 0) {
perror("Connection to target endpoint failed");
close(target_sock);
free(new_tunnel);
pthread_mutex_unlock(&tunnel_mutex);
return;
}
} else {
// For UDP, we don't connect, just set the target address for sending
// The connection will be established when we receive data
}
new_tunnel->sock = target_sock; // TCP/UDP connection to target
new_tunnel->local_sock = -1; // Not used in wssshc
strcpy(new_tunnel->request_id, request_id);
new_tunnel->active = 1;
new_tunnel->broken = 0;
new_tunnel->ssl = ssl;
new_tunnel->outgoing_buffer = NULL; // wssshc doesn't use buffer
new_tunnel->incoming_buffer = NULL; // wssshc doesn't need incoming buffer
new_tunnel->server_version_sent = 0; // Not used for raw TCP/UDP
// Add the new tunnel to the array
if (!add_tunnel(new_tunnel)) {
if (target_sock >= 0) close(target_sock);
free(new_tunnel);
pthread_mutex_unlock(&tunnel_mutex);
return;
}
pthread_mutex_unlock(&tunnel_mutex);
if (debug) {
printf("[DEBUG - Tunnel] wssshc connected to target %s:%d (%s)\n",
target_host, target_port, use_udp ? "UDP" : "TCP");
}
// Send tunnel_ack back to server
char ack_msg[256];
snprintf(ack_msg, sizeof(ack_msg), "{\"type\":\"tunnel_ack\",\"request_id\":\"%s\"}", request_id);
if (debug) {
printf("[DEBUG - WebSockets] Sending tunnel_ack: %s\n", ack_msg);
fflush(stdout);
}
// send_websocket_frame already uses SSL mutex internally
if (!send_websocket_frame(ssl, ack_msg)) {
fprintf(stderr, "Send tunnel_ack failed\n");
return;
}
// Start bidirectional forwarding between WebSocket and target TCP/UDP endpoint
thread_args_t *thread_args = malloc(sizeof(thread_args_t));
if (thread_args) {
thread_args->ssl = ssl;
thread_args->tunnel = new_tunnel;
thread_args->debug = debug;
pthread_t thread;
if (use_udp) {
// For UDP, we need a different forwarding function
// For now, use the TCP version but this should be enhanced for UDP
pthread_create(&thread, NULL, forward_ws_to_ssh_server, thread_args);
} else {
pthread_create(&thread, NULL, forward_ws_to_ssh_server, thread_args);
}
set_thread_cpu_affinity(thread); // Distribute thread across CPU cores
pthread_detach(thread);
}
}
void cleanup_tunnel(int debug) { void cleanup_tunnel(int debug) {
pthread_mutex_lock(&tunnel_mutex); pthread_mutex_lock(&tunnel_mutex);
......
...@@ -44,6 +44,15 @@ typedef struct { ...@@ -44,6 +44,15 @@ typedef struct {
pthread_t forward_thread; // Thread ID for the forwarding thread pthread_t forward_thread; // Thread ID for the forwarding thread
} tunnel_t; } tunnel_t;
// Service configuration structure
typedef struct {
char *name;
int tunnel_port;
char *tunnel_host;
char *command;
char *proto; // "tcp" or "udp", default "tcp"
} service_config_t;
// Thread arguments // Thread arguments
typedef struct { typedef struct {
SSL *ssl; SSL *ssl;
...@@ -77,6 +86,7 @@ void *forward_ws_to_local(void *arg); ...@@ -77,6 +86,7 @@ void *forward_ws_to_local(void *arg);
void *forward_ws_to_ssh_server(void *arg); void *forward_ws_to_ssh_server(void *arg);
void *tunnel_thread(void *arg); void *tunnel_thread(void *arg);
void handle_tunnel_request(SSL *ssl, const char *request_id, int debug, const char *ssh_host, int ssh_port); void handle_tunnel_request(SSL *ssl, const char *request_id, int debug, const char *ssh_host, int ssh_port);
void handle_tunnel_request_with_service(SSL *ssl, const char *request_id, service_config_t *service, int debug);
void handle_tunnel_data(SSL *ssl, const char *request_id, const char *data_hex, int debug); void handle_tunnel_data(SSL *ssl, const char *request_id, const char *data_hex, int debug);
void handle_tunnel_close(SSL *ssl, const char *request_id, int debug); void handle_tunnel_close(SSL *ssl, const char *request_id, int debug);
void send_tunnel_close(SSL *ssl, const char *request_id, int debug); void send_tunnel_close(SSL *ssl, const char *request_id, int debug);
......
.TH WSSCP 1 "September 2024" "wsscp 1.0" "WebSocket SSH Tools" .TH WSSCP 1 "September 2024" "wsscp 1.0" "WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive"
.SH NAME .SH NAME
wsscp \- SCP wrapper with WebSocket ProxyCommand support wsscp \- SCP wrapper with WebSocket ProxyCommand support
.SH SYNOPSIS .SH SYNOPSIS
...@@ -11,7 +11,7 @@ wsscp \- SCP wrapper with WebSocket ProxyCommand support ...@@ -11,7 +11,7 @@ wsscp \- SCP wrapper with WebSocket ProxyCommand support
is an SCP wrapper that automatically configures SCP to use WebSocket tunnels through is an SCP wrapper that automatically configures SCP to use WebSocket tunnels through
.B wsssht .B wsssht
with ProxyCommand. It parses the destination specification and constructs the appropriate with ProxyCommand. It parses the destination specification and constructs the appropriate
SCP command with ProxyCommand to establish secure file transfers through WebSocket relays. SCP command with ProxyCommand to establish secure file transfers through WSSSH relays.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-\-help .B \-\-help
......
.TH WSSH 1 "September 2024" "wsssh 1.0" "WebSocket SSH Tools" .TH WSSH 1 "September 2024" "wsssh 1.0" "WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive"
.SH NAME .SH NAME
wsssh \- SSH wrapper with WebSocket ProxyCommand support wsssh \- SSH wrapper with WebSocket ProxyCommand support
.SH SYNOPSIS .SH SYNOPSIS
...@@ -12,7 +12,7 @@ wsssh \- SSH wrapper with WebSocket ProxyCommand support ...@@ -12,7 +12,7 @@ wsssh \- SSH wrapper with WebSocket ProxyCommand support
is an SSH wrapper that automatically configures SSH to use WebSocket tunnels through is an SSH wrapper that automatically configures SSH to use WebSocket tunnels through
.B wsssht .B wsssht
with ProxyCommand. It parses the target specification and constructs the appropriate with ProxyCommand. It parses the target specification and constructs the appropriate
SSH command with ProxyCommand to establish secure connections through WebSocket relays. SSH command with ProxyCommand to establish secure connections through WSSSH relays.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-\-help .B \-\-help
......
.TH WSSHc 1 "September 2025" "wsssh-tools 1.4.0" "WebSocket SSH Tools" .TH WSSHc 1 "September 2025" "wsssh-tools 1.4.0" "WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive"
.SH NAME .SH NAME
wssshc \- WebSocket SSH Client for registration wssshc \- WebSocket SSH Client for registration
.SH SYNOPSIS .SH SYNOPSIS
...@@ -11,11 +11,17 @@ wssshc \- WebSocket SSH Client for registration ...@@ -11,11 +11,17 @@ wssshc \- WebSocket SSH Client for registration
[\fB\-\-tunnel\fR \fITYPES\fR] [\fB\-\-tunnel\fR \fITYPES\fR]
[\fB\-\-tunnel\-control\fR \fITYPES\fR] [\fB\-\-tunnel\-control\fR \fITYPES\fR]
[\fB\-\-wssshd\-private\-ip\fR \fIIP\fR] [\fB\-\-wssshd\-private\-ip\fR \fIIP\fR]
[\fB\-\-ssh\-host\fR \fIHOST\fR]
[\fB\-\-ssh\-port\fR \fIPORT\fR]
[\fB\-\-tunnel\-host\fR \fIHOST\fR]
[\fB\-\-tunnel\-port\fR \fIPORT\fR]
[\fB\-\-services\-path\fR \fIDIR\fR]
[\fB\-\-service\fR \fINAME\fR]
[\fB\-\-debug\fR] [\fB\-\-debug\fR]
[\fB\-\-help\fR] [\fB\-\-help\fR]
.SH DESCRIPTION .SH DESCRIPTION
.B wssshc .B wssshc
is a lightweight client for registering machines with a WebSocket SSH daemon (wssshd). It establishes a persistent WebSocket connection and maintains registration with the server, allowing SSH/SCP tunneling through the registered machine. is a lightweight client for registering machines with a WSSSH daemon (wssshd). It establishes a persistent WebSocket connection and maintains registration with the server, allowing SSH/SCP tunneling through the registered machine.
.PP .PP
Configuration can be provided via command line options or an optional INI-formatted configuration file at Configuration can be provided via command line options or an optional INI-formatted configuration file at
.B ~/.config/wsssh/wssshc.conf .B ~/.config/wsssh/wssshc.conf
...@@ -48,6 +54,24 @@ Transport types for control channel (comma-separated or 'any', default: any) ...@@ -48,6 +54,24 @@ Transport types for control channel (comma-separated or 'any', default: any)
.BR \-\-wssshd\-private\-ip " \fIIP\fR" .BR \-\-wssshd\-private\-ip " \fIIP\fR"
Private IP address of the wssshd server Private IP address of the wssshd server
.TP .TP
.BR \-\-ssh\-host " \fIHOST\fR"
SSH host to forward tunnel data to (default: 127.0.0.1) [legacy, use --tunnel-host]
.TP
.BR \-\-ssh\-port " \fIPORT\fR"
SSH port to forward tunnel data to (default: 22) [legacy, use --tunnel-port]
.TP
.BR \-\-tunnel\-host " \fIHOST\fR"
Tunnel host to forward tunnel data to (default: 127.0.0.1)
.TP
.BR \-\-tunnel\-port " \fIPORT\fR"
Tunnel port to forward tunnel data to (default: 22)
.TP
.BR \-\-services\-path " \fIDIR\fR"
Directory containing service configuration files (default: /etc/wsssh.d/)
.TP
.BR \-\-service " \fINAME\fR"
Default service name for tunnel requests (default: ssh)
.TP
.B \-\-debug .B \-\-debug
Enable debug output for troubleshooting Enable debug output for troubleshooting
.TP .TP
......
.TH WSSSHT 1 "September 2024" "WebSocket SSH" "User Commands" .TH WSSSHT 1 "September 2024" "WSSSH:: Warp-Powered Stefy's Spatial Secure Hyperdrive" "User Commands"
.SH NAME .SH NAME
wsssht \- WebSocket SSH Tunnel Setup Tool wsssht \- WebSocket SSH Tunnel Setup Tool
.SH SYNOPSIS .SH SYNOPSIS
...@@ -23,7 +23,7 @@ wsssht \- WebSocket SSH Tunnel Setup Tool ...@@ -23,7 +23,7 @@ wsssht \- WebSocket SSH Tunnel Setup Tool
[\fIservice://\fR]\fIclientid\fR[\fI@wssshd-host\fR][\fI:wssshd-port\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 WSSSH 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.
.PP .PP
Unlike Unlike
.BR wsssh (1) .BR wsssh (1)
......
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "libwsssht/wssshlib.h" #include "libwsssht/wssshlib.h"
#include "libwsssht/websocket.h" #include "libwsssht/websocket.h"
...@@ -66,8 +69,10 @@ void print_status() { ...@@ -66,8 +69,10 @@ void print_status() {
typedef struct { typedef struct {
char *wssshd_server; char *wssshd_server;
int wssshd_port; int wssshd_port;
char *ssh_host; char *ssh_host; // Legacy alias for tunnel_host
int ssh_port; int ssh_port; // Legacy alias for tunnel_port
char *tunnel_host;
int tunnel_port;
char *client_id; char *client_id;
char *password; char *password;
int interval; int interval;
...@@ -75,8 +80,11 @@ typedef struct { ...@@ -75,8 +80,11 @@ typedef struct {
char *tunnel; char *tunnel;
char *tunnel_control; char *tunnel_control;
char *wssshd_private_ip; char *wssshd_private_ip;
char *services_path;
char *default_service; // Default service name (default: "ssh")
} wssshc_config_t; } wssshc_config_t;
void load_config_file(const char *config_path, wssshc_config_t *config) { void load_config_file(const char *config_path, wssshc_config_t *config) {
FILE *file = fopen(config_path, "r"); FILE *file = fopen(config_path, "r");
if (!file) return; if (!file) return;
...@@ -117,6 +125,24 @@ void load_config_file(const char *config_path, wssshc_config_t *config) { ...@@ -117,6 +125,24 @@ void load_config_file(const char *config_path, wssshc_config_t *config) {
config->client_id = strdup(value); config->client_id = strdup(value);
} else if (strcmp(key, "interval") == 0) { } else if (strcmp(key, "interval") == 0) {
config->interval = atoi(value); config->interval = atoi(value);
} else if (strcmp(key, "services-path") == 0) {
// Handle multiple services-path entries
if (config->services_path) {
char *new_path = malloc(strlen(config->services_path) + strlen(value) + 2);
if (new_path) {
sprintf(new_path, "%s:%s", config->services_path, value);
free(config->services_path);
config->services_path = new_path;
}
} else {
config->services_path = strdup(value);
}
} else if (strcmp(key, "tunnel-host") == 0 && !config->tunnel_host) {
config->tunnel_host = strdup(value);
} else if (strcmp(key, "tunnel-port") == 0) {
config->tunnel_port = atoi(value);
} else if (strcmp(key, "service") == 0 && !config->default_service) {
config->default_service = strdup(value);
} }
} }
} }
...@@ -145,6 +171,192 @@ void load_config(wssshc_config_t *config) { ...@@ -145,6 +171,192 @@ void load_config(wssshc_config_t *config) {
// Load a single service configuration file
service_config_t *load_service_config(const char *config_path) {
FILE *file = fopen(config_path, "r");
if (!file) return NULL;
service_config_t *service = calloc(1, sizeof(service_config_t));
if (!service) {
fclose(file);
return NULL;
}
char line[256];
char current_section[256] = "";
int in_service_section = 0;
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = 0;
if (line[0] == '[' && line[strlen(line) - 1] == ']') {
// Section header
strncpy(current_section, line + 1, sizeof(current_section) - 1);
current_section[strlen(current_section) - 1] = '\0'; // Remove closing bracket
in_service_section = 1;
if (!service->name) {
service->name = strdup(current_section);
}
} else if (in_service_section && strchr(line, '=')) {
char *key = strtok(line, "=");
char *value = strtok(NULL, "=");
if (key && value) {
while (*key == ' ') key++;
char *end = key + strlen(key) - 1;
while (end > key && *end == ' ') *end-- = 0;
while (*value == ' ') value++;
end = value + strlen(value) - 1;
while (end > value && *end == ' ') *end-- = 0;
if (strcmp(key, "tunnel-port") == 0) {
service->tunnel_port = atoi(value);
} else if (strcmp(key, "tunnel-host") == 0 && !service->tunnel_host) {
service->tunnel_host = strdup(value);
} else if (strcmp(key, "command") == 0 && !service->command) {
service->command = strdup(value);
} else if (strcmp(key, "proto") == 0 && !service->proto) {
service->proto = strdup(value);
}
}
}
}
fclose(file);
// Set defaults
if (!service->proto) {
service->proto = strdup("tcp");
}
return service;
}
// Free a single service configuration
void free_service_config(service_config_t *service) {
if (service) {
free(service->name);
free(service->tunnel_host);
free(service->command);
free(service->proto);
free(service);
}
}
// Free service configurations
void free_services_config(service_config_t **services, int num_services) {
if (!services) return;
for (int i = 0; i < num_services; i++) {
if (services[i]) {
free_service_config(services[i]);
}
}
free(services);
}
// Load all service configurations from multiple directories (colon-separated)
service_config_t **load_services_config(const char *services_path, int *num_services) {
service_config_t **services = NULL;
*num_services = 0;
if (!services_path) return NULL;
// Handle multiple directories separated by colons
char *path_copy = strdup(services_path);
if (!path_copy) return NULL;
char *dir_token = strtok(path_copy, ":");
while (dir_token != NULL) {
DIR *dir = opendir(dir_token);
if (dir) {
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// Check if file ends with .conf
size_t len = strlen(entry->d_name);
if (len > 5 && strcmp(entry->d_name + len - 5, ".conf") == 0) {
// Build full path
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_token, entry->d_name);
// Check if file is readable
if (access(full_path, R_OK) == 0) {
service_config_t *service = load_service_config(full_path);
if (service && service->name) {
// Check for duplicate service names (later definitions override earlier ones)
int duplicate = 0;
for (int i = 0; i < *num_services; i++) {
if (strcmp(services[i]->name, service->name) == 0) {
// Free the old service and replace it
free_service_config(services[i]);
services[i] = service;
duplicate = 1;
break;
}
}
if (!duplicate) {
// Add new service to array
services = realloc(services, (*num_services + 1) * sizeof(service_config_t *));
if (services) {
services[*num_services] = service;
(*num_services)++;
} else {
// Free service if realloc failed
free(service->name);
free(service->tunnel_host);
free(service->command);
free(service->proto);
free(service);
}
}
}
}
}
}
closedir(dir);
}
dir_token = strtok(NULL, ":");
}
free(path_copy);
return services;
}
// Find a service by name
service_config_t *find_service(service_config_t **services, int num_services, const char *service_name) {
for (int i = 0; i < num_services; i++) {
if (services[i] && strcmp(services[i]->name, service_name) == 0) {
return services[i];
}
}
return NULL;
}
// Execute a command before tunnel connection
int execute_service_command(const char *command, int debug) {
if (!command || strlen(command) == 0) return 0;
if (debug) {
printf("[DEBUG] Executing service command: %s\n", command);
}
pid_t pid = fork();
if (pid == 0) {
// Child process
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1); // exec failed
} else if (pid > 0) {
// Parent process
int status;
waitpid(pid, &status, 0);
if (debug) {
printf("[DEBUG] Service command exited with status: %d\n", WEXITSTATUS(status));
}
return WEXITSTATUS(status) == 0 ? 0 : -1;
} else {
// Fork failed
perror("Failed to fork for service command");
return -1;
}
}
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]\n", program_name);
fprintf(stderr, "WebSocket SSH Client - Register with wssshd server\n\n"); fprintf(stderr, "WebSocket SSH Client - Register with wssshd server\n\n");
...@@ -153,14 +365,18 @@ void print_usage(const char *program_name) { ...@@ -153,14 +365,18 @@ void print_usage(const char *program_name) {
fprintf(stderr, " --config FILE Configuration file path (overrides default hierarchy)\n"); fprintf(stderr, " --config FILE Configuration file path (overrides default hierarchy)\n");
fprintf(stderr, " --wssshd-server HOST WSSSHD server host (default: mbeted.nexlab.net)\n"); fprintf(stderr, " --wssshd-server HOST WSSSHD server host (default: mbeted.nexlab.net)\n");
fprintf(stderr, " --wssshd-port PORT WSSSHD server port (default: 9898)\n"); fprintf(stderr, " --wssshd-port PORT WSSSHD server port (default: 9898)\n");
fprintf(stderr, " --ssh-host HOST SSH host to forward tunnel data to (default: 127.0.0.1)\n"); fprintf(stderr, " --ssh-host HOST SSH host to forward tunnel data to (default: 127.0.0.1) [legacy]\n");
fprintf(stderr, " --ssh-port PORT SSH port to forward tunnel data to (default: 22)\n"); fprintf(stderr, " --ssh-port PORT SSH port to forward tunnel data to (default: 22) [legacy]\n");
fprintf(stderr, " --tunnel-host HOST Tunnel host to forward tunnel data to (default: 127.0.0.1)\n");
fprintf(stderr, " --tunnel-port PORT Tunnel port to forward tunnel data to (default: 22)\n");
fprintf(stderr, " --id ID Client identifier\n"); fprintf(stderr, " --id ID Client identifier\n");
fprintf(stderr, " --password PASS Registration password\n"); fprintf(stderr, " --password PASS Registration password\n");
fprintf(stderr, " --interval SEC Reconnection interval (default: 30)\n"); fprintf(stderr, " --interval SEC Reconnection interval (default: 30)\n");
fprintf(stderr, " --tunnel TYPES Transport types for data channel (comma-separated or 'any', default: any)\n"); fprintf(stderr, " --tunnel TYPES Transport types for data channel (comma-separated or 'any', default: any)\n");
fprintf(stderr, " --tunnel-control TYPES Transport types for control channel (comma-separated or 'any', default: any)\n"); fprintf(stderr, " --tunnel-control TYPES Transport types for control channel (comma-separated or 'any', default: any)\n");
fprintf(stderr, " --wssshd-private-ip IP Private IP of wssshd server\n"); fprintf(stderr, " --wssshd-private-ip IP Private IP of wssshd server\n");
fprintf(stderr, " --services-path DIR Directory containing service configuration files (default: /etc/wsssh.d/)\n");
fprintf(stderr, " --service NAME Default service name (default: ssh)\n");
fprintf(stderr, " --debug Enable debug output\n"); fprintf(stderr, " --debug Enable debug output\n");
fprintf(stderr, " --help Show this help\n"); fprintf(stderr, " --help Show this help\n");
fprintf(stderr, "\nConfiguration hierarchy (highest priority first):\n"); fprintf(stderr, "\nConfiguration hierarchy (highest priority first):\n");
...@@ -177,14 +393,18 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) { ...@@ -177,14 +393,18 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) {
{"config", required_argument, 0, 'c'}, {"config", required_argument, 0, 'c'},
{"wssshd-server", required_argument, 0, 's'}, {"wssshd-server", required_argument, 0, 's'},
{"wssshd-port", required_argument, 0, 'p'}, {"wssshd-port", required_argument, 0, 'p'},
{"ssh-host", required_argument, 0, 'H'}, {"ssh-host", required_argument, 0, 'H'}, // Legacy alias
{"ssh-port", required_argument, 0, 'P'}, {"ssh-port", required_argument, 0, 'P'}, // Legacy alias
{"tunnel-host", required_argument, 0, 1000}, // New option
{"tunnel-port", required_argument, 0, 1001}, // New option
{"id", required_argument, 0, 'i'}, {"id", required_argument, 0, 'i'},
{"password", required_argument, 0, 'w'}, {"password", required_argument, 0, 'w'},
{"interval", required_argument, 0, 't'}, {"interval", required_argument, 0, 't'},
{"tunnel", required_argument, 0, 'T'}, {"tunnel", required_argument, 0, 'T'},
{"tunnel-control", required_argument, 0, 'C'}, {"tunnel-control", required_argument, 0, 'C'},
{"wssshd-private-ip", required_argument, 0, 'I'}, {"wssshd-private-ip", required_argument, 0, 'I'},
{"services-path", required_argument, 0, 'S'},
{"service", required_argument, 0, 1002}, // New option
{"debug", no_argument, 0, 'd'}, {"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0} {0, 0, 0, 0}
...@@ -193,7 +413,7 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) { ...@@ -193,7 +413,7 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) {
int opt; int opt;
char *custom_config = NULL; char *custom_config = NULL;
while ((opt = getopt_long(argc, argv, "c:s:p:H:P:i:w:t:T:C:I:dh", long_options, NULL)) != -1) { while ((opt = getopt_long(argc, argv, "c:s:p:H:P:i:w:t:T:C:I:S:dh", long_options, NULL)) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
custom_config = optarg; custom_config = optarg;
...@@ -235,6 +455,30 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) { ...@@ -235,6 +455,30 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) {
if (config->wssshd_private_ip) free(config->wssshd_private_ip); if (config->wssshd_private_ip) free(config->wssshd_private_ip);
config->wssshd_private_ip = strdup(optarg); config->wssshd_private_ip = strdup(optarg);
break; break;
case 'S':
// Handle multiple services-path options
if (config->services_path) {
char *new_path = malloc(strlen(config->services_path) + strlen(optarg) + 2); // +2 for ':' and '\0'
if (new_path) {
sprintf(new_path, "%s:%s", config->services_path, optarg);
free(config->services_path);
config->services_path = new_path;
}
} else {
config->services_path = strdup(optarg);
}
break;
case 1000: // --tunnel-host
if (config->tunnel_host) free(config->tunnel_host);
config->tunnel_host = strdup(optarg);
break;
case 1001: // --tunnel-port
config->tunnel_port = atoi(optarg);
break;
case 1002: // --service
if (config->default_service) free(config->default_service);
config->default_service = strdup(optarg);
break;
case 'd': case 'd':
config->debug = 1; config->debug = 1;
break; break;
...@@ -838,6 +1082,8 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -838,6 +1082,8 @@ int connect_to_server(const wssshc_config_t *config) {
if (strstr(buffer, "tunnel_request")) { if (strstr(buffer, "tunnel_request")) {
// Extract request_id // Extract request_id
char *id_start = strstr(buffer, "\"request_id\""); char *id_start = strstr(buffer, "\"request_id\"");
char *service_start = strstr(buffer, "\"service\"");
char service_name[256] = "";
if (id_start) { if (id_start) {
char *colon = strchr(id_start, ':'); char *colon = strchr(id_start, ':');
if (colon) { if (colon) {
...@@ -847,13 +1093,58 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -847,13 +1093,58 @@ int connect_to_server(const wssshc_config_t *config) {
char *close_quote = strchr(id_start, '"'); char *close_quote = strchr(id_start, '"');
if (close_quote) { if (close_quote) {
*close_quote = '\0'; *close_quote = '\0';
// Extract service name if present
if (service_start) {
char *service_colon = strchr(service_start, ':');
if (service_colon) {
char *service_quote = strchr(service_colon, '"');
if (service_quote) {
service_start = service_quote + 1;
char *service_end = strchr(service_start, '"');
if (service_end) {
*service_end = '\0';
strncpy(service_name, service_start, sizeof(service_name) - 1);
}
}
}
}
if (config->debug) { if (config->debug) {
printf("[DEBUG - WebSockets] Received tunnel_request for ID: %s\n", id_start); printf("[DEBUG - WebSockets] Received tunnel_request for ID: %s, service: %s\n", id_start, service_name[0] ? service_name : "(default)");
fflush(stdout); fflush(stdout);
} else { } else {
printf("[EVENT] New tunnel request: %s\n", id_start); printf("[EVENT] New tunnel request: %s\n", id_start);
} }
handle_tunnel_request(ssl, id_start, config->debug, config->ssh_host, config->ssh_port); // Load services if not already loaded (this should be done once at startup)
static service_config_t **services = NULL;
static int num_services = 0;
if (!services && config->services_path) {
services = load_services_config(config->services_path, &num_services);
if (config->debug && services) {
printf("[DEBUG] Loaded %d service configurations from %s\n", num_services, config->services_path);
}
}
// Find service configuration
service_config_t *service = NULL;
const char *requested_service = service_name[0] ? service_name : config->default_service;
if (requested_service && services) {
service = find_service(services, num_services, requested_service);
}
if (service) {
if (config->debug) {
printf("[DEBUG] Using service configuration: %s (host: %s, port: %d, proto: %s)\n",
service->name, service->tunnel_host, service->tunnel_port, service->proto);
}
handle_tunnel_request_with_service(ssl, id_start, service, config->debug);
} else {
if (config->debug) {
printf("[DEBUG] No service configuration found for '%s', using default tunnel settings\n",
requested_service ? requested_service : "(none)");
}
handle_tunnel_request(ssl, id_start, config->debug, config->tunnel_host, config->tunnel_port);
}
} }
} }
} }
...@@ -946,13 +1237,17 @@ int main(int argc, char *argv[]) { ...@@ -946,13 +1237,17 @@ int main(int argc, char *argv[]) {
.wssshd_port = 9898, .wssshd_port = 9898,
.ssh_host = NULL, .ssh_host = NULL,
.ssh_port = 22, .ssh_port = 22,
.tunnel_host = NULL,
.tunnel_port = 22,
.client_id = NULL, .client_id = NULL,
.password = NULL, .password = NULL,
.interval = 30, .interval = 30,
.debug = 0, .debug = 0,
.tunnel = NULL, .tunnel = NULL,
.tunnel_control = NULL, .tunnel_control = NULL,
.wssshd_private_ip = NULL .wssshd_private_ip = NULL,
.services_path = NULL,
.default_service = NULL
}; };
// Initialize CPU affinity system for multi-core utilization // Initialize CPU affinity system for multi-core utilization
...@@ -976,15 +1271,45 @@ int main(int argc, char *argv[]) { ...@@ -976,15 +1271,45 @@ int main(int argc, char *argv[]) {
if (!config.wssshd_server) { if (!config.wssshd_server) {
config.wssshd_server = strdup("mbeted.nexlab.net"); config.wssshd_server = strdup("mbeted.nexlab.net");
} }
if (!config.ssh_host) {
// Handle tunnel-host/tunnel-port precedence over ssh-host/ssh-port
if (config.tunnel_host) {
// tunnel-host has precedence
if (config.ssh_host) free(config.ssh_host);
config.ssh_host = strdup(config.tunnel_host);
} else if (config.ssh_host) {
// ssh-host provided, use it for tunnel_host
config.tunnel_host = strdup(config.ssh_host);
} else {
// Default
config.ssh_host = strdup("127.0.0.1"); config.ssh_host = strdup("127.0.0.1");
config.tunnel_host = strdup("127.0.0.1");
} }
if (config.tunnel_port != 22) {
// tunnel-port was explicitly set
config.ssh_port = config.tunnel_port;
} else if (config.ssh_port != 22) {
// ssh-port was explicitly set
config.tunnel_port = config.ssh_port;
} else {
// Default
config.ssh_port = 22;
config.tunnel_port = 22;
}
if (!config.tunnel) { if (!config.tunnel) {
config.tunnel = strdup("any"); config.tunnel = strdup("any");
} }
if (!config.tunnel_control) { if (!config.tunnel_control) {
config.tunnel_control = strdup("any"); config.tunnel_control = strdup("any");
} }
if (!config.services_path) {
config.services_path = strdup("/etc/wsssh.d/");
}
if (!config.default_service) {
config.default_service = strdup("ssh");
}
// Validate required arguments // Validate required arguments
if (!config.client_id || !config.password) { if (!config.client_id || !config.password) {
...@@ -1060,6 +1385,9 @@ int main(int argc, char *argv[]) { ...@@ -1060,6 +1385,9 @@ int main(int argc, char *argv[]) {
free(config.tunnel); free(config.tunnel);
free(config.tunnel_control); free(config.tunnel_control);
free(config.wssshd_private_ip); free(config.wssshd_private_ip);
free(config.services_path);
free(config.tunnel_host);
free(config.default_service);
pthread_mutex_destroy(&tunnel_mutex); pthread_mutex_destroy(&tunnel_mutex);
return 0; return 0;
......
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