Merge from vendor branch GDB:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / rdataset.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001, 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: rdataset.c,v 1.58.2.5 2004/03/09 06:11:06 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <isc/buffer.h>
25 #include <isc/mem.h>
26 #include <isc/random.h>
27 #include <isc/util.h>
28
29 #include <dns/name.h>
30 #include <dns/ncache.h>
31 #include <dns/rdata.h>
32 #include <dns/rdataset.h>
33 #include <dns/compress.h>
34
35 void
36 dns_rdataset_init(dns_rdataset_t *rdataset) {
37
38         /*
39          * Make 'rdataset' a valid, disassociated rdataset.
40          */
41
42         REQUIRE(rdataset != NULL);
43
44         rdataset->magic = DNS_RDATASET_MAGIC;
45         rdataset->methods = NULL;
46         ISC_LINK_INIT(rdataset, link);
47         rdataset->rdclass = 0;
48         rdataset->type = 0;
49         rdataset->ttl = 0;
50         rdataset->trust = 0;
51         rdataset->covers = 0;
52         rdataset->attributes = 0;
53         rdataset->private1 = NULL;
54         rdataset->private2 = NULL;
55         rdataset->private3 = NULL;
56         rdataset->privateuint4 = 0;
57         rdataset->private5 = NULL;
58 }
59
60 void
61 dns_rdataset_invalidate(dns_rdataset_t *rdataset) {
62
63         /*
64          * Invalidate 'rdataset'.
65          */
66
67         REQUIRE(DNS_RDATASET_VALID(rdataset));
68         REQUIRE(rdataset->methods == NULL);
69
70         rdataset->magic = 0;
71         ISC_LINK_INIT(rdataset, link);
72         rdataset->rdclass = 0;
73         rdataset->type = 0;
74         rdataset->ttl = 0;
75         rdataset->trust = 0;
76         rdataset->covers = 0;
77         rdataset->attributes = 0;
78         rdataset->private1 = NULL;
79         rdataset->private2 = NULL;
80         rdataset->private3 = NULL;
81         rdataset->privateuint4 = 0;
82         rdataset->private5 = NULL;
83 }
84
85 void
86 dns_rdataset_disassociate(dns_rdataset_t *rdataset) {
87
88         /*
89          * Disassociate 'rdataset' from its rdata, allowing it to be reused.
90          */
91
92         REQUIRE(DNS_RDATASET_VALID(rdataset));
93         REQUIRE(rdataset->methods != NULL);
94
95         (rdataset->methods->disassociate)(rdataset);
96         rdataset->methods = NULL;
97         ISC_LINK_INIT(rdataset, link);
98         rdataset->rdclass = 0;
99         rdataset->type = 0;
100         rdataset->ttl = 0;
101         rdataset->trust = 0;
102         rdataset->covers = 0;
103         rdataset->attributes = 0;
104         rdataset->private1 = NULL;
105         rdataset->private2 = NULL;
106         rdataset->private3 = NULL;
107         rdataset->privateuint4 = 0;
108         rdataset->private5 = NULL;
109 }
110
111 isc_boolean_t
112 dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
113         /*
114          * Is 'rdataset' associated?
115          */
116
117         REQUIRE(DNS_RDATASET_VALID(rdataset));
118
119         if (rdataset->methods != NULL)
120                 return (ISC_TRUE);
121
122         return (ISC_FALSE);
123 }
124
125 static void
126 question_disassociate(dns_rdataset_t *rdataset) {
127         UNUSED(rdataset);
128 }
129
130 static isc_result_t
131 question_cursor(dns_rdataset_t *rdataset) {
132         UNUSED(rdataset);
133         
134         return (ISC_R_NOMORE);
135 }
136
137 static void
138 question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
139         /*
140          * This routine should never be called.
141          */
142         UNUSED(rdataset);
143         UNUSED(rdata);
144         
145         REQUIRE(0);
146 }
147
148 static void
149 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
150         *target = *source;
151 }
152
153 static unsigned int
154 question_count(dns_rdataset_t *rdataset) {
155         /*
156          * This routine should never be called.
157          */
158         UNUSED(rdataset);
159         REQUIRE(0);
160
161         return (0);
162 }
163
164 static dns_rdatasetmethods_t question_methods = {
165         question_disassociate,
166         question_cursor,
167         question_cursor,
168         question_current,
169         question_clone,
170         question_count
171 };
172
173 void
174 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
175                           dns_rdatatype_t type)
176 {
177
178         /*
179          * Make 'rdataset' a valid, associated, question rdataset, with a
180          * question class of 'rdclass' and type 'type'.
181          */
182
183         REQUIRE(DNS_RDATASET_VALID(rdataset));
184         REQUIRE(rdataset->methods == NULL);
185
186         rdataset->methods = &question_methods;
187         rdataset->rdclass = rdclass;
188         rdataset->type = type;
189         rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
190 }
191
192 unsigned int
193 dns_rdataset_count(dns_rdataset_t *rdataset) {
194
195         /*
196          * Return the number of records in 'rdataset'.
197          */
198
199         REQUIRE(DNS_RDATASET_VALID(rdataset));
200         REQUIRE(rdataset->methods != NULL);
201
202         return ((rdataset->methods->count)(rdataset));
203 }
204
205 void
206 dns_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
207
208         /*
209          * Make 'target' refer to the same rdataset as 'source'.
210          */
211
212         REQUIRE(DNS_RDATASET_VALID(source));
213         REQUIRE(source->methods != NULL);
214         REQUIRE(DNS_RDATASET_VALID(target));
215         REQUIRE(target->methods == NULL);
216
217         (source->methods->clone)(source, target);
218 }
219
220 isc_result_t
221 dns_rdataset_first(dns_rdataset_t *rdataset) {
222
223         /*
224          * Move the rdata cursor to the first rdata in the rdataset (if any).
225          */
226
227         REQUIRE(DNS_RDATASET_VALID(rdataset));
228         REQUIRE(rdataset->methods != NULL);
229
230         return ((rdataset->methods->first)(rdataset));
231 }
232
233 isc_result_t
234 dns_rdataset_next(dns_rdataset_t *rdataset) {
235
236         /*
237          * Move the rdata cursor to the next rdata in the rdataset (if any).
238          */
239
240         REQUIRE(DNS_RDATASET_VALID(rdataset));
241         REQUIRE(rdataset->methods != NULL);
242
243         return ((rdataset->methods->next)(rdataset));
244 }
245
246 void
247 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
248
249         /*
250          * Make 'rdata' refer to the current rdata.
251          */
252
253         REQUIRE(DNS_RDATASET_VALID(rdataset));
254         REQUIRE(rdataset->methods != NULL);
255
256         (rdataset->methods->current)(rdataset, rdata);
257 }
258
259 #define MAX_SHUFFLE     32
260 #define WANT_FIXED(r)   (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0)
261
262 struct towire_sort {
263         int key;
264         dns_rdata_t *rdata;
265 };
266
267 static int
268 towire_compare(const void *av, const void *bv) {
269         const struct towire_sort *a = (const struct towire_sort *) av;
270         const struct towire_sort *b = (const struct towire_sort *) bv;
271         return (a->key - b->key);
272 }
273
274 static isc_result_t
275 towiresorted(dns_rdataset_t *rdataset, dns_name_t *owner_name,
276              dns_compress_t *cctx, isc_buffer_t *target,
277              dns_rdatasetorderfunc_t order, void *order_arg,
278              isc_boolean_t partial, unsigned int *countp,
279              void **state)
280 {
281         dns_rdata_t rdata = DNS_RDATA_INIT;
282         isc_region_t r;
283         isc_result_t result;
284         unsigned int i, count, added;
285         isc_buffer_t savedbuffer, rdlen, rrbuffer;
286         unsigned int headlen;
287         isc_boolean_t question = ISC_FALSE;
288         isc_boolean_t shuffle = ISC_FALSE;
289         dns_rdata_t *shuffled = NULL, shuffled_fixed[MAX_SHUFFLE];
290         struct towire_sort *sorted = NULL, sorted_fixed[MAX_SHUFFLE];
291
292         UNUSED(state);
293
294         /*
295          * Convert 'rdataset' to wire format, compressing names as specified
296          * in cctx, and storing the result in 'target'.
297          */
298
299         REQUIRE(DNS_RDATASET_VALID(rdataset));
300         REQUIRE(countp != NULL);
301         REQUIRE((order == NULL) == (order_arg == NULL));
302         REQUIRE(cctx != NULL && cctx->mctx != NULL);
303
304         count = 0;
305         if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
306                 question = ISC_TRUE;
307                 count = 1;
308                 result = dns_rdataset_first(rdataset);
309                 INSIST(result == ISC_R_NOMORE);
310         } else if (rdataset->type == 0) {
311                 /*
312                  * This is a negative caching rdataset.
313                  */
314                 return (dns_ncache_towire(rdataset, cctx, target, countp));
315         } else {
316                 count = (rdataset->methods->count)(rdataset);
317                 result = dns_rdataset_first(rdataset);
318                 if (result == ISC_R_NOMORE)
319                         return (ISC_R_SUCCESS);
320                 if (result != ISC_R_SUCCESS)
321                         return (result);
322         }
323
324         /*
325          * Do we want to shuffle this anwer?
326          */
327         if (!question && count > 1 &&
328             (!WANT_FIXED(rdataset) || order != NULL) &&
329             rdataset->type != dns_rdatatype_sig)
330                 shuffle = ISC_TRUE;
331
332         if (shuffle && count > MAX_SHUFFLE) {
333                 shuffled = isc_mem_get(cctx->mctx, count * sizeof(*shuffled));
334                 sorted = isc_mem_get(cctx->mctx, count * sizeof(*sorted));
335                 if (shuffled == NULL || sorted == NULL)
336                         shuffle = ISC_FALSE;
337         } else {
338                 shuffled = shuffled_fixed;
339                 sorted = sorted_fixed;
340         }
341
342         if (shuffle) {
343                 /*
344                  * First we get handles to all of the rdata.
345                  */
346                 i = 0;
347                 do {
348                         INSIST(i < count);
349                         dns_rdata_init(&shuffled[i]);
350                         dns_rdataset_current(rdataset, &shuffled[i]);
351                         i++;
352                         result = dns_rdataset_next(rdataset);
353                 } while (result == ISC_R_SUCCESS);
354                 if (result != ISC_R_NOMORE)
355                         goto cleanup;
356                 INSIST(i == count);
357                 /*
358                  * Now we shuffle.
359                  */
360                 if (order != NULL) {
361                         /*
362                          * Sorted order.
363                          */
364                         for (i = 0; i < count; i++) {
365                                 sorted[i].key = (*order)(&shuffled[i],
366                                                          order_arg);
367                                 sorted[i].rdata = &shuffled[i];
368                         }
369                         qsort(sorted, count, sizeof(sorted[0]),
370                               towire_compare);
371                 } else {
372                         /*
373                          * "Cyclic" order.
374                          */
375                         isc_uint32_t val;
376                         unsigned int j;
377
378                         isc_random_get(&val);
379                         j = val % count;
380                         for (i = 0; i < count; i++) {
381                                 sorted[j].key = 0; /* Unused */
382                                 sorted[j].rdata = &shuffled[i];
383                                 j++;
384                                 if (j == count)
385                                         j = 0; /* Wrap around. */
386                         }
387                 }
388         }
389
390         savedbuffer = *target;
391         i = 0;
392         added = 0;
393
394         do {
395                 /*
396                  * Copy out the name, type, class, ttl.
397                  */
398                 
399                 rrbuffer = *target;
400                 dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
401                 result = dns_name_towire(owner_name, cctx, target);
402                 if (result != ISC_R_SUCCESS)
403                         goto rollback;
404                 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
405                 if (!question)
406                         headlen += sizeof(dns_ttl_t)
407                                 + 2;  /* XXX 2 for rdata len */
408                 isc_buffer_availableregion(target, &r);
409                 if (r.length < headlen) {
410                         result = ISC_R_NOSPACE;
411                         goto rollback;
412                 }
413                 isc_buffer_putuint16(target, rdataset->type);
414                 isc_buffer_putuint16(target, rdataset->rdclass);
415                 if (!question) {
416                         isc_buffer_putuint32(target, rdataset->ttl);
417
418                         /*
419                          * Save space for rdlen.
420                          */
421                         rdlen = *target;
422                         isc_buffer_add(target, 2);
423
424                         /*
425                          * Copy out the rdata
426                          */
427                         if (shuffle)
428                                 rdata = *(sorted[i].rdata);
429                         else {
430                                 dns_rdata_reset(&rdata);
431                                 dns_rdataset_current(rdataset, &rdata);
432                         }
433                         result = dns_rdata_towire(&rdata, cctx, target);
434                         if (result != ISC_R_SUCCESS)
435                                 goto rollback;
436                         INSIST((target->used >= rdlen.used + 2) &&
437                                (target->used - rdlen.used - 2 < 65536));
438                         isc_buffer_putuint16(&rdlen,
439                                              (isc_uint16_t)(target->used -
440                                                             rdlen.used - 2));
441                         added++;
442                 }
443
444                 if (shuffle) {
445                         i++;
446                         if (i == count)
447                                 result = ISC_R_NOMORE;
448                         else
449                                 result = ISC_R_SUCCESS;
450                 } else {
451                         result = dns_rdataset_next(rdataset);
452                 }
453         } while (result == ISC_R_SUCCESS);
454
455         if (result != ISC_R_NOMORE)
456                 goto rollback;
457
458         *countp += count;
459
460         result = ISC_R_SUCCESS;
461         goto cleanup;
462
463         rollback:
464         if (partial && result == ISC_R_NOSPACE) {
465                 INSIST(rrbuffer.used < 65536);
466                 dns_compress_rollback(cctx, (isc_uint16_t)rrbuffer.used);
467                 *countp += added;
468                 *target = rrbuffer;
469                 goto cleanup;
470         }
471         INSIST(savedbuffer.used < 65536);
472         dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used);
473         *countp = 0;
474         *target = savedbuffer;
475
476         cleanup:
477         if (sorted != NULL && sorted != sorted_fixed)
478                 isc_mem_put(cctx->mctx, sorted, count * sizeof(*sorted));
479         if (shuffled != NULL && shuffled != shuffled_fixed)
480                 isc_mem_put(cctx->mctx, shuffled, count * sizeof(*shuffled));
481         return (result);
482 }
483
484 isc_result_t
485 dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
486                           dns_name_t *owner_name,
487                           dns_compress_t *cctx,
488                           isc_buffer_t *target,
489                           dns_rdatasetorderfunc_t order,
490                           void *order_arg,
491                           unsigned int *countp)
492 {
493         return (towiresorted(rdataset, owner_name, cctx, target,
494                              order, order_arg, ISC_FALSE, countp, NULL));
495 }
496
497 isc_result_t
498 dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
499                            dns_name_t *owner_name,
500                            dns_compress_t *cctx,
501                            isc_buffer_t *target,
502                            dns_rdatasetorderfunc_t order,
503                            void *order_arg,
504                            unsigned int *countp,
505                            void **state)
506 {
507         REQUIRE(state == NULL); /* XXX remove when implemented */
508         return (towiresorted(rdataset, owner_name, cctx, target,
509                              order, order_arg, ISC_TRUE, countp, state));
510 }
511
512 isc_result_t
513 dns_rdataset_towire(dns_rdataset_t *rdataset,
514                     dns_name_t *owner_name,
515                     dns_compress_t *cctx,
516                     isc_buffer_t *target,
517                     unsigned int *countp)
518 {
519         return (towiresorted(rdataset, owner_name, cctx, target,
520                              NULL, NULL, ISC_FALSE, countp, NULL));
521 }
522
523 isc_result_t
524 dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
525                             dns_additionaldatafunc_t add, void *arg)
526 {
527         dns_rdata_t rdata = DNS_RDATA_INIT;
528         isc_result_t result;
529
530         /*
531          * For each rdata in rdataset, call 'add' for each name and type in the
532          * rdata which is subject to additional section processing.
533          */
534
535         REQUIRE(DNS_RDATASET_VALID(rdataset));
536         REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0);
537
538         result = dns_rdataset_first(rdataset);
539         if (result != ISC_R_SUCCESS)
540                 return (result);
541
542         do {
543                 dns_rdataset_current(rdataset, &rdata);
544                 result = dns_rdata_additionaldata(&rdata, add, arg);
545                 if (result == ISC_R_SUCCESS)
546                         result = dns_rdataset_next(rdataset);
547                 dns_rdata_reset(&rdata);
548         } while (result == ISC_R_SUCCESS);
549
550         if (result != ISC_R_NOMORE)
551                 return (result);
552
553         return (ISC_R_SUCCESS);
554 }
555