2 * Copyright (C) 2004, 2005, 2007, 2010 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
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.
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.
18 /* $Id: ncache.c,v 1.41.288.2 2010/02/25 10:56:01 tbox Exp $ */
24 #include <isc/buffer.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>
35 * The format of an ncache rdata is a sequence of one or more records of
36 * the following format:
41 * rdata length These two occur 'rdata count'
46 static inline isc_result_t
47 copy_rdataset(dns_rdataset_t
*rdataset
, isc_buffer_t
*buffer
) {
51 dns_rdata_t rdata
= DNS_RDATA_INIT
;
54 * Copy the rdataset count to the buffer.
56 isc_buffer_availableregion(buffer
, &ar
);
58 return (ISC_R_NOSPACE
);
59 count
= dns_rdataset_count(rdataset
);
60 INSIST(count
<= 65535);
61 isc_buffer_putuint16(buffer
, (isc_uint16_t
)count
);
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
);
70 return (ISC_R_NOSPACE
);
72 * Copy the rdata length to the buffer.
74 isc_buffer_putuint16(buffer
, (isc_uint16_t
)r
.length
);
76 * Copy the rdata to the buffer.
78 result
= isc_buffer_copyregion(buffer
, &r
);
79 if (result
!= ISC_R_SUCCESS
)
81 dns_rdata_reset(&rdata
);
82 result
= dns_rdataset_next(rdataset
);
84 if (result
!= ISC_R_NOMORE
)
87 return (ISC_R_SUCCESS
);
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
)
98 dns_rdataset_t
*rdataset
;
103 dns_rdata_t rdata
= DNS_RDATA_INIT
;
104 dns_rdataset_t ncrdataset
;
105 dns_rdatalist_t ncrdatalist
;
106 unsigned char data
[4096];
109 * Convert the authority data from 'message' into a negative cache
110 * rdataset, and store it in 'cache' at 'node'.
113 REQUIRE(message
!= NULL
);
116 * We assume that all data in the authority section has been
117 * validated by the caller.
121 * First, build an ncache rdata in buffer.
125 isc_buffer_init(&buffer
, data
, sizeof(data
));
126 if (message
->counts
[DNS_SECTION_AUTHORITY
])
127 result
= dns_message_firstname(message
, DNS_SECTION_AUTHORITY
);
129 result
= ISC_R_NOMORE
;
130 while (result
== ISC_R_SUCCESS
) {
132 dns_message_currentname(message
, DNS_SECTION_AUTHORITY
,
134 if ((name
->attributes
& DNS_NAMEATTR_NCACHE
) != 0) {
135 for (rdataset
= ISC_LIST_HEAD(name
->list
);
137 rdataset
= ISC_LIST_NEXT(rdataset
, link
)) {
138 if ((rdataset
->attributes
&
139 DNS_RDATASETATTR_NCACHE
) == 0)
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
)
148 if (trust
> rdataset
->trust
)
149 trust
= rdataset
->trust
;
151 * Copy the owner name to the buffer.
153 dns_name_toregion(name
, &r
);
154 result
= isc_buffer_copyregion(&buffer
,
156 if (result
!= ISC_R_SUCCESS
)
159 * Copy the type to the buffer.
161 isc_buffer_availableregion(&buffer
,
164 return (ISC_R_NOSPACE
);
165 isc_buffer_putuint16(&buffer
,
168 * Copy the rdataset into the buffer.
170 result
= copy_rdataset(rdataset
,
172 if (result
!= ISC_R_SUCCESS
)
177 result
= dns_message_nextname(message
, DNS_SECTION_AUTHORITY
);
179 if (result
!= ISC_R_NOMORE
)
182 if (trust
== 0xffff) {
184 * We didn't find any authority data from which to create a
185 * negative cache rdataset. In particular, we have no SOA.
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).
191 * We will now build a suitable negative cache rdataset that
192 * will cause zero bytes to be emitted when converted to
197 * The ownername must exist, but it doesn't matter what value
198 * it has. We use the root name.
200 dns_name_toregion(dns_rootname
, &r
);
201 result
= isc_buffer_copyregion(&buffer
, &r
);
202 if (result
!= ISC_R_SUCCESS
)
205 * Copy the type and a zero rdata count to the buffer.
207 isc_buffer_availableregion(&buffer
, &r
);
209 return (ISC_R_NOSPACE
);
210 isc_buffer_putuint16(&buffer
, 0);
211 isc_buffer_putuint16(&buffer
, 0);
213 * RFC2308, section 5, says that negative answers without
214 * SOAs should not be cached.
220 if ((message
->flags
& DNS_MESSAGEFLAG_AA
) != 0 &&
221 message
->counts
[DNS_SECTION_ANSWER
] == 0) {
223 * The response has aa set and we haven't followed
224 * any CNAME or DNAME chains.
226 trust
= dns_trust_authauthority
;
228 trust
= dns_trust_additional
;
232 * Now add it to the cache.
234 INSIST(trust
!= 0xffff);
235 isc_buffer_usedregion(&buffer
, &r
);
237 rdata
.length
= r
.length
;
238 rdata
.rdclass
= dns_db_class(cache
);
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
);
249 ISC_LIST_APPEND(ncrdatalist
.rdata
, &rdata
, link
);
251 dns_rdataset_init(&ncrdataset
);
252 RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist
, &ncrdataset
)
254 ncrdataset
.trust
= trust
;
255 if (message
->rcode
== dns_rcode_nxdomain
)
256 ncrdataset
.attributes
|= DNS_RDATASETATTR_NXDOMAIN
;
258 return (dns_db_addrdataset(cache
, node
, NULL
, now
, &ncrdataset
,
263 dns_ncache_towire(dns_rdataset_t
*rdataset
, dns_compress_t
*cctx
,
264 isc_buffer_t
*target
, unsigned int options
,
265 unsigned int *countp
)
267 dns_rdata_t rdata
= DNS_RDATA_INIT
;
269 isc_region_t remaining
, tavailable
;
270 isc_buffer_t source
, savedbuffer
, rdlen
;
272 dns_rdatatype_t type
;
273 unsigned int i
, rcount
, count
;
276 * Convert the negative caching rdataset 'rdataset' to wire format,
277 * compressing names as specified in 'cctx', and storing the result in
281 REQUIRE(rdataset
!= NULL
);
282 REQUIRE(rdataset
->type
== 0);
284 result
= dns_rdataset_first(rdataset
);
285 if (result
!= ISC_R_SUCCESS
)
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
);
292 savedbuffer
= *target
;
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
;
303 INSIST(remaining
.length
>= 4);
304 type
= isc_buffer_getuint16(&source
);
305 rcount
= isc_buffer_getuint16(&source
);
307 for (i
= 0; i
< rcount
; i
++) {
309 * Get the length of this rdata and set up an
310 * rdata structure for it.
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
;
319 rdata
.rdclass
= rdataset
->rdclass
;
320 INSIST(remaining
.length
>= rdata
.length
);
321 isc_buffer_forward(&source
, rdata
.length
);
323 if ((options
& DNS_NCACHETOWIRE_OMITDNSSEC
) != 0 &&
324 dns_rdatatype_isdnssec(type
))
330 dns_compress_setmethods(cctx
, DNS_COMPRESS_GLOBAL14
);
331 result
= dns_name_towire(&name
, cctx
, target
);
332 if (result
!= ISC_R_SUCCESS
)
336 * See if we have space for type, class, ttl, and
337 * rdata length. Write the type, class, and ttl.
339 isc_buffer_availableregion(target
, &tavailable
);
340 if (tavailable
.length
< 10) {
341 result
= ISC_R_NOSPACE
;
344 isc_buffer_putuint16(target
, type
);
345 isc_buffer_putuint16(target
, rdataset
->rdclass
);
346 isc_buffer_putuint32(target
, rdataset
->ttl
);
349 * Save space for rdata length.
352 isc_buffer_add(target
, 2);
357 result
= dns_rdata_towire(&rdata
, cctx
, target
);
358 if (result
!= ISC_R_SUCCESS
)
362 * Set the rdata length field to the compressed
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
-
373 isc_buffer_remainingregion(&source
, &remaining
);
374 } while (remaining
.length
> 0);
378 return (ISC_R_SUCCESS
);
381 INSIST(savedbuffer
.used
< 65536);
382 dns_compress_rollback(cctx
, (isc_uint16_t
)savedbuffer
.used
);
384 *target
= savedbuffer
;
390 rdataset_disassociate(dns_rdataset_t
*rdataset
) {
395 rdataset_first(dns_rdataset_t
*rdataset
) {
396 unsigned char *raw
= rdataset
->private3
;
399 count
= raw
[0] * 256 + raw
[1];
401 rdataset
->private5
= NULL
;
402 return (ISC_R_NOMORE
);
406 * The privateuint4 field is the number of rdata beyond the cursor
407 * position, so we decrement the total count by one before storing
411 rdataset
->privateuint4
= count
;
412 rdataset
->private5
= raw
;
414 return (ISC_R_SUCCESS
);
418 rdataset_next(dns_rdataset_t
*rdataset
) {
423 count
= rdataset
->privateuint4
;
425 return (ISC_R_NOMORE
);
427 rdataset
->privateuint4
= count
;
428 raw
= rdataset
->private5
;
429 length
= raw
[0] * 256 + raw
[1];
431 rdataset
->private5
= raw
;
433 return (ISC_R_SUCCESS
);
437 rdataset_current(dns_rdataset_t
*rdataset
, dns_rdata_t
*rdata
) {
438 unsigned char *raw
= rdataset
->private5
;
441 REQUIRE(raw
!= NULL
);
443 r
.length
= raw
[0] * 256 + raw
[1];
446 dns_rdata_fromregion(rdata
, rdataset
->rdclass
, rdataset
->type
, &r
);
450 rdataset_clone(dns_rdataset_t
*source
, dns_rdataset_t
*target
) {
454 * Reset iterator state.
456 target
->privateuint4
= 0;
457 target
->private5
= NULL
;
461 rdataset_count(dns_rdataset_t
*rdataset
) {
462 unsigned char *raw
= rdataset
->private3
;
465 count
= raw
[0] * 256 + raw
[1];
470 static dns_rdatasetmethods_t rdataset_methods
= {
471 rdataset_disassociate
,
487 dns_ncache_getrdataset(dns_rdataset_t
*ncacherdataset
, dns_name_t
*name
,
488 dns_rdatatype_t type
, dns_rdataset_t
*rdataset
)
491 dns_rdata_t rdata
= DNS_RDATA_INIT
;
492 isc_region_t remaining
;
495 dns_rdatatype_t ttype
;
496 unsigned int i
, rcount
;
499 REQUIRE(ncacherdataset
!= NULL
);
500 REQUIRE(ncacherdataset
->type
== 0);
501 REQUIRE(name
!= NULL
);
502 REQUIRE(!dns_rdataset_isassociated(rdataset
));
503 REQUIRE(type
!= dns_rdatatype_rrsig
);
505 result
= dns_rdataset_first(ncacherdataset
);
506 if (result
!= ISC_R_SUCCESS
)
508 dns_rdataset_current(ncacherdataset
, &rdata
);
509 INSIST(dns_rdataset_next(ncacherdataset
) == ISC_R_NOMORE
);
510 isc_buffer_init(&source
, rdata
.data
, rdata
.length
);
511 isc_buffer_add(&source
, rdata
.length
);
514 dns_name_init(&tname
, NULL
);
515 isc_buffer_remainingregion(&source
, &remaining
);
516 dns_name_fromregion(&tname
, &remaining
);
517 INSIST(remaining
.length
>= tname
.length
);
518 isc_buffer_forward(&source
, tname
.length
);
519 remaining
.length
-= tname
.length
;
521 INSIST(remaining
.length
>= 4);
522 ttype
= isc_buffer_getuint16(&source
);
524 if (ttype
== type
&& dns_name_equal(&tname
, name
)) {
525 isc_buffer_remainingregion(&source
, &remaining
);
529 rcount
= isc_buffer_getuint16(&source
);
530 for (i
= 0; i
< rcount
; i
++) {
531 isc_buffer_remainingregion(&source
, &remaining
);
532 INSIST(remaining
.length
>= 2);
533 length
= isc_buffer_getuint16(&source
);
534 isc_buffer_remainingregion(&source
, &remaining
);
535 INSIST(remaining
.length
>= length
);
536 isc_buffer_forward(&source
, length
);
538 isc_buffer_remainingregion(&source
, &remaining
);
539 } while (remaining
.length
> 0);
541 if (remaining
.length
== 0)
542 return (ISC_R_NOTFOUND
);
544 rdataset
->methods
= &rdataset_methods
;
545 rdataset
->rdclass
= ncacherdataset
->rdclass
;
546 rdataset
->type
= type
;
547 rdataset
->covers
= 0;
548 rdataset
->ttl
= ncacherdataset
->ttl
;
549 rdataset
->trust
= ncacherdataset
->trust
;
550 rdataset
->private1
= NULL
;
551 rdataset
->private2
= NULL
;
553 rdataset
->private3
= remaining
.base
;
556 * Reset iterator state.
558 rdataset
->privateuint4
= 0;
559 rdataset
->private5
= NULL
;
560 return (ISC_R_SUCCESS
);