/*
 * ==================================================================================
 * IFTP Protocol Implementation - ICMP File Transfer Protocol
 *
 * ISA Project - ICMP Covert Channel File Transfer System
 * Author: Roman Necas (xnecasr00)
 * Date: 13. October 2025
 *
 * DESCRIPTION:
 * This module implements the IFTP (ICMP File Transfer Protocol), a custom
 * application-layer protocol designed for reliable file transfer over ICMP.
 * It provides packet creation, parsing, validation, and integrity checking.
 *
 * KEY FUNCTIONS:
 * - create_iftp_packet() - Generic IFTP packet creation with CRC32
 * - parse_iftp_packet() - Parse and validate received IFTP packets
 * - create_start_packet() - Create START packet with file metadata
 * - create_data_packet() - Create DATA packet with encrypted file chunk
 * - create_end_packet() - Create END packet signaling transfer completion
 * - create_ack_packet() - Create ACK packet for acknowledgment
 * - create_nack_packet() - Create NACK packet with error information
 * - calculate_crc32() - CRC32 checksum for data integrity
 * - validate_iftp_header() - Comprehensive header validation
 *
 * IFTP PACKET STRUCTURE:
 * All IFTP packets consist of a 24-byte fixed-size header followed by
 * variable-length payload:
 *
 * [IFTP Header - 24 bytes]
 *   - magic (4 bytes): Protocol identifier (0xDEADBEEF)
 *   - version (2 bytes): Protocol version (currently 1)
 *   - session_id (2 bytes): Unique session identifier
 *   - msg_type (1 byte): Message type (START/DATA/END/ACK/NACK/HEARTBEAT)
 *   - flags (1 byte): Status flags (COMPRESSED/URGENT/FRAGMENTED/ENCRYPTED)
 *   - seq_num (4 bytes): Sequence number for ordering
 *   - fragment_current (4 bytes): Current fragment number
 *   - fragment_total (4 bytes): Total fragments in this chunk
 *   - payload_len (2 bytes): Length of payload in bytes
 *   - crc32 (4 bytes): CRC32 checksum of payload
 *
 * [Payload - 0 to MAX_PAYLOAD_SIZE bytes]
 *   - Content depends on msg_type
 *
 * MESSAGE TYPES:
 * 1. START (0x01): Initiate file transfer
 *    - Payload: iftp_metadata_t structure
 *      - filename[256]: Basename of file
 *      - file_size (8 bytes): Total file size in bytes
 *      - iv[16]: AES initialization vector
 *      - salt[32]: PBKDF2 salt for key derivation
 *
 * 2. DATA (0x02): File chunk transmission
 *    - Payload: Encrypted file data (up to 1400 bytes)
 *    - Uses seq_num for ordering
 *    - Supports fragmentation if chunk > MTU
 *
 * 3. END (0x03): Transfer completion signal
 *    - No payload (payload_len = 0)
 *    - seq_num indicates last data packet sequence
 *
 * 4. ACK (0x04): Positive acknowledgment
 *    - No payload
 *    - seq_num matches acknowledged packet
 *
 * 5. NACK (0x05): Negative acknowledgment with error
 *    - Payload: iftp_error_t structure
 *      - error_code: Error type (INVALID_PACKET, CHECKSUM_ERROR, etc.)
 *      - failed_seq_num: Sequence number that failed
 *      - error_message[128]: Human-readable error description
 *
 * 6. HEARTBEAT (0x06): Keep-alive message
 *    - No payload
 *    - Used to prevent session timeout
 *
 * TRANSFER FLOW:
 * Client                          Server
 *   |                               |
 *   |---(1) START packet ---------->|  Contains metadata (filename, size, IV, salt)
 *   |<---------- ACK (seq=0) ----(2)|  Acknowledges START
 *   |                               |
 *   |---(3) DATA packet (seq=1) --->|  First encrypted chunk
 *   |<---------- ACK (seq=1) ----(4)|  Acknowledges DATA
 *   |                               |
 *   |---(5) DATA packet (seq=2) --->|  Second encrypted chunk
 *   |<---------- ACK (seq=2) ----(6)|  Acknowledges DATA
 *   |                               |
 *   |       ... (more DATA) ...     |
 *   |                               |
 *   |---(N) END packet ------------->|  Transfer complete
 *   |<---------- ACK (seq=N) ----(N+1)| Final acknowledgment
 *   |                               |
 *
 * CRC32 INTEGRITY CHECKING:
 * - CRC32 (Cyclic Redundancy Check, 32-bit) provides data integrity
 * - Polynomial: 0xEDB88320 (reversed 0x04C11DB7)
 * - Initial value: 0xFFFFFFFF
 * - Final XOR: 0xFFFFFFFF
 * - Calculated over payload only (not header)
 * - Detects single-bit errors, burst errors, and most multi-bit errors
 * - NOT cryptographically secure (use for error detection, not authentication)
 *
 * NETWORK BYTE ORDER:
 * - All multi-byte fields stored in network byte order (big-endian)
 * - Functions iftp_header_to_network() and iftp_header_to_host()
 *   handle conversion using htonl()/htons() and ntohl()/ntohs()
 * - Ensures interoperability across different CPU architectures
 *
 * FRAGMENTATION SUPPORT:
 * - Large DATA payloads can be split into multiple fragments
 * - Each fragment has same seq_num but different fragment_current
 * - fragment_current: 0-indexed fragment number (0, 1, 2, ...)
 * - fragment_total: Total number of fragments for this sequence
 * - Receiver reassembles fragments before decryption
 * - Currently not used (chunk_size ≤ 1400), but framework is in place
 *
 * ERROR CODES (NACK):
 * - IFTP_ERROR_NONE (0x00): No error
 * - IFTP_ERROR_INVALID_PACKET (0x01): Malformed packet
 * - IFTP_ERROR_SEQUENCE_ERROR (0x02): Unexpected sequence number
 * - IFTP_ERROR_CHECKSUM_ERROR (0x03): CRC32 mismatch
 * - IFTP_ERROR_CRYPTO_ERROR (0x04): Decryption failure
 * - IFTP_ERROR_FILE_ERROR (0x05): File I/O error
 * - IFTP_ERROR_TIMEOUT (0x06): Session timeout
 * - IFTP_ERROR_SESSION_ERROR (0x07): Invalid session ID
 *
 * IMPLEMENTATION NOTES:
 * - All packet creation functions return 0 on success, -1 on failure
 * - parse_iftp_packet() validates magic number and CRC32
 * - validate_iftp_header() performs comprehensive header checks
 * - Supports protocol extensibility through version field
 * - CRC32 table is pre-computed for fast checksum calculation
 *
 * SECURITY CONSIDERATIONS:
 * - CRC32 provides error detection, NOT security (attacker can forge)
 * - Security comes from AES-256 encryption at crypto layer
 * - Protocol assumes ICMP layer provides basic packet delivery
 * - No built-in replay attack protection (could add timestamp/nonce)
 * ==================================================================================
 */

