#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_new_term_message( char *buf, size_t len, struct runes_protocol_new_term_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_new_term_message( struct runes_protocol_new_term_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_new_term_message( struct runes_protocol_new_term_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, int *outtype, char **outbuf, size_t *outlen) { uint32_t len, type; char *buf; if (!runes_protocol_read_bytes(sock, sizeof(len), (char *)&len)) { return 0; } len = ntohl(len); if (!runes_protocol_read_bytes(sock, sizeof(type), (char *)&type)) { return 0; } type = ntohl(type); 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; *outtype = type; return 1; } int runes_protocol_send_packet(int sock, int type, char *buf, size_t len) { uint32_t len32 = len, type32 = type; if (len > RUNES_MAX_PACKET_SIZE) { runes_warn("packet of size %d is too big", len); return 0; } if (type < 0 || type >= RUNES_PROTOCOL_NUM_MESSAGE_TYPES) { runes_warn("unknown message type %d", type); 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; } type32 = htonl(type32); if (!runes_protocol_write_bytes(sock, (char *)&type32, sizeof(type32))) { 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; }