dholm.com
msgbartop
I need this baby in a month send me nine women!
msgbarbottom

21 Apr 08 Simple Buffer Parsing With C99

A friend of mine asked some questions relating to socket programming which spurred me to create a simple example based on an application I wrote a while back. It’s pretty basic stuff but I decided to post it here in case anyone has any feedback to offer.

I use flexible arrays where suitable so warm up your C99-compilers. There is virtually no error checking done in the example so be vary, also I didn’t compile it. ;)

static const uint32_t PROTOCOL_MAGIC=0xdead001;
struct HelloHeader {
    uint32_t magic;
    uint32_t version;
};

enum Command {
    Command_NONE,
    Command_FILE_LIST,
    Command_GET_FILE,
};

struct CmdHeader {
    uint32_t cmd;
};

struct File {
    char name[256];
    uint64_t size;
};

struct FileList {
    uint32_t numFiles;
    File[] files;
};

struct GetFile {
    uint64_t len;
    /* null-terminated filename followed by data */
    char[256] name;
    char[] data;
};

uint64_t ntohll(uint64_t n)
{
#ifdef BIG_ENDIAN
    return n;
#else
    return (((uint64_t) ntohl(n)) << 32) + ntohl(n >> 32);
#endif
}

char* processCommand000301(char* buffer, size_t bufferLen)
{
    /* ... */
}

char* processCommand010000(char* buffer, size_t bufferLen)
{
    char* ptr = buffer;

    struct CmdHeader* commandHeader = (struct CmdHeader*) ptr;
    commandHeader->cmd = ntohl(commandHeader->cmd);
    ptr += sizeof(struct CmdHeader);
    switch (commandHeader->cmd) {
        case Command_FILE_LIST: {
            struct FileList* fileList = (struct FileList*) ptr;
            fileList->numFiles = ntohl(fileList->numFiles);
            ptr += sizeof(struct FileList);
            for (int i = 0; i < fileList.numFiles; ++i) {
                fileList.files[i].size = ntohll(fileList.files[i].size);
                printf("%s %lld\n", fileList.files[i].name, fileList.files[i].size);
                ptr += sizeof(struct File);
            }
            break;
        }

        case Command_GET_FILE: {
            struct GetFile* getFile = (struct GetFile*) ptr;
            getFile->len = ntohll(getFile->len);
            FILE* file = fopen(getFile.name, "w");
            fwrite(ptr, 1, getFile.len, file);
            ptr += getFile.len;
            fclose(file);
            break;
        }

        default:
            /* Unrecognized command! */
            return NULL;
    }

    return ptr;
}

int processBuffer(char* buffer, size_t bufferLen)
{
    char* ptr = buffer;

    struct HelloHeader* helloHeader = (struct HelloHeader*) buffer;
    helloHeader->magic = ntohl(helloHeader->magic);
    helloHeader->version = ntohl(helloHeader->version);
    if (helloHeader->magic != PROTOCOL_MAGIC) {
        /* Invalid header! */
        return -1;
    }
    ptr += sizeof(struct HelloHeader);

    while (ptr < (buffer + bufferLen)) {
        switch (helloHeader->version) {
            case 0x00010000:
                /* version 1.0.0 */
                ptr = processCommand010000(buffer, (size_t) ptr - buffer);
                break;

            case 0x00000301:
                /* version 0.3.1 */
                ptr = processCommand000301(buffer, (size_t) ptr - buffer);
                break;

            default:
                /* Unsupported version! */
                return -1;
        }
    }
    return 0;
}

P.S. I’m impressed by WordPress’ ability to consistently botch preformatted text when using the WYSIWYG editor. D.S.

Similar Posts:



Reader's Comments

  1. |

    What is the purpose of the ntohll function?

  2. |

    This was initially written for a network protocol so I assumed all data types were in network byte order during transmission. POSIX does not define a function for converting 64-bit values from network byte order so that function implements it.
    I see now that it isn’t endian safe though so I will update the listing.

  3. |

    If you want to be extra careful you should probably memcpy the structures out of the buffers to a local representation to avoid alignment issues.



Leave a Comment