7 n__network__msg_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00

src/n_network_msg.c

Network messages function. More...

Attributes

Name
long long g_netw_msg_deleted
long long g_netw_msg_created
long long g_netw_bytes_sent
Add a message to send in aimed NETWORK.
long long g_netw_bytes_recv

Detailed Description

Network messages function.

Author: Castagnier Mickael

Version: 1.0

Date: 10/05/2005

Attributes Documentation

variable g_netw_msg_deleted

static long long g_netw_msg_deleted = 0;

variable g_netw_msg_created

static long long g_netw_msg_created = 0;

variable g_netw_bytes_sent

long long g_netw_bytes_sent;

Add a message to send in aimed NETWORK.

Parameters:

  • netw NETWORK where add the message
  • msg the message to add

Return: TRUE if success FALSE on error

variable g_netw_bytes_recv

long long g_netw_bytes_recv;

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_network.h"
#include "nilorea/n_network_msg.h"

/* Lifecycle counters. Exposed via netw_msg_stats_get(). Messages are
 * created and deleted from any thread, so the counters are only touched
 * with relaxed atomic operations: advisory telemetry, not a correctness
 * signal. */
static long long g_netw_msg_created = 0;
static long long g_netw_msg_deleted = 0;
void netw_msg_stats_get(long long* created, long long* deleted) {
    if (created) *created = __atomic_load_n(&g_netw_msg_created, __ATOMIC_RELAXED);
    if (deleted) *deleted = __atomic_load_n(&g_netw_msg_deleted, __ATOMIC_RELAXED);
}

/* Process-wide byte counters for the two netw_*_msg pump points
 * (accumulated in n_network.c with relaxed atomic adds). Exposed from the
 * same header, read here with relaxed atomic loads. */
extern long long g_netw_bytes_sent; /* defined in n_network.c */
extern long long g_netw_bytes_recv; /* defined in n_network.c */
void netw_bytes_stats_get(long long* sent, long long* recv) {
    if (sent) *sent = __atomic_load_n(&g_netw_bytes_sent, __ATOMIC_RELAXED);
    if (recv) *recv = __atomic_load_n(&g_netw_bytes_recv, __ATOMIC_RELAXED);
}

double double_swap(double value) {
    double swaped = 0;
    const unsigned char* src = (const unsigned char*)&value;
    unsigned char* dst = (unsigned char*)&swaped;

    /* swap bytes */
    dst[0] = src[7];
    dst[1] = src[6];
    dst[2] = src[5];
    dst[3] = src[4];
    dst[4] = src[3];
    dst[5] = src[2];
    dst[6] = src[1];
    dst[7] = src[0];

    return swaped;
} /* double_swap() */

double htond(double value) {
    /* if byte order is little like x86, PC, ... we swap to network notation */
#if (BYTEORDER_ENDIAN == BYTEORDER_LITTLE_ENDIAN)
    double ret = double_swap(value);
#else
    double ret = value;
#endif
    return ret;
} /* htond() */

double ntohd(double value) {
    /* if byte order is little like x86, PC, ... we swap to network notation */
#if (BYTEORDER_ENDIAN == BYTEORDER_LITTLE_ENDIAN)
    double ret = double_swap(value);
#else
    double ret = value;
#endif
    return ret;
} /* ntohd() */

int create_msg(NETW_MSG** msg) {
    if ((*msg)) {
        n_log(LOG_ERR, "create_msg destination is valid and should be NULL !");
        return FALSE;
    }

    Malloc((*msg), NETW_MSG, 1);
    __n_assert(msg && (*msg), return FALSE);

    (*msg)->tabstr = new_generic_list(MAX_LIST_ITEMS);
    __n_assert((*msg)->tabstr, Free((*msg)); return FALSE);
    (*msg)->tabint = new_generic_list(MAX_LIST_ITEMS);
    __n_assert((*msg)->tabint, list_destroy(&(*msg)->tabstr); Free((*msg)); return FALSE);
    (*msg)->tabflt = new_generic_list(MAX_LIST_ITEMS);
    __n_assert((*msg)->tabflt, list_destroy(&(*msg)->tabstr); list_destroy(&(*msg)->tabint); Free((*msg)); return FALSE);

    __atomic_fetch_add(&g_netw_msg_created, 1, __ATOMIC_RELAXED);
    return TRUE;
} /* create_msg( ... ) */

