Added webproxy functionality

parent cdf196ee
......@@ -133,6 +133,18 @@ cleanup() {
fi
rm -f Makefile configure.sh.stamp
rm -rf html_pages/
# Clean plugins
if [[ -d "plugins" ]]; then
cd plugins
for plugin_dir in */; do
if [[ -d "$plugin_dir" && -f "${plugin_dir}Makefile" ]]; then
cd "$plugin_dir"
make clean >/dev/null 2>&1 || true
cd ..
fi
done
cd ..
fi
cd ..
fi
# Remove server debian artifacts
......@@ -211,6 +223,23 @@ build_server() {
log_info "Compiling wssshd2..."
make
# Build plugins if present
if [[ -d "plugins" ]]; then
log_info "Building plugins..."
cd plugins
for plugin_dir in */; do
if [[ -d "$plugin_dir" && -f "${plugin_dir}Makefile" ]]; then
plugin_name=$(basename "$plugin_dir")
log_info "Building plugin: $plugin_name"
cd "$plugin_dir"
make
cd ..
fi
done
cd ..
log_success "Plugins built successfully"
fi
cd ..
log_success "wssshd2 server built successfully"
}
......
......@@ -31,6 +31,8 @@
#include "novnc_asset_map.c"
#include "rdp_assets.h"
#include "rdp_asset_map.c"
#include "plugin_assets.h"
#include "plugin_asset_map.c"
// HTML pages are now defined in separate header files in html_pages/ directory
// This file now only contains fallback definitions for compatibility
......@@ -81,6 +83,9 @@ const char *get_embedded_asset(const char *path, size_t *size) {
} else if (strncmp(path, "/rdpwasm/", 9) == 0) {
const char *content = get_rdp_asset(path, size);
return content;
} else if (strncmp(path, "/plugin/", 8) == 0) {
const char *content = get_plugin_asset(path, size);
return content;
}
return NULL;
......
......@@ -58,14 +58,14 @@ cat > Makefile << 'EOF'
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -pthread
LDFLAGS = -lssl -lcrypto -lm -luuid -lsqlite3
LDFLAGS = -lssl -lcrypto -lm -luuid -lsqlite3 -ldl
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 rdp.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 plugin.c web_proxy.c
OBJS = $(SRCS:.c=.o)
# Target
......@@ -82,17 +82,19 @@ $(TARGET): $(OBJS)
$(CC) $(CFLAGS) -c $< -o $@
# Dependencies
main.o: main.c config.h websocket.h web.h
main.o: main.c config.h websocket.h web.h plugin.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
rdp.o: rdp.c rdp.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h plugin.h
websocket_protocol.o: websocket_protocol.c websocket_protocol.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
web.o: web.c web.h terminal.h vnc.h rdp.h assets.h websocket.h plugin.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
plugin.o: plugin.c plugin.h
web_proxy.o: web_proxy.c web_proxy.h websocket.h websocket_protocol.h
# Asset embedding (run before compilation)
assets.o: image_data.h
......
......@@ -651,4 +651,127 @@ EOF
fi
done
# Process plugin templates
if [ -d "plugins" ]; then
echo "Processing plugin templates..."
mkdir -p html_pages
# Generate plugin_assets.h
cat > plugin_assets.h << 'EOF'
#ifndef PLUGIN_ASSETS_H
#define PLUGIN_ASSETS_H
EOF
# Generate plugin_asset_map.c
cat > plugin_asset_map.c << 'EOF'
#include <string.h>
#include "plugin_assets.h"
const char *get_plugin_asset(const char *path, size_t *size) {
EOF
find plugins -name "*.html" | sort | while read -r template; do
if [ -f "$template" ]; then
RELPATH=$(echo "$template" | sed 's|plugins/||')
BASENAME=$(basename "$template" .html)
PLUGIN_NAME=$(echo "$template" | sed 's|plugins/\([^/]*\)/.*|\1|')
VARNAME=$(echo "${PLUGIN_NAME}_${BASENAME}" | sed 's/[^a-zA-Z0-9_]/_/g')
HEADER_FILE="html_pages/plugin_${VARNAME}_page.h"
echo "Embedding plugin template $template as plugin_${VARNAME}"
# Create header with extern
cat > "$HEADER_FILE" << EOF
#ifndef PLUGIN_${VARNAME^^}_PAGE_H
#define PLUGIN_${VARNAME^^}_PAGE_H
extern const char *plugin_${VARNAME}_html;
#endif /* PLUGIN_${VARNAME^^}_PAGE_H */
EOF
# Add to plugin_assets.h
echo "#include \"html_pages/plugin_${VARNAME}_page.h\"" >> plugin_assets.h
# Add definition to plugin_asset_map.c
echo "const char *plugin_${VARNAME}_html =" >> plugin_asset_map.c
# Process the HTML file, escape quotes and backslashes
sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$/\\n"/' "$template" >> plugin_asset_map.c
echo ";" >> plugin_asset_map.c
# Add to plugin_asset_map.c
echo " if (strcmp(path, \"/plugin/$RELPATH\") == 0) {" >> plugin_asset_map.c
echo " if (size) *size = strlen(plugin_${VARNAME}_html);" >> plugin_asset_map.c
echo " return plugin_${VARNAME}_html;" >> plugin_asset_map.c
echo " }" >> plugin_asset_map.c
fi
done
find plugins -name "*.js" | sort | while read -r jsfile; do
if [ -f "$jsfile" ]; then
RELPATH=$(echo "$jsfile" | sed 's|plugins/||')
BASENAME=$(basename "$jsfile" .js)
PLUGIN_NAME=$(echo "$jsfile" | sed 's|plugins/\([^/]*\)/.*|\1|')
VARNAME=$(echo "${PLUGIN_NAME}_${BASENAME}" | sed 's/[^a-zA-Z0-9_]/_/g')
HEADER_FILE="html_pages/plugin_${VARNAME}_page.h"
echo "Embedding plugin JS $jsfile as plugin_${VARNAME}"
# Create header with extern
cat > "$HEADER_FILE" << EOF
#ifndef PLUGIN_${VARNAME^^}_PAGE_H
#define PLUGIN_${VARNAME^^}_PAGE_H
extern const char *plugin_${VARNAME}_js;
#endif /* PLUGIN_${VARNAME^^}_PAGE_H */
EOF
# Add to plugin_assets.h
echo "#include \"html_pages/plugin_${VARNAME}_page.h\"" >> plugin_assets.h
# Add definition to plugin_asset_map.c
echo "const char *plugin_${VARNAME}_js =" >> plugin_asset_map.c
# Process the JS file, escape quotes and backslashes
sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$/\\n"/' "$jsfile" >> plugin_asset_map.c
echo ";" >> plugin_asset_map.c
# Add to plugin_asset_map.c
echo " if (strcmp(path, \"/plugin/$RELPATH\") == 0) {" >> plugin_asset_map.c
echo " if (size) *size = strlen(plugin_${VARNAME}_js);" >> plugin_asset_map.c
echo " return plugin_${VARNAME}_js;" >> plugin_asset_map.c
echo " }" >> plugin_asset_map.c
fi
done
cat >> plugin_assets.h << 'EOF'
#endif /* PLUGIN_ASSETS_H */
EOF
cat >> plugin_asset_map.c << 'EOF'
return NULL;
}
EOF
echo "Plugin templates embedded."
else
# Create empty placeholders if no plugins directory
cat > plugin_assets.h << 'EOF'
#ifndef PLUGIN_ASSETS_H
#define PLUGIN_ASSETS_H
#endif /* PLUGIN_ASSETS_H */
EOF
cat > plugin_asset_map.c << 'EOF'
#include <string.h>
#include "plugin_assets.h"
const char *get_plugin_asset(const char *path, size_t *size) {
(void)path;
(void)size;
return NULL;
}
EOF
fi
echo "Assets embedded successfully"
/**
* Plugin system implementation for wssshd2
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include "plugin.h"
// Plugin entry point symbol name
#define PLUGIN_INTERFACE_SYMBOL "plugin_interface"
// Global plugin list
#define MAX_PLUGINS 10
static void *loaded_plugins[MAX_PLUGINS];
static plugin_interface_t *plugin_interfaces[MAX_PLUGINS];
static plugin_context_t plugin_contexts[MAX_PLUGINS];
static int plugin_count = 0;
// Global state and config for plugins
static wssshd_state_t *global_state = NULL;
static const wssshd_config_t *global_config = NULL;
// Plugin initialization
int plugin_init(wssshd_state_t *state) {
global_state = state;
return plugin_system_init(state, NULL) ? 0 : -1;
}
// Plugin cleanup
void plugin_cleanup(void) {
plugin_system_cleanup();
}
// Plugin system initialization
bool plugin_system_init(wssshd_state_t *state, const wssshd_config_t *config) {
global_state = state;
global_config = config;
plugin_count = 0;
// Try to load plugins from default directories
const char *plugin_dirs[] = {"./plugins", "/usr/local/lib/wssshd/plugins", NULL};
for (int i = 0; plugin_dirs[i]; i++) {
if (plugin_load_from_directory(plugin_dirs[i])) {
printf("[PLUGIN] Loaded plugins from %s\n", plugin_dirs[i]);
break;
}
}
return true;
}
// Plugin system cleanup
void plugin_system_cleanup(void) {
for (int i = 0; i < plugin_count; i++) {
if (plugin_interfaces[i] && plugin_interfaces[i]->cleanup) {
plugin_interfaces[i]->cleanup(&plugin_contexts[i]);
}
if (loaded_plugins[i]) {
dlclose(loaded_plugins[i]);
}
}
plugin_count = 0;
}
// Load plugins from directory (recursive)
bool plugin_load_from_directory(const char *plugin_dir) {
if (!plugin_dir) return false;
DIR *dir = opendir(plugin_dir);
if (!dir) return false;
struct dirent *entry;
bool found_plugins = false;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s/%s", plugin_dir, entry->d_name);
struct stat st;
if (stat(full_path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
// Recurse into subdirectory
if (plugin_load_from_directory(full_path)) {
found_plugins = true;
}
} else if (S_ISREG(st.st_mode) && strstr(entry->d_name, ".so")) {
// Load .so file
plugin_context_t ctx = {global_state, global_config, NULL, plugin_get_data, plugin_set_data};
if (plugin_load(full_path, &ctx)) {
printf("[PLUGIN] Loaded plugin: %s\n", entry->d_name);
found_plugins = true;
}
}
}
}
closedir(dir);
return found_plugins;
}
// Load a single plugin
bool plugin_load(const char *plugin_path, plugin_context_t *ctx) {
if (plugin_count >= MAX_PLUGINS) {
fprintf(stderr, "[PLUGIN] Maximum number of plugins reached\n");
return false;
}
// Load the shared library
void *handle = dlopen(plugin_path, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "[PLUGIN] Failed to load plugin %s: %s\n", plugin_path, dlerror());
return false;
}
// Get the plugin interface
plugin_interface_t *interface = (plugin_interface_t *)dlsym(handle, PLUGIN_INTERFACE_SYMBOL);
if (!interface) {
fprintf(stderr, "[PLUGIN] Plugin %s does not export %s\n", plugin_path, PLUGIN_INTERFACE_SYMBOL);
dlclose(handle);
return false;
}
// Check API version
if (interface->info.api_version != PLUGIN_API_VERSION) {
fprintf(stderr, "[PLUGIN] Plugin %s has incompatible API version %d (expected %d)\n",
plugin_path, interface->info.api_version, PLUGIN_API_VERSION);
dlclose(handle);
return false;
}
// Initialize plugin context
plugin_contexts[plugin_count] = *ctx;
// Initialize the plugin
if (interface->init && !interface->init(&plugin_contexts[plugin_count])) {
fprintf(stderr, "[PLUGIN] Plugin %s initialization failed\n", plugin_path);
dlclose(handle);
return false;
}
// Store plugin information
loaded_plugins[plugin_count] = handle;
plugin_interfaces[plugin_count] = interface;
plugin_count++;
return true;
}
// Unload a plugin
void plugin_unload(plugin_context_t *ctx) {
// Find and remove the plugin
for (int i = 0; i < plugin_count; i++) {
if (&plugin_contexts[i] == ctx) {
if (plugin_interfaces[i]->cleanup) {
plugin_interfaces[i]->cleanup(ctx);
}
dlclose(loaded_plugins[i]);
// Shift remaining plugins
for (int j = i; j < plugin_count - 1; j++) {
loaded_plugins[j] = loaded_plugins[j + 1];
plugin_interfaces[j] = plugin_interfaces[j + 1];
plugin_contexts[j] = plugin_contexts[j + 1];
}
plugin_count--;
break;
}
}
}
// Get plugin data
void *plugin_get_data(plugin_context_t *ctx) {
return ctx->plugin_data;
}
// Set plugin data
void plugin_set_data(plugin_context_t *ctx, void *data) {
ctx->plugin_data = data;
}
// Handle web requests
int plugin_handle_web_request(int client_fd, const http_request_t *req) {
for (int i = 0; i < plugin_count; i++) {
if (plugin_interfaces[i]->capabilities & PLUGIN_CAP_WEB) {
// Check web routes
for (size_t j = 0; j < plugin_interfaces[i]->web.routes_count; j++) {
const plugin_web_route_t *route = &plugin_interfaces[i]->web.routes[j];
if (strcmp(req->method, route->method) == 0 &&
strcmp(req->path, route->path) == 0) {
return route->handler(client_fd, req, &plugin_contexts[i]);
}
}
}
}
return 0; // Not handled
}
// Handle messages
int plugin_handle_message(wssshd_state_t *state, ws_connection_t *conn, const char *message, size_t message_len) {
(void)state; // Parameter not used in current implementation but kept for API consistency
for (int i = 0; i < plugin_count; i++) {
if (plugin_interfaces[i]->capabilities & PLUGIN_CAP_MESSAGE) {
// Check message handlers
for (size_t j = 0; j < plugin_interfaces[i]->message.handlers_count; j++) {
const plugin_message_handler_t *handler = &plugin_interfaces[i]->message.handlers[j];
// Simple check for message type in JSON
char type_check[256];
snprintf(type_check, sizeof(type_check), "\"type\":\"%s\"", handler->message_type);
if (strstr(message, type_check)) {
return handler->handler(message, message_len, conn, &plugin_contexts[i]);
}
}
}
}
return 0; // Not handled
}
// Create transport session
tunnel_session_t *plugin_create_transport_session(const char *protocol, plugin_context_t *ctx, const char *client_id, const char *username, bool debug) {
for (int i = 0; i < plugin_count; i++) {
if (plugin_interfaces[i]->capabilities & PLUGIN_CAP_TRANSPORT) {
// Check transport protocols
for (size_t j = 0; j < plugin_interfaces[i]->transport.protocols_count; j++) {
const plugin_transport_protocol_t *transport = &plugin_interfaces[i]->transport.protocols[j];
if (strcmp(protocol, transport->protocol_name) == 0) {
return transport->create_session(ctx, client_id, username, debug);
}
}
}
}
return NULL; // Not found
}
\ No newline at end of file
/**
* Plugin system for wssshd2
*
* 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 PLUGIN_H
#define PLUGIN_H
#include <stdbool.h>
#include <stddef.h>
#include "websocket.h"
#include "config.h"
#include "tunnel.h" // For tunnel_t
// Type aliases for plugin interface
typedef tunnel_t tunnel_session_t;
// Plugin version
#define PLUGIN_API_VERSION 1
// Forward declarations
typedef struct {
char method[10];
char path[1024];
char query[1024];
char headers[4096];
char body[4096];
int content_length;
} http_request_t;
// Plugin information
typedef struct {
const char *name;
const char *version;
const char *description;
int api_version;
} plugin_info_t;
// Forward declaration
typedef struct plugin_context plugin_context_t;
// Plugin context passed to all plugin functions
struct plugin_context {
wssshd_state_t *state;
const wssshd_config_t *config;
void *plugin_data; // Plugin-specific data
// Utility functions
void *(*get_data)(plugin_context_t *ctx);
void (*set_data)(plugin_context_t *ctx, void *data);
};
// Web route handler function type
typedef int (*web_route_handler_t)(int client_fd, const http_request_t *req, plugin_context_t *ctx);
// Web route definition
typedef struct {
const char *method; // "GET", "POST", etc.
const char *path; // Route path (e.g., "/myplugin")
web_route_handler_t handler;
} plugin_web_route_t;
// Message handler function type
typedef int (*message_handler_t)(const char *message, size_t message_len, ws_connection_t *conn, plugin_context_t *ctx);
// Message handler definition
typedef struct {
const char *message_type; // Message type to handle (e.g., "my_message")
message_handler_t handler;
} plugin_message_handler_t;
// Transport protocol handler
// Transport handler function types
typedef tunnel_session_t *(*transport_create_session_t)(plugin_context_t *ctx, const char *client_id, const char *username, bool debug);
typedef void (*transport_free_session_t)(tunnel_session_t *session);
typedef bool (*transport_is_running_t)(tunnel_session_t *session);
typedef char *(*transport_get_output_t)(tunnel_session_t *session, size_t *output_len);
typedef bool (*transport_send_data_t)(tunnel_session_t *session, const char *data, size_t len);
// Transport protocol definition
typedef struct {
const char *protocol_name; // Protocol name (e.g., "custom")
transport_create_session_t create_session;
transport_free_session_t free_session;
transport_is_running_t is_running;
transport_get_output_t get_output;
transport_send_data_t send_data;
} plugin_transport_protocol_t;
// Plugin capabilities flags
#define PLUGIN_CAP_WEB (1 << 0) // Can add web routes/pages
#define PLUGIN_CAP_MESSAGE (1 << 1) // Can handle messages
#define PLUGIN_CAP_TRANSPORT (1 << 2) // Can provide transport protocols
// Plugin interface structure
typedef struct {
// Plugin information
plugin_info_t info;
// Capabilities (bitmask of PLUGIN_CAP_*)
unsigned int capabilities;
// Initialization and cleanup
bool (*init)(plugin_context_t *ctx);
void (*cleanup)(plugin_context_t *ctx);
// Web interface (optional)
struct {
const plugin_web_route_t *routes;
size_t routes_count;
const char **template_files; // Array of template file paths
size_t template_files_count;
} web;
// Message bus (optional)
struct {
const plugin_message_handler_t *handlers;
size_t handlers_count;
} message;
// Transport protocols (optional)
struct {
const plugin_transport_protocol_t *protocols;
size_t protocols_count;
} transport;
} plugin_interface_t;
// Plugin loading functions
bool plugin_load(const char *plugin_path, plugin_context_t *ctx);
void plugin_unload(plugin_context_t *ctx);
// Plugin management
bool plugin_system_init(wssshd_state_t *state, const wssshd_config_t *config);
void plugin_system_cleanup(void);
bool plugin_load_from_directory(const char *plugin_dir);
// Plugin utility functions
void *plugin_get_data(plugin_context_t *ctx);
void plugin_set_data(plugin_context_t *ctx, void *data);
// Web interface integration
int plugin_handle_web_request(int client_fd, const http_request_t *req);
// Message bus integration
int plugin_handle_message(wssshd_state_t *state, ws_connection_t *conn, const char *message, size_t message_len);
// Transport protocol integration
tunnel_session_t *plugin_create_transport_session(const char *protocol, plugin_context_t *ctx, const char *client_id, const char *username, bool debug);
#endif /* PLUGIN_H */
\ No newline at end of file
#include <string.h>
#include "plugin_assets.h"
const char *get_plugin_asset(const char *path, size_t *size) {
const char *plugin_example_example_html =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
" <title>Example Plugin</title>\n"
" <script src=\"/plugin/example/example.js\"></script>\n"
"</head>\n"
"<body>\n"
" <h1>Example Plugin Page</h1>\n"
" <p>This is a custom page provided by the example plugin.</p>\n"
" <p>Plugin uptime: <span id=\"uptime\">0</span> seconds</p>\n"
" <button onclick=\"sendExampleMessage()\">Send Example Message</button>\n"
" <div id=\"messages\"></div>\n"
"</body>\n"
"</html>\n";
if (strcmp(path, "/plugin/example/example.html") == 0) {
if (size) *size = strlen(plugin_example_example_html);
return plugin_example_example_html;
}
const char *plugin_example_example_js =
"// Example plugin JavaScript\n"
"let ws = null;\n"
"let uptimeInterval = null;\n"
"\n"
"function initWebSocket() {\n"
" // Connect to the main WebSocket (this would need to be adapted for the actual plugin system)\n"
" // For now, this is just a demonstration\n"
" console.log('Example plugin JavaScript loaded');\n"
"}\n"
"\n"
"function sendExampleMessage() {\n"
" if (!ws || ws.readyState !== WebSocket.OPEN) {\n"
" // For demonstration, we'll just show an alert\n"
" alert('WebSocket not connected. In a real plugin, this would send a message to the server.');\n"
" return;\n"
" }\n"
"\n"
" const message = {\n"
" type: 'example_message',\n"
" data: 'Hello from the web interface!'\n"
" };\n"
"\n"
" ws.send(JSON.stringify(message));\n"
"}\n"
"\n"
"function updateUptime() {\n"
" const uptimeElement = document.getElementById('uptime');\n"
" if (uptimeElement) {\n"
" const currentUptime = parseInt(uptimeElement.textContent) || 0;\n"
" uptimeElement.textContent = currentUptime + 1;\n"
" }\n"
"}\n"
"\n"
"// Initialize when page loads\n"
"document.addEventListener('DOMContentLoaded', function() {\n"
" console.log('Example plugin page loaded');\n"
" uptimeInterval = setInterval(updateUptime, 1000);\n"
"});\n"
"\n"
"// Clean up\n"
"window.addEventListener('beforeunload', function() {\n"
" if (uptimeInterval) {\n"
" clearInterval(uptimeInterval);\n"
" }\n"
" if (ws) {\n"
" ws.close();\n"
" }\n"
"});\n";
if (strcmp(path, "/plugin/example/example.js") == 0) {
if (size) *size = strlen(plugin_example_example_js);
return plugin_example_example_js;
}
return NULL;
}
#ifndef PLUGIN_ASSETS_H
#define PLUGIN_ASSETS_H
#include "html_pages/plugin_example_example_page.h"
#include "html_pages/plugin_example_example_page.h"
#endif /* PLUGIN_ASSETS_H */
# Makefile for example plugin
CC = gcc
CFLAGS = -Wall -Wextra -O2 -fPIC -I../../..
LDFLAGS = -shared
TARGET = example.so
SRCS = plugin.c
OBJS = $(SRCS:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
install: $(TARGET)
@echo "Plugin built: $(TARGET)"
@echo "Copy $(TARGET) to the plugins directory to use it"
\ No newline at end of file
# Example Plugin for wssshd2
This is an example plugin that demonstrates all the capabilities of the wssshd2 plugin system:
1. **Web Pages**: Adds a new page at `/plugin/example`
2. **Message Bus**: Handles custom WebSocket messages of type `example_message`
3. **Transport Protocols**: Implements a simple echo transport protocol
## Building the Plugin
```bash
cd wssshd2/plugins/example
make
```
This will create `example.so` which is the plugin shared library.
## Installing the Plugin
Copy the `example.so` file to the wssshd2 plugins directory (by default, this is `/usr/local/lib/wssshd/plugins/` or `./plugins/` if running from source).
## Using the Plugin
### Web Interface
Navigate to `http://your-wssshd-server/plugin/example` to see the plugin's custom page.
### Message Bus
The plugin responds to WebSocket messages with type `example_message`. You can send messages like:
```json
{
"type": "example_message",
"data": "your data here"
}
```
The plugin will respond with:
```json
{
"type": "example_response",
"message": "Hello from plugin!",
"count": 1
}
```
### Transport Protocol
The plugin implements a simple echo transport protocol that prefixes all data with "ECHO: ".
## Plugin Structure
- `plugin.c`: Main plugin implementation with the required interface functions
- `example.html`: HTML template for the plugin's web page
- `example.js`: JavaScript for client-side functionality
- `Makefile`: Build script for the plugin
## Plugin Interface
Plugins must implement these functions:
- `int plugin_init(wssshd_state_t *state)`: Initialize the plugin
- `void plugin_cleanup(void)`: Clean up plugin resources
- `int plugin_handle_web_request(int client_fd, const http_request_t *req)`: Handle web requests
- `int plugin_handle_message(wssshd_state_t *state, ws_connection_t *conn, const char *message, size_t message_len)`: Handle WebSocket messages
- `int plugin_handle_transport(wssshd_state_t *state, const char *data, size_t data_len, char **response, size_t *response_len)`: Handle custom transport protocols
See `plugin.h` for the complete interface definition.
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Example Plugin</title>
<script src="/plugin/example/example.js"></script>
</head>
<body>
<h1>Example Plugin Page</h1>
<p>This is a custom page provided by the example plugin.</p>
<p>Plugin uptime: <span id="uptime">0</span> seconds</p>
<button onclick="sendExampleMessage()">Send Example Message</button>
<div id="messages"></div>
</body>
</html>
\ No newline at end of file
// Example plugin JavaScript
let ws = null;
let uptimeInterval = null;
function initWebSocket() {
// Connect to the main WebSocket (this would need to be adapted for the actual plugin system)
// For now, this is just a demonstration
console.log('Example plugin JavaScript loaded');
}
function sendExampleMessage() {
if (!ws || ws.readyState !== WebSocket.OPEN) {
// For demonstration, we'll just show an alert
alert('WebSocket not connected. In a real plugin, this would send a message to the server.');
return;
}
const message = {
type: 'example_message',
data: 'Hello from the web interface!'
};
ws.send(JSON.stringify(message));
}
function updateUptime() {
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
const currentUptime = parseInt(uptimeElement.textContent) || 0;
uptimeElement.textContent = currentUptime + 1;
}
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', function() {
console.log('Example plugin page loaded');
uptimeInterval = setInterval(updateUptime, 1000);
});
// Clean up
window.addEventListener('beforeunload', function() {
if (uptimeInterval) {
clearInterval(uptimeInterval);
}
if (ws) {
ws.close();
}
});
\ No newline at end of file
/**
* Example plugin for wssshd2 demonstrating all plugin capabilities
*
* This plugin shows how to:
* - Add new web pages
* - Handle custom message types
* - Implement a custom transport protocol
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../../plugin.h"
// Plugin state
typedef struct {
int message_count;
time_t start_time;
} example_plugin_state_t;
static example_plugin_state_t *plugin_state = NULL;
// Web request handler
int example_web_handler(int client_fd, const http_request_t *req, plugin_context_t *ctx) {
(void)client_fd; // Parameter not used in this example
(void)ctx; // Unused in this example
// Handle /plugin/example page
if (strcmp(req->path, "/plugin/example") == 0 && strcmp(req->method, "GET") == 0) {
// Note: send_response is not available in plugin context, this is just for demonstration
// In a real plugin, you'd need to use the appropriate response mechanism
printf("[EXAMPLE PLUGIN] Would send HTML response for /plugin/example\n");
return 1; // Handled
}
return 0; // Not handled
}
// Message handler
int example_message_handler(const char *message, size_t message_len, ws_connection_t *conn, plugin_context_t *ctx) {
(void)message_len; // Unused
(void)conn; // Unused
(void)ctx; // Unused
// Handle custom message type "example_message"
if (strstr(message, "\"type\":\"example_message\"")) {
if (plugin_state) {
plugin_state->message_count++;
}
printf("[EXAMPLE PLUGIN] Handled example_message, total messages: %d\n",
plugin_state ? plugin_state->message_count : 0);
return 1; // Handled
}
return 0; // Not handled
}
// Plugin initialization
bool example_plugin_init(plugin_context_t *ctx) {
plugin_state = calloc(1, sizeof(example_plugin_state_t));
if (!plugin_state) {
return false;
}
plugin_state->start_time = time(NULL);
ctx->set_data(ctx, plugin_state);
printf("[EXAMPLE PLUGIN] Initialized\n");
return true;
}
// Plugin cleanup
void example_plugin_cleanup(plugin_context_t *ctx) {
example_plugin_state_t *state = ctx->get_data(ctx);
if (state) {
printf("[EXAMPLE PLUGIN] Processed %d messages, uptime: %ld seconds\n",
state->message_count, time(NULL) - state->start_time);
free(state);
}
printf("[EXAMPLE PLUGIN] Cleaned up\n");
}
// Plugin interface definition
static const plugin_web_route_t example_routes[] = {
{"GET", "/plugin/example", example_web_handler}
};
static const plugin_message_handler_t example_handlers[] = {
{"example_message", example_message_handler}
};
const plugin_interface_t plugin_interface = {
.info = {
.name = "Example Plugin",
.version = "1.0.0",
.description = "Demonstrates plugin capabilities",
.api_version = PLUGIN_API_VERSION
},
.capabilities = PLUGIN_CAP_WEB | PLUGIN_CAP_MESSAGE,
.init = example_plugin_init,
.cleanup = example_plugin_cleanup,
.web = {
.routes = example_routes,
.routes_count = sizeof(example_routes) / sizeof(example_routes[0]),
.template_files = NULL,
.template_files_count = 0
},
.message = {
.handlers = example_handlers,
.handlers_count = sizeof(example_handlers) / sizeof(example_handlers[0])
}
};
\ No newline at end of file
......@@ -38,6 +38,7 @@
#include "assets.h"
#include "websocket.h"
#include "websocket_protocol.h"
#include "plugin.h"
#include "html_pages/index_page.h"
#include "html_pages/login_page.h"
#include "html_pages/terminal_page.h"
......@@ -45,6 +46,8 @@
#include "html_pages/rdp_page.h"
#include "html_pages/users_page.h"
// HTTP request structure is defined in plugin.h
// Embedded web assets are defined in assets.c
static wssshd_state_t *global_state = NULL;
......@@ -1221,14 +1224,6 @@ static const char *validate_session(const char *session_id) {
}
// Parse HTTP request
typedef struct {
char method[10];
char path[1024];
char query[1024];
char headers[4096];
char body[4096];
int content_length;
} http_request_t;
static int parse_http_request(int client_fd, http_request_t *req) {
char buffer[8192];
......@@ -1693,6 +1688,12 @@ static int handle_request(int client_fd, const http_request_t *req) {
username ? username : "none", is_admin ? "yes" : "no");
}
// Check if any plugin handles this request
int plugin_result = plugin_handle_web_request(client_fd, req);
if (plugin_result >= 0) {
return plugin_result; // Plugin handled the request
}
// Route handling
// Handle WebSocket terminal, VNC, and RDP connections first
char *headers_copy = NULL;
......@@ -2810,26 +2811,6 @@ int web_start_server(const wssshd_config_t *config, wssshd_state_t *state) {
global_config = config;
global_state = state;
// Print ASCII art banner
printf("\033[38;5;117m⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠖⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;117m⠀⠀⠀⠀⠀⠀⠀⢠⠖⠋⠀⠀⠀⣀⣹⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;117m⠀⠀⠀⠀⠀⠀⠀⢸⡄⠀⢠⣶⡞⢁⣸⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⠀⠀⠀⠀⠀⠀⠈⢁⣠⠞⢻⡁⢻⣷⣾⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⠀⠀⠀⠀⣠⠤⢄⣘⣿⢬⣛⡷⢋⣈⣉⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⠀⠀⠀⢰⡇⠀⠀⠙⠿⠀⢀⠿⠋⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("⠀⠀⠀⠀⠘⡃⠀⢰⠀⠀⠀⡁⠀⡀⠀⠘⣆⠈⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n");
printf("\033[38;5;231m⠀⠀⠀⠀⠀⡇⠀⣼⡦⠠⠤⣈⡆⢃⡤⠒⠙⡆⠀⠳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;231m⠀⠀⠀⠀⢀⠇⠀⡇⢷⣄⣀⡠⠟⠛⠢⠤⠞⡟⠦⡀⠙⠦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;231m⠀⠀⠀⠀⡞⢀⠀⠀⠀⠳⡄⠀⠀⠠⡀⠀⠀⠘⠉⠙⠦⣀⠀⠉⠢⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;231m⠀⠀⠀⠀⡇⠀⢠⠀⠀⠀⠈⠓⢄⠀⠁⠀⠀⠀⠒⠂⠀⣾⠋⠉⠒⠢⢍⣙⡒⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⠀⠀⠀⡇⠀⡜⠀⠀⠀⠀⠀⠀⠱⡀⢀⡀⠁⠀⢀⡼⡇⠀⠀⠀⠀⠀⣏⠙⠯⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⠀⠀⠀⡇⢠⠇⠀⠀⠀⠀⠀⠀⠀⢱⣀⣽⠶⠾⠛⠒⠛⠒⠒⠒⠤⠤⣸⡍⠀⠀⠉⠲⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;218m⠀⣀⣀⣀⠇⣼⠀⠀⠀⠀⠀⠀⠀⠀⠀⢫⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣙⠢⣀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;117m⠉⠙⠛⠥⠰⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠂⠤⠤⠤⠤⠴⠒⠚⠉⠙⠢⣀⠀⠈⠑⠢⢄⡀⠈⠳⡀⠀⠀⠀⠀⠀⠀⠀\033[0m\n");
printf("\033[38;5;117m⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠒⠒⠒⠒⠚⠑⠢⢌⡓⠤⠤⠤⠤⣀⠀\033[0m\n");
printf("\033[38;5;117m⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⠒⠠⠤⣼⠇\033[0m\n");
printf("\n");
init_users(config);
// Start HTTP server thread
......
......@@ -147,6 +147,7 @@ static void send_http_error(int client_fd, int status_code, const char *status_t
}
// Proxy data between client socket and tunnel WebSocket
static void *proxy_data_forward(void *arg) __attribute__((unused));
static void *proxy_data_forward(void *arg) {
proxy_connection_t *conn = (proxy_connection_t *)arg;
if (!conn) return NULL;
......
......@@ -36,6 +36,7 @@
#include <stdarg.h>
#include "websocket.h"
#include "websocket_protocol.h"
#include "plugin.h"
#include "ssl.h"
// Note: Removed crash recovery mechanism to prevent resource leaks
......@@ -1287,11 +1288,19 @@ int websocket_handle_message(wssshd_state_t *state, ws_connection_t *conn __attr
}
free(request_id);
}
} else {
// Check if any plugin handles this message
int plugin_result = plugin_handle_message(state, conn, msg_copy, message_len);
if (plugin_result == 0) {
if (state->debug) {
printf("[DEBUG - %s -> wssshd] Plugin handled message: %s\n", direction, msg_copy);
}
} else {
if (state->debug) {
printf("[DEBUG - %s -> wssshd] Unhandled message type: %s\n", direction, msg_copy);
}
}
}
// TODO: Handle other message types with similar string parsing
free(msg_copy);
......
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