Connect wsssht stdin/stdout to VNC WebSocket channel

- Add output buffering to vnc_session_t structure
- Modify read_pipe_output to buffer wsssht stdout
- Update vnc_get_output to return buffered data
- Modify websocket_vnc_handler to send output to WebSocket client
- Enable bidirectional communication for VNC WebSocket connections
parent 5a4dcbd2
......@@ -53,8 +53,26 @@ static void *read_pipe_output(void *arg) {
ssize_t bytes_read = read(session->master_fd, buffer, sizeof(buffer));
if (bytes_read <= 0) break;
// For VNC, we don't buffer output like terminal, just pass through
// But since WebSocket handles it, we can ignore for now
// Append to session output buffer
pthread_mutex_lock(&session->output_mutex);
if (session->output_used + bytes_read > session->output_size) {
size_t new_size = session->output_size * 2;
while (new_size < session->output_used + bytes_read) {
new_size *= 2;
}
char *new_buf = realloc(session->output_buffer, new_size);
if (new_buf) {
session->output_buffer = new_buf;
session->output_size = new_size;
} else {
// Out of memory, skip this data
pthread_mutex_unlock(&session->output_mutex);
continue;
}
}
memcpy(session->output_buffer + session->output_used, buffer, bytes_read);
session->output_used += bytes_read;
pthread_mutex_unlock(&session->output_mutex);
}
}
......@@ -78,19 +96,30 @@ vnc_session_t *vnc_create_session(const wssshd_config_t *config, const char *cli
strncpy(session->username, username, sizeof(session->username) - 1);
}
// Initialize output buffer
session->output_buffer = malloc(4096);
if (!session->output_buffer) {
free(session);
return NULL;
}
session->output_size = 4096;
session->output_used = 0;
pthread_mutex_init(&session->output_mutex, NULL);
// Create pipe for communication
int pipefd[2];
if (pipe(pipefd) < 0) {
free(session->output_buffer);
free(session);
return NULL;
}
session->master_fd = pipefd[0]; // read end
// Build command: wsssht --wssshd-port 9898 --pipe vnc://{client_id}@mbetter.nexlab.net
// Build command: wsssht --pipe --wssshd-port 9898 vnc://{client_id}@mbetter.nexlab.net
char command[1024];
snprintf(command, sizeof(command),
"wsssht --wssshd-port %d --pipe vnc://%s@%s",
"wsssht --pipe --wssshd-port %d vnc://%s@%s",
config->port, client_id, config->domain);
// Fork and execute
......@@ -98,6 +127,7 @@ vnc_session_t *vnc_create_session(const wssshd_config_t *config, const char *cli
if (pid < 0) {
close(pipefd[0]);
close(pipefd[1]);
free(session->output_buffer);
free(session);
return NULL;
}
......@@ -132,6 +162,11 @@ void vnc_free_session(vnc_session_t *session) {
close(session->master_fd);
}
if (session->output_buffer) {
free(session->output_buffer);
}
pthread_mutex_destroy(&session->output_mutex);
free(session);
}
......@@ -147,10 +182,36 @@ bool vnc_send_data(vnc_session_t *session, const char *data, size_t len) {
return written > 0;
}
char *vnc_get_output(vnc_session_t *session __attribute__((unused)), size_t *len) {
// For VNC, output is handled directly by WebSocket
*len = 0;
return NULL;
char *vnc_get_output(vnc_session_t *session, size_t *len) {
if (!session) {
*len = 0;
return NULL;
}
pthread_mutex_lock(&session->output_mutex);
if (session->output_used == 0) {
// For WebSocket connections, don't wait - return immediately
pthread_mutex_unlock(&session->output_mutex);
*len = 0;
return NULL;
}
// Return the buffer and length
*len = session->output_used;
char *output = session->output_buffer;
session->output_buffer = malloc(4096);
if (session->output_buffer) {
session->output_size = 4096;
} else {
// Out of memory
*len = 0;
pthread_mutex_unlock(&session->output_mutex);
return NULL;
}
session->output_used = 0;
pthread_mutex_unlock(&session->output_mutex);
return output;
}
bool vnc_disconnect(vnc_session_t *session) {
......
......@@ -31,6 +31,10 @@ typedef struct {
int proc_pid;
int master_fd; // pipe fd
bool active;
char *output_buffer;
size_t output_size;
size_t output_used;
pthread_mutex_t output_mutex;
} vnc_session_t;
vnc_session_t *vnc_create_session(const wssshd_config_t *config, const char *client_id, const char *username);
......
......@@ -316,38 +316,41 @@ static void *websocket_vnc_handler(void *arg) {
printf("WebSocket VNC handler started for client: %s\n", ws_conn->client_id);
// Wait for VNC session to be established (up to 30 seconds)
vnc_session_t *session = NULL;
int wait_count = 0;
const int max_wait = 300; // 30 seconds at 100ms intervals
while (ws_conn->active && ws_conn->ws_conn->state == WS_STATE_OPEN && wait_count < max_wait) {
pthread_mutex_lock(&vncs_mutex);
for (int i = 0; i < active_vncs_count; i++) {
if (strcmp(active_vncs[i]->request_id, ws_conn->request_id) == 0 && strlen(ws_conn->request_id) > 0) {
session = active_vncs[i];
break;
}
}
pthread_mutex_unlock(&vncs_mutex);
if (session) {
printf("WebSocket VNC handler found session for request_id: %s\n", ws_conn->request_id);
break;
}
usleep(100000); // 100ms
wait_count++;
// Launch wsssht for VNC immediately when WebSocket opens
if (global_config && global_config->debug) {
printf("[DEBUG] Launching wsssht for VNC client %s\n", ws_conn->client_id);
}
vnc_session_t *session = vnc_create_session(global_config, ws_conn->client_id, ws_conn->username);
if (!session) {
printf("WebSocket VNC handler timed out waiting for session for client: %s\n", ws_conn->client_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_TEXT, "{\"error\":\"VNC session not established\"}", 41);
printf("Failed to create VNC session for client: %s\n", ws_conn->client_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_TEXT, "{\"error\":\"Failed to launch VNC session\"}", 40);
remove_websocket_vnc_connection(ws_conn->request_id);
return NULL;
}
printf("WebSocket VNC handler running for request_id: %s\n", ws_conn->request_id);
// Update the WebSocket connection with the request_id
size_t len = strlen(session->request_id);
if (len >= sizeof(ws_conn->request_id)) {
len = sizeof(ws_conn->request_id) - 1;
}
memcpy(ws_conn->request_id, session->request_id, len);
ws_conn->request_id[len] = '\0';
// Add session to active list
pthread_mutex_lock(&vncs_mutex);
if (active_vncs_count < MAX_ACTIVE_VNCS) {
active_vncs[active_vncs_count++] = session;
printf("WebSocket VNC handler running for request_id: %s\n", ws_conn->request_id);
} else {
vnc_free_session(session);
printf("Too many active VNC sessions, connection rejected for client: %s\n", ws_conn->client_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_TEXT, "{\"error\":\"Too many active VNC sessions\"}", 38);
remove_websocket_vnc_connection(ws_conn->request_id);
pthread_mutex_unlock(&vncs_mutex);
return NULL;
}
pthread_mutex_unlock(&vncs_mutex);
while (ws_conn->active && ws_conn->ws_conn->state == WS_STATE_OPEN) {
// Check if VNC session is still active
......@@ -358,6 +361,15 @@ static void *websocket_vnc_handler(void *arg) {
break;
}
// Send any available output immediately
size_t output_len = 0;
char *output = vnc_get_output(session, &output_len);
if (output && output_len > 0) {
printf("WebSocket sending %zu bytes of VNC output for request_id: %s\n", output_len, ws_conn->request_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_BINARY, output, output_len);
free(output);
}
// Check for input from WebSocket (non-blocking)
uint8_t opcode = 0;
void *data = NULL;
......@@ -1421,6 +1433,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
}
if (is_valid_upgrade) {
// Web interface WebSockets can use non-TLS for noVNC connections
// Set socket to non-blocking mode for WebSocket
int flags = fcntl(client_fd, F_GETFL, 0);
......@@ -1428,8 +1441,8 @@ static int handle_request(int client_fd, const http_request_t *req) {
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
}
// Create WebSocket connection
ws_connection_t *ws_conn = ws_connection_create(NULL, client_fd); // No SSL for web interface
// Create WebSocket connection (plain, no SSL for web interface)
ws_connection_t *ws_conn = ws_connection_create_plain(client_fd);
if (!ws_conn) {
free(headers_copy);
send_response(client_fd, 500, "Internal Server Error", "text/plain", "Failed to create WebSocket connection", 35, NULL, NULL);
......@@ -1447,12 +1460,23 @@ static int handle_request(int client_fd, const http_request_t *req) {
// Send WebSocket handshake response
char response[512];
int response_len = snprintf(response, sizeof(response),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"\r\n", accept_key);
int response_len;
if (is_vnc) {
response_len = snprintf(response, sizeof(response),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"Sec-WebSocket-Protocol: binary\r\n"
"\r\n", accept_key);
} else {
response_len = snprintf(response, sizeof(response),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"\r\n", accept_key);
}
free(accept_key);
......@@ -1929,7 +1953,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
// Generate VNC HTML with client_id
char html[32768];
int len = snprintf(html, sizeof(html), vnc_page_html,
client_id, client_id, client_id, client_id);
client_id, client_id, client_id, client_id, client_id);
send_response(client_fd, 200, "OK", "text/html", html, len, NULL, NULL);
} else {
// Handle VNC actions (connect, disconnect)
......@@ -1951,57 +1975,7 @@ static int handle_request(int client_fd, const http_request_t *req) {
}
if (strcmp(req->method, "POST") == 0) {
if (strcmp(action, "connect") == 0) {
// Create VNC session
vnc_session_t *session = vnc_create_session(global_config, client_id, username);
if (global_config && global_config->debug_web) {
printf("[WEB-DEBUG] Created VNC session for %s@%s\n", username, client_id);
}
if (session) {
// Add to active sessions
pthread_mutex_lock(&vncs_mutex);
if (active_vncs_count < MAX_ACTIVE_VNCS) {
active_vncs[active_vncs_count++] = session;
// Update WebSocket connection with request_id
pthread_mutex_lock(&websocket_vnc_mutex);
websocket_vnc_conn_t *ws_conn = NULL;
for (int i = 0; i < websocket_vnc_connections_count; i++) {
if (websocket_vnc_connections[i].active &&
strcmp(websocket_vnc_connections[i].client_id, client_id) == 0) {
ws_conn = &websocket_vnc_connections[i];
break;
}
}
if (ws_conn) {
size_t len = strlen(session->request_id);
if (len >= sizeof(ws_conn->request_id)) {
len = sizeof(ws_conn->request_id) - 1;
}
memcpy(ws_conn->request_id, session->request_id, len);
ws_conn->request_id[len] = '\0';
printf("Updated WebSocket VNC connection for client %s with request_id: %s\n", client_id, session->request_id);
} else {
printf("Warning: Could not find WebSocket VNC connection for client %s\n", client_id);
}
pthread_mutex_unlock(&websocket_vnc_mutex);
char json[256];
int len = snprintf(json, sizeof(json), "{\"request_id\":\"%s\"}", session->request_id);
pthread_mutex_unlock(&vncs_mutex);
send_response(client_fd, 200, "OK", "application/json", json, len, NULL, NULL);
} else {
vnc_free_session(session);
pthread_mutex_unlock(&vncs_mutex);
send_response(client_fd, 500, "Internal Server Error", "application/json",
"{\"error\":\"Too many active VNC sessions\"}", 38, NULL, NULL);
}
} else {
send_response(client_fd, 500, "Internal Server Error", "application/json",
"{\"error\":\"Failed to create VNC session\"}", 40, NULL, NULL);
}
return 0;
} else if (strcmp(action, "disconnect") == 0) {
if (strcmp(action, "disconnect") == 0) {
char request_id[37] = "";
// Parse form data
......
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