#include "secret.h"

/* CRC32 calculation:
 * - Standard CRC32 (IEEE 802.3) used to detect packet corruption.
 * - Fast lookup via precomputed table.
 * - Not a cryptographic MAC — do not rely on it for authentication.
 */
static const uint32_t crc32_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

uint32_t calculate_crc32(const void *data, size_t len) {
    uint32_t crc = 0xFFFFFFFF;
    const uint8_t *bytes = (const uint8_t *)data;

    for (size_t i = 0; i < len; i++) {
        crc = crc32_table[(crc ^ bytes[i]) & 0xFF] ^ (crc >> 8);
    }

    return crc ^ 0xFFFFFFFF;
}

/* Convert header fields to network byte order:
 * - Ensures multi-byte fields are sent in big-endian across architectures.
 */
static void iftp_header_to_network(iftp_header_t *header) {
    header->magic = htonl(header->magic);
    header->session_id = htons(header->session_id);
    header->seq_num = htonl(header->seq_num);
    header->fragment_current = htonl(header->fragment_current);
    header->fragment_total = htonl(header->fragment_total);
    header->payload_len = htons(header->payload_len);
    header->crc32 = htonl(header->crc32);
}

/* Convert header fields from network byte order to host:
 * - Must be called after receiving header bytes before inspection.
 */
static void iftp_header_to_host(iftp_header_t *header) {
    header->magic = ntohl(header->magic);
    header->session_id = ntohs(header->session_id);
    header->seq_num = ntohl(header->seq_num);
    header->fragment_current = ntohl(header->fragment_current);
    header->fragment_total = ntohl(header->fragment_total);
    header->payload_len = ntohs(header->payload_len);
    header->crc32 = ntohl(header->crc32);
}

/* Create a full IFTP packet in packet_buffer:
 * - Copies header and payload, computes CRC32 over payload.
 * - Returns 0 on success, -1 on error (invalid args or oversized payload).
 * - Caller must ensure packet_buffer is large enough.
 */
int create_iftp_packet(const iftp_header_t *header, const void *payload,
                      uint8_t *packet_buffer, size_t *packet_len) {
    if (!header || !packet_buffer || !packet_len) {
        print_error("Invalid parameters for packet creation");
        return -1;
    }

    iftp_header_t net_header = *header;
    size_t payload_size = payload ? header->payload_len : 0;

    if (sizeof(iftp_header_t) + payload_size > MAX_PAYLOAD_SIZE + sizeof(iftp_header_t)) {
        print_error("Packet size exceeds maximum allowed");
        return -1;
    }

    uint8_t *temp_data = malloc(sizeof(iftp_header_t) + payload_size);
    if (!temp_data) {
        print_error("Memory allocation failed");
        return -1;
    }

    memcpy(temp_data, &net_header, sizeof(iftp_header_t));
    if (payload && payload_size > 0) {
        memcpy(temp_data + sizeof(iftp_header_t), payload, payload_size);
    }

    net_header.crc32 = calculate_crc32(temp_data + sizeof(iftp_header_t), payload_size);

    iftp_header_to_network(&net_header);

    memcpy(packet_buffer, &net_header, sizeof(iftp_header_t));
    if (payload && payload_size > 0) {
        memcpy(packet_buffer + sizeof(iftp_header_t), payload, payload_size);
    }

    *packet_len = sizeof(iftp_header_t) + payload_size;

    free(temp_data);
    return 0;
}

/* Parse and validate received IFTP packet:
 * - Verifies header length, magic value, and CRC32 over payload.
 * - Converts header to host order before returning.
 * - Returns 0 on success, -1 on failure.
 */
int parse_iftp_packet(const uint8_t *packet_buffer, size_t packet_len,
                     iftp_header_t *header, uint8_t *payload, size_t *payload_len) {
    if (!packet_buffer || !header || !payload_len) {
        print_error("Invalid parameters for packet parsing");
        return -1;
    }

    if (packet_len < sizeof(iftp_header_t)) {
        return -1;
    }

    memcpy(header, packet_buffer, sizeof(iftp_header_t));
    iftp_header_to_host(header);

    if (header->magic != IFTP_MAGIC) {
        return -1;
    }

    size_t expected_payload_len = header->payload_len;
    if (packet_len != sizeof(iftp_header_t) + expected_payload_len) {
        print_error("Packet length mismatch");
        return -1;
    }

    if (expected_payload_len > 0) {
        if (payload) {
            memcpy(payload, packet_buffer + sizeof(iftp_header_t), expected_payload_len);
        }

        uint32_t calculated_crc = calculate_crc32(packet_buffer + sizeof(iftp_header_t),
                                                expected_payload_len);
        if (calculated_crc != header->crc32) {
            print_error("CRC checksum mismatch - packet corrupted");
            return -1;
        }
    }

    *payload_len = expected_payload_len;
    return 0;
}

/* Create START packet containing transfer metadata:
 * - payload contains filename, file_size, IV and salt required for crypto.
 * - session_id must be non-zero.
 */
int create_start_packet(uint16_t session_id, const iftp_metadata_t *metadata,
                       uint8_t *packet_buffer, size_t *packet_len) {
    if (!metadata || !packet_buffer || !packet_len) {
        print_error("Invalid parameters for START packet creation");
        return -1;
    }

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_START;
    header.flags = 0;
    header.seq_num = 0;
    header.fragment_current = 0;
    header.fragment_total = 0;
    header.payload_len = sizeof(iftp_metadata_t);

    return create_iftp_packet(&header, metadata, packet_buffer, packet_len);
}

/* Create DATA packet with encrypted chunk:
 * - seq_num identifies ordering; fragmentation fields support reassembly.
 */