int delete_msg(NETW_MSG** msg) {
    __n_assert(msg, return FALSE);
    if ((*msg)) {
        if ((*msg)->tabint) {
            list_destroy(&(*msg)->tabint);
        }
        if ((*msg)->tabflt) {
            list_destroy(&(*msg)->tabflt);
        }
        if ((*msg)->tabstr) {
            list_destroy(&(*msg)->tabstr);
        }
        Free((*msg));
        __atomic_fetch_add(&g_netw_msg_deleted, 1, __ATOMIC_RELAXED);
    } else {
        n_log(LOG_ERR, "(*msg) already NULL");
        return FALSE;
    }
    return TRUE;
} /* delete_msg */

int empty_msg(NETW_MSG** msg) {
    __n_assert(msg && (*msg), return FALSE);

    list_empty((*msg)->tabint);
    list_empty((*msg)->tabflt);
    list_empty((*msg)->tabstr);

    return TRUE;
} /* empty_msg(...) */

int add_nb_to_msg(NETW_MSG* msg, double value) {
    double* new_val = NULL;

    __n_assert(msg, return FALSE);

    Malloc(new_val, double, 1);
    __n_assert(new_val, return FALSE);

    new_val[0] = value;

    if (list_push(msg->tabflt, new_val, free) == FALSE) {
        Free(new_val);
        return FALSE;
    }
    return TRUE;
} /* add_nb_to_msg( ... ) */

int add_int_to_msg(NETW_MSG* msg, int value) {
    int* new_val = NULL;

    __n_assert(msg, return FALSE);

    Malloc(new_val, int, 1);
    __n_assert(new_val, return FALSE);

    new_val[0] = value;

    if (list_push(msg->tabint, new_val, free) == FALSE) {
        Free(new_val);
        return FALSE;
    }
    return TRUE;
} /* add_int_to_msg( ... ) */

int add_nstrptr_to_msg(NETW_MSG* msg, N_STR* str) {
    __n_assert(msg, return FALSE);
    __n_assert(str, return FALSE);
    __n_assert(str->data, return FALSE);
    return list_push(msg->tabstr, str, free_nstr_ptr);
} /* add_nstrptr_to_msg(...) */

int add_nstrdup_to_msg(NETW_MSG* msg, N_STR* str) {
    __n_assert(msg, return FALSE);
    __n_assert(str, return FALSE);
    __n_assert(str->data, return FALSE);
    N_STR* dup = nstrdup(str);
    __n_assert(dup, return FALSE);
    return list_push(msg->tabstr, dup, free_nstr_ptr);
} /* add_nstrdup_to_msg(...) */

int add_strdup_to_msg(NETW_MSG* msg, const char* str) {
    __n_assert(msg, return FALSE);
    __n_assert(str, return FALSE);

    N_STR* dup = char_to_nstr(str);
    __n_assert(dup, return FALSE);

    return list_push(msg->tabstr, dup, free_nstr_ptr);
} /* add_strdup_to_msg(...) */

int add_bytes_to_msg(NETW_MSG* msg, const void* bytes, size_t len) {
    __n_assert(msg, return FALSE);
    __n_assert(bytes, return FALSE);

    /* new_nstr(N) sets length=N and allocates N+1 bytes. The wire decoder
     * requires written < length (length reserves room for a trailing NUL),
     * so we ask for len+1 to keep the invariant when written == len. */
    N_STR* blob = new_nstr(len + 1);
    __n_assert(blob, return FALSE);
    if (len > 0) memcpy(blob->data, bytes, len);
    blob->data[len] = '\0';
    blob->written = len;

    if (list_push(msg->tabstr, blob, free_nstr_ptr) == FALSE) {
        free_nstr(&blob);
        return FALSE;
    }
    return TRUE;
} /* add_bytes_to_msg(...) */

int get_nb_from_msg(NETW_MSG* msg, double* value) {
    double* val = NULL;

    __n_assert(msg, return FALSE);

    val = list_shift(msg->tabflt, double);
    if (val) {
        (*value) = val[0];
        Free(val);
        return TRUE;
    }
    return FALSE;
} /* get_nb_from_msg(...) */

int get_int_from_msg(NETW_MSG* msg, int* value) {
    int* val = NULL;

    __n_assert(msg, return FALSE);

    val = list_shift(msg->tabint, int);
    if (val) {
        (*value) = val[0];
        Free(val);
        return TRUE;
    }
    return FALSE;

} /* get_int_from_msg(...) */

int get_nstr_from_msg(NETW_MSG* msg, N_STR** value) {
    N_STR* val = NULL;

    __n_assert(msg, return FALSE);

    val = list_shift(msg->tabstr, N_STR);
    if (val) {
        if ((*value)) {
            n_log(LOG_ERR, "Previous pointer value %p overriden by pointer %p", (*value), val);
        }
        (*value) = val;
        return TRUE;
    }
    return FALSE;
} /* get_nstr_from_msg(...) */

int get_str_from_msg(NETW_MSG* msg, char** value) {
    N_STR* val = NULL;

    __n_assert(msg, return FALSE);

    val = list_shift(msg->tabstr, N_STR);
    if (val) {
        if ((*value)) {
            n_log(LOG_ERR, "Previous pointer value %p overriden by pointer %p", (*value), val);
        }

        (*value) = val->data;
        Free(val);
        return TRUE;
    }
    return FALSE;
} /* get_str_from_msg(...) */

int get_bytes_from_msg(NETW_MSG* msg, void** out_bytes, size_t* out_len) {
    __n_assert(msg, return FALSE);
    __n_assert(out_bytes, return FALSE);
    __n_assert(out_len, return FALSE);

    N_STR* val = list_shift(msg->tabstr, N_STR);
    if (!val) return FALSE;

    if (!val->data || val->written == 0) {
        FreeNoLog(val->data);
        Free(val);
        *out_bytes = NULL;
        *out_len = 0;
        return FALSE;
    }

    /* Hand the allocation to the caller, reuses the N_STR buffer to
     * avoid an extra copy. The caller frees with plain free(). */
    *out_bytes = val->data;
    *out_len = val->written;
    val->data = NULL;
    Free(val);
    return TRUE;
} /* get_bytes_from_msg(...) */

