/** @file protocol.c * @date April 24, 2009 */ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include "common.h" #include "protocol.h" /** A structure that helps read from a buffer. */ typedef struct reader_t { /** A pointer to the buffer. */ const msg_buffer_t * buffer; /** The index of the next byte to read. */ size_t index; } reader_t; /** A structure that helps write to a buffer. */ typedef struct writer_t { /** A pointer to the buffer. */ msg_buffer_t * buffer; } writer_t; static void init_reader(reader_t * reader, const msg_buffer_t * buffer); static void init_writer(writer_t * writer, msg_buffer_t * buffer); static int read_uchar(reader_t * reader, u_char * value); static int read_ushort(reader_t * reader, u_short * value); static int read_ulong(reader_t * reader, u_long * value); static int read_string(reader_t * reader, char * value, size_t size); static void write_uchar(const writer_t * writer, u_char value); static void write_ushort(const writer_t * writer, u_short value); static void write_ulong(const writer_t * writer, u_long value); static void write_string(const writer_t * writer, const char * value); /* ------------------------------------------------------------------------- */ int msg_abort_read(msg_abort_t * msg, const msg_buffer_t * buffer) { reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->id)); RETURN_IF_FAILURE(read_string(&reader, msg->reason, REASON_SIZE)); /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_ABORT); /* TODO Verify no exta bytes were sent for this message and all others. */ /* RETURN_IF_FALSE(reader->index == msg->size); */ /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_abort_write(msg_buffer_t * buffer, const msg_abort_t * msg) { writer_t writer; assert(NULL != buffer); assert(NULL != msg); assert(msg->type == MSG_TYPE_ABORT); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->id); write_string(&writer, msg->reason); } /* ------------------------------------------------------------------------- */ int msg_data_read(msg_data_t * msg, const msg_buffer_t * buffer) { size_t index; reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->id)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->packet)); /* Read the array. */ for (index = 0; reader.index < buffer->size; index++) { u_char ch; RETURN_IF_FAILURE(read_uchar(&reader, &ch)); msg->data[index] = ch; } /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_DATA); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_data_write( msg_buffer_t * buffer, const msg_data_t * msg, u_short datasize) { writer_t writer; u_short index; assert(NULL != buffer); assert(NULL != msg); assert(datasize >= DATA_MIN_SIZE); assert(datasize <= DATA_MAX_SIZE); assert(msg->type == MSG_TYPE_DATA); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->id); write_ulong(&writer, msg->packet); /* Write the array. */ for (index = 0; index < datasize; index++) write_uchar(&writer, msg->data[index]); } /* ------------------------------------------------------------------------- */ int msg_done_read(msg_done_t * msg, const msg_buffer_t * buffer) { reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->id)); /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_DONE); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_done_write(msg_buffer_t * buffer, const msg_done_t * msg) { writer_t writer; assert(NULL != buffer); assert(NULL != msg); assert(msg->type == MSG_TYPE_DONE); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->id); } /* ------------------------------------------------------------------------- */ int msg_init_read(msg_init_t * msg, const msg_buffer_t * buffer) { reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->checksum)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->filesize)); RETURN_IF_FAILURE(read_ushort(&reader, &msg->datasize)); RETURN_IF_FAILURE(read_string(&reader, msg->name, NAME_SIZE)); /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_INIT); RETURN_IF_FALSE(msg->filesize > 0); RETURN_IF_FALSE(msg->datasize >= DATA_MIN_SIZE); RETURN_IF_FALSE(msg->datasize <= DATA_MAX_SIZE); RETURN_IF_FAILURE(protocol_name_verify(msg->name)); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_init_write(msg_buffer_t * buffer, const msg_init_t * msg) { writer_t writer; assert(NULL != buffer); assert(NULL != msg); assert(msg->type == MSG_TYPE_INIT); assert(msg->filesize > 0); assert(msg->datasize >= DATA_MIN_SIZE); assert(msg->datasize <= DATA_MAX_SIZE); assert(EXIT_SUCCESS == protocol_name_verify(msg->name)); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->checksum); write_ulong(&writer, msg->filesize); write_ushort(&writer, msg->datasize); write_string(&writer, msg->name); } /* ------------------------------------------------------------------------- */ int msg_retry_read(msg_retry_t * msg, const msg_buffer_t * buffer) { reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->id)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->first)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->last)); /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_RETRY); RETURN_IF_FALSE(msg->first <= msg->last); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_retry_write(msg_buffer_t * buffer, const msg_retry_t * msg) { writer_t writer; assert(NULL != buffer); assert(NULL != msg); assert(msg->type == MSG_TYPE_RETRY); assert(msg->first <= msg->last); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->id); write_ulong(&writer, msg->first); write_ulong(&writer, msg->last); } /* ------------------------------------------------------------------------- */ int msg_start_read(msg_start_t * msg, const msg_buffer_t * buffer) { reader_t reader; assert(NULL != msg); assert(NULL != buffer); /* Initialize the reader. */ init_reader(&reader, buffer); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(&reader, &msg->type)); RETURN_IF_FAILURE(read_ulong(&reader, &msg->id)); /* Verify the values. */ RETURN_IF_FALSE(msg->type == MSG_TYPE_START); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ void msg_start_write(msg_buffer_t * buffer, const msg_start_t * msg) { writer_t writer; assert(NULL != buffer); assert(NULL != msg); assert(msg->type == MSG_TYPE_START); /* Initialize the writer and write the values. */ init_writer(&writer, buffer); write_uchar(&writer, msg->type); write_ulong(&writer, msg->id); } /* ------------------------------------------------------------------------- */ u_short protocol_data_size(const msg_init_t * msg, u_long packet) { u_long packets; assert(NULL != msg); /* Determine the total number of packets. */ packets = protocol_packet_count(msg); assert(packet < packets); /* Return the remainder for the last packet. */ if (packet == packets - 1) return msg->filesize % msg->datasize; /* Otherwise, return the full data size. */ return msg->datasize; } /* ------------------------------------------------------------------------- */ int protocol_name_verify(const char * name) { size_t len; size_t dots; assert(NULL != name); /* Check the length. */ len = strlen(name); RETURN_IF_FALSE(len > 0); RETURN_IF_FALSE(len <= NAME_MAX_LENGTH); /* Check each character. */ for (dots = 0; *name != '\0'; name++) { if (isalpha((int)*name)) continue; /* Check for too many dots. */ RETURN_IF_FALSE(*name == '.'); RETURN_IF_FALSE(++dots == 1); } /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ u_long protocol_packet_count(const msg_init_t * msg) { u_long packets; assert(NULL != msg); /* First, divide the file size by the datasize. */ packets = msg->filesize / msg->datasize; /* Add an additional packet if there is a remainder. */ if (msg->filesize % msg->datasize != 0) packets++; /* Return the total number of packets. */ return packets; } /* ------------------------------------------------------------------------- */ long protocol_packet_offset(const msg_init_t * msg, u_long packet) { assert(NULL != msg); assert(packet < protocol_packet_count(msg)); return (long)(packet * msg->datasize); } /* ------------------------------------------------------------------------- */ /** Initializes the reader based on the buffer. * * @param reader The writer. * @param buffer The buffer. */ static void init_reader(reader_t * reader, const msg_buffer_t * buffer) { assert(NULL != reader); assert(NULL != buffer); reader->buffer = buffer; reader->index = 0; } /* ------------------------------------------------------------------------- */ /** Initializes the writer based on the buffer. * * @param writer The writer. * @param buffer The buffer. */ static void init_writer(writer_t * writer, msg_buffer_t * buffer) { assert(NULL != writer); assert(NULL != buffer); writer->buffer = buffer; writer->buffer->size = 0; } /* ------------------------------------------------------------------------- */ /** Reads a string from the buffer. * * @param reader The destination. * @param value The value. * @param size The maximum size of the string. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int read_string(reader_t * reader, char * value, size_t size) { size_t count; assert(NULL != reader); assert(NULL != value); /* Loop over each character. */ count = 0; for (;;) { u_char ch; /* Read and store the next character. */ RETURN_IF_FAILURE(read_uchar(reader, &ch)); value[count++] = (char)ch; /* Check for the NULL terminator. */ if (ch == 0) break; /* Ensure the string length is valid. */ RETURN_IF_FALSE(count < size); } /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ /** Reads a u_char from the buffer. * * @param reader The destination. * @param value The value. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int read_uchar(reader_t * reader, u_char * value) { assert(NULL != reader); assert(NULL != reader->buffer); assert(NULL != value); /* Verify this is a valid character. */ RETURN_IF_FALSE(reader->index < reader->buffer->size); /* Read the value. */ *value = reader->buffer->data[reader->index++]; /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ /** Reads a u_long from the buffer. * * @param reader The destination. * @param value The value. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int read_ulong(reader_t * reader, u_long * value) { u_char ch[4]; assert(NULL != reader); assert(NULL != value); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(reader, &ch[0])); RETURN_IF_FAILURE(read_uchar(reader, &ch[1])); RETURN_IF_FAILURE(read_uchar(reader, &ch[2])); RETURN_IF_FAILURE(read_uchar(reader, &ch[3])); /* Combine the values. */ *value = (u_long)( ((u_long)ch[0] << 24) | ((u_long)ch[1] << 16) | ((u_long)ch[2] << 8) | ((u_long)ch[3] << 0)); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ /** Reads a u_short from the buffer. * * @param reader The destination. * @param value The value. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int read_ushort(reader_t * reader, u_short * value) { u_char ch[2]; assert(NULL != reader); assert(NULL != value); /* Read the values. */ RETURN_IF_FAILURE(read_uchar(reader, &ch[0])); RETURN_IF_FAILURE(read_uchar(reader, &ch[1])); /* Combine the values. */ *value = (u_short)((ch[0] << 8) | ch[1]); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ /** Writes a string to a buffer. * * @param writer The writer. * @param value The value. */ static void write_string(const writer_t * writer, const char * value) { assert(NULL != value); /* Loop over each character. */ for (;;) { /* Write the character. */ write_uchar(writer, (u_char)(*value)); /* Check for the NULL terminator. */ if (*value == '\0') break; /* Advance to the next character in the string. */ value++; } } /* ------------------------------------------------------------------------- */ /** Writes a u_char to a buffer. * * @param writer The writer. * @param value The value. */ static void write_uchar(const writer_t * writer, u_char value) { assert(NULL != writer); assert(NULL != writer->buffer); assert(writer->buffer->size < PACKET_MAX_SIZE); /* Write the value. */ writer->buffer->data[writer->buffer->size++] = value; } /* ------------------------------------------------------------------------- */ /** Writes a u_long to a buffer. * * @param writer The writer. * @param value The value. */ static void write_ulong(const writer_t * writer, u_long value) { /* Write the values. */ write_uchar(writer, (u_char)((value >> 24) & 0xff)); write_uchar(writer, (u_char)((value >> 16) & 0xff)); write_uchar(writer, (u_char)((value >> 8) & 0xff)); write_uchar(writer, (u_char)((value >> 0) & 0xff)); } /* ------------------------------------------------------------------------- */ /** Writes a u_short to a buffer. * * @param writer The writer. * @param value The value. */ static void write_ushort(const writer_t * writer, u_short value) { /* Write the values. */ write_uchar(writer, (u_char)((value >> 8) & 0xff)); write_uchar(writer, (u_char)((value >> 0) & 0xff)); }