From d5b52a1302ef4409424ec0633ccf9bd72359ea32 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 14 May 2016 22:03:43 -0400 Subject: refactor/rewrite the client/server protocol handling also pass the environment and current directory over as well --- src/protocol.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 src/protocol.c (limited to 'src/protocol.c') diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 0000000..6451f63 --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "runes.h" +#include "protocol.h" + +#define RUNES_MAX_PACKET_SIZE (1024*1024) + +static int runes_protocol_store_u32(GArray *buf, uint32_t i); +static int runes_protocol_store_strvec(GArray *buf, char *vec[]); +static int runes_protocol_store_str(GArray *buf, char *str); +static ssize_t runes_protocol_load_u32(char *buf, size_t len, uint32_t *i); +static ssize_t runes_protocol_load_strvec(char *buf, size_t len, char ***vec); +static ssize_t runes_protocol_load_str(char *buf, size_t len, char **str); +static int runes_protocol_read_bytes(int sock, size_t len, char *outbuf); +static int runes_protocol_write_bytes(int sock, char *buf, size_t len); + +int runes_protocol_parse_message( + char *buf, size_t len, struct runes_protocol_message *outmsg) +{ + uint32_t version; + size_t offset = 0; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(outmsg); \ + return 0; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, version); + if (version != RUNES_PROTOCOL_MESSAGE_VERSION) { + /* backcompat code can go here eventually */ + runes_warn("unknown protocol version %d", version); + free(outmsg); + return 0; + } + + LOAD(u32, outmsg->argc); + LOAD(strvec, outmsg->argv); + LOAD(strvec, outmsg->envp); + LOAD(str, outmsg->cwd); + +#undef LOAD + + return 1; +} + +int runes_protocol_create_message( + struct runes_protocol_message *msg, char **outbuf, size_t *outlen) +{ + GArray *buf; + + buf = g_array_new(FALSE, FALSE, 1); + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + g_array_unref(buf); \ + return 0; \ + } \ + } while (0) + + STORE(u32, RUNES_PROTOCOL_MESSAGE_VERSION); + + STORE(u32, msg->argc); + STORE(strvec, msg->argv); + STORE(strvec, msg->envp); + STORE(str, msg->cwd); + +#undef STORE + + *outbuf = malloc(buf->len); + memcpy(*outbuf, buf->data, buf->len); + *outlen = buf->len; + + g_array_unref(buf); + + return 1; +} + +void runes_protocol_free_message(struct runes_protocol_message *msg) +{ + char **p; + + p = msg->argv; + while (*p) { + free(*p); + p++; + } + free(msg->argv); + + p = msg->envp; + while (*p) { + free(*p); + p++; + } + free(msg->envp); + + free(msg->cwd); +} + +int runes_protocol_read_packet(int sock, char **outbuf, size_t *outlen) +{ + uint32_t len; + char *buf; + + if (!runes_protocol_read_bytes(sock, sizeof(len), (char *)&len)) { + return 0; + } + + len = ntohl(len); + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("packet of size %d is too big", len); + return 0; + } + buf = malloc(len); + + if (!runes_protocol_read_bytes(sock, len, buf)) { + free(buf); + return 0; + } + + *outlen = len; + *outbuf = buf; + + return 1; +} + +int runes_protocol_send_packet(int sock, char *buf, size_t len) +{ + uint32_t len32 = len; + + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("packet of size %d is too big", len); + return 0; + } + + len32 = htonl(len32); + + if (!runes_protocol_write_bytes(sock, (char *)&len32, sizeof(len32))) { + runes_warn("failed to write packet to socket"); + return 0; + } + + if (!runes_protocol_write_bytes(sock, buf, len)) { + runes_warn("failed to write packet to socket"); + return 0; + } + + return 1; +} + +static int runes_protocol_store_u32(GArray *buf, uint32_t i) +{ + uint32_t ni = htonl(i); + + g_array_append_vals(buf, (void *)&ni, sizeof(ni)); + + return 1; +} + +static int runes_protocol_store_strvec(GArray *buf, char *vec[]) +{ + size_t len, i; + + for (len = 0; vec[len]; ++len); + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("string vector of size %d is too big", len); + return 0; + } + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + return 0; \ + } \ + } while (0) + + STORE(u32, len); + for (i = 0; i < len; ++i) { + STORE(str, vec[i]); + } + +#undef STORE + + return 1; +} + +static int runes_protocol_store_str(GArray *buf, char *str) +{ + size_t len = strlen(str); + + if (len > RUNES_MAX_PACKET_SIZE) { + runes_warn("string of size %d is too big", len); + return 0; + } + +#define STORE(type, val) \ + do { \ + if (!runes_protocol_store_##type(buf, val)) { \ + return 0; \ + } \ + } while (0) + + STORE(u32, len); + + g_array_append_vals(buf, str, len); + +#undef STORE + + return 1; +} + +static ssize_t runes_protocol_load_u32(char *buf, size_t len, uint32_t *i) +{ + uint32_t hi; + + if (len < sizeof(uint32_t)) { + return -1; + } + + hi = ((uint32_t *)buf)[0]; + *i = ntohl(hi); + + return sizeof(uint32_t); +} + +static ssize_t runes_protocol_load_strvec(char *buf, size_t len, char ***vec) +{ + ssize_t offset = 0; + uint32_t veclen = 0; + size_t i; + + *vec = NULL; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(*vec); \ + for (i = 0; i < veclen; ++i) { \ + free(*vec[i]); \ + } \ + return -1; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, veclen); + if (veclen > len) { + runes_warn("string of size %d is too big", strlen); + return -1; + } + + *vec = calloc(veclen + 1, sizeof(char *)); + + for (i = 0; i < veclen; ++i) { + LOAD(str, (*vec)[i]); + } + (*vec)[veclen] = NULL; + +#undef LOAD + + return offset; +} + +static ssize_t runes_protocol_load_str(char *buf, size_t len, char **str) +{ + ssize_t offset = 0; + uint32_t strlen; + + *str = NULL; + +#define LOAD(type, var) \ + do { \ + ssize_t incr; \ + incr = runes_protocol_load_##type(buf + offset, len - offset, &var); \ + if (incr < 0) { \ + free(*str); \ + return -1; \ + } \ + offset += incr; \ + } while (0) + + LOAD(u32, strlen); + if (strlen > len) { + runes_warn("string of size %d is too big", strlen); + return -1; + } + + *str = malloc(strlen + 1); + + memcpy(*str, buf + offset, strlen); + (*str)[strlen] = '\0'; + offset += strlen; + +#undef LOAD + + return offset; +} + +static int runes_protocol_write_bytes(int sock, char *buf, size_t len) +{ + size_t bytes = 0; + + while (bytes < len) { + ssize_t sent; + + sent = send(sock, buf + bytes, len - bytes, 0); + if (sent <= 0 && errno != EINTR) { + runes_warn( + "couldn't write %d bytes to socket: %s", + len, strerror(errno)); + return 0; + } + bytes += sent; + } + + return 1; +} + +static int runes_protocol_read_bytes(int sock, size_t len, char *outbuf) +{ + size_t bytes = 0; + + while (bytes < len) { + ssize_t got; + + got = recv(sock, outbuf + bytes, len - bytes, 0); + if (got <= 0 && errno != EINTR) { + runes_warn( + "couldn't read %d bytes from socket: %s", + len, strerror(errno)); + return 0; + } + bytes += got; + } + + return 1; +} -- cgit v1.2.3-54-g00ecf