adding validated services? patching forcad_local.py

This commit is contained in:
Your Name
2026-08-13 08:32:35 +07:00
parent d485f61169
commit e795614e45
1459 changed files with 420036 additions and 436 deletions

View File

@@ -0,0 +1,14 @@
# syntax=docker/dockerfile:1.7
FROM debian:13 AS polyphonia-builder
RUN apt update && apt install -y llvm-19-dev clang-19 libpq-dev cmake ninja-build build-essential
WORKDIR /build
COPY obfuscator_src /build/obfuscator
COPY polyphonia_server_src /build/polyphonia_server
RUN cd polyphonia_server && make all
FROM scratch
COPY --from=polyphonia-builder /build/polyphonia_server/dist/bin/polyphonia /polyphonia-binary

View File

@@ -0,0 +1,19 @@
#!/bin/bash
docker buildx build -f Dockerfile.build-polyphonia -t polyphonia-ad-ctf-service-obfuscated-server-binary --load .
pushd /tmp
rm -rf TMPDIR_FOR_polyphonia-ad-ctf-service-obfuscated-server-binary && mkdir TMPDIR_FOR_polyphonia-ad-ctf-service-obfuscated-server-binary && cd TMPDIR_FOR_polyphonia-ad-ctf-service-obfuscated-server-binary
cid=$(docker create polyphonia-ad-ctf-service-obfuscated-server-binary /dummy)
docker export "$cid" | tar -xv
docker rm "$cid"
popd
rm -rf ./../../services/polyphonia/dist
mkdir ./../../services/polyphonia/dist
mv /tmp/TMPDIR_FOR_polyphonia-ad-ctf-service-obfuscated-server-binary/polyphonia-binary ./../../services/polyphonia/dist/polyphonia-server-binary
rm -rf /tmp/TMPDIR_FOR_polyphonia-ad-ctf-service-obfuscated-server-binary
echo "[+] Built ok; binary is in ./../../services/polyphonia/dist/polyphonia-binary"

View File

@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.20)
project(VMObfuscator)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories(${LLVM_INCLUDE_DIRS} include)
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
# Build the (single) pass as a loadable module
add_library(VMObfuscatorPass MODULE src/pass/VMObfuscatorPass.cpp)
# LLVM is built without RTTI by default
set_target_properties(VMObfuscatorPass PROPERTIES
COMPILE_FLAGS "-fno-rtti"
POSITION_INDEPENDENT_CODE ON
)
# Link against LLVM libraries
target_link_libraries(VMObfuscatorPass PRIVATE ${LLVM_LIBRARIES})

View File

