adding validated services? patching forcad_local.py
This commit is contained in:
81
OmCTF-2025/sources/polyphonia/polyphonia_server_src/Makefile
Normal file
81
OmCTF-2025/sources/polyphonia/polyphonia_server_src/Makefile
Normal file
@@ -0,0 +1,81 @@
|
||||
## Native and VM-obfuscated build for Polyphonia
|
||||
|
||||
# Tools
|
||||
CC := clang-19
|
||||
CXX := clang++-19
|
||||
OPT := opt-19
|
||||
LLC := llc-19
|
||||
LLVM_CONFIG := llvm-config-19
|
||||
|
||||
# Dirs
|
||||
SRC_DIR := src
|
||||
OUT := dist
|
||||
OBJ := $(OUT)/obj
|
||||
BIN := $(OUT)/bin
|
||||
|
||||
# Obfuscator
|
||||
OBF_DIR := ../obfuscator
|
||||
PASS_SO := $(OBF_DIR)/build/libVMObfuscatorPass.so
|
||||
LLVM_DIR ?= /usr/lib/llvm-19/lib/cmake/llvm
|
||||
RUNTIME_OBJ := $(OBJ)/vm_runtime.o
|
||||
|
||||
# Flags
|
||||
VM_TRACE ?= 0
|
||||
CFLAGS := -Wall -O2 -pthread -I$(OBF_DIR)/include -DVM_ENABLE_TRACE=$(VM_TRACE) -DPG_ENABLED -I/usr/include/postgresql
|
||||
LDFLAGS := -lpthread -no-pie -lpq
|
||||
IR_CFLAGS ?= -O0 -fno-vectorize -fno-slp-vectorize
|
||||
SOURCES := $(SRC_DIR)/polyphonia_pg.c $(SRC_DIR)/tone_library.c
|
||||
|
||||
TARGET := $(BIN)/polyphonia
|
||||
|
||||
.PHONY: all vm native clean run pass runtime dirs
|
||||
|
||||
all: vm
|
||||
|
||||
dirs:
|
||||
@mkdir -p $(OBJ) $(BIN)
|
||||
|
||||
# Build obfuscator pass if needed
|
||||
pass:
|
||||
cmake -S $(OBF_DIR) -B $(OBF_DIR)/build -DLLVM_DIR=$(LLVM_DIR)
|
||||
cmake --build $(OBF_DIR)/build
|
||||
|
||||
# Build runtime object in-place
|
||||
runtime: $(RUNTIME_OBJ)
|
||||
|
||||
$(RUNTIME_OBJ): $(OBF_DIR)/src/runtime/vm_runtime.c $(OBF_DIR)/include/vm_defs.h | dirs
|
||||
$(CC) -I$(OBF_DIR)/include -Wall -O2 -DVM_ENABLE_TRACE=$(VM_TRACE) -c -o $@ $<
|
||||
|
||||
# VM-obfuscated build: compile all to bitcode, link, obfuscate once
|
||||
BC_OBJS := $(patsubst $(SRC_DIR)/%.c,$(OBJ)/%.bc,$(SOURCES))
|
||||
|
||||
$(OBJ)/%.bc: $(SRC_DIR)/%.c | dirs
|
||||
$(CC) $(CFLAGS) $(IR_CFLAGS) -emit-llvm -c $< -o $@
|
||||
|
||||
$(OBJ)/program.bc: $(BC_OBJS) | dirs
|
||||
llvm-link-19 $^ -o $@
|
||||
|
||||
$(OBJ)/program_scalar.bc: $(OBJ)/program.bc | pass
|
||||
$(OPT) -passes="scalarizer" $< -o $@
|
||||
|
||||
$(OBJ)/program_obf.ll: $(OBJ)/program_scalar.bc $(PASS_SO) | pass
|
||||
$(OPT) -load-pass-plugin=$(PASS_SO) -passes="vm-obfuscate" $< -o $@ -S
|
||||
|
||||
$(OBJ)/program_obf.s: $(OBJ)/program_obf.ll
|
||||
$(LLC) $< -o $@
|
||||
|
||||
vm: dirs pass runtime $(OBJ)/program_obf.s
|
||||
$(CC) $(OBJ)/program_obf.s $(RUNTIME_OBJ) -o $(TARGET) $(LDFLAGS)
|
||||
@echo "Built VM-obfuscated server: $(TARGET)"
|
||||
|
||||
# Native build (without obfuscation)
|
||||
native: dirs
|
||||
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) $(LDFLAGS)
|
||||
@echo "Built native server: $(TARGET)"
|
||||
|
||||
run: vm
|
||||
@$(TARGET)
|
||||
|
||||
clean:
|
||||
rm -rf $(OUT)
|
||||
@echo "Cleaned polyphonia build artifacts"
|
||||
@@ -0,0 +1,716 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PORT 3000
|
||||
#define BUFFER_SIZE 32768
|
||||
#define MAX_USERS 524288
|
||||
#define MAX_SESSIONS 524288
|
||||
#define SESSION_ID_LEN 64
|
||||
#define SESSION_COOKIE_HEX_LEN (SESSION_ID_LEN * 2)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char username[64];
|
||||
char password_hash[65];
|
||||
int id;
|
||||
} User;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char session_id[SESSION_ID_LEN];
|
||||
int user_id;
|
||||
time_t created_at;
|
||||
} Session;
|
||||
|
||||
static User users[MAX_USERS];
|
||||
static Session sessions[MAX_SESSIONS];
|
||||
static int user_count = 0;
|
||||
static int session_count = 0;
|
||||
static pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t sessions_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Simple hash function
|
||||
void simple_hash(const char *input, char *output)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
while ((c = *input++))
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
sprintf(output, "%016lx", hash);
|
||||
}
|
||||
|
||||
// Generate random session ID bytes
|
||||
void generate_session_id(unsigned char *session_id)
|
||||
{
|
||||
for (int i = 0; i < SESSION_ID_LEN; i++)
|
||||
{
|
||||
session_id[i] = (unsigned char)(rand() % 256);
|
||||
}
|
||||
}
|
||||
|
||||
static int hex_value(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return 10 + (c - 'a');
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return 10 + (c - 'A');
|
||||
return -1;
|
||||
}
|
||||
|
||||
void bytes_to_hex(const unsigned char *input, size_t len, char *output)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
sprintf(output + i * 2, "%02x", input[i]);
|
||||
}
|
||||
output[len * 2] = '\0';
|
||||
}
|
||||
|
||||
int hex_to_bytes(const char *hex, unsigned char *output, size_t out_len)
|
||||
{
|
||||
if (!hex)
|
||||
return 0;
|
||||
|
||||
size_t hex_len = strlen(hex);
|
||||
if (hex_len != out_len * 2)
|
||||
return 0;
|
||||
|
||||
for (size_t i = 0; i < out_len; i++)
|
||||
{
|
||||
int high = hex_value(hex[i * 2]);
|
||||
int low = hex_value(hex[i * 2 + 1]);
|
||||
if (high < 0 || low < 0)
|
||||
return 0;
|
||||
output[i] = (unsigned char)(high * 16 + low);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rc4_apply(const unsigned char *key, size_t key_len, const unsigned char *input, unsigned char *output,
|
||||
size_t len)
|
||||
{
|
||||
uint32_t s[256];
|
||||
for (uint32_t i = 0; i < 256; i++)
|
||||
{
|
||||
s[i] = i;
|
||||
}
|
||||
|
||||
uint32_t j = 0;
|
||||
for (uint32_t i = 0; i < 256; i++)
|
||||
{
|
||||
j = (j + s[i] + key[i % key_len]) & 0xFFu;
|
||||
uint32_t tmp = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = tmp;
|
||||
}
|
||||
|
||||
uint32_t i_idx = 0;
|
||||
j = 0;
|
||||
for (size_t n = 0; n < len; n++)
|
||||
{
|
||||
i_idx = (i_idx + 1) & 0xFFu;
|
||||
j = (j + s[i_idx]) & 0xFFu;
|
||||
uint32_t tmp = s[i_idx];
|
||||
s[i_idx] = s[j];
|
||||
s[j] = tmp;
|
||||
uint32_t t = (s[i_idx] + s[j]) & 0xFFu;
|
||||
output[n] = (unsigned char)(input[n] ^ (unsigned char)s[t]);
|
||||
}
|
||||
}
|
||||
|
||||
// Derive RC4 key bytes at runtime using deterministic LCG with fixed seed
|
||||
static inline uint16_t lcg_rand16(uint32_t *state)
|
||||
{
|
||||
*state = (214013u * (*state) + 2531011u) & 0x7FFFFFFFu;
|
||||
return (uint16_t)((*state) >> 16);
|
||||
}
|
||||
|
||||
static void derive_rc4_key(unsigned char *out_key, size_t key_len)
|
||||
{
|
||||
uint32_t st = 0; // fixed seed
|
||||
size_t i = 0;
|
||||
while (i < key_len)
|
||||
{
|
||||
uint16_t r = lcg_rand16(&st);
|
||||
unsigned char hi = (unsigned char)((r >> 8) & 0xFF);
|
||||
unsigned char lo = (unsigned char)(r & 0xFF);
|
||||
if (i < key_len)
|
||||
out_key[i++] = hi;
|
||||
if (i < key_len)
|
||||
out_key[i++] = lo;
|
||||
}
|
||||
}
|
||||
|
||||
// Get content type from file extension
|
||||
const char *get_content_type(const char *path)
|
||||
{
|
||||
const char *ext = strrchr(path, '.');
|
||||
if (!ext)
|
||||
return "text/plain";
|
||||
|
||||
if (strcmp(ext, ".html") == 0)
|
||||
return "text/html";
|
||||
if (strcmp(ext, ".css") == 0)
|
||||
return "text/css";
|
||||
if (strcmp(ext, ".js") == 0)
|
||||
return "application/javascript";
|
||||
if (strcmp(ext, ".json") == 0)
|
||||
return "application/json";
|
||||
if (strcmp(ext, ".png") == 0)
|
||||
return "image/png";
|
||||
if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0)
|
||||
return "image/jpeg";
|
||||
if (strcmp(ext, ".svg") == 0)
|
||||
return "image/svg+xml";
|
||||
if (strcmp(ext, ".ico") == 0)
|
||||
return "image/x-icon";
|
||||
if (strcmp(ext, ".woff") == 0)
|
||||
return "font/woff";
|
||||
if (strcmp(ext, ".woff2") == 0)
|
||||
return "font/woff2";
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
// Parse JSON field
|
||||
char *parse_json_field(const char *json, const char *field, char *output, int max_len)
|
||||
{
|
||||
char search[100];
|
||||
snprintf(search, sizeof(search), "\"%s\":", field);
|
||||
|
||||
char *start = strstr(json, search);
|
||||
if (!start)
|
||||
return NULL;
|
||||
|
||||
start += strlen(search);
|
||||
while (*start && (*start == ' ' || *start == '"'))
|
||||
start++;
|
||||
|
||||
char *end = start;
|
||||
while (*end && *end != '"' && *end != ',' && *end != '}')
|
||||
end++;
|
||||
|
||||
int len = end - start;
|
||||
if (len >= max_len)
|
||||
len = max_len - 1;
|
||||
|
||||
strncpy(output, start, len);
|
||||
output[len] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Parse cookie
|
||||
char *parse_cookie(const char *headers, const char *name, char *output, int max_len)
|
||||
{
|
||||
char *cookie_line = strstr(headers, "Cookie:");
|
||||
if (!cookie_line)
|
||||
return NULL;
|
||||
|
||||
char search[100];
|
||||
snprintf(search, sizeof(search), "%s=", name);
|
||||
|
||||
char *cookie_start = strstr(cookie_line, search);
|
||||
if (!cookie_start)
|
||||
return NULL;
|
||||
|
||||
cookie_start += strlen(search);
|
||||
char *cookie_end = cookie_start;
|
||||
|
||||
while (*cookie_end && *cookie_end != ';' && *cookie_end != '\r' && *cookie_end != '\n')
|
||||
{
|
||||
cookie_end++;
|
||||
}
|
||||
|
||||
int len = cookie_end - cookie_start;
|
||||
if (len >= max_len)
|
||||
len = max_len - 1;
|
||||
|
||||
strncpy(output, cookie_start, len);
|
||||
output[len] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Find user by username
|
||||
User *find_user(const char *username)
|
||||
{
|
||||
pthread_mutex_lock(&users_mutex);
|
||||
for (int i = 0; i < user_count; i++)
|
||||
{
|
||||
if (strcmp(users[i].username, username) == 0)
|
||||
{
|
||||
pthread_mutex_unlock(&users_mutex);
|
||||
return &users[i];
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&users_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find session
|
||||
Session *find_session(const unsigned char *session_id)
|
||||
{
|
||||
if (!session_id)
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_lock(&sessions_mutex);
|
||||
for (int i = 0; i < session_count; i++)
|
||||
{
|
||||
if (memcmp(sessions[i].session_id, session_id, SESSION_ID_LEN) == 0)
|
||||
{
|
||||
if (time(NULL) - sessions[i].created_at < 86400)
|
||||
{
|
||||
pthread_mutex_unlock(&sessions_mutex);
|
||||
return &sessions[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&sessions_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
User *find_user_by_id(int id)
|
||||
{
|
||||
User *result = NULL;
|
||||
pthread_mutex_lock(&users_mutex);
|
||||
for (int i = 0; i < user_count; i++)
|
||||
{
|
||||
if (users[i].id == id)
|
||||
{
|
||||
result = &users[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&users_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
int authenticate_cookie(const char *cookie_hex, Session **out_session, User **out_user)
|
||||
{
|
||||
if (!cookie_hex)
|
||||
return 0;
|
||||
|
||||
if (out_session)
|
||||
*out_session = NULL;
|
||||
if (out_user)
|
||||
*out_user = NULL;
|
||||
|
||||
unsigned char session_bytes[SESSION_ID_LEN];
|
||||
if (!hex_to_bytes(cookie_hex, session_bytes, SESSION_ID_LEN))
|
||||
return 0;
|
||||
|
||||
Session *session = find_session(session_bytes);
|
||||
if (session)
|
||||
{
|
||||
if (out_session)
|
||||
*out_session = session;
|
||||
if (out_user)
|
||||
*out_user = find_user_by_id(session->user_id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char decrypted[SESSION_ID_LEN];
|
||||
unsigned char keybuf[22];
|
||||
derive_rc4_key(keybuf, sizeof(keybuf));
|
||||
rc4_apply(keybuf, sizeof(keybuf), session_bytes, decrypted, SESSION_ID_LEN);
|
||||
|
||||
char username[65];
|
||||
size_t name_len = 0;
|
||||
while (name_len < sizeof(username) - 1 && name_len < SESSION_ID_LEN && decrypted[name_len] != '\0')
|
||||
{
|
||||
username[name_len] = (char)decrypted[name_len];
|
||||
name_len++;
|
||||
}
|
||||
username[name_len] = '\0';
|
||||
|
||||
if (name_len == 0)
|
||||
return 0;
|
||||
|
||||
User *user = find_user(username);
|
||||
if (!user)
|
||||
return 0;
|
||||
|
||||
if (out_user)
|
||||
*out_user = user;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Send HTTP response
|
||||
void send_response(int client_fd, const char *status, const char *content_type, const char *body,
|
||||
const char *extra_headers)
|
||||
{
|
||||
char response[BUFFER_SIZE];
|
||||
int len = snprintf(response, sizeof(response),
|
||||
"HTTP/1.1 %s\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %lu\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"%s"
|
||||
"\r\n"
|
||||
"%s",
|
||||
status, content_type, strlen(body), extra_headers ? extra_headers : "", body);
|
||||
|
||||
send(client_fd, response, len, 0);
|
||||
}
|
||||
|
||||
// Send file
|
||||
void send_file(int client_fd, const char *filepath)
|
||||
{
|
||||
FILE *file = fopen(filepath, "rb");
|
||||
if (!file)
|
||||
{
|
||||
send_response(client_fd, "404 Not Found", "text/plain", "File not found", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
char *content = malloc(file_size + 1);
|
||||
fread(content, 1, file_size, file);
|
||||
content[file_size] = '\0';
|
||||
fclose(file);
|
||||
|
||||
const char *content_type = get_content_type(filepath);
|
||||
|
||||
char header[BUFFER_SIZE];
|
||||
snprintf(header, sizeof(header),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"\r\n",
|
||||
content_type, file_size);
|
||||
|
||||
send(client_fd, header, strlen(header), 0);
|
||||
send(client_fd, content, file_size, 0);
|
||||
|
||||
free(content);
|
||||
}
|
||||
|
||||
// Handle API requests
|
||||
void handle_api_request(int client_fd, const char *method, const char *path, const char *headers, const char *body)
|
||||
{
|
||||
// Handle CORS preflight
|
||||
if (strcmp(method, "OPTIONS") == 0)
|
||||
{
|
||||
send_response(client_fd, "200 OK", "text/plain", "", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register endpoint
|
||||
if (strcmp(path, "/api/register") == 0 && strcmp(method, "POST") == 0)
|
||||
{
|
||||
char username[64], password[64];
|
||||
|
||||
if (!parse_json_field(body, "username", username, sizeof(username)) ||
|
||||
!parse_json_field(body, "password", password, sizeof(password)))
|
||||
{
|
||||
send_response(client_fd, "400 Bad Request", "application/json",
|
||||
"{\"error\":\"Missing username or password\"}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (find_user(username))
|
||||
{
|
||||
send_response(client_fd, "400 Bad Request", "application/json", "{\"error\":\"User already exists\"}",
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&users_mutex);
|
||||
if (user_count < MAX_USERS)
|
||||
{
|
||||
strncpy(users[user_count].username, username, 63);
|
||||
users[user_count].username[63] = '\0';
|
||||
simple_hash(password, users[user_count].password_hash);
|
||||
users[user_count].id = user_count + 1;
|
||||
user_count++;
|
||||
pthread_mutex_unlock(&users_mutex);
|
||||
|
||||
send_response(client_fd, "200 OK", "application/json", "{\"success\":true}", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_unlock(&users_mutex);
|
||||
send_response(client_fd, "500 Internal Server Error", "application/json",
|
||||
"{\"error\":\"User limit reached\"}", NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Login endpoint
|
||||
if (strcmp(path, "/api/login") == 0 && strcmp(method, "POST") == 0)
|
||||
{
|
||||
char username[64], password[64];
|
||||
|
||||
if (!parse_json_field(body, "username", username, sizeof(username)) ||
|
||||
!parse_json_field(body, "password", password, sizeof(password)))
|
||||
{
|
||||
send_response(client_fd, "400 Bad Request", "application/json",
|
||||
"{\"error\":\"Missing username or password\"}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
User *user = find_user(username);
|
||||
if (!user)
|
||||
{
|
||||
send_response(client_fd, "401 Unauthorized", "application/json", "{\"error\":\"Invalid credentials\"}",
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
char password_hash[65];
|
||||
simple_hash(password, password_hash);
|
||||
|
||||
if (strcmp(user->password_hash, password_hash) != 0)
|
||||
{
|
||||
send_response(client_fd, "401 Unauthorized", "application/json", "{\"error\":\"Invalid credentials\"}",
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create session
|
||||
pthread_mutex_lock(&sessions_mutex);
|
||||
if (session_count >= MAX_SESSIONS)
|
||||
{
|
||||
session_count = 0;
|
||||
}
|
||||
|
||||
generate_session_id(sessions[session_count].session_id);
|
||||
sessions[session_count].user_id = user->id;
|
||||
sessions[session_count].created_at = time(NULL);
|
||||
|
||||
char cookie_header[256];
|
||||
char session_cookie[SESSION_COOKIE_HEX_LEN + 1];
|
||||
bytes_to_hex(sessions[session_count].session_id, SESSION_ID_LEN, session_cookie);
|
||||
snprintf(cookie_header, sizeof(cookie_header), "Set-Cookie: session=%s; HttpOnly; Max-Age=86400\r\n",
|
||||
session_cookie);
|
||||
|
||||
session_count++;
|
||||
pthread_mutex_unlock(&sessions_mutex);
|
||||
|
||||
char response_body[256];
|
||||
snprintf(response_body, sizeof(response_body), "{\"success\":true,\"username\":\"%s\"}", user->username);
|
||||
|
||||
send_response(client_fd, "200 OK", "application/json", response_body, cookie_header);
|
||||
return;
|
||||
}
|
||||
|
||||
// Logout endpoint
|
||||
if (strcmp(path, "/api/logout") == 0 && strcmp(method, "POST") == 0)
|
||||
{
|
||||
char session_cookie[SESSION_COOKIE_HEX_LEN + 1];
|
||||
|
||||
if (parse_cookie(headers, "session", session_cookie, sizeof(session_cookie)))
|
||||
{
|
||||
unsigned char session_bytes[SESSION_ID_LEN];
|
||||
if (hex_to_bytes(session_cookie, session_bytes, SESSION_ID_LEN))
|
||||
{
|
||||
pthread_mutex_lock(&sessions_mutex);
|
||||
for (int i = 0; i < session_count; i++)
|
||||
{
|
||||
if (memcmp(sessions[i].session_id, session_bytes, SESSION_ID_LEN) == 0)
|
||||
{
|
||||
if (i < session_count - 1)
|
||||
{
|
||||
sessions[i] = sessions[session_count - 1];
|
||||
}
|
||||
session_count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&sessions_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
send_response(client_fd, "200 OK", "application/json", "{\"success\":true}",
|
||||
"Set-Cookie: session=; HttpOnly; Max-Age=0\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// User info endpoint
|
||||
if (strcmp(path, "/api/user") == 0 && strcmp(method, "GET") == 0)
|
||||
{
|
||||
char session_cookie[SESSION_COOKIE_HEX_LEN + 1];
|
||||
|
||||
if (!parse_cookie(headers, "session", session_cookie, sizeof(session_cookie)))
|
||||
{
|
||||
send_response(client_fd, "401 Unauthorized", "application/json", "{\"error\":\"Not authenticated\"}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Session *session = NULL;
|
||||
User *user = NULL;
|
||||
if (!authenticate_cookie(session_cookie, &session, &user))
|
||||
{
|
||||
send_response(client_fd, "401 Unauthorized", "application/json", "{\"error\":\"Session expired\"}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user && session)
|
||||
{
|
||||
user = find_user_by_id(session->user_id);
|
||||
}
|
||||
|
||||
if (!user)
|
||||
{
|
||||
send_response(client_fd, "401 Unauthorized", "application/json", "{\"error\":\"User not found\"}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
char response_body[256];
|
||||
snprintf(response_body, sizeof(response_body), "{\"username\":\"%s\",\"id\":%d}", user->username, user->id);
|
||||
send_response(client_fd, "200 OK", "application/json", response_body, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Removed legacy /api/generate endpoint (no longer used)
|
||||
|
||||
send_response(client_fd, "404 Not Found", "application/json", "{\"error\":\"Not found\"}", NULL);
|
||||
}
|
||||
|
||||
// Handle client connection
|
||||
void *handle_client(void *arg)
|
||||
{
|
||||
int client_fd = *(int *)arg;
|
||||
free(arg);
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
int bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0);
|
||||
|
||||
if (bytes_read <= 0)
|
||||
{
|
||||
close(client_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
// Parse HTTP request
|
||||
char method[16], path[256], version[16];
|
||||
sscanf(buffer, "%s %s %s", method, path, version);
|
||||
|
||||
// Find body (after double CRLF)
|
||||
char *body = strstr(buffer, "\r\n\r\n");
|
||||
if (body)
|
||||
body += 4;
|
||||
|
||||
printf("[%s] %s %s\n", method, path, body ? "(with body)" : "");
|
||||
|
||||
// Handle API requests
|
||||
if (strncmp(path, "/api/", 5) == 0)
|
||||
{
|
||||
handle_api_request(client_fd, method, path, buffer, body);
|
||||
}
|
||||
// Serve static files
|
||||
else
|
||||
{
|
||||
char filepath[512];
|
||||
|
||||
// Serve index.html for root or client routes
|
||||
if (strcmp(path, "/") == 0 || strncmp(path, "/dashboard", 10) == 0 || strncmp(path, "/auth", 5) == 0)
|
||||
{
|
||||
snprintf(filepath, sizeof(filepath), "frontend-dist/index.html");
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(filepath, sizeof(filepath), "frontend-dist%s", path);
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
struct stat st;
|
||||
if (stat(filepath, &st) != 0)
|
||||
{
|
||||
// If not found and not an API call, serve index.html for client routing
|
||||
if (strncmp(path, "/api/", 5) != 0)
|
||||
{
|
||||
snprintf(filepath, sizeof(filepath), "frontend-dist/index.html");
|
||||
}
|
||||
}
|
||||
|
||||
send_file(client_fd, filepath);
|
||||
}
|
||||
|
||||
close(client_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd < 0)
|
||||
{
|
||||
perror("Socket creation failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
||||
{
|
||||
perror("Setsockopt failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_in 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)
|
||||
{
|
||||
perror("Bind failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 10) < 0)
|
||||
{
|
||||
perror("Listen failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PolyPhonia server running on port %d\n", PORT);
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
|
||||
int *client_fd = malloc(sizeof(int));
|
||||
*client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
|
||||
|
||||
if (*client_fd < 0)
|
||||
{
|
||||
perror("Accept failed");
|
||||
free(client_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, handle_client, client_fd);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
|
||||
close(server_fd);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user