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}