N_STR* make_str_from_msg(NETW_MSG* msg) {
    size_t str_length = 0;
    LIST_NODE* node = NULL;

    N_STR *strptr = NULL,
          *generated_str = NULL;

    char* charptr = NULL;

    __n_assert(msg, return NULL);

    /* the first thing to do is to computer the resulting string size
       the first 12 octets are for the number of ints , floats, strings in the message
       */
    str_length = 3 * sizeof(int32_t) + (msg->tabint->nb_items * sizeof(int32_t)) + (msg->tabflt->nb_items * sizeof(double));

    /* let's count the eventual string blocks */
    node = msg->tabstr->start;
    while (node) {
        strptr = (N_STR*)node->ptr;
        str_length += sizeof(int32_t) + sizeof(int32_t) + strptr->written;
        node = node->next;
    }

    /* preparing the new string */
    Malloc(generated_str, N_STR, 1);
    __n_assert(generated_str, return NULL);
    Malloc(generated_str->data, char, str_length + 1);
    __n_assert(generated_str->data, Free(generated_str); return NULL);

    generated_str->length = str_length + 1;
    generated_str->written = str_length;

    /* copying header */
    charptr = generated_str->data;

    /* testing limits */
    if (msg->tabint->nb_items >= UINT32_MAX) {
        n_log(LOG_ERR, "too much items (>=UINT32_MAX) in int list of message %p", msg);
        free_nstr(&generated_str);
        return NULL;
    }
    if (msg->tabflt->nb_items >= UINT32_MAX) {
        n_log(LOG_ERR, "too much items (>=UINT32_MAX) in float list of message %p", msg);
        free_nstr(&generated_str);
        return NULL;
    }
    if (msg->tabstr->nb_items >= UINT32_MAX) {
        n_log(LOG_ERR, "too much items (>=UINT32_MAX) in string list of message %p", msg);
        free_nstr(&generated_str);
        return NULL;
    }

    /* byte order */
    uint32_t nb_int_items = htonl((uint32_t)msg->tabint->nb_items);
    uint32_t nb_float_items = htonl((uint32_t)msg->tabflt->nb_items);
    uint32_t nb_str_items = htonl((uint32_t)msg->tabstr->nb_items);

    /* number of ints inside the message */
    memcpy(charptr, &nb_int_items, sizeof(uint32_t));
    charptr += sizeof(uint32_t);
    /* number of doubles inside the message */
    memcpy(charptr, &nb_float_items, sizeof(uint32_t));
    charptr += sizeof(uint32_t);
    /* number of string inside the message */
    memcpy(charptr, &nb_str_items, sizeof(uint32_t));
    charptr += sizeof(uint32_t);

    /* if there are numbers, copy all them ! */
    node = msg->tabint->start;
    while (node) {
        uint32_t* nbptr = (uint32_t*)node->ptr;
        uint32_t val = htonl(nbptr[0]);
        memcpy(charptr, &val, sizeof(uint32_t));
        charptr += sizeof(uint32_t);
        node = node->next;
    }

    /* if there are numbers, copy all them ! */
    node = msg->tabflt->start;
    while (node) {
        const double* nbptr = (const double*)node->ptr;
        double val = htond(nbptr[0]);
        memcpy(charptr, &val, sizeof(double));
        charptr += sizeof(double);
        node = node->next;
    }

    /* if there are strings, copy all them ! */
    if (msg->tabstr->nb_items > 0) {
        node = msg->tabstr->start;
        while (node) {
            strptr = (N_STR*)node->ptr;
            if (strptr->length >= UINT32_MAX) {
                n_log(LOG_ERR, "string too long (>=UINT32_MAX) in string list of message %p", msg);
                free_nstr(&generated_str);
                return NULL;
            }
            uint32_t var = htonl((uint32_t)strptr->length);
            memcpy(charptr, &var, sizeof(uint32_t));
            charptr += sizeof(uint32_t);

            if (strptr->written >= UINT32_MAX) {
                n_log(LOG_ERR, "string too long (>=UINT32_MAX) in string list of message %p", msg);
                free_nstr(&generated_str);
                return NULL;
            }
            var = htonl((uint32_t)strptr->written);
            memcpy(charptr, &var, sizeof(uint32_t));
            charptr += sizeof(uint32_t);

            memcpy(charptr, strptr->data, strptr->written);
            charptr += strptr->written;
            node = node->next;
        }
    }

    return generated_str;
} /* make_str_from_msg(...) */