int create_data_packet(uint16_t session_id, uint32_t seq_num, uint32_t frag_current,
                      uint32_t frag_total, const void *data, size_t data_len,
                      uint8_t *packet_buffer, size_t *packet_len) {
    if (!data || !packet_buffer || !packet_len) {
        print_error("Invalid parameters for DATA packet creation");
        return -1;
    }

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_DATA;
    header.flags = 0;
    header.seq_num = seq_num;
    header.fragment_current = frag_current;
    header.fragment_total = frag_total;
    header.payload_len = data_len;

    return create_iftp_packet(&header, data, packet_buffer, packet_len);
}

/* Create END packet signalling transfer completion:
 * - No payload. seq_num indicates last data packet sequence.
 */
int create_end_packet(uint16_t session_id, uint32_t seq_num,
                     uint8_t *packet_buffer, size_t *packet_len) {
    if (!packet_buffer || !packet_len) {
        print_error("Invalid parameters for END packet creation");
        return -1;
    }

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_END;
    header.flags = 0;
    header.seq_num = seq_num;
    header.fragment_current = 0;
    header.fragment_total = 0;
    header.payload_len = 0;

    return create_iftp_packet(&header, NULL, packet_buffer, packet_len);
}

/* Create ACK packet:
 * - Acknowledges receipt of a specific seq_num (or START with seq=0).
 */
int create_ack_packet(uint16_t session_id, uint32_t seq_num,
                     uint8_t *packet_buffer, size_t *packet_len) {
    ERROR_CHECK(packet_buffer != NULL, ERR_INVALID_ARGS, "Packet buffer cannot be NULL");
    ERROR_CHECK(packet_len != NULL, ERR_INVALID_ARGS, "Packet length pointer cannot be NULL");

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_ACK;
    header.flags = 0;
    header.seq_num = seq_num;
    header.fragment_current = 0;
    header.fragment_total = 0;
    header.payload_len = 0;


    return create_iftp_packet(&header, NULL, packet_buffer, packet_len);
}


/* Create NACK packet with error info:
 * - Includes an iftp_error_t payload describing failure reason and seq number.
 */
int create_nack_packet(uint16_t session_id, uint32_t seq_num, iftp_error_code_t error_code,
                      const char *error_message, uint8_t *packet_buffer, size_t *packet_len) {
    ERROR_CHECK(packet_buffer != NULL, ERR_INVALID_ARGS, "Packet buffer cannot be NULL");
    ERROR_CHECK(packet_len != NULL, ERR_INVALID_ARGS, "Packet length pointer cannot be NULL");

    iftp_error_t error_data = {0};
    error_data.error_code = error_code;
    error_data.failed_seq_num = seq_num;

    if (error_message) {
        strncpy(error_data.error_message, error_message, sizeof(error_data.error_message) - 1);
        error_data.error_message[sizeof(error_data.error_message) - 1] = '\0';
    }

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_NACK;
    header.flags = 0;
    header.seq_num = seq_num;
    header.fragment_current = 0;
    header.fragment_total = 0;
    header.payload_len = sizeof(iftp_error_t);

    return create_iftp_packet(&header, &error_data, packet_buffer, packet_len);
}

/* Create HEARTBEAT packet:
 * - Lightweight keep-alive to prevent session timeout.
 */
int create_heartbeat_packet(uint16_t session_id, uint32_t seq_num,
                           uint8_t *packet_buffer, size_t *packet_len) {
    ERROR_CHECK(packet_buffer != NULL, ERR_INVALID_ARGS, "Packet buffer cannot be NULL");
    ERROR_CHECK(packet_len != NULL, ERR_INVALID_ARGS, "Packet length pointer cannot be NULL");

    iftp_header_t header = {0};
    header.magic = IFTP_MAGIC;
    header.session_id = session_id;
    header.msg_type = IFTP_HEARTBEAT;
    header.flags = 0;
    header.seq_num = seq_num;
    header.fragment_current = 0;
    header.fragment_total = 0;
    header.payload_len = 0;

    return create_iftp_packet(&header, NULL, packet_buffer, packet_len);
}

/* Validate IFTP header fields:
 * - Checks magic, session_id, valid msg_type, payload size, and fragment consistency.
 * - Returns 0 for valid header, -1 for invalid header with appropriate ERROR_SET.
 */
