1 1.1 christos /* $NetBSD: rdataslab.c,v 1.1 2024/02/18 20:57:33 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.1 christos * SPDX-License-Identifier: MPL-2.0 7 1.1 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.1 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.1 christos #include <stdbool.h> 19 1.1 christos #include <stdlib.h> 20 1.1 christos 21 1.1 christos #include <isc/mem.h> 22 1.1 christos #include <isc/region.h> 23 1.1 christos #include <isc/string.h> /* Required for HP/UX (and others?) */ 24 1.1 christos #include <isc/util.h> 25 1.1 christos 26 1.1 christos #include <dns/rdata.h> 27 1.1 christos #include <dns/rdataset.h> 28 1.1 christos #include <dns/rdataslab.h> 29 1.1 christos #include <dns/result.h> 30 1.1 christos 31 1.1 christos /* 32 1.1 christos * The rdataslab structure allows iteration to occur in both load order 33 1.1 christos * and DNSSEC order. The structure is as follows: 34 1.1 christos * 35 1.1 christos * header (reservelen bytes) 36 1.1 christos * record count (2 bytes) 37 1.1 christos * offset table (4 x record count bytes in load order) 38 1.1 christos * data records 39 1.1 christos * data length (2 bytes) 40 1.1 christos * order (2 bytes) 41 1.1 christos * meta data (1 byte for RRSIG's) 42 1.1 christos * data (data length bytes) 43 1.1 christos * 44 1.1 christos * If DNS_RDATASET_FIXED is defined to be zero (0) the format of a 45 1.1 christos * rdataslab is as follows: 46 1.1 christos * 47 1.1 christos * header (reservelen bytes) 48 1.1 christos * record count (2 bytes) 49 1.1 christos * data records 50 1.1 christos * data length (2 bytes) 51 1.1 christos * meta data (1 byte for RRSIG's) 52 1.1 christos * data (data length bytes) 53 1.1 christos * 54 1.1 christos * Offsets are from the end of the header. 55 1.1 christos * 56 1.1 christos * Load order traversal is performed by walking the offset table to find 57 1.1 christos * the start of the record (DNS_RDATASET_FIXED = 1). 58 1.1 christos * 59 1.1 christos * DNSSEC order traversal is performed by walking the data records. 60 1.1 christos * 61 1.1 christos * The order is stored with record to allow for efficient reconstruction 62 1.1 christos * of the offset table following a merge or subtraction. 63 1.1 christos * 64 1.1 christos * The iterator methods in rbtdb support both load order and DNSSEC order 65 1.1 christos * iteration. 66 1.1 christos * 67 1.1 christos * WARNING: 68 1.1 christos * rbtdb.c directly interacts with the slab's raw structures. If the 69 1.1 christos * structure changes then rbtdb.c also needs to be updated to reflect 70 1.1 christos * the changes. See the areas tagged with "RDATASLAB". 71 1.1 christos */ 72 1.1 christos 73 1.1 christos struct xrdata { 74 1.1 christos dns_rdata_t rdata; 75 1.1 christos unsigned int order; 76 1.1 christos }; 77 1.1 christos 78 1.1 christos /*% Note: the "const void *" are just to make qsort happy. */ 79 1.1 christos static int 80 1.1 christos compare_rdata(const void *p1, const void *p2) { 81 1.1 christos const struct xrdata *x1 = p1; 82 1.1 christos const struct xrdata *x2 = p2; 83 1.1 christos return (dns_rdata_compare(&x1->rdata, &x2->rdata)); 84 1.1 christos } 85 1.1 christos 86 1.1 christos #if DNS_RDATASET_FIXED 87 1.1 christos static void 88 1.1 christos fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable, 89 1.1 christos unsigned length) { 90 1.1 christos unsigned int i, j; 91 1.1 christos unsigned char *raw; 92 1.1 christos 93 1.1 christos for (i = 0, j = 0; i < length; i++) { 94 1.1 christos if (offsettable[i] == 0) { 95 1.1 christos continue; 96 1.1 christos } 97 1.1 christos 98 1.1 christos /* 99 1.1 christos * Fill in offset table. 100 1.1 christos */ 101 1.1 christos raw = &offsetbase[j * 4 + 2]; 102 1.1 christos *raw++ = (offsettable[i] & 0xff000000) >> 24; 103 1.1 christos *raw++ = (offsettable[i] & 0xff0000) >> 16; 104 1.1 christos *raw++ = (offsettable[i] & 0xff00) >> 8; 105 1.1 christos *raw = offsettable[i] & 0xff; 106 1.1 christos 107 1.1 christos /* 108 1.1 christos * Fill in table index. 109 1.1 christos */ 110 1.1 christos raw = offsetbase + offsettable[i] + 2; 111 1.1 christos *raw++ = (j & 0xff00) >> 8; 112 1.1 christos *raw = j++ & 0xff; 113 1.1 christos } 114 1.1 christos } 115 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 116 1.1 christos 117 1.1 christos isc_result_t 118 1.1 christos dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, 119 1.1 christos isc_region_t *region, unsigned int reservelen) { 120 1.1 christos /* 121 1.1 christos * Use &removed as a sentinel pointer for duplicate 122 1.1 christos * rdata as rdata.data == NULL is valid. 123 1.1 christos */ 124 1.1 christos static unsigned char removed; 125 1.1 christos struct xrdata *x; 126 1.1 christos unsigned char *rawbuf; 127 1.1 christos #if DNS_RDATASET_FIXED 128 1.1 christos unsigned char *offsetbase; 129 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 130 1.1 christos unsigned int buflen; 131 1.1 christos isc_result_t result; 132 1.1 christos unsigned int nitems; 133 1.1 christos unsigned int nalloc; 134 1.1 christos unsigned int i; 135 1.1 christos #if DNS_RDATASET_FIXED 136 1.1 christos unsigned int *offsettable; 137 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 138 1.1 christos unsigned int length; 139 1.1 christos 140 1.1 christos buflen = reservelen + 2; 141 1.1 christos 142 1.1 christos nitems = dns_rdataset_count(rdataset); 143 1.1 christos 144 1.1 christos /* 145 1.1 christos * If there are no rdata then we can just need to allocate a header 146 1.1 christos * with zero a record count. 147 1.1 christos */ 148 1.1 christos if (nitems == 0) { 149 1.1 christos if (rdataset->type != 0) { 150 1.1 christos return (ISC_R_FAILURE); 151 1.1 christos } 152 1.1 christos rawbuf = isc_mem_get(mctx, buflen); 153 1.1 christos region->base = rawbuf; 154 1.1 christos region->length = buflen; 155 1.1 christos rawbuf += reservelen; 156 1.1 christos *rawbuf++ = 0; 157 1.1 christos *rawbuf = 0; 158 1.1 christos return (ISC_R_SUCCESS); 159 1.1 christos } 160 1.1 christos 161 1.1 christos if (nitems > 0xffff) { 162 1.1 christos return (ISC_R_NOSPACE); 163 1.1 christos } 164 1.1 christos 165 1.1 christos /* 166 1.1 christos * Remember the original number of items. 167 1.1 christos */ 168 1.1 christos nalloc = nitems; 169 1.1 christos x = isc_mem_get(mctx, nalloc * sizeof(struct xrdata)); 170 1.1 christos 171 1.1 christos /* 172 1.1 christos * Save all of the rdata members into an array. 173 1.1 christos */ 174 1.1 christos result = dns_rdataset_first(rdataset); 175 1.1 christos if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) { 176 1.1 christos goto free_rdatas; 177 1.1 christos } 178 1.1 christos for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) { 179 1.1 christos INSIST(result == ISC_R_SUCCESS); 180 1.1 christos dns_rdata_init(&x[i].rdata); 181 1.1 christos dns_rdataset_current(rdataset, &x[i].rdata); 182 1.1 christos INSIST(x[i].rdata.data != &removed); 183 1.1 christos #if DNS_RDATASET_FIXED 184 1.1 christos x[i].order = i; 185 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 186 1.1 christos result = dns_rdataset_next(rdataset); 187 1.1 christos } 188 1.1 christos if (i != nalloc || result != ISC_R_NOMORE) { 189 1.1 christos /* 190 1.1 christos * Somehow we iterated over fewer rdatas than 191 1.1 christos * dns_rdataset_count() said there were or there 192 1.1 christos * were more items than dns_rdataset_count said 193 1.1 christos * there were. 194 1.1 christos */ 195 1.1 christos result = ISC_R_FAILURE; 196 1.1 christos goto free_rdatas; 197 1.1 christos } 198 1.1 christos 199 1.1 christos /* 200 1.1 christos * Put into DNSSEC order. 201 1.1 christos */ 202 1.1 christos if (nalloc > 1U) { 203 1.1 christos qsort(x, nalloc, sizeof(struct xrdata), compare_rdata); 204 1.1 christos } 205 1.1 christos 206 1.1 christos /* 207 1.1 christos * Remove duplicates and compute the total storage required. 208 1.1 christos * 209 1.1 christos * If an rdata is not a duplicate, accumulate the storage size 210 1.1 christos * required for the rdata. We do not store the class, type, etc, 211 1.1 christos * just the rdata, so our overhead is 2 bytes for the number of 212 1.1 christos * records, and 8 for each rdata, (length(2), offset(4) and order(2)) 213 1.1 christos * and then the rdata itself. 214 1.1 christos */ 215 1.1 christos for (i = 1; i < nalloc; i++) { 216 1.1 christos if (compare_rdata(&x[i - 1].rdata, &x[i].rdata) == 0) { 217 1.1 christos x[i - 1].rdata.data = &removed; 218 1.1 christos #if DNS_RDATASET_FIXED 219 1.1 christos /* 220 1.1 christos * Preserve the least order so A, B, A -> A, B 221 1.1 christos * after duplicate removal. 222 1.1 christos */ 223 1.1 christos if (x[i - 1].order < x[i].order) { 224 1.1 christos x[i].order = x[i - 1].order; 225 1.1 christos } 226 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 227 1.1 christos nitems--; 228 1.1 christos } else { 229 1.1 christos #if DNS_RDATASET_FIXED 230 1.1 christos buflen += (8 + x[i - 1].rdata.length); 231 1.1 christos #else /* if DNS_RDATASET_FIXED */ 232 1.1 christos buflen += (2 + x[i - 1].rdata.length); 233 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 234 1.1 christos /* 235 1.1 christos * Provide space to store the per RR meta data. 236 1.1 christos */ 237 1.1 christos if (rdataset->type == dns_rdatatype_rrsig) { 238 1.1 christos buflen++; 239 1.1 christos } 240 1.1 christos } 241 1.1 christos } 242 1.1 christos 243 1.1 christos /* 244 1.1 christos * Don't forget the last item! 245 1.1 christos */ 246 1.1 christos #if DNS_RDATASET_FIXED 247 1.1 christos buflen += (8 + x[i - 1].rdata.length); 248 1.1 christos #else /* if DNS_RDATASET_FIXED */ 249 1.1 christos buflen += (2 + x[i - 1].rdata.length); 250 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 251 1.1 christos /* 252 1.1 christos * Provide space to store the per RR meta data. 253 1.1 christos */ 254 1.1 christos if (rdataset->type == dns_rdatatype_rrsig) { 255 1.1 christos buflen++; 256 1.1 christos } 257 1.1 christos 258 1.1 christos /* 259 1.1 christos * Ensure that singleton types are actually singletons. 260 1.1 christos */ 261 1.1 christos if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) { 262 1.1 christos /* 263 1.1 christos * We have a singleton type, but there's more than one 264 1.1 christos * RR in the rdataset. 265 1.1 christos */ 266 1.1 christos result = DNS_R_SINGLETON; 267 1.1 christos goto free_rdatas; 268 1.1 christos } 269 1.1 christos 270 1.1 christos /* 271 1.1 christos * Allocate the memory, set up a buffer, start copying in 272 1.1 christos * data. 273 1.1 christos */ 274 1.1 christos rawbuf = isc_mem_get(mctx, buflen); 275 1.1 christos 276 1.1 christos #if DNS_RDATASET_FIXED 277 1.1 christos /* Allocate temporary offset table. */ 278 1.1 christos offsettable = isc_mem_get(mctx, nalloc * sizeof(unsigned int)); 279 1.1 christos memset(offsettable, 0, nalloc * sizeof(unsigned int)); 280 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 281 1.1 christos 282 1.1 christos region->base = rawbuf; 283 1.1 christos region->length = buflen; 284 1.1 christos 285 1.1 christos memset(rawbuf, 0, buflen); 286 1.1 christos rawbuf += reservelen; 287 1.1 christos 288 1.1 christos #if DNS_RDATASET_FIXED 289 1.1 christos offsetbase = rawbuf; 290 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 291 1.1 christos 292 1.1 christos *rawbuf++ = (nitems & 0xff00) >> 8; 293 1.1 christos *rawbuf++ = (nitems & 0x00ff); 294 1.1 christos 295 1.1 christos #if DNS_RDATASET_FIXED 296 1.1 christos /* Skip load order table. Filled in later. */ 297 1.1 christos rawbuf += nitems * 4; 298 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 299 1.1 christos 300 1.1 christos for (i = 0; i < nalloc; i++) { 301 1.1 christos if (x[i].rdata.data == &removed) { 302 1.1 christos continue; 303 1.1 christos } 304 1.1 christos #if DNS_RDATASET_FIXED 305 1.1 christos offsettable[x[i].order] = rawbuf - offsetbase; 306 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 307 1.1 christos length = x[i].rdata.length; 308 1.1 christos if (rdataset->type == dns_rdatatype_rrsig) { 309 1.1 christos length++; 310 1.1 christos } 311 1.1 christos INSIST(length <= 0xffff); 312 1.1 christos *rawbuf++ = (length & 0xff00) >> 8; 313 1.1 christos *rawbuf++ = (length & 0x00ff); 314 1.1 christos #if DNS_RDATASET_FIXED 315 1.1 christos rawbuf += 2; /* filled in later */ 316 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 317 1.1 christos /* 318 1.1 christos * Store the per RR meta data. 319 1.1 christos */ 320 1.1 christos if (rdataset->type == dns_rdatatype_rrsig) { 321 1.1 christos *rawbuf++ = (x[i].rdata.flags & DNS_RDATA_OFFLINE) 322 1.1 christos ? DNS_RDATASLAB_OFFLINE 323 1.1 christos : 0; 324 1.1 christos } 325 1.1 christos memmove(rawbuf, x[i].rdata.data, x[i].rdata.length); 326 1.1 christos rawbuf += x[i].rdata.length; 327 1.1 christos } 328 1.1 christos 329 1.1 christos #if DNS_RDATASET_FIXED 330 1.1 christos fillin_offsets(offsetbase, offsettable, nalloc); 331 1.1 christos isc_mem_put(mctx, offsettable, nalloc * sizeof(unsigned int)); 332 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 333 1.1 christos 334 1.1 christos result = ISC_R_SUCCESS; 335 1.1 christos 336 1.1 christos free_rdatas: 337 1.1 christos isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata)); 338 1.1 christos return (result); 339 1.1 christos } 340 1.1 christos 341 1.1 christos unsigned int 342 1.1 christos dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) { 343 1.1 christos unsigned int count, length; 344 1.1 christos unsigned char *current; 345 1.1 christos 346 1.1 christos REQUIRE(slab != NULL); 347 1.1 christos 348 1.1 christos current = slab + reservelen; 349 1.1 christos count = *current++ * 256; 350 1.1 christos count += *current++; 351 1.1 christos #if DNS_RDATASET_FIXED 352 1.1 christos current += (4 * count); 353 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 354 1.1 christos while (count > 0) { 355 1.1 christos count--; 356 1.1 christos length = *current++ * 256; 357 1.1 christos length += *current++; 358 1.1 christos #if DNS_RDATASET_FIXED 359 1.1 christos current += length + 2; 360 1.1 christos #else /* if DNS_RDATASET_FIXED */ 361 1.1 christos current += length; 362 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 363 1.1 christos } 364 1.1 christos 365 1.1 christos return ((unsigned int)(current - slab)); 366 1.1 christos } 367 1.1 christos 368 1.1 christos unsigned int 369 1.1 christos dns_rdataslab_rdatasize(unsigned char *slab, unsigned int reservelen) { 370 1.1 christos unsigned int count, length, rdatalen = 0; 371 1.1 christos unsigned char *current; 372 1.1 christos 373 1.1 christos REQUIRE(slab != NULL); 374 1.1 christos 375 1.1 christos current = slab + reservelen; 376 1.1 christos count = *current++ * 256; 377 1.1 christos count += *current++; 378 1.1 christos #if DNS_RDATASET_FIXED 379 1.1 christos current += (4 * count); 380 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 381 1.1 christos while (count > 0) { 382 1.1 christos count--; 383 1.1 christos length = *current++ * 256; 384 1.1 christos length += *current++; 385 1.1 christos rdatalen += length; 386 1.1 christos #if DNS_RDATASET_FIXED 387 1.1 christos current += length + 2; 388 1.1 christos #else /* if DNS_RDATASET_FIXED */ 389 1.1 christos current += length; 390 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 391 1.1 christos } 392 1.1 christos 393 1.1 christos return (rdatalen); 394 1.1 christos } 395 1.1 christos 396 1.1 christos unsigned int 397 1.1 christos dns_rdataslab_count(unsigned char *slab, unsigned int reservelen) { 398 1.1 christos unsigned int count; 399 1.1 christos unsigned char *current; 400 1.1 christos 401 1.1 christos REQUIRE(slab != NULL); 402 1.1 christos 403 1.1 christos current = slab + reservelen; 404 1.1 christos count = *current++ * 256; 405 1.1 christos count += *current++; 406 1.1 christos return (count); 407 1.1 christos } 408 1.1 christos 409 1.1 christos /* 410 1.1 christos * Make the dns_rdata_t 'rdata' refer to the slab item 411 1.1 christos * beginning at '*current', which is part of a slab of type 412 1.1 christos * 'type' and class 'rdclass', and advance '*current' to 413 1.1 christos * point to the next item in the slab. 414 1.1 christos */ 415 1.1 christos static void 416 1.1 christos rdata_from_slab(unsigned char **current, dns_rdataclass_t rdclass, 417 1.1 christos dns_rdatatype_t type, dns_rdata_t *rdata) { 418 1.1 christos unsigned char *tcurrent = *current; 419 1.1 christos isc_region_t region; 420 1.1 christos unsigned int length; 421 1.1 christos bool offline = false; 422 1.1 christos 423 1.1 christos length = *tcurrent++ * 256; 424 1.1 christos length += *tcurrent++; 425 1.1 christos 426 1.1 christos if (type == dns_rdatatype_rrsig) { 427 1.1 christos if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0) { 428 1.1 christos offline = true; 429 1.1 christos } 430 1.1 christos length--; 431 1.1 christos tcurrent++; 432 1.1 christos } 433 1.1 christos region.length = length; 434 1.1 christos #if DNS_RDATASET_FIXED 435 1.1 christos tcurrent += 2; 436 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 437 1.1 christos region.base = tcurrent; 438 1.1 christos tcurrent += region.length; 439 1.1 christos dns_rdata_fromregion(rdata, rdclass, type, ®ion); 440 1.1 christos if (offline) { 441 1.1 christos rdata->flags |= DNS_RDATA_OFFLINE; 442 1.1 christos } 443 1.1 christos *current = tcurrent; 444 1.1 christos } 445 1.1 christos 446 1.1 christos /* 447 1.1 christos * Return true iff 'slab' (slab data of type 'type' and class 'rdclass') 448 1.1 christos * contains an rdata identical to 'rdata'. This does case insensitive 449 1.1 christos * comparisons per DNSSEC. 450 1.1 christos */ 451 1.1 christos static bool 452 1.1 christos rdata_in_slab(unsigned char *slab, unsigned int reservelen, 453 1.1 christos dns_rdataclass_t rdclass, dns_rdatatype_t type, 454 1.1 christos dns_rdata_t *rdata) { 455 1.1 christos unsigned int count, i; 456 1.1 christos unsigned char *current; 457 1.1 christos dns_rdata_t trdata = DNS_RDATA_INIT; 458 1.1 christos int n; 459 1.1 christos 460 1.1 christos current = slab + reservelen; 461 1.1 christos count = *current++ * 256; 462 1.1 christos count += *current++; 463 1.1 christos 464 1.1 christos #if DNS_RDATASET_FIXED 465 1.1 christos current += (4 * count); 466 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 467 1.1 christos 468 1.1 christos for (i = 0; i < count; i++) { 469 1.1 christos rdata_from_slab(¤t, rdclass, type, &trdata); 470 1.1 christos 471 1.1 christos n = dns_rdata_compare(&trdata, rdata); 472 1.1 christos if (n == 0) { 473 1.1 christos return (true); 474 1.1 christos } 475 1.1 christos if (n > 0) { /* In DNSSEC order. */ 476 1.1 christos break; 477 1.1 christos } 478 1.1 christos dns_rdata_reset(&trdata); 479 1.1 christos } 480 1.1 christos return (false); 481 1.1 christos } 482 1.1 christos 483 1.1 christos isc_result_t 484 1.1 christos dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, 485 1.1 christos unsigned int reservelen, isc_mem_t *mctx, 486 1.1 christos dns_rdataclass_t rdclass, dns_rdatatype_t type, 487 1.1 christos unsigned int flags, unsigned char **tslabp) { 488 1.1 christos unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data; 489 1.1 christos unsigned int ocount, ncount, count, olength, tlength, tcount, length; 490 1.1 christos dns_rdata_t ordata = DNS_RDATA_INIT; 491 1.1 christos dns_rdata_t nrdata = DNS_RDATA_INIT; 492 1.1 christos bool added_something = false; 493 1.1 christos unsigned int oadded = 0; 494 1.1 christos unsigned int nadded = 0; 495 1.1 christos unsigned int nncount = 0; 496 1.1 christos #if DNS_RDATASET_FIXED 497 1.1 christos unsigned int oncount; 498 1.1 christos unsigned int norder = 0; 499 1.1 christos unsigned int oorder = 0; 500 1.1 christos unsigned char *offsetbase; 501 1.1 christos unsigned int *offsettable; 502 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 503 1.1 christos 504 1.1 christos /* 505 1.1 christos * XXX Need parameter to allow "delete rdatasets in nslab" merge, 506 1.1 christos * or perhaps another merge routine for this purpose. 507 1.1 christos */ 508 1.1 christos 509 1.1 christos REQUIRE(tslabp != NULL && *tslabp == NULL); 510 1.1 christos REQUIRE(oslab != NULL && nslab != NULL); 511 1.1 christos 512 1.1 christos ocurrent = oslab + reservelen; 513 1.1 christos ocount = *ocurrent++ * 256; 514 1.1 christos ocount += *ocurrent++; 515 1.1 christos #if DNS_RDATASET_FIXED 516 1.1 christos ocurrent += (4 * ocount); 517 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 518 1.1 christos ostart = ocurrent; 519 1.1 christos ncurrent = nslab + reservelen; 520 1.1 christos ncount = *ncurrent++ * 256; 521 1.1 christos ncount += *ncurrent++; 522 1.1 christos #if DNS_RDATASET_FIXED 523 1.1 christos ncurrent += (4 * ncount); 524 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 525 1.1 christos INSIST(ocount > 0 && ncount > 0); 526 1.1 christos 527 1.1 christos #if DNS_RDATASET_FIXED 528 1.1 christos oncount = ncount; 529 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 530 1.1 christos 531 1.1 christos /* 532 1.1 christos * Yes, this is inefficient! 533 1.1 christos */ 534 1.1 christos 535 1.1 christos /* 536 1.1 christos * Figure out the length of the old slab's data. 537 1.1 christos */ 538 1.1 christos olength = 0; 539 1.1 christos for (count = 0; count < ocount; count++) { 540 1.1 christos length = *ocurrent++ * 256; 541 1.1 christos length += *ocurrent++; 542 1.1 christos #if DNS_RDATASET_FIXED 543 1.1 christos olength += length + 8; 544 1.1 christos ocurrent += length + 2; 545 1.1 christos #else /* if DNS_RDATASET_FIXED */ 546 1.1 christos olength += length + 2; 547 1.1 christos ocurrent += length; 548 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 549 1.1 christos } 550 1.1 christos 551 1.1 christos /* 552 1.1 christos * Start figuring out the target length and count. 553 1.1 christos */ 554 1.1 christos tlength = reservelen + 2 + olength; 555 1.1 christos tcount = ocount; 556 1.1 christos 557 1.1 christos /* 558 1.1 christos * Add in the length of rdata in the new slab that aren't in 559 1.1 christos * the old slab. 560 1.1 christos */ 561 1.1 christos do { 562 1.1 christos dns_rdata_init(&nrdata); 563 1.1 christos rdata_from_slab(&ncurrent, rdclass, type, &nrdata); 564 1.1 christos if (!rdata_in_slab(oslab, reservelen, rdclass, type, &nrdata)) { 565 1.1 christos /* 566 1.1 christos * This rdata isn't in the old slab. 567 1.1 christos */ 568 1.1 christos #if DNS_RDATASET_FIXED 569 1.1 christos tlength += nrdata.length + 8; 570 1.1 christos #else /* if DNS_RDATASET_FIXED */ 571 1.1 christos tlength += nrdata.length + 2; 572 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 573 1.1 christos if (type == dns_rdatatype_rrsig) { 574 1.1 christos tlength++; 575 1.1 christos } 576 1.1 christos tcount++; 577 1.1 christos nncount++; 578 1.1 christos added_something = true; 579 1.1 christos } 580 1.1 christos ncount--; 581 1.1 christos } while (ncount > 0); 582 1.1 christos ncount = nncount; 583 1.1 christos 584 1.1 christos if (((flags & DNS_RDATASLAB_EXACT) != 0) && (tcount != ncount + ocount)) 585 1.1 christos { 586 1.1 christos return (DNS_R_NOTEXACT); 587 1.1 christos } 588 1.1 christos 589 1.1 christos if (!added_something && (flags & DNS_RDATASLAB_FORCE) == 0) { 590 1.1 christos return (DNS_R_UNCHANGED); 591 1.1 christos } 592 1.1 christos 593 1.1 christos /* 594 1.1 christos * Ensure that singleton types are actually singletons. 595 1.1 christos */ 596 1.1 christos if (tcount > 1 && dns_rdatatype_issingleton(type)) { 597 1.1 christos /* 598 1.1 christos * We have a singleton type, but there's more than one 599 1.1 christos * RR in the rdataset. 600 1.1 christos */ 601 1.1 christos return (DNS_R_SINGLETON); 602 1.1 christos } 603 1.1 christos 604 1.1 christos if (tcount > 0xffff) { 605 1.1 christos return (ISC_R_NOSPACE); 606 1.1 christos } 607 1.1 christos 608 1.1 christos /* 609 1.1 christos * Copy the reserved area from the new slab. 610 1.1 christos */ 611 1.1 christos tstart = isc_mem_get(mctx, tlength); 612 1.1 christos memmove(tstart, nslab, reservelen); 613 1.1 christos tcurrent = tstart + reservelen; 614 1.1 christos #if DNS_RDATASET_FIXED 615 1.1 christos offsetbase = tcurrent; 616 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 617 1.1 christos 618 1.1 christos /* 619 1.1 christos * Write the new count. 620 1.1 christos */ 621 1.1 christos *tcurrent++ = (tcount & 0xff00) >> 8; 622 1.1 christos *tcurrent++ = (tcount & 0x00ff); 623 1.1 christos 624 1.1 christos #if DNS_RDATASET_FIXED 625 1.1 christos /* 626 1.1 christos * Skip offset table. 627 1.1 christos */ 628 1.1 christos tcurrent += (tcount * 4); 629 1.1 christos 630 1.1 christos offsettable = isc_mem_get(mctx, 631 1.1 christos (ocount + oncount) * sizeof(unsigned int)); 632 1.1 christos memset(offsettable, 0, (ocount + oncount) * sizeof(unsigned int)); 633 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 634 1.1 christos 635 1.1 christos /* 636 1.1 christos * Merge the two slabs. 637 1.1 christos */ 638 1.1 christos ocurrent = ostart; 639 1.1 christos INSIST(ocount != 0); 640 1.1 christos #if DNS_RDATASET_FIXED 641 1.1 christos oorder = ocurrent[2] * 256 + ocurrent[3]; 642 1.1 christos INSIST(oorder < ocount); 643 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 644 1.1 christos rdata_from_slab(&ocurrent, rdclass, type, &ordata); 645 1.1 christos 646 1.1 christos ncurrent = nslab + reservelen + 2; 647 1.1 christos #if DNS_RDATASET_FIXED 648 1.1 christos ncurrent += (4 * oncount); 649 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 650 1.1 christos 651 1.1 christos if (ncount > 0) { 652 1.1 christos do { 653 1.1 christos dns_rdata_reset(&nrdata); 654 1.1 christos #if DNS_RDATASET_FIXED 655 1.1 christos norder = ncurrent[2] * 256 + ncurrent[3]; 656 1.1 christos 657 1.1 christos INSIST(norder < oncount); 658 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 659 1.1 christos rdata_from_slab(&ncurrent, rdclass, type, &nrdata); 660 1.1 christos } while (rdata_in_slab(oslab, reservelen, rdclass, type, 661 1.1 christos &nrdata)); 662 1.1 christos } 663 1.1 christos 664 1.1 christos while (oadded < ocount || nadded < ncount) { 665 1.1 christos bool fromold; 666 1.1 christos if (oadded == ocount) { 667 1.1 christos fromold = false; 668 1.1 christos } else if (nadded == ncount) { 669 1.1 christos fromold = true; 670 1.1 christos } else { 671 1.1 christos fromold = (dns_rdata_compare(&ordata, &nrdata) < 0); 672 1.1 christos } 673 1.1 christos if (fromold) { 674 1.1 christos #if DNS_RDATASET_FIXED 675 1.1 christos offsettable[oorder] = tcurrent - offsetbase; 676 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 677 1.1 christos length = ordata.length; 678 1.1 christos data = ordata.data; 679 1.1 christos if (type == dns_rdatatype_rrsig) { 680 1.1 christos length++; 681 1.1 christos data--; 682 1.1 christos } 683 1.1 christos *tcurrent++ = (length & 0xff00) >> 8; 684 1.1 christos *tcurrent++ = (length & 0x00ff); 685 1.1 christos #if DNS_RDATASET_FIXED 686 1.1 christos tcurrent += 2; /* fill in later */ 687 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 688 1.1 christos memmove(tcurrent, data, length); 689 1.1 christos tcurrent += length; 690 1.1 christos oadded++; 691 1.1 christos if (oadded < ocount) { 692 1.1 christos dns_rdata_reset(&ordata); 693 1.1 christos #if DNS_RDATASET_FIXED 694 1.1 christos oorder = ocurrent[2] * 256 + ocurrent[3]; 695 1.1 christos INSIST(oorder < ocount); 696 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 697 1.1 christos rdata_from_slab(&ocurrent, rdclass, type, 698 1.1 christos &ordata); 699 1.1 christos } 700 1.1 christos } else { 701 1.1 christos #if DNS_RDATASET_FIXED 702 1.1 christos offsettable[ocount + norder] = tcurrent - offsetbase; 703 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 704 1.1 christos length = nrdata.length; 705 1.1 christos data = nrdata.data; 706 1.1 christos if (type == dns_rdatatype_rrsig) { 707 1.1 christos length++; 708 1.1 christos data--; 709 1.1 christos } 710 1.1 christos *tcurrent++ = (length & 0xff00) >> 8; 711 1.1 christos *tcurrent++ = (length & 0x00ff); 712 1.1 christos #if DNS_RDATASET_FIXED 713 1.1 christos tcurrent += 2; /* fill in later */ 714 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 715 1.1 christos memmove(tcurrent, data, length); 716 1.1 christos tcurrent += length; 717 1.1 christos nadded++; 718 1.1 christos if (nadded < ncount) { 719 1.1 christos do { 720 1.1 christos dns_rdata_reset(&nrdata); 721 1.1 christos #if DNS_RDATASET_FIXED 722 1.1 christos norder = ncurrent[2] * 256 + 723 1.1 christos ncurrent[3]; 724 1.1 christos INSIST(norder < oncount); 725 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 726 1.1 christos rdata_from_slab(&ncurrent, rdclass, 727 1.1 christos type, &nrdata); 728 1.1 christos } while (rdata_in_slab(oslab, reservelen, 729 1.1 christos rdclass, type, &nrdata)); 730 1.1 christos } 731 1.1 christos } 732 1.1 christos } 733 1.1 christos 734 1.1 christos #if DNS_RDATASET_FIXED 735 1.1 christos fillin_offsets(offsetbase, offsettable, ocount + oncount); 736 1.1 christos 737 1.1 christos isc_mem_put(mctx, offsettable, 738 1.1 christos (ocount + oncount) * sizeof(unsigned int)); 739 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 740 1.1 christos 741 1.1 christos INSIST(tcurrent == tstart + tlength); 742 1.1 christos 743 1.1 christos *tslabp = tstart; 744 1.1 christos 745 1.1 christos return (ISC_R_SUCCESS); 746 1.1 christos } 747 1.1 christos 748 1.1 christos isc_result_t 749 1.1 christos dns_rdataslab_subtract(unsigned char *mslab, unsigned char *sslab, 750 1.1 christos unsigned int reservelen, isc_mem_t *mctx, 751 1.1 christos dns_rdataclass_t rdclass, dns_rdatatype_t type, 752 1.1 christos unsigned int flags, unsigned char **tslabp) { 753 1.1 christos unsigned char *mcurrent, *sstart, *scurrent, *tstart, *tcurrent; 754 1.1 christos unsigned int mcount, scount, rcount, count, tlength, tcount, i; 755 1.1 christos dns_rdata_t srdata = DNS_RDATA_INIT; 756 1.1 christos dns_rdata_t mrdata = DNS_RDATA_INIT; 757 1.1 christos #if DNS_RDATASET_FIXED 758 1.1 christos unsigned char *offsetbase; 759 1.1 christos unsigned int *offsettable; 760 1.1 christos unsigned int order; 761 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 762 1.1 christos 763 1.1 christos REQUIRE(tslabp != NULL && *tslabp == NULL); 764 1.1 christos REQUIRE(mslab != NULL && sslab != NULL); 765 1.1 christos 766 1.1 christos mcurrent = mslab + reservelen; 767 1.1 christos mcount = *mcurrent++ * 256; 768 1.1 christos mcount += *mcurrent++; 769 1.1 christos scurrent = sslab + reservelen; 770 1.1 christos scount = *scurrent++ * 256; 771 1.1 christos scount += *scurrent++; 772 1.1 christos INSIST(mcount > 0 && scount > 0); 773 1.1 christos 774 1.1 christos /* 775 1.1 christos * Yes, this is inefficient! 776 1.1 christos */ 777 1.1 christos 778 1.1 christos /* 779 1.1 christos * Start figuring out the target length and count. 780 1.1 christos */ 781 1.1 christos tlength = reservelen + 2; 782 1.1 christos tcount = 0; 783 1.1 christos rcount = 0; 784 1.1 christos 785 1.1 christos #if DNS_RDATASET_FIXED 786 1.1 christos mcurrent += 4 * mcount; 787 1.1 christos scurrent += 4 * scount; 788 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 789 1.1 christos sstart = scurrent; 790 1.1 christos 791 1.1 christos /* 792 1.1 christos * Add in the length of rdata in the mslab that aren't in 793 1.1 christos * the sslab. 794 1.1 christos */ 795 1.1 christos for (i = 0; i < mcount; i++) { 796 1.1 christos unsigned char *mrdatabegin = mcurrent; 797 1.1 christos rdata_from_slab(&mcurrent, rdclass, type, &mrdata); 798 1.1 christos scurrent = sstart; 799 1.1 christos for (count = 0; count < scount; count++) { 800 1.1 christos dns_rdata_reset(&srdata); 801 1.1 christos rdata_from_slab(&scurrent, rdclass, type, &srdata); 802 1.1 christos if (dns_rdata_compare(&mrdata, &srdata) == 0) { 803 1.1 christos break; 804 1.1 christos } 805 1.1 christos } 806 1.1 christos if (count == scount) { 807 1.1 christos /* 808 1.1 christos * This rdata isn't in the sslab, and thus isn't 809 1.1 christos * being subtracted. 810 1.1 christos */ 811 1.1 christos tlength += (unsigned int)(mcurrent - mrdatabegin); 812 1.1 christos tcount++; 813 1.1 christos } else { 814 1.1 christos rcount++; 815 1.1 christos } 816 1.1 christos dns_rdata_reset(&mrdata); 817 1.1 christos } 818 1.1 christos 819 1.1 christos #if DNS_RDATASET_FIXED 820 1.1 christos tlength += (4 * tcount); 821 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 822 1.1 christos 823 1.1 christos /* 824 1.1 christos * Check that all the records originally existed. The numeric 825 1.1 christos * check only works as rdataslabs do not contain duplicates. 826 1.1 christos */ 827 1.1 christos if (((flags & DNS_RDATASLAB_EXACT) != 0) && (rcount != scount)) { 828 1.1 christos return (DNS_R_NOTEXACT); 829 1.1 christos } 830 1.1 christos 831 1.1 christos /* 832 1.1 christos * Don't continue if the new rdataslab would be empty. 833 1.1 christos */ 834 1.1 christos if (tcount == 0) { 835 1.1 christos return (DNS_R_NXRRSET); 836 1.1 christos } 837 1.1 christos 838 1.1 christos /* 839 1.1 christos * If nothing is going to change, we can stop. 840 1.1 christos */ 841 1.1 christos if (rcount == 0) { 842 1.1 christos return (DNS_R_UNCHANGED); 843 1.1 christos } 844 1.1 christos 845 1.1 christos /* 846 1.1 christos * Copy the reserved area from the mslab. 847 1.1 christos */ 848 1.1 christos tstart = isc_mem_get(mctx, tlength); 849 1.1 christos memmove(tstart, mslab, reservelen); 850 1.1 christos tcurrent = tstart + reservelen; 851 1.1 christos #if DNS_RDATASET_FIXED 852 1.1 christos offsetbase = tcurrent; 853 1.1 christos 854 1.1 christos offsettable = isc_mem_get(mctx, mcount * sizeof(unsigned int)); 855 1.1 christos memset(offsettable, 0, mcount * sizeof(unsigned int)); 856 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 857 1.1 christos 858 1.1 christos /* 859 1.1 christos * Write the new count. 860 1.1 christos */ 861 1.1 christos *tcurrent++ = (tcount & 0xff00) >> 8; 862 1.1 christos *tcurrent++ = (tcount & 0x00ff); 863 1.1 christos 864 1.1 christos #if DNS_RDATASET_FIXED 865 1.1 christos tcurrent += (4 * tcount); 866 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 867 1.1 christos 868 1.1 christos /* 869 1.1 christos * Copy the parts of mslab not in sslab. 870 1.1 christos */ 871 1.1 christos mcurrent = mslab + reservelen; 872 1.1 christos mcount = *mcurrent++ * 256; 873 1.1 christos mcount += *mcurrent++; 874 1.1 christos #if DNS_RDATASET_FIXED 875 1.1 christos mcurrent += (4 * mcount); 876 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 877 1.1 christos for (i = 0; i < mcount; i++) { 878 1.1 christos unsigned char *mrdatabegin = mcurrent; 879 1.1 christos #if DNS_RDATASET_FIXED 880 1.1 christos order = mcurrent[2] * 256 + mcurrent[3]; 881 1.1 christos INSIST(order < mcount); 882 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 883 1.1 christos rdata_from_slab(&mcurrent, rdclass, type, &mrdata); 884 1.1 christos scurrent = sstart; 885 1.1 christos for (count = 0; count < scount; count++) { 886 1.1 christos dns_rdata_reset(&srdata); 887 1.1 christos rdata_from_slab(&scurrent, rdclass, type, &srdata); 888 1.1 christos if (dns_rdata_compare(&mrdata, &srdata) == 0) { 889 1.1 christos break; 890 1.1 christos } 891 1.1 christos } 892 1.1 christos if (count == scount) { 893 1.1 christos /* 894 1.1 christos * This rdata isn't in the sslab, and thus should be 895 1.1 christos * copied to the tslab. 896 1.1 christos */ 897 1.1 christos unsigned int length; 898 1.1 christos length = (unsigned int)(mcurrent - mrdatabegin); 899 1.1 christos #if DNS_RDATASET_FIXED 900 1.1 christos offsettable[order] = tcurrent - offsetbase; 901 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 902 1.1 christos memmove(tcurrent, mrdatabegin, length); 903 1.1 christos tcurrent += length; 904 1.1 christos } 905 1.1 christos dns_rdata_reset(&mrdata); 906 1.1 christos } 907 1.1 christos 908 1.1 christos #if DNS_RDATASET_FIXED 909 1.1 christos fillin_offsets(offsetbase, offsettable, mcount); 910 1.1 christos 911 1.1 christos isc_mem_put(mctx, offsettable, mcount * sizeof(unsigned int)); 912 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 913 1.1 christos 914 1.1 christos INSIST(tcurrent == tstart + tlength); 915 1.1 christos 916 1.1 christos *tslabp = tstart; 917 1.1 christos 918 1.1 christos return (ISC_R_SUCCESS); 919 1.1 christos } 920 1.1 christos 921 1.1 christos bool 922 1.1 christos dns_rdataslab_equal(unsigned char *slab1, unsigned char *slab2, 923 1.1 christos unsigned int reservelen) { 924 1.1 christos unsigned char *current1, *current2; 925 1.1 christos unsigned int count1, count2; 926 1.1 christos unsigned int length1, length2; 927 1.1 christos 928 1.1 christos current1 = slab1 + reservelen; 929 1.1 christos count1 = *current1++ * 256; 930 1.1 christos count1 += *current1++; 931 1.1 christos 932 1.1 christos current2 = slab2 + reservelen; 933 1.1 christos count2 = *current2++ * 256; 934 1.1 christos count2 += *current2++; 935 1.1 christos 936 1.1 christos if (count1 != count2) { 937 1.1 christos return (false); 938 1.1 christos } 939 1.1 christos 940 1.1 christos #if DNS_RDATASET_FIXED 941 1.1 christos current1 += (4 * count1); 942 1.1 christos current2 += (4 * count2); 943 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 944 1.1 christos 945 1.1 christos while (count1 > 0) { 946 1.1 christos length1 = *current1++ * 256; 947 1.1 christos length1 += *current1++; 948 1.1 christos 949 1.1 christos length2 = *current2++ * 256; 950 1.1 christos length2 += *current2++; 951 1.1 christos 952 1.1 christos #if DNS_RDATASET_FIXED 953 1.1 christos current1 += 2; 954 1.1 christos current2 += 2; 955 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 956 1.1 christos 957 1.1 christos if (length1 != length2 || 958 1.1 christos memcmp(current1, current2, length1) != 0) 959 1.1 christos { 960 1.1 christos return (false); 961 1.1 christos } 962 1.1 christos 963 1.1 christos current1 += length1; 964 1.1 christos current2 += length1; 965 1.1 christos 966 1.1 christos count1--; 967 1.1 christos } 968 1.1 christos return (true); 969 1.1 christos } 970 1.1 christos 971 1.1 christos bool 972 1.1 christos dns_rdataslab_equalx(unsigned char *slab1, unsigned char *slab2, 973 1.1 christos unsigned int reservelen, dns_rdataclass_t rdclass, 974 1.1 christos dns_rdatatype_t type) { 975 1.1 christos unsigned char *current1, *current2; 976 1.1 christos unsigned int count1, count2; 977 1.1 christos dns_rdata_t rdata1 = DNS_RDATA_INIT; 978 1.1 christos dns_rdata_t rdata2 = DNS_RDATA_INIT; 979 1.1 christos 980 1.1 christos current1 = slab1 + reservelen; 981 1.1 christos count1 = *current1++ * 256; 982 1.1 christos count1 += *current1++; 983 1.1 christos 984 1.1 christos current2 = slab2 + reservelen; 985 1.1 christos count2 = *current2++ * 256; 986 1.1 christos count2 += *current2++; 987 1.1 christos 988 1.1 christos if (count1 != count2) { 989 1.1 christos return (false); 990 1.1 christos } 991 1.1 christos 992 1.1 christos #if DNS_RDATASET_FIXED 993 1.1 christos current1 += (4 * count1); 994 1.1 christos current2 += (4 * count2); 995 1.1 christos #endif /* if DNS_RDATASET_FIXED */ 996 1.1 christos 997 1.1 christos while (count1-- > 0) { 998 1.1 christos rdata_from_slab(¤t1, rdclass, type, &rdata1); 999 1.1 christos rdata_from_slab(¤t2, rdclass, type, &rdata2); 1000 1.1 christos if (dns_rdata_compare(&rdata1, &rdata2) != 0) { 1001 1.1 christos return (false); 1002 1.1 christos } 1003 1.1 christos dns_rdata_reset(&rdata1); 1004 1.1 christos dns_rdata_reset(&rdata2); 1005 1.1 christos } 1006 1.1 christos return (true); 1007 1.1 christos } 1008