#define _GNU_SOURCE

#include <arpa/inet.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int transport_read_full(int fd, void *buf, size_t len) {
    unsigned char *p = (unsigned char *)buf;
    size_t off = 0;

    while (off < len) {
        ssize_t n = read(fd, p + off, len - off);
        if (n == 0) {
            return -1;
        }
        if (n < 0) {
            if (errno == EINTR) {
                continue;
            }
            return -1;
        }
        off += (size_t)n;
    }

    return 0;
}

static int transport_write_full(int fd, const void *buf, size_t len) {
    const unsigned char *p = (const unsigned char *)buf;
    size_t off = 0;

    while (off < len) {
        ssize_t n = write(fd, p + off, len - off);
        if (n < 0) {
            if (errno == EINTR) {
                continue;
            }
            return -1;
        }
        off += (size_t)n;
    }

    return 0;
}

int transport_send_framed(int fd, const char *payload) {
    uint32_t len;
    uint32_t be_len;

    if (payload == NULL) {
        return -1;
    }

    len = (uint32_t)strlen(payload);
    be_len = htonl(len);

    if (transport_write_full(fd, &be_len, sizeof(be_len)) != 0) {
        return -1;
    }
    if (transport_write_full(fd, payload, len) != 0) {
        return -1;
    }

    return 0;
}

int transport_recv_framed(int fd, char **out_payload, size_t max_size) {
    uint32_t be_len;
    uint32_t len;
    char *payload;

    if (out_payload == NULL || max_size == 0) {
        return -1;
    }

    *out_payload = NULL;

    if (transport_read_full(fd, &be_len, sizeof(be_len)) != 0) {
        return -1;
    }

    len = ntohl(be_len);
    if (len == 0 || len > max_size) {
        return -1;
    }

    payload = (char *)malloc((size_t)len + 1U);
    if (payload == NULL) {
        return -1;
    }

    if (transport_read_full(fd, payload, len) != 0) {
        free(payload);
        return -1;
    }

    payload[len] = '\0';
    *out_payload = payload;
    return 0;
}
