Skip to content

Instantly share code, notes, and snippets.

@piscisaureus
Created November 1, 2012 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piscisaureus/f9c4a4598a489b0b46f5 to your computer and use it in GitHub Desktop.
Save piscisaureus/f9c4a4598a489b0b46f5 to your computer and use it in GitHub Desktop.
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ioutil.h"
#include "packet.h"
/* Define functions to parse individual packet types */
#define PROLOGUE(lc, uc) \
static ssize_t parse_##lc##_packet(char* wire_data, size_t wire_data_size, \
lc##_packet_t* packet) { \
char* field_data; \
size_t wire_data_pos; \
size_t field_data_pos; \
\
if (packet != NULL) { \
field_data = (char*) (packet + 1); \
packet->type = uc##_PACKET; \
} else { \
field_data = NULL; \
} \
\
wire_data_pos = 0; \
field_data_pos = 0;
#define NUMBER(field) \
{ \
if (wire_data_size < wire_data_pos + sizeof(int32_t)) \
return -1; \
\
if (packet) \
packet->field = *(uint32_t*) (wire_data + wire_data_pos); \
wire_data_pos += sizeof(uint32_t); \
}
#define STRING(field) \
{ \
uint32_t len; \
\
if (wire_data_size < wire_data_pos + sizeof(int32_t)) \
return -1; \
\
len = *(uint32_t*) (wire_data + wire_data_pos); \
field_data_pos += sizeof(uint32_t); \
wire_data_pos += sizeof(uint32_t); \
\
if (wire_data_size < wire_data_pos + len) \
return -1; \
\
if (field_data) { \
memcpy(field_data + field_data_pos, wire_data + wire_data_pos, len); \
field_data[field_data_pos + len] = '\0'; \
packet->field = field_data + field_data_pos; \
} \
\
wire_data_pos += len; \
field_data_pos += len + 1; \
}
#define STRING_LIST(count_field, array_field) \
{ \
int32_t i, count; \
size_t array_size; \
\
if (wire_data_size < wire_data_pos + sizeof(int32_t)) \
return -1; \
\
count = *(uint32_t*) (wire_data + wire_data_pos); \
\
if (packet != NULL) \
packet->count_field = count; \
\
wire_data_pos += sizeof(uint32_t); \
\
/* Allocate space for the array, and an additional NULL. */ \
array_size = sizeof(char*) * (count + 1); \
\
if (packet) { \
packet->array_field = (char**) field_data + field_data_pos; \
packet->array_field[count] = NULL; \
} \
\
field_data_pos += array_size; \
\
for (i = 0; i < count; i++) { \
STRING(array_field[i]) \
} \
}
#define EPILOGUE(lc, uc) \
return sizeof(*packet) + field_data_pos; \
}
ALL_PACKET_TYPES();
#undef PROLOGUE
#undef NUMBER
#undef STRING
#undef STRING_LIST
#undef EPILOGUE
/* Define packet reading function. */
ssize_t read_packet(int fd, packet_t** packet_ptr) {
char* wire_data;
ssize_t r, result;
struct {
int32_t type;
int32_t size;
} __attribute__((packed)) header;
r = read_all(fd, &header, sizeof header);
if (r < 0) {
return -1;
} else if (r != sizeof header) {
errno = EBADMSG;
return -1;
}
wire_data = malloc(header.size);
if (wire_data == NULL)
return -1;
r = read_all(fd, wire_data, header.size);
if (r < 0) {
return -1;
} else if (r != header.size) {
errno = EBADMSG;
return -1;
}
switch ((packet_type_t) header.type) {
#define PROLOGUE(lc, uc) \
case uc##_PACKET: { \
lc##_packet_t* packet; \
ssize_t size; \
\
size = parse_##lc##_packet(wire_data, header.size, NULL); \
if (size < 0) { \
result = -1; \
break; \
} \
\
packet = malloc(size); \
if (packet == NULL) { \
result = -1; \
break; \
} \
\
size = parse_##lc##_packet(wire_data, header.size, packet); \
if (size < 0) { \
free(packet); \
packet = NULL; \
result = -1; \
break; \
} \
\
result = size; \
*packet_ptr = (packet_t*) packet; \
break; \
}
#define NUMBER(field) /* empty */
#define STRING(field) /* empty */
#define STRING_LIST(count_field, array_field) /* empty */
#define EPILOGUE(lc, uc) /* empty */
ALL_PACKET_TYPES()
#undef PROLOGUE
#undef NUMBER
#undef STRING
#undef STRING_LIST
#undef EPILOGUE
default:
errno = EBADMSG;
result = -1;
}
free(wire_data);
return result;
}
#ifndef _PACKET_H
#define _PACKET_H
#include <stdint.h>
#include <sys/types.h>
/* Packet layouts. */
#define START_PACKET() \
PROLOGUE(start, START) \
STRING(tag) \
STRING(filename) \
STRING(cwd) \
STRING_LIST(argc, argv) \
STRING_LIST(envc, envv) \
EPILOGUE(start, START)
#define STOP_PACKET() \
PROLOGUE(stop, STOP) \
STRING(tag) \
EPILOGUE(stop, STOP)
#define ALL_PACKET_TYPES() \
START_PACKET() \
STOP_PACKET()
/* Define enum with types. */
#define PROLOGUE(lc, uc) uc##_PACKET,
#define NUMBER(field) /* empty */
#define STRING(field) /* empty */
#define STRING_LIST(count_field, array_field) /* empty */
#define EPILOGUE(lc, uc) /* empty */
typedef enum {
ALL_PACKET_TYPES()
PACKET_TYPE_MAX
} packet_type_t;
#undef PROLOGUE
#undef NUMBER
#undef STRING
#undef STRING_LIST
#undef EPILOGUE
/* Common packet header. */
typedef struct {
packet_type_t type;
} packet_t;
/* Define structs. */
#define PROLOGUE(lc, uc) \
typedef struct { \
packet_type_t type;
#define NUMBER(field) \
int32_t field;
#define STRING(field) \
char* field;
#define STRING_LIST(count_field, array_field) \
int32_t count_field; \
char** array_field;
#define EPILOGUE(lc, uc) \
} lc##_packet_t;
ALL_PACKET_TYPES()
#undef PROLOGUE
#undef NUMBER
#undef STRING
#undef STRING_LIST
#undef EPILOGUE
ssize_t read_packet(int fd, packet_t** packet_ptr);
#endif /* PACKET_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment