summaryrefslogtreecommitdiffstats
path: root/src/tinyframe.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tinyframe.c')
-rw-r--r--src/tinyframe.c403
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
+
+*/