tcp-chat
Multi-room TCP chat server in C. Per-client pthreads, a three-way handshake protocol with sequence numbers, room management, and an encryption layer with AES-256-GCM and hardware entropy collection.
tcp-chat is a multi-client, multi-room chat server written in C from scratch on top of POSIX sockets and pthreads. Each incoming TCP connection gets its own thread and a client_session_t that tracks state across three phases: CONNECTED, IN_LOBBY, and IN_ROOM. Before a session enters the lobby, it completes a custom three-way handshake (SYN → SYN-ACK → ACK) that establishes a sequence number and a nickname. The room manager assigns short 6-character room IDs and enforces a 20-client-per-room limit. A security foundation layer provides per-client encryption contexts with AES-256-GCM as primary cipher and ChaCha20-Poly1305 as backup, seeded from hardware entropy sources.
Server & Session Model
server_t owns the listening socket, a client_manager, and a room_manager. server_accept_clients runs the accept loop: each successful accept calls client_manager_add_new_client to register the socket and allocate a client_session_t, then spawns a pthread pointing to client_session_handler_thread.
client_session_t tracks everything per connection: socket fd, peer address, nickname, thread handle, handshake state, sequence number, current state (CONNECTED / IN_LOBBY / IN_ROOM), current room ID, and security validation status. The session handler reads this struct at the start of each iteration and writes it as state transitions happen.
int server_accept_clients(server_t *server) {
server->is_running = 1;
while (server->is_running) {
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int client_socket = accept(server->socket_fd,
(struct sockaddr *)&client_addr, &len);
if (client_socket < 0) { /* handle EINTR, EAGAIN, errors */ continue; }
int client_id = client_manager_add_new_client(
&server->client_manager, client_socket, client_addr);
client_session_t *session =
client_manager_get_session(&server->client_manager, client_id);
pthread_create(&session->thread, NULL,
client_session_handler_thread, args);
}
}Handshake Protocol
Before a client can enter the lobby it must complete a custom three-way handshake modelled on TCP's SYN/SYN-ACK/ACK exchange. handshake_packet_t carries the packet type, a 32-bit sequence number, a 32-bit acknowledgment number, and the client's chosen nickname in a fixed-size field.
The server side (handshake_perform_server_side) receives the client's SYN, records the client sequence number, generates its own sequence number via handshake_generate_sequence_number, sends SYN-ACK with both numbers, then waits for ACK. If the ACK matches the expected ack_num the session is marked HANDSHAKE_ESTABLISHED and the sequence number is stored in client_session_t.sequence_number for message ordering.
typedef enum {
HANDSHAKE_IDLE,
HANDSHAKE_SYN_SENT,
HANDSHAKE_SYN_RECEIVED,
HANDSHAKE_ESTABLISHED,
HANDSHAKE_FAILED
} handshake_state_t;
typedef struct {
uint8_t type; // SYN=0, SYN_ACK=1, ACK=2
uint32_t seq_num;
uint32_t ack_num;
char nickname[MAX_NICKNAME_LEN];
uint8_t padding[7];
} handshake_packet_t;
uint32_t handshake_generate_sequence_number(void);
int handshake_perform_server_side(client_session_t *client);Security Foundation
Each client connection gets a security_context_t with stack canaries at both ends, an encrypted encryption_context_t pointer, and hardware entropy. encryption_context_t holds two independent cipher contexts: AES-256-GCM as primary and ChaCha20-Poly1305 as backup. Both use 32-byte keys, nonces, and 64-bit counters. The active key is rotated automatically based on key_rotation_counter.
Entropy is collected from three hardware sources into a 2048-byte entropy_pool_t: RDRAND (if hw_security_caps_t.rdrand_available is set), timing jitter from nanosecond-resolution clocks, and thermal sensor data. Sources are mixed via mix_entropy_sources before any key material is derived. The pool tracks an entropy_estimate and enforces a minimum quality threshold (MIN_ENTROPY_QUALITY = 7.9 bits per byte) before use.
typedef struct {
aes256_gcm_context_t primary_cipher; // AES-256-GCM
chacha20_poly1305_context_t backup_cipher; // ChaCha20-Poly1305
uint8_t session_key[32];
uint8_t message_key[32];
uint64_t key_rotation_counter;
uint32_t sequence_number;
pthread_mutex_t crypto_mutex;
} encryption_context_t;
typedef struct {
uint8_t hw_entropy[1024]; // RDRAND
uint8_t timing_entropy[512]; // nanosecond jitter
uint8_t thermal_entropy[256]; // temperature sensors
uint8_t mixed_pool[2048]; // combined
uint32_t entropy_estimate; // must exceed MIN_ENTROPY_QUALITY
pthread_mutex_t pool_mutex;
} entropy_pool_t;
#define MIN_ENTROPY_QUALITY 7.9