NETW_MSG* make_msg_from_str(N_STR* str) {
    NETW_MSG* generated_msg = NULL;
    N_STR* tmpstr = NULL;

    char* charptr = NULL;

    uint32_t it,
        nb_int = 0,
        nb_flt = 0,
        nb_str = 0;
    int32_t tmpnb = 0;

    double tmpflt = 0;

    __n_assert(str, return NULL);

    charptr = str->data;

    /* need at least 3 uint32_t for the header */
    if (str->written < 3 * sizeof(uint32_t)) {
        n_log(LOG_ERR, "message too short: %zu bytes, need at least %zu", str->written, 3 * sizeof(uint32_t));
        return NULL;
    }

    create_msg(&generated_msg);

    __n_assert(generated_msg, return NULL);

    memcpy(&nb_int, charptr, sizeof(uint32_t));
    charptr += sizeof(uint32_t);
    nb_int = ntohl(nb_int);

    memcpy(&nb_flt, charptr, sizeof(uint32_t));
    charptr += sizeof(uint32_t);
    nb_flt = ntohl(nb_flt);

    memcpy(&nb_str, charptr, sizeof(uint32_t));
    charptr += sizeof(uint32_t);
    nb_str = ntohl(nb_str);

    /* validate that the message is large enough for the claimed int and float counts.
     * Use division-based checks to avoid size_t overflow on 32-bit platforms. */
    size_t remaining = str->written - 3 * sizeof(uint32_t);

    if (nb_int > remaining / sizeof(uint32_t)) {
        n_log(LOG_ERR, "message claims %u ints but only %zu bytes remain", nb_int, remaining);
        delete_msg(&generated_msg);
        return NULL;
    }
    remaining -= (size_t)nb_int * sizeof(uint32_t);

    if (nb_flt > remaining / sizeof(double)) {
        n_log(LOG_ERR, "message claims %u floats but only %zu bytes remain", nb_flt, remaining);
        delete_msg(&generated_msg);
        return NULL;
    }
    remaining -= (size_t)nb_flt * sizeof(double);

    if (nb_int > 0) {
        for (it = 0; it < nb_int; it++) {
            memcpy(&tmpnb, charptr, sizeof(uint32_t));
            tmpnb = (int32_t)ntohl((uint32_t)tmpnb);
            charptr += sizeof(uint32_t);
            if (add_int_to_msg(generated_msg, tmpnb) == FALSE) {
                n_log(LOG_ERR, "failed to add int %u/%u to message", it, nb_int);
                delete_msg(&generated_msg);
                return NULL;
            }
        }
    }

    if (nb_flt > 0) {
        for (it = 0; it < nb_flt; it++) {
            memcpy(&tmpflt, charptr, sizeof(double));
            tmpflt = ntohd(tmpflt);
            charptr += sizeof(double);
            if (add_nb_to_msg(generated_msg, tmpflt) == FALSE) {
                n_log(LOG_ERR, "failed to add float %u/%u to message", it, nb_flt);
                delete_msg(&generated_msg);
                return NULL;
            }
        }
    }

    if (nb_str > 0) {
        /* sanity check: each string needs at least 2 uint32_t for headers */
        if ((size_t)nb_str > remaining / (2 * sizeof(uint32_t))) {
            n_log(LOG_ERR, "message claims %u strings but only %zu bytes remain (need at least %zu for headers)",
                  nb_str, remaining, (size_t)nb_str * 2 * sizeof(uint32_t));
            delete_msg(&generated_msg);
            return NULL;
        }

        for (it = 0; it < nb_str; it++) {
            /* check space for length + written headers */
            if (remaining < 2 * sizeof(uint32_t)) {
                n_log(LOG_ERR, "string %u/%u: not enough data for string headers (%zu remaining)", it, nb_str, remaining);
                delete_msg(&generated_msg);
                return NULL;
            }

            Malloc(tmpstr, N_STR, 1);
            __n_assert(tmpstr, { delete_msg(&generated_msg); return NULL; });

            uint32_t var = 0;
            memcpy(&var, charptr, sizeof(uint32_t));
            charptr += sizeof(uint32_t);
            tmpstr->length = ntohl(var);

            memcpy(&var, charptr, sizeof(uint32_t));
            charptr += sizeof(uint32_t);
            tmpstr->written = ntohl(var);
            remaining -= 2 * sizeof(uint32_t);

            /* validate written < length (N_STR requires room for trailing '\0') */
            if (tmpstr->length == 0 || tmpstr->written >= tmpstr->length) {
                n_log(LOG_ERR, "string %u/%u: written (%zu) >= length (%zu), violates N_STR invariant", it, nb_str, tmpstr->written, tmpstr->length);
                Free(tmpstr);
                delete_msg(&generated_msg);
                return NULL;
            }

            /* validate remaining buffer has enough for written bytes */
            if (tmpstr->written > remaining) {
                n_log(LOG_ERR, "string %u/%u: written size %zu exceeds remaining %zu bytes", it, nb_str, tmpstr->written, remaining);
                Free(tmpstr);
                delete_msg(&generated_msg);
                return NULL;
            }

            /* sanity cap on allocation size to prevent DoS via huge allocation */
            if (tmpstr->length > NETW_MSG_MAX_STRING_LENGTH) {
                n_log(LOG_ERR, "string %u/%u: length %zu exceeds NETW_MSG_MAX_STRING_LENGTH (%zu)", it, nb_str, tmpstr->length, (size_t)NETW_MSG_MAX_STRING_LENGTH);
                Free(tmpstr);
                delete_msg(&generated_msg);
                return NULL;
            }

            /* allocate based on written+1 (not untrusted length) to prevent memory-DoS
             * where attacker sets length near cap but written tiny */
            Malloc(tmpstr->data, char, tmpstr->written + 1);
            __n_assert(tmpstr->data, { Free(tmpstr); delete_msg(&generated_msg); return NULL; });
            tmpstr->length = tmpstr->written + 1;

            memcpy(tmpstr->data, charptr, tmpstr->written);
            tmpstr->data[tmpstr->written] = '\0';
            charptr += tmpstr->written;
            remaining -= tmpstr->written;

            if (add_nstrptr_to_msg(generated_msg, tmpstr) == FALSE) {
                Free(tmpstr->data);
                Free(tmpstr);
                delete_msg(&generated_msg);
                return NULL;
            }
        }
    }
    return generated_msg;
} /* make_msg_from_str(...) */

