1*bcda20f6Schristos /* $NetBSD: ncache.c,v 1.10 2025/01/26 16:25:23 christos Exp $ */ 2d68c78b8Schristos 3d68c78b8Schristos /* 4d68c78b8Schristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5d68c78b8Schristos * 68596601aSchristos * SPDX-License-Identifier: MPL-2.0 78596601aSchristos * 8d68c78b8Schristos * This Source Code Form is subject to the terms of the Mozilla Public 9d68c78b8Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this 10fce770bdSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11d68c78b8Schristos * 12d68c78b8Schristos * See the COPYRIGHT file distributed with this work for additional 13d68c78b8Schristos * information regarding copyright ownership. 14d68c78b8Schristos */ 15d68c78b8Schristos 16d68c78b8Schristos /*! \file */ 17d68c78b8Schristos 18d4a20c3eSchristos #include <inttypes.h> 19d4a20c3eSchristos #include <stdbool.h> 20d4a20c3eSchristos 21d68c78b8Schristos #include <isc/buffer.h> 22d68c78b8Schristos #include <isc/util.h> 23d68c78b8Schristos 24d68c78b8Schristos #include <dns/db.h> 25d68c78b8Schristos #include <dns/message.h> 26d68c78b8Schristos #include <dns/ncache.h> 27d68c78b8Schristos #include <dns/rdata.h> 28d68c78b8Schristos #include <dns/rdatalist.h> 29d68c78b8Schristos #include <dns/rdataset.h> 30d68c78b8Schristos #include <dns/rdatastruct.h> 31d68c78b8Schristos 325e267ba4Schristos #define DNS_NCACHE_RDATA 100U 33d68c78b8Schristos 34d68c78b8Schristos /* 35*bcda20f6Schristos * The format of an ncache rdata is a sequence of zero or more records 36*bcda20f6Schristos * of the following format: 37d68c78b8Schristos * 38d68c78b8Schristos * owner name 39d68c78b8Schristos * type 40d68c78b8Schristos * trust 41d68c78b8Schristos * rdata count 42*bcda20f6Schristos * rdata length These two occur 'rdata 43*bcda20f6Schristos * rdata count' times. 44d68c78b8Schristos * 45d68c78b8Schristos */ 46d68c78b8Schristos 47*bcda20f6Schristos static uint8_t 48*bcda20f6Schristos atomic_getuint8(isc_buffer_t *b) { 49*bcda20f6Schristos atomic_uchar *cp = isc_buffer_current(b); 50*bcda20f6Schristos uint8_t ret = atomic_load_relaxed(cp); 51*bcda20f6Schristos isc_buffer_forward(b, 1); 52*bcda20f6Schristos return ret; 53*bcda20f6Schristos } 54*bcda20f6Schristos 55d68c78b8Schristos static isc_result_t 56d68c78b8Schristos addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, 575606745fSchristos dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl, 585606745fSchristos dns_ttl_t maxttl, bool optout, bool secure, 59d68c78b8Schristos dns_rdataset_t *addedrdataset); 60d68c78b8Schristos 618596601aSchristos static isc_result_t 62d68c78b8Schristos copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) { 63d68c78b8Schristos isc_result_t result; 64d68c78b8Schristos unsigned int count; 65d68c78b8Schristos isc_region_t ar, r; 66d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 67d68c78b8Schristos 68d68c78b8Schristos /* 69d68c78b8Schristos * Copy the rdataset count to the buffer. 70d68c78b8Schristos */ 71d68c78b8Schristos isc_buffer_availableregion(buffer, &ar); 725606745fSchristos if (ar.length < 2) { 73*bcda20f6Schristos return ISC_R_NOSPACE; 745606745fSchristos } 75d68c78b8Schristos count = dns_rdataset_count(rdataset); 76d68c78b8Schristos INSIST(count <= 65535); 77d4a20c3eSchristos isc_buffer_putuint16(buffer, (uint16_t)count); 78d68c78b8Schristos 79d68c78b8Schristos result = dns_rdataset_first(rdataset); 80d68c78b8Schristos while (result == ISC_R_SUCCESS) { 81d68c78b8Schristos dns_rdataset_current(rdataset, &rdata); 82d68c78b8Schristos dns_rdata_toregion(&rdata, &r); 83d68c78b8Schristos INSIST(r.length <= 65535); 84d68c78b8Schristos isc_buffer_availableregion(buffer, &ar); 855606745fSchristos if (ar.length < 2) { 86*bcda20f6Schristos return ISC_R_NOSPACE; 875606745fSchristos } 88d68c78b8Schristos /* 89d68c78b8Schristos * Copy the rdata length to the buffer. 90d68c78b8Schristos */ 91d4a20c3eSchristos isc_buffer_putuint16(buffer, (uint16_t)r.length); 92d68c78b8Schristos /* 93d68c78b8Schristos * Copy the rdata to the buffer. 94d68c78b8Schristos */ 95d68c78b8Schristos result = isc_buffer_copyregion(buffer, &r); 965606745fSchristos if (result != ISC_R_SUCCESS) { 97*bcda20f6Schristos return result; 985606745fSchristos } 99d68c78b8Schristos dns_rdata_reset(&rdata); 100d68c78b8Schristos result = dns_rdataset_next(rdataset); 101d68c78b8Schristos } 1025606745fSchristos if (result != ISC_R_NOMORE) { 103*bcda20f6Schristos return result; 1045606745fSchristos } 105d68c78b8Schristos 106*bcda20f6Schristos return ISC_R_SUCCESS; 107d68c78b8Schristos } 108d68c78b8Schristos 109d68c78b8Schristos isc_result_t 110d68c78b8Schristos dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, 1115606745fSchristos dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl, 1125606745fSchristos dns_ttl_t maxttl, dns_rdataset_t *addedrdataset) { 113*bcda20f6Schristos return addoptout(message, cache, node, covers, now, minttl, maxttl, 114*bcda20f6Schristos false, false, addedrdataset); 115d68c78b8Schristos } 116d68c78b8Schristos 117d68c78b8Schristos isc_result_t 118d68c78b8Schristos dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache, 119d68c78b8Schristos dns_dbnode_t *node, dns_rdatatype_t covers, 1205606745fSchristos isc_stdtime_t now, dns_ttl_t minttl, dns_ttl_t maxttl, 1215606745fSchristos bool optout, dns_rdataset_t *addedrdataset) { 122*bcda20f6Schristos return addoptout(message, cache, node, covers, now, minttl, maxttl, 123*bcda20f6Schristos optout, true, addedrdataset); 124d68c78b8Schristos } 125d68c78b8Schristos 126d68c78b8Schristos static isc_result_t 127d68c78b8Schristos addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, 1285606745fSchristos dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t minttl, 1295606745fSchristos dns_ttl_t maxttl, bool optout, bool secure, 1305606745fSchristos dns_rdataset_t *addedrdataset) { 131d68c78b8Schristos isc_result_t result; 132d68c78b8Schristos isc_buffer_t buffer; 133d68c78b8Schristos isc_region_t r; 134d68c78b8Schristos dns_rdataset_t *rdataset; 135d68c78b8Schristos dns_rdatatype_t type; 136d68c78b8Schristos dns_name_t *name; 137d68c78b8Schristos dns_ttl_t ttl; 138d68c78b8Schristos dns_trust_t trust; 139d68c78b8Schristos dns_rdata_t rdata[DNS_NCACHE_RDATA]; 140d68c78b8Schristos dns_rdataset_t ncrdataset; 141d68c78b8Schristos dns_rdatalist_t ncrdatalist; 1425e267ba4Schristos unsigned char data[65536]; 143d68c78b8Schristos unsigned int next = 0; 144d68c78b8Schristos 145d68c78b8Schristos /* 146d68c78b8Schristos * Convert the authority data from 'message' into a negative cache 147d68c78b8Schristos * rdataset, and store it in 'cache' at 'node'. 148d68c78b8Schristos */ 149d68c78b8Schristos 150d68c78b8Schristos REQUIRE(message != NULL); 151d68c78b8Schristos 152d68c78b8Schristos /* 153d68c78b8Schristos * We assume that all data in the authority section has been 154d68c78b8Schristos * validated by the caller. 155d68c78b8Schristos */ 156d68c78b8Schristos 157d68c78b8Schristos /* 158d68c78b8Schristos * Initialize the list. 159d68c78b8Schristos */ 160d68c78b8Schristos dns_rdatalist_init(&ncrdatalist); 161d68c78b8Schristos ncrdatalist.rdclass = dns_db_class(cache); 162d68c78b8Schristos ncrdatalist.covers = covers; 163d68c78b8Schristos ncrdatalist.ttl = maxttl; 164d68c78b8Schristos 165d68c78b8Schristos /* 166d68c78b8Schristos * Build an ncache rdatas into buffer. 167d68c78b8Schristos */ 168d68c78b8Schristos ttl = maxttl; 169d68c78b8Schristos trust = 0xffff; 170d68c78b8Schristos isc_buffer_init(&buffer, data, sizeof(data)); 1715606745fSchristos if (message->counts[DNS_SECTION_AUTHORITY]) { 172d68c78b8Schristos result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 1735606745fSchristos } else { 174d68c78b8Schristos result = ISC_R_NOMORE; 1755606745fSchristos } 176d68c78b8Schristos while (result == ISC_R_SUCCESS) { 177d68c78b8Schristos name = NULL; 1785606745fSchristos dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 179*bcda20f6Schristos if (name->attributes.ncache) { 180d68c78b8Schristos for (rdataset = ISC_LIST_HEAD(name->list); 181d68c78b8Schristos rdataset != NULL; 1825606745fSchristos rdataset = ISC_LIST_NEXT(rdataset, link)) 1835606745fSchristos { 184d68c78b8Schristos if ((rdataset->attributes & 185903adeddSchristos DNS_RDATASETATTR_NCACHE) == 0) 186903adeddSchristos { 187d68c78b8Schristos continue; 1885606745fSchristos } 189d68c78b8Schristos type = rdataset->type; 1905606745fSchristos if (type == dns_rdatatype_rrsig) { 191d68c78b8Schristos type = rdataset->covers; 1925606745fSchristos } 193d68c78b8Schristos if (type == dns_rdatatype_soa || 194d68c78b8Schristos type == dns_rdatatype_nsec || 1955606745fSchristos type == dns_rdatatype_nsec3) 1965606745fSchristos { 197d4a20c3eSchristos if (ttl > rdataset->ttl) { 198d68c78b8Schristos ttl = rdataset->ttl; 199d4a20c3eSchristos } 200d4a20c3eSchristos if (ttl < minttl) { 201d4a20c3eSchristos ttl = minttl; 202d4a20c3eSchristos } 203d4a20c3eSchristos if (trust > rdataset->trust) { 204d68c78b8Schristos trust = rdataset->trust; 205d4a20c3eSchristos } 206d68c78b8Schristos /* 207d68c78b8Schristos * Copy the owner name to the buffer. 208d68c78b8Schristos */ 209d68c78b8Schristos dns_name_toregion(name, &r); 210d68c78b8Schristos result = isc_buffer_copyregion(&buffer, 211d68c78b8Schristos &r); 2125606745fSchristos if (result != ISC_R_SUCCESS) { 213*bcda20f6Schristos return result; 2145606745fSchristos } 215d68c78b8Schristos /* 216d68c78b8Schristos * Copy the type to the buffer. 217d68c78b8Schristos */ 2185606745fSchristos isc_buffer_availableregion(&buffer, &r); 2195606745fSchristos if (r.length < 3) { 220*bcda20f6Schristos return ISC_R_NOSPACE; 2215606745fSchristos } 222d68c78b8Schristos isc_buffer_putuint16(&buffer, 223d68c78b8Schristos rdataset->type); 2245606745fSchristos isc_buffer_putuint8( 2255606745fSchristos &buffer, 226d68c78b8Schristos (unsigned char)rdataset->trust); 227d68c78b8Schristos /* 228d68c78b8Schristos * Copy the rdataset into the buffer. 229d68c78b8Schristos */ 230d68c78b8Schristos result = copy_rdataset(rdataset, 231d68c78b8Schristos &buffer); 2325606745fSchristos if (result != ISC_R_SUCCESS) { 233*bcda20f6Schristos return result; 2345606745fSchristos } 235d68c78b8Schristos 2365606745fSchristos if (next >= DNS_NCACHE_RDATA) { 237*bcda20f6Schristos return ISC_R_NOSPACE; 2385606745fSchristos } 239d68c78b8Schristos dns_rdata_init(&rdata[next]); 240d68c78b8Schristos isc_buffer_remainingregion(&buffer, &r); 241d68c78b8Schristos rdata[next].data = r.base; 242d68c78b8Schristos rdata[next].length = r.length; 243d68c78b8Schristos rdata[next].rdclass = 244d68c78b8Schristos ncrdatalist.rdclass; 245d68c78b8Schristos rdata[next].type = 0; 246d68c78b8Schristos rdata[next].flags = 0; 247d68c78b8Schristos ISC_LIST_APPEND(ncrdatalist.rdata, 248d68c78b8Schristos &rdata[next], link); 249d68c78b8Schristos isc_buffer_forward(&buffer, r.length); 250d68c78b8Schristos next++; 251d68c78b8Schristos } 252d68c78b8Schristos } 253d68c78b8Schristos } 254d68c78b8Schristos result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); 255d68c78b8Schristos } 2565606745fSchristos if (result != ISC_R_NOMORE) { 257*bcda20f6Schristos return result; 2585606745fSchristos } 259d68c78b8Schristos 260d68c78b8Schristos if (trust == 0xffff) { 261d68c78b8Schristos if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 && 2625606745fSchristos message->counts[DNS_SECTION_ANSWER] == 0) 2635606745fSchristos { 264d68c78b8Schristos /* 265d68c78b8Schristos * The response has aa set and we haven't followed 266d68c78b8Schristos * any CNAME or DNAME chains. 267d68c78b8Schristos */ 268d68c78b8Schristos trust = dns_trust_authauthority; 2695606745fSchristos } else { 270d68c78b8Schristos trust = dns_trust_additional; 2715606745fSchristos } 272d68c78b8Schristos ttl = 0; 273d68c78b8Schristos } 274d68c78b8Schristos 275d68c78b8Schristos INSIST(trust != 0xffff); 276d68c78b8Schristos 277d68c78b8Schristos ncrdatalist.ttl = ttl; 278d68c78b8Schristos 279d68c78b8Schristos dns_rdataset_init(&ncrdataset); 280*bcda20f6Schristos dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset); 2815606745fSchristos if (!secure && trust > dns_trust_answer) { 282d68c78b8Schristos trust = dns_trust_answer; 2835606745fSchristos } 284d68c78b8Schristos ncrdataset.trust = trust; 285d68c78b8Schristos ncrdataset.attributes |= DNS_RDATASETATTR_NEGATIVE; 2865606745fSchristos if (message->rcode == dns_rcode_nxdomain) { 287d68c78b8Schristos ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN; 2885606745fSchristos } 2895606745fSchristos if (optout) { 290d68c78b8Schristos ncrdataset.attributes |= DNS_RDATASETATTR_OPTOUT; 2915606745fSchristos } 292d68c78b8Schristos 293*bcda20f6Schristos return dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, 0, 294*bcda20f6Schristos addedrdataset); 295d68c78b8Schristos } 296d68c78b8Schristos 297d68c78b8Schristos isc_result_t 298d68c78b8Schristos dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx, 299d68c78b8Schristos isc_buffer_t *target, unsigned int options, 3005606745fSchristos unsigned int *countp) { 301d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 302d68c78b8Schristos isc_result_t result; 303d68c78b8Schristos isc_region_t remaining, tavailable; 304d68c78b8Schristos isc_buffer_t source, savedbuffer, rdlen; 305d68c78b8Schristos dns_name_t name; 306d68c78b8Schristos dns_rdatatype_t type; 307d68c78b8Schristos unsigned int i, rcount, count; 308d68c78b8Schristos 309d68c78b8Schristos /* 310d68c78b8Schristos * Convert the negative caching rdataset 'rdataset' to wire format, 311d68c78b8Schristos * compressing names as specified in 'cctx', and storing the result in 312d68c78b8Schristos * 'target'. 313d68c78b8Schristos */ 314d68c78b8Schristos 315d68c78b8Schristos REQUIRE(rdataset != NULL); 316d68c78b8Schristos REQUIRE(rdataset->type == 0); 317d68c78b8Schristos REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); 318d68c78b8Schristos 319d68c78b8Schristos savedbuffer = *target; 320d68c78b8Schristos count = 0; 321d68c78b8Schristos 322d68c78b8Schristos result = dns_rdataset_first(rdataset); 323d68c78b8Schristos while (result == ISC_R_SUCCESS) { 324d68c78b8Schristos dns_rdataset_current(rdataset, &rdata); 325d68c78b8Schristos isc_buffer_init(&source, rdata.data, rdata.length); 326d68c78b8Schristos isc_buffer_add(&source, rdata.length); 327d68c78b8Schristos dns_name_init(&name, NULL); 328d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 329d68c78b8Schristos dns_name_fromregion(&name, &remaining); 330d68c78b8Schristos INSIST(remaining.length >= name.length); 331d68c78b8Schristos isc_buffer_forward(&source, name.length); 332d68c78b8Schristos remaining.length -= name.length; 333d68c78b8Schristos 334d68c78b8Schristos INSIST(remaining.length >= 5); 335d68c78b8Schristos type = isc_buffer_getuint16(&source); 336d68c78b8Schristos isc_buffer_forward(&source, 1); 337d68c78b8Schristos rcount = isc_buffer_getuint16(&source); 338d68c78b8Schristos 339d68c78b8Schristos for (i = 0; i < rcount; i++) { 340d68c78b8Schristos /* 341d68c78b8Schristos * Get the length of this rdata and set up an 342d68c78b8Schristos * rdata structure for it. 343d68c78b8Schristos */ 344d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 345d68c78b8Schristos INSIST(remaining.length >= 2); 346d68c78b8Schristos dns_rdata_reset(&rdata); 347d68c78b8Schristos rdata.length = isc_buffer_getuint16(&source); 348d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 349d68c78b8Schristos rdata.data = remaining.base; 350d68c78b8Schristos rdata.type = type; 351d68c78b8Schristos rdata.rdclass = rdataset->rdclass; 352d68c78b8Schristos INSIST(remaining.length >= rdata.length); 353d68c78b8Schristos isc_buffer_forward(&source, rdata.length); 354d68c78b8Schristos 355d68c78b8Schristos if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 && 356d68c78b8Schristos dns_rdatatype_isdnssec(type)) 3575606745fSchristos { 358d68c78b8Schristos continue; 3595606745fSchristos } 360d68c78b8Schristos 361d68c78b8Schristos /* 362d68c78b8Schristos * Write the name. 363d68c78b8Schristos */ 364*bcda20f6Schristos dns_compress_setpermitted(cctx, true); 365*bcda20f6Schristos result = dns_name_towire(&name, cctx, target, NULL); 3665606745fSchristos if (result != ISC_R_SUCCESS) { 367d68c78b8Schristos goto rollback; 3685606745fSchristos } 369d68c78b8Schristos 370d68c78b8Schristos /* 371d68c78b8Schristos * See if we have space for type, class, ttl, and 372d68c78b8Schristos * rdata length. Write the type, class, and ttl. 373d68c78b8Schristos */ 374d68c78b8Schristos isc_buffer_availableregion(target, &tavailable); 375d68c78b8Schristos if (tavailable.length < 10) { 376d68c78b8Schristos result = ISC_R_NOSPACE; 377d68c78b8Schristos goto rollback; 378d68c78b8Schristos } 379d68c78b8Schristos isc_buffer_putuint16(target, type); 380d68c78b8Schristos isc_buffer_putuint16(target, rdataset->rdclass); 381d68c78b8Schristos isc_buffer_putuint32(target, rdataset->ttl); 382d68c78b8Schristos 383d68c78b8Schristos /* 384d68c78b8Schristos * Save space for rdata length. 385d68c78b8Schristos */ 386d68c78b8Schristos rdlen = *target; 387d68c78b8Schristos isc_buffer_add(target, 2); 388d68c78b8Schristos 389d68c78b8Schristos /* 390d68c78b8Schristos * Write the rdata. 391d68c78b8Schristos */ 392d68c78b8Schristos result = dns_rdata_towire(&rdata, cctx, target); 3935606745fSchristos if (result != ISC_R_SUCCESS) { 394d68c78b8Schristos goto rollback; 3955606745fSchristos } 396d68c78b8Schristos 397d68c78b8Schristos /* 398d68c78b8Schristos * Set the rdata length field to the compressed 399d68c78b8Schristos * length. 400d68c78b8Schristos */ 401d68c78b8Schristos INSIST((target->used >= rdlen.used + 2) && 402d68c78b8Schristos (target->used - rdlen.used - 2 < 65536)); 4035606745fSchristos isc_buffer_putuint16( 4045606745fSchristos &rdlen, 4055606745fSchristos (uint16_t)(target->used - rdlen.used - 2)); 406d68c78b8Schristos 407d68c78b8Schristos count++; 408d68c78b8Schristos } 409d68c78b8Schristos INSIST(isc_buffer_remaininglength(&source) == 0); 410d68c78b8Schristos result = dns_rdataset_next(rdataset); 411d68c78b8Schristos dns_rdata_reset(&rdata); 412d68c78b8Schristos } 4135606745fSchristos if (result != ISC_R_NOMORE) { 414d68c78b8Schristos goto rollback; 4155606745fSchristos } 416d68c78b8Schristos 417d68c78b8Schristos *countp = count; 418d68c78b8Schristos 419*bcda20f6Schristos return ISC_R_SUCCESS; 420d68c78b8Schristos 421d68c78b8Schristos rollback: 422*bcda20f6Schristos dns_compress_rollback(cctx, savedbuffer.used); 423d68c78b8Schristos *countp = 0; 424d68c78b8Schristos *target = savedbuffer; 425d68c78b8Schristos 426*bcda20f6Schristos return result; 427d68c78b8Schristos } 428d68c78b8Schristos 429d68c78b8Schristos static void 430*bcda20f6Schristos rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) { 431d68c78b8Schristos UNUSED(rdataset); 432d68c78b8Schristos } 433d68c78b8Schristos 434d68c78b8Schristos static isc_result_t 435d68c78b8Schristos rdataset_first(dns_rdataset_t *rdataset) { 436*bcda20f6Schristos unsigned char *raw; 437d68c78b8Schristos unsigned int count; 438d68c78b8Schristos 439*bcda20f6Schristos raw = rdataset->ncache.raw; 440d68c78b8Schristos count = raw[0] * 256 + raw[1]; 441d68c78b8Schristos if (count == 0) { 442*bcda20f6Schristos rdataset->ncache.iter_pos = NULL; 443*bcda20f6Schristos return ISC_R_NOMORE; 444d68c78b8Schristos } 445d68c78b8Schristos /* 446*bcda20f6Schristos * iter_count is the number of rdata beyond the cursor position, 447*bcda20f6Schristos * so we decrement the total count by one before storing it. 448d68c78b8Schristos */ 449*bcda20f6Schristos rdataset->ncache.iter_pos = raw + 2; 450*bcda20f6Schristos rdataset->ncache.iter_count = count - 1; 451*bcda20f6Schristos return ISC_R_SUCCESS; 452d68c78b8Schristos } 453d68c78b8Schristos 454d68c78b8Schristos static isc_result_t 455d68c78b8Schristos rdataset_next(dns_rdataset_t *rdataset) { 456d68c78b8Schristos unsigned int count; 457d68c78b8Schristos unsigned int length; 458d68c78b8Schristos unsigned char *raw; 459d68c78b8Schristos 460*bcda20f6Schristos raw = rdataset->ncache.iter_pos; 461*bcda20f6Schristos count = rdataset->ncache.iter_count; 4625606745fSchristos if (count == 0) { 463*bcda20f6Schristos rdataset->ncache.iter_pos = NULL; 464*bcda20f6Schristos return ISC_R_NOMORE; 4655606745fSchristos } 466d68c78b8Schristos 467*bcda20f6Schristos length = raw[0] * 256 + raw[1]; 468*bcda20f6Schristos rdataset->ncache.iter_pos = raw + 2 + length; 469*bcda20f6Schristos rdataset->ncache.iter_count = count - 1; 470*bcda20f6Schristos return ISC_R_SUCCESS; 471d68c78b8Schristos } 472d68c78b8Schristos 473d68c78b8Schristos static void 474d68c78b8Schristos rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 475*bcda20f6Schristos unsigned char *raw; 476d68c78b8Schristos isc_region_t r; 477d68c78b8Schristos 478*bcda20f6Schristos raw = rdataset->ncache.iter_pos; 479d68c78b8Schristos REQUIRE(raw != NULL); 480d68c78b8Schristos 481d68c78b8Schristos r.length = raw[0] * 256 + raw[1]; 482*bcda20f6Schristos r.base = raw + 2; 483d68c78b8Schristos dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); 484d68c78b8Schristos } 485d68c78b8Schristos 486d68c78b8Schristos static void 487*bcda20f6Schristos rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) { 488d68c78b8Schristos *target = *source; 489*bcda20f6Schristos target->ncache.iter_pos = NULL; 490*bcda20f6Schristos target->ncache.iter_count = 0; 491d68c78b8Schristos } 492d68c78b8Schristos 493d68c78b8Schristos static unsigned int 494d68c78b8Schristos rdataset_count(dns_rdataset_t *rdataset) { 495*bcda20f6Schristos unsigned char *raw; 496d68c78b8Schristos unsigned int count; 497d68c78b8Schristos 498*bcda20f6Schristos raw = rdataset->ncache.raw; 499d68c78b8Schristos count = raw[0] * 256 + raw[1]; 500d68c78b8Schristos 501*bcda20f6Schristos return count; 502d68c78b8Schristos } 503d68c78b8Schristos 504d68c78b8Schristos static void 505d68c78b8Schristos rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) { 506*bcda20f6Schristos atomic_uchar *raw; 507d68c78b8Schristos 508*bcda20f6Schristos raw = (atomic_uchar *)rdataset->ncache.raw; 509*bcda20f6Schristos atomic_store_relaxed(&raw[-1], (unsigned char)trust); 5108596601aSchristos rdataset->trust = trust; 511d68c78b8Schristos } 512d68c78b8Schristos 513d68c78b8Schristos static dns_rdatasetmethods_t rdataset_methods = { 514*bcda20f6Schristos .disassociate = rdataset_disassociate, 515*bcda20f6Schristos .first = rdataset_first, 516*bcda20f6Schristos .next = rdataset_next, 517*bcda20f6Schristos .current = rdataset_current, 518*bcda20f6Schristos .clone = rdataset_clone, 519*bcda20f6Schristos .count = rdataset_count, 520*bcda20f6Schristos .settrust = rdataset_settrust, 521d68c78b8Schristos }; 522d68c78b8Schristos 523d68c78b8Schristos isc_result_t 524d68c78b8Schristos dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, 5255606745fSchristos dns_rdatatype_t type, dns_rdataset_t *rdataset) { 526d68c78b8Schristos isc_result_t result; 527d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 528d68c78b8Schristos isc_region_t remaining; 529d68c78b8Schristos isc_buffer_t source; 530d68c78b8Schristos dns_name_t tname; 531d68c78b8Schristos dns_rdatatype_t ttype; 532d68c78b8Schristos dns_trust_t trust = dns_trust_none; 533d68c78b8Schristos dns_rdataset_t rclone; 534d68c78b8Schristos 535d68c78b8Schristos REQUIRE(ncacherdataset != NULL); 536*bcda20f6Schristos REQUIRE(DNS_RDATASET_VALID(ncacherdataset)); 537d68c78b8Schristos REQUIRE(ncacherdataset->type == 0); 538d68c78b8Schristos REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); 539d68c78b8Schristos REQUIRE(name != NULL); 540d68c78b8Schristos REQUIRE(!dns_rdataset_isassociated(rdataset)); 541d68c78b8Schristos REQUIRE(type != dns_rdatatype_rrsig); 542d68c78b8Schristos 543d68c78b8Schristos dns_rdataset_init(&rclone); 544d68c78b8Schristos dns_rdataset_clone(ncacherdataset, &rclone); 545d68c78b8Schristos result = dns_rdataset_first(&rclone); 546d68c78b8Schristos while (result == ISC_R_SUCCESS) { 547d68c78b8Schristos dns_rdataset_current(&rclone, &rdata); 548d68c78b8Schristos isc_buffer_init(&source, rdata.data, rdata.length); 549d68c78b8Schristos isc_buffer_add(&source, rdata.length); 550d68c78b8Schristos dns_name_init(&tname, NULL); 551d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 552d68c78b8Schristos dns_name_fromregion(&tname, &remaining); 553d68c78b8Schristos INSIST(remaining.length >= tname.length); 554d68c78b8Schristos isc_buffer_forward(&source, tname.length); 555d68c78b8Schristos remaining.length -= tname.length; 556d68c78b8Schristos 557d68c78b8Schristos INSIST(remaining.length >= 3); 558d68c78b8Schristos ttype = isc_buffer_getuint16(&source); 559d68c78b8Schristos 560d68c78b8Schristos if (ttype == type && dns_name_equal(&tname, name)) { 561*bcda20f6Schristos trust = atomic_getuint8(&source); 562d68c78b8Schristos INSIST(trust <= dns_trust_ultimate); 563d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 564d68c78b8Schristos break; 565d68c78b8Schristos } 566d68c78b8Schristos result = dns_rdataset_next(&rclone); 567d68c78b8Schristos dns_rdata_reset(&rdata); 568d68c78b8Schristos } 569d68c78b8Schristos dns_rdataset_disassociate(&rclone); 5705606745fSchristos if (result == ISC_R_NOMORE) { 571*bcda20f6Schristos return ISC_R_NOTFOUND; 5725606745fSchristos } 5735606745fSchristos if (result != ISC_R_SUCCESS) { 574*bcda20f6Schristos return result; 5755606745fSchristos } 576d68c78b8Schristos 577d68c78b8Schristos INSIST(remaining.length != 0); 578d68c78b8Schristos 579d68c78b8Schristos rdataset->methods = &rdataset_methods; 580d68c78b8Schristos rdataset->rdclass = ncacherdataset->rdclass; 581d68c78b8Schristos rdataset->type = type; 582d68c78b8Schristos rdataset->covers = 0; 583d68c78b8Schristos rdataset->ttl = ncacherdataset->ttl; 584d68c78b8Schristos rdataset->trust = trust; 585*bcda20f6Schristos rdataset->ncache.raw = remaining.base; 586*bcda20f6Schristos rdataset->ncache.iter_pos = NULL; 587*bcda20f6Schristos rdataset->ncache.iter_count = 0; 588d68c78b8Schristos 589*bcda20f6Schristos return ISC_R_SUCCESS; 590d68c78b8Schristos } 591d68c78b8Schristos 592d68c78b8Schristos isc_result_t 593d68c78b8Schristos dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, 5945606745fSchristos dns_rdatatype_t covers, dns_rdataset_t *rdataset) { 595d68c78b8Schristos dns_name_t tname; 596d68c78b8Schristos dns_rdata_rrsig_t rrsig; 597d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 598d68c78b8Schristos dns_rdataset_t rclone; 599d68c78b8Schristos dns_rdatatype_t type; 600d68c78b8Schristos dns_trust_t trust = dns_trust_none; 601d68c78b8Schristos isc_buffer_t source; 602d68c78b8Schristos isc_region_t remaining, sigregion; 603d68c78b8Schristos isc_result_t result; 604d68c78b8Schristos unsigned char *raw; 605d68c78b8Schristos unsigned int count; 606d68c78b8Schristos 607d68c78b8Schristos REQUIRE(ncacherdataset != NULL); 608d68c78b8Schristos REQUIRE(ncacherdataset->type == 0); 609d68c78b8Schristos REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); 610d68c78b8Schristos REQUIRE(name != NULL); 611d68c78b8Schristos REQUIRE(!dns_rdataset_isassociated(rdataset)); 612d68c78b8Schristos 613d68c78b8Schristos dns_rdataset_init(&rclone); 614d68c78b8Schristos dns_rdataset_clone(ncacherdataset, &rclone); 615d68c78b8Schristos result = dns_rdataset_first(&rclone); 616d68c78b8Schristos while (result == ISC_R_SUCCESS) { 617d68c78b8Schristos dns_rdataset_current(&rclone, &rdata); 618d68c78b8Schristos isc_buffer_init(&source, rdata.data, rdata.length); 619d68c78b8Schristos isc_buffer_add(&source, rdata.length); 620d68c78b8Schristos dns_name_init(&tname, NULL); 621d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 622d68c78b8Schristos dns_name_fromregion(&tname, &remaining); 623d68c78b8Schristos INSIST(remaining.length >= tname.length); 624d68c78b8Schristos isc_buffer_forward(&source, tname.length); 625d68c78b8Schristos isc_region_consume(&remaining, tname.length); 626d68c78b8Schristos 627d68c78b8Schristos INSIST(remaining.length >= 2); 628d68c78b8Schristos type = isc_buffer_getuint16(&source); 629d68c78b8Schristos isc_region_consume(&remaining, 2); 630d68c78b8Schristos 631d68c78b8Schristos if (type != dns_rdatatype_rrsig || 632903adeddSchristos !dns_name_equal(&tname, name)) 633903adeddSchristos { 634d68c78b8Schristos result = dns_rdataset_next(&rclone); 635d68c78b8Schristos dns_rdata_reset(&rdata); 636d68c78b8Schristos continue; 637d68c78b8Schristos } 638d68c78b8Schristos 639d68c78b8Schristos INSIST(remaining.length >= 1); 640*bcda20f6Schristos trust = atomic_getuint8(&source); 641d68c78b8Schristos INSIST(trust <= dns_trust_ultimate); 642d68c78b8Schristos isc_region_consume(&remaining, 1); 643d68c78b8Schristos 644d68c78b8Schristos raw = remaining.base; 645d68c78b8Schristos count = raw[0] * 256 + raw[1]; 646d68c78b8Schristos INSIST(count > 0); 647d68c78b8Schristos raw += 2; 648d68c78b8Schristos sigregion.length = raw[0] * 256 + raw[1]; 649d68c78b8Schristos raw += 2; 650d68c78b8Schristos sigregion.base = raw; 651d68c78b8Schristos dns_rdata_reset(&rdata); 652d68c78b8Schristos dns_rdata_fromregion(&rdata, rdataset->rdclass, 653d68c78b8Schristos dns_rdatatype_rrsig, &sigregion); 654d68c78b8Schristos (void)dns_rdata_tostruct(&rdata, &rrsig, NULL); 655d68c78b8Schristos if (rrsig.covered == covers) { 656d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 657d68c78b8Schristos break; 658d68c78b8Schristos } 659d68c78b8Schristos 660d68c78b8Schristos result = dns_rdataset_next(&rclone); 661d68c78b8Schristos dns_rdata_reset(&rdata); 662d68c78b8Schristos } 663d68c78b8Schristos dns_rdataset_disassociate(&rclone); 6645606745fSchristos if (result == ISC_R_NOMORE) { 665*bcda20f6Schristos return ISC_R_NOTFOUND; 6665606745fSchristos } 6675606745fSchristos if (result != ISC_R_SUCCESS) { 668*bcda20f6Schristos return result; 6695606745fSchristos } 670d68c78b8Schristos 671d68c78b8Schristos INSIST(remaining.length != 0); 672d68c78b8Schristos 673d68c78b8Schristos rdataset->methods = &rdataset_methods; 674d68c78b8Schristos rdataset->rdclass = ncacherdataset->rdclass; 675d68c78b8Schristos rdataset->type = dns_rdatatype_rrsig; 676d68c78b8Schristos rdataset->covers = covers; 677d68c78b8Schristos rdataset->ttl = ncacherdataset->ttl; 678d68c78b8Schristos rdataset->trust = trust; 679*bcda20f6Schristos rdataset->ncache.raw = remaining.base; 680*bcda20f6Schristos rdataset->ncache.iter_pos = NULL; 681*bcda20f6Schristos rdataset->ncache.iter_count = 0; 682d68c78b8Schristos 683*bcda20f6Schristos return ISC_R_SUCCESS; 684d68c78b8Schristos } 685d68c78b8Schristos 686d68c78b8Schristos void 687d68c78b8Schristos dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found, 6885606745fSchristos dns_rdataset_t *rdataset) { 689d68c78b8Schristos dns_rdata_t rdata = DNS_RDATA_INIT; 690d68c78b8Schristos dns_trust_t trust; 691d68c78b8Schristos isc_region_t remaining, sigregion; 692d68c78b8Schristos isc_buffer_t source; 693d68c78b8Schristos dns_name_t tname; 694*bcda20f6Schristos dns_rdatatype_t type, covers; 695d68c78b8Schristos unsigned int count; 696d68c78b8Schristos dns_rdata_rrsig_t rrsig; 697d68c78b8Schristos unsigned char *raw; 698d68c78b8Schristos 699d68c78b8Schristos REQUIRE(ncacherdataset != NULL); 700d68c78b8Schristos REQUIRE(ncacherdataset->type == 0); 701d68c78b8Schristos REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); 702d68c78b8Schristos REQUIRE(found != NULL); 703d68c78b8Schristos REQUIRE(!dns_rdataset_isassociated(rdataset)); 704d68c78b8Schristos 705d68c78b8Schristos dns_rdataset_current(ncacherdataset, &rdata); 706d68c78b8Schristos isc_buffer_init(&source, rdata.data, rdata.length); 707d68c78b8Schristos isc_buffer_add(&source, rdata.length); 708d68c78b8Schristos 709d68c78b8Schristos dns_name_init(&tname, NULL); 710d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 711d68c78b8Schristos dns_name_fromregion(found, &remaining); 712d68c78b8Schristos INSIST(remaining.length >= found->length); 713d68c78b8Schristos isc_buffer_forward(&source, found->length); 714d68c78b8Schristos remaining.length -= found->length; 715d68c78b8Schristos 716d68c78b8Schristos INSIST(remaining.length >= 5); 717d68c78b8Schristos type = isc_buffer_getuint16(&source); 718*bcda20f6Schristos trust = atomic_getuint8(&source); 719d68c78b8Schristos INSIST(trust <= dns_trust_ultimate); 720d68c78b8Schristos isc_buffer_remainingregion(&source, &remaining); 721d68c78b8Schristos 722*bcda20f6Schristos covers = 0; 723d68c78b8Schristos if (type == dns_rdatatype_rrsig) { 724d68c78b8Schristos /* 725d68c78b8Schristos * Extract covers from RRSIG. 726d68c78b8Schristos */ 727d68c78b8Schristos raw = remaining.base; 728d68c78b8Schristos count = raw[0] * 256 + raw[1]; 729d68c78b8Schristos INSIST(count > 0); 730d68c78b8Schristos raw += 2; 731d68c78b8Schristos sigregion.length = raw[0] * 256 + raw[1]; 732d68c78b8Schristos raw += 2; 733d68c78b8Schristos sigregion.base = raw; 734d68c78b8Schristos dns_rdata_reset(&rdata); 735bb5aa156Schristos dns_rdata_fromregion(&rdata, ncacherdataset->rdclass, type, 7365606745fSchristos &sigregion); 737d68c78b8Schristos (void)dns_rdata_tostruct(&rdata, &rrsig, NULL); 738*bcda20f6Schristos covers = rrsig.covered; 7395606745fSchristos } 740*bcda20f6Schristos 741*bcda20f6Schristos rdataset->methods = &rdataset_methods; 742*bcda20f6Schristos rdataset->rdclass = ncacherdataset->rdclass; 743*bcda20f6Schristos rdataset->type = type; 744*bcda20f6Schristos rdataset->covers = covers; 745d68c78b8Schristos rdataset->ttl = ncacherdataset->ttl; 746d68c78b8Schristos rdataset->trust = trust; 747*bcda20f6Schristos rdataset->ncache.raw = remaining.base; 748*bcda20f6Schristos rdataset->ncache.iter_pos = NULL; 749*bcda20f6Schristos rdataset->ncache.iter_count = 0; 750d68c78b8Schristos } 751