/* * ================================================================================== * State Machine Header - Protocol State Management * * ISA Project * Author: Roman Necas (xnecasr00) * Date: 13. October 2025 * * PURPOSE: * This header defines the state machine layer for the ICMP covert channel file * transfer protocol. It implements separate state machines for client and server * modes, providing structured protocol flow control, timeout handling, and error * recovery. * * RESPONSIBILITIES: * - Client state machine: Manages file transmission states (START → DATA → END) * - Server state machine: Manages file reception states (LISTENING → RECEIVING → CLOSING) * - State transition validation: Ensures only valid state changes occur * - Timeout detection: Detects idle sessions and triggers retransmissions * - Retry logic with exponential backoff: Prevents network flooding * - Error recovery: Handles recoverable errors vs fatal errors * - Statistics tracking: Monitors state duration, retry counts, etc. * * STATE MACHINE PATTERN: * Both client and server use event-driven state machines: * - Current state: Represents current protocol phase * - Events: Triggers for state transitions (packet received, timeout, error) * - Transitions: Valid state changes defined by protocol * - Actions: Side effects when entering/exiting states * * CLIENT STATE MACHINE: * Manages the file sending protocol: * IDLE → START_SENT → DATA_TRANSFER ⇄ DATA_WAIT_ACK → END_SENT → COMPLETED * Any state can transition to ERROR or ABORT on failure * * SERVER STATE MACHINE: * Manages the file receiving protocol: * LISTENING → SESSION_INIT → RECEIVING_DATA ⇄ DATA_ACK_SENT → SESSION_CLOSING → COMPLETED * Any state can transition to ERROR or TIMEOUT on failure * * TIMEOUT HANDLING: * - Client timeouts trigger retransmissions with exponential backoff * - Server timeouts indicate client failure or network partition * - Timeout values increase: 2s → 4s → 8s → 16s → 30s (capped) * ================================================================================== */ #ifndef STATE_MACHINE_H #define STATE_MACHINE_H #include #include /* * ================================================================================== * CLIENT STATES * ================================================================================== */ /* * client_state_t - Client-side protocol states * * These states represent the phases of file transmission from client to server. * The client progresses through these states sequentially, with possible loops * between DATA_TRANSFER and DATA_WAIT_ACK for each chunk. * * STATE DIAGRAM: * * IDLE * ↓ * START_SENT ──────┐ * ↓ │ (timeout/retry) * DATA_TRANSFER │ * ↓ │ * DATA_WAIT_ACK ───┘ * ↓ (ACK received) * [Loop back to DATA_TRANSFER if more chunks] * ↓ (all chunks sent) * END_SENT ────────┐ * ↓ │ (timeout/retry) * COMPLETED │ * ↓ * ERROR/ABORT (from any state on failure) */ typedef enum { /* * CLIENT_IDLE - Initial state before transfer begins * * Entry: When client_sm_init() is called * Exit: When file is opened and ready to send * Next: CLIENT_START_SENT * * Activities in this state: * - Open file for reading * - Generate session ID * - Prepare metadata (filename, file size, IV, salt) */ CLIENT_IDLE, /* * CLIENT_START_SENT - START packet sent, waiting for ACK * * Entry: After sending START packet with file metadata * Exit: When START-ACK received or timeout/max retries * Next: CLIENT_DATA_TRANSFER (success), CLIENT_ERROR (failure) * * Activities in this state: * - Wait for START-ACK from server * - Retry sending START on timeout (with exponential backoff) * - Check retry count against MAX_RETRIES * * Timeout behavior: Retry up to MAX_RETRIES times before failing */ CLIENT_START_SENT, /* * CLIENT_DATA_TRANSFER - Sending DATA packet * * Entry: After START-ACK received, or after previous DATA-ACK * Exit: After DATA packet sent * Next: CLIENT_DATA_WAIT_ACK * * Activities in this state: * - Read next chunk from file * - Encrypt chunk * - Create DATA packet with IFTP header * - Send DATA packet * - Increment sequence number */ CLIENT_DATA_TRANSFER, /* * CLIENT_DATA_WAIT_ACK - Waiting for ACK after DATA packet * * Entry: After DATA packet sent * Exit: When DATA-ACK received, NACK received, or timeout * Next: CLIENT_DATA_TRANSFER (more chunks), CLIENT_END_SENT (all sent), * CLIENT_DATA_WAIT_ACK (retry on timeout) * * Activities in this state: * - Wait for ACK matching current sequence number * - Retry sending DATA on timeout (with exponential backoff) * - Check retry count against MAX_RETRIES * - Check if more chunks remain to send * * Timeout behavior: Retry same DATA packet up to MAX_RETRIES times * NACK handling: Resend DATA immediately (counts as retry) */ CLIENT_DATA_WAIT_ACK, /* * CLIENT_END_SENT - END packet sent, waiting for final ACK * * Entry: After all DATA packets acknowledged * Exit: When END-ACK received or timeout/max retries * Next: CLIENT_COMPLETED (success), CLIENT_ERROR (failure) * * Activities in this state: * - Wait for END-ACK from server * - Retry sending END on timeout * - Check retry count against MAX_RETRIES * * Timeout behavior: Retry up to MAX_RETRIES times before failing */ CLIENT_END_SENT, /* * CLIENT_COMPLETED - Transfer completed successfully * * Entry: After END-ACK received * Exit: None (terminal state) * Next: None * * Activities in this state: * - Close file * - Print transfer statistics * - Clean up resources * * This is a terminal success state. */ CLIENT_COMPLETED, /* * CLIENT_ERROR - Transfer failed due to error * * Entry: From any state on unrecoverable error * Exit: None (terminal state) * Next: None * * Causes: * - Max retries exceeded (MAX_RETRIES = 5) * - File I/O error (disk read failure) * - Network error (cannot send packets) * - Protocol error (invalid packet from server) * * This is a terminal failure state. */ CLIENT_ERROR, /* * CLIENT_ABORT - Transfer aborted by user * * Entry: From any state on user interrupt (SIGINT) * Exit: None (terminal state) * Next: None * * Causes: * - User presses Ctrl+C * - User sends SIGTERM * - Application shutdown requested * * This is a terminal state, resources should be cleaned up gracefully. */ CLIENT_ABORT } client_state_t; /* * ================================================================================== * SERVER STATES * ================================================================================== */ /* * server_state_t - Server-side protocol states * * These states represent the phases of file reception from client. * The server remains in LISTENING until a START packet arrives, then * progresses through reception states. * * STATE DIAGRAM: * * LISTENING * ↓ (START received) * SESSION_INIT * ↓ (START-ACK sent) * RECEIVING_DATA * ↓ (DATA received) * DATA_ACK_SENT * ↓ (more DATA expected) * RECEIVING_DATA ──┐ * ↓ │ (loop for each DATA packet) * DATA_ACK_SENT ───┘ * ↓ (END received) * SESSION_CLOSING * ↓ (END-ACK sent) * COMPLETED * * ERROR/TIMEOUT (from any state on failure) */ typedef enum { /* * SERVER_LISTENING - Waiting for START packet from any client * * Entry: When server_sm_init() is called, or after previous session completes * Exit: When valid START packet received * Next: SERVER_SESSION_INIT * * Activities in this state: * - Poll for incoming ICMP packets (non-blocking) * - Validate packet magic number (0xDEADBEEF) * - Check packet type (must be START) * - Extract metadata from START packet * * Timeout: No timeout in this state (server waits indefinitely) */ SERVER_LISTENING, /* * SERVER_SESSION_INIT - New session initialized, sending START-ACK * * Entry: After valid START packet received * Exit: After START-ACK sent * Next: SERVER_RECEIVING_DATA * * Activities in this state: * - Create new server session structure * - Extract metadata (filename, file_size, IV, salt) * - Open output file (basename only for security) * - Initialize crypto engine for decryption * - Send START-ACK to client * - Set expected_seq to 0 for first DATA packet * * Error handling: Transition to SERVER_ERROR if file cannot be created */ SERVER_SESSION_INIT, /* * SERVER_RECEIVING_DATA - Waiting for DATA packet * * Entry: After START-ACK sent, or after previous DATA-ACK sent * Exit: When valid DATA packet received or timeout * Next: SERVER_DATA_ACK_SENT (DATA received), SERVER_TIMEOUT (timeout) * * Activities in this state: * - Wait for DATA packet with expected sequence number * - Validate sequence number (must match expected_seq) * - Validate CRC32 checksum * - Decrypt data chunk * - Write chunk to file * * Timeout: If no packet received within idle_timeout (default 30s), * transition to SERVER_TIMEOUT */ SERVER_RECEIVING_DATA, /* * SERVER_DATA_ACK_SENT - ACK sent for received DATA packet * * Entry: After valid DATA packet received and written * Exit: Immediately after ACK sent * Next: SERVER_RECEIVING_DATA (more DATA expected), * SERVER_SESSION_CLOSING (END packet received) * * Activities in this state: * - Send DATA-ACK with acknowledged sequence number * - Increment expected_seq for next DATA packet * - Update transfer statistics * * This is a transient state - transitions immediately to RECEIVING_DATA */ SERVER_DATA_ACK_SENT, /* * SERVER_SESSION_CLOSING - END packet received, sending final ACK * * Entry: After valid END packet received * Exit: After END-ACK sent * Next: SERVER_COMPLETED * * Activities in this state: * - Verify all expected data received (bytes_written == file_size) * - Finalize file (close handle) * - Send END-ACK to client * - Clean up session resources */ SERVER_SESSION_CLOSING, /* * SERVER_COMPLETED - Session completed successfully * * Entry: After END-ACK sent * Exit: None (terminal state for this session) * Next: SERVER_LISTENING (for next session) * * Activities in this state: * - Print transfer statistics * - Clean up crypto resources * - Reset to LISTENING for next client * * This is a terminal success state for the current session. * Server can return to LISTENING to handle next client. */ SERVER_COMPLETED, /* * SERVER_ERROR - Session failed due to error * * Entry: From any state on unrecoverable error * Exit: None (terminal state for this session) * Next: SERVER_LISTENING (after cleanup) * * Causes: * - File write error (disk full, permissions) * - Decryption failure (wrong key, corrupted data) * - Protocol violation (invalid packet, wrong session ID) * - Checksum mismatch (data corruption) * * This is a terminal failure state. Server should clean up and return * to LISTENING for next client. */ SERVER_ERROR, /* * SERVER_TIMEOUT - Session timed out waiting for client * * Entry: From RECEIVING_DATA when idle_timeout exceeded * Exit: None (terminal state for this session) * Next: SERVER_LISTENING (after cleanup) * * Causes: * - Client crashed or network disconnected * - Client waiting for ACK that was lost * - Network partition * * Server should clean up incomplete file and return to LISTENING. */ SERVER_TIMEOUT } server_state_t; /* * ================================================================================== * PROTOCOL EVENTS * ================================================================================== */ /* * protocol_event_t - Events that trigger state machine transitions * * These events are generated by the protocol engine based on network activity, * timeouts, file I/O results, and user actions. */ typedef enum { /* * EVENT_TIMEOUT - Timeout occurred waiting for response * * Client: Triggers retry of last packet sent (START, DATA, or END) * Server: Triggers transition to SERVER_TIMEOUT if in RECEIVING_DATA * * Event data: NULL */ EVENT_TIMEOUT, /* * EVENT_PACKET_RECEIVED - Valid packet received from peer * * Client: START-ACK, DATA-ACK, or END-ACK received * Server: START, DATA, or END packet received * * Event data: Pointer to received packet (iftp_header_t*) */ EVENT_PACKET_RECEIVED, /* * EVENT_PACKET_SENT - Packet successfully sent to peer * * Client: START, DATA, or END packet sent * Server: ACK packet sent * * Event data: Pointer to sent packet (iftp_header_t*) */ EVENT_PACKET_SENT, /* * EVENT_ACK_RECEIVED - ACK packet received matching expected sequence * * Client: Triggers transition from WAIT_ACK to next state * Server: Not used * * Event data: Pointer to ACK packet (iftp_header_t*) */ EVENT_ACK_RECEIVED, /* * EVENT_ERROR - Unrecoverable error occurred * * Triggers transition to ERROR state from any state. * * Event data: Pointer to error code (error_code_t*) */ EVENT_ERROR, /* * EVENT_FILE_EOF - End of file reached during read * * Client: All chunks sent, transition to END_SENT * Server: Not used * * Event data: NULL */ EVENT_FILE_EOF, /* * EVENT_USER_ABORT - User requested abort (Ctrl+C, SIGTERM) * * Triggers transition to ABORT state from any state. * * Event data: NULL */ EVENT_USER_ABORT, /* * EVENT_RETRY_EXCEEDED - Maximum retry count exceeded * * Triggers transition to ERROR state when retries exhausted. * * Event data: NULL */ EVENT_RETRY_EXCEEDED } protocol_event_t; /* * ================================================================================== * CLIENT STATE MACHINE STRUCTURE * ================================================================================== */ /* * client_state_machine_t - Client state tracking and control * * This structure maintains all state for the client-side protocol engine. * It tracks the current protocol phase, retry logic, sequence numbers, and * timing information. * * LIFECYCLE: * 1. Initialized by client_sm_init() * 2. Transitions managed by client_sm_transition() * 3. Timeouts checked by client_sm_check_timeout() * 4. Events handled by client_sm_handle_event() * 5. Reset by client_sm_reset() if retry needed */ typedef struct { /* * current_state - Current protocol state * * One of the client_state_t enum values. * Modified by client_sm_transition() only. */ client_state_t current_state; /* * previous_state - Previous state before current * * Used for error recovery and debugging. * Can transition back to previous_state if error is recoverable. */ client_state_t previous_state; /* * current_seq - Current sequence number for packets * * Incremented after each DATA packet sent. * Used in IFTP header seq_num field. * Range: 0 to UINT32_MAX (wraps around) * * Sequence number lifecycle: * - START packet: seq_num = 0 * - DATA packet 1: seq_num = 1 * - DATA packet 2: seq_num = 2 * - ... * - END packet: seq_num = (last DATA seq_num + 1) */ uint32_t current_seq; /* * ack_expected - Expected sequence number in ACK response * * When waiting for ACK, this holds the sequence number we expect * to see in the ACK packet. Must match current_seq. */ uint32_t ack_expected; /* * retry_count - Number of retries attempted for current packet * * Incremented each time a timeout occurs and packet is resent. * Reset to 0 when ACK received. * Compared against MAX_RETRIES (5) to detect failure. * * If retry_count >= MAX_RETRIES: * - Generate EVENT_RETRY_EXCEEDED * - Transition to CLIENT_ERROR */ uint32_t retry_count; /* * state_entry_time - When current state was entered (Unix timestamp) * * Set by client_sm_transition() using time(NULL). * Used to calculate: * - Time spent in current state: time(NULL) - state_entry_time * - State duration statistics */ time_t state_entry_time; /* * last_activity - Last time any network activity occurred * * Updated when: * - Packet sent * - Packet received * * Used to detect idle sessions and calculate overall session duration. */ time_t last_activity; /* * timeout_seconds - Timeout duration for current state * * How long to wait before considering state timed out. * Compared against: time(NULL) - state_entry_time * * Exponential backoff: * - Retry 0: 2 seconds (INITIAL_TIMEOUT) * - Retry 1: 4 seconds * - Retry 2: 8 seconds * - Retry 3: 16 seconds * - Retry 4: 30 seconds (MAX_TIMEOUT) * - Retry 5: Fail (MAX_RETRIES exceeded) * * Formula: timeout = min(INITIAL_TIMEOUT * (2 ^ retry_count), MAX_TIMEOUT) */ int timeout_seconds; /* * session_id - Unique session identifier (16-bit) * * Generated by client_sm_init() using utils_generate_session_id() * (cryptographically secure random number). * * Used in all IFTP packets to distinguish this transfer from others. * Server uses this to track which session a packet belongs to. */ uint16_t session_id; } client_state_machine_t; /* * ================================================================================== * SERVER STATE MACHINE STRUCTURE * ================================================================================== */ /* * server_state_machine_t - Server state tracking and control * * This structure maintains all state for the server-side protocol engine. * It tracks the current reception phase, expected sequence numbers, and * session timeout information. * * LIFECYCLE: * 1. Initialized by server_sm_init() * 2. Transitions managed by server_sm_transition() * 3. Timeouts checked by server_sm_check_timeout() * 4. Events handled by server_sm_handle_event() * 5. Reset by server_sm_reset() after session completes */ typedef struct { /* * current_state - Current protocol state * * One of the server_state_t enum values. * Modified by server_sm_transition() only. */ server_state_t current_state; /* * previous_state - Previous state before current * * Used for debugging and state transition validation. */ server_state_t previous_state; /* * expected_seq - Expected sequence number in next DATA packet * * Initialized to 0 when START packet received. * Incremented after each valid DATA packet. * * Used to: * - Validate received DATA packet has correct seq_num * - Detect duplicate packets (seq_num < expected_seq) * - Detect skipped packets (seq_num > expected_seq) */ uint32_t expected_seq; /* * last_ack_sent - Sequence number of last ACK sent * * Used to detect: * - Duplicate ACKs (can happen if client didn't receive ACK) * - ACK retransmission needed */ uint32_t last_ack_sent; /* * state_entry_time - When current state was entered * * Set by server_sm_transition() using time(NULL). * Used to calculate time spent in current state. */ time_t state_entry_time; /* * last_activity - Last time packet received from client * * Updated when valid packet received from client. * Used to detect client failure: * If (time(NULL) - last_activity) > idle_timeout: * Transition to SERVER_TIMEOUT */ time_t last_activity; /* * idle_timeout - Maximum idle time before timeout (seconds) * * Default: SESSION_TIMEOUT (30 seconds) * * If no packet received from client within this time, assume: * - Client crashed * - Network partition * - All retries exhausted on client side * * Server transitions to SERVER_TIMEOUT and cleans up session. */ int idle_timeout; /* * session_id - Session identifier from client * * Extracted from START packet. * Used to validate that DATA/END packets belong to this session. * * Packets with different session_id are ignored or trigger * new session creation (if in LISTENING state). */ uint16_t session_id; } server_state_machine_t; /* * ================================================================================== * CORE STATE MACHINE FUNCTIONS * ================================================================================== */ /* * client_sm_init - Initialize client state machine * * Sets up initial state for new file transfer session: * - current_state = CLIENT_IDLE * - current_seq = 0 * - retry_count = 0 * - timeout_seconds = INITIAL_TIMEOUT (2) * - Generates session_id using utils_generate_session_id() * * @param sm: State machine structure to initialize * @param session_id: Session ID to use (0 = generate new ID) */ void client_sm_init(client_state_machine_t *sm, uint16_t session_id); /* * server_sm_init - Initialize server state machine * * Sets up initial state for server listening mode: * - current_state = SERVER_LISTENING * - expected_seq = 0 * - idle_timeout = SESSION_TIMEOUT (30) * * @param sm: State machine structure to initialize */ void server_sm_init(server_state_machine_t *sm); /* * client_sm_transition - Transition client to new state * * Validates transition is legal, updates state, and records timing information. * * @param sm: Client state machine * @param new_state: Desired next state * @param reason: Human-readable transition reason (for debugging) * @return: 0 on success, -1 if transition invalid */ int client_sm_transition(client_state_machine_t *sm, client_state_t new_state, const char *reason); /* * server_sm_transition - Transition server to new state * * Validates transition is legal, updates state, and records timing information. * * @param sm: Server state machine * @param new_state: Desired next state * @param reason: Human-readable transition reason (for debugging) * @return: 0 on success, -1 if transition invalid */ int server_sm_transition(server_state_machine_t *sm, server_state_t new_state, const char *reason); /* * client_sm_check_timeout - Check if client state has timed out * * Compares (current_time - state_entry_time) against timeout_seconds. * * @param sm: Client state machine * @return: 1 if timed out, 0 if not timed out */ int client_sm_check_timeout(client_state_machine_t *sm); /* * server_sm_check_timeout - Check if server session has timed out * * Compares (current_time - last_activity) against idle_timeout. * * @param sm: Server state machine * @return: 1 if timed out, 0 if not timed out */ int server_sm_check_timeout(server_state_machine_t *sm); /* * client_sm_handle_event - Process event and transition accordingly * * Event-driven state machine update: * - Validates event is valid for current state * - Performs appropriate transition * - Updates sequence numbers, retry counts * * @param sm: Client state machine * @param event: Event that occurred * @param event_data: Event-specific data (packet, error code, etc.) * @return: 0 on success, -1 on error */ int client_sm_handle_event(client_state_machine_t *sm, protocol_event_t event, void *event_data); /* * server_sm_handle_event - Process event and transition accordingly * * @param sm: Server state machine * @param event: Event that occurred * @param event_data: Event-specific data * @return: 0 on success, -1 on error */ int server_sm_handle_event(server_state_machine_t *sm, protocol_event_t event, void *event_data); /* * ================================================================================== * UTILITY FUNCTIONS * ================================================================================== */ /* * client_state_name - Get human-readable name for client state * @return: String name like "CLIENT_IDLE", "CLIENT_DATA_TRANSFER", etc. */ const char *client_state_name(client_state_t state); /* * server_state_name - Get human-readable name for server state * @return: String name like "SERVER_LISTENING", "SERVER_RECEIVING_DATA", etc. */ const char *server_state_name(server_state_t state); /* * event_name - Get human-readable name for protocol event * @return: String name like "EVENT_TIMEOUT", "EVENT_ACK_RECEIVED", etc. */ const char *event_name(protocol_event_t event); /* * ================================================================================== * ENHANCED STATE MACHINE FUNCTIONS * ================================================================================== */ /* * client_sm_is_valid_transition - Check if state transition is valid * * Validates state machine invariants. Prevents illegal transitions like: * - CLIENT_IDLE → CLIENT_END_SENT (skipping DATA phase) * - CLIENT_COMPLETED → CLIENT_DATA_TRANSFER (no restart from completed) * * @return: 1 if transition valid, 0 if invalid */ int client_sm_is_valid_transition(client_state_t current, client_state_t next); /* * server_sm_is_valid_transition - Check if state transition is valid * @return: 1 if transition valid, 0 if invalid */ int server_sm_is_valid_transition(server_state_t current, server_state_t next); /* * client_sm_can_recover - Check if error is recoverable * * Determines if client can retry after error: * - retry_count < MAX_RETRIES: recoverable * - retry_count >= MAX_RETRIES: not recoverable * * @return: 1 if can recover, 0 if cannot */ int client_sm_can_recover(const client_state_machine_t *sm); /* * server_sm_can_recover - Check if server error is recoverable * @return: 1 if can recover, 0 if cannot */ int server_sm_can_recover(const server_state_machine_t *sm); /* * client_sm_reset - Reset state machine for retry * * Resets to IDLE state while preserving session_id. * Used for retry after recoverable error. */ void client_sm_reset(client_state_machine_t *sm); /* * server_sm_reset - Reset state machine to LISTENING * * Used after session completion or error to prepare for next client. */ void server_sm_reset(server_state_machine_t *sm); /* * client_sm_handle_timeout_with_backoff - Handle timeout with exponential backoff * * Increments retry_count and calculates new timeout: * timeout = min(INITIAL_TIMEOUT * (2 ^ retry_count), MAX_TIMEOUT) * * @return: 0 if retry possible, -1 if max retries exceeded */ int client_sm_handle_timeout_with_backoff(client_state_machine_t *sm); /* * client_sm_get_statistics - Get state machine statistics * * Formats statistics string with: * - Current state and time in state * - Retry count * - Sequence number * - Session ID * * @param sm: Client state machine * @param buffer: Output buffer for statistics string * @param buffer_size: Size of output buffer * @return: Number of bytes written, or -1 on error */ int client_sm_get_statistics(const client_state_machine_t *sm, char *buffer, size_t buffer_size); /* * server_sm_get_statistics - Get state machine statistics * @return: Number of bytes written, or -1 on error */ int server_sm_get_statistics(const server_state_machine_t *sm, char *buffer, size_t buffer_size); #endif /* STATE_MACHINE_H */