#ifndef NO_NETMSG_API

N_STR* netmsg_make_ping(int type, int id_from, int id_to, int time) {
    NETW_MSG* msg = NULL;

    N_STR* tmpstr = NULL;

    create_msg(&msg);

    __n_assert(msg, return NULL);

    if (add_int_to_msg(msg, type) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id_from) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id_to) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, time) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    tmpstr = make_str_from_msg(msg);
    delete_msg(&msg);

    return tmpstr;
} /* netmsg_make_ping() */

N_STR* netmsg_make_ident(int type, int id, N_STR* name, N_STR* passwd) {
    NETW_MSG* msg = NULL;
    N_STR* tmpstr = NULL;

    create_msg(&msg);
    __n_assert(msg, return NULL);

    if (add_int_to_msg(msg, type) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    if (add_nstrdup_to_msg(msg, name) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nstrdup_to_msg(msg, passwd) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    tmpstr = make_str_from_msg(msg);
    delete_msg(&msg);

    return tmpstr;
} /* netmsg_make_ident() */

N_STR* netmsg_make_position_msg(int id, double X, double Y, double vx, double vy, double acc_x, double acc_y, int time_stamp) {
    NETW_MSG* msg = NULL;
    N_STR* tmpstr = NULL;

    create_msg(&msg);

    __n_assert(msg, return NULL);

    if (add_int_to_msg(msg, NETMSG_POSITION) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, X) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, Y) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, vx) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, vy) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, acc_x) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nb_to_msg(msg, acc_y) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, time_stamp) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    tmpstr = make_str_from_msg(msg);
    delete_msg(&msg);
    return tmpstr;
} /* netmsg_make_position_msg() */

N_STR* netmsg_make_string_msg(int id_from, int id_to, N_STR* name, N_STR* chan, N_STR* txt, int color) {
    NETW_MSG* msg = NULL;

    N_STR* tmpstr = NULL;

    create_msg(&msg);

    __n_assert(msg, return NULL);

    if (add_int_to_msg(msg, NETMSG_STRING) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id_from) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, id_to) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_int_to_msg(msg, color) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    if (add_nstrdup_to_msg(msg, name) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nstrdup_to_msg(msg, chan) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    if (add_nstrdup_to_msg(msg, txt) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }

    tmpstr = make_str_from_msg(msg);
    delete_msg(&msg);
    __n_assert(tmpstr, return NULL);

    return tmpstr;

} /* netmsg_make_string_msg */

