/*
* File Name: messages.c
* Author: Jade Cheng
* Date: March 29, 2009
* Course: ICS 451
* Assignment: Project 2
*/
#include "common.h"
#include "messages.h"
/** The control message header. */
#define CTRL_MSG_HEADER 0xcc
/** The data message header code. */
#define DATA_MSG_HEADER 0xdd
/** The listing message selector byte. */
#define LISTING_SELECTOR 0x4c
/** The request message selector byte. */
#define REQUEST_SELECTOR 0x52
/** The try message selector byte. */
#define TRY_SELECTOR 0x54
static int read_array(msg_buf_t * src, u_char * dst, u_short len);
static int read_name(msg_buf_t * src, char * dst);
static int read_uchar(msg_buf_t * src, u_char * dst);
static int read_ulong(msg_buf_t * src, u_long * dst);
static int read_ushort(msg_buf_t * src, u_short * dst);
static void write_array(msg_buf_t * dst, const u_char * towrite, size_t len);
static void write_name(msg_buf_t * dst, const char * towrite);
static void write_uchar(msg_buf_t * dst, u_char towrite);
static void write_ulong(msg_buf_t * dst, u_long towrite);
static void write_ushort(msg_buf_t * dst, u_short towrite);
/* ------------------------------------------------------------------------ */
extern int data_msg_read(msg_buf_t * src, data_msg_t * dst)
{
u_char header;
u_short count;
assert(src != NULL);
assert(dst != NULL);
/* Start to read from index 0 and increment as the function reads. */
src->index = 0;
/* Read the header of the message, which should be 0xdd. */
RETURN_IF_FAILED(read_uchar(src, &header));
RETURN_IF_FALSE(header == (u_char)DATA_MSG_HEADER);
/* Read the name of the file. */
RETURN_IF_FAILED(read_name(src, dst->name));
/* Read the size of the data contents. */
RETURN_IF_FAILED(read_ushort(src, &count));
RETURN_IF_FALSE(count <= MAX_CONTENT_LENGTH);
dst->size = count;
/* Read the data contents. */
RETURN_IF_FAILED(read_array(src, dst->data, dst->size));
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern void data_msg_write(msg_buf_t * dst, const data_msg_t * src)
{
int name_len;
assert(dst != NULL);
assert(src != NULL);
assert(src->size <= MAX_CONTENT_LENGTH);
/* Reset the destination message buffer. */
memset(dst, 0, sizeof(*dst));
name_len = strlen(src->name);
/* Write the header. */
write_uchar(dst, (u_char)DATA_MSG_HEADER);
/* Write the name of the file. */
write_name(dst, src->name);
/* Write the length of the data contents in bytes. */
write_ushort(dst, src->size);
/* Write the data contents. */
write_array(dst, src->data, src->size);
}
/* ------------------------------------------------------------------------ */
extern int listing_msg_read(msg_buf_t * src, listing_msg_t * dst)
{
u_char header1, header2;
u_short count;
int i;
assert(src != NULL);
assert(dst != NULL);
/* Start to read from index 0 and increment as the function reads. */
src->index = 0;
/* Read the first header of the message, which should be 0xcc. */
RETURN_IF_FAILED(read_uchar(src, &header1));
RETURN_IF_FALSE(header1 == (u_char)CTRL_MSG_HEADER);
/* Read the second header of the message, which should be 0x4c. */
RETURN_IF_FAILED(read_uchar(src, &header2));
RETURN_IF_FALSE(header2 == (u_char)LISTING_SELECTOR);
/* Read the number of items in the message. */
RETURN_IF_FAILED(read_ushort(src, &count));
RETURN_IF_FALSE(count <= MAX_LISTING_COUNT);
dst->count = count;
/* Read the each name contained in each item. */
for (i = 0; i < dst->count; i++)
RETURN_IF_FAILED(read_name(src, dst->entries[i]));
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern void listing_msg_write(msg_buf_t * dst, listing_msg_t * src)
{
int i;
assert(dst != NULL);
assert(src != NULL);
assert(src->count <= MAX_LISTING_COUNT);
/* Reset the destination message buffer. */
memset(dst, 0, sizeof(*dst));
/* Write the first header. */
write_uchar(dst, (u_char)CTRL_MSG_HEADER);
/* Write the second header. */
write_uchar(dst, (u_char)LISTING_SELECTOR);
/* Write the number of items contained in this listing message. */
write_ushort(dst, src->count);
/* Write all items, which are file names. */
for (i = 0; i < src->count; i++)
write_name(dst, src->entries[i]);
}
/* ------------------------------------------------------------------------ */
extern int request_msg_read(msg_buf_t * src, request_msg_t * dst)
{
u_char header1, header2;
assert(src != NULL);
assert(dst != NULL);
/* Start to read from index 0 and increment as the function reads. */
src->index = 0;
RETURN_IF_FAILED(read_uchar(src, &header1));
RETURN_IF_FALSE(header1 == (u_char)CTRL_MSG_HEADER);
/* Read the header of the message, which should be 0x52. */
RETURN_IF_FAILED(read_uchar(src, &header2));
RETURN_IF_FALSE(header2 == (u_char)REQUEST_SELECTOR);
/* Read the name of the file contained in the message. */
RETURN_IF_FAILED(read_name(src, dst->name));
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern void request_msg_write(msg_buf_t * dst, request_msg_t * src)
{
assert(dst != NULL);
assert(src != NULL);
/* Reset the destination message buffer. */
memset(dst, 0, sizeof(*dst));
/* Write the first header. */
write_uchar(dst, (u_char)CTRL_MSG_HEADER);
/* Write the second header. */
write_uchar(dst, (u_char)REQUEST_SELECTOR);
/* Write the name of file contained in the request message. */
write_name(dst, src->name);
}
/* ------------------------------------------------------------------------ */
extern int try_msg_read(msg_buf_t * src, try_msg_t * dst)
{
u_char header1, header2;
int i;
assert(src != NULL);
assert(dst != NULL);
/* Start to read from index 0 and increment as the function reads. */
src->index = 0;
/* Read the first header of the message, which should be 0xcc. */
RETURN_IF_FAILED(read_uchar(src, &header1));
RETURN_IF_FALSE(header1 == (u_char)CTRL_MSG_HEADER);
/* Read the second header of the message, which should be 0x54. */
RETURN_IF_FAILED(read_uchar(src, &header2));
RETURN_IF_FALSE(header2 == (u_char)TRY_SELECTOR);
/* Read the name of the file contained in the message. */
RETURN_IF_FAILED(read_name(src, dst->name));
/* Read the number of peer item contained in the message. */
RETURN_IF_FAILED(read_ushort(src, &dst->count));
RETURN_IF_FALSE(dst->count <= MAX_TRY_COUNT);
/* Read each peer contained in each item. */
for (i = 0; i < dst->count; i++) {
RETURN_IF_FAILED(read_ulong(src, &dst->peers[i].addr));
RETURN_IF_FAILED(read_ushort(src, &dst->peers[i].port));
}
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern void try_msg_write(msg_buf_t * dst, try_msg_t * src)
{
int i;
assert(dst != NULL);
assert(src != NULL);
assert(src->count <= MAX_TRY_COUNT);
/* Reset the destination message buffer. */
memset(dst, 0, sizeof(*dst));
/* Write the first header. */
write_uchar(dst, (u_char)CTRL_MSG_HEADER);
/* Write the second header. */
write_uchar(dst, (u_char)TRY_SELECTOR);
/* Write the name of the file contained in the try message. */
write_name(dst, src->name);
/* Write the number of peers contained in the try message. */
write_ushort(dst, src->count);
/* Write each peer. */
for (i = 0; i < src->count; i++) {
write_ulong(dst, src->peers[i].addr);
write_ushort(dst, src->peers[i].port);
}
}
/* ------------------------------------------------------------------------ */
extern msg_type get_msg_type(msg_buf_t * src)
{
assert (src != NULL);
if (src->size < 2)
return msg_type_unknown;
/* Check for data messages. */
if (src->buffer[0] == (u_char)DATA_MSG_HEADER)
return msg_type_data;
/* Check for control messages. */
if (src->buffer[0] == (u_char)CTRL_MSG_HEADER) {
if (src->buffer[1] == (u_char)LISTING_SELECTOR)
return msg_type_listing;
if (src->buffer[1] == (u_char)REQUEST_SELECTOR)
return msg_type_request;
if (src->buffer[1] == (u_char)TRY_SELECTOR)
return msg_type_try;
}
return msg_type_unknown;
}
/* ------------------------------------------------------------------------ */
/**
* Reads an array of u_char from a message buffer.
*
* @param src The message buffer.
* @param dst An array of uchar memory.
* @param len The length in bytes to read.
*
* @return EXIT_SUCCESS or EXIT_FAILURE.
*/
static int read_array(msg_buf_t * src, u_char * dst, u_short len)
{
u_char a;
size_t i;
int j;
assert(src != NULL);
assert(dst != NULL);
i = 0;
for (j = 0; j < len; j++) {
RETURN_IF_FAILED(read_uchar(src, &a));
dst[i++] = a;
}
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
/**
* Reads a file name from a message buffer.
*
* @param src The message buffer.
* @param dst The file name.
*
* @return EXIT_SUCCESS or EXIT_FAILURE.
*/
static int read_name(msg_buf_t * src, char * dst)
{
u_char a;
size_t i;
assert(src != NULL);
assert(dst != NULL);
/* File names are treated as strings, so use '\0' to indicate the end. */
i = 0;
do {
RETURN_IF_FALSE(i != MAX_NAME_LENGTH);
RETURN_IF_FAILED(read_uchar(src, &a));
dst[i++] = a;
} while(a != '\0');
RETURN_IF_FAILED(verify_name(dst));
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
/**
* Reads a u_char from a message buffer.
*
* @param src The message buffer.
* @param dst The u_char.
*
* @return EXIT_SUCCESS or EXIT_FAILURE.
*/
static int read_uchar(msg_buf_t * src, u_char * dst)
{
assert(src != NULL);
assert(dst != NULL);
/* Make sure the reading is not beyond the buffer size. */
RETURN_IF_FALSE(src->index < src->size);
/* Read a byte at the next index, and increment the index. */
*dst = src->buffer[(src->index)++];
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
/**
* Reads peer infomation from a message buffer.
*
* @param src The message buffer.
* @param dst The peer infomation.
*
* @return EXIT_SUCCESS or EXIT_FAILURE.
*/
static int read_ulong(msg_buf_t * src, u_long * dst)
{
u_char a, b, c, d;
assert(src != NULL);
assert(dst != NULL);
/* Read four bytes out of the message buffer. */
RETURN_IF_FAILED(read_uchar(src, &a));
RETURN_IF_FAILED(read_uchar(src, &b));
RETURN_IF_FAILED(read_uchar(src, &c));
RETURN_IF_FAILED(read_uchar(src, &d));
/* Read the unsigned long in MSB and store it in host order. */
*dst = 0;
*dst |= a << 24;
*dst |= b << 16;
*dst |= c << 8;
*dst |= d;
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
/**
* Reads u_shor number from a message buffer in the form of msg_buf_t.
*
* @param src The message buffer in the form of msg_buf_t.
* @param dst A u_shor number memory space to read to.
*
* @return EXIT_SUCCESS or EXIT_FAILURE.
*/
static int read_ushort(msg_buf_t * src, u_short * dst)
{
u_char a, b;
assert(src != NULL);
assert(dst != NULL);
/* Read two bytes out of the message buffer. */
RETURN_IF_FAILED(read_uchar(src, &a));
RETURN_IF_FAILED(read_uchar(src, &b));
/* Read teh unsigned short in MSB and store it in host order. */
*dst = 0;
*dst |= a << 8;
*dst |= b;
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
/**
* Write an array of u_char into a message buffer.
*
* @param dst The message buffer.
* @param src The array of uchar.
* @param len The length in bytes to write.
*/
static void write_array(msg_buf_t * dst, const u_char * src, size_t len)
{
int i;
assert(dst != NULL);
assert(src != NULL);
for (i = 0; i < len; i++)
write_uchar(dst, src[i]);
}
/* ------------------------------------------------------------------------ */
/**
* Write a name into a message buffer.
*
* @param dst The message buffer.
* @param src The name.
*/
static void write_name(msg_buf_t * dst, const char * src)
{
int i;
int len;
assert(dst != NULL);
assert(src != NULL);
assert(verify_name(src) == EXIT_SUCCESS);
len = strlen(src);
for (i = 0; i <= len; i++)
write_uchar(dst, src[i]);
}
/* ------------------------------------------------------------------------ */
/**
* Write a u_char into a message buffer.
*
* @param dst The message buffer.
* @param src The u_char.
*/
static void write_uchar(msg_buf_t * dst, u_char src)
{
assert(dst != NULL);
assert(dst->size < MESSAGE_BUFFER_SIZE);
dst->buffer[dst->size++] = src;
}
/* ------------------------------------------------------------------------ */
/**
* Write peer infomation into a message buffer.
*
* @param dst The message buffer.
* @param src The peer information.
*/
static void write_ulong(msg_buf_t * dst, u_long src)
{
assert(dst != NULL);
/* Convert the host order to MSB. */
write_uchar(dst, (u_char)(src >> 24));
write_uchar(dst, (u_char)((src >> 16) & 0xff));
write_uchar(dst, (u_char)((src >> 8) & 0xff));
write_uchar(dst, (u_char)(src & 0xff));
}
/* ------------------------------------------------------------------------ */
/**
* Write a u_short number into a message buffer.
*
* @param dst The message buffer.
* @param src The u_short number.
*/
static void write_ushort(msg_buf_t * dst, u_short src)
{
assert(dst != NULL);
/* Convert the host order to MSB. */
write_uchar(dst, (u_char)(src >> 8));
write_uchar(dst, (u_char)(src & 0xff));
}
/* ------------------------------------------------------------------------ */
#ifdef TESTS
static int tests() {
msg_buf buf;
data_msg msg1;
data_msg msg2;
listing_msg msg3;
listing_msg msg4;
request_msg msg5;
request_msg msg6;
try_msg msg7;
try_msg msg8;
msg_type type;
u_char data[5] = {'a', 'b', 'c', 'd', 'e'};
int i;
strncpy(msg1.name, "jade", 5);
for (i = 0; i < 5; i++)
msg1.data[i] = data[i];
msg1.data_len = 5;
data_msg_write(&buf, &msg1);
print_buffer(buf.msg_buf, buf.buf_count);
type = get_msg_type(&buf);
if (type == data_type_msg) {
data_msg_read(&buf, &msg2);
print_buffer((u_char *)msg2.name, strlen(msg2.name));
print_buffer(msg2.data, msg2.data_len);
}
strncpy(msg3.entries[0], "abcde", 6);
strncpy(msg3.entries[1], "fghijk", 7);
strncpy(msg3.entries[2], "lmno", 5);
strncpy(msg3.entries[3], "pqr", 4);
msg3.count = 4;
listing_msg_write(&buf, &msg3);
print_buffer(buf.msg_buf, buf.buf_count);
type = get_msg_type(&buf);
if (type == listing_type_msg) {
listing_msg_read(&buf, &msg4);
for(i = 0; i < msg4.count; i++)
print_buffer((u_char *)msg4.entries[i], strlen(msg4.entries[i]));
}
strncpy(msg5.name, "monkey", 7);
request_msg_write(&buf, &msg5);
print_buffer(buf.msg_buf, buf.buf_count);
type = get_msg_type(&buf);
if(type == request_type_msg) {
request_msg_read(&buf, &msg6);
print_buffer((u_char *)msg6.name, strlen(msg6.name));
}
strncpy(msg7.name, "egg", 4);
msg7.peers[0].addr = 0x01020304;
msg7.peers[1].addr = 286331153;
msg7.peers[2].addr = 286331154;
msg7.peers[3].addr = 286331155;
msg7.peers[0].port = 0x0102;
msg7.peers[1].port = 11;
msg7.peers[2].port = 12;
msg7.peers[3].port = 13;
msg7.count = 4;
try_msg_write(&buf, &msg7);
print_buffer(buf.msg_buf, buf.buf_count);
type = get_msg_type(&buf);
if(type == try_type_msg) {
try_msg_read(&buf, &msg8);
print_buffer((u_char *)msg8.name, strlen(msg8.name));
}
return EXIT_SUCCESS;
}
#endif /* TESTS */