feat: Implement peer-to-peer tunneling architecture with transport selection

- Add --tunnel, --tunnel-control, --service options to wsssh and wsscp
- Implement transport definitions with is_relay property and weight-based selection
- Add WebSocket transport with is_relay=true as primary transport
- Update server-side tunnel handling for new transport attributes
- Enhance configuration system with new tunneling options
- Update all man pages with comprehensive tunneling documentation
- Fix PyInstaller template loading for frozen executables
- Add transport list expansion for 'any' option functionality
- Implement connection retry logic with weight-based prioritization
- Update CHANGELOG.md, TODO.md, README.md, and DOCUMENTATION.md
parent 25f2f3cb
......@@ -66,6 +66,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed transport negotiation between clients and server
- Resolved issues with transport selection and fallback logic
- **PyInstaller Template Loading**: Fixed Flask template loading in frozen executables
- Added `sys` import to `wsssd/web.py` for frozen executable detection
- Implemented proper template and static folder configuration for PyInstaller bundles
- Added `app.template_folder` and `app.static_folder` configuration for frozen executables
- Fixed template loading issues when running wssshd as a standalone executable
- Ensured web interface works correctly in both development and production environments
### Documentation
- **Man Pages**: Updated all manual pages with new tunneling options
- Enhanced `wsssh.1` with `--tunnel`, `--tunnel-control`, and `--service` options
......
......@@ -15,6 +15,12 @@
- Modified client-side C implementations with new command-line options
- Updated all man pages with comprehensive tunneling option documentation
- Fixed critical transport list expansion for "any" option functionality
- [x] **PyInstaller Template Loading Fix**: Fixed Flask template loading in frozen executables
- Added missing `sys` import to `wsssd/web.py` for frozen executable detection
- Implemented proper template and static folder configuration for PyInstaller bundles
- Added `app.template_folder` and `app.static_folder` configuration for frozen executables
- Fixed template loading issues when running wssshd as a standalone executable
- Ensured web interface works correctly in both development and production environments
## Recently Completed (v1.4.9)
- [x] **PyInstaller Build Issues**: Critical fixes for frozen application deployment
......
......@@ -3,6 +3,7 @@ Flask web interface for wssshd
"""
import os
import sys
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
......@@ -13,6 +14,16 @@ from .terminal import create_terminal_session, send_terminal_data, get_terminal_
# Flask app
app = Flask(__name__)
# Handle template and static folders for frozen executables
if getattr(sys, 'frozen', False):
# Running as bundled executable
bundle_dir = sys._MEIPASS
template_dir = os.path.join(bundle_dir, 'templates')
static_dir = os.path.join(bundle_dir, 'static')
app.template_folder = template_dir
app.static_folder = static_dir
app.config['SECRET_KEY'] = 'wsssh-secret-key-change-in-production'
config_dir = os.path.expanduser('~/.config/wssshd')
os.makedirs(config_dir, exist_ok=True)
......
......@@ -930,13 +930,19 @@ int reconnect_websocket(tunnel_t *tunnel, const char *wssshd_host, int wssshd_po
char *expanded_tunnel = expand_transport_list("any", 0); // Data channel transports
char *expanded_tunnel_control = expand_transport_list("any", 1); // Control channel transports
// Select best transport based on weight (lowest weight = highest priority)
char *best_tunnel = select_best_transport(expanded_tunnel);
char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
snprintf(tunnel_request_msg, sizeof(tunnel_request_msg),
"{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"ssh\"}",
client_id, request_id, expanded_tunnel, expanded_tunnel_control);
client_id, request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control);
if (!send_websocket_frame(ssl, tunnel_request_msg)) {
free(expanded_tunnel);
free(expanded_tunnel_control);
if (best_tunnel) free(best_tunnel);
if (best_tunnel_control) free(best_tunnel_control);
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
......@@ -945,6 +951,8 @@ int reconnect_websocket(tunnel_t *tunnel, const char *wssshd_host, int wssshd_po
free(expanded_tunnel);
free(expanded_tunnel_control);
if (best_tunnel) free(best_tunnel);
if (best_tunnel_control) free(best_tunnel_control);
// Read acknowledgment
bytes_read = SSL_read(ssl, buffer, sizeof(buffer));
......@@ -1080,13 +1088,19 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
char *expanded_tunnel = expand_transport_list("any", 0); // Data channel transports
char *expanded_tunnel_control = expand_transport_list("any", 1); // Control channel transports
// Select best transport based on weight (lowest weight = highest priority)
char *best_tunnel = select_best_transport(expanded_tunnel);
char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
snprintf(tunnel_request_msg, sizeof(tunnel_request_msg),
"{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"ssh\"}",
client_id, request_id, expanded_tunnel, expanded_tunnel_control);
client_id, request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control);
if (!send_websocket_frame(ssl, tunnel_request_msg)) {
free(expanded_tunnel);
free(expanded_tunnel_control);
if (best_tunnel) free(best_tunnel);
if (best_tunnel_control) free(best_tunnel_control);
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
......@@ -1095,6 +1109,8 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
free(expanded_tunnel);
free(expanded_tunnel_control);
if (best_tunnel) free(best_tunnel);
if (best_tunnel_control) free(best_tunnel_control);
if (debug) {
printf("[DEBUG] Tunnel request sent for client: %s, request_id: %s\n", client_id, request_id);
......
......@@ -388,20 +388,26 @@ int connect_to_server(const wssshc_config_t *config) {
char *expanded_tunnel = expand_transport_list(config->tunnel, 0); // 0 for data channel
char *expanded_tunnel_control = expand_transport_list(config->tunnel_control, 1); // 1 for control channel
// Select best transport based on weight (lowest weight = highest priority)
char *best_tunnel = select_best_transport(expanded_tunnel);
char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
// Send registration message
if (config->debug) {
printf("[DEBUG] Sending registration message...\n");
fflush(stdout);
}
int reg_result = send_registration_message(ssl, config->client_id, config->password, expanded_tunnel, expanded_tunnel_control, config->wssshd_private_ip);
int reg_result = send_registration_message(ssl, config->client_id, config->password, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config->wssshd_private_ip);
// Free expanded transport lists
// Free expanded transport lists and best transport selections
if (expanded_tunnel != config->tunnel) {
free(expanded_tunnel);
}
if (expanded_tunnel_control != config->tunnel_control) {
free(expanded_tunnel_control);
}
if (best_tunnel) free(best_tunnel);
if (best_tunnel_control) free(best_tunnel_control);
if (!reg_result) {
fprintf(stderr, "Failed to send registration message\n");
SSL_free(ssl);
......
......@@ -29,6 +29,7 @@
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
// Global signal flag
volatile sig_atomic_t sigint_received = 0;
......@@ -254,6 +255,54 @@ char *expand_transport_list(const char *transport_spec, int for_relay) {
return strdup(transport_spec);
}
// Get the weight of a transport (lower number = higher priority)
int get_transport_weight(const char *transport) {
if (strcmp(transport, "websocket") == 0) {
return WEBSOCKET_WEIGHT;
}
// Default weight for unknown transports
return 999;
}
// Select the transport with the lowest weight (highest priority) from a comma-separated list
char *select_best_transport(const char *transport_list) {
if (!transport_list) {
return NULL;
}
char *list_copy = strdup(transport_list);
if (!list_copy) {
return NULL;
}
char *best_transport = NULL;
int best_weight = INT_MAX;
char *token = strtok(list_copy, ",");
while (token) {
// Trim whitespace
while (*token == ' ' || *token == '\t') token++;
char *end = token + strlen(token) - 1;
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
int weight = get_transport_weight(token);
if (weight < best_weight) {
best_weight = weight;
best_transport = token;
}
token = strtok(NULL, ",");
}
char *result = NULL;
if (best_transport) {
result = strdup(best_transport);
}
free(list_copy);
return result;
}
char *autodetect_local_ip() {
struct ifaddrs *ifaddr, *ifa;
char *selected_ip = NULL;
......
......@@ -83,9 +83,14 @@ typedef struct {
#define SUPPORTED_TRANSPORTS "websocket"
#define RELAY_TRANSPORTS "websocket"
// Transport weights (lower number = higher priority, 0 = highest priority)
#define WEBSOCKET_WEIGHT 100
// Function declarations
char *read_config_value(const char *key);
char *expand_transport_list(const char *transport_spec, int for_relay);
char *select_best_transport(const char *transport_list);
int get_transport_weight(const char *transport);
void print_trans_flag(void);
void print_palestinian_flag(void);
int find_available_port(void);
......
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