Add RDP support and fix VNC WebSocket issues

parent c3b379fb
This diff is collapsed.
# Makefile for wssshd2 - Generated by configure.sh
# Do not edit manually, run ./configure.sh instead
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -pthread
LDFLAGS = -lssl -lcrypto -lm -luuid -lsqlite3
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man
CONFIGDIR = /etc
# Source files
SRCS = main.c config.c tunnel.c terminal.c vnc.c websocket.c websocket_protocol.c web.c assets.c ssl.c
OBJS = $(SRCS:.c=.o)
# Target
TARGET = wssshd
.PHONY: all clean install uninstall
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Dependencies
main.o: main.c config.h websocket.h web.h
config.o: config.c config.h
tunnel.o: tunnel.c tunnel.h websocket.h
terminal.o: terminal.c terminal.h config.h
vnc.o: vnc.c vnc.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h
websocket_protocol.o: websocket_protocol.c websocket_protocol.h
web.o: web.c web.h terminal.h vnc.h assets.h websocket.h html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h html_pages/users_page.h
assets.o: assets.c assets.h
ssl.o: ssl.c ssl.h
# Asset embedding (run before compilation)
assets.o: image_data.h
image_data.h: embed_assets.sh
./embed_assets.sh
# HTML page generation
html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h: embed_assets.sh
./embed_assets.sh
clean:
rm -f $(OBJS) $(TARGET) image_data.h favicon_data.h
install: $(TARGET)
install -d $(DESTDIR)$(BINDIR)
install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/
install -d $(DESTDIR)$(CONFIGDIR)
[ -f $(DESTDIR)$(CONFIGDIR)/wssshd.conf ] || install -m 644 wssshd.conf.example $(DESTDIR)$(CONFIGDIR)/wssshd.conf
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 wssshd.8 $(DESTDIR)$(MANDIR)/man8/
uninstall:
rm -f $(DESTDIR)$(BINDIR)/$(TARGET)
rm -f $(DESTDIR)$(MANDIR)/man8/wssshd.8
# Development targets
debug: CFLAGS += -g -DDEBUG
debug: clean all
test: $(TARGET)
@echo "Running basic functionality test..."
./$(TARGET) --help || true
distclean: clean
rm -f Makefile
# Help target
help:
@echo "Available targets:"
@echo " all - Build wssshd2 (default)"
@echo " clean - Remove build artifacts"
@echo " install - Install wssshd2 to system"
@echo " uninstall - Remove wssshd2 from system"
@echo " debug - Build with debug symbols"
@echo " test - Run basic tests"
@echo " distclean - Remove all generated files"
@echo " help - Show this help"
......@@ -26,6 +26,7 @@
#include "html_pages/vnc_page.h"
#include "html_pages/xterm_page.h"
#include "html_pages/xterm_addon_page.h"
#include "html_pages/mstsc_page.h"
#include "html_pages/novnc_css_page.h"
#include "novnc_asset_map.c"
......@@ -65,6 +66,9 @@ const char *get_embedded_asset(const char *path, size_t *size) {
} else if (strcmp(path, "/xterm-addon-fit.js") == 0) {
if (size) *size = strlen(xterm_addon_fit_js);
return xterm_addon_fit_js;
} else if (strcmp(path, "/mstsc.js") == 0) {
if (size) *size = strlen(mstsc_js);
return mstsc_js;
} else if (strcmp(path, "/novnc.css") == 0) {
if (size) *size = strlen(novnc_css);
return novnc_css;
......
......@@ -42,6 +42,7 @@ static void set_default_config(wssshd_config_t *config) {
config->debug_web = false;
config->debug_database = false;
config->debug_vnc = false;
config->debug_rdp = false;
}
static void load_config_file(wssshd_config_t *config, const char *config_file) {
......@@ -158,6 +159,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
{"debug-web", no_argument, 0, 'E'},
{"debug-database", no_argument, 0, 'F'},
{"debug-vnc", no_argument, 0, 'G'},
{"debug-rdp", no_argument, 0, 'H'},
{"help", no_argument, 0, '?'},
{0, 0, 0, 0}
};
......@@ -165,7 +167,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stDEFG?", long_options, &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stDEFGH?", long_options, &option_index)) != -1) {
switch (opt) {
case 'c':
if (config->config_file) free(config->config_file);
......@@ -211,6 +213,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
case 'G':
config->debug_vnc = true;
break;
case 'H':
config->debug_rdp = true;
break;
case '?':
printf("Usage: %s [OPTIONS]\n", argv[0]);
printf("Options:\n");
......@@ -227,6 +232,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
printf(" --debug-web Enable comprehensive web interface debug output\n");
printf(" --debug-database Enable database debug output\n");
printf(" --debug-vnc Enable VNC debug output\n");
printf(" --debug-rdp Enable RDP debug output\n");
printf(" --help Show this help\n");
free_config(config);
exit(0);
......@@ -304,4 +310,5 @@ void print_config(const wssshd_config_t *config) {
printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no");
printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no");
printf(" Debug VNC: %s\n", config->debug_vnc ? "yes" : "no");
printf(" Debug RDP: %s\n", config->debug_rdp ? "yes" : "no");
}
\ No newline at end of file
......@@ -37,6 +37,7 @@ typedef struct {
bool debug_web;
bool debug_database;
bool debug_vnc;
bool debug_rdp;
} wssshd_config_t;
// Function declarations
......
......@@ -65,7 +65,7 @@ MANDIR = $(PREFIX)/share/man
CONFIGDIR = /etc
# Source files
SRCS = main.c config.c tunnel.c terminal.c vnc.c websocket.c websocket_protocol.c web.c assets.c ssl.c
SRCS = main.c config.c tunnel.c terminal.c vnc.c rdp.c websocket.c websocket_protocol.c web.c assets.c ssl.c
OBJS = $(SRCS:.c=.o)
# Target
......@@ -87,9 +87,10 @@ config.o: config.c config.h
tunnel.o: tunnel.c tunnel.h websocket.h
terminal.o: terminal.c terminal.h config.h
vnc.o: vnc.c vnc.h config.h
rdp.o: rdp.c rdp.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h
websocket_protocol.o: websocket_protocol.c websocket_protocol.h
web.o: web.c web.h terminal.h vnc.h assets.h websocket.h html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h html_pages/users_page.h
web.o: web.c web.h terminal.h vnc.h rdp.h assets.h websocket.h html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h html_pages/rdp_page.h html_pages/users_page.h
assets.o: assets.c assets.h
ssl.o: ssl.c ssl.h
......@@ -100,7 +101,7 @@ image_data.h: embed_assets.sh
./embed_assets.sh
# HTML page generation
html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h: embed_assets.sh
html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h html_pages/rdp_page.h: embed_assets.sh
./embed_assets.sh
clean:
......
......@@ -172,6 +172,59 @@ static const char *xterm_addon_fit_js = "";
EOF
fi
# Embed mstsc.js
if [ -f templates/mstsc.min.js ]; then
echo "Embedding mstsc.min.js..."
mkdir -p html_pages
HEADER_FILE="html_pages/mstsc_page.h"
cat > "$HEADER_FILE" << EOF
/**
* mstsc.js library for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MSTSC_PAGE_H
#define MSTSC_PAGE_H
// mstsc.js library
const char *mstsc_js =
EOF
# Process the JS file, escape quotes and backslashes
sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$/\\n"/' templates/mstsc.min.js >> "$HEADER_FILE"
# Close the string and header
cat >> "$HEADER_FILE" << EOF
;
#endif /* MSTSC_PAGE_H */
EOF
else
echo "Warning: mstsc.min.js not found, creating empty placeholder"
mkdir -p html_pages
cat > html_pages/mstsc_page.h << 'EOF'
#ifndef MSTSC_PAGE_H
#define MSTSC_PAGE_H
static const char *mstsc_js = "";
#endif /* MSTSC_PAGE_H */
EOF
fi
# Embed noVNC CSS
if [ -f templates/novnc.css ]; then
echo "Embedding novnc.css..."
......
This diff is collapsed.
This diff is collapsed.
/**
* index page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef INDEX_PAGE_H
#define INDEX_PAGE_H
// index page HTML template
static const char *index_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Dashboard - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">%s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"%s\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row\">\n"
" <div class=\"col-md-8\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-server\"></i> Registered Clients\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <div id=\"client-list\">%s</div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"\n"
" <div class=\"col-md-4\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-cogs\"></i> Quick Actions\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" %s\n"
" <button class=\"btn btn-outline-secondary btn-sm w-100\" onclick=\"location.reload()\">\n"
" <i class=\"fas fa-sync\"></i> Refresh Status\n"
" </button>\n"
" </div>\n"
" </div>\n"
"\n"
" <div class=\"card mt-3\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-info-circle\"></i> System Info\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <p class=\"mb-1\"><strong>WebSocket Port:</strong> <span id=\"websocket-port\">%d</span></p>\n"
" <p class=\"mb-1\"><strong>Domain:</strong> <span id=\"domain\">%s</span></p>\n"
" <p class=\"mb-0\"><strong>Connected Clients:</strong> <span id=\"client-count\">%d</span></p>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"function showNotification(message, type = 'info') {\n"
" const notificationArea = document.getElementById('notification-area');\n"
" const notification = document.createElement('div');\n"
" notification.className = `alert alert-${type} alert-dismissible fade show`;\n"
" notification.innerHTML = `${message}<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\"></button>`;\n"
" notificationArea.appendChild(notification);\n"
" setTimeout(() => {\n"
" if (notification.parentNode) {\n"
" notification.remove();\n"
" }\n"
" }, 5000);\n"
"}\n"
"\n"
"function updateClients() {\n"
" fetch('/api/clients')\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" // Update client count\n"
" document.getElementById('client-count').textContent = data.count;\n"
"\n"
" // Generate HTML for client list\n"
" let clientListHtml = '';\n"
" if (data.count === 0) {\n"
" clientListHtml = '<div class=\"text-center py-5\"><i class=\"fas fa-server fa-4x text-muted mb-3\"></i><h4 class=\"text-muted\">No clients registered</h4><p class=\"text-muted\">Clients will appear here when they register.</p></div>';\n"
" } else {\n"
" for (const [clientId, clientData] of Object.entries(data.clients)) {\n"
" const statusIcon = clientData.status === 'connected' ? 'fa-desktop text-success' : 'fa-server text-warning';\n"
" const statusText = clientData.status === 'connected' ? 'Connected' : 'Registered';\n"
" const services = clientData.services;\n"
" let actionsHtml = '';\n"
" if (services.includes('ssh')) {\n"
" actionsHtml += `<a href=\"/terminal/${clientId}\" class=\"btn btn-primary btn-sm me-1\"><i class=\"fas fa-terminal\"></i> SSH</a>`;\n"
" }\n"
" if (services.includes('vnc')) {\n"
" actionsHtml += `<a href=\"/vnc/${clientId}\" class=\"btn btn-info btn-sm me-1\"><i class=\"fas fa-desktop\"></i> VNC</a>`;\n"
" }\n"
" clientListHtml += `\n"
" <div class=\"col-md-4 mb-3\">\n"
" <div class=\"card client-card h-100\">\n"
" <div class=\"card-body text-center\">\n"
" <i class=\"fas ${statusIcon} fa-3x mb-3\"></i>\n"
" <h5 class=\"card-title\">${clientId}</h5>\n"
" <p class=\"card-text text-muted\">${statusText}</p>\n"
" <p class=\"card-text small\">Services: ${services}</p>\n"
" <div class=\"d-flex justify-content-center\">${actionsHtml}</div>\n"
" </div>\n"
" </div>\n"
" </div>`;\n"
" }\n"
" }\n"
"\n"
" // Update client list only if changed\n"
" const clientListDiv = document.getElementById('client-list');\n"
" if (clientListDiv.innerHTML !== clientListHtml) {\n"
" clientListDiv.innerHTML = clientListHtml;\n"
" }\n"
" })\n"
" .catch(error => {\n"
" console.log('Error fetching client data:', error);\n"
" });\n"
"}\n"
"\n"
"// Update every 5 seconds\n"
"setInterval(updateClients, 5000);\n"
"\n"
"// Initial update after 1 second\n"
"setTimeout(updateClients, 1000);\n"
"</script>\n"
"</body>\n"
"</html>\n";
#endif /* INDEX_PAGE_H */
/**
* login page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef LOGIN_PAGE_H
#define LOGIN_PAGE_H
// login page HTML template
static const char *login_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Login - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"</div></nav>\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row justify-content-center\">\n"
" <div class=\"col-md-6\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\"><i class=\"fas fa-sign-in-alt\"></i> Login</h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <form method=\"post\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"username\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"username\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"password\" class=\"form-label\">Password</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"password\" name=\"password\" required>\n"
" </div>\n"
" <button type=\"submit\" class=\"btn btn-primary\">\n"
" <i class=\"fas fa-sign-in-alt\"></i> Login\n"
" </button>\n"
" </form>\n"
" <div class=\"mt-3\">\n"
" <small class=\"text-muted\">\n"
" Default credentials: admin / admin123\n"
" </small>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"</body>\n"
"</html>\n";
#endif /* LOGIN_PAGE_H */
#ifndef NOVNC_BASE64_JS_PAGE_H
#define NOVNC_BASE64_JS_PAGE_H
extern const char *novnc_base64_js;
#endif /* NOVNC_BASE64_JS_PAGE_H */
#ifndef NOVNC_DECODERS_COPYRECT_JS_PAGE_H
#define NOVNC_DECODERS_COPYRECT_JS_PAGE_H
extern const char *novnc_decoders_copyrect_js;
#endif /* NOVNC_DECODERS_COPYRECT_JS_PAGE_H */
#ifndef NOVNC_DECODERS_HEXTILE_JS_PAGE_H
#define NOVNC_DECODERS_HEXTILE_JS_PAGE_H
extern const char *novnc_decoders_hextile_js;
#endif /* NOVNC_DECODERS_HEXTILE_JS_PAGE_H */
#ifndef NOVNC_DECODERS_JPEG_JS_PAGE_H
#define NOVNC_DECODERS_JPEG_JS_PAGE_H
extern const char *novnc_decoders_jpeg_js;
#endif /* NOVNC_DECODERS_JPEG_JS_PAGE_H */
#ifndef NOVNC_DECODERS_RAW_JS_PAGE_H
#define NOVNC_DECODERS_RAW_JS_PAGE_H
extern const char *novnc_decoders_raw_js;
#endif /* NOVNC_DECODERS_RAW_JS_PAGE_H */
#ifndef NOVNC_DECODERS_RRE_JS_PAGE_H
#define NOVNC_DECODERS_RRE_JS_PAGE_H
extern const char *novnc_decoders_rre_js;
#endif /* NOVNC_DECODERS_RRE_JS_PAGE_H */
#ifndef NOVNC_DECODERS_TIGHT_JS_PAGE_H
#define NOVNC_DECODERS_TIGHT_JS_PAGE_H
extern const char *novnc_decoders_tight_js;
#endif /* NOVNC_DECODERS_TIGHT_JS_PAGE_H */
#ifndef NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H
#define NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H
extern const char *novnc_decoders_tightpng_js;
#endif /* NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H */
#ifndef NOVNC_DECODERS_ZRLE_JS_PAGE_H
#define NOVNC_DECODERS_ZRLE_JS_PAGE_H
extern const char *novnc_decoders_zrle_js;
#endif /* NOVNC_DECODERS_ZRLE_JS_PAGE_H */
#ifndef NOVNC_DEFLATOR_JS_PAGE_H
#define NOVNC_DEFLATOR_JS_PAGE_H
extern const char *novnc_deflator_js;
#endif /* NOVNC_DEFLATOR_JS_PAGE_H */
#ifndef NOVNC_DES_JS_PAGE_H
#define NOVNC_DES_JS_PAGE_H
extern const char *novnc_des_js;
#endif /* NOVNC_DES_JS_PAGE_H */
#ifndef NOVNC_DISPLAY_JS_PAGE_H
#define NOVNC_DISPLAY_JS_PAGE_H
extern const char *novnc_display_js;
#endif /* NOVNC_DISPLAY_JS_PAGE_H */
#ifndef NOVNC_ENCODINGS_JS_PAGE_H
#define NOVNC_ENCODINGS_JS_PAGE_H
extern const char *novnc_encodings_js;
#endif /* NOVNC_ENCODINGS_JS_PAGE_H */
#ifndef NOVNC_INFLATOR_JS_PAGE_H
#define NOVNC_INFLATOR_JS_PAGE_H
extern const char *novnc_inflator_js;
#endif /* NOVNC_INFLATOR_JS_PAGE_H */
#ifndef NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H
#define NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H
extern const char *novnc_input_domkeytable_js;
#endif /* NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H */
#ifndef NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H
#define NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H
extern const char *novnc_input_fixedkeys_js;
#endif /* NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H */
#ifndef NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H
#define NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H
extern const char *novnc_input_gesturehandler_js;
#endif /* NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYBOARD_JS_PAGE_H
#define NOVNC_INPUT_KEYBOARD_JS_PAGE_H
extern const char *novnc_input_keyboard_js;
#endif /* NOVNC_INPUT_KEYBOARD_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYSYM_JS_PAGE_H
#define NOVNC_INPUT_KEYSYM_JS_PAGE_H
extern const char *novnc_input_keysym_js;
#endif /* NOVNC_INPUT_KEYSYM_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H
#define NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H
extern const char *novnc_input_keysymdef_js;
#endif /* NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H */
#ifndef NOVNC_INPUT_UTIL_JS_PAGE_H
#define NOVNC_INPUT_UTIL_JS_PAGE_H
extern const char *novnc_input_util_js;
#endif /* NOVNC_INPUT_UTIL_JS_PAGE_H */
#ifndef NOVNC_INPUT_VKEYS_JS_PAGE_H
#define NOVNC_INPUT_VKEYS_JS_PAGE_H
extern const char *novnc_input_vkeys_js;
#endif /* NOVNC_INPUT_VKEYS_JS_PAGE_H */
#ifndef NOVNC_INPUT_XTSCANCODES_JS_PAGE_H
#define NOVNC_INPUT_XTSCANCODES_JS_PAGE_H
extern const char *novnc_input_xtscancodes_js;
#endif /* NOVNC_INPUT_XTSCANCODES_JS_PAGE_H */
#ifndef NOVNC_RA2_JS_PAGE_H
#define NOVNC_RA2_JS_PAGE_H
extern const char *novnc_ra2_js;
#endif /* NOVNC_RA2_JS_PAGE_H */
#ifndef NOVNC_RFB_JS_PAGE_H
#define NOVNC_RFB_JS_PAGE_H
extern const char *novnc_rfb_js;
#endif /* NOVNC_RFB_JS_PAGE_H */
#ifndef NOVNC_UTIL_BROWSER_JS_PAGE_H
#define NOVNC_UTIL_BROWSER_JS_PAGE_H
extern const char *novnc_util_browser_js;
#endif /* NOVNC_UTIL_BROWSER_JS_PAGE_H */
#ifndef NOVNC_UTIL_CURSOR_JS_PAGE_H
#define NOVNC_UTIL_CURSOR_JS_PAGE_H
extern const char *novnc_util_cursor_js;
#endif /* NOVNC_UTIL_CURSOR_JS_PAGE_H */
#ifndef NOVNC_UTIL_ELEMENT_JS_PAGE_H
#define NOVNC_UTIL_ELEMENT_JS_PAGE_H
extern const char *novnc_util_element_js;
#endif /* NOVNC_UTIL_ELEMENT_JS_PAGE_H */
#ifndef NOVNC_UTIL_EVENTS_JS_PAGE_H
#define NOVNC_UTIL_EVENTS_JS_PAGE_H
extern const char *novnc_util_events_js;
#endif /* NOVNC_UTIL_EVENTS_JS_PAGE_H */
#ifndef NOVNC_UTIL_EVENTTARGET_JS_PAGE_H
#define NOVNC_UTIL_EVENTTARGET_JS_PAGE_H
extern const char *novnc_util_eventtarget_js;
#endif /* NOVNC_UTIL_EVENTTARGET_JS_PAGE_H */
#ifndef NOVNC_UTIL_INT_JS_PAGE_H
#define NOVNC_UTIL_INT_JS_PAGE_H
extern const char *novnc_util_int_js;
#endif /* NOVNC_UTIL_INT_JS_PAGE_H */
#ifndef NOVNC_UTIL_LOGGING_JS_PAGE_H
#define NOVNC_UTIL_LOGGING_JS_PAGE_H
extern const char *novnc_util_logging_js;
#endif /* NOVNC_UTIL_LOGGING_JS_PAGE_H */
#ifndef NOVNC_UTIL_MD5_JS_PAGE_H
#define NOVNC_UTIL_MD5_JS_PAGE_H
extern const char *novnc_util_md5_js;
#endif /* NOVNC_UTIL_MD5_JS_PAGE_H */
#ifndef NOVNC_UTIL_STRINGS_JS_PAGE_H
#define NOVNC_UTIL_STRINGS_JS_PAGE_H
extern const char *novnc_util_strings_js;
#endif /* NOVNC_UTIL_STRINGS_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_adler32_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_constants_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_crc32_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_deflate_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inffast_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inflate_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inftrees_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_messages_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_trees_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_zstream_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H */
#ifndef NOVNC_WEBSOCK_JS_PAGE_H
#define NOVNC_WEBSOCK_JS_PAGE_H
extern const char *novnc_websock_js;
#endif /* NOVNC_WEBSOCK_JS_PAGE_H */
This diff is collapsed.
/**
* users page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef USERS_PAGE_H
#define USERS_PAGE_H
// users page HTML template
static const char *users_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Users - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">%s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"<div class=\"container mt-4\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header d-flex justify-content-between align-items-center\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-users\"></i> User Management\n"
" </h3>\n"
" <div>\n"
" <a href=\"/\" class=\"btn btn-outline-secondary btn-sm me-2\">\n"
" <i class=\"fas fa-home\"></i> Back to Home\n"
" </a>\n"
" <button class=\"btn btn-primary btn-sm\" data-bs-toggle=\"modal\" data-bs-target=\"#addUserModal\">\n"
" <i class=\"fas fa-plus\"></i> Add User\n"
" </button>\n"
" </div>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <div class=\"table-responsive\">\n"
" <table class=\"table table-striped\">\n"
" <thead>\n"
" <tr>\n"
" <th>Username</th>\n"
" <th>Role</th>\n"
" <th>Actions</th>\n"
" </tr>\n"
" </thead>\n"
" <tbody>\n"
" %s\n"
" </tbody>\n"
" </table>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<!-- Add User Modal -->\n"
"<div class=\"modal fade\" id=\"addUserModal\" tabindex=\"-1\">\n"
" <div class=\"modal-dialog\">\n"
" <div class=\"modal-content\">\n"
" <div class=\"modal-header\">\n"
" <h5 class=\"modal-title\">Add New User</h5>\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>\n"
" </div>\n"
" <form id=\"addUserForm\">\n"
" <div class=\"modal-body\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"addUsername\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"addUsername\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"addPassword\" class=\"form-label\">Password</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"addPassword\" name=\"password\" required>\n"
" </div>\n"
" <div class=\"mb-3 form-check\">\n"
" <input type=\"checkbox\" class=\"form-check-input\" id=\"addIsAdmin\" name=\"is_admin\">\n"
" <label class=\"form-check-label\" for=\"addIsAdmin\">Administrator</label>\n"
" </div>\n"
" </div>\n"
" <div class=\"modal-footer\">\n"
" <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button>\n"
" <button type=\"submit\" class=\"btn btn-primary\">Add User</button>\n"
" </div>\n"
" </form>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<!-- Edit User Modal -->\n"
"<div class=\"modal fade\" id=\"editUserModal\" tabindex=\"-1\">\n"
" <div class=\"modal-dialog\">\n"
" <div class=\"modal-content\">\n"
" <div class=\"modal-header\">\n"
" <h5 class=\"modal-title\">Edit User</h5>\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>\n"
" </div>\n"
" <form id=\"editUserForm\">\n"
" <input type=\"hidden\" id=\"editUserId\" name=\"user_id\">\n"
" <div class=\"modal-body\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"editUsername\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"editUsername\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"editPassword\" class=\"form-label\">New Password (leave empty to keep current)</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"editPassword\" name=\"password\">\n"
" </div>\n"
" <div class=\"mb-3 form-check\">\n"
" <input type=\"checkbox\" class=\"form-check-input\" id=\"editIsAdmin\" name=\"is_admin\">\n"
" <label class=\"form-check-label\" for=\"editIsAdmin\">Administrator</label>\n"
" </div>\n"
" </div>\n"
" <div class=\"modal-footer\">\n"
" <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button>\n"
" <button type=\"submit\" class=\"btn btn-primary\">Update User</button>\n"
" </div>\n"
" </form>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"function editUser(userId, username, isAdmin) {\n"
" document.getElementById('editUserId').value = userId;\n"
" document.getElementById('editUsername').value = username;\n"
" document.getElementById('editPassword').value = '';\n"
" document.getElementById('editIsAdmin').checked = isAdmin;\n"
" new bootstrap.Modal(document.getElementById('editUserModal')).show();\n"
"}\n"
"\n"
"function deleteUser(userId, username) {\n"
" if (confirm(`Are you sure you want to delete user \"${username}\"?`)) {\n"
" fetch(`/delete_user/${userId}`, {\n"
" method: 'POST',\n"
" headers: {'Content-Type': 'application/x-www-form-urlencoded'}\n"
" })\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) location.reload();\n"
" else alert('Error: ' + data.error);\n"
" });\n"
" }\n"
"}\n"
"\n"
"document.getElementById('addUserForm').addEventListener('submit', function(e) {\n"
" e.preventDefault();\n"
" const formData = new FormData(this);\n"
" fetch('/add_user', {method: 'POST', body: formData})\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) {\n"
" bootstrap.Modal.getInstance(document.getElementById('addUserModal')).hide();\n"
" location.reload();\n"
" } else alert('Error: ' + data.error);\n"
" });\n"
"});\n"
"\n"
"document.getElementById('editUserForm').addEventListener('submit', function(e) {\n"
" e.preventDefault();\n"
" const formData = new FormData(this);\n"
" const userId = document.getElementById('editUserId').value;\n"
" fetch(`/edit_user/${userId}`, {method: 'POST', body: formData})\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) {\n"
" bootstrap.Modal.getInstance(document.getElementById('editUserModal')).hide();\n"
" location.reload();\n"
" } else alert('Error: ' + data.error);\n"
" });\n"
"});\n"
"</script>\n"
"</body>\n"
"</html>\n";
#endif /* USERS_PAGE_H */
This diff is collapsed.
/**
* xterm-addon-fit.js library for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef XTERM_ADDON_PAGE_H
#define XTERM_ADDON_PAGE_H
// xterm-addon-fit.js library
const char *xterm_addon_fit_js =
"!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{\"use strict\";var e={};return(()=>{var t=e;Object.defineProperty(t,\"__esModule\",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue(\"height\")),s=Math.max(0,parseInt(i.getPropertyValue(\"width\"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue(\"padding-top\"))+parseInt(n.getPropertyValue(\"padding-bottom\"))),a=s-(parseInt(n.getPropertyValue(\"padding-right\"))+parseInt(n.getPropertyValue(\"padding-left\")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));\n"
"//# sourceMappingURL=xterm-addon-fit.js.map\n";
#endif /* XTERM_ADDON_PAGE_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
......@@ -18,8 +18,8 @@
#include "html_pages/novnc_input_fixedkeys_js_page.h"
#include "html_pages/novnc_input_gesturehandler_js_page.h"
#include "html_pages/novnc_input_keyboard_js_page.h"
#include "html_pages/novnc_input_keysym_js_page.h"
#include "html_pages/novnc_input_keysymdef_js_page.h"
#include "html_pages/novnc_input_keysym_js_page.h"
#include "html_pages/novnc_input_util_js_page.h"
#include "html_pages/novnc_input_vkeys_js_page.h"
#include "html_pages/novnc_input_xtscancodes_js_page.h"
......
/**
* RDP and WebSocket proxy handling implementation for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <signal.h>
#include <uuid/uuid.h>
#include <pthread.h>
#include "rdp.h"
#include "config.h"
// Thread function to read pipe output
static void *read_pipe_output(void *arg) {
rdp_session_t *session = (rdp_session_t *)arg;
char buffer[1024];
fd_set readfds;
struct timeval tv;
while (1) {
FD_ZERO(&readfds);
FD_SET(session->read_fd, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 100000; // 100ms timeout
int ret = select(session->read_fd + 1, &readfds, NULL, NULL, &tv);
if (ret < 0) break;
if (ret == 0) continue; // timeout
if (FD_ISSET(session->read_fd, &readfds)) {
ssize_t bytes_read = read(session->read_fd, buffer, sizeof(buffer));
if (bytes_read <= 0) break;
if (session->debug_rdp) {
printf("RDP: Received %zd bytes from pipe\n", bytes_read);
// Debug: print first 20 bytes in hex
printf("RDP received data (first 20 bytes): ");
for (ssize_t i = 0; i < bytes_read && i < 20; i++) {
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
}
// 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);
}
}
return NULL;
}
rdp_session_t *rdp_create_session(const wssshd_config_t *config, const char *client_id, const char *username, bool debug_rdp) {
rdp_session_t *session = calloc(1, sizeof(rdp_session_t));
if (!session) return NULL;
// Generate UUID for request_id
uuid_t uuid;
uuid_generate(uuid);
uuid_unparse(uuid, session->request_id);
// Copy client_id and username
if (client_id) {
strncpy(session->client_id, client_id, sizeof(session->client_id) - 1);
}
if (username) {
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;
session->debug_rdp = debug_rdp;
pthread_mutex_init(&session->output_mutex, NULL);
// Create pipes for bidirectional communication
int stdout_pipe[2]; // parent reads from child stdout
int stdin_pipe[2]; // parent writes to child stdin
if (pipe(stdout_pipe) < 0 || pipe(stdin_pipe) < 0) {
if (stdout_pipe[0] >= 0) close(stdout_pipe[0]);
if (stdout_pipe[1] >= 0) close(stdout_pipe[1]);
if (stdin_pipe[0] >= 0) close(stdin_pipe[0]);
if (stdin_pipe[1] >= 0) close(stdin_pipe[1]);
free(session->output_buffer);
free(session);
return NULL;
}
session->read_fd = stdout_pipe[0]; // parent reads from child stdout
session->write_fd = stdin_pipe[1]; // parent writes to child stdin
// Build command: wsssht --pipe --wssshd-port 9898 rdp://{client_id}@mbetter.nexlab.net
char command[1024];
snprintf(command, sizeof(command),
"wsssht --pipe --wssshd-port %d rdp://%s@%s",
config->port, client_id, config->domain);
// Fork and execute
pid_t pid = fork();
if (pid < 0) {
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stdin_pipe[0]);
close(stdin_pipe[1]);
free(session->output_buffer);
free(session);
return NULL;
}
if (pid == 0) { // Child process
// Redirect stdout to stdout_pipe
dup2(stdout_pipe[1], 1);
// Redirect stdin from stdin_pipe
dup2(stdin_pipe[0], 0);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stdin_pipe[0]);
close(stdin_pipe[1]);
// Execute command
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1); // Should not reach here
}
// Parent process
close(stdout_pipe[1]); // close write end of stdout pipe
close(stdin_pipe[0]); // close read end of stdin pipe
session->proc_pid = pid;
// Start output reading thread
pthread_t thread;
pthread_create(&thread, NULL, read_pipe_output, session);
pthread_detach(thread);
return session;
}
void rdp_free_session(rdp_session_t *session) {
if (!session) return;
if (session->read_fd >= 0) {
close(session->read_fd);
session->read_fd = -1;
}
if (session->write_fd >= 0) {
close(session->write_fd);
session->write_fd = -1;
}
if (session->output_buffer) {
free(session->output_buffer);
session->output_buffer = NULL;
}
pthread_mutex_destroy(&session->output_mutex);
free(session);
}
bool rdp_send_data(rdp_session_t *session, const char *data, size_t len) {
if (!session || session->write_fd < 0) return false;
// Check if process is still running
if (waitpid(session->proc_pid, NULL, WNOHANG) > 0) {
return false;
}
if (session->debug_rdp) {
printf("RDP: Sending %zu bytes to pipe\n", len);
// Debug: print first 20 bytes in hex
printf("RDP send data (first 20 bytes): ");
for (size_t i = 0; i < len && i < 20; i++) {
printf("%02x ", (unsigned char)data[i]);
}
printf("\n");
}
ssize_t written = write(session->write_fd, data, len);
if (session->debug_rdp) {
printf("RDP: Wrote %zd bytes to pipe\n", written);
}
return written > 0;
}
char *rdp_get_output(rdp_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 rdp_disconnect(rdp_session_t *session) {
if (!session) return false;
if (session->proc_pid > 0) {
kill(session->proc_pid, SIGTERM);
// Wait for graceful termination
for (int i = 0; i < 30; i++) { // Wait up to 3 seconds
if (waitpid(session->proc_pid, NULL, WNOHANG) > 0) {
break;
}
usleep(100000); // 100ms
}
// Force kill if still running
if (waitpid(session->proc_pid, NULL, WNOHANG) == 0) {
kill(session->proc_pid, SIGKILL);
waitpid(session->proc_pid, NULL, 0);
}
}
return true;
}
bool rdp_is_running(rdp_session_t *session) {
if (!session || session->proc_pid <= 0) return false;
return waitpid(session->proc_pid, NULL, WNOHANG) == 0;
}
\ No newline at end of file
/**
* noVNC CSS for wssshd
* RDP and WebSocket proxy handling implementation for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
......@@ -17,11 +17,33 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef NOVNC_CSS_PAGE_H
#define NOVNC_CSS_PAGE_H
#ifndef RDP_H
#define RDP_H
// noVNC CSS
const char *novnc_css =
"Couldn't find the requested file /lib/rfb.css in @novnc/novnc.\n";
#include <stdbool.h>
#include <uuid/uuid.h>
#include "config.h"
#endif /* NOVNC_CSS_PAGE_H */
typedef struct {
char request_id[37];
char client_id[256];
char username[50];
int proc_pid;
int read_fd; // pipe read end
int write_fd; // pipe write end
bool active;
bool debug_rdp;
char *output_buffer;
size_t output_size;
size_t output_used;
pthread_mutex_t output_mutex;
} rdp_session_t;
rdp_session_t *rdp_create_session(const wssshd_config_t *config, const char *client_id, const char *username, bool debug_rdp);
void rdp_free_session(rdp_session_t *session);
bool rdp_send_data(rdp_session_t *session, const char *data, size_t len);
char *rdp_get_output(rdp_session_t *session, size_t *len);
bool rdp_disconnect(rdp_session_t *session);
bool rdp_is_running(rdp_session_t *session);
#endif /* RDP_H */
\ No newline at end of file
/*
* Copyright (c) 2015 Sylvain Peyrefitte
*
* This file is part of mstsc.js.
*
* mstsc.j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
/**
* Use for domain declaration
*/
Mstsc = function () {
}
Mstsc.prototype = {
// shortcut
$ : function (id) {
return document.getElementById(id);
},
/**
* Compute screen offset for a target element
* @param el {DOM element}
* @return {top : {integer}, left {integer}}
*/
elementOffset : function (el) {
var x = 0;
var y = 0;
while (el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop )) {
x += el.offsetLeft - el.scrollLeft;
y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: y, left: x };
},
/**
* Try to detect browser
* @returns {String} [firefox|chrome|ie]
*/
browser : function () {
if (typeof InstallTrigger !== 'undefined') {
return 'firefox';
}
if (!!window.chrome) {
return 'chrome';
}
if (!!document.docuemntMode) {
return 'ie';
}
return null;
},
/**
* Try to detect language
* @returns
*/
locale : function () {
return window.navigator.userLanguage || window.navigator.language;
}
}
})();
this.Mstsc = new Mstsc();
\ No newline at end of file
......@@ -986,6 +986,11 @@ class RFB extends EventTargetMixin {
return;
}
// Debug: log received data
const debugLen = Math.min(50, this._sock.rQlen);
const debugData = new Uint8Array(this._sock.rQ.buffer, this._sock.rQi, debugLen);
console.log("[VNC-CLIENT] Received data (first", debugLen, "bytes):", Array.from(debugData).map(b => b.toString(16).padStart(2, '0')).join(' '));
console.log("[VNC-CLIENT] Processing message in state:", this._rfbConnectionState);
switch (this._rfbConnectionState) {
......
This diff is collapsed.
......@@ -66,12 +66,13 @@
}
.vnc-container.full-size {
overflow: auto;
max-height: none;
max-height: calc(100vh - 200px);
height: auto;
}
.vnc-container.full-size #noVNC_screen {
min-width: fit-content;
min-height: fit-content;
height: auto;
}
/* GNOME-like window decorations */
.vnc-window {
......
[wssshd]
host = 0.0.0.0
port = 9898
password = mbetter4ntan1
domain = mbetter.nexlab.net
web-host = 0.0.0.0
web-port = 9899
web-https = false
[wssshd]
host = 127.0.0.1
port = 19998
password = test123
domain = test.local
......@@ -38,7 +38,7 @@ static void *read_pipe_output(void *arg) {
fd_set readfds;
struct timeval tv;
while (1) {
while (!session->thread_stop) {
FD_ZERO(&readfds);
FD_SET(session->read_fd, &readfds);
......@@ -115,6 +115,7 @@ vnc_session_t *vnc_create_session(const wssshd_config_t *config, const char *cli
session->output_size = 4096;
session->output_used = 0;
session->debug_vnc = debug_vnc;
session->thread_stop = 0;
pthread_mutex_init(&session->output_mutex, NULL);
// Create pipes for bidirectional communication
......@@ -183,6 +184,9 @@ vnc_session_t *vnc_create_session(const wssshd_config_t *config, const char *cli
void vnc_free_session(vnc_session_t *session) {
if (!session) return;
// Signal thread to stop
session->thread_stop = 1;
if (session->read_fd >= 0) {
close(session->read_fd);
session->read_fd = -1;
......@@ -192,6 +196,9 @@ void vnc_free_session(vnc_session_t *session) {
session->write_fd = -1;
}
// Wait a bit for thread to exit
usleep(200000); // 200ms
if (session->output_buffer) {
free(session->output_buffer);
session->output_buffer = NULL;
......
......@@ -33,6 +33,7 @@ typedef struct {
int write_fd; // pipe write end
bool active;
bool debug_vnc;
volatile int thread_stop;
char *output_buffer;
size_t output_size;
size_t output_used;
......
This diff is collapsed.
......@@ -1491,7 +1491,11 @@ void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_i
tunnel_t *tunnel = find_tunnel_by_request_id(request_id);
if (tunnel) {
tunnel->active = 0;
int was_active = (active_tunnel == tunnel);
remove_tunnel(request_id);
if (was_active) {
active_tunnel = NULL;
}
if (debug) {
printf("[DEBUG - Tunnel] Tunnel %s closed\n", request_id);
fflush(stdout);
......
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