int validate_iftp_header(const iftp_header_t *header) {
    ERROR_CHECK(header != NULL, ERR_INVALID_ARGS, "Header cannot be NULL");

    if (header->magic != IFTP_MAGIC) {
        ERROR_SET(ERR_PROTOCOL_INVALID, "Invalid magic number");
        return -1;
    }

    if (header->session_id == 0) {
        ERROR_SET(ERR_PROTOCOL_INVALID, "Invalid session ID");
        return -1;
    }

    switch (header->msg_type) {
        case IFTP_START:
        case IFTP_DATA:
        case IFTP_END:
        case IFTP_ACK:
        case IFTP_NACK:
        case IFTP_HEARTBEAT:
            break;
        default:
            ERROR_SET(ERR_PROTOCOL_INVALID, "Unknown message type");
            return -1;
    }

    if (header->payload_len > MAX_PAYLOAD_SIZE) {
        ERROR_SET(ERR_PROTOCOL_INVALID, "Payload size exceeds maximum");
        return -1;
    }

    /* Validate fragment numbers */
    if (header->fragment_current > header->fragment_total) {
        ERROR_SET(ERR_PROTOCOL_INVALID, "Invalid fragment numbers");
        return -1;
    }

    return 0;
}

/* Human-readable helpers for message types and error codes:
 * - Useful for logging and debugging.
 */
const char *iftp_msg_type_to_string(iftp_msg_type_t msg_type) {
    switch (msg_type) {
        case IFTP_START: return "START";
        case IFTP_DATA: return "DATA";
        case IFTP_END: return "END";
        case IFTP_ACK: return "ACK";
        case IFTP_NACK: return "NACK";
        case IFTP_HEARTBEAT: return "HEARTBEAT";
        default: return "UNKNOWN";
    }
}

const char *iftp_error_to_string(iftp_error_code_t error_code) {
    switch (error_code) {
        case IFTP_ERROR_NONE: return "No error";
        case IFTP_ERROR_INVALID_PACKET: return "Invalid packet";
        case IFTP_ERROR_SEQUENCE_ERROR: return "Sequence error";
        case IFTP_ERROR_CHECKSUM_ERROR: return "Checksum error";
        case IFTP_ERROR_CRYPTO_ERROR: return "Cryptographic error";
        case IFTP_ERROR_FILE_ERROR: return "File error";
        case IFTP_ERROR_TIMEOUT: return "Timeout";
        case IFTP_ERROR_SESSION_ERROR: return "Session error";
        default: return "Unknown error";
    }
}

/* Protocol session lifecycle helpers:
 * - Initialize session state, update send/recv sequence expectations,
 *   and check for session timeouts.
 * - Ensures sequential integrity of transfer (seq mismatch sets protocol error).
 */
int protocol_session_init(protocol_session_t *session, uint16_t session_id) {
    ERROR_CHECK(session != NULL, ERR_INVALID_ARGS, "Session cannot be NULL");

    memset(session, 0, sizeof(protocol_session_t));
    session->session_id = session_id;
    session->next_seq_send = 1;  /* Start at 1, 0 is reserved for START */
    session->next_seq_recv = 1;
    session->last_activity = time(NULL);

    return 0;
}

int protocol_session_update_send(protocol_session_t *session, uint32_t seq_num) {
    ERROR_CHECK(session != NULL, ERR_INVALID_ARGS, "Session cannot be NULL");

    if (seq_num != session->next_seq_send) {
        ERROR_SET(ERR_PROTOCOL_SEQUENCE, "Sequence number mismatch on send");
        return -1;
    }

    session->next_seq_send++;
    session->last_activity = time(NULL);

    return 0;
}

int protocol_session_update_recv(protocol_session_t *session, uint32_t seq_num) {
    ERROR_CHECK(session != NULL, ERR_INVALID_ARGS, "Session cannot be NULL");

    if (seq_num != session->next_seq_recv) {
        ERROR_SET(ERR_PROTOCOL_SEQUENCE, "Sequence number mismatch on receive");
        return -1;
    }

    session->next_seq_recv++;
    session->last_ack_sent = seq_num;
    session->last_activity = time(NULL);

    return 0;
}

/* Check whether a session has timed out:
 * - Returns 1 if timed out (> timeout_seconds), 0 otherwise.
 */
int protocol_session_check_timeout(const protocol_session_t *session, int timeout_seconds) {
    ERROR_CHECK(session != NULL, ERR_INVALID_ARGS, "Session cannot be NULL");

    time_t now = time(NULL);
    return (now - session->last_activity) > timeout_seconds;
}