921 lines
28 KiB
C
921 lines
28 KiB
C
/*
|
|
* ==================================================================================
|
|
* 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 <time.h>
|
|
#include <stdint.h>
|
|
|
|
/*
|
|
* ==================================================================================
|
|
* 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 */
|