]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/dns/zt.c
Fix BIND remote denial of service vulnerability. [SA-15:27]
[FreeBSD/releng/9.3.git] / contrib / bind9 / lib / dns / zt.c
1 /*
2  * Copyright (C) 2004-2007, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  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$ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/file.h>
25 #include <isc/magic.h>
26 #include <isc/mem.h>
27 #include <isc/string.h>
28 #include <isc/task.h>
29 #include <isc/util.h>
30
31 #include <dns/log.h>
32 #include <dns/name.h>
33 #include <dns/rbt.h>
34 #include <dns/rdataclass.h>
35 #include <dns/result.h>
36 #include <dns/view.h>
37 #include <dns/zone.h>
38 #include <dns/zt.h>
39
40 struct dns_zt {
41         /* Unlocked. */
42         unsigned int            magic;
43         isc_mem_t               *mctx;
44         dns_rdataclass_t        rdclass;
45         isc_rwlock_t            rwlock;
46         dns_zt_allloaded_t      loaddone;
47         void *                  loaddone_arg;
48         /* Locked by lock. */
49         isc_boolean_t           flush;
50         isc_uint32_t            references;
51         unsigned int            loads_pending;
52         dns_rbt_t               *table;
53 };
54
55 #define ZTMAGIC                 ISC_MAGIC('Z', 'T', 'b', 'l')
56 #define VALID_ZT(zt)            ISC_MAGIC_VALID(zt, ZTMAGIC)
57
58 static void
59 auto_detach(void *, void *);
60
61 static isc_result_t
62 load(dns_zone_t *zone, void *uap);
63
64 static isc_result_t
65 asyncload(dns_zone_t *zone, void *callback);
66
67 static isc_result_t
68 loadnew(dns_zone_t *zone, void *uap);
69
70 static isc_result_t
71 freezezones(dns_zone_t *zone, void *uap);
72
73 static isc_result_t
74 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
75
76 isc_result_t
77 dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp)
78 {
79         dns_zt_t *zt;
80         isc_result_t result;
81
82         REQUIRE(ztp != NULL && *ztp == NULL);
83
84         zt = isc_mem_get(mctx, sizeof(*zt));
85         if (zt == NULL)
86                 return (ISC_R_NOMEMORY);
87
88         zt->table = NULL;
89         result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
90         if (result != ISC_R_SUCCESS)
91                 goto cleanup_zt;
92
93         result = isc_rwlock_init(&zt->rwlock, 0, 0);
94         if (result != ISC_R_SUCCESS)
95                 goto cleanup_rbt;
96
97         zt->mctx = NULL;
98         isc_mem_attach(mctx, &zt->mctx);
99         zt->references = 1;
100         zt->flush = ISC_FALSE;
101         zt->rdclass = rdclass;
102         zt->magic = ZTMAGIC;
103         zt->loaddone = NULL;
104         zt->loaddone_arg = NULL;
105         zt->loads_pending = 0;
106         *ztp = zt;
107
108         return (ISC_R_SUCCESS);
109
110    cleanup_rbt:
111         dns_rbt_destroy(&zt->table);
112
113    cleanup_zt:
114         isc_mem_put(mctx, zt, sizeof(*zt));
115
116         return (result);
117 }
118
119 isc_result_t
120 dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
121         isc_result_t result;
122         dns_zone_t *dummy = NULL;
123         dns_name_t *name;
124
125         REQUIRE(VALID_ZT(zt));
126
127         name = dns_zone_getorigin(zone);
128
129         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
130
131         result = dns_rbt_addname(zt->table, name, zone);
132         if (result == ISC_R_SUCCESS)
133                 dns_zone_attach(zone, &dummy);
134
135         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
136
137         return (result);
138 }
139
140 isc_result_t
141 dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
142         isc_result_t result;
143         dns_name_t *name;
144
145         REQUIRE(VALID_ZT(zt));
146
147         name = dns_zone_getorigin(zone);
148
149         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
150
151         result = dns_rbt_deletename(zt->table, name, ISC_FALSE);
152
153         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
154
155         return (result);
156 }
157
158 isc_result_t
159 dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
160             dns_name_t *foundname, dns_zone_t **zonep)
161 {
162         isc_result_t result;
163         dns_zone_t *dummy = NULL;
164         unsigned int rbtoptions = 0;
165
166         REQUIRE(VALID_ZT(zt));
167
168         if ((options & DNS_ZTFIND_NOEXACT) != 0)
169                 rbtoptions |= DNS_RBTFIND_NOEXACT;
170
171         RWLOCK(&zt->rwlock, isc_rwlocktype_read);
172
173         result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
174                                   (void **) (void*)&dummy);
175         if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
176                 dns_zone_attach(dummy, zonep);
177
178         RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
179
180         return (result);
181 }
182
183 void
184 dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
185
186         REQUIRE(VALID_ZT(zt));
187         REQUIRE(ztp != NULL && *ztp == NULL);
188
189         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
190
191         INSIST(zt->references > 0);
192         zt->references++;
193         INSIST(zt->references != 0);
194
195         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
196
197         *ztp = zt;
198 }
199
200 static isc_result_t
201 flush(dns_zone_t *zone, void *uap) {
202         UNUSED(uap);
203         return (dns_zone_flush(zone));
204 }
205
206 static void
207 zt_destroy(dns_zt_t *zt) {
208         if (zt->flush)
209                 (void)dns_zt_apply(zt, ISC_FALSE, flush, NULL);
210         dns_rbt_destroy(&zt->table);
211         isc_rwlock_destroy(&zt->rwlock);
212         zt->magic = 0;
213         isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
214 }
215
216 static void
217 zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) {
218         isc_boolean_t destroy = ISC_FALSE;
219         dns_zt_t *zt;
220
221         REQUIRE(ztp != NULL && VALID_ZT(*ztp));
222
223         zt = *ztp;
224
225         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
226
227         INSIST(zt->references > 0);
228         zt->references--;
229         if (zt->references == 0)
230                 destroy = ISC_TRUE;
231         if (need_flush)
232                 zt->flush = ISC_TRUE;
233
234         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
235
236         if (destroy)
237                 zt_destroy(zt);
238
239         *ztp = NULL;
240 }
241
242 void
243 dns_zt_flushanddetach(dns_zt_t **ztp) {
244         zt_flushanddetach(ztp, ISC_TRUE);
245 }
246
247 void
248 dns_zt_detach(dns_zt_t **ztp) {
249         zt_flushanddetach(ztp, ISC_FALSE);
250 }
251
252 isc_result_t
253 dns_zt_load(dns_zt_t *zt, isc_boolean_t stop) {
254         isc_result_t result;
255
256         REQUIRE(VALID_ZT(zt));
257
258         RWLOCK(&zt->rwlock, isc_rwlocktype_read);
259         result = dns_zt_apply(zt, stop, load, NULL);
260         RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
261         return (result);
262 }
263
264 static isc_result_t
265 load(dns_zone_t *zone, void *uap) {
266         isc_result_t result;
267         UNUSED(uap);
268
269         result = dns_zone_load(zone);
270         if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE)
271                 result = ISC_R_SUCCESS;
272
273         return (result);
274 }
275
276 isc_result_t
277 dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) {
278         isc_result_t result;
279         static dns_zt_zoneloaded_t dl = doneloading;
280         int pending;
281
282         REQUIRE(VALID_ZT(zt));
283
284         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
285
286         INSIST(zt->loads_pending == 0);
287         result = dns_zt_apply2(zt, ISC_FALSE, NULL, asyncload, &dl);
288
289         pending = zt->loads_pending;
290         if (pending != 0) {
291                 zt->loaddone = alldone;
292                 zt->loaddone_arg = arg;
293         }
294
295         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
296
297         if (pending == 0)
298                 alldone(arg);
299
300         return (result);
301 }
302
303 /*
304  * Initiates asynchronous loading of zone 'zone'.  'callback' is a
305  * pointer to a function which will be used to inform the caller when
306  * the zone loading is complete.
307  */
308 static isc_result_t
309 asyncload(dns_zone_t *zone, void *callback) {
310         isc_result_t result;
311         dns_zt_zoneloaded_t *loaded = callback;
312         dns_zt_t *zt;
313
314         REQUIRE(zone != NULL);
315         zt = dns_zone_getview(zone)->zonetable;
316         INSIST(VALID_ZT(zt));
317
318         result = dns_zone_asyncload(zone, *loaded, zt);
319         if (result == ISC_R_SUCCESS) {
320                 INSIST(zt->references > 0);
321                 zt->references++;
322                 INSIST(zt->references != 0);
323                 zt->loads_pending++;
324         }
325         return (ISC_R_SUCCESS);
326 }
327
328 isc_result_t
329 dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) {
330         isc_result_t result;
331
332         REQUIRE(VALID_ZT(zt));
333
334         RWLOCK(&zt->rwlock, isc_rwlocktype_read);
335         result = dns_zt_apply(zt, stop, loadnew, NULL);
336         RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
337         return (result);
338 }
339
340 static isc_result_t
341 loadnew(dns_zone_t *zone, void *uap) {
342         isc_result_t result;
343         UNUSED(uap);
344
345         result = dns_zone_loadnew(zone);
346         if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
347             result == DNS_R_DYNAMIC)
348                 result = ISC_R_SUCCESS;
349         return (result);
350 }
351
352 isc_result_t
353 dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) {
354         isc_result_t result, tresult;
355
356         REQUIRE(VALID_ZT(zt));
357
358         RWLOCK(&zt->rwlock, isc_rwlocktype_read);
359         result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze);
360         RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
361         if (tresult == ISC_R_NOTFOUND)
362                 tresult = ISC_R_SUCCESS;
363         return ((result == ISC_R_SUCCESS) ? tresult : result);
364 }
365
366 static isc_result_t
367 freezezones(dns_zone_t *zone, void *uap) {
368         isc_boolean_t freeze = *(isc_boolean_t *)uap;
369         isc_boolean_t frozen;
370         isc_result_t result = ISC_R_SUCCESS;
371         char classstr[DNS_RDATACLASS_FORMATSIZE];
372         char zonename[DNS_NAME_FORMATSIZE];
373         dns_zone_t *raw = NULL;
374         dns_view_t *view;
375         const char *vname;
376         const char *sep;
377         int level;
378
379         dns_zone_getraw(zone, &raw);
380         if (raw != NULL)
381                 zone = raw;
382         if (dns_zone_gettype(zone) != dns_zone_master) {
383                 if (raw != NULL)
384                         dns_zone_detach(&raw);
385                 return (ISC_R_SUCCESS);
386         }
387         if (!dns_zone_isdynamic(zone, ISC_TRUE)) {
388                 if (raw != NULL)
389                         dns_zone_detach(&raw);
390                 return (ISC_R_SUCCESS);
391         }
392
393         frozen = dns_zone_getupdatedisabled(zone);
394         if (freeze) {
395                 if (frozen)
396                         result = DNS_R_FROZEN;
397                 if (result == ISC_R_SUCCESS)
398                         result = dns_zone_flush(zone);
399         } else {
400                 if (frozen) {
401                         result = dns_zone_load(zone);
402                         if (result == DNS_R_CONTINUE ||
403                             result == DNS_R_UPTODATE)
404                                 result = ISC_R_SUCCESS;
405                 }
406         }
407         if (result == ISC_R_SUCCESS)
408                 dns_zone_setupdatedisabled(zone, freeze);
409         view = dns_zone_getview(zone);
410         if (strcmp(view->name, "_bind") == 0 ||
411             strcmp(view->name, "_default") == 0)
412         {
413                 vname = "";
414                 sep = "";
415         } else {
416                 vname = view->name;
417                 sep = " ";
418         }
419         dns_rdataclass_format(dns_zone_getclass(zone), classstr,
420                               sizeof(classstr));
421         dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
422         level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
423         isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
424                       level, "%s zone '%s/%s'%s%s: %s",
425                       freeze ? "freezing" : "thawing",
426                       zonename, classstr, sep, vname,
427                       isc_result_totext(result));
428         if (raw != NULL)
429                 dns_zone_detach(&raw);
430         return (result);
431 }
432
433 isc_result_t
434 dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
435              isc_result_t (*action)(dns_zone_t *, void *), void *uap)
436 {
437         return (dns_zt_apply2(zt, stop, NULL, action, uap));
438 }
439
440 isc_result_t
441 dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
442               isc_result_t (*action)(dns_zone_t *, void *), void *uap)
443 {
444         dns_rbtnode_t *node;
445         dns_rbtnodechain_t chain;
446         isc_result_t result, tresult = ISC_R_SUCCESS;
447         dns_zone_t *zone;
448
449         REQUIRE(VALID_ZT(zt));
450         REQUIRE(action != NULL);
451
452         dns_rbtnodechain_init(&chain, zt->mctx);
453         result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
454         if (result == ISC_R_NOTFOUND) {
455                 /*
456                  * The tree is empty.
457                  */
458                 tresult = result;
459                 result = ISC_R_NOMORE;
460         }
461         while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
462                 result = dns_rbtnodechain_current(&chain, NULL, NULL,
463                                                   &node);
464                 if (result == ISC_R_SUCCESS) {
465                         zone = node->data;
466                         if (zone != NULL)
467                                 result = (action)(zone, uap);
468                         if (result != ISC_R_SUCCESS && stop) {
469                                 tresult = result;
470                                 goto cleanup;   /* don't break */
471                         } else if (result != ISC_R_SUCCESS &&
472                                    tresult == ISC_R_SUCCESS)
473                                 tresult = result;
474                 }
475                 result = dns_rbtnodechain_next(&chain, NULL, NULL);
476         }
477         if (result == ISC_R_NOMORE)
478                 result = ISC_R_SUCCESS;
479
480  cleanup:
481         dns_rbtnodechain_invalidate(&chain);
482         if (sub != NULL)
483                 *sub = tresult;
484
485         return (result);
486 }
487
488 /*
489  * Decrement the loads_pending counter; when counter reaches
490  * zero, call the loaddone callback that was initially set by
491  * dns_zt_asyncload().
492  */
493 static isc_result_t
494 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
495         isc_boolean_t destroy = ISC_FALSE;
496         dns_zt_allloaded_t alldone = NULL;
497         void *arg = NULL;
498
499         UNUSED(zone);
500         UNUSED(task);
501
502         REQUIRE(VALID_ZT(zt));
503
504         RWLOCK(&zt->rwlock, isc_rwlocktype_write);
505         INSIST(zt->loads_pending != 0);
506         INSIST(zt->references != 0);
507         zt->references--;
508         if (zt->references == 0)
509                 destroy = ISC_TRUE;
510         zt->loads_pending--;
511         if (zt->loads_pending == 0) {
512                 alldone = zt->loaddone;
513                 arg = zt->loaddone_arg;
514                 zt->loaddone = NULL;
515                 zt->loaddone_arg = NULL;
516         }
517         RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
518
519         if (alldone != NULL)
520                 alldone(arg);
521
522         if (destroy)
523                 zt_destroy(zt);
524
525         return (ISC_R_SUCCESS);
526 }
527
528 /***
529  *** Private
530  ***/
531
532 static void
533 auto_detach(void *data, void *arg) {
534         dns_zone_t *zone = data;
535
536         UNUSED(arg);
537
538         dns_zone_detach(&zone);
539 }