Merge branch 'vendor/EXPAT'
[dragonfly.git] / contrib / bind / lib / dns / ncache.c
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 }