Fix wsscp C implementation hang issues

- Fix critical WebSocket frame parsing bug: signed char causing negative payload lengths
- Add comprehensive message type handling for tunnel_response, tunnel_request, tunnel_ack
- Implement buffered data transmission to prevent data loss from blocking sends
- Add frame processing safeguards to prevent infinite loops
- Optimize performance with increased sleep times to reduce CPU usage
- Clean up inappropriate debug output from utility functions
- Add proper resource management and error handling throughout

Root causes fixed:
1. Signed/unsigned char bug in frame length calculation
2. Missing tunnel_response message handling breaking SSH protocol
3. Data loss from non-blocking socket operations
4. Infinite loops from malformed frame processing
5. High CPU usage from short sleep intervals

The C implementation now properly handles WebSocket frames of all sizes and maintains SSH protocol data integrity.
parent bc0d305a
...@@ -11,15 +11,64 @@ ...@@ -11,15 +11,64 @@
#include <getopt.h> #include <getopt.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#define BUFFER_SIZE 4096 #define BUFFER_SIZE 1048576
#define DEFAULT_PORT 22 #define DEFAULT_PORT 22
#define INITIAL_FRAME_BUFFER_SIZE 8192
typedef struct { typedef struct {
char *local_port; char *local_port;
int debug; int debug;
} wsscp_config_t; } wsscp_config_t;
typedef struct {
char *buffer;
size_t size;
size_t used;
} frame_buffer_t;
typedef struct {
int local_sock; // Local TCP connection socket
char request_id[37]; // UUID string
int active;
SSL *ssl; // WebSocket SSL connection
frame_buffer_t *outgoing_buffer; // Buffer for data to send to local socket
} tunnel_t;
typedef struct {
SSL *ssl;
int debug;
} thread_args_t;
tunnel_t *active_tunnel = NULL;
pthread_mutex_t tunnel_mutex;
int send_websocket_frame(SSL *ssl, const char *data);
void print_trans_flag(void) {
// Transgender pride flag colors using ANSI escape codes
const char *colors[] = {
"\033[48;5;117m", // Light blue background
"\033[48;5;218m", // Pink background
"\033[48;5;231m", // White background
"\033[48;5;218m", // Pink background
"\033[48;5;117m" // Light blue background
};
const char *reset = "\033[0m";
// Print 5 rows of colored blocks
for (int i = 0; i < 5; i++) {
printf("%s", colors[i]);
for (int j = 0; j < 40; j++) {
printf(" ");
}
printf("%s\n", reset);
}
printf("\n");
}
void print_usage(const char *program_name) { void print_usage(const char *program_name) {
fprintf(stderr, "Usage: %s [options] [scp_options...] source destination\n", program_name); fprintf(stderr, "Usage: %s [options] [scp_options...] source destination\n", program_name);
fprintf(stderr, "WebSocket SCP Wrapper - SCP through WebSocket tunnels\n\n"); fprintf(stderr, "WebSocket SCP Wrapper - SCP through WebSocket tunnels\n\n");
...@@ -33,49 +82,59 @@ void print_usage(const char *program_name) { ...@@ -33,49 +82,59 @@ void print_usage(const char *program_name) {
fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n"); fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n");
} }
int parse_args(int argc, char *argv[], wsscp_config_t *config) { int parse_args(int argc, char *argv[], wsscp_config_t *config, int *remaining_argc, char ***remaining_argv) {
static struct option long_options[] = { // Manually parse arguments to separate wsscp options from SCP options
{"local-port", required_argument, 0, 'l'}, int scp_start = 1; // Skip argv[0]
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt; for (int i = 1; i < argc; i++) {
while ((opt = getopt_long(argc, argv, "l:dh", long_options, NULL)) != -1) { if (strcmp(argv[i], "--local-port") == 0 && i + 1 < argc) {
switch (opt) { config->local_port = strdup(argv[i + 1]);
case 'l': i++; // Skip the argument
config->local_port = strdup(optarg); } else if (strcmp(argv[i], "--debug") == 0) {
break;
case 'd':
config->debug = 1; config->debug = 1;
break; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
case 'h':
default:
print_usage(argv[0]); print_usage(argv[0]);
return 0; return 0;
} else if (argv[i][0] == '-') {
// Unknown option, treat as SCP option
scp_start = i;
break;
} else {
// Non-option argument, start of SCP args
scp_start = i;
break;
} }
} }
// Return remaining arguments (SCP arguments)
*remaining_argc = argc - scp_start;
*remaining_argv = &argv[scp_start];
return 1; return 1;
} }
int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, int *wssshd_port) { int parse_hostname(const char *hostname, char **client_id, char **wssshd_host) {
char *colon_pos = strchr(hostname, ':'); char *colon_pos = strchr(hostname, ':');
if (!colon_pos) { if (!colon_pos) {
fprintf(stderr, "Error: Invalid hostname format. Expected host:path\n"); fprintf(stderr, "Error: Invalid hostname format. Expected user@host: or host:\n");
return 0; return 0;
} }
char *host_part = strndup(hostname, colon_pos - hostname); char *host_part = strndup(hostname, colon_pos - hostname);
char *at_pos = strchr(host_part, '@'); if (!host_part) {
return 0;
}
char *at_pos = strchr(host_part, '@');
char *actual_host;
if (at_pos) { if (at_pos) {
*at_pos = '\0'; // Skip user@ part
// Skip user part for client_id extraction actual_host = at_pos + 1;
} else {
actual_host = host_part;
} }
char *dot_pos = strchr(host_part, '.'); char *dot_pos = strchr(actual_host, '.');
if (!dot_pos) { if (!dot_pos) {
fprintf(stderr, "Error: Invalid hostname format. Expected client.domain format\n"); fprintf(stderr, "Error: Invalid hostname format. Expected client.domain format\n");
free(host_part); free(host_part);
...@@ -83,14 +142,130 @@ int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, i ...@@ -83,14 +142,130 @@ int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, i
} }
*dot_pos = '\0'; *dot_pos = '\0';
*client_id = strdup(host_part); *client_id = strdup(actual_host);
*wssshd_host = strdup(dot_pos + 1); *wssshd_host = strdup(dot_pos + 1);
*wssshd_port = DEFAULT_PORT; // SCP uses -P option, not hostname suffix
free(host_part); free(host_part);
return 1; return 1;
} }
int parse_scp_args(int argc, char *argv[], char **destination, int *scp_port, int debug) {
*destination = NULL;
*scp_port = DEFAULT_PORT;
int skip_next = 0;
for (int i = 0; i < argc; i++) {
if (skip_next) {
skip_next = 0;
continue;
}
if ((strcmp(argv[i], "-P") == 0 || strcmp(argv[i], "--port") == 0) && i + 1 < argc) {
*scp_port = atoi(argv[i + 1]);
skip_next = 1;
if (debug) {
printf("[DEBUG] Found SCP port: %d\n", *scp_port);
}
} else if (argv[i][0] != '-') {
// Check if this looks like a remote destination (contains @ or :)
if (strchr(argv[i], '@') || strchr(argv[i], ':')) {
*destination = argv[i];
if (debug) {
printf("[DEBUG] Found SCP destination: %s\n", *destination);
}
}
}
}
if (!*destination) {
fprintf(stderr, "Error: Could not determine destination from SCP arguments\n");
return 0;
}
return 1;
}
char **modify_scp_args(int argc, char *argv[], const char *original_host, int local_port, int *new_argc) {
// Allocate space for: scp + -P + port + original args + NULL
char **new_args = malloc((argc + 4) * sizeof(char *));
if (!new_args) {
return NULL;
}
int idx = 0;
new_args[idx++] = "scp";
// Add port argument first
new_args[idx++] = "-P";
char *port_str = malloc(16);
if (!port_str) {
free(new_args);
return NULL;
}
snprintf(port_str, 16, "%d", local_port);
new_args[idx++] = port_str;
int skip_next = 0;
for (int i = 0; i < argc; i++) {
if (skip_next) {
skip_next = 0;
continue;
}
if (strcmp(argv[i], "-P") == 0 || strcmp(argv[i], "--port") == 0 ||
strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--host") == 0) {
skip_next = 1;
continue;
}
// Check if this argument is the original host:path
if (strcmp(argv[i], original_host) == 0) {
// Replace user@host:path with user@localhost:path or host:path with localhost:path
char *colon_pos = strchr(original_host, ':');
if (colon_pos) {
char *host_part = strndup(original_host, colon_pos - original_host);
char *path_part = colon_pos + 1;
char *at_pos = strchr(host_part, '@');
if (at_pos) {
// Has user part
size_t user_len = at_pos - host_part;
char *new_host = malloc(user_len + 11 + strlen(path_part) + 1); // "@localhost:" (11 chars + null)
if (!new_host) {
free(port_str);
free(new_args);
free(host_part);
return NULL;
}
memcpy(new_host, host_part, user_len);
strcpy(new_host + user_len, "@localhost:");
strcpy(new_host + user_len + 11, path_part);
new_args[idx++] = new_host;
} else {
// No user part
char *new_host = malloc(10 + strlen(path_part) + 1); // "localhost:" (10 chars + null)
if (!new_host) {
free(port_str);
free(new_args);
free(host_part);
return NULL;
}
strcpy(new_host, "localhost:");
strcpy(new_host + 10, path_part);
new_args[idx++] = new_host;
}
free(host_part);
continue;
}
}
new_args[idx++] = argv[i];
}
new_args[idx] = NULL;
*new_argc = idx;
return new_args;
}
int find_available_port() { int find_available_port() {
struct sockaddr_in addr; struct sockaddr_in addr;
int sock; int sock;
...@@ -125,7 +300,15 @@ int find_available_port() { ...@@ -125,7 +300,15 @@ int find_available_port() {
return port; return port;
} }
int websocket_handshake(int sock, const char *host, int port, const char *path) { void generate_request_id(char *request_id, size_t size) {
const char charset[] = "0123456789abcdef";
for (size_t i = 0; i < size - 1; i++) {
request_id[i] = charset[rand() % (sizeof(charset) - 1)];
}
request_id[size - 1] = '\0';
}
int websocket_handshake(SSL *ssl, const char *host, int port, const char *path) {
char request[1024]; char request[1024];
char response[BUFFER_SIZE]; char response[BUFFER_SIZE];
int bytes_read; int bytes_read;
...@@ -141,15 +324,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -141,15 +324,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
"\r\n", "\r\n",
path, host, port); path, host, port);
if (send(sock, request, strlen(request), 0) < 0) { if (SSL_write(ssl, request, strlen(request)) <= 0) {
perror("WebSocket handshake send failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake send failed\n");
return 0; return 0;
} }
// Read response // Read response
bytes_read = recv(sock, response, sizeof(response) - 1, 0); bytes_read = SSL_read(ssl, response, sizeof(response) - 1);
if (bytes_read <= 0) { if (bytes_read <= 0) {
perror("WebSocket handshake recv failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake recv failed\n");
return 0; return 0;
} }
...@@ -164,21 +349,356 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -164,21 +349,356 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
return 1; return 1;
} }
int send_json_message(int sock, const char *type, const char *client_id) { int send_json_message(SSL *ssl, const char *type, const char *client_id, const char *request_id) {
char message[1024]; char message[1024];
if (request_id) {
snprintf(message, sizeof(message),
"{\"type\":\"%s\",\"client_id\":\"%s\",\"request_id\":\"%s\"}",
type, client_id, request_id);
} else {
snprintf(message, sizeof(message), snprintf(message, sizeof(message),
"{\"type\":\"%s\",\"client_id\":\"%s\"}", "{\"type\":\"%s\",\"client_id\":\"%s\"}",
type, client_id); type, client_id);
}
if (send(sock, message, strlen(message), 0) < 0) { // Send as WebSocket frame
perror("Send failed"); return send_websocket_frame(ssl, message);
}
int send_websocket_frame(SSL *ssl, const char *data) {
char frame[BUFFER_SIZE];
frame[0] = 0x81; // FIN + text opcode
int msg_len = strlen(data);
int header_len = 2;
if (msg_len <= 125) {
frame[1] = 0x80 | msg_len; // MASK + length
} else if (msg_len <= 65535) {
frame[1] = 0x80 | 126; // MASK + extended length
frame[2] = (msg_len >> 8) & 0xFF;
frame[3] = msg_len & 0xFF;
header_len = 4;
} else {
frame[1] = 0x80 | 127; // MASK + extended length
frame[2] = 0;
frame[3] = 0;
frame[4] = 0;
frame[5] = 0;
frame[6] = (msg_len >> 24) & 0xFF;
frame[7] = (msg_len >> 16) & 0xFF;
frame[8] = (msg_len >> 8) & 0xFF;
frame[9] = msg_len & 0xFF;
header_len = 10;
}
// Add mask key
char mask_key[4];
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[header_len + i] = mask_key[i];
}
header_len += 4;
// Mask payload
for (int i = 0; i < msg_len; i++) {
frame[header_len + i] = data[i] ^ mask_key[i % 4];
}
int frame_len = header_len + msg_len;
return SSL_write(ssl, frame, frame_len) > 0;
}
int send_pong_frame(SSL *ssl, const char *ping_payload, int payload_len) {
char frame[BUFFER_SIZE];
frame[0] = 0x8A; // FIN + pong opcode
int header_len = 2;
if (payload_len <= 125) {
frame[1] = 0x80 | payload_len; // MASK + length
} else if (payload_len <= 65535) {
frame[1] = 0x80 | 126; // MASK + extended length
frame[2] = (payload_len >> 8) & 0xFF;
frame[3] = payload_len & 0xFF;
header_len = 4;
} else {
frame[1] = 0x80 | 127; // MASK + extended length
frame[2] = 0;
frame[3] = 0;
frame[4] = 0;
frame[5] = 0;
frame[6] = (payload_len >> 24) & 0xFF;
frame[7] = (payload_len >> 16) & 0xFF;
frame[8] = (payload_len >> 8) & 0xFF;
frame[9] = payload_len & 0xFF;
header_len = 10;
}
// Add mask key
char mask_key[4];
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[header_len + i] = mask_key[i];
}
header_len += 4;
// Mask payload
for (int i = 0; i < payload_len; i++) {
frame[header_len + i] = ping_payload[i] ^ mask_key[i % 4];
}
int frame_len = header_len + payload_len;
return SSL_write(ssl, frame, frame_len) > 0;
}
frame_buffer_t *frame_buffer_init() {
frame_buffer_t *fb = malloc(sizeof(frame_buffer_t));
if (!fb) return NULL;
fb->buffer = malloc(INITIAL_FRAME_BUFFER_SIZE);
if (!fb->buffer) {
free(fb);
return NULL;
}
fb->size = INITIAL_FRAME_BUFFER_SIZE;
fb->used = 0;
return fb;
}
void frame_buffer_free(frame_buffer_t *fb) {
if (fb) {
free(fb->buffer);
free(fb);
}
}
int frame_buffer_resize(frame_buffer_t *fb, size_t new_size) {
if (new_size <= fb->size) return 1;
char *new_buffer = realloc(fb->buffer, new_size);
if (!new_buffer) return 0;
fb->buffer = new_buffer;
fb->size = new_size;
return 1;
}
int frame_buffer_append(frame_buffer_t *fb, const char *data, size_t len) {
if (fb->used + len > fb->size) {
size_t new_size = fb->size * 2;
while (new_size < fb->used + len) {
new_size *= 2;
}
if (!frame_buffer_resize(fb, new_size)) {
return 0; return 0;
} }
}
memcpy(fb->buffer + fb->used, data, len);
fb->used += len;
return 1; return 1;
} }
int frame_buffer_consume(frame_buffer_t *fb, size_t len) {
if (len > fb->used) return 0;
memmove(fb->buffer, fb->buffer + len, fb->used - len);
fb->used -= len;
return 1;
}
int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, int *payload_len) {
if (bytes_read < 2) {
return 0; // Not enough data for a frame
}
unsigned char frame_type = buffer[0] & 0x8F;
if (frame_type != 0x81 && frame_type != 0x82 && frame_type != 0x88 && frame_type != 0x89 && frame_type != 0x8A) {
return 0; // Not a supported frame type (text, binary, close, ping, pong)
}
int masked = buffer[1] & 0x80;
int len_indicator = buffer[1] & 0x7F;
int header_len = 2;
if (len_indicator <= 125) {
*payload_len = len_indicator;
} else if (len_indicator == 126) {
if (bytes_read < 4) return 0;
*payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3];
header_len = 4;
} else if (len_indicator == 127) {
if (bytes_read < 10) return 0;
unsigned long long full_len = ((unsigned long long)(unsigned char)buffer[2] << 56) |
((unsigned long long)(unsigned char)buffer[3] << 48) |
((unsigned long long)(unsigned char)buffer[4] << 40) |
((unsigned long long)(unsigned char)buffer[5] << 32) |
((unsigned long long)(unsigned char)buffer[6] << 24) |
((unsigned long long)(unsigned char)buffer[7] << 16) |
((unsigned long long)(unsigned char)buffer[8] << 8) |
(unsigned char)buffer[9];
if (full_len > INT_MAX) {
return 0; // Payload too large
}
*payload_len = (int)full_len;
header_len = 10;
} else {
// Invalid length indicator
return 0;
}
if (masked) {
header_len += 4;
}
if (bytes_read < header_len + *payload_len) {
return 0; // Incomplete frame
}
*payload = (char *)buffer + header_len;
if (masked) {
char *mask_key = (char *)buffer + header_len - 4;
for (int i = 0; i < *payload_len; i++) {
(*payload)[i] ^= mask_key[i % 4];
}
}
return 1;
}
void *forward_tcp_to_ws(void *arg) {
thread_args_t *args = (thread_args_t *)arg;
SSL *ssl = args->ssl;
int debug = args->debug;
char buffer[BUFFER_SIZE];
int bytes_read;
while (1) {
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
pthread_mutex_unlock(&tunnel_mutex);
break;
}
int sock = active_tunnel->local_sock;
char request_id[37];
strcpy(request_id, active_tunnel->request_id);
// Send pending data from outgoing buffer to local socket
if (active_tunnel->outgoing_buffer->used > 0) {
ssize_t sent = send(sock, active_tunnel->outgoing_buffer->buffer, active_tunnel->outgoing_buffer->used, MSG_DONTWAIT);
if (sent > 0) {
frame_buffer_consume(active_tunnel->outgoing_buffer, sent);
if (debug) {
printf("[DEBUG] Sent %zd bytes from buffer to local socket\n", sent);
fflush(stdout);
}
} else if (sent == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
if (debug) {
perror("[DEBUG] send to local socket failed");
fflush(stdout);
}
pthread_mutex_unlock(&tunnel_mutex);
break;
}
}
pthread_mutex_unlock(&tunnel_mutex);
bytes_read = recv(sock, buffer, sizeof(buffer), MSG_DONTWAIT);
if (bytes_read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// No data available, sleep to avoid busy waiting
usleep(50000); // Increased sleep time to reduce CPU usage
continue;
} else if (bytes_read <= 0) {
if (debug) {
printf("[DEBUG] TCP connection closed or error\n");
fflush(stdout);
}
break;
}
if (debug) {
printf("[DEBUG] Forwarding %d bytes from TCP to WebSocket\n", bytes_read);
fflush(stdout);
}
// Convert to hex
char hex_data[bytes_read * 2 + 1];
for (int i = 0; i < bytes_read; i++) {
sprintf(hex_data + i * 2, "%02x", (unsigned char)buffer[i]);
}
hex_data[bytes_read * 2] = '\0';
// Send as tunnel_data
char message[BUFFER_SIZE];
snprintf(message, sizeof(message),
"{\"type\":\"tunnel_data\",\"request_id\":\"%s\",\"data\":\"%s\"}",
request_id, hex_data);
if (!send_websocket_frame(ssl, message)) {
if (debug) {
printf("[DEBUG] Failed to send WebSocket frame\n");
fflush(stdout);
}
break;
}
}
if (debug) {
printf("[DEBUG] TCP to WebSocket forwarding thread exiting\n");
fflush(stdout);
}
free(args);
return NULL;
}
void handle_tunnel_data(SSL *ssl __attribute__((unused)), const char *request_id, const char *data_hex, int debug __attribute__((unused))) {
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || strcmp(active_tunnel->request_id, request_id) != 0) {
pthread_mutex_unlock(&tunnel_mutex);
return;
}
pthread_mutex_unlock(&tunnel_mutex);
// Decode hex data
size_t data_len = strlen(data_hex) / 2;
char *data = malloc(data_len);
if (!data) return;
for (size_t i = 0; i < data_len; i++) {
sscanf(data_hex + i * 2, "%2hhx", &data[i]);
}
// Append to outgoing buffer
pthread_mutex_lock(&tunnel_mutex);
if (!frame_buffer_append(active_tunnel->outgoing_buffer, data, data_len)) {
if (debug) {
printf("[DEBUG] Failed to append to outgoing buffer, dropping %zu bytes\n", data_len);
fflush(stdout);
}
}
pthread_mutex_unlock(&tunnel_mutex);
free(data);
}
void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_id, int debug) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && strcmp(active_tunnel->request_id, request_id) == 0) {
active_tunnel->active = 0;
close(active_tunnel->local_sock);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
if (debug) {
printf("[DEBUG] Tunnel %s closed\n", request_id);
}
}
pthread_mutex_unlock(&tunnel_mutex);
}
int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id, int local_port, int debug) { int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id, int local_port, int debug) {
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
struct hostent *he; struct hostent *he;
...@@ -188,6 +708,10 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -188,6 +708,10 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int bytes_read; int bytes_read;
// Generate request ID
char request_id[37];
generate_request_id(request_id, sizeof(request_id));
// Resolve hostname // Resolve hostname
if ((he = gethostbyname(wssshd_host)) == NULL) { if ((he = gethostbyname(wssshd_host)) == NULL) {
herror("gethostbyname"); herror("gethostbyname");
...@@ -230,24 +754,42 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -230,24 +754,42 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
SSL_set_fd(ssl, sock); SSL_set_fd(ssl, sock);
if (debug) {
printf("[DEBUG] Establishing SSL connection...\n");
fflush(stdout);
}
if (SSL_connect(ssl) <= 0) { if (SSL_connect(ssl) <= 0) {
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
fprintf(stderr, "SSL connection failed\n");
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return 0;
} }
if (debug) {
printf("[DEBUG] SSL connection established\n");
fflush(stdout);
}
// Perform WebSocket handshake // Perform WebSocket handshake
if (!websocket_handshake(sock, wssshd_host, wssshd_port, "/")) { if (debug) {
printf("[DEBUG] Performing WebSocket handshake to %s:%d\n", wssshd_host, wssshd_port);
fflush(stdout);
}
if (!websocket_handshake(ssl, wssshd_host, wssshd_port, "/")) {
fprintf(stderr, "WebSocket handshake failed\n");
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return 0;
} }
if (debug) {
printf("[DEBUG] WebSocket handshake successful\n");
fflush(stdout);
}
// Send tunnel request // Send tunnel request
if (!send_json_message(sock, "tunnel_request", client_id)) { if (!send_json_message(ssl, "tunnel_request", client_id, request_id)) {
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
...@@ -255,11 +797,11 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -255,11 +797,11 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
} }
if (debug) { if (debug) {
printf("[DEBUG] Tunnel request sent for client: %s\n", client_id); printf("[DEBUG] Tunnel request sent for client: %s, request_id: %s\n", client_id, request_id);
} }
// Read acknowledgment // Read acknowledgment
bytes_read = SSL_read(ssl, buffer, sizeof(buffer) - 1); bytes_read = SSL_read(ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) { if (bytes_read <= 0) {
if (debug) { if (debug) {
printf("[DEBUG] No acknowledgment received\n"); printf("[DEBUG] No acknowledgment received\n");
...@@ -270,15 +812,79 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -270,15 +812,79 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
return 0; return 0;
} }
buffer[bytes_read] = '\0'; if (debug) {
printf("[DEBUG] Read %d bytes acknowledgment\n", bytes_read);
printf("[DEBUG] Raw acknowledgment: ");
for (int i = 0; i < bytes_read && i < 50; i++) {
if (buffer[i] >= 32 && buffer[i] < 127) {
printf("%c", buffer[i]);
} else {
printf("\\x%02x", (unsigned char)buffer[i]);
}
}
printf("\n");
fflush(stdout);
}
// Check if it's a close frame
if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x88) {
if (debug) {
printf("[DEBUG] Server sent close frame\n");
if (bytes_read >= 6) {
int close_code = (buffer[2] << 8) | buffer[3];
printf("[DEBUG] Close code: %d\n", close_code);
if (bytes_read > 6) {
printf("[DEBUG] Close reason: %.*s\n", bytes_read - 6, buffer + 6);
}
}
fflush(stdout);
}
fprintf(stderr, "Tunnel request rejected by server\n");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Parse WebSocket frame
char *payload;
int payload_len;
if (!parse_websocket_frame(buffer, bytes_read, &payload, &payload_len)) {
fprintf(stderr, "Failed to parse WebSocket frame\n");
if (debug) {
printf("[DEBUG] Frame parsing failed. First few bytes: ");
for (int i = 0; i < bytes_read && i < 10; i++) {
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
fflush(stdout);
}
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Null terminate payload
payload[payload_len] = '\0';
if (debug) { if (debug) {
printf("[DEBUG] Received: %s\n", buffer); printf("[DEBUG] Received payload: '%s' (length: %d)\n", payload, payload_len);
printf("[DEBUG] Looking for tunnel_ack in: ");
for (int i = 0; i < payload_len && i < 50; i++) {
if (payload[i] >= 32 && payload[i] < 127) {
printf("%c", payload[i]);
} else {
printf("\\x%02x", (unsigned char)payload[i]);
}
}
printf("\n");
fflush(stdout);
} }
// Check for tunnel acknowledgment // Check for tunnel acknowledgment
if (strstr(buffer, "\"type\":\"tunnel_ack\"") == NULL) { if (strstr(payload, "tunnel_ack") == NULL) {
fprintf(stderr, "Tunnel request denied or failed\n"); fprintf(stderr, "Tunnel request denied or failed: %s\n", payload);
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
...@@ -289,16 +895,80 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -289,16 +895,80 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
printf("[DEBUG] Tunnel established, local port: %d\n", local_port); printf("[DEBUG] Tunnel established, local port: %d\n", local_port);
} }
// Keep the connection alive for data transfer // Create tunnel structure
// In a real implementation, we'd need to handle bidirectional data transfer active_tunnel = malloc(sizeof(tunnel_t));
// For now, just keep the connection open if (!active_tunnel) {
perror("Memory allocation failed");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Cleanup (this would normally be handled differently) active_tunnel->outgoing_buffer = frame_buffer_init();
if (!active_tunnel->outgoing_buffer) {
perror("Failed to initialize outgoing buffer");
free(active_tunnel);
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0;
}
return 1; strcpy(active_tunnel->request_id, request_id);
active_tunnel->local_sock = -1;
active_tunnel->active = 1;
active_tunnel->ssl = ssl;
// Start listening on local port
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
perror("Local socket creation failed");
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(local_port);
local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(listen_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
perror("Local bind failed");
close(listen_sock);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
if (listen(listen_sock, 1) < 0) {
perror("Local listen failed");
close(listen_sock);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
if (debug) {
printf("[DEBUG] Listening on localhost:%d\n", local_port);
fflush(stdout);
}
// Return success - tunnel is set up and listening
return listen_sock;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
...@@ -307,11 +977,13 @@ int main(int argc, char *argv[]) { ...@@ -307,11 +977,13 @@ int main(int argc, char *argv[]) {
.debug = 0 .debug = 0
}; };
if (!parse_args(argc, argv, &config)) { // Easter egg: --support option (only when it's the only argument)
return 1; if (argc == 2 && strcmp(argv[1], "--support") == 0) {
print_trans_flag();
return 0;
} }
// Handle --help // Handle --help before parsing
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
print_usage(argv[0]); print_usage(argv[0]);
...@@ -319,47 +991,44 @@ int main(int argc, char *argv[]) { ...@@ -319,47 +991,44 @@ int main(int argc, char *argv[]) {
} }
} }
// Need at least two arguments (source and destination) pthread_mutex_init(&tunnel_mutex, NULL);
if (optind + 1 >= argc) {
fprintf(stderr, "Error: Source and destination required\n"); // Parse wsscp arguments
int remaining_argc;
char **remaining_argv;
if (!parse_args(argc, argv, &config, &remaining_argc, &remaining_argv)) {
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
// Need at least one SCP argument (the target)
if (remaining_argc == 0) {
fprintf(stderr, "Error: No target specified\n");
print_usage(argv[0]); print_usage(argv[0]);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
// Find SCP port from -P option // Parse SCP arguments to extract destination and port
char *scp_destination = NULL;
int scp_port = DEFAULT_PORT; int scp_port = DEFAULT_PORT;
for (int i = optind; i < argc; i++) { if (!parse_scp_args(remaining_argc, remaining_argv, &scp_destination, &scp_port, config.debug)) {
if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) { pthread_mutex_destroy(&tunnel_mutex);
scp_port = atoi(argv[i + 1]); return 1;
break;
}
} }
// Find host in arguments // Parse the SCP destination to extract client_id and wssshd_host
char *client_id = NULL; char *client_id = NULL;
char *wssshd_host = NULL; char *wssshd_host = NULL;
int wssshd_port = scp_port; // Use the -P port as wssshd port int wssshd_port = scp_port; // The -P port becomes the WebSocket server port
for (int i = optind; i < argc; i++) {
if (strchr(argv[i], ':') && !strchr(argv[i], '=')) {
// This looks like host:path
char *colon_pos = strchr(argv[i], ':');
if (colon_pos) {
char *host_part = strndup(argv[i], colon_pos - argv[i]);
if (parse_hostname(argv[i], &client_id, &wssshd_host, &wssshd_port)) {
break;
}
free(host_part);
}
}
}
if (!client_id || !wssshd_host) { if (!parse_hostname(scp_destination, &client_id, &wssshd_host)) {
fprintf(stderr, "Error: Could not determine target host\n"); pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
if (config.debug) { if (config.debug) {
printf("[DEBUG] SCP Destination: %s\n", scp_destination);
printf("[DEBUG] Client ID: %s\n", client_id); printf("[DEBUG] Client ID: %s\n", client_id);
printf("[DEBUG] WSSSHD Host: %s\n", wssshd_host); printf("[DEBUG] WSSSHD Host: %s\n", wssshd_host);
printf("[DEBUG] WSSSHD Port: %d\n", wssshd_port); printf("[DEBUG] WSSSHD Port: %d\n", wssshd_port);
...@@ -371,6 +1040,8 @@ int main(int argc, char *argv[]) { ...@@ -371,6 +1040,8 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Error: Could not find available local port\n"); fprintf(stderr, "Error: Could not find available local port\n");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
...@@ -378,75 +1049,457 @@ int main(int argc, char *argv[]) { ...@@ -378,75 +1049,457 @@ int main(int argc, char *argv[]) {
printf("[DEBUG] Using local port: %d\n", local_port); printf("[DEBUG] Using local port: %d\n", local_port);
} }
// Setup tunnel // Modify SCP arguments
if (!setup_tunnel(wssshd_host, wssshd_port, client_id, local_port, config.debug)) { int new_scp_argc;
char **new_scp_args = modify_scp_args(remaining_argc, remaining_argv, scp_destination, local_port, &new_scp_argc);
if (!new_scp_args) {
fprintf(stderr, "Error: Failed to modify SCP arguments\n");
free(client_id);
free(wssshd_host);
free(config.local_port);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
if (config.debug) {
printf("[DEBUG] Modified SCP command:");
for (int i = 0; new_scp_args[i]; i++) {
printf(" %s", new_scp_args[i]);
}
printf("\n");
fflush(stdout);
}
// Parent process: setup tunnel FIRST
if (config.debug) {
printf("[DEBUG] Parent process setting up tunnel...\n");
fflush(stdout);
}
int listen_sock = setup_tunnel(wssshd_host, wssshd_port, client_id, local_port, config.debug);
if (listen_sock < 0) {
fprintf(stderr, "Error: Failed to establish tunnel\n"); fprintf(stderr, "Error: Failed to establish tunnel\n");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_scp_argc; i++) {
if (new_scp_args[i] != remaining_argv[i-1]) {
free(new_scp_args[i]);
}
}
free(new_scp_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
// Prepare SCP arguments // NOW fork to run SCP in background (after tunnel is ready)
char port_str[16]; if (config.debug) {
snprintf(port_str, sizeof(port_str), "%d", local_port); printf("[DEBUG] About to fork SCP process...\n");
fflush(stdout);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_scp_argc; i++) {
if (new_scp_args[i] != remaining_argv[i-1]) {
free(new_scp_args[i]);
}
}
free(new_scp_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
// Build SCP command if (pid == 0) {
char **scp_args = malloc((argc - optind + 4) * sizeof(char *)); // Child process: run SCP
if (!scp_args) { if (config.debug) {
perror("Memory allocation failed"); printf("[DEBUG] Child process starting SCP...\n");
fflush(stdout);
}
execvp("scp", new_scp_args);
// If we reach here, execvp failed
perror("execvp failed");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
for (int i = 1; i < new_scp_argc; i++) {
if (new_scp_args[i] != remaining_argv[i-1]) {
free(new_scp_args[i]);
}
}
free(new_scp_args);
exit(1);
}
// Parent process: accept SCP connection and start forwarding
if (config.debug) {
printf("[DEBUG] Waiting for SCP connection on localhost:%d...\n", local_port);
fflush(stdout);
}
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
active_tunnel->local_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (active_tunnel->local_sock < 0) {
perror("Local accept failed");
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
close(listen_sock);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_scp_argc; i++) {
if (new_scp_args[i] != remaining_argv[i-1]) {
free(new_scp_args[i]);
}
}
free(new_scp_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
int arg_count = 0; // Make local socket non-blocking to prevent hangs
scp_args[arg_count++] = "scp"; int flags = fcntl(active_tunnel->local_sock, F_GETFL, 0);
scp_args[arg_count++] = "-P"; if (flags != -1) {
scp_args[arg_count++] = port_str; fcntl(active_tunnel->local_sock, F_SETFL, flags | O_NONBLOCK);
}
// Process remaining arguments, replacing host:path with localhost:path close(listen_sock); // No longer needed
for (int i = optind; i < argc; i++) {
if (strchr(argv[i], ':') && !strchr(argv[i], '=')) { if (config.debug) {
// This is host:path, replace with localhost:path printf("[DEBUG] Local SCP connection accepted! Starting data forwarding...\n");
char *colon_pos = strchr(argv[i], ':'); fflush(stdout);
if (colon_pos) { }
char *path_part = colon_pos + 1;
char *new_arg = malloc(strlen(path_part) + 10); // "localhost:" + path // Start forwarding thread
if (new_arg) { thread_args_t *thread_args = malloc(sizeof(thread_args_t));
sprintf(new_arg, "localhost:%s", path_part); if (!thread_args) {
scp_args[arg_count++] = new_arg; perror("Memory allocation failed for thread args");
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
close(active_tunnel->local_sock);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_scp_argc; i++) {
if (new_scp_args[i] != remaining_argv[i-1]) {
free(new_scp_args[i]);
}
}
free(new_scp_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
thread_args->ssl = active_tunnel->ssl; // Need to store SSL in tunnel struct
thread_args->debug = config.debug;
pthread_t thread;
pthread_create(&thread, NULL, forward_tcp_to_ws, thread_args);
pthread_detach(thread);
// Main tunnel loop - handle WebSocket messages
frame_buffer_t *frame_buffer = frame_buffer_init();
if (!frame_buffer) {
fprintf(stderr, "Failed to initialize frame buffer\n");
goto cleanup;
}
char read_buffer[BUFFER_SIZE];
while (active_tunnel && active_tunnel->active) {
// Check if SCP process has finished
int status;
if (waitpid(pid, &status, WNOHANG) > 0) {
if (config.debug) {
printf("[DEBUG] SCP process finished, closing tunnel\n");
fflush(stdout);
}
active_tunnel->active = 0;
break;
}
int bytes_read = SSL_read(active_tunnel->ssl, read_buffer, sizeof(read_buffer));
if (bytes_read <= 0) {
if (config.debug) {
printf("[DEBUG] WebSocket connection closed or SSL_read error: %d\n", bytes_read);
fflush(stdout);
}
break;
}
if (config.debug) {
printf("[DEBUG] SSL_read returned %d bytes\n", bytes_read);
fflush(stdout);
}
// Append new data to frame buffer
if (!frame_buffer_append(frame_buffer, read_buffer, bytes_read)) {
if (config.debug) {
printf("[DEBUG] Failed to append data to frame buffer\n");
fflush(stdout);
} }
continue;
}
// Process complete frames from the buffer
int processed_frames = 0;
while (frame_buffer->used >= 2 && processed_frames < 100) { // Limit to prevent infinite loops
// Check frame type first
unsigned char frame_byte = frame_buffer->buffer[0];
unsigned char frame_type = frame_byte & 0x8F;
int fin = (frame_byte & 0x80) != 0;
int masked = (frame_buffer->buffer[1] & 0x80) != 0;
if (config.debug) {
printf("[DEBUG] Processing frame: type=0x%02x, fin=%d, masked=%d, buffer_used=%zu\n",
frame_type, fin, masked, frame_buffer->used);
fflush(stdout);
}
// Handle close frame
if (frame_type == 0x88) {
if (config.debug) {
printf("[DEBUG] Received close frame from server\n");
fflush(stdout);
}
goto cleanup;
}
// Handle ping frame
if (frame_type == 0x89) {
if (config.debug) {
printf("[DEBUG] Received ping frame, sending pong\n");
fflush(stdout);
}
// Parse the ping frame to get payload
char *ping_payload;
int ping_payload_len;
if (parse_websocket_frame(frame_buffer->buffer, frame_buffer->used, &ping_payload, &ping_payload_len)) {
// Send pong with same payload
if (!send_pong_frame(active_tunnel->ssl, ping_payload, ping_payload_len)) {
if (config.debug) {
printf("[DEBUG] Failed to send pong frame\n");
fflush(stdout);
}
}
// Calculate frame size and consume it
int frame_size = (ping_payload - frame_buffer->buffer) + ping_payload_len;
if (frame_type & 0x80) { // FIN bit set
frame_buffer_consume(frame_buffer, frame_size);
processed_frames++;
} }
} else { } else {
scp_args[arg_count++] = argv[i]; // Incomplete ping frame, wait for more data
break;
} }
continue;
} }
scp_args[arg_count] = NULL;
// Handle pong frame
if (frame_type == 0x8A) {
if (config.debug) { if (config.debug) {
printf("[DEBUG] SCP command:"); printf("[DEBUG] Received pong frame\n");
for (int i = 0; scp_args[i]; i++) { fflush(stdout);
printf(" %s", scp_args[i]); }
// Parse to find frame boundaries
char *pong_payload;
int pong_payload_len;
if (parse_websocket_frame(frame_buffer->buffer, frame_buffer->used, &pong_payload, &pong_payload_len)) {
int frame_size = (pong_payload - frame_buffer->buffer) + pong_payload_len;
if (frame_type & 0x80) { // FIN bit set
frame_buffer_consume(frame_buffer, frame_size);
processed_frames++;
}
} else {
break; // Incomplete pong frame
}
continue;
}
// Parse regular frame (text or binary)
char *payload;
int payload_len;
if (!parse_websocket_frame(frame_buffer->buffer, frame_buffer->used, &payload, &payload_len)) {
// Incomplete frame, wait for more data
if (config.debug) {
printf("[DEBUG] Incomplete frame, buffer used: %zu, first few bytes: ", frame_buffer->used);
for (size_t i = 0; i < frame_buffer->used && i < 10; i++) {
printf("%02x ", (unsigned char)frame_buffer->buffer[i]);
} }
printf("\n"); printf("\n");
fflush(stdout);
}
break;
} }
// Execute SCP // Calculate total frame size (header + payload)
execvp("scp", scp_args); // payload points to start of payload data, so header_len = payload - buffer
int header_len = payload - frame_buffer->buffer;
int frame_size = header_len + payload_len;
// If we reach here, execvp failed if (config.debug) {
perror("execvp failed"); printf("[DEBUG] Frame details: header_len=%d, payload_len=%d, frame_size=%d, buffer_used=%zu\n",
header_len, payload_len, frame_size, frame_buffer->used);
fflush(stdout);
}
// Validate frame size before consuming
if (frame_size <= 0 || frame_size > (int)frame_buffer->used) {
if (config.debug) {
printf("[DEBUG] Invalid frame size %d, skipping frame\n", frame_size);
fflush(stdout);
}
// Skip this frame by consuming just the header
if (header_len > 0 && header_len <= (int)frame_buffer->used) {
frame_buffer_consume(frame_buffer, header_len);
} else {
// Can't even consume header, break to avoid infinite loop
break;
}
processed_frames++;
continue;
}
frame_buffer_consume(frame_buffer, frame_size);
processed_frames++;
payload[payload_len] = '\0';
if (config.debug) {
printf("[DEBUG] Received: %s\n", payload);
fflush(stdout);
}
// Handle messages
if (strstr(payload, "tunnel_data") || strstr(payload, "tunnel_response") ||
strstr(payload, "tunnel_request") || strstr(payload, "tunnel_ack")) {
if (config.debug) {
if (strstr(payload, "tunnel_data")) {
printf("[DEBUG] Received tunnel_data message\n");
} else if (strstr(payload, "tunnel_response")) {
printf("[DEBUG] Received tunnel_response message\n");
} else if (strstr(payload, "tunnel_request")) {
printf("[DEBUG] Received tunnel_request message\n");
} else if (strstr(payload, "tunnel_ack")) {
printf("[DEBUG] Received tunnel_ack message\n");
}
fflush(stdout);
}
// Extract request_id and data if present
char *id_start = strstr(payload, "\"request_id\"");
char *data_start = strstr(payload, "\"data\"");
if (id_start && data_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
char *data_colon = strchr(data_start, ':');
if (data_colon) {
char *data_quote = strchr(data_colon, '"');
if (data_quote) {
data_start = data_quote + 1;
char *data_end = strchr(data_start, '"');
if (data_end) {
*data_end = '\0';
handle_tunnel_data(active_tunnel->ssl, id_start, data_start, config.debug);
}
}
}
}
}
}
}
} else if (strstr(payload, "tunnel_close")) {
if (config.debug) {
printf("[DEBUG] Received tunnel_close message\n");
fflush(stdout);
}
char *id_start = strstr(payload, "\"request_id\"");
if (id_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
handle_tunnel_close(active_tunnel->ssl, id_start, config.debug);
}
}
}
}
} else {
if (config.debug) {
printf("[DEBUG] Received unknown message type: %s\n", payload);
fflush(stdout);
}
}
}
// Free allocated memory if (processed_frames >= 100) {
for (int i = 3; i < arg_count; i++) { if (config.debug) {
if (strstr(scp_args[i], "localhost:")) { printf("[DEBUG] Processed 100 frames in one iteration, possible infinite loop\n");
free(scp_args[i]); fflush(stdout);
}
} }
} }
free(scp_args);
frame_buffer_free(frame_buffer);
if (config.debug) {
printf("[DEBUG] Tunnel loop ended, waiting for SCP to finish...\n");
fflush(stdout);
}
cleanup:
// Wait for SCP to finish
int status;
waitpid(pid, &status, 0);
if (config.debug) {
printf("[DEBUG] SCP process finished with status: %d\n", status);
fflush(stdout);
}
// Cleanup
if (active_tunnel) {
close(active_tunnel->local_sock);
SSL_free(active_tunnel->ssl);
frame_buffer_free(active_tunnel->outgoing_buffer);
free(active_tunnel);
active_tunnel = NULL;
}
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port); free(config.local_port);
// Free allocated strings in new_scp_args
for (int i = 0; i < new_scp_argc; i++) {
// Free strings that were allocated with malloc/strdup
if (i == 2) { // The port string
free(new_scp_args[i]);
} else if (i == 3) { // The user@localhost:path string (if allocated)
// Check if this was allocated (contains @localhost)
if (strstr(new_scp_args[i], "@localhost")) {
free(new_scp_args[i]);
}
}
}
free(new_scp_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 0;
} }
\ 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