/* * Author Jerry Lundström * 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 . */ #include "config.h" #include "tinyframe/tinyframe.h" #include #ifdef HAVE_ENDIAN_H #include #else #ifdef HAVE_SYS_ENDIAN_H #include #else #ifdef HAVE_MACHINE_ENDIAN_H #include #endif #endif #endif #include #ifdef TINYFRAME_TRACE #include #include 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 accept & ready frame: - - ... (additional control fields) control stop & finish: nothing more */