diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-14 15:08:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-14 15:08:02 +0000 |
commit | 8d76eb12d5223d0fc3ea65d8d97f1feda2fcc340 (patch) | |
tree | 0fd0dd005a08843d538923c1a31d968a417dc4bf /src/tinyframe.c | |
parent | Initial commit. (diff) | |
download | tinyframe-8d76eb12d5223d0fc3ea65d8d97f1feda2fcc340.tar.xz tinyframe-8d76eb12d5223d0fc3ea65d8d97f1feda2fcc340.zip |
Adding upstream version 0.1.1.upstream/0.1.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tinyframe.c')
-rw-r--r-- | src/tinyframe.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/src/tinyframe.c b/src/tinyframe.c new file mode 100644 index 0000000..3615b59 --- /dev/null +++ b/src/tinyframe.c @@ -0,0 +1,403 @@ +/* + * Author Jerry Lundström <jerry@dns-oarc.net> + * Copyright (c) 2020, OARC, Inc. + * All rights reserved. + * + * This file is part of the tinyframe library. + * + * tinyframe library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * tinyframe library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "tinyframe/tinyframe.h" + +#include <string.h> +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#else +#ifdef HAVE_SYS_ENDIAN_H +#include <sys/endian.h> +#else +#ifdef HAVE_MACHINE_ENDIAN_H +#include <machine/endian.h> +#endif +#endif +#endif +#include <assert.h> +#ifdef TINYFRAME_TRACE +#include <stdio.h> +#include <ctype.h> +static const char* printable_string(const uint8_t* data, size_t len) +{ + static char buf[512], hex; + size_t r = 0, w = 0; + + while (r < len && w < sizeof(buf) - 1) { + if (isprint(data[r])) { + buf[w++] = data[r++]; + } else { + if (w + 4 >= sizeof(buf) - 1) { + break; + } + + buf[w++] = '\\'; + buf[w++] = 'x'; + hex = (data[r] & 0xf0) >> 4; + if (hex > 9) { + buf[w++] = 'a' + (hex - 10); + } else { + buf[w++] = '0' + hex; + } + hex = data[r++] & 0xf; + if (hex > 9) { + buf[w++] = 'a' + (hex - 10); + } else { + buf[w++] = '0' + hex; + } + } + } + if (w >= sizeof(buf)) { + buf[sizeof(buf) - 1] = 0; + } else { + buf[w] = 0; + } + + return buf; +} +#define trace(x...) \ + fprintf(stderr, "tinyframe %s(): ", __func__); \ + fprintf(stderr, x); \ + fprintf(stderr, "\n"); \ + fflush(stderr); +#else +#define trace(x...) +#define printable_string(x...) +#endif + +const char* const tinyframe_state_string[] = { + "control", + "control_field", + "frame", + "done", +}; + +const char* const tinyframe_result_string[] = { + "ok", + "error", + "have_control", + "have_control_field", + "have_frame", + "stopped", + "finished", + "need_more", +}; + +static inline uint32_t _need32(const void* ptr) +{ + uint32_t v; + memcpy(&v, ptr, sizeof(v)); + return be32toh(v); +} + +static inline void _put32(void* ptr, uint32_t v) +{ + uint32_t be_v = htobe32(v); + memcpy(ptr, &be_v, sizeof(be_v)); +} + +static inline enum tinyframe_result __read_control(struct tinyframe_reader* handle, const uint8_t* data, size_t len) +{ + if (len < 12) { + trace("data len %zu < 12, need more", len); + return tinyframe_need_more; + } + handle->control.length = _need32(data); // "escape" + if (handle->control.length) { + trace("control length !zero, error, header: %s", printable_string(data, 12)); + return tinyframe_error; + } + handle->control.length = _need32(data + 4); // length + if (handle->control.length > TINYFRAME_CONTROL_FRAME_LENGTH_MAX) { + trace("control length > max, error, header: %s", printable_string(data, 12)); + return tinyframe_error; + } + handle->control.type = _need32(data + 8); // type + + switch (handle->control.type) { + case TINYFRAME_CONTROL_ACCEPT: + case TINYFRAME_CONTROL_START: + case TINYFRAME_CONTROL_READY: + break; + case TINYFRAME_CONTROL_STOP: + handle->state = tinyframe_done; + handle->bytes_read = 12; + return tinyframe_stopped; + case TINYFRAME_CONTROL_FINISH: + handle->state = tinyframe_done; + handle->bytes_read = 12; + return tinyframe_finished; + default: + trace("control type %d invalid, error", handle->control.type); + return tinyframe_error; + } + + // "escape" and length are not included in length, if not just type + // then we have control fields + if (handle->control.length > 4) { + handle->state = tinyframe_control_field; + handle->control_length = handle->control.length - 4; // - type length + handle->control_length_left = handle->control_length; + trace("control %d len %zu, next fields", handle->control.type, handle->control_length); + } else { + handle->state = tinyframe_frame; + trace("control %d len %zu, next frame", handle->control.type, handle->control_length); + } + + handle->bytes_read = 12; + return tinyframe_have_control; +} + +enum tinyframe_result tinyframe_read(struct tinyframe_reader* handle, const uint8_t* data, size_t len) +{ + assert(handle); + assert(data); + + switch (handle->state) { + case tinyframe_control: + return __read_control(handle, data, len); + + case tinyframe_control_field: + if (len < 8) { + trace("data len %zu < 8 for control field, need more", len); + return tinyframe_need_more; + } + handle->control_field.type = _need32(data); + switch (handle->control_field.type) { + case TINYFRAME_CONTROL_FIELD_CONTENT_TYPE: + break; + default: + trace("control field type %d invalid, error", handle->control_field.type); + return tinyframe_error; + } + + handle->control_field.length = _need32(data + 4); + if (handle->control_field.length > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) { + trace("control field length > max, error"); + return tinyframe_error; + } + if (len - 8 < handle->control_field.length) { + trace("data len %zu < control field length, need more", len - 8); + return tinyframe_need_more; + } + + // did we over read control frame length? + if (handle->control_length_left < handle->control_field.length + 8) { + trace("control field goes beyond control, error"); + return tinyframe_error; + } + handle->control_length_left -= handle->control_field.length + 8; + if (!handle->control_length_left) { + trace("no control fields left, next frame"); + handle->state = tinyframe_frame; + } + + handle->control_field.data = data + 8; + handle->bytes_read = 8 + handle->control_field.length; + trace("control field %d, data: %s", handle->control_field.type, printable_string(handle->control_field.data, handle->control_field.length)); + return tinyframe_have_control_field; + + case tinyframe_frame: + if (len < 4) { + trace("data len %zu < 4 for frame, need more", len); + return tinyframe_need_more; + } + handle->frame.length = _need32(data); + if (!handle->frame.length) { + handle->state = tinyframe_control; + return __read_control(handle, data, len); + } + + if (len - 4 < handle->frame.length) { + trace("data len %zu < frame length, need more", len - 4); + return tinyframe_need_more; + } + + handle->frame.data = data + 4; + handle->bytes_read = 4 + handle->frame.length; + trace("frame data [%zu]: %s...", handle->bytes_read, printable_string(data, handle->bytes_read > 20 ? 20 : handle->bytes_read)); + return tinyframe_have_frame; + + case tinyframe_done: + break; + } + + return tinyframe_error; +} + +enum tinyframe_result tinyframe_write_control(struct tinyframe_writer* handle, uint8_t* out, size_t len, uint32_t type, const struct tinyframe_control_field* fields, size_t num_fields) +{ + size_t out_len = 12; + size_t n; + uint8_t* outp; + + assert(handle); + assert(out); + assert(!num_fields || (num_fields && fields)); + + for (n = 0; n < num_fields; n++) { + switch (fields[n].type) { + case TINYFRAME_CONTROL_FIELD_CONTENT_TYPE: + break; + default: + trace("field %zu type %d invalid, error", n, fields[n].type); + return tinyframe_error; + } + if (fields[n].length > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) { + trace("field %zu length > max, error", n); + return tinyframe_error; + } + out_len += 8 + fields[n].length; + } + + if (len < out_len) { + trace("not enought space, need more"); + return tinyframe_need_more; + } + + _put32(out, 0); // "escape" + _put32(out + 4, out_len - 8); // length + // - 8 is because "escape" and length is not included in length + _put32(out + 8, type); // type + + outp = out + 12; + for (n = 0; n < num_fields; n++) { + _put32(outp, fields[n].type); + _put32(outp + 4, fields[n].length); + if (fields[n].length) { + assert(fields[n].data); + memcpy(outp + 8, fields[n].data, fields[n].length); + } + outp += 8 + fields[n].length; + } + + handle->bytes_wrote = out_len; + trace("control %u data: %s", type, printable_string(out, handle->bytes_wrote)); + return tinyframe_ok; +} + +enum tinyframe_result tinyframe_write_control_start(struct tinyframe_writer* handle, uint8_t* out, size_t len, const char* content_type, size_t content_type_len) +{ + assert(handle); + assert(out); + assert(content_type); + + if (content_type_len > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) { + trace("field length > max, error"); + return tinyframe_error; + } + if (len < 12 + 8 + content_type_len) { + trace("not enought space, need more"); + return tinyframe_need_more; + } + + _put32(out, 0); // "escape" + _put32(out + 4, 12 + 8 + content_type_len - 8); // length + // - 8 is because "escape" and length is not included in length + _put32(out + 8, TINYFRAME_CONTROL_START); // type + _put32(out + 12, TINYFRAME_CONTROL_FIELD_CONTENT_TYPE); // field type + _put32(out + 16, content_type_len); // field length + memcpy(out + 20, content_type, content_type_len); // field data + + handle->bytes_wrote = 12 + 8 + content_type_len; + trace("control start data: %s", printable_string(out, handle->bytes_wrote)); + return tinyframe_ok; +} + +enum tinyframe_result tinyframe_write_frame(struct tinyframe_writer* handle, uint8_t* out, size_t len, const uint8_t* data, uint32_t data_len) +{ + assert(handle); + assert(out); + assert(data); + + if (len < 4 + data_len) { + trace("not enought space, need more"); + return tinyframe_need_more; + } + + _put32(out, data_len); // length + memcpy(out + 4, data, data_len); // frame + + handle->bytes_wrote = 4 + data_len; + trace("frame data: %s...", printable_string(out, handle->bytes_wrote > 20 ? 20 : handle->bytes_wrote)); + return tinyframe_ok; +} + +enum tinyframe_result tinyframe_write_control_stop(struct tinyframe_writer* handle, uint8_t* out, size_t len) +{ + assert(handle); + assert(out); + + if (len < 12) { + trace("not enought space, need more"); + return tinyframe_need_more; + } + + _put32(out, 0); // "escape" + _put32(out + 4, 12 - 8); // length + // - 8 is because "escape" and length is not included in length + _put32(out + 8, TINYFRAME_CONTROL_STOP); // type + + handle->bytes_wrote = 12; + trace("control stop data: %s", printable_string(out, handle->bytes_wrote)); + return tinyframe_ok; +} + +void tinyframe_set_header(uint8_t* frame, uint32_t frame_length) +{ + assert(frame); + + _put32(frame, frame_length); +} + +/* + +frame: +- 32 length (if zero == control frame) +- data + +control frame: +- 32 bit "escape" (always zero) +- 32 bit frame length +- 32 bit control type + +control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE: +- 32 bit field type == FSTRM_CONTROL_FIELD_CONTENT_TYPE +- 32 bit content type length +- content type + +control start frame: +- <control frame> +- <control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE> + +control accept & ready frame: +- <control frame> +- <control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE> + ... + (additional control fields) + +control stop & finish: + nothing more + +*/ |