adding validated services? patching forcad_local.py
This commit is contained in:
14
OmCTF-2025/sources/polyphonia/Dockerfile.build-polyphonia
Normal file
14
OmCTF-2025/sources/polyphonia/Dockerfile.build-polyphonia
Normal 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
|
||||
|
||||
19
OmCTF-2025/sources/polyphonia/build.sh
Executable file
19
OmCTF-2025/sources/polyphonia/build.sh
Executable 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"
|
||||
23
OmCTF-2025/sources/polyphonia/obfuscator_src/CMakeLists.txt
Normal file
23
OmCTF-2025/sources/polyphonia/obfuscator_src/CMakeLists.txt
Normal 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})
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
91
OmCTF-2025/sources/polyphonia/obfuscator_src/tests/run_varargs_parity.sh
Executable file
91
OmCTF-2025/sources/polyphonia/obfuscator_src/tests/run_varargs_parity.sh
Executable 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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