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