2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.24.2.6 2004/03/09 06:11:04 marka Exp $ */
22 #include <isc/buffer.h>
26 #include <dns/message.h>
27 #include <dns/ncache.h>
28 #include <dns/rdata.h>
29 #include <dns/rdatalist.h>
30 #include <dns/rdataset.h>
33 * The format of an ncache rdata is a sequence of one or more records of
34 * the following format:
39 * rdata length These two occur 'rdata count'
44 static inline isc_result_t
45 copy_rdataset(dns_rdataset_t
*rdataset
, isc_buffer_t
*buffer
) {
49 dns_rdata_t rdata
= DNS_RDATA_INIT
;
52 * Copy the rdataset count to the buffer.
54 isc_buffer_availableregion(buffer
, &ar
);
56 return (ISC_R_NOSPACE
);
57 count
= dns_rdataset_count(rdataset
);
58 INSIST(count
<= 65535);
59 isc_buffer_putuint16(buffer
, (isc_uint16_t
)count
);
61 result
= dns_rdataset_first(rdataset
);
62 while (result
== ISC_R_SUCCESS
) {
63 dns_rdataset_current(rdataset
, &rdata
);
64 dns_rdata_toregion(&rdata
, &r
);
65 INSIST(r
.length
<= 65535);
66 isc_buffer_availableregion(buffer
, &ar
);
68 return (ISC_R_NOSPACE
);
70 * Copy the rdata length to the buffer.
72 isc_buffer_putuint16(buffer
, (isc_uint16_t
)r
.length
);
74 * Copy the rdata to the buffer.
76 result
= isc_buffer_copyregion(buffer
, &r
);
77 if (result
!= ISC_R_SUCCESS
)
79 dns_rdata_reset(&rdata
);
80 result
= dns_rdataset_next(rdataset
);
82 if (result
!= ISC_R_NOMORE
)
85 return (ISC_R_SUCCESS
);
89 dns_ncache_add(dns_message_t
*message
, dns_db_t
*cache
, dns_dbnode_t
*node
,
90 dns_rdatatype_t covers
, isc_stdtime_t now
, dns_ttl_t maxttl
,
91 dns_rdataset_t
*addedrdataset
)
96 dns_rdataset_t
*rdataset
;
101 dns_rdata_t rdata
= DNS_RDATA_INIT
;
102 dns_rdataset_t ncrdataset
;
103 dns_rdatalist_t ncrdatalist
;
104 unsigned char data
[4096];
107 * Convert the authority data from 'message' into a negative cache
108 * rdataset, and store it in 'cache' at 'node'.
111 REQUIRE(message
!= NULL
);
114 * We assume that all data in the authority section has been
115 * validated by the caller.
119 * First, build an ncache rdata in buffer.
123 isc_buffer_init(&buffer
, data
, sizeof(data
));
124 if (message
->counts
[DNS_SECTION_AUTHORITY
])
125 result
= dns_message_firstname(message
, DNS_SECTION_AUTHORITY
);
127 result
= ISC_R_NOMORE
;
128 while (result
== ISC_R_SUCCESS
) {
130 dns_message_currentname(message
, DNS_SECTION_AUTHORITY
,
132 if ((name
->attributes
& DNS_NAMEATTR_NCACHE
) != 0) {
133 for (rdataset
= ISC_LIST_HEAD(name
->list
);
135 rdataset
= ISC_LIST_NEXT(rdataset
, link
)) {
136 if ((rdataset
->attributes
&
137 DNS_RDATASETATTR_NCACHE
) == 0)
139 type
= rdataset
->type
;
140 if (type
== dns_rdatatype_sig
)
141 type
= rdataset
->covers
;
142 if (type
== dns_rdatatype_soa
||
143 type
== dns_rdatatype_nxt
) {
144 if (ttl
> rdataset
->ttl
)
146 if (trust
> rdataset
->trust
)
147 trust
= rdataset
->trust
;
149 * Copy the owner name to the buffer.
151 dns_name_toregion(name
, &r
);
152 result
= isc_buffer_copyregion(&buffer
,
154 if (result
!= ISC_R_SUCCESS
)
157 * Copy the type to the buffer.
159 isc_buffer_availableregion(&buffer
,
162 return (ISC_R_NOSPACE
);
163 isc_buffer_putuint16(&buffer
,
166 * Copy the rdataset into the buffer.
168 result
= copy_rdataset(rdataset
,
170 if (result
!= ISC_R_SUCCESS
)
175 result
= dns_message_nextname(message
, DNS_SECTION_AUTHORITY
);
177 if (result
!= ISC_R_NOMORE
)
180 if (trust
== 0xffff) {
182 * We didn't find any authority data from which to create a
183 * negative cache rdataset. In particular, we have no SOA.
185 * We trust that the caller wants negative caching, so this
186 * means we have a "type 3 nxdomain" or "type 3 nodata"
187 * response (see RFC 2308 for details).
189 * We will now build a suitable negative cache rdataset that
190 * will cause zero bytes to be emitted when converted to
195 * The ownername must exist, but it doesn't matter what value
196 * it has. We use the root name.
198 dns_name_toregion(dns_rootname
, &r
);
199 result
= isc_buffer_copyregion(&buffer
, &r
);
200 if (result
!= ISC_R_SUCCESS
)
203 * Copy the type and a zero rdata count to the buffer.
205 isc_buffer_availableregion(&buffer
, &r
);
207 return (ISC_R_NOSPACE
);
208 isc_buffer_putuint16(&buffer
, 0);
209 isc_buffer_putuint16(&buffer
, 0);
211 * RFC 2308, section 5, says that negative answers without
212 * SOAs should not be cached.
218 if ((message
->flags
& DNS_MESSAGEFLAG_AA
) != 0 &&
219 message
->counts
[DNS_SECTION_ANSWER
] == 0) {
221 * The response has aa set and we haven't followed
222 * any CNAME or DNAME chains.
224 trust
= dns_trust_authauthority
;
226 trust
= dns_trust_additional
;
230 * Now add it to the cache.
232 INSIST(trust
!= 0xffff);
233 isc_buffer_usedregion(&buffer
, &r
);
235 rdata
.length
= r
.length
;
236 rdata
.rdclass
= dns_db_class(cache
);
240 ncrdatalist
.rdclass
= rdata
.rdclass
;
241 ncrdatalist
.type
= 0;
242 ncrdatalist
.covers
= covers
;
243 ncrdatalist
.ttl
= ttl
;
244 ISC_LIST_INIT(ncrdatalist
.rdata
);
245 ISC_LINK_INIT(&ncrdatalist
, link
);
247 ISC_LIST_APPEND(ncrdatalist
.rdata
, &rdata
, link
);
249 dns_rdataset_init(&ncrdataset
);
250 dns_rdatalist_tordataset(&ncrdatalist
, &ncrdataset
);
251 ncrdataset
.trust
= trust
;
252 if (message
->rcode
== dns_rcode_nxdomain
)
253 ncrdataset
.attributes
|= DNS_RDATASETATTR_NXDOMAIN
;
255 return (dns_db_addrdataset(cache
, node
, NULL
, now
, &ncrdataset
,
260 dns_ncache_towire(dns_rdataset_t
*rdataset
, dns_compress_t
*cctx
,
261 isc_buffer_t
*target
, unsigned int *countp
)
263 dns_rdata_t rdata
= DNS_RDATA_INIT
;
265 isc_region_t remaining
, tavailable
;
266 isc_buffer_t source
, savedbuffer
, rdlen
;
268 dns_rdatatype_t type
;
269 unsigned int i
, rcount
, count
;
272 * Convert the negative caching rdataset 'rdataset' to wire format,
273 * compressing names as specified in 'cctx', and storing the result in
277 REQUIRE(rdataset
!= NULL
);
278 REQUIRE(rdataset
->type
== 0);
280 result
= dns_rdataset_first(rdataset
);
281 if (result
!= ISC_R_SUCCESS
)
283 dns_rdataset_current(rdataset
, &rdata
);
284 INSIST(dns_rdataset_next(rdataset
) == ISC_R_NOMORE
);
285 isc_buffer_init(&source
, rdata
.data
, rdata
.length
);
286 isc_buffer_add(&source
, rdata
.length
);
288 savedbuffer
= *target
;
292 dns_name_init(&name
, NULL
);
293 isc_buffer_remainingregion(&source
, &remaining
);
294 dns_name_fromregion(&name
, &remaining
);
295 INSIST(remaining
.length
>= name
.length
);
296 isc_buffer_forward(&source
, name
.length
);
297 remaining
.length
-= name
.length
;
299 INSIST(remaining
.length
>= 4);
300 type
= isc_buffer_getuint16(&source
);
301 rcount
= isc_buffer_getuint16(&source
);
303 for (i
= 0; i
< rcount
; i
++) {
305 * Get the length of this rdata and set up an
306 * rdata structure for it.
308 isc_buffer_remainingregion(&source
, &remaining
);
309 INSIST(remaining
.length
>= 2);
310 dns_rdata_reset(&rdata
);
311 rdata
.length
= isc_buffer_getuint16(&source
);
312 isc_buffer_remainingregion(&source
, &remaining
);
313 rdata
.data
= remaining
.base
;
315 rdata
.rdclass
= rdataset
->rdclass
;
316 INSIST(remaining
.length
>= rdata
.length
);
317 isc_buffer_forward(&source
, rdata
.length
);
322 dns_compress_setmethods(cctx
, DNS_COMPRESS_GLOBAL14
);
323 result
= dns_name_towire(&name
, cctx
, target
);
324 if (result
!= ISC_R_SUCCESS
)
328 * See if we have space for type, class, ttl, and
329 * rdata length. Write the type, class, and ttl.
331 isc_buffer_availableregion(target
, &tavailable
);
332 if (tavailable
.length
< 10) {
333 result
= ISC_R_NOSPACE
;
336 isc_buffer_putuint16(target
, type
);
337 isc_buffer_putuint16(target
, rdataset
->rdclass
);
338 isc_buffer_putuint32(target
, rdataset
->ttl
);
341 * Save space for rdata length.
344 isc_buffer_add(target
, 2);
349 result
= dns_rdata_towire(&rdata
, cctx
, target
);
350 if (result
!= ISC_R_SUCCESS
)
354 * Set the rdata length field to the compressed
357 INSIST((target
->used
>= rdlen
.used
+ 2) &&
358 (target
->used
- rdlen
.used
- 2 < 65536));
359 isc_buffer_putuint16(&rdlen
,
360 (isc_uint16_t
)(target
->used
-
365 isc_buffer_remainingregion(&source
, &remaining
);
366 } while (remaining
.length
> 0);
370 return (ISC_R_SUCCESS
);
373 INSIST(savedbuffer
.used
< 65536);
374 dns_compress_rollback(cctx
, (isc_uint16_t
)savedbuffer
.used
);
376 *target
= savedbuffer
;