/* * Usurpataion --- clinet-server protocol implementation. * * Copyright (C) 2019 Ramūnas Mažeikis * * This program 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; version 2.1 * of the License. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /** * Common parts of protocol implementation. Handling of anything that actually * deals with connection descriptor has to be implemented by device and daemon * separately. */ #include #include #include #include #include "tlv.h" #include "net.h" #include "utils.h" struct tlv_header { enum tlv_type type; uint16_t size; }; /** * Returns tlv_size from packet */ uint16_t tlv_size(char *buf); /** * Returns tlv_type from packet. */ enum tlv_type tlv_get_type(char *buf); /* Changes to be made: * * Trust tlv size from raw data * * Don't fail on 0 length. In that case data points to NULL * * tlv_parser should be opaque. * * Make a constructor that takes a user-supplied buffer. */ int tlv_get(struct tlv_parser *parser, struct tlv *ret) { int retval = 0; size_t packet_size; size_t final_size; struct tlv *crr_tlv = parser->data + parser->offset; packet_size = tlv_size(parser->data); final_size = parser->offset + crr_tlv->length + sizeof(struct tlv_header); if (final_size >= packet_size) { return retval = E_TLV_OVERFLOW; } else { if (crr_tlv->length != 0) { ret->data = parser->data + parser->offset + sizeof(struct tlv_header); } else { ret->data = NULL; } ret->type = crr_tlv->type; ret->length = crr_tlv->length; parser->offset += sizeof(struct tlv_header) + ret->length; if (final_size == packet_size) { retval = END_OF_PACKET; } } return retval; } int tlv_parser_init(struct tlv_parser *parser, char *buf, struct tlv *ret) { /* Data points to the beggining of the buffer for size and * type acquisition purposes. * */ struct tlv_header *data = buf; /* It is known that the first tlv will be of the meta kind. * We can safely skip the header. * */ parser->data = buf + sizeof(struct tlv_header); ret->type = data->type; ret->length = data->size; } uint16_t tlv_size(char *buf) { struct tlv_header *data = buf; return htons(data->size); } enum tlv_type tlv_get_type(char *buf) { struct tlv_header *data = buf; return READ_ENUM(data->type); } size_t tlv_data_size(struct tlv_parser *parser) { size_t size; if (parser->offset + sizeof(enum tlv_type) + sizeof(size_t) >= parser->size) { size = 0; } else { memcpy(&size, parser->data + parser->offset + sizeof(enum tlv_type), sizeof(size_t)); } return size; } void tlv_destroy(struct tlv *t) { size_t i = 0; size_t tlv_count; struct tlv *arr; switch (t->type) { case REGURAL: case HEARTBEAT: case DISCOVERY: tlv_count = t->length / sizeof(struct tlv); arr = t->data; for (i = 0; i < tlv_count; i++) { tlv_destroy(&arr[i]); } default: free(t->data); t->length = 0; break; } } size_t tlv_raw_size(const struct tlv *t) { return sizeof(*t) + t->length; } int tlv_push_data(struct tlv *t, const char *data, size_t size) { int ret = 0; size_t final_size = tlv_raw_size(t) + size; if (final_size > TLV_SZ_MAX_RAW) { ret = E_TLV_OVERFLOW; } else { t->data = realloc(t->data, final_size); memcpy(t->data + t->length, data, size); t->length = final_size; } return ret; } void tlv_init(struct tlv *t, enum tlv_type type) { t->type = type; t->length = 0; t->data = NULL; } int tlv_push_tlv(struct tlv *t, const struct tlv *other) { int ret = 0; size_t other_size; size_t final_size; other_size = tlv_raw_size(other); final_size = tlv_raw_size(t) + other_size; if (final_size > TLV_SZ_MAX_RAW) { ret = E_TLV_OVERFLOW; } else { tlv_get_raw(other, t->data + t->length); t->length = final_size; } return ret; }