aboutsummaryrefslogblamecommitdiffstats
path: root/src/protocol.c
blob: 1e9991fea5d99c3a47f6aa6d7907dfa7728ded15 (plain) (tree)





















                                                                              

                                                                          
































                                                                              


                                                






























                                                       

                                                



















                    

                                                          
 
                       




                                                                      
                     





                                                                        












                                                        
                    



             
                                                                         
 
                                        





                                                        



                                                               
 
                         




                                                                           





                                                                             




































































































































































































                                                                              
#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_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;
}