aboutsummaryrefslogtreecommitdiffstats
path: root/src/protocol.c
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2016-05-14 22:03:43 -0400
committerJesse Luehrs <doy@tozt.net>2016-05-14 22:03:43 -0400
commitd5b52a1302ef4409424ec0633ccf9bd72359ea32 (patch)
tree76d90c6b51f8bf234cc19cce75147d6089a1b808 /src/protocol.c
parentbfe96a6aa5fa3ca9a05b658efbcaee9dc01b5235 (diff)
downloadrunes-d5b52a1302ef4409424ec0633ccf9bd72359ea32.tar.gz
runes-d5b52a1302ef4409424ec0633ccf9bd72359ea32.zip
refactor/rewrite the client/server protocol handling
also pass the environment and current directory over as well
Diffstat (limited to 'src/protocol.c')
-rw-r--r--src/protocol.c351
1 files changed, 351 insertions, 0 deletions
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 <arpa/inet.h>
+#include <errno.h>
+#include <glib.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
+}