Skip to content

File syn_transport_tcp.c

File List > net > syn_transport_tcp.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_TRANSPORT_TCP) || SYN_USE_TRANSPORT_TCP

#include "../util/syn_assert.h"
#include "syn_transport_tcp.h"

#include <string.h>

static bool tcp_send(const uint8_t *data, size_t len, void *ctx)
{
    const SYN_TransportTcp *tcp = (const SYN_TransportTcp *)ctx;
    SYN_ASSERT(tcp != NULL);
    SYN_ASSERT(len <= (size_t)0xFFFF);

    if (tcp->sock == SYN_SOCKET_INVALID)
        return false;

    uint8_t len_hdr[2];
    len_hdr[0] = (uint8_t)(len >> 8);
    len_hdr[1] = (uint8_t)(len & 0xFF);

    if (syn_port_sock_send_all(tcp->sock, len_hdr, 2) != 2) {
        return false;
    }

    if (len > 0 && data != NULL) {
        if (syn_port_sock_send_all(tcp->sock, data, len) != (int)len) {
            return false;
        }
    }

    return true;
}

static bool tcp_recv(uint8_t *data, size_t max_len, size_t *out_len, void *ctx)
{
    SYN_TransportTcp *tcp = (SYN_TransportTcp *)ctx;
    SYN_ASSERT(tcp != NULL);
    SYN_ASSERT(data != NULL);
    SYN_ASSERT(out_len != NULL);

    if (tcp->sock == SYN_SOCKET_INVALID)
        return false;

    /* Poll in a loop until we either run out of available data on socket or finish a packet */
    for (;;) {
        if (tcp->state == 0) {
            uint8_t b;
            int n = syn_port_sock_recv(tcp->sock, &b, 1, 0); /* non-blocking */
            if (n <= 0)
                return false; /* no data, timeout, or closed */
            tcp->payload_len = (uint16_t)((uint16_t)b << 8);
            tcp->state = 1;
        } else if (tcp->state == 1) {
            uint8_t b;
            int n = syn_port_sock_recv(tcp->sock, &b, 1, 0);
            if (n <= 0)
                return false;
            tcp->payload_len |= b;
            tcp->bytes_read = 0;
            tcp->state = 2;

            if (tcp->payload_len > sizeof(tcp->rx_buf)) {
                /* Framing error: packet too large for transport buffer */
                tcp->state = 0;
                return false;
            }
        } else if (tcp->state == 2) {
            uint16_t remaining = tcp->payload_len - tcp->bytes_read;
            if (remaining == 0) {
                /* Empty packet */
                *out_len = 0;
                tcp->state = 0;
                return true;
            }

            int n = syn_port_sock_recv(tcp->sock, tcp->rx_buf + tcp->bytes_read, remaining, 0);
            if (n <= 0)
                return false;

            tcp->bytes_read += (uint16_t)n;
            if (tcp->bytes_read == tcp->payload_len) {
                if (tcp->payload_len <= max_len) {
                    memcpy(data, tcp->rx_buf, tcp->payload_len);
                    *out_len = tcp->payload_len;
                    tcp->state = 0;
                    return true;
                } else {
                    /* Output buffer too small, drop it */
                    tcp->state = 0;
                    return false;
                }
            }
        }
    }
}

void syn_transport_tcp_init(SYN_Transport *t, SYN_TransportTcp *tcp, SYN_Socket sock)
{
    SYN_ASSERT(t != NULL);
    SYN_ASSERT(tcp != NULL);

    memset(tcp, 0, sizeof(*tcp));
    tcp->sock = sock;
    tcp->state = 0;

    t->send = tcp_send;
    t->recv = tcp_recv;
    t->ctx = tcp;
}

#endif /* SYN_USE_TRANSPORT_TCP */