/* * TLV helpers are adapted from ESP homekit: * * * The MIT License (MIT) * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include #include "pair-tlv.h" #include "pair-internal.h" static int tlv_add_value_(pair_tlv_values_t *values, uint8_t type, uint8_t *value, size_t size) { pair_tlv_t *tlv = malloc(sizeof(pair_tlv_t)); if (!tlv) { return PAIR_TLV_ERROR_MEMORY; } tlv->type = type; tlv->size = size; tlv->value = value; tlv->next = NULL; if (!values->head) { values->head = tlv; } else { pair_tlv_t *t = values->head; while (t->next) { t = t->next; } t->next = tlv; } return 0; } pair_tlv_values_t * pair_tlv_new() { pair_tlv_values_t *values = malloc(sizeof(pair_tlv_values_t)); if (!values) return NULL; values->head = NULL; return values; } void pair_tlv_free(pair_tlv_values_t *values) { pair_tlv_t *t = values->head; while (t) { pair_tlv_t *t2 = t; t = t->next; if (t2->value) free(t2->value); free(t2); } free(values); } int pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size) { uint8_t *data = NULL; int ret; if (size) { data = malloc(size); if (!data) { return PAIR_TLV_ERROR_MEMORY; } memcpy(data, value, size); } ret = tlv_add_value_(values, type, data, size); if (ret < 0) free(data); return ret; } pair_tlv_t * pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type) { pair_tlv_t *t = values->head; while (t) { if (t->type == type) return t; t = t->next; } return NULL; } int pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size) { size_t required_size = 0; pair_tlv_t *t = values->head; while (t) { required_size += t->size + 2 * ((t->size + 254) / 255); t = t->next; } if (*size < required_size) { *size = required_size; return PAIR_TLV_ERROR_INSUFFICIENT_SIZE; } *size = required_size; t = values->head; while (t) { uint8_t *data = t->value; if (!t->size) { buffer[0] = t->type; buffer[1] = 0; buffer += 2; t = t->next; continue; } size_t remaining = t->size; while (remaining) { buffer[0] = t->type; size_t chunk_size = (remaining > 255) ? 255 : remaining; buffer[1] = chunk_size; memcpy(&buffer[2], data, chunk_size); remaining -= chunk_size; buffer += chunk_size + 2; data += chunk_size; } t = t->next; } return 0; } int pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values) { size_t i = 0; int ret; while (i < length) { uint8_t type = buffer[i]; size_t size = 0; uint8_t *data = NULL; // scan TLVs to accumulate total size of subsequent TLVs with same type (chunked data) size_t j = i; while (j < length && buffer[j] == type && buffer[j+1] == 255) { size_t chunk_size = buffer[j+1]; size += chunk_size; j += chunk_size + 2; } if (j < length && buffer[j] == type) { size_t chunk_size = buffer[j+1]; size += chunk_size; } // allocate memory to hold all pieces of chunked data and copy data there if (size != 0) { data = malloc(size); if (!data) return PAIR_TLV_ERROR_MEMORY; uint8_t *p = data; size_t remaining = size; while (remaining) { size_t chunk_size = buffer[i+1]; memcpy(p, &buffer[i+2], chunk_size); p += chunk_size; i += chunk_size + 2; remaining -= chunk_size; } } ret = tlv_add_value_(values, type, data, size); if (ret < 0) { free(data); return ret; } } return 0; } #ifdef DEBUG_PAIR void pair_tlv_debug(const pair_tlv_values_t *values) { printf("Received TLV values\n"); for (pair_tlv_t *t=values->head; t; t=t->next) { printf("Type %d value (%zu bytes): \n", t->type, t->size); hexdump("", t->value, t->size); } } #endif