Initialisation depot
This commit is contained in:
11
RdpBroker/src/.dockerignore
Normal file
11
RdpBroker/src/.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
||||
# .dockerignore
|
||||
build/
|
||||
bin/
|
||||
*.o
|
||||
*.so
|
||||
*.a
|
||||
.git/
|
||||
.gitignore
|
||||
README.md
|
||||
docs/
|
||||
chart/
|
||||
50
RdpBroker/src/Dockerfile
Normal file
50
RdpBroker/src/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
# Build stage
|
||||
FROM alpine:3.22 AS builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
musl-dev \
|
||||
make \
|
||||
openldap-dev \
|
||||
yaml-dev
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /build
|
||||
|
||||
# Copy source files
|
||||
COPY *.c *.h Makefile ./
|
||||
|
||||
# Build the application
|
||||
RUN make deps-alpine && make
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:3.22
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache \
|
||||
libldap \
|
||||
yaml \
|
||||
ca-certificates
|
||||
|
||||
# Create app user
|
||||
RUN addgroup -g 1000 rdpbroker && \
|
||||
adduser -D -u 1000 -G rdpbroker rdpbroker
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /etc/rdpbroker /var/log/rdpbroker && \
|
||||
chown -R rdpbroker:rdpbroker /etc/rdpbroker /var/log/rdpbroker
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/bin/rdpbroker /usr/local/bin/rdpbroker
|
||||
|
||||
# Set permissions
|
||||
RUN chmod +x /usr/local/bin/rdpbroker
|
||||
|
||||
# Switch to non-root user
|
||||
USER rdpbroker
|
||||
|
||||
# Expose RDP port
|
||||
EXPOSE 3389
|
||||
|
||||
# Set entrypoint
|
||||
ENTRYPOINT ["/usr/local/bin/rdpbroker"]
|
||||
62
RdpBroker/src/Makefile
Normal file
62
RdpBroker/src/Makefile
Normal file
@@ -0,0 +1,62 @@
|
||||
# RdpBroker Makefile
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -pthread -D_GNU_SOURCE
|
||||
LDFLAGS = -pthread -lldap -llber -lyaml
|
||||
|
||||
# Directories
|
||||
SRC_DIR = .
|
||||
BUILD_DIR = build
|
||||
BIN_DIR = bin
|
||||
|
||||
# Source files
|
||||
SOURCES = main.c config.c auth.c rdp_server.c session_manager.c
|
||||
OBJECTS = $(SOURCES:%.c=$(BUILD_DIR)/%.o)
|
||||
TARGET = $(BIN_DIR)/rdpbroker
|
||||
|
||||
# Default target
|
||||
all: directories $(TARGET)
|
||||
|
||||
# Create necessary directories
|
||||
directories:
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
# Link
|
||||
$(TARGET): $(OBJECTS)
|
||||
@echo "Linking $(TARGET)..."
|
||||
$(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS)
|
||||
@echo "Build complete: $(TARGET)"
|
||||
|
||||
# Compile
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@echo "Compiling $<..."
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Clean
|
||||
clean:
|
||||
@echo "Cleaning build files..."
|
||||
rm -rf $(BUILD_DIR) $(BIN_DIR)
|
||||
@echo "Clean complete"
|
||||
|
||||
# Install dependencies (Debian/Ubuntu)
|
||||
deps-debian:
|
||||
@echo "Installing dependencies for Debian/Ubuntu..."
|
||||
apt-get update
|
||||
apt-get install -y build-essential libldap2-dev libyaml-dev
|
||||
|
||||
# Install dependencies (Alpine - for Docker)
|
||||
deps-alpine:
|
||||
@echo "Installing dependencies for Alpine..."
|
||||
apk add --no-cache gcc musl-dev make openldap-dev yaml-dev
|
||||
|
||||
# Run
|
||||
run: $(TARGET)
|
||||
@echo "Running RdpBroker..."
|
||||
$(TARGET)
|
||||
|
||||
# Debug build
|
||||
debug: CFLAGS += -g -DDEBUG
|
||||
debug: clean all
|
||||
|
||||
.PHONY: all clean directories deps-debian deps-alpine run debug
|
||||
109
RdpBroker/src/auth.c
Normal file
109
RdpBroker/src/auth.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ldap.h>
|
||||
#include "rdp_broker.h"
|
||||
|
||||
int authenticate_user(const char *username, const char *password,
|
||||
const char *samba_server, int samba_port,
|
||||
const char *base_dn) {
|
||||
|
||||
LOG(LOG_DEBUG, "Authenticating user: %s", username);
|
||||
|
||||
if (!username || !password || strlen(password) == 0) {
|
||||
LOG(LOG_WARN, "Empty username or password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Perform LDAP bind to validate credentials */
|
||||
int result = ldap_bind_check(samba_server, samba_port, username,
|
||||
password, base_dn);
|
||||
|
||||
if (result == 0) {
|
||||
LOG(LOG_INFO, "Authentication successful for user: %s", username);
|
||||
return 0;
|
||||
} else {
|
||||
LOG(LOG_WARN, "Authentication failed for user: %s", username);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ldap_bind_check(const char *server, int port, const char *username,
|
||||
const char *password, const char *base_dn) {
|
||||
LDAP *ld = NULL;
|
||||
int rc;
|
||||
char ldap_uri[512];
|
||||
char bind_dn[512];
|
||||
int version = LDAP_VERSION3;
|
||||
|
||||
/* Construct LDAP URI */
|
||||
snprintf(ldap_uri, sizeof(ldap_uri), "ldap://%s:%d", server, port);
|
||||
|
||||
/* Initialize LDAP connection */
|
||||
rc = ldap_initialize(&ld, ldap_uri);
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
LOG(LOG_ERROR, "LDAP initialization failed: %s", ldap_err2string(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set LDAP version */
|
||||
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
|
||||
if (rc != LDAP_OPT_SUCCESS) {
|
||||
LOG(LOG_ERROR, "Failed to set LDAP version: %s", ldap_err2string(rc));
|
||||
ldap_unbind_ext_s(ld, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Construct bind DN - typically cn=username,base_dn or username@domain */
|
||||
/* For Samba AD, we can use username@domain format or userPrincipalName */
|
||||
/* Here we'll try simple bind with CN format first */
|
||||
snprintf(bind_dn, sizeof(bind_dn), "cn=%s,%s", username, base_dn);
|
||||
|
||||
/* Attempt to bind */
|
||||
struct berval cred;
|
||||
cred.bv_val = (char *)password;
|
||||
cred.bv_len = strlen(password);
|
||||
|
||||
rc = ldap_sasl_bind_s(ld, bind_dn, LDAP_SASL_SIMPLE, &cred,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (rc != LDAP_SUCCESS) {
|
||||
/* Try alternative format: username@domain */
|
||||
/* Extract domain from base_dn (DC=example,DC=com -> example.com) */
|
||||
char domain[256] = {0};
|
||||
const char *dc = strstr(base_dn, "DC=");
|
||||
if (dc) {
|
||||
const char *ptr = dc + 3;
|
||||
char *out = domain;
|
||||
while (*ptr && (out - domain) < sizeof(domain) - 1) {
|
||||
if (*ptr == ',') {
|
||||
ptr++;
|
||||
if (strncmp(ptr, "DC=", 3) == 0) {
|
||||
*out++ = '.';
|
||||
ptr += 3;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*out++ = *ptr++;
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
if (strlen(domain) > 0) {
|
||||
snprintf(bind_dn, sizeof(bind_dn), "%s@%s", username, domain);
|
||||
rc = ldap_sasl_bind_s(ld, bind_dn, LDAP_SASL_SIMPLE, &cred,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ldap_unbind_ext_s(ld, NULL, NULL);
|
||||
|
||||
if (rc == LDAP_SUCCESS) {
|
||||
LOG(LOG_DEBUG, "LDAP bind successful for: %s", username);
|
||||
return 0;
|
||||
} else {
|
||||
LOG(LOG_DEBUG, "LDAP bind failed: %s", ldap_err2string(rc));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
162
RdpBroker/src/config.c
Normal file
162
RdpBroker/src/config.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <yaml.h>
|
||||
#include "rdp_broker.h"
|
||||
|
||||
int load_config(broker_config_t *config) {
|
||||
const char *env_value;
|
||||
|
||||
/* Initialize config with defaults */
|
||||
memset(config, 0, sizeof(broker_config_t));
|
||||
|
||||
/* Load from environment variables */
|
||||
env_value = getenv("SAMBA_AD_SERVER");
|
||||
if (env_value) {
|
||||
strncpy(config->samba_server, env_value, MAX_HOSTNAME_LEN - 1);
|
||||
} else {
|
||||
LOG(LOG_ERROR, "SAMBA_AD_SERVER environment variable not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
env_value = getenv("SAMBA_AD_PORT");
|
||||
config->samba_port = env_value ? atoi(env_value) : 389;
|
||||
|
||||
env_value = getenv("SAMBA_AD_BASE_DN");
|
||||
if (env_value) {
|
||||
strncpy(config->base_dn, env_value, MAX_PATH_LEN - 1);
|
||||
} else {
|
||||
LOG(LOG_ERROR, "SAMBA_AD_BASE_DN environment variable not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
env_value = getenv("RDP_LISTEN_PORT");
|
||||
config->rdp_listen_port = env_value ? atoi(env_value) : 3389;
|
||||
|
||||
env_value = getenv("TARGETS_CONFIG_PATH");
|
||||
if (env_value) {
|
||||
strncpy(config->targets_config_path, env_value, MAX_PATH_LEN - 1);
|
||||
} else {
|
||||
strncpy(config->targets_config_path, "/etc/rdpbroker/targets.yaml",
|
||||
MAX_PATH_LEN - 1);
|
||||
}
|
||||
|
||||
env_value = getenv("LOG_LEVEL");
|
||||
if (env_value) {
|
||||
if (strcmp(env_value, "DEBUG") == 0) {
|
||||
config->log_level = LOG_DEBUG;
|
||||
} else if (strcmp(env_value, "INFO") == 0) {
|
||||
config->log_level = LOG_INFO;
|
||||
} else if (strcmp(env_value, "WARN") == 0) {
|
||||
config->log_level = LOG_WARN;
|
||||
} else if (strcmp(env_value, "ERROR") == 0) {
|
||||
config->log_level = LOG_ERROR;
|
||||
} else {
|
||||
config->log_level = LOG_INFO;
|
||||
}
|
||||
} else {
|
||||
config->log_level = LOG_INFO;
|
||||
}
|
||||
|
||||
global_log_level = config->log_level;
|
||||
|
||||
/* Load targets configuration */
|
||||
return load_targets(config, config->targets_config_path);
|
||||
}
|
||||
|
||||
int load_targets(broker_config_t *config, const char *path) {
|
||||
FILE *file;
|
||||
yaml_parser_t parser;
|
||||
yaml_event_t event;
|
||||
int done = 0;
|
||||
int in_targets = 0;
|
||||
int in_target = 0;
|
||||
char key[256] = {0};
|
||||
rdp_target_t current_target;
|
||||
|
||||
memset(¤t_target, 0, sizeof(rdp_target_t));
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file) {
|
||||
LOG(LOG_ERROR, "Failed to open targets file: %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!yaml_parser_initialize(&parser)) {
|
||||
LOG(LOG_ERROR, "Failed to initialize YAML parser");
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
yaml_parser_set_input_file(&parser, file);
|
||||
config->target_count = 0;
|
||||
|
||||
/* Simple YAML parsing - this is a basic implementation */
|
||||
/* In production, use a more robust YAML library */
|
||||
while (!done) {
|
||||
if (!yaml_parser_parse(&parser, &event)) {
|
||||
LOG(LOG_ERROR, "YAML parser error");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case YAML_SCALAR_EVENT:
|
||||
if (strcmp((char *)event.data.scalar.value, "targets") == 0) {
|
||||
in_targets = 1;
|
||||
} else if (in_targets && strcmp(key, "name") == 0) {
|
||||
strncpy(current_target.name,
|
||||
(char *)event.data.scalar.value,
|
||||
MAX_HOSTNAME_LEN - 1);
|
||||
key[0] = '\0';
|
||||
} else if (in_targets && strcmp(key, "host") == 0) {
|
||||
strncpy(current_target.host,
|
||||
(char *)event.data.scalar.value,
|
||||
MAX_HOSTNAME_LEN - 1);
|
||||
key[0] = '\0';
|
||||
} else if (in_targets && strcmp(key, "port") == 0) {
|
||||
current_target.port = atoi((char *)event.data.scalar.value);
|
||||
key[0] = '\0';
|
||||
} else if (in_targets && strcmp(key, "description") == 0) {
|
||||
strncpy(current_target.description,
|
||||
(char *)event.data.scalar.value,
|
||||
MAX_DESCRIPTION_LEN - 1);
|
||||
key[0] = '\0';
|
||||
|
||||
/* Target is complete, add it */
|
||||
if (config->target_count < MAX_TARGETS) {
|
||||
memcpy(&config->targets[config->target_count],
|
||||
¤t_target,
|
||||
sizeof(rdp_target_t));
|
||||
config->target_count++;
|
||||
LOG(LOG_DEBUG, "Loaded target: %s (%s:%d)",
|
||||
current_target.name, current_target.host,
|
||||
current_target.port);
|
||||
}
|
||||
memset(¤t_target, 0, sizeof(rdp_target_t));
|
||||
} else if (in_targets) {
|
||||
strncpy(key, (char *)event.data.scalar.value, sizeof(key) - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case YAML_STREAM_END_EVENT:
|
||||
done = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
yaml_event_delete(&event);
|
||||
}
|
||||
|
||||
yaml_parser_delete(&parser);
|
||||
fclose(file);
|
||||
|
||||
LOG(LOG_INFO, "Loaded %d targets from %s", config->target_count, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_config(broker_config_t *config) {
|
||||
/* Nothing to free for now, but placeholder for future use */
|
||||
(void)config;
|
||||
}
|
||||
84
RdpBroker/src/main.c
Normal file
84
RdpBroker/src/main.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include "rdp_broker.h"
|
||||
|
||||
int global_log_level = LOG_INFO;
|
||||
volatile bool running = true; /* Global running flag */
|
||||
|
||||
void signal_handler(int signum) {
|
||||
if (signum == SIGINT || signum == SIGTERM) {
|
||||
LOG(LOG_INFO, "Received signal %d, shutting down...", signum);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
void print_banner(void) {
|
||||
printf("\n");
|
||||
printf("╔═══════════════════════════════════════════════════════╗\n");
|
||||
printf("║ RDP Broker v1.0.0 ║\n");
|
||||
printf("║ RDP Connection Broker with Samba AD Auth ║\n");
|
||||
printf("╚═══════════════════════════════════════════════════════╝\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
broker_config_t config;
|
||||
session_manager_t session_manager;
|
||||
int ret;
|
||||
|
||||
print_banner();
|
||||
|
||||
/* Install signal handlers */
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGPIPE, SIG_IGN); /* Ignore broken pipe signals */
|
||||
|
||||
/* Load configuration */
|
||||
LOG(LOG_INFO, "Loading configuration...");
|
||||
ret = load_config(&config);
|
||||
if (ret != 0) {
|
||||
LOG(LOG_ERROR, "Failed to load configuration");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "Configuration loaded successfully");
|
||||
LOG(LOG_INFO, "Samba AD Server: %s:%d", config.samba_server, config.samba_port);
|
||||
LOG(LOG_INFO, "RDP Listen Port: %d", config.rdp_listen_port);
|
||||
LOG(LOG_INFO, "Loaded %d target(s)", config.target_count);
|
||||
|
||||
/* Initialize session manager */
|
||||
LOG(LOG_INFO, "Initializing session manager...");
|
||||
ret = init_session_manager(&session_manager);
|
||||
if (ret != 0) {
|
||||
LOG(LOG_ERROR, "Failed to initialize session manager");
|
||||
free_config(&config);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Start RDP server */
|
||||
LOG(LOG_INFO, "Starting RDP server on port %d...", config.rdp_listen_port);
|
||||
ret = start_rdp_server(&config, &session_manager);
|
||||
if (ret != 0) {
|
||||
LOG(LOG_ERROR, "Failed to start RDP server");
|
||||
free_config(&config);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Main loop - monitor sessions */
|
||||
LOG(LOG_INFO, "RDP Broker is running. Press Ctrl+C to stop.");
|
||||
while (running) {
|
||||
sleep(30); /* Log every 30 seconds */
|
||||
log_active_sessions(&session_manager);
|
||||
cleanup_inactive_sessions(&session_manager, 3600); /* 1 hour timeout */
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
LOG(LOG_INFO, "Cleaning up...");
|
||||
free_config(&config);
|
||||
|
||||
LOG(LOG_INFO, "RDP Broker stopped");
|
||||
return 0;
|
||||
}
|
||||
114
RdpBroker/src/rdp_broker.h
Normal file
114
RdpBroker/src/rdp_broker.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef RDP_BROKER_H
|
||||
#define RDP_BROKER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Global running flag for graceful shutdown */
|
||||
extern volatile bool running;
|
||||
|
||||
#define MAX_TARGETS 100
|
||||
#define MAX_SESSIONS 1000
|
||||
#define MAX_USERNAME_LEN 256
|
||||
#define MAX_HOSTNAME_LEN 256
|
||||
#define MAX_DESCRIPTION_LEN 512
|
||||
#define MAX_PATH_LEN 1024
|
||||
|
||||
/* Configuration structures */
|
||||
typedef struct {
|
||||
char name[MAX_HOSTNAME_LEN];
|
||||
char host[MAX_HOSTNAME_LEN];
|
||||
int port;
|
||||
char description[MAX_DESCRIPTION_LEN];
|
||||
} rdp_target_t;
|
||||
|
||||
typedef struct {
|
||||
char samba_server[MAX_HOSTNAME_LEN];
|
||||
int samba_port;
|
||||
char base_dn[MAX_PATH_LEN];
|
||||
int rdp_listen_port;
|
||||
char targets_config_path[MAX_PATH_LEN];
|
||||
int log_level;
|
||||
rdp_target_t targets[MAX_TARGETS];
|
||||
int target_count;
|
||||
} broker_config_t;
|
||||
|
||||
/* Session management structures */
|
||||
typedef struct {
|
||||
uint32_t session_id;
|
||||
char username[MAX_USERNAME_LEN];
|
||||
char client_ip[64];
|
||||
char target_host[MAX_HOSTNAME_LEN];
|
||||
int target_port;
|
||||
time_t start_time;
|
||||
time_t last_activity;
|
||||
bool active;
|
||||
int client_fd;
|
||||
int target_fd;
|
||||
} session_info_t;
|
||||
|
||||
typedef struct {
|
||||
session_info_t sessions[MAX_SESSIONS];
|
||||
int session_count;
|
||||
uint32_t next_session_id;
|
||||
} session_manager_t;
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
/* config.c */
|
||||
int load_config(broker_config_t *config);
|
||||
int load_targets(broker_config_t *config, const char *path);
|
||||
void free_config(broker_config_t *config);
|
||||
|
||||
/* auth.c */
|
||||
int authenticate_user(const char *username, const char *password,
|
||||
const char *samba_server, int samba_port,
|
||||
const char *base_dn);
|
||||
int ldap_bind_check(const char *server, int port, const char *username,
|
||||
const char *password, const char *base_dn);
|
||||
|
||||
/* rdp_server.c */
|
||||
int start_rdp_server(broker_config_t *config, session_manager_t *sm);
|
||||
int handle_rdp_connection(int client_fd, broker_config_t *config,
|
||||
session_manager_t *sm);
|
||||
int handle_auth_protocol(int client_fd, char *initial_buffer, int initial_len,
|
||||
broker_config_t *config, session_manager_t *sm,
|
||||
const char *client_ip);
|
||||
void send_json_error(int fd, const char *message);
|
||||
int send_targets_json(int fd, broker_config_t *config, const char *username);
|
||||
int present_login_screen(int client_fd, char *username, char *password);
|
||||
int present_target_menu(int client_fd, broker_config_t *config,
|
||||
rdp_target_t **selected_target);
|
||||
int forward_rdp_connection(int client_fd, const char *target_host,
|
||||
int target_port, session_info_t *session);
|
||||
|
||||
/* session_manager.c */
|
||||
int init_session_manager(session_manager_t *sm);
|
||||
session_info_t* create_session(session_manager_t *sm, const char *username,
|
||||
const char *client_ip, rdp_target_t *target,
|
||||
int client_fd);
|
||||
int update_session_activity(session_info_t *session);
|
||||
int close_session(session_manager_t *sm, uint32_t session_id);
|
||||
void log_active_sessions(session_manager_t *sm);
|
||||
void cleanup_inactive_sessions(session_manager_t *sm, int timeout_seconds);
|
||||
|
||||
/* Logging macros */
|
||||
typedef enum {
|
||||
LOG_DEBUG = 0,
|
||||
LOG_INFO = 1,
|
||||
LOG_WARN = 2,
|
||||
LOG_ERROR = 3
|
||||
} log_level_t;
|
||||
|
||||
extern int global_log_level;
|
||||
|
||||
#define LOG(level, ...) do { \
|
||||
if (level >= global_log_level) { \
|
||||
fprintf(stderr, "[%s] ", #level); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif /* RDP_BROKER_H */
|
||||
564
RdpBroker/src/rdp_server.c
Normal file
564
RdpBroker/src/rdp_server.c
Normal file
@@ -0,0 +1,564 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include "rdp_broker.h"
|
||||
|
||||
typedef struct {
|
||||
int client_fd;
|
||||
broker_config_t *config;
|
||||
session_manager_t *session_mgr;
|
||||
} connection_handler_args_t;
|
||||
|
||||
void *handle_connection_thread(void *arg);
|
||||
int create_listening_socket(int port);
|
||||
|
||||
int start_rdp_server(broker_config_t *config, session_manager_t *sm) {
|
||||
int server_fd;
|
||||
int client_fd;
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
pthread_t thread_id;
|
||||
struct timeval timeout;
|
||||
fd_set read_fds;
|
||||
|
||||
/* Create listening socket */
|
||||
server_fd = create_listening_socket(config->rdp_listen_port);
|
||||
if (server_fd < 0) {
|
||||
LOG(LOG_ERROR, "Failed to create listening socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "RDP server listening on port %d", config->rdp_listen_port);
|
||||
|
||||
/* Accept connections in a loop */
|
||||
while (running) {
|
||||
/* Use select with timeout to check running flag periodically */
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(server_fd, &read_fds);
|
||||
timeout.tv_sec = 1; /* 1 second timeout */
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int ready = select(server_fd + 1, &read_fds, NULL, NULL, &timeout);
|
||||
|
||||
if (ready < 0) {
|
||||
if (!running) break; /* Shutting down */
|
||||
LOG(LOG_ERROR, "Select failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ready == 0) {
|
||||
/* Timeout - check running flag and continue */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Socket is ready for accept */
|
||||
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
|
||||
if (client_fd < 0) {
|
||||
if (!running) break; /* Shutting down */
|
||||
LOG(LOG_ERROR, "Accept failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
char client_ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
|
||||
LOG(LOG_INFO, "New connection from %s:%d", client_ip,
|
||||
ntohs(client_addr.sin_port));
|
||||
|
||||
/* Create thread to handle connection */
|
||||
connection_handler_args_t *args = malloc(sizeof(connection_handler_args_t));
|
||||
if (!args) {
|
||||
LOG(LOG_ERROR, "Failed to allocate memory for connection handler");
|
||||
close(client_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
args->client_fd = client_fd;
|
||||
args->config = config;
|
||||
args->session_mgr = sm;
|
||||
|
||||
if (pthread_create(&thread_id, NULL, handle_connection_thread, args) != 0) {
|
||||
LOG(LOG_ERROR, "Failed to create thread: %s", strerror(errno));
|
||||
free(args);
|
||||
close(client_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Detach thread so it cleans up automatically */
|
||||
pthread_detach(thread_id);
|
||||
}
|
||||
|
||||
/* Clean shutdown */
|
||||
LOG(LOG_INFO, "Closing server socket...");
|
||||
close(server_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *handle_connection_thread(void *arg) {
|
||||
connection_handler_args_t *args = (connection_handler_args_t *)arg;
|
||||
int ret;
|
||||
|
||||
LOG(LOG_DEBUG, "Handling connection on fd %d", args->client_fd);
|
||||
|
||||
ret = handle_rdp_connection(args->client_fd, args->config, args->session_mgr);
|
||||
|
||||
if (ret != 0) {
|
||||
LOG(LOG_WARN, "Connection handler failed");
|
||||
}
|
||||
|
||||
close(args->client_fd);
|
||||
free(args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int create_listening_socket(int port) {
|
||||
int server_fd;
|
||||
struct sockaddr_in server_addr;
|
||||
int opt = 1;
|
||||
|
||||
/* Create socket */
|
||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd < 0) {
|
||||
LOG(LOG_ERROR, "Socket creation failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set socket options */
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||
LOG(LOG_ERROR, "Setsockopt failed: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Bind socket */
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
LOG(LOG_ERROR, "Bind failed: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Listen */
|
||||
if (listen(server_fd, 10) < 0) {
|
||||
LOG(LOG_ERROR, "Listen failed: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return server_fd;
|
||||
}
|
||||
|
||||
/* Handle AUTH/SELECT protocol from web-gateway */
|
||||
int handle_auth_protocol(int client_fd, char *initial_buffer, int initial_len,
|
||||
broker_config_t *config, session_manager_t *sm,
|
||||
const char *client_ip) {
|
||||
char username[MAX_USERNAME_LEN] = {0};
|
||||
char password[MAX_USERNAME_LEN] = {0};
|
||||
char target_name[MAX_HOSTNAME_LEN] = {0};
|
||||
char buffer[4096];
|
||||
rdp_target_t *selected_target = NULL;
|
||||
session_info_t *session = NULL;
|
||||
int ret;
|
||||
|
||||
/* Parse AUTH\nusername\npassword\n from initial_buffer */
|
||||
char *line = initial_buffer;
|
||||
char *lines[3];
|
||||
int line_count = 0;
|
||||
|
||||
char *token = strtok(line, "\n");
|
||||
while (token != NULL && line_count < 3) {
|
||||
lines[line_count++] = token;
|
||||
token = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
if (line_count < 3 || strcmp(lines[0], "AUTH") != 0) {
|
||||
LOG(LOG_ERROR, "Invalid AUTH protocol format");
|
||||
send_json_error(client_fd, "Invalid protocol format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(username, lines[1], MAX_USERNAME_LEN - 1);
|
||||
strncpy(password, lines[2], MAX_USERNAME_LEN - 1);
|
||||
|
||||
/* Authenticate user */
|
||||
LOG(LOG_INFO, "Authenticating user: %s from %s", username, client_ip);
|
||||
if (authenticate_user(username, password, config->samba_server,
|
||||
config->samba_port, config->base_dn) != 0) {
|
||||
LOG(LOG_WARN, "Authentication failed for user: %s", username);
|
||||
send_json_error(client_fd, "Invalid credentials");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "User %s authenticated successfully", username);
|
||||
|
||||
/* Send targets list as JSON */
|
||||
if (send_targets_json(client_fd, config, username) != 0) {
|
||||
LOG(LOG_ERROR, "Failed to send targets list");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for SELECT message */
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
ret = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
||||
if (ret <= 0) {
|
||||
LOG(LOG_ERROR, "Failed to receive SELECT message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parse SELECT\ntarget_name\n */
|
||||
line_count = 0;
|
||||
line = buffer;
|
||||
token = strtok(line, "\n");
|
||||
while (token != NULL && line_count < 2) {
|
||||
lines[line_count++] = token;
|
||||
token = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
if (line_count < 2 || strcmp(lines[0], "SELECT") != 0) {
|
||||
LOG(LOG_ERROR, "Invalid SELECT protocol format");
|
||||
send_json_error(client_fd, "Invalid protocol format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(target_name, lines[1], MAX_HOSTNAME_LEN - 1);
|
||||
|
||||
/* Find target by name */
|
||||
for (int i = 0; i < config->target_count; i++) {
|
||||
if (strcmp(config->targets[i].name, target_name) == 0) {
|
||||
selected_target = &config->targets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected_target) {
|
||||
LOG(LOG_ERROR, "Target not found: %s", target_name);
|
||||
send_json_error(client_fd, "Target not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "User %s selected target: %s (%s:%d)",
|
||||
username, selected_target->name, selected_target->host, selected_target->port);
|
||||
|
||||
/* Send RDP ready message */
|
||||
const char *ready_msg = "{\"type\":\"rdp_ready\"}\n\n";
|
||||
send(client_fd, ready_msg, strlen(ready_msg), 0);
|
||||
|
||||
/* Create session */
|
||||
session = create_session(sm, username, client_ip, selected_target, client_fd);
|
||||
if (!session) {
|
||||
LOG(LOG_ERROR, "Failed to create session");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect to target RDP server and forward traffic */
|
||||
LOG(LOG_INFO, "Connecting to target RDP server %s:%d",
|
||||
selected_target->host, selected_target->port);
|
||||
|
||||
ret = forward_rdp_connection(client_fd, selected_target->host,
|
||||
selected_target->port, session);
|
||||
|
||||
/* Cleanup session */
|
||||
close_session(sm, session->session_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send JSON error message */
|
||||
void send_json_error(int fd, const char *message) {
|
||||
char buffer[512];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"{\"type\":\"auth_failed\",\"message\":\"%s\"}\n\n", message);
|
||||
send(fd, buffer, strlen(buffer), 0);
|
||||
}
|
||||
|
||||
/* Send targets list as JSON */
|
||||
int send_targets_json(int fd, broker_config_t *config, const char *username) {
|
||||
char buffer[8192];
|
||||
int offset = 0;
|
||||
|
||||
/* Start JSON */
|
||||
offset += snprintf(buffer + offset, sizeof(buffer) - offset,
|
||||
"{\"type\":\"auth_success\",\"targets\":[");
|
||||
|
||||
/* Add targets - for now, all targets (TODO: filter by user groups) */
|
||||
for (int i = 0; i < config->target_count; i++) {
|
||||
if (i > 0) {
|
||||
offset += snprintf(buffer + offset, sizeof(buffer) - offset, ",");
|
||||
}
|
||||
|
||||
offset += snprintf(buffer + offset, sizeof(buffer) - offset,
|
||||
"{\"name\":\"%s\",\"host\":\"%s\",\"port\":%d,\"description\":\"%s\"}",
|
||||
config->targets[i].name,
|
||||
config->targets[i].host,
|
||||
config->targets[i].port,
|
||||
config->targets[i].description);
|
||||
}
|
||||
|
||||
/* End JSON */
|
||||
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "]}\n\n");
|
||||
|
||||
LOG(LOG_DEBUG, "Sending %d targets to user %s", config->target_count, username);
|
||||
|
||||
return send(fd, buffer, strlen(buffer), 0) > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int handle_rdp_connection(int client_fd, broker_config_t *config,
|
||||
session_manager_t *sm) {
|
||||
char buffer[4096];
|
||||
char username[MAX_USERNAME_LEN] = {0};
|
||||
char password[MAX_USERNAME_LEN] = {0};
|
||||
rdp_target_t *selected_target = NULL;
|
||||
session_info_t *session = NULL;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
char client_ip[INET_ADDRSTRLEN] = {0};
|
||||
int ret;
|
||||
|
||||
/* Get client IP */
|
||||
if (getpeername(client_fd, (struct sockaddr *)&addr, &addr_len) == 0) {
|
||||
inet_ntop(AF_INET, &addr.sin_addr, client_ip, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
/* Read first message to detect protocol type */
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
ret = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
||||
if (ret <= 0) {
|
||||
LOG(LOG_ERROR, "Failed to receive initial message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if this is AUTH protocol (web-gateway) */
|
||||
if (strncmp(buffer, "AUTH\n", 5) == 0) {
|
||||
LOG(LOG_INFO, "Detected AUTH protocol from web-gateway");
|
||||
return handle_auth_protocol(client_fd, buffer, ret, config, sm, client_ip);
|
||||
} else {
|
||||
/* Legacy RDP protocol - not supported anymore */
|
||||
LOG(LOG_WARN, "Legacy RDP protocol not supported, use web-gateway");
|
||||
const char *msg = "ERROR: Please use web-gateway for access\n";
|
||||
send(client_fd, msg, strlen(msg), 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int present_login_screen(int client_fd, char *username, char *password) {
|
||||
/* This is a simplified implementation */
|
||||
/* In a real implementation, this would use RDP protocol to present a login UI */
|
||||
/* For now, we'll use a simple text-based protocol */
|
||||
|
||||
const char *prompt_user = "RDP Broker Login\nUsername: ";
|
||||
const char *prompt_pass = "Password: ";
|
||||
char buffer[1024];
|
||||
int n;
|
||||
|
||||
/* Send username prompt */
|
||||
if (send(client_fd, prompt_user, strlen(prompt_user), 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Receive username */
|
||||
n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
buffer[n] = '\0';
|
||||
|
||||
/* Remove newline */
|
||||
char *newline = strchr(buffer, '\n');
|
||||
if (newline) *newline = '\0';
|
||||
newline = strchr(buffer, '\r');
|
||||
if (newline) *newline = '\0';
|
||||
|
||||
strncpy(username, buffer, MAX_USERNAME_LEN - 1);
|
||||
|
||||
/* Send password prompt */
|
||||
if (send(client_fd, prompt_pass, strlen(prompt_pass), 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Receive password */
|
||||
n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
buffer[n] = '\0';
|
||||
|
||||
/* Remove newline */
|
||||
newline = strchr(buffer, '\n');
|
||||
if (newline) *newline = '\0';
|
||||
newline = strchr(buffer, '\r');
|
||||
if (newline) *newline = '\0';
|
||||
|
||||
strncpy(password, buffer, MAX_USERNAME_LEN - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int present_target_menu(int client_fd, broker_config_t *config,
|
||||
rdp_target_t **selected_target) {
|
||||
char buffer[4096];
|
||||
char input[256];
|
||||
int n, selection;
|
||||
|
||||
/* Build menu */
|
||||
snprintf(buffer, sizeof(buffer), "\nAvailable RDP Targets:\n");
|
||||
for (int i = 0; i < config->target_count; i++) {
|
||||
char line[512];
|
||||
snprintf(line, sizeof(line), "%d. %s - %s (%s:%d)\n",
|
||||
i + 1,
|
||||
config->targets[i].name,
|
||||
config->targets[i].description,
|
||||
config->targets[i].host,
|
||||
config->targets[i].port);
|
||||
strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
|
||||
}
|
||||
strncat(buffer, "\nSelect target (1-", sizeof(buffer) - strlen(buffer) - 1);
|
||||
char num[16];
|
||||
snprintf(num, sizeof(num), "%d): ", config->target_count);
|
||||
strncat(buffer, num, sizeof(buffer) - strlen(buffer) - 1);
|
||||
|
||||
/* Send menu */
|
||||
if (send(client_fd, buffer, strlen(buffer), 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Receive selection */
|
||||
n = recv(client_fd, input, sizeof(input) - 1, 0);
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
input[n] = '\0';
|
||||
|
||||
selection = atoi(input);
|
||||
|
||||
if (selection < 1 || selection > config->target_count) {
|
||||
const char *msg = "Invalid selection\n";
|
||||
send(client_fd, msg, strlen(msg), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*selected_target = &config->targets[selection - 1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int forward_rdp_connection(int client_fd, const char *target_host,
|
||||
int target_port, session_info_t *session) {
|
||||
int target_fd;
|
||||
struct sockaddr_in target_addr;
|
||||
fd_set read_fds;
|
||||
int max_fd;
|
||||
char buffer[8192];
|
||||
int n;
|
||||
|
||||
/* Connect to target */
|
||||
target_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (target_fd < 0) {
|
||||
LOG(LOG_ERROR, "Failed to create target socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&target_addr, 0, sizeof(target_addr));
|
||||
target_addr.sin_family = AF_INET;
|
||||
target_addr.sin_port = htons(target_port);
|
||||
|
||||
if (inet_pton(AF_INET, target_host, &target_addr.sin_addr) <= 0) {
|
||||
LOG(LOG_ERROR, "Invalid target address: %s", target_host);
|
||||
close(target_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(target_fd, (struct sockaddr *)&target_addr,
|
||||
sizeof(target_addr)) < 0) {
|
||||
LOG(LOG_ERROR, "Failed to connect to target %s:%d: %s",
|
||||
target_host, target_port, strerror(errno));
|
||||
close(target_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "Connected to target %s:%d", target_host, target_port);
|
||||
session->target_fd = target_fd;
|
||||
|
||||
/* Forward data bidirectionally - no messages sent, just raw RDP forwarding */
|
||||
max_fd = (client_fd > target_fd) ? client_fd : target_fd;
|
||||
|
||||
LOG(LOG_INFO, "Starting bidirectional forwarding loop (client_fd=%d, target_fd=%d)",
|
||||
client_fd, target_fd);
|
||||
|
||||
while (running) {
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(client_fd, &read_fds);
|
||||
FD_SET(target_fd, &read_fds);
|
||||
|
||||
int sel_ret = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
|
||||
if (sel_ret < 0) {
|
||||
LOG(LOG_ERROR, "Select failed: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
if (sel_ret == 0) {
|
||||
/* Timeout - continue loop to check running flag */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Data from client to target */
|
||||
if (FD_ISSET(client_fd, &read_fds)) {
|
||||
n = recv(client_fd, buffer, sizeof(buffer), 0);
|
||||
if (n < 0) {
|
||||
LOG(LOG_ERROR, "Error receiving from client: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (n == 0) {
|
||||
LOG(LOG_INFO, "Client connection closed gracefully");
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(LOG_DEBUG, "Forwarding %d bytes from client to target", n);
|
||||
if (send(target_fd, buffer, n, 0) != n) {
|
||||
LOG(LOG_ERROR, "Failed to forward to target");
|
||||
break;
|
||||
}
|
||||
|
||||
update_session_activity(session);
|
||||
}
|
||||
|
||||
/* Data from target to client */
|
||||
if (FD_ISSET(target_fd, &read_fds)) {
|
||||
n = recv(target_fd, buffer, sizeof(buffer), 0);
|
||||
if (n < 0) {
|
||||
LOG(LOG_ERROR, "Error receiving from target: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (n == 0) {
|
||||
LOG(LOG_INFO, "Target connection closed gracefully");
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(LOG_DEBUG, "Forwarding %d bytes from target to client", n);
|
||||
if (send(client_fd, buffer, n, 0) != n) {
|
||||
LOG(LOG_ERROR, "Failed to forward to client");
|
||||
break;
|
||||
}
|
||||
|
||||
update_session_activity(session);
|
||||
}
|
||||
}
|
||||
|
||||
close(target_fd);
|
||||
session->target_fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
177
RdpBroker/src/session_manager.c
Normal file
177
RdpBroker/src/session_manager.c
Normal file
@@ -0,0 +1,177 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include "rdp_broker.h"
|
||||
|
||||
static pthread_mutex_t session_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
int init_session_manager(session_manager_t *sm) {
|
||||
if (!sm) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(sm, 0, sizeof(session_manager_t));
|
||||
sm->next_session_id = 1;
|
||||
sm->session_count = 0;
|
||||
|
||||
LOG(LOG_DEBUG, "Session manager initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
session_info_t* create_session(session_manager_t *sm, const char *username,
|
||||
const char *client_ip, rdp_target_t *target,
|
||||
int client_fd) {
|
||||
session_info_t *session = NULL;
|
||||
|
||||
pthread_mutex_lock(&session_mutex);
|
||||
|
||||
if (sm->session_count >= MAX_SESSIONS) {
|
||||
LOG(LOG_ERROR, "Maximum sessions reached (%d)", MAX_SESSIONS);
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find free slot */
|
||||
for (int i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (!sm->sessions[i].active) {
|
||||
session = &sm->sessions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
LOG(LOG_ERROR, "No free session slot available");
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize session */
|
||||
memset(session, 0, sizeof(session_info_t));
|
||||
session->session_id = sm->next_session_id++;
|
||||
strncpy(session->username, username, MAX_USERNAME_LEN - 1);
|
||||
strncpy(session->client_ip, client_ip, sizeof(session->client_ip) - 1);
|
||||
strncpy(session->target_host, target->host, MAX_HOSTNAME_LEN - 1);
|
||||
session->target_port = target->port;
|
||||
session->start_time = time(NULL);
|
||||
session->last_activity = session->start_time;
|
||||
session->active = true;
|
||||
session->client_fd = client_fd;
|
||||
session->target_fd = -1;
|
||||
|
||||
sm->session_count++;
|
||||
|
||||
LOG(LOG_INFO, "Created session %u: %s@%s -> %s:%d",
|
||||
session->session_id, username, client_ip,
|
||||
target->host, target->port);
|
||||
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
int update_session_activity(session_info_t *session) {
|
||||
if (!session || !session->active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&session_mutex);
|
||||
session->last_activity = time(NULL);
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_session(session_manager_t *sm, uint32_t session_id) {
|
||||
session_info_t *session = NULL;
|
||||
|
||||
pthread_mutex_lock(&session_mutex);
|
||||
|
||||
/* Find session */
|
||||
for (int i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sm->sessions[i].active && sm->sessions[i].session_id == session_id) {
|
||||
session = &sm->sessions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
LOG(LOG_WARN, "Session %u not found", session_id);
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
time_t duration = time(NULL) - session->start_time;
|
||||
|
||||
LOG(LOG_INFO, "Closing session %u: %s -> %s:%d (duration: %ld seconds)",
|
||||
session->session_id, session->username,
|
||||
session->target_host, session->target_port, duration);
|
||||
|
||||
session->active = false;
|
||||
sm->session_count--;
|
||||
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_active_sessions(session_manager_t *sm) {
|
||||
int count = 0;
|
||||
time_t now = time(NULL);
|
||||
|
||||
pthread_mutex_lock(&session_mutex);
|
||||
|
||||
LOG(LOG_INFO, "=== Active Sessions Report ===");
|
||||
|
||||
for (int i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sm->sessions[i].active) {
|
||||
session_info_t *s = &sm->sessions[i];
|
||||
time_t duration = now - s->start_time;
|
||||
time_t idle = now - s->last_activity;
|
||||
|
||||
LOG(LOG_INFO, "Session %u: %s@%s -> %s:%d | Duration: %lds | Idle: %lds",
|
||||
s->session_id, s->username, s->client_ip,
|
||||
s->target_host, s->target_port, duration, idle);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
LOG(LOG_INFO, "No active sessions");
|
||||
} else {
|
||||
LOG(LOG_INFO, "Total active sessions: %d", count);
|
||||
}
|
||||
|
||||
LOG(LOG_INFO, "=============================");
|
||||
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
}
|
||||
|
||||
void cleanup_inactive_sessions(session_manager_t *sm, int timeout_seconds) {
|
||||
time_t now = time(NULL);
|
||||
int cleaned = 0;
|
||||
|
||||
pthread_mutex_lock(&session_mutex);
|
||||
|
||||
for (int i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sm->sessions[i].active) {
|
||||
time_t idle = now - sm->sessions[i].last_activity;
|
||||
|
||||
if (idle > timeout_seconds) {
|
||||
LOG(LOG_WARN, "Cleaning up inactive session %u (idle: %ld seconds)",
|
||||
sm->sessions[i].session_id, idle);
|
||||
|
||||
sm->sessions[i].active = false;
|
||||
sm->session_count--;
|
||||
cleaned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cleaned > 0) {
|
||||
LOG(LOG_INFO, "Cleaned up %d inactive session(s)", cleaned);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&session_mutex);
|
||||
}
|
||||
2
RdpBroker/src/test.sh
Executable file
2
RdpBroker/src/test.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker container run -it -e SAMBA_AD_SERVER=192.168.100.240 -e SAMBA_AD_BASE_DN="DC=aipice,DC=local" -v /data/apps/RdpBroker/targets.yaml:/etc/rdpbroker/targets.yaml -p 3389:3389 easylinux/rdp-broker:1.0-1 sh
|
||||
Reference in New Issue
Block a user