N_STR* netmsg_make_quit_msg(void) {
    NETW_MSG* msg = NULL;
    N_STR* tmpstr = NULL;

    create_msg(&msg);
    __n_assert(msg, return NULL);

    if (add_int_to_msg(msg, NETMSG_QUIT) == FALSE) {
        delete_msg(&msg);
        return NULL;
    }
    tmpstr = make_str_from_msg(msg);
    delete_msg(&msg);
    __n_assert(tmpstr, return NULL);

    return tmpstr;
} /* netmsg_make_quit_msg() */

int netw_msg_get_type(N_STR* msg) {
    uint32_t nb_int = 0;
    int32_t tmpnb = 0;
    char* charptr = NULL;

    __n_assert(msg, return -1);
    __n_assert(msg->data, return -1);

    /* need at least 3*uint32 header + 1*uint32 for the type int */
    if (msg->written < 4 * sizeof(uint32_t)) return -1;

    charptr = msg->data;

    /* read nb_int from the header to verify the int section is present */
    memcpy(&nb_int, charptr, sizeof(uint32_t));
    nb_int = ntohl(nb_int);

    if (nb_int < 1) {
        n_log(LOG_ERR, "netw_msg_get_type: message has no int fields (nb_int=%u), cannot read type", nb_int);
        return -1;
    }

    /* bypass nb_int, nb_flt, nb_str header fields to reach the first int (type) */
    charptr += 3 * sizeof(uint32_t);
    memcpy(&tmpnb, charptr, sizeof(uint32_t));
    tmpnb = (int32_t)ntohl((uint32_t)tmpnb);

    return tmpnb;
} /* netw_msg_get_type(...) */

int netw_get_ident(N_STR* msg, int* type, int* ident, N_STR** name, N_STR** passwd) {
    NETW_MSG* netmsg = NULL;

    __n_assert(msg, return FALSE);

    netmsg = make_msg_from_str(msg);

    __n_assert(netmsg, return FALSE);

    if (get_int_from_msg(netmsg, type) != TRUE ||
        get_int_from_msg(netmsg, ident) != TRUE) {
        n_log(LOG_ERR, "failed to extract type/ident from ident message");
        delete_msg(&netmsg);
        return FALSE;
    }

    if ((*type) != NETMSG_IDENT_REQUEST && (*type) != NETMSG_IDENT_REPLY_OK && (*type) != NETMSG_IDENT_REPLY_NOK) {
        n_log(LOG_ERR, "unknow (*type) %d", (*type));
        delete_msg(&netmsg);
        return FALSE;
    }

    if (get_nstr_from_msg(netmsg, name) != TRUE ||
        get_nstr_from_msg(netmsg, passwd) != TRUE) {
        n_log(LOG_ERR, "failed to extract name/passwd from ident message");
        free_nstr(name);
        free_nstr(passwd);
        delete_msg(&netmsg);
        return FALSE;
    }

    delete_msg(&netmsg);

    return TRUE;
} /* netw_get_ident( ... ) */

