diff options
Diffstat (limited to 'src/core/circuit.c')
| -rw-r--r-- | src/core/circuit.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/core/circuit.c b/src/core/circuit.c new file mode 100644 index 0000000..04ac264 --- /dev/null +++ b/src/core/circuit.c @@ -0,0 +1,337 @@ +#include "hybbx/circuit.h" + +#include <string.h> + +static uint32_t read_be32(const uint8_t *p) +{ + return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | + ((uint32_t)p[2] << 8) | (uint32_t)p[3]; +} + +static uint16_t read_be16(const uint8_t *p) +{ + return (uint16_t)(((uint16_t)p[0] << 8) | (uint16_t)p[1]); +} + +static void write_be32(uint8_t *p, uint32_t v) +{ + p[0] = (uint8_t)((v >> 24) & 0xFFu); + p[1] = (uint8_t)((v >> 16) & 0xFFu); + p[2] = (uint8_t)((v >> 8) & 0xFFu); + p[3] = (uint8_t)(v & 0xFFu); +} + +static void write_be16(uint8_t *p, uint16_t v) +{ + p[0] = (uint8_t)((v >> 8) & 0xFFu); + p[1] = (uint8_t)(v & 0xFFu); +} + +const char *hybbx_circuit_proto_name(hybbx_circuit_proto_t proto) +{ + switch (proto) { + case HYBBX_CIRCUIT_PROTO_AX25: + return "ax25"; + case HYBBX_CIRCUIT_PROTO_AX25_UI: + return "ax25_ui"; + case HYBBX_CIRCUIT_PROTO_LINK_AUTH: + return "link_auth"; + case HYBBX_CIRCUIT_PROTO_LINK_AUTH_ACK: + return "link_auth_ack"; + case HYBBX_CIRCUIT_PROTO_FLOW_CTRL: + return "flow_ctrl"; + case HYBBX_CIRCUIT_PROTO_TERMINAL: + return "terminal"; + case HYBBX_CIRCUIT_PROTO_PROXY_MAIL: + return "proxy_mail"; + case HYBBX_CIRCUIT_PROTO_PROXY_CHAT: + return "proxy_chat"; + case HYBBX_CIRCUIT_PROTO_RESERVED_APRS: + return "aprs"; + case HYBBX_CIRCUIT_PROTO_RESERVED_NETROM: + return "netrom"; + default: + return "unknown"; + } +} + +void hybbx_circuit_decoder_init(hybbx_circuit_decoder_t *dec) +{ + if (dec == NULL) { + return; + } + + memset(dec, 0, sizeof(*dec)); +} + +static void circuit_deliver(hybbx_circuit_decoder_t *dec, + hybbx_circuit_frame_cb cb, void *userdata) +{ + const uint8_t *payload; + size_t payload_len; + + if (dec->len < HYBBX_CIRCUIT_HEADER_SIZE) { + dec->len = 0; + dec->need = 0; + dec->have_header = 0; + return; + } + + payload = dec->buf + HYBBX_CIRCUIT_HEADER_SIZE; + payload_len = dec->len - HYBBX_CIRCUIT_HEADER_SIZE; + + if (cb != NULL) { + cb(dec->proto, dec->flags, payload, payload_len, userdata); + } + + dec->len = 0; + dec->need = 0; + dec->have_header = 0; +} + +static int circuit_parse_header(hybbx_circuit_decoder_t *dec) +{ + const uint8_t *h = dec->buf; + + if (h[0] != HYBBX_CIRCUIT_MAGIC_0 || h[1] != HYBBX_CIRCUIT_MAGIC_1 || + h[2] != HYBBX_CIRCUIT_MAGIC_2 || h[3] != HYBBX_CIRCUIT_VERSION) { + return 0; + } + + dec->proto = (hybbx_circuit_proto_t)h[4]; + dec->flags = read_be16(h + 5); + dec->need = (size_t)read_be32(h + 7); + if (dec->need > HYBBX_CIRCUIT_MAX_PAYLOAD) { + return 0; + } + + return 1; +} + +void hybbx_circuit_decoder_feed(hybbx_circuit_decoder_t *dec, + const uint8_t *data, size_t len, + hybbx_circuit_frame_cb cb, void *userdata) +{ + size_t i; + + if (dec == NULL || data == NULL) { + return; + } + + for (i = 0; i < len; i++) { + if (dec->len >= sizeof(dec->buf)) { + hybbx_circuit_decoder_init(dec); + } + + dec->buf[dec->len++] = data[i]; + + if (!dec->have_header) { + if (dec->len < HYBBX_CIRCUIT_HEADER_SIZE) { + continue; + } + if (!circuit_parse_header(dec)) { + memmove(dec->buf, dec->buf + 1, dec->len - 1); + dec->len--; + continue; + } + dec->have_header = 1; + } + + if (dec->len < HYBBX_CIRCUIT_HEADER_SIZE + dec->need) { + continue; + } + + circuit_deliver(dec, cb, userdata); + } +} + +size_t hybbx_circuit_encode(hybbx_circuit_proto_t proto, uint16_t flags, + const uint8_t *payload, size_t payload_len, + uint8_t *out, size_t out_cap) +{ + if (out == NULL || out_cap < HYBBX_CIRCUIT_HEADER_SIZE) { + return 0; + } + + if (payload_len > HYBBX_CIRCUIT_MAX_PAYLOAD) { + return 0; + } + + if (payload_len > 0 && payload == NULL) { + return 0; + } + + if (HYBBX_CIRCUIT_HEADER_SIZE + payload_len > out_cap) { + return 0; + } + + out[0] = HYBBX_CIRCUIT_MAGIC_0; + out[1] = HYBBX_CIRCUIT_MAGIC_1; + out[2] = HYBBX_CIRCUIT_MAGIC_2; + out[3] = HYBBX_CIRCUIT_VERSION; + out[4] = (uint8_t)proto; + write_be16(out + 5, flags); + + write_be32(out + 7, (uint32_t)payload_len); + if (payload_len > 0) { + memcpy(out + HYBBX_CIRCUIT_HEADER_SIZE, payload, payload_len); + } + + return HYBBX_CIRCUIT_HEADER_SIZE + payload_len; +} + +size_t hybbx_circuit_encode_ax25(const uint8_t *frame, size_t frame_len, + uint16_t flags, + uint8_t *out, size_t out_cap) +{ + return hybbx_circuit_encode(HYBBX_CIRCUIT_PROTO_AX25, + (uint16_t)(flags | HYBBX_CIRCUIT_FLAG_RX), + frame, frame_len, out, out_cap); +} + +static size_t pack_path_payload(const hybbx_ax25_path_t *path, + const uint8_t *payload, size_t payload_len, + uint8_t *out, size_t out_cap) +{ + size_t pos = 0; + unsigned i; + + if (path == NULL || out == NULL) { + return 0; + } + + if (1 + payload_len > out_cap) { + return 0; + } + + out[pos++] = (uint8_t)path->digi_count; + if (pos + HYBBX_AX25_ADDR_ENCODED * (2 + path->digi_count) > out_cap) { + return 0; + } + + hybbx_ax25_encode_address(out + pos, &path->dest, 0); + pos += HYBBX_AX25_ADDR_ENCODED; + for (i = 0; i < path->digi_count; i++) { + hybbx_ax25_encode_address(out + pos, &path->digi[i], 0); + pos += HYBBX_AX25_ADDR_ENCODED; + } + hybbx_ax25_encode_address(out + pos, &path->source, 1); + pos += HYBBX_AX25_ADDR_ENCODED; + + if (payload_len > 0) { + if (pos + payload_len > out_cap) { + return 0; + } + memcpy(out + pos, payload, payload_len); + pos += payload_len; + } + + return pos; +} + +size_t hybbx_circuit_encode_ax25_ui(const hybbx_ax25_path_t *path, + const uint8_t *payload, size_t payload_len, + uint16_t flags, + uint8_t *out, size_t out_cap) +{ + uint8_t body[HYBBX_CIRCUIT_MAX_PAYLOAD]; + size_t body_len; + + body_len = pack_path_payload(path, payload, payload_len, body, sizeof(body)); + if (body_len == 0 && payload_len > 0) { + return 0; + } + + return hybbx_circuit_encode(HYBBX_CIRCUIT_PROTO_AX25_UI, + (uint16_t)(flags | HYBBX_CIRCUIT_FLAG_PATH), + body, body_len, out, out_cap); +} + +size_t hybbx_circuit_encode_terminal(const char *data, size_t len, + uint8_t *out, size_t out_cap) +{ + return hybbx_circuit_encode(HYBBX_CIRCUIT_PROTO_TERMINAL, + HYBBX_CIRCUIT_FLAG_TX, + (const uint8_t *)data, len, out, out_cap); +} + +size_t hybbx_circuit_encode_link_msg(hybbx_circuit_proto_t proto, + const char *payload, size_t payload_len, + uint8_t *out, size_t out_cap) +{ + if (proto != HYBBX_CIRCUIT_PROTO_LINK_AUTH && + proto != HYBBX_CIRCUIT_PROTO_LINK_AUTH_ACK) { + return 0; + } + + return hybbx_circuit_encode(proto, HYBBX_CIRCUIT_FLAG_NONE, + (const uint8_t *)payload, payload_len, + out, out_cap); +} + +static void decode_address_field(const uint8_t *in, hybbx_ax25_address_t *out) +{ + size_t i; + + for (i = 0; i < HYBBX_AX25_CALL_MAX; i++) { + char ch = (char)((in[i] >> 1) & 0x7Fu); + out->call[i] = ch == ' ' ? '\0' : ch; + } + out->call[HYBBX_AX25_CALL_MAX] = '\0'; + out->ssid = (unsigned)((in[6] >> 1) & 0x0Fu); +} + +size_t hybbx_circuit_unpack_ax25_ui(const uint8_t *payload, size_t payload_len, + hybbx_ax25_path_t *path, + uint8_t *ui, size_t ui_cap) +{ + size_t pos = 0; + unsigned digi_count; + unsigned i; + + if (payload == NULL || payload_len < 1) { + return 0; + } + + if (path != NULL) { + memset(path, 0, sizeof(*path)); + } + + digi_count = payload[0]; + if (digi_count > HYBBX_AX25_MAX_DIGI) { + return 0; + } + + pos = 1; + if (pos + HYBBX_AX25_ADDR_ENCODED * (size_t)(2 + digi_count) > payload_len) { + return 0; + } + + if (path != NULL) { + decode_address_field(payload + pos, &path->dest); + pos += HYBBX_AX25_ADDR_ENCODED; + for (i = 0; i < digi_count; i++) { + decode_address_field(payload + pos, &path->digi[i]); + pos += HYBBX_AX25_ADDR_ENCODED; + } + path->digi_count = digi_count; + decode_address_field(payload + pos, &path->source); + } else { + pos += HYBBX_AX25_ADDR_ENCODED * (size_t)(2 + digi_count); + } + pos += HYBBX_AX25_ADDR_ENCODED; + + { + size_t ui_len = payload_len - pos; + + if (ui_len > ui_cap) { + return 0; + } + + if (ui_len > 0 && ui != NULL) { + memcpy(ui, payload + pos, ui_len); + } + + return ui_len; + } +} |