@@ -0,0 +1,85 @@
#ifndef VM_DEFS_H
#define VM_DEFS_H
#include <stddef.h>
#include <stdint.h>
// VM Opcodes - extended set for complex operations
typedef enum
{
OP_PUSH_IMM = 0x01, // Push immediate value onto stack
OP_STORE = 0x02, // Store value to memory
OP_LOAD = 0x03, // Load value from memory
OP_ADD = 0x04, // Add two values
OP_RET = 0x05, // Return from VM
OP_CALL = 0x06, // Call function (reserved)
OP_PUSH_ARG = 0x07, // Push function argument
OP_SUB = 0x08, // Subtract two values
OP_MUL = 0x09, // Multiply two values
OP_DIV = 0x0A, // Divide two values
OP_CMP_GT = 0x0B, // Compare greater than
OP_CMP_LT = 0x0C, // Compare less than
OP_CMP_EQ = 0x0D, // Compare equal
OP_CMP_NE = 0x0E, // Compare not equal
OP_BR_COND = 0x0F, // Conditional branch (if top of stack != 0)
OP_JMP = 0x10, // Unconditional jump
// Extended ALU and memory ops
OP_AND = 0x11,
OP_OR = 0x12,
OP_XOR = 0x13,
OP_SHL = 0x14,
OP_SHR = 0x15,
// Arithmetic right shift (sign-propagating)
OP_ASHR = 0x27,
OP_LOAD8 = 0x16,
OP_STORE8 = 0x17,
OP_LOAD32 = 0x18,
OP_STORE32 = 0x19,
OP_LOAD64 = 0x1A,
OP_STORE64 = 0x1B,
OP_TAG_LOCAL = 0x1C,
OP_UREM = 0x1D,
OP_SREM = 0x1E,
OP_CMP_GE = 0x1F,
OP_CMP_LE = 0x20,
OP_PUSH_GLOB = 0x21,
OP_SELECT = 0x22,
OP_LOAD16 = 0x23,
OP_STORE16 = 0x24,
// Pointer store (64-bit) converts tagged local to real address
OP_STOREPTR64 = 0x25,
OP_RESET_SP = 0x26,
OP_HALT = 0xFF // Stop VM execution
} VMOpcode;
// VM State (unified)
typedef struct
{
uint8_t *bytecode; // Bytecode array
size_t pc; // Program counter
int64_t stack[256]; // Operand stack (64-bit for pointers)
int sp; // Stack pointer
int64_t memory[256]; // Local memory for variables
int64_t *args; // Pointer to function arguments
int arg_count; // Number of arguments
int debug; // Debug flag for tracing
uint64_t crypto_key; // Active runtime key for operand encryption
unsigned char local_mem[65536]; // VM-local byte-addressable memory
size_t scratch_top; // Cursor into local_mem for transient decrypt buffers
// Cache decrypted vm_data_table items per VM invocation to stabilize lifetimes
uint16_t dec_idx[512];
uint32_t dec_off[512];
uint16_t dec_count;
size_t pc_bias; // header size encoded into jump targets
// Encrypted bytecode fetch state (per-fetch rolling decryption)
uint8_t *code_ptr; // Pointer to code payload start (after header)
uint32_t code_len; // Encrypted payload length in bytes
int code_encrypted; // 1 if payload is encrypted
uint64_t code_base_state; // base xorshift64* state for stream (derived from key+salt)
uint64_t code_cur_state; // current xorshift64* state for block generation
uint64_t code_cur_ks; // current 8-byte keystream cache
int code_bpos; // next byte index in code_cur_ks [0..8]
size_t code_off; // current offset into payload for fetch state
} VMState;
#endif // VM_DEFS_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void inner(const char *status, const char *content_type, const char *body)
{
const char *crlf = "\r\n";
const char *h1 = "HTTP/1.1 ";
const char *h_ct = "Content-Type: ";
const char *h_cl = "Content-Length: ";
char *buf = (char *)malloc(1024);
size_t cap = 1024, off = 0;
// helper
#define APPEND_STR(S) do { const char *ss_ = (S); if (ss_) { size_t nn_ = strlen(ss_); if (off + nn_ > cap) nn_ = (cap > off) ? cap - off : 0; if (nn_) memcpy(buf + off, ss_, nn_); off += nn_; } } while (0)
#define APPEND_N(S,N) do { const char *ss2_ = (S); size_t nn2_ = (N); if (ss2_) { if (off + nn2_ > cap) nn2_ = (cap > off) ? cap - off : 0; if (nn2_) memcpy(buf + off, ss2_, nn2_); off += nn2_; } } while (0)
char lenbuf[32];
int blen = (int)strlen(body);
snprintf(lenbuf, sizeof lenbuf, "%d", blen);
APPEND_STR(h1); APPEND_STR(status); APPEND_STR(crlf);
APPEND_STR(h_ct); APPEND_STR(content_type); APPEND_STR(crlf);
APPEND_STR(h_cl); APPEND_STR(lenbuf); APPEND_STR(crlf);
APPEND_STR(crlf);
APPEND_STR(body);
if (off < cap) buf[off] = 0; else buf[cap-1] = 0;
puts(buf);
free(buf);
}
int main(void)
{
const char *status = "400 Bad Request";
const char *ctype = "application/json";
const char *body = "{\"ok\":false}";
inner(status, ctype, body);
return 0;
}

View File

@@ -0,0 +1,37 @@
#include <stdio.h>
#include <string.h>
static void extract(const char *json) {
char tone_sequences_json[8192] = "[]";
const char *ts_pos = strstr(json, "\"toneSequences\":");
if (ts_pos)
{
const char *p = strchr(ts_pos, '[');
if (p)
{
int depth = 0;
const char *q = p;
while (*q && (q - p) < (int)sizeof(tone_sequences_json) - 2)
{
if (*q == '[') depth++;
else if (*q == ']') { depth--; if (depth == 0) { q++; break; } }
q++;
}
size_t len = (size_t)(q - p);
if (len > 0 && len < sizeof(tone_sequences_json))
{
memcpy(tone_sequences_json, p, len);
tone_sequences_json[len] = '\0';
}
}
}
printf("tone_sequences=%s\n", tone_sequences_json);
}
int main(void) {
const char *input =
"{\"name\":\"xxxsed\",\"description\":\"\",\"toneSequences\":[{\"id\":1758442926199.0464,\"baseNote\":60,\"intervalType\":\"unison\",\"chordType\":\"none\",\"tempo\":120,\"duration\":4,\"notes\":[60]}]}";
extract(input);
return 0;
}