int netw_get_position(N_STR* msg, int* id, double* X, double* Y, double* vx, double* vy, double* acc_x, double* acc_y, int* time_stamp) {
    NETW_MSG* netmsg = NULL;

    int type = 0;

    __n_assert(msg, return FALSE);

    netmsg = make_msg_from_str(msg);

    __n_assert(netmsg, return FALSE);

    if (get_int_from_msg(netmsg, &type) != TRUE) {
        n_log(LOG_ERR, "failed to extract type from position message");
        delete_msg(&netmsg);
        return FALSE;
    }

    if (type != NETMSG_POSITION) {
        delete_msg(&netmsg);
        return FALSE;
    }

    if (get_int_from_msg(netmsg, id) != TRUE ||
        get_nb_from_msg(netmsg, X) != TRUE ||
        get_nb_from_msg(netmsg, Y) != TRUE ||
        get_nb_from_msg(netmsg, vx) != TRUE ||
        get_nb_from_msg(netmsg, vy) != TRUE ||
        get_nb_from_msg(netmsg, acc_x) != TRUE ||
        get_nb_from_msg(netmsg, acc_y) != TRUE ||
        get_int_from_msg(netmsg, time_stamp) != TRUE) {
        n_log(LOG_ERR, "failed to extract fields from position message");
        delete_msg(&netmsg);
        return FALSE;
    }

    delete_msg(&netmsg);

    return TRUE;
} /* netw_get_position( ... ) */

int netw_get_string(N_STR* msg, int* id, N_STR** name, N_STR** chan, N_STR** txt, int* color) {
    NETW_MSG* netmsg = NULL;

    int type = 0;

    __n_assert(msg, return FALSE);

    netmsg = make_msg_from_str(msg);

    __n_assert(netmsg, return FALSE);

    if (get_int_from_msg(netmsg, &type) != TRUE) {
        n_log(LOG_ERR, "failed to extract type from string message");
        delete_msg(&netmsg);
        return FALSE;
    }

    if (type != NETMSG_STRING) {
        delete_msg(&netmsg);
        return FALSE;
    }

    int id_to = 0;
    if (get_int_from_msg(netmsg, id) != TRUE ||
        get_int_from_msg(netmsg, &id_to) != TRUE ||
        get_int_from_msg(netmsg, color) != TRUE) {
        n_log(LOG_ERR, "failed to extract int fields from string message");
        delete_msg(&netmsg);
        return FALSE;
    }

    if (get_nstr_from_msg(netmsg, name) != TRUE ||
        get_nstr_from_msg(netmsg, chan) != TRUE ||
        get_nstr_from_msg(netmsg, txt) != TRUE) {
        n_log(LOG_ERR, "failed to extract string fields from string message");
        free_nstr(name);
        free_nstr(chan);
        free_nstr(txt);
        delete_msg(&netmsg);
        return FALSE;
    }

    delete_msg(&netmsg);

    return TRUE;

} /* netw_get_string( ... ) */

int netw_get_ping(N_STR* msg, int* type, int* from, int* to, int* time) {
    NETW_MSG* netmsg;

    __n_assert(msg, return FALSE);

    netmsg = make_msg_from_str(msg);

    __n_assert(netmsg, return FALSE);

    if (get_int_from_msg(netmsg, type) != TRUE) {
        n_log(LOG_ERR, "failed to extract type from ping message");
        delete_msg(&netmsg);
        return FALSE;
    }

    if ((*type) != NETMSG_PING_REQUEST && (*type) != NETMSG_PING_REPLY) {
        delete_msg(&netmsg);
        return FALSE;
    }

    if (get_int_from_msg(netmsg, from) != TRUE ||
        get_int_from_msg(netmsg, to) != TRUE ||
        get_int_from_msg(netmsg, time) != TRUE) {
        n_log(LOG_ERR, "failed to extract fields from ping message");
        delete_msg(&netmsg);
        return FALSE;
    }

    delete_msg(&netmsg);

    return TRUE;
} /* netw_get_ping( ... ) */

int netw_get_quit(N_STR* msg) {
    __n_assert(msg, return FALSE);

    NETW_MSG* netmsg = NULL;
    netmsg = make_msg_from_str(msg);
    __n_assert(netmsg, return FALSE);

    int type = -1;
    get_int_from_msg(netmsg, &type);
    if (type != NETMSG_QUIT) {
        delete_msg(&netmsg);
        return FALSE;
    }
    delete_msg(&netmsg);
    return TRUE;
} /* netw_get_quit( ... ) */

#endif  // NO_NETMSG_API