| 1 | /* |
| 2 | * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") |
| 3 | * Copyright (C) 1999-2003 Internet Software Consortium. |
| 4 | * |
| 5 | * Permission to use, copy, modify, and/or distribute this software for any |
| 6 | * purpose with or without fee is hereby granted, provided that the above |
| 7 | * copyright notice and this permission notice appear in all copies. |
| 8 | * |
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH |
| 10 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| 11 | * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, |
| 12 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
| 13 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
| 14 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 15 | * PERFORMANCE OF THIS SOFTWARE. |
| 16 | */ |
| 17 | |
| 18 | /* $Id: ncache.c,v 1.41 2007/06/19 23:47:16 tbox Exp $ */ |
| 19 | |
| 20 | /*! \file */ |
| 21 | |
| 22 | #include <config.h> |
| 23 | |
| 24 | #include <isc/buffer.h> |
| 25 | #include <isc/util.h> |
| 26 | |
| 27 | #include <dns/db.h> |
| 28 | #include <dns/message.h> |
| 29 | #include <dns/ncache.h> |
| 30 | #include <dns/rdata.h> |
| 31 | #include <dns/rdatalist.h> |
| 32 | #include <dns/rdataset.h> |
| 33 | |
| 34 | /* |
| 35 | * The format of an ncache rdata is a sequence of one or more records of |
| 36 | * the following format: |
| 37 | * |
| 38 | * owner name |
| 39 | * type |
| 40 | * rdata count |
| 41 | * rdata length These two occur 'rdata count' |
| 42 | * rdata times. |
| 43 | * |
| 44 | */ |
| 45 | |
| 46 | static inline isc_result_t |
| 47 | copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) { |
| 48 | isc_result_t result; |
| 49 | unsigned int count; |
| 50 | isc_region_t ar, r; |
| 51 | dns_rdata_t rdata = DNS_RDATA_INIT; |
| 52 | |
| 53 | /* |
| 54 | * Copy the rdataset count to the buffer. |
| 55 | */ |
| 56 | isc_buffer_availableregion(buffer, &ar); |
| 57 | if (ar.length < 2) |
| 58 | return (ISC_R_NOSPACE); |
| 59 | count = dns_rdataset_count(rdataset); |
| 60 | INSIST(count <= 65535); |
| 61 | isc_buffer_putuint16(buffer, (isc_uint16_t)count); |
| 62 | |
| 63 | result = dns_rdataset_first(rdataset); |
| 64 | while (result == ISC_R_SUCCESS) { |
| 65 | dns_rdataset_current(rdataset, &rdata); |
| 66 | dns_rdata_toregion(&rdata, &r); |
| 67 | INSIST(r.length <= 65535); |
| 68 | isc_buffer_availableregion(buffer, &ar); |
| 69 | if (ar.length < 2) |
| 70 | return (ISC_R_NOSPACE); |
| 71 | /* |
| 72 | * Copy the rdata length to the buffer. |
| 73 | */ |
| 74 | isc_buffer_putuint16(buffer, (isc_uint16_t)r.length); |
| 75 | /* |
| 76 | * Copy the rdata to the buffer. |
| 77 | */ |
| 78 | result = isc_buffer_copyregion(buffer, &r); |
| 79 | if (result != ISC_R_SUCCESS) |
| 80 | return (result); |
| 81 | dns_rdata_reset(&rdata); |
| 82 | result = dns_rdataset_next(rdataset); |
| 83 | } |
| 84 | if (result != ISC_R_NOMORE) |
| 85 | return (result); |
| 86 | |
| 87 | return (ISC_R_SUCCESS); |
| 88 | } |
| 89 | |
| 90 | isc_result_t |
| 91 | dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, |
| 92 | dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, |
| 93 | dns_rdataset_t *addedrdataset) |
| 94 | { |
| 95 | isc_result_t result; |
| 96 | isc_buffer_t buffer; |
| 97 | isc_region_t r; |
| 98 | dns_rdataset_t *rdataset; |
| 99 | dns_rdatatype_t type; |
| 100 | dns_name_t *name; |
| 101 | dns_ttl_t ttl; |
| 102 | dns_trust_t trust; |
| 103 | dns_rdata_t rdata = DNS_RDATA_INIT; |
| 104 | dns_rdataset_t ncrdataset; |
| 105 | dns_rdatalist_t ncrdatalist; |
| 106 | unsigned char data[4096]; |
| 107 | |
| 108 | /* |
| 109 | * Convert the authority data from 'message' into a negative cache |
| 110 | * rdataset, and store it in 'cache' at 'node'. |
| 111 | */ |
| 112 | |
| 113 | REQUIRE(message != NULL); |
| 114 | |
| 115 | /* |
| 116 | * We assume that all data in the authority section has been |
| 117 | * validated by the caller. |
| 118 | */ |
| 119 | |
| 120 | /* |
| 121 | * First, build an ncache rdata in buffer. |
| 122 | */ |
| 123 | ttl = maxttl; |
| 124 | trust = 0xffff; |
| 125 | isc_buffer_init(&buffer, data, sizeof(data)); |
| 126 | if (message->counts[DNS_SECTION_AUTHORITY]) |
| 127 | result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); |
| 128 | else |
| 129 | result = ISC_R_NOMORE; |
| 130 | while (result == ISC_R_SUCCESS) { |
| 131 | name = NULL; |
| 132 | dns_message_currentname(message, DNS_SECTION_AUTHORITY, |
| 133 | &name); |
| 134 | if ((name->attributes & DNS_NAMEATTR_NCACHE) != 0) { |
| 135 | for (rdataset = ISC_LIST_HEAD(name->list); |
| 136 | rdataset != NULL; |
| 137 | rdataset = ISC_LIST_NEXT(rdataset, link)) { |
| 138 | if ((rdataset->attributes & |
| 139 | DNS_RDATASETATTR_NCACHE) == 0) |
| 140 | continue; |
| 141 | type = rdataset->type; |
| 142 | if (type == dns_rdatatype_rrsig) |
| 143 | type = rdataset->covers; |
| 144 | if (type == dns_rdatatype_soa || |
| 145 | type == dns_rdatatype_nsec) { |
| 146 | if (ttl > rdataset->ttl) |
| 147 | ttl = rdataset->ttl; |
| 148 | if (trust > rdataset->trust) |
| 149 | trust = rdataset->trust; |
| 150 | /* |
| 151 | * Copy the owner name to the buffer. |
| 152 | */ |
| 153 | dns_name_toregion(name, &r); |
| 154 | result = isc_buffer_copyregion(&buffer, |
| 155 | &r); |
| 156 | if (result != ISC_R_SUCCESS) |
| 157 | return (result); |
| 158 | /* |
| 159 | * Copy the type to the buffer. |
| 160 | */ |
| 161 | isc_buffer_availableregion(&buffer, |
| 162 | &r); |
| 163 | if (r.length < 2) |
| 164 | return (ISC_R_NOSPACE); |
| 165 | isc_buffer_putuint16(&buffer, |
| 166 | rdataset->type); |
| 167 | /* |
| 168 | * Copy the rdataset into the buffer. |
| 169 | */ |
| 170 | result = copy_rdataset(rdataset, |
| 171 | &buffer); |
| 172 | if (result != ISC_R_SUCCESS) |
| 173 | return (result); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); |
| 178 | } |
| 179 | if (result != ISC_R_NOMORE) |
| 180 | return (result); |
| 181 | |
| 182 | if (trust == 0xffff) { |
| 183 | /* |
| 184 | * We didn't find any authority data from which to create a |
| 185 | * negative cache rdataset. In particular, we have no SOA. |
| 186 | * |
| 187 | * We trust that the caller wants negative caching, so this |
| 188 | * means we have a "type 3 nxdomain" or "type 3 nodata" |
| 189 | * response (see RFC2308 for details). |
| 190 | * |
| 191 | * We will now build a suitable negative cache rdataset that |
| 192 | * will cause zero bytes to be emitted when converted to |
| 193 | * wire format. |
| 194 | */ |
| 195 | |
| 196 | /* |
| 197 | * The ownername must exist, but it doesn't matter what value |
| 198 | * it has. We use the root name. |
| 199 | */ |
| 200 | dns_name_toregion(dns_rootname, &r); |
| 201 | result = isc_buffer_copyregion(&buffer, &r); |
| 202 | if (result != ISC_R_SUCCESS) |
| 203 | return (result); |
| 204 | /* |
| 205 | * Copy the type and a zero rdata count to the buffer. |
| 206 | */ |
| 207 | isc_buffer_availableregion(&buffer, &r); |
| 208 | if (r.length < 4) |
| 209 | return (ISC_R_NOSPACE); |
| 210 | isc_buffer_putuint16(&buffer, 0); |
| 211 | isc_buffer_putuint16(&buffer, 0); |
| 212 | /* |
| 213 | * RFC2308, section 5, says that negative answers without |
| 214 | * SOAs should not be cached. |
| 215 | */ |
| 216 | ttl = 0; |
| 217 | /* |
| 218 | * Set trust. |
| 219 | */ |
| 220 | if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 && |
| 221 | message->counts[DNS_SECTION_ANSWER] == 0) { |
| 222 | /* |
| 223 | * The response has aa set and we haven't followed |
| 224 | * any CNAME or DNAME chains. |
| 225 | */ |
| 226 | trust = dns_trust_authauthority; |
| 227 | } else |
| 228 | trust = dns_trust_additional; |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | * Now add it to the cache. |
| 233 | */ |
| 234 | INSIST(trust != 0xffff); |
| 235 | isc_buffer_usedregion(&buffer, &r); |
| 236 | rdata.data = r.base; |
| 237 | rdata.length = r.length; |
| 238 | rdata.rdclass = dns_db_class(cache); |
| 239 | rdata.type = 0; |
| 240 | rdata.flags = 0; |
| 241 | |
| 242 | ncrdatalist.rdclass = rdata.rdclass; |
| 243 | ncrdatalist.type = 0; |
| 244 | ncrdatalist.covers = covers; |
| 245 | ncrdatalist.ttl = ttl; |
| 246 | ISC_LIST_INIT(ncrdatalist.rdata); |
| 247 | ISC_LINK_INIT(&ncrdatalist, link); |
| 248 | |
| 249 | ISC_LIST_APPEND(ncrdatalist.rdata, &rdata, link); |
| 250 | |
| 251 | dns_rdataset_init(&ncrdataset); |
| 252 | RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset) |
| 253 | == ISC_R_SUCCESS); |
| 254 | ncrdataset.trust = trust; |
| 255 | if (message->rcode == dns_rcode_nxdomain) |
| 256 | ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN; |
| 257 | |
| 258 | return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, |
| 259 | 0, addedrdataset)); |
| 260 | } |
| 261 | |
| 262 | isc_result_t |
| 263 | dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx, |
| 264 | isc_buffer_t *target, unsigned int options, |
| 265 | unsigned int *countp) |
| 266 | { |
| 267 | dns_rdata_t rdata = DNS_RDATA_INIT; |
| 268 | isc_result_t result; |
| 269 | isc_region_t remaining, tavailable; |
| 270 | isc_buffer_t source, savedbuffer, rdlen; |
| 271 | dns_name_t name; |
| 272 | dns_rdatatype_t type; |
| 273 | unsigned int i, rcount, count; |
| 274 | |
| 275 | /* |
| 276 | * Convert the negative caching rdataset 'rdataset' to wire format, |
| 277 | * compressing names as specified in 'cctx', and storing the result in |
| 278 | * 'target'. |
| 279 | */ |
| 280 | |
| 281 | REQUIRE(rdataset != NULL); |
| 282 | REQUIRE(rdataset->type == 0); |
| 283 | |
| 284 | result = dns_rdataset_first(rdataset); |
| 285 | if (result != ISC_R_SUCCESS) |
| 286 | return (result); |
| 287 | dns_rdataset_current(rdataset, &rdata); |
| 288 | INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); |
| 289 | isc_buffer_init(&source, rdata.data, rdata.length); |
| 290 | isc_buffer_add(&source, rdata.length); |
| 291 | |
| 292 | savedbuffer = *target; |
| 293 | |
| 294 | count = 0; |
| 295 | do { |
| 296 | dns_name_init(&name, NULL); |
| 297 | isc_buffer_remainingregion(&source, &remaining); |
| 298 | dns_name_fromregion(&name, &remaining); |
| 299 | INSIST(remaining.length >= name.length); |
| 300 | isc_buffer_forward(&source, name.length); |
| 301 | remaining.length -= name.length; |
| 302 | |
| 303 | INSIST(remaining.length >= 4); |
| 304 | type = isc_buffer_getuint16(&source); |
| 305 | rcount = isc_buffer_getuint16(&source); |
| 306 | |
| 307 | for (i = 0; i < rcount; i++) { |
| 308 | /* |
| 309 | * Get the length of this rdata and set up an |
| 310 | * rdata structure for it. |
| 311 | */ |
| 312 | isc_buffer_remainingregion(&source, &remaining); |
| 313 | INSIST(remaining.length >= 2); |
| 314 | dns_rdata_reset(&rdata); |
| 315 | rdata.length = isc_buffer_getuint16(&source); |
| 316 | isc_buffer_remainingregion(&source, &remaining); |
| 317 | rdata.data = remaining.base; |
| 318 | rdata.type = type; |
| 319 | rdata.rdclass = rdataset->rdclass; |
| 320 | INSIST(remaining.length >= rdata.length); |
| 321 | isc_buffer_forward(&source, rdata.length); |
| 322 | |
| 323 | if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 && |
| 324 | dns_rdatatype_isdnssec(type)) |
| 325 | continue; |
| 326 | |
| 327 | /* |
| 328 | * Write the name. |
| 329 | */ |
| 330 | dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); |
| 331 | result = dns_name_towire(&name, cctx, target); |
| 332 | if (result != ISC_R_SUCCESS) |
| 333 | goto rollback; |
| 334 | |
| 335 | /* |
| 336 | * See if we have space for type, class, ttl, and |
| 337 | * rdata length. Write the type, class, and ttl. |
| 338 | */ |
| 339 | isc_buffer_availableregion(target, &tavailable); |
| 340 | if (tavailable.length < 10) { |
| 341 | result = ISC_R_NOSPACE; |
| 342 | goto rollback; |
| 343 | } |
| 344 | isc_buffer_putuint16(target, type); |
| 345 | isc_buffer_putuint16(target, rdataset->rdclass); |
| 346 | isc_buffer_putuint32(target, rdataset->ttl); |
| 347 | |
| 348 | /* |
| 349 | * Save space for rdata length. |
| 350 | */ |
| 351 | rdlen = *target; |
| 352 | isc_buffer_add(target, 2); |
| 353 | |
| 354 | /* |
| 355 | * Write the rdata. |
| 356 | */ |
| 357 | result = dns_rdata_towire(&rdata, cctx, target); |
| 358 | if (result != ISC_R_SUCCESS) |
| 359 | goto rollback; |
| 360 | |
| 361 | /* |
| 362 | * Set the rdata length field to the compressed |
| 363 | * length. |
| 364 | */ |
| 365 | INSIST((target->used >= rdlen.used + 2) && |
| 366 | (target->used - rdlen.used - 2 < 65536)); |
| 367 | isc_buffer_putuint16(&rdlen, |
| 368 | (isc_uint16_t)(target->used - |
| 369 | rdlen.used - 2)); |
| 370 | |
| 371 | count++; |
| 372 | } |
| 373 | isc_buffer_remainingregion(&source, &remaining); |
| 374 | } while (remaining.length > 0); |
| 375 | |
| 376 | *countp = count; |
| 377 | |
| 378 | return (ISC_R_SUCCESS); |
| 379 | |
| 380 | rollback: |
| 381 | INSIST(savedbuffer.used < 65536); |
| 382 | dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used); |
| 383 | *countp = 0; |
| 384 | *target = savedbuffer; |
| 385 | |
| 386 | return (result); |
| 387 | } |
| 388 | |
| 389 | static void |
| 390 | rdataset_disassociate(dns_rdataset_t *rdataset) { |
| 391 | UNUSED(rdataset); |
| 392 | } |
| 393 | |
| 394 | static isc_result_t |
| 395 | rdataset_first(dns_rdataset_t *rdataset) { |
| 396 | unsigned char *raw = rdataset->private3; |
| 397 | unsigned int count; |
| 398 | |
| 399 | count = raw[0] * 256 + raw[1]; |
| 400 | if (count == 0) { |
| 401 | rdataset->private5 = NULL; |
| 402 | return (ISC_R_NOMORE); |
| 403 | } |
| 404 | raw += 2; |
| 405 | /* |
| 406 | * The privateuint4 field is the number of rdata beyond the cursor |
| 407 | * position, so we decrement the total count by one before storing |
| 408 | * it. |
| 409 | */ |
| 410 | count--; |
| 411 | rdataset->privateuint4 = count; |
| 412 | rdataset->private5 = raw; |
| 413 | |
| 414 | return (ISC_R_SUCCESS); |
| 415 | } |
| 416 | |
| 417 | static isc_result_t |
| 418 | rdataset_next(dns_rdataset_t *rdataset) { |
| 419 | unsigned int count; |
| 420 | unsigned int length; |
| 421 | unsigned char *raw; |
| 422 | |
| 423 | count = rdataset->privateuint4; |
| 424 | if (count == 0) |
| 425 | return (ISC_R_NOMORE); |
| 426 | count--; |
| 427 | rdataset->privateuint4 = count; |
| 428 | raw = rdataset->private5; |
| 429 | length = raw[0] * 256 + raw[1]; |
| 430 | raw += length + 2; |
| 431 | rdataset->private5 = raw; |
| 432 | |
| 433 | return (ISC_R_SUCCESS); |
| 434 | } |
| 435 | |
| 436 | static void |
| 437 | rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { |
| 438 | unsigned char *raw = rdataset->private5; |
| 439 | isc_region_t r; |
| 440 | |
| 441 | REQUIRE(raw != NULL); |
| 442 | |
| 443 | r.length = raw[0] * 256 + raw[1]; |
| 444 | raw += 2; |
| 445 | r.base = raw; |
| 446 | dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); |
| 447 | } |
| 448 | |
| 449 | static void |
| 450 | rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { |
| 451 | *target = *source; |
| 452 | |
| 453 | /* |
| 454 | * Reset iterator state. |
| 455 | */ |
| 456 | target->privateuint4 = 0; |
| 457 | target->private5 = NULL; |
| 458 | } |
| 459 | |
| 460 | static unsigned int |
| 461 | rdataset_count(dns_rdataset_t *rdataset) { |
| 462 | unsigned char *raw = rdataset->private3; |
| 463 | unsigned int count; |
| 464 | |
| 465 | count = raw[0] * 256 + raw[1]; |
| 466 | |
| 467 | return (count); |
| 468 | } |
| 469 | |
| 470 | static dns_rdatasetmethods_t rdataset_methods = { |
| 471 | rdataset_disassociate, |
| 472 | rdataset_first, |
| 473 | rdataset_next, |
| 474 | rdataset_current, |
| 475 | rdataset_clone, |
| 476 | rdataset_count, |
| 477 | NULL, |
| 478 | NULL, |
| 479 | NULL, |
| 480 | NULL, |
| 481 | NULL |
| 482 | }; |
| 483 | |
| 484 | isc_result_t |
| 485 | dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, |
| 486 | dns_rdatatype_t type, dns_rdataset_t *rdataset) |
| 487 | { |
| 488 | isc_result_t result; |
| 489 | dns_rdata_t rdata = DNS_RDATA_INIT; |
| 490 | isc_region_t remaining; |
| 491 | isc_buffer_t source; |
| 492 | dns_name_t tname; |
| 493 | dns_rdatatype_t ttype; |
| 494 | unsigned int i, rcount; |
| 495 | isc_uint16_t length; |
| 496 | |
| 497 | REQUIRE(ncacherdataset != NULL); |
| 498 | REQUIRE(ncacherdataset->type == 0); |
| 499 | REQUIRE(name != NULL); |
| 500 | REQUIRE(!dns_rdataset_isassociated(rdataset)); |
| 501 | REQUIRE(type != dns_rdatatype_rrsig); |
| 502 | |
| 503 | result = dns_rdataset_first(ncacherdataset); |
| 504 | if (result != ISC_R_SUCCESS) |
| 505 | return (result); |
| 506 | dns_rdataset_current(ncacherdataset, &rdata); |
| 507 | INSIST(dns_rdataset_next(ncacherdataset) == ISC_R_NOMORE); |
| 508 | isc_buffer_init(&source, rdata.data, rdata.length); |
| 509 | isc_buffer_add(&source, rdata.length); |
| 510 | |
| 511 | do { |
| 512 | dns_name_init(&tname, NULL); |
| 513 | isc_buffer_remainingregion(&source, &remaining); |
| 514 | dns_name_fromregion(&tname, &remaining); |
| 515 | INSIST(remaining.length >= tname.length); |
| 516 | isc_buffer_forward(&source, tname.length); |
| 517 | remaining.length -= tname.length; |
| 518 | |
| 519 | INSIST(remaining.length >= 4); |
| 520 | ttype = isc_buffer_getuint16(&source); |
| 521 | |
| 522 | if (ttype == type && dns_name_equal(&tname, name)) { |
| 523 | isc_buffer_remainingregion(&source, &remaining); |
| 524 | break; |
| 525 | } |
| 526 | |
| 527 | rcount = isc_buffer_getuint16(&source); |
| 528 | for (i = 0; i < rcount; i++) { |
| 529 | isc_buffer_remainingregion(&source, &remaining); |
| 530 | INSIST(remaining.length >= 2); |
| 531 | length = isc_buffer_getuint16(&source); |
| 532 | isc_buffer_remainingregion(&source, &remaining); |
| 533 | INSIST(remaining.length >= length); |
| 534 | isc_buffer_forward(&source, length); |
| 535 | } |
| 536 | isc_buffer_remainingregion(&source, &remaining); |
| 537 | } while (remaining.length > 0); |
| 538 | |
| 539 | if (remaining.length == 0) |
| 540 | return (ISC_R_NOTFOUND); |
| 541 | |
| 542 | rdataset->methods = &rdataset_methods; |
| 543 | rdataset->rdclass = ncacherdataset->rdclass; |
| 544 | rdataset->type = type; |
| 545 | rdataset->covers = 0; |
| 546 | rdataset->ttl = ncacherdataset->ttl; |
| 547 | rdataset->trust = ncacherdataset->trust; |
| 548 | rdataset->private1 = NULL; |
| 549 | rdataset->private2 = NULL; |
| 550 | |
| 551 | rdataset->private3 = remaining.base; |
| 552 | |
| 553 | /* |
| 554 | * Reset iterator state. |
| 555 | */ |
| 556 | rdataset->privateuint4 = 0; |
| 557 | rdataset->private5 = NULL; |
| 558 | return (ISC_R_SUCCESS); |
| 559 | } |