View File

@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdint.h>
typedef unsigned Oid;
typedef struct { int dummy; } PGconn;
static int myexecParams(PGconn *c, const char *cmd, int nParams,
const Oid *paramTypes,
const char *const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat)
{
(void)c; (void)paramTypes; (void)paramLengths; (void)paramFormats; (void)resultFormat;
printf("CMD:%s\n", cmd);
printf("N:%d\n", nParams);
for (int i = 0; i < nParams; ++i) {
const char *p = paramValues[i];
printf("P[%d]=%s\n", i, p ? p : "(null)");
}
return 1234;
}
int main(void)
{
PGconn conn; // dummy
char uid[16]; snprintf(uid, sizeof(uid), "%d", 42);
char name[32] = "Hello";
char descr[32] = "World";
char json[64] = "[1,2,3]";
const char *params[4] = { uid, name, descr, json };
const char *query = "INSERT INTO t (a,b,c,d) VALUES ($1::int,$2,$3,$4::jsonb)";
int r = myexecParams(&conn, query, 4, NULL, params, NULL, NULL, 0);
printf("RET:%d\n", r);
return 0;
}

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# Be tolerant to individual failures; aggregate results at end.
set -uo pipefail
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
BUILD_DIR="$ROOT_DIR/build"
PASS_SO="$BUILD_DIR/libVMObfuscatorPass.so"
RUNTIME_O="$BUILD_DIR/vm_runtime.o"
TEST_BUILD_DIR="$BUILD_DIR/tests"
RUN_DIR="$BUILD_DIR/run"
mkdir -p "$TEST_BUILD_DIR" "$RUN_DIR"
if [[ ! -f "$PASS_SO" ]]; then
echo "Pass plugin not found at $PASS_SO" >&2
exit 1
fi
if [[ ! -f "$RUNTIME_O" ]]; then
echo "Runtime object not found at $RUNTIME_O" >&2
exit 1
fi
mask_ptrs() {
# Mask long hex addresses to keep parity (avoid masking small constants like 0xBEEF)
sed -E 's/0x[0-9a-fA-F]{6,}/0xPTR/g'
}
ok=0
fail=0
for c in "$ROOT_DIR/tests/varargs"/*.c; do
name=$(basename "$c" .c)
echo "== $name =="
base_bin="$TEST_BUILD_DIR/${name}_base"
obf_obj="$TEST_BUILD_DIR/${name}_obf.o"
obf_bin="$TEST_BUILD_DIR/${name}_obf"
# Compile baseline
clang -O2 "$c" -o "$base_bin"
# Compile to bitcode, run the pass with opt, then compile to object
bc_tmp="$TEST_BUILD_DIR/${name}.bc"
obf_bc="$TEST_BUILD_DIR/${name}_obf.bc"
clang -O2 -I"$ROOT_DIR/include" -emit-llvm -c "$c" -o "$bc_tmp"
opt-18 -load-pass-plugin="$PASS_SO" -passes=vm-obfuscate "$bc_tmp" -o "$obf_bc"
clang -O2 -c "$obf_bc" -o "$obf_obj"
clang -O2 "$obf_obj" "$RUNTIME_O" -lpthread -o "$obf_bin"
# Run each in its own run dir
base_run_dir="$RUN_DIR/${name}_base"
obf_run_dir="$RUN_DIR/${name}_obf"
rm -rf "$base_run_dir" "$obf_run_dir"
mkdir -p "$base_run_dir" "$obf_run_dir"
# Run baseline
(cd "$base_run_dir" && "$base_bin" > stdout.txt 2> stderr.txt || true)
# Run obfuscated
(cd "$obf_run_dir" && "$obf_bin" > stdout.txt 2> stderr.txt || true)
# Compare stdout with masking
if ! diff -u <(mask_ptrs < "$base_run_dir/stdout.txt") <(mask_ptrs < "$obf_run_dir/stdout.txt") >/dev/null; then
echo " [FAIL] stdout differs"
echo "--- baseline stdout"; cat "$base_run_dir/stdout.txt" | mask_ptrs
echo "--- obfuscated stdout"; cat "$obf_run_dir/stdout.txt" | mask_ptrs
((fail++))
continue
fi
# If a file output exists, compare it too
if [[ -f "$base_run_dir/tests_out_file.txt" || -f "$obf_run_dir/tests_out_file.txt" ]]; then
if [[ ! -f "$base_run_dir/tests_out_file.txt" || ! -f "$obf_run_dir/tests_out_file.txt" ]]; then
echo " [FAIL] file output presence mismatch"
((fail++))
continue
fi
if ! diff -u <(mask_ptrs < "$base_run_dir/tests_out_file.txt") <(mask_ptrs < "$obf_run_dir/tests_out_file.txt") >/dev/null; then
echo " [FAIL] file output differs"
echo "--- baseline file"; cat "$base_run_dir/tests_out_file.txt" | mask_ptrs
echo "--- obfuscated file"; cat "$obf_run_dir/tests_out_file.txt" | mask_ptrs
((fail++))
continue
fi
fi
echo " [OK] parity matched"
((ok++))
done
echo
echo "Summary: $ok OK, $fail FAIL"
exit $fail

View File

@@ -0,0 +1,18 @@
#include <stdio.h>
#include <string.h>
int main(void) {
// Build a format string at runtime in a local buffer
char fmt[64];
strcpy(fmt, "X:%.*s Y:%*d Z:%s W:%s END\n");
char s_local[32];
for (int i = 0; i < (int)sizeof s_local; ++i) s_local[i] = (char)('0' + (i % 10));
s_local[31] = '\0';
const char *g = "GLOB";
int p = 5, w = 3;
printf(fmt, p, "abcdefghijk", w, 9, g, s_local + 7);
return 0;
}

View File

@@ -0,0 +1,12 @@
#include <stdio.h>
int main(void) {
// Many arguments across a single printf to stress call shim
// Avoid %p to keep outputs comparable across obfuscated/non-obfuscated runs.
printf("M:%d %d %d %d %d %d %d %d %d %d %d %d | %s %s %s | %.*s %*d\n",
1,2,3,4,5,6,7,8,9,10,11,12,
"aa","bb","cc",
3, "abcdefgh", 5, 777);
return 0;
}

View File

@@ -0,0 +1,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char g_fmt[] = "start:%s mid:%*d end:%s done\n";
static const char g_hello[] = "HELLO";
int main(void) {
char local[32];
for (int i = 0; i < (int)sizeof local; ++i) local[i] = (char)('A' + (i % 26));
local[31] = '\0';
const char *s1 = g_hello;
const char *s2 = local + 5; // pointer into local buffer
int width = 7;
// printf with global-encrypted fmt and both global/local string args
printf(g_fmt, s1, width, 42, s2);
// more mixed specifiers including width/precision stars with strings
const char *s3 = "abcdefg";
int prec = 3;
int w2 = 6;
printf("mix:%.*s|%*d|%s|%s|%%\n", prec, s3, w2, -12345, s1, s2);
// Check %s with NULL and empty string
const char *nulls = NULL;
const char *emptys = "";
printf("null:%s empty:%s end\n", nulls, emptys);
return 0;
}

View File

@@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char g_tag[] = "XYZ";
int main(void) {
char buf[256];
char local[64];
for (int i = 0; i < (int)sizeof local; ++i) local[i] = (char)('a' + (i % 26));
local[63] = '\0';
// snprintf with dynamic width and precision, and local/global %s
int w = 8, p = 4;
int n = snprintf(buf, sizeof(buf), "A:%*d B:%.*s C:%s D:%s END", w, 321, p, "qwerty", g_tag, local + 2);
printf("snlen=%d buf='%s'\n", n, buf);
// fprintf to a file
FILE *f = fopen("tests_out_file.txt", "w");
if (!f) {
perror("fopen");
return 1;
}
fprintf(f, "FILE:%s|%.*s|%d|%s\n", g_tag, 5, "ZYXWVUT", -77, local + 10);
fclose(f);
return 0;
}

View 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"

View File

@@ -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