/* Copyright (C) 2011 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include "libknot/consts.h" #include "libknot/common.h" #include "common/mempattern.h" #include "libknot/rrset.h" #include "libknot/rrset-dump.h" #include "common/descriptor.h" #include "libknot/util/debug.h" #include "libknot/util/utils.h" #include "libknot/packet/response.h" #include "libknot/util/wire.h" #include "libknot/dname.h" #include "libknot/rdata.h" static uint32_t rrset_rdata_offset(const knot_rrset_t *rrset, size_t pos) { if (rrset == NULL || rrset->rdata_indices == NULL || pos >= rrset->rdata_count || pos == 0) { return 0; } assert(rrset->rdata_count >= 2); return rrset->rdata_indices[pos - 1]; } uint8_t *rrset_rdata_pointer(const knot_rrset_t *rrset, size_t pos) { if (rrset == NULL || rrset->rdata == NULL || pos >= rrset->rdata_count) { return NULL; } return rrset->rdata + rrset_rdata_offset(rrset, pos); } static uint16_t rrset_rdata_naptr_bin_chunk_size(const knot_rrset_t *rrset, size_t pos) { if (rrset == NULL || pos >= rrset->rdata_count) { return 0; } size_t size = 0; uint8_t *rdata = rrset_rdata_pointer(rrset, pos); assert(rdata); /* Two shorts at the beginning. */ size += 4; /* 3 binary TXTs with length in the first byte. */ for (int i = 0; i < 3; i++) { size += *(rdata + size) + 1; } /* * Dname remaning, but we usually want to get to the DNAME, so * there's no need to include it in the returned size. */ return size; } void knot_rrset_rdata_dump(const knot_rrset_t *rrset, size_t rdata_pos) { dbg_rrset_exec_detail( dbg_rrset_detail(" ------- RDATA pos=%zu -------\n", rdata_pos); if (rrset->rdata_count == 0) { dbg_rrset_detail(" There are no rdata in this RRset!\n"); dbg_rrset_detail(" ------- RDATA -------\n"); return; } const rdata_descriptor_t *desc = get_rdata_descriptor(knot_rrset_type(rrset)); size_t offset = 0; for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { int item = desc->block_types[i]; const uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos); if (descriptor_item_is_dname(item)) { const knot_dname_t *dname = rdata + offset; char *name = knot_dname_to_str(dname); dbg_rrset_detail("block=%d: (%p) DNAME=%s\n", i, dname, name); free(name); offset += knot_dname_size(dname); } else if (descriptor_item_is_fixed(item)) { dbg_rrset_detail("block=%d Raw data (size=%d):\n", i, item); dbg_rrset_hex_detail((char *)(rdata + offset), item); offset += item; } else if (descriptor_item_is_remainder(item)) { dbg_rrset_detail("block=%d Remainder (size=%zu):\n", i, rrset_rdata_item_size(rrset, rdata_pos) - offset); dbg_rrset_hex_detail((char *)(rdata + offset), rrset_rdata_item_size(rrset, rdata_pos) - offset); } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); uint16_t naptr_chunk_size = rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos); dbg_rrset_detail("NAPTR, REGEXP block (size=%u):\n", naptr_chunk_size); dbg_rrset_hex_detail((char *)(rdata + offset), naptr_chunk_size); offset += naptr_chunk_size; } } ); } static size_t rrset_rdata_remainder_size(const knot_rrset_t *rrset, size_t offset, size_t pos) { size_t ret = rrset_rdata_item_size(rrset, pos) - offset; assert(ret <= rrset_rdata_size_total(rrset)); return ret; } int rrset_rdata_compare_one(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, size_t pos1, size_t pos2) { assert(rrset1 != NULL); assert(rrset2 != NULL); uint8_t *r1 = rrset_rdata_pointer(rrset1, pos1); uint8_t *r2 = rrset_rdata_pointer(rrset2, pos2); assert(rrset1->type == rrset2->type); const rdata_descriptor_t *desc = get_rdata_descriptor(rrset1->type); int cmp = 0; size_t offset = 0; // TODO: this can be much simpler: Get data for memcmp and sizes in ifs // compare only once for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { if (descriptor_item_is_dname(desc->block_types[i])) { const knot_dname_t *dname1 = r1 + offset; int size1 = knot_dname_size(dname1); const knot_dname_t *dname2 = r2 + offset; int size2 = knot_dname_size(dname2); cmp = memcmp(dname1, dname2, size1 <= size2 ? size1 : size2); if (cmp == 0 && size1 != size2) { cmp = size1 < size2 ? -1 : 1; } offset += knot_dname_size(dname1); } else if (descriptor_item_is_fixed(desc->block_types[i])) { cmp = memcmp(r1 + offset, r2 + offset, desc->block_types[i]); offset += desc->block_types[i]; } else if (descriptor_item_is_remainder(desc->block_types[i])) { size_t size1 = rrset_rdata_remainder_size(rrset1, offset, pos1); size_t size2 = rrset_rdata_remainder_size(rrset2, offset, pos2); cmp = memcmp(r1 + offset, r2 + offset, size1 <= size2 ? size1 : size2); if (cmp == 0 && size1 != size2) { cmp = size1 < size2 ? -1 : 1; } /* No need to move offset, this should be end anyway. */ assert(desc->block_types[i + 1] == KNOT_RDATA_WF_END); } else { assert(rrset1->type == KNOT_RRTYPE_NAPTR); uint16_t naptr_chunk_size1 = rrset_rdata_naptr_bin_chunk_size(rrset1, pos1); uint16_t naptr_chunk_size2 = rrset_rdata_naptr_bin_chunk_size(rrset2, pos2); cmp = memcmp(r1, r2, naptr_chunk_size1 <= naptr_chunk_size2 ? naptr_chunk_size1 : naptr_chunk_size2); if (cmp == 0 && naptr_chunk_size1 == naptr_chunk_size2) { cmp = naptr_chunk_size1 < naptr_chunk_size2 ? -1 : 1; } /* * It does not matter which one we assign. If the * offsets were different, then cmp != 0, if yes, * NAPTR DNAME will be on correct offset. */ offset += naptr_chunk_size1; } if (cmp != 0) { return cmp; } } assert(cmp == 0); return 0; } static int knot_rrset_header_to_wire(const knot_rrset_t *rrset, uint8_t **pos, size_t max_size, knot_compr_t *compr, size_t *size) { // Common size of items: type, class and ttl. const size_t type_cls_ttl_len = 2 * sizeof(uint16_t) + sizeof(uint32_t); // Rdata length item size. const size_t rrlen_len = sizeof(uint16_t); if (rrset->owner == NULL) { return KNOT_EMALF; } const uint8_t *owner; uint8_t owner_len; // Use compressed owner. if (compr) { owner = compr->owner.wire + compr->owner.pos; owner_len = compr->owner.size; // Use rrset owner. } else { owner = rrset->owner; owner_len = knot_dname_size(owner); } dbg_response("Max size: %zu, compressed owner: %s, owner size: %u\n", max_size, compr ? "yes" : "no", owner_len); // Check wire space for header. if (*size + owner_len + type_cls_ttl_len + rrlen_len > max_size) { dbg_rrset_detail("Header does not fit into wire.\n"); return KNOT_ESPACE; } // Write owner, type, class and ttl to wire. *pos += knot_dname_to_wire(*pos, owner, KNOT_DNAME_MAXLEN); dbg_rrset_detail(" Type: %u\n", rrset->type); knot_wire_write_u16(*pos, rrset->type); *pos += sizeof(uint16_t); dbg_rrset_detail(" Class: %u\n", rrset->rclass); knot_wire_write_u16(*pos, rrset->rclass); *pos += sizeof(uint16_t); dbg_rrset_detail(" TTL: %u\n", rrset->ttl); knot_wire_write_u32(*pos, rrset->ttl); *pos += sizeof(uint32_t); *size += owner_len + type_cls_ttl_len; return KNOT_EOK; } /* [code-review] Split to more functions, this one's too long. */ static int knot_rrset_rdata_to_wire_one(const knot_rrset_t *rrset, uint16_t rdata_pos, uint8_t **pos, size_t max_size, size_t *rr_size, knot_compr_t *compr) { assert(rrset); assert(pos); /* Put RR header to wire. */ size_t size = 0; int ret = knot_rrset_header_to_wire(rrset, pos, max_size, compr, &size); if (ret != KNOT_EOK) { dbg_response("Failed to convert RR header to wire (%s).\n", knot_strerror(ret)); return ret; } // save space for RDLENGTH uint8_t *rdlength_pos = *pos; *pos += 2; size += 2; if (compr) { compr->wire_pos += size; } /* Get pointer into RDATA array. */ uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos); assert(rdata); /* Offset into one RDATA array. */ size_t offset = 0; /* Actual RDLENGTH. */ uint16_t rdlength = 0; const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type); for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { int item = desc->block_types[i]; if (compr && descriptor_item_is_compr_dname(item)) { const knot_dname_t *dname = rdata + offset; int ret = knot_response_compress_dname(dname, compr, *pos, max_size - size - rdlength); if (ret < 0) { return KNOT_ESPACE; } assert(ret + size + rdlength <= max_size); dbg_response_exec_detail( char *name = knot_dname_to_str(dname); dbg_response_detail("Compressed dname=%s size: %d\n", name, ret); free(name); ); *pos += ret; rdlength += ret; offset += knot_dname_size(dname); compr->wire_pos += ret; } else if (descriptor_item_is_dname(item)) { const knot_dname_t *dname = rdata + offset; dbg_rrset_exec_detail( char *name = knot_dname_to_str(dname); dbg_rrset_detail("Saving this DNAME=%s\n", name); free(name); ); // save whole domain name size_t maxb = max_size - size - rdlength; int dname_size = knot_dname_to_wire(*pos, dname, maxb); if (dname_size < 0) return KNOT_ESPACE; dbg_rrset_detail("Uncompressed dname size: %d\n", dname_size); *pos += dname_size; rdlength += dname_size; offset += dname_size; if (compr) { compr->wire_pos += dname_size; } } else if (descriptor_item_is_fixed(item)) { dbg_rrset_detail("Saving static chunk, size=%d\n", item); /* Fixed length chunk. */ if (size + rdlength + item > max_size) { return KNOT_ESPACE; } memcpy(*pos, rdata + offset, item); *pos += item; rdlength += item; offset += item; if (compr) { compr->wire_pos += item; } } else if (descriptor_item_is_remainder(item)) { /* Check that the remainder fits to stream. */ size_t remainder_size = rrset_rdata_remainder_size(rrset, offset, rdata_pos); dbg_rrset_detail("Saving remaining chunk, size=%zu, " "size with remainder=%zu\n", remainder_size, size + rdlength + remainder_size); if (size + rdlength + remainder_size > max_size) { dbg_rrset("rr: to_wire: Remainder does not fit " "to wire.\n"); return KNOT_ESPACE; } memcpy(*pos, rdata + offset, remainder_size); *pos += remainder_size; rdlength += remainder_size; offset += remainder_size; if (compr) { compr->wire_pos += remainder_size; } } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); /* Store the binary chunk. */ uint16_t chunk_size = rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos); if (size + rdlength + chunk_size > max_size) { dbg_rrset("rr: to_wire: NAPTR chunk does not " "fit to wire.\n"); return KNOT_ESPACE; } memcpy(*pos, rdata + offset, chunk_size); *pos += chunk_size; rdlength += chunk_size; offset += chunk_size; if (compr) { compr->wire_pos += chunk_size; } } } knot_wire_write_u16(rdlength_pos, rdlength); size += rdlength; *rr_size = size; assert(size <= max_size); return KNOT_EOK; } static int knot_rrset_to_wire_aux(const knot_rrset_t *rrset, uint8_t **pos, size_t max_size, compression_param_t *comp) { uint8_t wf_owner[256]; size_t size = 0; assert(rrset != NULL); assert(rrset->owner != NULL); assert(pos != NULL); assert(*pos != NULL); dbg_rrset_detail("Max size: %zu, owner: %p, owner size: %d\n", max_size, rrset, knot_dname_size(rrset->owner)); knot_compr_t compr_info; if (comp) { dbg_response_detail("Compressing RR owner: %s.\n", rrset->owner); compr_info.table = comp->compressed_dnames; compr_info.wire = comp->wire; compr_info.wire_pos = comp->wire_pos; int ret = knot_response_compress_dname(rrset->owner, &compr_info, wf_owner, max_size); if (ret < 0) { return KNOT_ESPACE; } compr_info.owner.pos = 0; compr_info.owner.wire = wf_owner; compr_info.owner.size = ret; dbg_response_detail("Compressed owner has size=%d\n", compr_info.owner.size); } dbg_rrset_detail("Max size: %zu, size: %zu\n", max_size, size); // No RDATA, just save header and 0 RDLENGTH. if (rrset->rdata_count == 0) { size_t header_size = 0; int ret = knot_rrset_header_to_wire(rrset, pos, max_size, comp ? &compr_info : NULL, &header_size); if (ret != KNOT_EOK) { return ret; } // Save zero rdata length. knot_wire_write_u16(*pos, 0); *pos += sizeof(uint16_t); header_size += sizeof(uint16_t); return header_size; } // Save rrset records. for (uint16_t i = 0; i < rrset->rdata_count; ++i) { dbg_rrset_detail("rrset: to_wire: Current max_size=%zu\n", max_size); size_t rr_size = 0; int ret = knot_rrset_rdata_to_wire_one(rrset, i, pos, max_size, &rr_size, comp ? &compr_info : NULL); if (ret != KNOT_EOK) { dbg_rrset("rrset: to_wire: Cannot convert RR. " "Reason: %s.\n", knot_strerror(ret)); return ret; } dbg_rrset_detail("Converted RR nr=%d, size=%zu\n", i, rr_size); /* Change size of whole RRSet. */ size += rr_size; /* Change max size. */ max_size -= rr_size; } dbg_rrset_detail("Max size: %zu, size: %zu\n", max_size, size); return size; } static int knot_rrset_rdata_store_binary(uint8_t *rdata, size_t *offset, size_t packet_offset, const uint8_t *wire, size_t *pos, size_t rdlength, size_t size) { assert(rdata); assert(wire); /* Check that size is OK. */ if ((*pos - packet_offset) + size > rdlength) { dbg_rrset("rrset: rdata_store_binary: Read of size=%zu on " "position %zu exceeded RDLENGTH by %zu octets.\n", size, *pos, ((*pos - packet_offset) + size) - rdlength); return KNOT_ESPACE; } /* Store actual data. */ memcpy(rdata + *offset, wire + *pos, size); *offset += size; *pos += size; return KNOT_EOK; } static size_t rrset_binary_size_one(const knot_rrset_t *rrset, size_t rdata_pos) { const rdata_descriptor_t *desc = get_rdata_descriptor(knot_rrset_type(rrset)); size_t offset = 0; size_t size = 0; for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { int item = desc->block_types[i]; uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos); if (descriptor_item_is_dname(item)) { const knot_dname_t *dname = rdata + offset; int dname_size = knot_dname_size(dname); offset += dname_size; size += dname_size; } else if (descriptor_item_is_fixed(item)) { offset += item; size += item; } else if (descriptor_item_is_remainder(item)) { size += rrset_rdata_item_size(rrset, rdata_pos) - offset; } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); uint16_t naptr_chunk_size = rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos); /* * Regular expressions in NAPTR are TXT's, so they * can be upto 64k long. */ size += naptr_chunk_size + 2; offset += naptr_chunk_size; } } return size; } static void rrset_serialize_rr(const knot_rrset_t *rrset, size_t rdata_pos, uint8_t *stream, size_t *size) { const rdata_descriptor_t *desc = get_rdata_descriptor(knot_rrset_type(rrset)); size_t offset = 0; *size = 0; for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { int item = desc->block_types[i]; uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos); assert(rdata); if (descriptor_item_is_dname(item)) { const knot_dname_t *dname = rdata + offset; int dname_len = knot_dname_to_wire(stream + *size, dname, KNOT_DNAME_MAXLEN); *size += dname_len; offset += dname_len; } else if (descriptor_item_is_fixed(item)) { memcpy(stream + *size, rdata + offset, item); offset += item; *size += item; } else if (descriptor_item_is_remainder(item)) { uint16_t remainder_size = rrset_rdata_item_size(rrset, rdata_pos) - offset; memcpy(stream + *size, rdata + offset, remainder_size); *size += remainder_size; } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); /* Copy static chunk. */ uint16_t naptr_chunk_size = rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos); /* We need a length. */ memcpy(stream + *size, &naptr_chunk_size, sizeof(uint16_t)); *size += sizeof(uint16_t); /* Write data. */ memcpy(stream + *size, rdata + offset, naptr_chunk_size); } } dbg_rrset_detail("RR nr=%zu serialized, size=%zu\n", rdata_pos, *size); } static int rrset_deserialize_rr(knot_rrset_t *rrset, size_t rdata_pos, uint8_t *stream, uint32_t rdata_size, size_t *read) { const rdata_descriptor_t *desc = get_rdata_descriptor(knot_rrset_type(rrset)); size_t stream_offset = 0; size_t rdata_offset = 0; for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { int item = desc->block_types[i]; uint8_t *rdata = rrset_rdata_pointer(rrset, rdata_pos); if (descriptor_item_is_dname(item)) { const knot_dname_t *dname = stream + stream_offset; int dname_size = knot_dname_size(dname); memcpy(rdata + rdata_offset, dname, dname_size); stream_offset += dname_size; rdata_offset += dname_size; } else if (descriptor_item_is_fixed(item)) { memcpy(rdata + rdata_offset, stream + stream_offset, item); rdata_offset += item; stream_offset += item; } else if (descriptor_item_is_remainder(item)) { size_t remainder_size = rdata_size - stream_offset; memcpy(rdata + rdata_offset, stream + stream_offset, remainder_size); stream_offset += remainder_size; } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); /* Read size. */ uint16_t naptr_chunk_size; memcpy(&naptr_chunk_size, stream + stream_offset, sizeof(uint16_t)); stream_offset += sizeof(uint16_t); memcpy(rdata + rdata_offset, stream + stream_offset, naptr_chunk_size); stream_offset += naptr_chunk_size; rdata_offset += rdata_offset; } } *read = stream_offset; return KNOT_EOK; } int knot_rrset_remove_rdata_pos(knot_rrset_t *rrset, size_t pos) { if (rrset == NULL || pos >= rrset->rdata_count) { return KNOT_EINVAL; } /* Reorganize the actual RDATA array. */ uint8_t *rdata_to_remove = rrset_rdata_pointer(rrset, pos); dbg_rrset_detail("rr: remove_rdata_pos: Removing data=%p on " "position=%zu\n", rdata_to_remove, pos); assert(rdata_to_remove); if (pos != rrset->rdata_count - 1) { /* Not the last item in array - we need to move the data. */ uint8_t *next_rdata = rrset_rdata_pointer(rrset, pos + 1); assert(next_rdata); size_t remainder_size = rrset_rdata_size_total(rrset) - rrset_rdata_offset(rrset, pos + 1); /* * Copy the all following RR data to where this item is. * No need to worry about exceeding allocated space now. */ memmove(rdata_to_remove, next_rdata, remainder_size); } uint32_t removed_size = rrset_rdata_item_size(rrset, pos); uint32_t new_size = rrset_rdata_size_total(rrset) - removed_size; if (new_size == 0) { assert(rrset->rdata_count == 1); free(rrset->rdata); rrset->rdata = NULL; free(rrset->rdata_indices); rrset->rdata_indices = NULL; } else { /* Resize RDATA array, the change might be worth reallocation.*/ rrset->rdata = xrealloc(rrset->rdata, new_size); /* * Handle RDATA indices. All indices larger than the removed one * have to be adjusted. Last index will be changed later. */ for (uint16_t i = pos; i < rrset->rdata_count - 1; ++i) { rrset->rdata_indices[i] = rrset->rdata_indices[i + 1] - removed_size; } /* Save size of the whole RDATA array. Note: probably not needed! */ rrset->rdata_indices[rrset->rdata_count - 2] = new_size; /* Resize indices, not always needed, but we'll do it to be proper. */ rrset->rdata_indices = xrealloc(rrset->rdata_indices, (rrset->rdata_count - 1) * sizeof(uint32_t)); } --rrset->rdata_count; dbg_rrset_detail("rrset: remove rdata pos: RR after removal:\n"); knot_rrset_dump(rrset); return KNOT_EOK; } uint32_t rrset_rdata_size_total(const knot_rrset_t *rrset) { if (rrset == NULL || rrset->rdata_indices == NULL || rrset->rdata_count == 0) { return 0; } // Last index denotes end of all RRs. return (rrset->rdata_indices[rrset->rdata_count - 1]); } knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type, uint16_t rclass, uint32_t ttl) { knot_rrset_t *ret = malloc(sizeof(knot_rrset_t)); if (ret == NULL) { ERR_ALLOC_FAILED; return NULL; } ret->rdata = NULL; ret->rdata_count = 0; ret->rdata_indices = NULL; ret->owner = owner; ret->type = type; ret->rclass = rclass; ret->ttl = ttl; ret->rrsigs = NULL; return ret; } knot_rrset_t *knot_rrset_new_from(const knot_rrset_t *tpl) { if (!tpl) { return NULL; } knot_dname_t *owner = knot_dname_copy(tpl->owner); if (!owner) { return NULL; } return knot_rrset_new(owner, tpl->type, tpl->rclass, tpl->ttl); } int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *rdata, uint16_t size) { if (rrset == NULL || rdata == NULL || size == 0) { return KNOT_EINVAL; } uint8_t *p = knot_rrset_create_rdata(rrset, size); memcpy(p, rdata, size); return KNOT_EOK; } static uint8_t* knot_rrset_create_rdata_at_pos(knot_rrset_t *rrset, size_t pos, uint16_t size) { if (rrset == NULL || pos > rrset->rdata_count) { return NULL; } if (pos == rrset->rdata_count) { return knot_rrset_create_rdata(rrset, size); } uint32_t total_size = rrset_rdata_size_total(rrset); // Realloc actual data. void *tmp = realloc(rrset->rdata, total_size + size); if (tmp) { rrset->rdata = tmp; } else { ERR_ALLOC_FAILED; return NULL; } /* * Move already existing data to from position we want to add to. * But only if we don't want to add new item after last item. */ uint8_t *old_pointer = rrset_rdata_pointer(rrset, pos); assert(old_pointer); memmove(old_pointer + size, old_pointer, rrset_rdata_size_total(rrset) - rrset_rdata_offset(rrset, pos)); // Realloc indices. We will allocate exact size to save space. tmp = realloc(rrset->rdata_indices, (rrset->rdata_count + 1) * sizeof(uint32_t)); if (tmp) { rrset->rdata_indices = tmp; } else { ERR_ALLOC_FAILED; return NULL; } // Move indices. memmove(rrset->rdata_indices + pos + 1, rrset->rdata_indices + pos, (rrset->rdata_count - pos) * sizeof(uint32_t)); // Init new index rrset->rdata_indices[pos] = pos ? rrset->rdata_indices[pos - 1] : 0; ++rrset->rdata_count; // Adjust all following items for (uint16_t i = pos; i < rrset->rdata_count; ++i) { rrset->rdata_indices[i] += size; } // Return pointer from correct position (now contains bogus data). return old_pointer; } static int knot_rrset_add_rdata_at_pos(knot_rrset_t *rrset, size_t pos, const uint8_t *rdata, uint16_t size) { if (rrset == NULL || rdata == NULL) { return KNOT_EINVAL; } uint8_t *p = knot_rrset_create_rdata_at_pos(rrset, pos, size); if (p == NULL) { return KNOT_ERROR; } memcpy(p, rdata, size); return KNOT_EOK; } /*----------------------------------------------------------------------------*/ uint8_t* knot_rrset_create_rdata(knot_rrset_t *rrset, uint16_t size) { if (rrset == NULL) { return NULL; } uint32_t total_size = rrset_rdata_size_total(rrset); /* Realloc indices. We will allocate exact size to save space. */ rrset->rdata_indices = xrealloc(rrset->rdata_indices, (rrset->rdata_count + 1) * sizeof(uint32_t)); /* Realloc actual data. */ rrset->rdata = xrealloc(rrset->rdata, total_size + size); /* Pointer to new memory. */ uint8_t *dst = rrset->rdata + total_size; /* Update indices. */ if (rrset->rdata_count == 0) { rrset->rdata_indices[0] = size; } else { rrset->rdata_indices[rrset->rdata_count - 1] = total_size; rrset->rdata_indices[rrset->rdata_count] = total_size + size; } ++rrset->rdata_count; return dst; } /*----------------------------------------------------------------------------*/ uint16_t rrset_rdata_item_size(const knot_rrset_t *rrset, size_t pos) { if (rrset == NULL || rrset->rdata_indices == NULL || rrset->rdata_count == 0) { //invalid case return 0; } if (rrset->rdata_count == 1 || pos == 0) { //first RR or only one RR (either size of first RR or total size) return rrset->rdata_indices[0]; } assert(rrset->rdata_count >= 2 && pos != 0); return rrset->rdata_indices[pos] - rrset->rdata_indices[pos - 1]; } int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs) { if (rrset == NULL) { return KNOT_EINVAL; } rrset->rrsigs = rrsigs; return KNOT_EOK; } int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs, knot_rrset_dupl_handling_t dupl) { if (rrset == NULL || rrsigs == NULL || !knot_dname_is_equal(rrset->owner, rrsigs->owner)) { return KNOT_EINVAL; } int rc; if (rrset->rrsigs != NULL) { if (dupl == KNOT_RRSET_DUPL_MERGE) { int merged, deleted_rrs; rc = knot_rrset_merge_sort(rrset->rrsigs, rrsigs, &merged, &deleted_rrs); if (rc != KNOT_EOK) { return rc; } else if (merged || deleted_rrs) { return 1; } else { return 0; } } else if (dupl == KNOT_RRSET_DUPL_SKIP) { return 2; } else if (dupl == KNOT_RRSET_DUPL_REPLACE) { rrset->rrsigs = rrsigs; } } else { if (rrset->ttl != rrsigs->ttl) { rrsigs->ttl = rrset->ttl; } rrset->rrsigs = rrsigs; } return KNOT_EOK; } const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset) { return rrset->owner; } knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset) { return rrset->owner; } int knot_rrset_set_owner(knot_rrset_t *rrset, const knot_dname_t *owner) { if (rrset == NULL) { return KNOT_EINVAL; } /* Copy the new owner. */ knot_dname_t *owner_copy = NULL; if (owner) { owner_copy = knot_dname_copy(owner); if (owner_copy == NULL) { return KNOT_ENOMEM; } } /* Free old owner and assign. */ knot_dname_free(&rrset->owner); rrset->owner = owner_copy; return KNOT_EOK; } void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl) { if (rrset) { rrset->ttl = ttl; } } uint16_t knot_rrset_type(const knot_rrset_t *rrset) { return rrset->type; } uint16_t knot_rrset_class(const knot_rrset_t *rrset) { return rrset->rclass; } uint32_t knot_rrset_ttl(const knot_rrset_t *rrset) { return rrset->ttl; } uint8_t *knot_rrset_get_rdata(const knot_rrset_t *rrset, size_t rdata_pos) { return rrset_rdata_pointer(rrset, rdata_pos); } uint16_t knot_rrset_rdata_rr_count(const knot_rrset_t *rrset) { if (rrset != NULL) { return rrset->rdata_count; } else { return 0; } } const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset) { if (rrset == NULL) { return NULL; } else { return rrset->rrsigs; } } knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset) { if (rrset == NULL) { return NULL; } else { return rrset->rrsigs; } } /*! * \brief Compare two RR sets, order of RDATA is not significant. */ int knot_rrset_rdata_equal(const knot_rrset_t *r1, const knot_rrset_t *r2) { if (r1 == NULL || r2 == NULL || (r1->type != r2->type) || r1->rdata_count == 0 || r2->rdata_count == 0) { return KNOT_EINVAL; } if (r1->rdata_count != r2->rdata_count) { return 0; } for (uint16_t i = 0; i < r1->rdata_count; i++) { bool found = false; for (uint16_t j = 0; j < r2->rdata_count; j++) { if (rrset_rdata_compare_one(r1, r2, i, j) == 0) { found = true; break; } } if (!found) { return 0; } } return 1; } int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, size_t max_size, uint16_t *rr_count, void *data) { if (rrset == NULL || wire == NULL || size == NULL || rr_count == NULL) { return KNOT_EINVAL; } compression_param_t *comp_data = (compression_param_t *)data; uint8_t *pos = wire; dbg_rrset_exec_detail( dbg_rrset_detail("Converting following RRSet:\n"); knot_rrset_dump(rrset); ); int ret = knot_rrset_to_wire_aux(rrset, &pos, max_size, comp_data); if (ret < 0) { // some RR didn't fit in, so no RRs should be used // TODO: remove last entries from compression table dbg_rrset_verb("Some RR didn't fit in.\n"); return KNOT_ESPACE; } // Check if the whole RRSet fit into packet. assert(ret <= max_size); assert(pos - wire == ret); *size = ret; dbg_rrset_detail("Size after: %zu\n", *size); // If the rrset is empty set record counter to 1. *rr_count = rrset->rdata_count > 0 ? rrset->rdata_count : 1; return KNOT_EOK; } int knot_rrset_to_wire_one(const knot_rrset_t *rrset, uint16_t rr_number, uint8_t *wire, size_t max_size, size_t *outsize, void *compr) { if (!rrset || !wire || !outsize) return KNOT_EINVAL; uint8_t *pos = wire; return knot_rrset_rdata_to_wire_one(rrset, rr_number, &pos, max_size, outsize, (knot_compr_t *)compr); } int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset, const uint8_t *wire, size_t *pos, size_t total_size, size_t rdlength) { if (rrset == NULL || wire == NULL || pos == NULL) { return KNOT_EINVAL; } if (rdlength == 0) { /* Nothing to parse, APLs can have 0 RDLENGTH, but only APLs. */ if (rrset->type == KNOT_RRTYPE_APL) { // Add empty RDATA return knot_rrset_create_rdata(rrset, 0) == NULL ? KNOT_ENOMEM : KNOT_EOK; } else { return KNOT_EOK; } } dbg_rrset_detail("rr: parse_rdata_wire: Parsing RDATA of size=%zu," " wire_size=%zu, type=%d.\n", rdlength, total_size, rrset->type); const rdata_descriptor_t *desc = get_rdata_descriptor(rrset->type); /* Check for obsolete record. */ if (desc->type_name == NULL) { desc = get_obsolete_rdata_descriptor(rrset->type); } /*! \todo This estimate is very rough - just to have enough space for * possible unpacked dname. Should be later replaced by exact * size counting. */ uint8_t rdata_buffer[rdlength + KNOT_DNAME_MAXLEN]; memset(rdata_buffer, 0, rdlength + KNOT_DNAME_MAXLEN); size_t offset = 0; // offset within in-memory RDATA size_t parsed = 0; // actual count of parsed octets const size_t packet_offset = *pos; /*! \todo [RRSet refactor] * This could be A LOT simpler - copy it as a whole, * unpack dnames and just do some format checks if necessary. * But it's questionable, if copying the memory when unpacking * dnames, wouldn't be too expensive. */ for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END && parsed < rdlength; ++i) { const int item = desc->block_types[i]; if (descriptor_item_is_dname(item)) { int wire_size = knot_dname_wire_check(wire + *pos, wire + *pos + rdlength, wire); if (wire_size <= 0) { return KNOT_EMALF; } int unpacked_size = knot_dname_unpack( rdata_buffer + offset, wire + *pos, KNOT_DNAME_MAXLEN, wire); if (unpacked_size <= 0) { return KNOT_EMALF; } parsed += wire_size; dbg_rrset_exec_detail( dbg_rrset_detail("rr: parse_rdata_wire: Parsed DNAME, " "length=%d.\n", wire_size); char *name = knot_dname_to_str(rdata_buffer + offset); dbg_rrset_detail("rr: parse_rdata_wire: Parsed " "DNAME=%s\n", name); free(name); ); *pos += wire_size; offset += unpacked_size; } else if (descriptor_item_is_fixed(item)) { dbg_rrset_detail("rr: parse_rdata_wire: Saving static " "chunk of size=%u\n", item); int ret = knot_rrset_rdata_store_binary(rdata_buffer, &offset, packet_offset, wire, pos, rdlength, item); if (ret != KNOT_EOK) { dbg_rrset("rrset: rdata_from_wire: " "Cannot store fixed RDATA chunk. " "Reason: %s.\n", knot_strerror(ret)); return ret; } parsed += item; } else if (descriptor_item_is_remainder(item)) { /* Item size has to be calculated. */ size_t remainder_size = rdlength - parsed; dbg_rrset_detail("rr: parse_rdata_wire: Saving remaining " "chunk of size=%zu\n", remainder_size); int ret = knot_rrset_rdata_store_binary(rdata_buffer, &offset, packet_offset, wire, pos, rdlength, remainder_size); if (ret != KNOT_EOK) { dbg_rrset("rrset: rdata_from_wire: " "Cannot store RDATA remainder of " "size=%zu, RDLENGTH=%zu. " "Reason: %s.\n", remainder_size, rdlength, knot_strerror(ret)); return ret; } parsed += remainder_size; } else { assert(rrset->type == KNOT_RRTYPE_NAPTR); /* Read fixed part - 2 shorts. */ const size_t naptr_fixed_part_size = 4; int ret = knot_rrset_rdata_store_binary(rdata_buffer, &offset, packet_offset, wire, pos, rdlength, naptr_fixed_part_size); if (ret != KNOT_EOK) { dbg_rrset("rrset: rdata_from_wire: " "Cannot store NAPTR fixed part. " "Reason: %s.\n", knot_strerror(ret)); return ret; } parsed += naptr_fixed_part_size; for (int j = 0; j < 3; ++j) { /* Read sizes of TXT's - one byte. */ uint8_t txt_size = *(wire + (*pos)) + 1; dbg_rrset_detail("rrset: rdata_from_wire: " "Read TXT nr=%d size=%d\n", j, txt_size); int ret = knot_rrset_rdata_store_binary(rdata_buffer, &offset, packet_offset, wire, pos, rdlength, txt_size); if (ret != KNOT_EOK) { dbg_rrset("rrset: rdata_from_wire: " "Cannot store NAPTR TXTs. " "Reason: %s.\n", knot_strerror(ret)); return ret; } parsed += txt_size; } } } uint8_t *rdata = knot_rrset_create_rdata(rrset, offset); if (rdata == NULL) { return KNOT_ENOMEM; } memcpy(rdata, rdata_buffer, offset); return KNOT_EOK; } int knot_rrset_equal(const knot_rrset_t *r1, const knot_rrset_t *r2, knot_rrset_compare_type_t cmp) { if (cmp == KNOT_RRSET_COMPARE_PTR) { return r1 == r2; } if (!knot_dname_is_equal(r1->owner, r2->owner)) return false; if (r1->rclass != r2->rclass || r1->type != r2->type) return false; if (cmp == KNOT_RRSET_COMPARE_WHOLE) return knot_rrset_rdata_equal(r1, r2); return true; } int knot_rrset_deep_copy_no_sig(const knot_rrset_t *from, knot_rrset_t **to) { if (from == NULL || to == NULL) { return KNOT_EINVAL; } dbg_rrset_detail("rr: deep_copy: Copying RRs of type %d\n", from->type); *to = xmalloc(sizeof(knot_rrset_t)); (*to)->owner = knot_dname_copy(from->owner); if ((*to)->owner == NULL) { free(*to); *to = NULL; return KNOT_ENOMEM; } (*to)->rclass = from->rclass; (*to)->ttl = from->ttl; (*to)->type = from->type; (*to)->rdata_count = from->rdata_count; (*to)->rrsigs = NULL; /* Just copy arrays - actual data + indices. */ (*to)->rdata = xmalloc(rrset_rdata_size_total(from)); memcpy((*to)->rdata, from->rdata, rrset_rdata_size_total(from)); (*to)->rdata_indices = xmalloc(sizeof(uint32_t) * from->rdata_count); memcpy((*to)->rdata_indices, from->rdata_indices, sizeof(uint32_t) * from->rdata_count); return KNOT_EOK; } int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to) { int result = knot_rrset_deep_copy_no_sig(from, to); if (result == KNOT_EOK && from->rrsigs != NULL) { result = knot_rrset_deep_copy_no_sig(from->rrsigs, &(*to)->rrsigs); if (result != KNOT_EOK) { knot_rrset_deep_free(to, 1); } } return result; } /*----------------------------------------------------------------------------*/ int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to) { if (!from || !to) { return KNOT_EINVAL; } knot_rrset_t *result = (knot_rrset_t *)malloc(sizeof(knot_rrset_t)); if (!result) { return KNOT_ENOMEM; } memcpy(result, from, sizeof(knot_rrset_t)); result->owner = knot_dname_copy(result->owner); if (!result->owner) { free(result); return KNOT_ENOMEM; } *to = result; return KNOT_EOK; } /*----------------------------------------------------------------------------*/ void knot_rrset_rotate(knot_rrset_t *rrset) { /*! \todo Maybe implement properly one day. */ //rrset->rdata = rrset->rdata->next; } /*----------------------------------------------------------------------------*/ void knot_rrset_free(knot_rrset_t **rrset) { if (rrset == NULL || *rrset == NULL) { return; } knot_dname_free(&(*rrset)->owner); free(*rrset); *rrset = NULL; } static void rrset_deep_free_content(knot_rrset_t *rrset, int free_owner) { assert(rrset); if (rrset->rrsigs) { assert(rrset->rrsigs->rrsigs == NULL); rrset_deep_free_content(rrset->rrsigs, free_owner); free(rrset->rrsigs); } free(rrset->rdata); free(rrset->rdata_indices); if (free_owner) { knot_dname_free(&rrset->owner); } } void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner) { /*! \bug The number of different frees in rrset is too damn high! */ if (rrset == NULL || *rrset == NULL) { return; } rrset_deep_free_content(*rrset, free_owner); free(*rrset); *rrset = NULL; } void knot_rrset_deep_free_no_sig(knot_rrset_t **rrset, int free_owner) { if (rrset == NULL || *rrset == NULL) { return; } free((*rrset)->rdata); free((*rrset)->rdata_indices); if (free_owner) { knot_dname_free(&(*rrset)->owner); } free(*rrset); *rrset = NULL; } static int knot_rrset_add_rr_n(knot_rrset_t *rrset, const knot_rrset_t *rr, size_t pos) { if (rrset == NULL || rr == NULL) { return KNOT_EINVAL; } if (!knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) { // Adding to a different header return KNOT_EINVAL; } uint8_t *new_rdata = knot_rrset_create_rdata(rrset, rrset_rdata_item_size(rr, pos)); if (new_rdata == NULL) { return KNOT_ERROR; } memcpy(new_rdata, rrset_rdata_pointer(rr, pos), rrset_rdata_item_size(rr, pos)); return KNOT_EOK; } int knot_rrset_merge(knot_rrset_t *rrset1, const knot_rrset_t *rrset2) { for (uint16_t i = 0; i < rrset2->rdata_count; ++i) { int ret = knot_rrset_add_rr_n(rrset1, rrset2, i); if (ret != KNOT_EOK) { return ret; } } return KNOT_EOK; } static int knot_rrset_add_rr_sort_n(knot_rrset_t *rrset, const knot_rrset_t *rr, int *merged, int *deleted, size_t pos) { if (rrset == NULL || rr == NULL) { dbg_rrset("rrset: add_rr_sort: NULL arguments."); return KNOT_EINVAL; } dbg_rrset_exec_detail( char *name = knot_dname_to_str(rrset->owner); dbg_rrset_detail("rrset: add_rr_sort: Merging %s.\n", name); free(name); ); if ((!knot_dname_is_equal(rrset->owner, rr->owner)) || rrset->rclass != rr->rclass || rrset->type != rr->type) { dbg_rrset("rrset: add_rr_sort: Trying to merge " "different RRs.\n"); return KNOT_EINVAL; } int found = 0; int duplicated = 0; // Compare RR with all RRs in the first RRSet. size_t insert_to = 0; for (uint16_t j = 0; j < rrset->rdata_count && (!duplicated && !found); ++j) { int cmp = rrset_rdata_compare_one(rrset, rr, j, pos); if (cmp == 0) { // Duplication - no need to merge this RR duplicated = 1; } else if (cmp > 0) { // Found position to insert found = 1; } else { // Not yet - it might be next position insert_to = j + 1; } } if (!duplicated) { *merged += 1; // = need to shallow free rrset2 // Insert RR to RRSet int ret = knot_rrset_add_rdata_at_pos(rrset, insert_to, rrset_rdata_pointer(rr, pos), rrset_rdata_item_size(rr, pos)); if (ret != KNOT_EOK) { dbg_rrset("rrset: add_rr: Could not " "add RDATA to RRSet. (%s)\n", knot_strerror(ret)); return ret; } } else { assert(!found); *deleted += 1; // = need to shallow free rr } return KNOT_EOK; } int knot_rrset_merge_sort(knot_rrset_t *rrset1, const knot_rrset_t *rrset2, int *merged_rrs, int *deleted_rrs) { int result = KNOT_EOK; int merged = 0; int deleted = 0; for (uint16_t i = 0; i < rrset2->rdata_count; ++i) { result = knot_rrset_add_rr_sort_n(rrset1, rrset2, &merged, &deleted, i); if (result != KNOT_EOK) { break; } } if (merged_rrs) { *merged_rrs = merged; } if (deleted_rrs) { *deleted_rrs = deleted; } return result; } /*! * \todo Not optimal, rewrite! */ int knot_rrset_sort_rdata(knot_rrset_t *rrset) { if (!rrset) { return KNOT_EINVAL; } // 1. create temporary rrset // 2. sort-merge given rrset into temporary rrset // 3. swap the contents, free the temporary knot_rrset_t *sorted = knot_rrset_new(rrset->owner, rrset->type, rrset->rclass, rrset->ttl); if (!sorted) { return KNOT_ENOMEM; } int result = knot_rrset_merge_sort(sorted, rrset, NULL, NULL); if (result != KNOT_EOK) { knot_rrset_deep_free(&sorted, 1); return result; } rrset_deep_free_content(rrset, 0); *rrset = *sorted; free(sorted); return KNOT_EOK; } bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr) { assert(rr != NULL); /* Is NSEC3 or non-empty RRSIG covering NSEC3. */ return ((knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3) || (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG && knot_rdata_rrsig_type_covered(rr, 0) == KNOT_RRTYPE_NSEC3)); } /*----------------------------------------------------------------------------*/ void knot_rrset_dump(const knot_rrset_t *rrset) { dbg_rrset_exec_detail( if (rrset == NULL) { return; } dbg_rrset_detail(" ------- RRSET -------\n"); char *name = knot_dname_to_str(rrset->owner); dbg_rrset_detail(" owner: %s\n", name); free(name); dbg_rrset_detail(" type: %u\n", rrset->type); dbg_rrset_detail(" class: %d\n", rrset->rclass); dbg_rrset_detail(" ttl: %d\n", rrset->ttl); dbg_rrset_detail(" RDATA count: %d\n", rrset->rdata_count); dbg_rrset_detail(" RRSIGs:\n"); if (rrset->rrsigs != NULL) { knot_rrset_dump(rrset->rrsigs); } else { dbg_rrset_detail(" none\n"); } dbg_rrset_detail("RDATA indices (total=%d):\n", rrset_rdata_size_total(rrset)); for (uint16_t i = 0; i < rrset->rdata_count; i++) { dbg_rrset_detail("%d=%d\n", i, rrset_rdata_offset(rrset, i)); } if (knot_rrset_rdata_rr_count(rrset) == 0) { dbg_rrset_detail("NO RDATA\n"); } for (uint16_t i = 0; i < knot_rrset_rdata_rr_count(rrset); i++) { knot_rrset_rdata_dump(rrset, i); } ); } uint64_t rrset_binary_size(const knot_rrset_t *rrset) { if (rrset == NULL || rrset->rdata_count == 0) { return 0; } uint64_t size = sizeof(uint64_t) + // size at the beginning knot_dname_size(knot_rrset_owner(rrset)) + // owner data sizeof(uint16_t) + // type sizeof(uint16_t) + // class sizeof(uint32_t) + // ttl sizeof(uint16_t) + //RR count sizeof(uint32_t) * rrset->rdata_count; // RR indices for (uint16_t i = 0; i < rrset->rdata_count; i++) { /* Space to store length of one RR. */ size += sizeof(uint32_t); /* Actual data. */ size += rrset_binary_size_one(rrset, i); } return size; } int rrset_serialize(const knot_rrset_t *rrset, uint8_t *stream, size_t *size) { if (rrset == NULL || rrset->rdata_count == 0) { return KNOT_EINVAL; } uint64_t rrset_length = rrset_binary_size(rrset); dbg_rrset_detail("rr: serialize: Binary size=%"PRIu64"\n", rrset_length); memcpy(stream, &rrset_length, sizeof(uint64_t)); size_t offset = sizeof(uint64_t); /* Save RR indices. Size first. */ memcpy(stream + offset, &rrset->rdata_count, sizeof(uint16_t)); offset += sizeof(uint16_t); memcpy(stream + offset, rrset->rdata_indices, rrset->rdata_count * sizeof(uint32_t)); offset += sizeof(uint32_t) * rrset->rdata_count; /* Save owner. */ offset += knot_dname_to_wire(stream + offset, rrset->owner, rrset_length - offset); /* Save static data. */ memcpy(stream + offset, &rrset->type, sizeof(uint16_t)); offset += sizeof(uint16_t); memcpy(stream + offset, &rrset->rclass, sizeof(uint16_t)); offset += sizeof(uint16_t); memcpy(stream + offset, &rrset->ttl, sizeof(uint32_t)); offset += sizeof(uint32_t); /* Copy RDATA. */ for (uint16_t i = 0; i < rrset->rdata_count; i++) { size_t size_one = 0; /* This cannot fail, if it does, RDATA are malformed. TODO */ /* TODO this can be written later. */ uint32_t rr_size = rrset_binary_size_one(rrset, i); dbg_rrset_detail("rr: serialize: RR index=%d size=%d\n", i, rr_size); memcpy(stream + offset, &rr_size, sizeof(uint32_t)); offset += sizeof(uint32_t); rrset_serialize_rr(rrset, i, stream + offset, &size_one); assert(size_one == rr_size); offset += size_one; } *size = offset; assert(*size == rrset_length); dbg_rrset_detail("rr: serialize: RRSet serialized, size=%zu\n", *size); return KNOT_EOK; } int rrset_serialize_alloc(const knot_rrset_t *rrset, uint8_t **stream, size_t *size) { /* Get the binary size. */ *size = rrset_binary_size(rrset); if (*size == 0) { /* Nothing to serialize. */ dbg_rrset("rrset: serialize alloc: No data to serialize.\n"); return KNOT_EINVAL; } /* Prepare memory. */ *stream = malloc(*size); if (*stream == NULL) { return KNOT_ENOMEM; } return rrset_serialize(rrset, *stream, size); } int rrset_deserialize(uint8_t *stream, size_t *stream_size, knot_rrset_t **rrset) { if (sizeof(uint64_t) > *stream_size) { dbg_rrset("rr: deserialize: No space for length.\n"); return KNOT_ESPACE; } uint64_t rrset_length = 0; memcpy(&rrset_length, stream, sizeof(uint64_t)); if (rrset_length > *stream_size) { dbg_rrset("rr: deserialize: No space for whole RRSet. " "(given=%zu needed=%"PRIu64")\n", *stream_size, rrset_length); return KNOT_ESPACE; } size_t offset = sizeof(uint64_t); uint16_t rdata_count = 0; memcpy(&rdata_count, stream + offset, sizeof(uint16_t)); offset += sizeof(uint16_t); uint32_t *rdata_indices = xmalloc(rdata_count * sizeof(uint32_t)); memcpy(rdata_indices, stream + offset, rdata_count * sizeof(uint32_t)); offset += rdata_count * sizeof(uint32_t); /* Read owner from the stream. */ unsigned owner_size = knot_dname_size(stream + offset); knot_dname_t *owner = knot_dname_copy_part(stream + offset, owner_size); assert(owner); offset += owner_size; /* Read type. */ uint16_t type = 0; memcpy(&type, stream + offset, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Read class. */ uint16_t rclass = 0; memcpy(&rclass, stream + offset, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Read TTL. */ uint32_t ttl = 0; memcpy(&ttl, stream + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); /* Create new RRSet. */ *rrset = knot_rrset_new(owner, type, rclass, ttl); if (*rrset == NULL) { knot_dname_free(&owner); free(rdata_indices); return KNOT_ENOMEM; } (*rrset)->rdata_indices = rdata_indices; (*rrset)->rdata_count = rdata_count; (*rrset)->rdata = xmalloc(rdata_indices[rdata_count - 1]); memset((*rrset)->rdata, 0, rdata_indices[rdata_count - 1]); /* Read RRs. */ for (uint16_t i = 0; i < (*rrset)->rdata_count; i++) { /* * There's always size of rdata in the beginning. * Needed because of remainders. */ uint32_t rdata_size = 0; memcpy(&rdata_size, stream + offset, sizeof(uint32_t)); offset += sizeof(uint32_t); size_t read = 0; int ret = rrset_deserialize_rr((*rrset), i, stream + offset, rdata_size, &read); if (ret != KNOT_EOK) { free((*rrset)->rdata); free(rdata_indices); return ret; } /* TODO handle malformations. */ dbg_rrset_detail("rr: deserialaze: RR read size=%zu," "actual=%"PRIu32"\n", read, rdata_size); assert(read == rdata_size); offset += read; } dbg_rrset_exec_detail( dbg_rrset_detail("RRSet deserialized:\n"); knot_rrset_dump(*rrset); ); *stream_size = *stream_size - offset; return KNOT_EOK; } int knot_rrset_find_rr_pos(const knot_rrset_t *rr_search_in, const knot_rrset_t *rr_reference, size_t pos, size_t *pos_out) { int found = 0; for (uint16_t i = 0; i < rr_search_in->rdata_count && !found; ++i) { if (rrset_rdata_compare_one(rr_search_in, rr_reference, i, pos) == 0) { *pos_out = i; found = 1; } } return found ? KNOT_EOK : KNOT_ENOENT; } static int knot_rrset_remove_rr(knot_rrset_t *rrset, const knot_rrset_t *rr_from, size_t rdata_pos) { /* * Position in first and second rrset can differ, we have * to search for position first. */ size_t pos_to_remove = 0; int ret = knot_rrset_find_rr_pos(rrset, rr_from, rdata_pos, &pos_to_remove); if (ret == KNOT_EOK) { /* Position found, can be removed. */ dbg_rrset_detail("rr: remove_rr: Counter position found=%zu\n", pos_to_remove); assert(pos_to_remove < rrset->rdata_count); ret = knot_rrset_remove_rdata_pos(rrset, pos_to_remove); if (ret != KNOT_EOK) { dbg_rrset("Cannot remove RDATA from RRSet (%s).\n", knot_strerror(ret)); return ret; } } else { dbg_rrset_verb("rr: remove_rr: RDATA not found (%s).\n", knot_strerror(ret)); return ret; } return KNOT_EOK; } static int knot_rrset_rdata_reset(knot_rrset_t *rrset) { if (rrset == NULL) { return KNOT_EINVAL; } rrset->rdata = NULL; rrset->rdata_indices = NULL; rrset->rdata_count = 0; return KNOT_EOK; } int knot_rrset_add_rr_from_rrset(knot_rrset_t *dest, const knot_rrset_t *source, size_t rdata_pos) { if (dest == NULL || source == NULL || rdata_pos >= source->rdata_count) { return KNOT_EINVAL; } /* Get size of RDATA to be copied. */ uint16_t item_size = rrset_rdata_item_size(source, rdata_pos); /* Reserve space in dest RRSet. */ uint8_t *rdata = knot_rrset_create_rdata(dest, item_size); if (rdata == NULL) { dbg_rrset("rr: add_rr_from_rrset: Could not create RDATA.\n"); return KNOT_ERROR; } /* Copy actual data. */ memcpy(rdata, rrset_rdata_pointer(source, rdata_pos), item_size); return KNOT_EOK; } int knot_rrset_remove_rr_using_rrset(knot_rrset_t *from, const knot_rrset_t *what, knot_rrset_t **rr_deleted) { if (from == NULL || what == NULL || rr_deleted == NULL) { return KNOT_EINVAL; } knot_rrset_t *return_rr = NULL; int ret = knot_rrset_shallow_copy(what, &return_rr); if (ret != KNOT_EOK) { dbg_rrset("remove_rr_using_rrset: Could not copy RRSet (%s).\n", knot_strerror(ret)); return ret; } /* Reset RDATA of returned RRSet. */ knot_rrset_rdata_reset(return_rr); return_rr->rrsigs = NULL; for (uint16_t i = 0; i < what->rdata_count; ++i) { ret = knot_rrset_remove_rr(from, what, i); if (ret == KNOT_EOK) { /* RR was removed, can be added to 'return' RRSet. */ ret = knot_rrset_add_rr_from_rrset(return_rr, what, i); if (ret != KNOT_EOK) { knot_rrset_deep_free(&return_rr, 0); dbg_xfrin("xfr: Could not add RR (%s).\n", knot_strerror(ret)); return ret; } dbg_rrset_detail("rrset: remove_rr_using_rrset: " "Successfuly removed and returned this RR:\n"); knot_rrset_rdata_dump(return_rr, return_rr->rdata_count - 1); } else if (ret != KNOT_ENOENT) { /* NOENT is OK, but other errors are not. */ dbg_rrset("rrset: remove_using_rrset: " "RRSet removal failed (%s).\n", knot_strerror(ret)); knot_rrset_deep_free(&return_rr, 0); return ret; } } *rr_deleted = return_rr; return KNOT_EOK; } int knot_rrset_remove_rr_using_rrset_del(knot_rrset_t *from, const knot_rrset_t *what) { knot_rrset_t *rr_removed = NULL; int ret = knot_rrset_remove_rr_using_rrset(from, what, &rr_removed); knot_rrset_deep_free(&rr_removed, 1); return ret; } void knot_rrset_set_class(knot_rrset_t *rrset, uint16_t rclass) { if (!rrset) { return; } rrset->rclass = rclass; } int knot_rrset_ds_check(const knot_rrset_t *rrset) { // Check if the legth of the digest corresponds to the proper size of // the digest according to the given algorithm for (uint16_t i = 0; i < rrset->rdata_count; ++i) { /* 4 bytes before actual digest. */ if (rrset_rdata_item_size(rrset, i) < 4) { /* Not even keytag, alg and alg type. */ return KNOT_EMALF; } uint16_t len = rrset_rdata_item_size(rrset, i) - 4; uint8_t type = rrset_rdata_pointer(rrset, i)[3]; if (type == 0 || len == 0) { return KNOT_EINVAL; } else if (len != knot_ds_digest_length(type)) { return KNOT_EDSDIGESTLEN; } } return KNOT_EOK; }