Merge from vendor branch BSDTAR:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / a6.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001  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: a6.c,v 1.20.2.1 2004/03/09 06:10:59 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/util.h>
23
24 #include <dns/a6.h>
25 #include <dns/name.h>
26 #include <dns/rdata.h>
27 #include <dns/rdataset.h>
28
29 #define A6CONTEXT_MAGIC         ISC_MAGIC('A', '6', 'X', 'X')
30 #define VALID_A6CONTEXT(ac)     ISC_MAGIC_VALID(ac, A6CONTEXT_MAGIC)
31
32 #define MAX_CHAINS      8
33 #define MAX_DEPTH       16
34
35 static inline void
36 maybe_disassociate(dns_rdataset_t *rdataset) {
37         if (dns_rdataset_isassociated(rdataset))
38                 dns_rdataset_disassociate(rdataset);
39 }
40
41 static isc_result_t
42 foreach(dns_a6context_t *a6ctx, dns_rdataset_t *parent, unsigned int depth,
43         unsigned int oprefixlen)
44 {
45         dns_rdata_t rdata = DNS_RDATA_INIT;
46         isc_region_t r;
47         dns_name_t name;
48         dns_rdataset_t child;
49         dns_rdataset_t childsig;
50         isc_result_t result;
51         isc_uint8_t prefixlen, octets;
52         isc_bitstring_t bitstring;
53         isc_stdtime_t expiration;
54
55         expiration = a6ctx->now + parent->ttl;
56         if (expiration < a6ctx->expiration || a6ctx->expiration == 0)
57                 a6ctx->expiration = expiration;
58
59         depth++;
60         result = dns_rdataset_first(parent);
61         while (result == ISC_R_SUCCESS) {
62                 dns_rdataset_current(parent, &rdata);
63                 dns_rdata_toregion(&rdata, &r);
64                 prefixlen = r.base[0];
65                 if (prefixlen > oprefixlen) {
66                         /*
67                          * Trying to go to a longer prefix is illegal.
68                          */
69                         goto next_a6;
70                 }
71                 if (prefixlen < 128) {
72                         isc_bitstring_init(&bitstring, &r.base[1],
73                                            128 - prefixlen, 128 - prefixlen,
74                                            ISC_TRUE);
75                         isc_bitstring_copy(&bitstring, 128 - oprefixlen,
76                                            &a6ctx->bitstring, 128 - oprefixlen,
77                                            oprefixlen - prefixlen);
78                 }
79                 octets = 16 - prefixlen / 8;
80                 if (prefixlen != 0) {
81                         if (depth < MAX_DEPTH) {
82                                 isc_region_consume(&r, octets + 1);
83                                 dns_name_init(&name, NULL);
84                                 dns_name_fromregion(&name, &r);
85                                 dns_rdataset_init(&child);
86                                 dns_rdataset_init(&childsig);
87                                 result = (a6ctx->find)(a6ctx->arg, &name,
88                                                        dns_rdatatype_a6,
89                                                        a6ctx->now,
90                                                        &child, &childsig);
91                                 if (result == ISC_R_SUCCESS) {
92                                         /*
93                                          * We've found a new A6 rrset.
94                                          */
95                                         if (a6ctx->rrset != NULL)
96                                                 (a6ctx->rrset)(a6ctx->arg,
97                                                                &name,
98                                                                &child,
99                                                                &childsig);
100                                         /*
101                                          * Keep following the chain.
102                                          */
103                                         result = foreach(a6ctx, &child, depth,
104                                                          prefixlen);
105                                         dns_rdataset_disassociate(&child);
106                                         maybe_disassociate(&childsig);
107                                         if (result != ISC_R_SUCCESS)
108                                                 break;
109                                 } else if (result == ISC_R_NOTFOUND &&
110                                            a6ctx->missing != NULL) {
111                                         /*
112                                          * We can't follow this chain, because
113                                          * we don't know the next link.
114                                          *
115                                          * We update the 'depth' and
116                                          * 'prefixlen' values so that the
117                                          * missing function can make a copy
118                                          * of the a6context and resume
119                                          * processing after it has found the
120                                          * missing a6 context.
121                                          */
122                                         a6ctx->depth = depth;
123                                         a6ctx->prefixlen = prefixlen;
124                                         (a6ctx->missing)(a6ctx, &name);
125                                 } else {
126                                         /*
127                                          * Either something went wrong, or
128                                          * we got a negative cache response.
129                                          * In either case, we can't follow
130                                          * this chain further, and we don't
131                                          * want to call the 'missing'
132                                          * function.
133                                          *
134                                          * Note that we currently require that
135                                          * the target of an A6 record is
136                                          * a canonical domain name.  If the
137                                          * find routine returns DNS_R_CNAME or
138                                          * DNS_R_DNAME, we do NOT follow the
139                                          * chain.
140                                          *
141                                          * We do want to clean up...
142                                          */
143                                         maybe_disassociate(&child);
144                                         maybe_disassociate(&childsig);
145                                 }
146                         }
147                 } else {
148                         /*
149                          * We have a complete chain.
150                          */
151                         if (a6ctx->address != NULL)
152                                 (a6ctx->address)(a6ctx);
153                 }
154         next_a6:
155                 dns_rdata_reset(&rdata);
156                 result = dns_rdataset_next(parent);
157                 if (result == ISC_R_SUCCESS) {
158                         a6ctx->chains++;
159                         if (a6ctx->chains > MAX_CHAINS)
160                                 return (ISC_R_QUOTA);
161                 }
162         }
163         if (result != ISC_R_NOMORE)
164                 return (result);
165         return (ISC_R_SUCCESS);
166 }
167
168 void
169 dns_a6_init(dns_a6context_t *a6ctx, dns_findfunc_t find, dns_rrsetfunc_t rrset,
170             dns_in6addrfunc_t address, dns_a6missingfunc_t missing, void *arg)
171 {
172         REQUIRE(a6ctx != NULL);
173         REQUIRE(find != NULL);
174
175         a6ctx->magic = A6CONTEXT_MAGIC;
176         a6ctx->find = find;
177         a6ctx->rrset = rrset;
178         a6ctx->missing = missing;
179         a6ctx->address = address;
180         a6ctx->arg = arg;
181         a6ctx->chains = 1;
182         a6ctx->depth = 0;
183         a6ctx->now = 0;
184         a6ctx->expiration = 0;
185         a6ctx->prefixlen = 128;
186         isc_bitstring_init(&a6ctx->bitstring,
187                            (unsigned char *)a6ctx->in6addr.s6_addr,
188                            128, 128, ISC_TRUE);
189 }
190
191 void
192 dns_a6_reset(dns_a6context_t *a6ctx) {
193         REQUIRE(VALID_A6CONTEXT(a6ctx));
194
195         a6ctx->chains = 1;
196         a6ctx->depth = 0;
197         a6ctx->expiration = 0;
198         a6ctx->prefixlen = 128;
199 }
200
201 void
202 dns_a6_invalidate(dns_a6context_t *a6ctx) {
203         REQUIRE(VALID_A6CONTEXT(a6ctx));
204
205         a6ctx->magic = 0;
206 }
207
208 void
209 dns_a6_copy(dns_a6context_t *source, dns_a6context_t *target) {
210         REQUIRE(VALID_A6CONTEXT(source));
211         REQUIRE(VALID_A6CONTEXT(target));
212
213         *target = *source;
214         isc_bitstring_init(&target->bitstring,
215                            (unsigned char *)target->in6addr.s6_addr,
216                            128, 128, ISC_TRUE);
217 }
218
219 isc_result_t
220 dns_a6_foreach(dns_a6context_t *a6ctx, dns_rdataset_t *rdataset,
221                isc_stdtime_t now)
222 {
223         isc_result_t result;
224
225         REQUIRE(VALID_A6CONTEXT(a6ctx));
226         REQUIRE(rdataset->type == dns_rdatatype_a6);
227
228         if (now == 0)
229                 isc_stdtime_get(&now);
230         a6ctx->now = now;
231
232         result = foreach(a6ctx, rdataset, a6ctx->depth, a6ctx->prefixlen);
233         if (result == ISC_R_QUOTA)
234                 result = ISC_R_SUCCESS;
235
236         return (result);
237 }