QUELLCODE / C

protocol.c

pico/protocol.c

1/*2 * OpenAss: nur privater, experimenteller Gebrauch; auf eigene Verantwortung.3 * Niemals Unternehmensrechner, Produktivsysteme, Industrieanlagen oder4 * sicherheitskritische Systeme anschließen oder bedienen.5 * Haftungshinweis und gesetzliche Grenzen: ../DISCLAIMER.txt.6 */7#include "protocol.h"89#include <inttypes.h>10#include <limits.h>11#include <stdio.h>12#include <string.h>1314uint16_t hb_crc16(const char *data, size_t length) {15    uint16_t crc = 0xffffu;16    for (size_t i = 0; i < length; ++i) {17        crc ^= (uint16_t)((uint16_t)(uint8_t)data[i] << 8u);18        for (unsigned bit = 0; bit < 8u; ++bit) {19            uint32_t shifted = (uint32_t)crc << 1u;20            crc = (uint16_t)((crc & 0x8000u) ? shifted ^ 0x1021u : shifted);21        }22    }23    return crc;24}2526static int hex_digit(char c) {27    if (c >= '0' && c <= '9') return c - '0';28    if (c >= 'A' && c <= 'F') return c - 'A' + 10;29    if (c >= 'a' && c <= 'f') return c - 'a' + 10;30    return -1;31}3233static bool decimal(const char *s, size_t length, bool signed_value, int64_t *out) {34    if (!length) return false;35    bool negative = signed_value && s[0] == '-';36    size_t i = negative ? 1u : 0u;37    if (i == length) return false;38    uint64_t value = 0;39    uint64_t maximum = signed_value ? (negative ? 2147483648u : 2147483647u) : UINT32_MAX;40    for (; i < length; ++i) {41        if (s[i] < '0' || s[i] > '9') return false;42        unsigned digit = (unsigned)(s[i] - '0');43        if (value > (maximum - digit) / 10u) return false;44        value = value * 10u + digit;45    }46    *out = negative ? -(int64_t)value : (int64_t)value;47    return true;48}4950hb_parse_status_t hb_parse(const char *line, size_t length, hb_request_t *out) {51    memset(out, 0, sizeof(*out));52    if (!line || length < 13u || length > HB_FRAME_MAX || memcmp(line, "HB1 ", 4u)) {53        return HB_PARSE_FORMAT;54    }55    // Extract an advisory sequence before checking CRC so a damaged frame can56    // receive ERR CRC with its sequence. No command is executed before validation.57    size_t position = 4u;58    size_t start = position;59    while (position < length && line[position] != ' ') ++position;60    int64_t value;61    if (position == length || !decimal(line + start, position - start, false, &value)) {62        return HB_PARSE_FORMAT;63    }64    out->seq = (uint32_t)value;6566    size_t body_length = length - 6u;67    if (line[body_length] != ' ' || line[body_length + 1u] != '*') return HB_PARSE_FORMAT;68    uint16_t supplied_crc = 0;69    for (size_t i = body_length + 2u; i < length; ++i) {70        int digit = hex_digit(line[i]);71        if (digit < 0) return HB_PARSE_FORMAT;72        supplied_crc = (uint16_t)((supplied_crc << 4u) | (unsigned)digit);73    }74    if (hb_crc16(line, body_length) != supplied_crc) return HB_PARSE_CRC;7576    start = ++position;77    while (position < body_length && line[position] != ' ') {78        if (line[position] < 'A' || line[position] > 'Z') return HB_PARSE_FORMAT;79        ++position;80    }81    size_t op_length = position - start;82    if (!op_length || op_length > HB_OP_MAX || start > body_length) return HB_PARSE_FORMAT;83    memcpy(out->op, line + start, op_length);84    out->op[op_length] = '\0';8586    while (position < body_length) {87        start = ++position;88        while (position < body_length && line[position] != ' ') ++position;89        if (out->argc == HB_ARG_MAX || !decimal(line + start, position - start, true, &value)) {90            return HB_PARSE_FORMAT;91        }92        out->args[out->argc++] = (int32_t)value;93    }94    return HB_PARSE_OK;95}9697bool hb_response(char *out, size_t capacity, uint32_t seq, const char *payload) {98    if (!out || !payload || !capacity) return false;99    int body_length = snprintf(out, capacity, "HB1 %" PRIu32 " %s", seq, payload);100    if (body_length < 0 || (size_t)body_length + 8u > capacity) return false;101    uint16_t crc = hb_crc16(out, (size_t)body_length);102    int suffix_length = snprintf(out + body_length, capacity - (size_t)body_length,103                                 " *%04X\n", (unsigned)crc);104    return suffix_length == 7;105}106107hb_sequence_status_t hb_sequence_check(const hb_sequence_t *state,108                                      const hb_request_t *request,109                                      const char *line, size_t length,110                                      bool allow_hello_reset) {111    if (!state->valid) return HB_SEQ_NEW;112    if (request->seq == state->seq && length == state->frame_length &&113        memcmp(line, state->frame, length) == 0) return HB_SEQ_DUPLICATE;114    if (allow_hello_reset && request->argc == 0u && strcmp(request->op, "HELLO") == 0) {115        return HB_SEQ_NEW;116    }117    if (request->seq == state->seq) return HB_SEQ_CONFLICT;118    return request->seq < state->seq ? HB_SEQ_OLD : HB_SEQ_NEW;119}120121void hb_sequence_commit(hb_sequence_t *state, const hb_request_t *request,122                        const char *line, size_t length, const char *response) {123    if (length > HB_FRAME_MAX || strlen(response) >= sizeof(state->response)) return;124    state->valid = true;125    state->seq = request->seq;126    state->frame_length = length;127    memcpy(state->frame, line, length);128    state->frame[length] = '\0';129    memcpy(state->response, response, strlen(response) + 1u);130}