messages.c

/*
 * 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 */
Valid HTML 4.01 Valid CSS