Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / named / update.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: update.c,v 1.88.2.13 2004/07/23 02:57:00 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/print.h>
23 #include <isc/string.h>
24 #include <isc/taskpool.h>
25 #include <isc/util.h>
26
27 #include <dns/db.h>
28 #include <dns/dbiterator.h>
29 #include <dns/diff.h>
30 #include <dns/dnssec.h>
31 #include <dns/events.h>
32 #include <dns/fixedname.h>
33 #include <dns/journal.h>
34 #include <dns/message.h>
35 #include <dns/nxt.h>
36 #include <dns/rdataclass.h>
37 #include <dns/rdataset.h>
38 #include <dns/rdatasetiter.h>
39 #include <dns/soa.h>
40 #include <dns/ssu.h>
41 #include <dns/view.h>
42 #include <dns/zone.h>
43 #include <dns/zt.h>
44
45 #include <named/client.h>
46 #include <named/log.h>
47 #include <named/update.h>
48
49 /*
50  * This module implements dynamic update as in RFC2136.
51  */
52
53 /*
54   XXX TODO:
55   - document strict minimality
56 */
57
58 /**************************************************************************/
59
60 /*
61  * Log level for tracing dynamic update protocol requests.
62  */
63 #define LOGLEVEL_PROTOCOL       ISC_LOG_INFO
64
65 /*
66  * Log level for low-level debug tracing.
67  */
68 #define LOGLEVEL_DEBUG          ISC_LOG_DEBUG(8)
69
70 /*
71  * Check an operation for failure.  These macros all assume that
72  * the function using them has a 'result' variable and a 'failure'
73  * label.
74  */
75 #define CHECK(op) \
76         do { result = (op);                                      \
77                if (result != ISC_R_SUCCESS) goto failure;        \
78         } while (0)
79
80 /*
81  * Fail unconditionally with result 'code', which must not
82  * be ISC_R_SUCCESS.  The reason for failure presumably has
83  * been logged already.
84  *
85  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
86  * from complaining about "end-of-loop code not reached".
87  */
88
89 #define FAIL(code) \
90         do {                                                    \
91                 result = (code);                                \
92                 if (result != ISC_R_SUCCESS) goto failure;      \
93         } while (0)
94
95 /*
96  * Fail unconditionally and log as a client error.
97  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
98  * from complaining about "end-of-loop code not reached".
99  */
100 #define FAILC(code, msg) \
101         do {                                                    \
102                 result = (code);                                \
103                 update_log(client, zone, LOGLEVEL_PROTOCOL,     \
104                               "update failed: %s (%s)",         \
105                               msg, isc_result_totext(result));  \
106                 if (result != ISC_R_SUCCESS) goto failure;      \
107         } while (0)
108
109 /*
110  * Fail unconditionally and log as a server error.
111  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
112  * from complaining about "end-of-loop code not reached".
113  */
114 #define FAILS(code, msg) \
115         do {                                                    \
116                 result = (code);                                \
117                 update_log(client, zone, LOGLEVEL_PROTOCOL,             \
118                               "error: %s: %s",                  \
119                               msg, isc_result_totext(result));  \
120                 if (result != ISC_R_SUCCESS) goto failure;      \
121         } while (0)
122
123 /**************************************************************************/
124
125 typedef struct rr rr_t;
126
127 struct rr {
128         /* dns_name_t name; */
129         isc_uint32_t            ttl;
130         dns_rdata_t             rdata;
131 };
132
133 typedef struct update_event update_event_t;
134
135 struct update_event {
136         ISC_EVENT_COMMON(update_event_t);
137         dns_zone_t              *zone;
138         isc_result_t            result;
139         dns_message_t           *answer;
140 };
141
142 /**************************************************************************/
143 /*
144  * Forward declarations.
145  */
146
147 static void update_action(isc_task_t *task, isc_event_t *event);
148 static void updatedone_action(isc_task_t *task, isc_event_t *event);
149 static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
150 static void forward_done(isc_task_t *task, isc_event_t *event);
151
152 /**************************************************************************/
153
154 static void
155 update_log(ns_client_t *client, dns_zone_t *zone,
156            int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);
157
158 static void
159 update_log(ns_client_t *client, dns_zone_t *zone,
160            int level, const char *fmt, ...)
161 {
162         va_list ap;
163         char message[4096];
164         char namebuf[DNS_NAME_FORMATSIZE];
165         char classbuf[DNS_RDATACLASS_FORMATSIZE];
166
167         if (client == NULL || zone == NULL)
168                 return;
169
170         if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE)
171                 return;
172
173         dns_name_format(dns_zone_getorigin(zone), namebuf,
174                         sizeof(namebuf));
175         dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
176                               sizeof(classbuf));
177
178         va_start(ap, fmt);
179         vsnprintf(message, sizeof message, fmt, ap);
180         va_end(ap);
181
182         ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
183                       level, "updating zone '%s/%s': %s",
184                       namebuf, classbuf, message);
185 }
186
187 /*
188  * Update a single RR in version 'ver' of 'db' and log the
189  * update in 'diff'.
190  *
191  * Ensures:
192  *   '*tuple' == NULL.  Either the tuple is freed, or its
193  *         ownership has been transferred to the diff.
194  */
195 static isc_result_t
196 do_one_tuple(dns_difftuple_t **tuple,
197              dns_db_t *db, dns_dbversion_t *ver,
198              dns_diff_t *diff)
199 {
200         dns_diff_t temp_diff;
201         isc_result_t result;
202
203         /*
204          * Create a singleton diff.
205          */
206         dns_diff_init(diff->mctx, &temp_diff);
207         ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
208
209         /*
210          * Apply it to the database.
211          */
212         result = dns_diff_apply(&temp_diff, db, ver);
213         ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
214         if (result != ISC_R_SUCCESS) {
215                 dns_difftuple_free(tuple);
216                 return (result);
217         }
218
219         /*
220          * Merge it into the current pending journal entry.
221          */
222         dns_diff_appendminimal(diff, tuple);
223
224         /*
225          * Do not clear temp_diff.
226          */
227         return (ISC_R_SUCCESS);
228 }
229
230 /*
231  * Perform the updates in 'updates' in version 'ver' of 'db' and log the
232  * update in 'diff'.
233  *
234  * Ensures:
235  *   'updates' is empty.
236  */
237 static isc_result_t
238 do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
239         dns_diff_t *diff)
240 {
241         isc_result_t result;
242         while (! ISC_LIST_EMPTY(updates->tuples)) {
243                 dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
244                 ISC_LIST_UNLINK(updates->tuples, t, link);
245                 CHECK(do_one_tuple(&t, db, ver, diff));
246         }
247         return (ISC_R_SUCCESS);
248
249  failure:
250         dns_diff_clear(diff);
251         return (result);
252 }
253
254 static isc_result_t
255 update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
256               dns_diffop_t op, dns_name_t *name,
257               dns_ttl_t ttl, dns_rdata_t *rdata)
258 {
259         dns_difftuple_t *tuple = NULL;
260         isc_result_t result;
261         result = dns_difftuple_create(diff->mctx, op,
262                                       name, ttl, rdata, &tuple);
263         if (result != ISC_R_SUCCESS)
264                 return (result);
265         return (do_one_tuple(&tuple, db, ver, diff));
266 }
267
268 /**************************************************************************/
269 /*
270  * Callback-style iteration over rdatasets and rdatas.
271  *
272  * foreach_rrset() can be used to iterate over the RRsets
273  * of a name and call a callback function with each
274  * one.  Similarly, foreach_rr() can be used to iterate
275  * over the individual RRs at name, optionally restricted
276  * to RRs of a given type.
277  *
278  * The callback functions are called "actions" and take
279  * two arguments: a void pointer for passing arbitrary
280  * context information, and a pointer to the current RRset
281  * or RR.  By convention, their names end in "_action".
282  */
283
284 /*
285  * XXXRTH  We might want to make this public somewhere in libdns.
286  */
287
288 /*
289  * Function type for foreach_rrset() iterator actions.
290  */
291 typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
292
293 /*
294  * Function type for foreach_rr() iterator actions.
295  */
296 typedef isc_result_t rr_func(void *data, rr_t *rr);
297
298 /*
299  * Internal context struct for foreach_node_rr().
300  */
301 typedef struct {
302         rr_func *       rr_action;
303         void *          rr_action_data;
304 } foreach_node_rr_ctx_t;
305
306 /*
307  * Internal helper function for foreach_node_rr().
308  */
309 static isc_result_t
310 foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
311         isc_result_t result;
312         foreach_node_rr_ctx_t *ctx = data;
313         for (result = dns_rdataset_first(rdataset);
314              result == ISC_R_SUCCESS;
315              result = dns_rdataset_next(rdataset))
316         {
317                 rr_t rr = { 0, DNS_RDATA_INIT };
318                 
319                 dns_rdataset_current(rdataset, &rr.rdata);
320                 rr.ttl = rdataset->ttl;
321                 result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
322                 if (result != ISC_R_SUCCESS)
323                         return (result);
324         }
325         if (result != ISC_R_NOMORE)
326                 return (result);
327         return (ISC_R_SUCCESS);
328 }
329
330 /*
331  * For each rdataset of 'name' in 'ver' of 'db', call 'action'
332  * with the rdataset and 'action_data' as arguments.  If the name
333  * does not exist, do nothing.
334  *
335  * If 'action' returns an error, abort iteration and return the error.
336  */
337 static isc_result_t
338 foreach_rrset(dns_db_t *db,
339               dns_dbversion_t *ver,
340               dns_name_t *name,
341               rrset_func *action,
342               void *action_data)
343 {
344         isc_result_t result;
345         dns_dbnode_t *node;
346         dns_rdatasetiter_t *iter;
347
348         node = NULL;
349         result = dns_db_findnode(db, name, ISC_FALSE, &node);
350         if (result == ISC_R_NOTFOUND)
351                 return (ISC_R_SUCCESS);
352         if (result != ISC_R_SUCCESS)
353                 return (result);
354
355         iter = NULL;
356         result = dns_db_allrdatasets(db, node, ver,
357                                      (isc_stdtime_t) 0, &iter);
358         if (result != ISC_R_SUCCESS)
359                 goto cleanup_node;
360
361         for (result = dns_rdatasetiter_first(iter);
362              result == ISC_R_SUCCESS;
363              result = dns_rdatasetiter_next(iter))
364         {
365                 dns_rdataset_t rdataset;
366
367                 dns_rdataset_init(&rdataset);
368                 dns_rdatasetiter_current(iter, &rdataset);
369
370                 result = (*action)(action_data, &rdataset);
371
372                 dns_rdataset_disassociate(&rdataset);
373                 if (result != ISC_R_SUCCESS)
374                         goto cleanup_iterator;
375         }
376         if (result == ISC_R_NOMORE)
377                 result = ISC_R_SUCCESS;
378
379  cleanup_iterator:
380         dns_rdatasetiter_destroy(&iter);
381
382  cleanup_node:
383         dns_db_detachnode(db, &node);
384
385         return (result);
386 }
387
388 /*
389  * For each RR of 'name' in 'ver' of 'db', call 'action'
390  * with the RR and 'action_data' as arguments.  If the name
391  * does not exist, do nothing.
392  *
393  * If 'action' returns an error, abort iteration
394  * and return the error.
395  */
396 static isc_result_t
397 foreach_node_rr(dns_db_t *db,
398             dns_dbversion_t *ver,
399             dns_name_t *name,
400             rr_func *rr_action,
401             void *rr_action_data)
402 {
403         foreach_node_rr_ctx_t ctx;
404         ctx.rr_action = rr_action;
405         ctx.rr_action_data = rr_action_data;
406         return (foreach_rrset(db, ver, name,
407                               foreach_node_rr_action, &ctx));
408 }
409
410
411 /*
412  * For each of the RRs specified by 'db', 'ver', 'name', 'type',
413  * (which can be dns_rdatatype_any to match any type), and 'covers', call
414  * 'action' with the RR and 'action_data' as arguments. If the name
415  * does not exist, or if no RRset of the given type exists at the name,
416  * do nothing.
417  *
418  * If 'action' returns an error, abort iteration and return the error.
419  */
420 static isc_result_t
421 foreach_rr(dns_db_t *db,
422            dns_dbversion_t *ver,
423            dns_name_t *name,
424            dns_rdatatype_t type,
425            dns_rdatatype_t covers,
426            rr_func *rr_action,
427            void *rr_action_data)
428 {
429
430         isc_result_t result;
431         dns_dbnode_t *node;
432         dns_rdataset_t rdataset;
433
434         if (type == dns_rdatatype_any)
435                 return (foreach_node_rr(db, ver, name,
436                                         rr_action, rr_action_data));
437
438         node = NULL;
439         result = dns_db_findnode(db, name, ISC_FALSE, &node);
440         if (result == ISC_R_NOTFOUND)
441                 return (ISC_R_SUCCESS);
442         if (result != ISC_R_SUCCESS)
443                 return (result);
444
445         dns_rdataset_init(&rdataset);
446         result = dns_db_findrdataset(db, node, ver, type, covers,
447                                      (isc_stdtime_t) 0, &rdataset, NULL);
448         if (result == ISC_R_NOTFOUND) {
449                 result = ISC_R_SUCCESS;
450                 goto cleanup_node;
451         }
452         if (result != ISC_R_SUCCESS)
453                 goto cleanup_node;
454
455         for (result = dns_rdataset_first(&rdataset);
456              result == ISC_R_SUCCESS;
457              result = dns_rdataset_next(&rdataset))
458         {
459                 rr_t rr = { 0, DNS_RDATA_INIT };
460                 dns_rdataset_current(&rdataset, &rr.rdata);
461                 rr.ttl = rdataset.ttl;
462                 result = (*rr_action)(rr_action_data, &rr);
463                 if (result != ISC_R_SUCCESS)
464                         goto cleanup_rdataset;
465         }
466         if (result != ISC_R_NOMORE)
467                 goto cleanup_rdataset;
468         result = ISC_R_SUCCESS;
469
470  cleanup_rdataset:
471         dns_rdataset_disassociate(&rdataset);
472  cleanup_node:
473         dns_db_detachnode(db, &node);
474
475         return (result);
476 }
477
478 /**************************************************************************/
479 /*
480  * Various tests on the database contents (for prerequisites, etc).
481  */
482
483 /*
484  * Function type for predicate functions that compare a database RR 'db_rr'
485  * against an update RR 'update_rr'.
486  */
487 typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
488
489 /*
490  * Helper function for rrset_exists().
491  */
492 static isc_result_t
493 rrset_exists_action(void *data, rr_t *rr) {
494         UNUSED(data);
495         UNUSED(rr);
496         return (ISC_R_EXISTS);
497 }
498
499 /*
500  * Utility macro for RR existence checking functions.
501  *
502  * If the variable 'result' has the value ISC_R_EXISTS or
503  * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
504  * respectively, and return success.
505  *
506  * If 'result' has any other value, there was a failure.
507  * Return the failure result code and do not set *exists.
508  *
509  * This would be more readable as "do { if ... } while(0)",
510  * but that form generates tons of warnings on Solaris 2.6.
511  */
512 #define RETURN_EXISTENCE_FLAG                           \
513         return ((result == ISC_R_EXISTS) ?              \
514                 (*exists = ISC_TRUE, ISC_R_SUCCESS) :   \
515                 ((result == ISC_R_SUCCESS) ?            \
516                  (*exists = ISC_FALSE, ISC_R_SUCCESS) : \
517                  result))
518
519 /*
520  * Set '*exists' to true iff an rrset of the given type exists,
521  * to false otherwise.
522  */
523 static isc_result_t
524 rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
525              dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
526              isc_boolean_t *exists)
527 {
528         isc_result_t result;
529         result = foreach_rr(db, ver, name, type, covers,
530                             rrset_exists_action, NULL);
531         RETURN_EXISTENCE_FLAG;
532 }
533
534 /*
535  * Helper function for cname_incompatible_rrset_exists.
536  */
537 static isc_result_t
538 cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
539         UNUSED(data);
540         if (rrset->type != dns_rdatatype_cname &&
541             ! dns_rdatatype_isdnssec(rrset->type))
542                 return (ISC_R_EXISTS);
543         return (ISC_R_SUCCESS);
544 }
545
546 /*
547  * Check whether there is an rrset incompatible with adding a CNAME RR,
548  * i.e., anything but another CNAME (which can be replaced) or a
549  * DNSSEC RR (which can coexist).
550  *
551  * If such an incompatible rrset exists, set '*exists' to ISC_TRUE.
552  * Otherwise, set it to ISC_FALSE.
553  */
554 static isc_result_t
555 cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
556                                 dns_name_t *name, isc_boolean_t *exists) {
557         isc_result_t result;
558         result = foreach_rrset(db, ver, name,
559                                cname_compatibility_action, NULL);
560         RETURN_EXISTENCE_FLAG;
561 }
562
563 /*
564  * Helper function for rr_count().
565  */
566 static isc_result_t
567 count_rr_action(void *data, rr_t *rr) {
568         int *countp = data;
569         UNUSED(rr);
570         (*countp)++;
571         return (ISC_R_SUCCESS);
572 }
573
574 /*
575  * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
576  */
577 static isc_result_t
578 rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
579          dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
580 {
581         *countp = 0;
582         return (foreach_rr(db, ver, name, type, covers,
583                            count_rr_action, countp));
584 }
585
586 /*
587  * Context struct and helper function for name_exists().
588  */
589
590 static isc_result_t
591 name_exists_action(void *data, dns_rdataset_t *rrset) {
592         UNUSED(data);
593         UNUSED(rrset);
594         return (ISC_R_EXISTS);
595 }
596
597 /*
598  * Set '*exists' to true iff the given name exists, to false otherwise.
599  */
600 static isc_result_t
601 name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
602             isc_boolean_t *exists)
603 {
604         isc_result_t result;
605         result = foreach_rrset(db, ver, name,
606                                name_exists_action, NULL);
607         RETURN_EXISTENCE_FLAG;
608 }
609
610 typedef struct {
611         dns_name_t *name, *signer;
612         dns_ssutable_t *table;
613 } ssu_check_t;
614
615 static isc_result_t
616 ssu_checkrule(void *data, dns_rdataset_t *rrset) {
617         ssu_check_t *ssuinfo = data;
618         isc_boolean_t result;
619
620         /*
621          * If we're deleting all records, it's ok to delete SIG and NXT even
622          * if we're normally not allowed to.
623          */
624         if (rrset->type == dns_rdatatype_sig ||
625             rrset->type == dns_rdatatype_nxt)
626                 return (ISC_TRUE);
627         result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
628                                          ssuinfo->name, rrset->type);
629         return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
630 }
631
632 static isc_boolean_t
633 ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
634              dns_ssutable_t *ssutable, dns_name_t *signer)
635 {
636         isc_result_t result;
637         ssu_check_t ssuinfo;
638
639         ssuinfo.name = name;
640         ssuinfo.table = ssutable;
641         ssuinfo.signer = signer;
642         result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
643         return (ISC_TF(result == ISC_R_SUCCESS));
644 }
645
646 /**************************************************************************/
647 /*
648  * Checking of "RRset exists (value dependent)" prerequisites.
649  *
650  * In the RFC2136 section 3.2.5, this is the pseudocode involving
651  * a variable called "temp", a mapping of <name, type> tuples to rrsets.
652  *
653  * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t"
654  * where each typle has op==DNS_DIFFOP_EXISTS.
655  */
656
657
658 /*
659  * Append a tuple asserting the existence of the RR with
660  * 'name' and 'rdata' to 'diff'.
661  */
662 static isc_result_t
663 temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
664         isc_result_t result;
665         dns_difftuple_t *tuple = NULL;
666
667         REQUIRE(DNS_DIFF_VALID(diff));
668         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
669                                       name, 0, rdata, &tuple));
670         ISC_LIST_APPEND(diff->tuples, tuple, link);
671  failure:
672         return (result);
673 }
674
675 /*
676  * Compare two rdatasets represented as sorted lists of tuples.
677  * All list elements must have the same owner name and type.
678  * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
679  * if not.
680  */
681 static isc_result_t
682 temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
683         for (;;) {
684                 if (a == NULL || b == NULL)
685                         break;
686                 INSIST(a->op == DNS_DIFFOP_EXISTS &&
687                        b->op == DNS_DIFFOP_EXISTS);
688                 INSIST(a->rdata.type == b->rdata.type);
689                 INSIST(dns_name_equal(&a->name, &b->name));
690                 if (dns_rdata_compare(&a->rdata, &b->rdata) != 0)
691                         return (DNS_R_NXRRSET);
692                 a = ISC_LIST_NEXT(a, link);
693                 b = ISC_LIST_NEXT(b, link);
694         }
695         if (a != NULL || b != NULL)
696                 return (DNS_R_NXRRSET);
697         return (ISC_R_SUCCESS);
698 }
699
700 /*
701  * A comparison function defining the sorting order for the entries
702  * in the "temp" data structure.  The major sort key is the owner name,
703  * followed by the type and rdata.
704  */
705 static int
706 temp_order(const void *av, const void *bv) {
707         dns_difftuple_t const * const *ap = av;
708         dns_difftuple_t const * const *bp = bv;
709         dns_difftuple_t const *a = *ap;
710         dns_difftuple_t const *b = *bp;
711         int r;
712         r = dns_name_compare(&a->name, &b->name);
713         if (r != 0)
714                 return (r);
715         r = (b->rdata.type - a->rdata.type);
716         if (r != 0)
717                 return (r);
718         r = dns_rdata_compare(&a->rdata, &b->rdata);
719         return (r);
720 }
721
722 /*
723  * Check the "RRset exists (value dependent)" prerequisite information
724  * in 'temp' against the contents of the database 'db'.
725  *
726  * Return ISC_R_SUCCESS if the prerequisites are satisfied,
727  * rcode(dns_rcode_nxrrset) if not.
728  */
729
730 static isc_result_t
731 temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
732            dns_dbversion_t *ver)
733 {
734         isc_result_t result;
735         dns_name_t *name;
736         dns_dbnode_t *node;
737         dns_difftuple_t *t;
738         dns_diff_t trash;
739
740         /* Exit early if the list is empty (for efficiency only). */
741         if (ISC_LIST_HEAD(temp->tuples) == NULL)
742                 return (ISC_R_SUCCESS);
743
744         /*
745          * Sort the prerequisite records by owner name,
746          * type, and rdata.
747          */
748         result = dns_diff_sort(temp, temp_order);
749         if (result != ISC_R_SUCCESS)
750                 return (result);
751
752         dns_diff_init(mctx, &trash);
753
754         /*
755          * For each name and type in the prerequisites,
756          * construct a sorted rdata list of the corresponding
757          * database contents, and compare the lists.
758          */
759         t = ISC_LIST_HEAD(temp->tuples);
760         while (t != NULL) {
761                 name = &t->name;
762
763                 /* A new unique name begins here. */
764                 node = NULL;
765                 result = dns_db_findnode(db, name, ISC_FALSE, &node);
766                 if (result == ISC_R_NOTFOUND)
767                         return (DNS_R_NXRRSET);
768                 if (result != ISC_R_SUCCESS)
769                         return (result);
770
771                 /* A new unique type begins here. */
772                 while (t != NULL && dns_name_equal(&t->name, name)) {
773                         dns_rdatatype_t type, covers;
774                         dns_rdataset_t rdataset;
775                         dns_diff_t d_rrs; /* Database RRs with
776                                                 this name and type */
777                         dns_diff_t u_rrs; /* Update RRs with
778                                                 this name and type */
779
780                         type = t->rdata.type;
781                         if (type == dns_rdatatype_sig)
782                                 covers = dns_rdata_covers(&t->rdata);
783                         else
784                                 covers = 0;
785
786                         /*
787                          * Collect all database RRs for this name and type
788                          * onto d_rrs and sort them.
789                          */
790                         dns_rdataset_init(&rdataset);
791                         result = dns_db_findrdataset(db, node, ver, type,
792                                                      covers, (isc_stdtime_t) 0,
793                                                      &rdataset, NULL);
794                         if (result != ISC_R_SUCCESS) {
795                                 dns_db_detachnode(db, &node);
796                                 return (DNS_R_NXRRSET);
797                         }
798
799                         dns_diff_init(mctx, &d_rrs);
800                         dns_diff_init(mctx, &u_rrs);
801
802                         for (result = dns_rdataset_first(&rdataset);
803                              result == ISC_R_SUCCESS;
804                              result = dns_rdataset_next(&rdataset))
805                         {
806                                 dns_rdata_t rdata = DNS_RDATA_INIT;
807                                 dns_rdataset_current(&rdataset, &rdata);
808                                 result = temp_append(&d_rrs, name, &rdata);
809                                 if (result != ISC_R_SUCCESS)
810                                         goto failure;
811                         }
812                         if (result != ISC_R_NOMORE)
813                                 goto failure;
814                         result = dns_diff_sort(&d_rrs, temp_order);
815                         if (result != ISC_R_SUCCESS)
816                                 goto failure;
817
818                         /*
819                          * Collect all update RRs for this name and type
820                          * onto u_rrs.  No need to sort them here -
821                          * they are already sorted.
822                          */
823                         while (t != NULL &&
824                                dns_name_equal(&t->name, name) &&
825                                t->rdata.type == type)
826                         {
827                                 dns_difftuple_t *next =
828                                         ISC_LIST_NEXT(t, link);
829                                 ISC_LIST_UNLINK(temp->tuples, t, link);
830                                 ISC_LIST_APPEND(u_rrs.tuples, t, link);
831                                 t = next;
832                         }
833
834                         /* Compare the two sorted lists. */
835                         result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
836                                                   ISC_LIST_HEAD(d_rrs.tuples));
837                         if (result != ISC_R_SUCCESS)
838                                 goto failure;
839
840                         /*
841                          * We are done with the tuples, but we can't free
842                          * them yet because "name" still points into one
843                          * of them.  Move them on a temporary list.
844                          */
845                         ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
846                         ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
847                         dns_rdataset_disassociate(&rdataset);
848
849                         continue;
850
851                     failure:
852                         dns_diff_clear(&d_rrs);
853                         dns_diff_clear(&u_rrs);
854                         dns_diff_clear(&trash);
855                         dns_rdataset_disassociate(&rdataset);
856                         dns_db_detachnode(db, &node);
857                         return (result);
858                 }
859
860                 dns_db_detachnode(db, &node);
861         }
862
863         dns_diff_clear(&trash);
864         return (ISC_R_SUCCESS);
865 }
866
867 /**************************************************************************/
868 /*
869  * Conditional deletion of RRs.
870  */
871
872 /*
873  * Context structure for delete_if().
874  */
875
876 typedef struct {
877         rr_predicate *predicate;
878         dns_db_t *db;
879         dns_dbversion_t *ver;
880         dns_diff_t *diff;
881         dns_name_t *name;
882         dns_rdata_t *update_rr;
883 } conditional_delete_ctx_t;
884
885 /*
886  * Predicate functions for delete_if().
887  */
888
889 /*
890  * Return true iff 'update_rr' is neither a SOA nor an NS RR.
891  */
892 static isc_boolean_t
893 type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
894         UNUSED(update_rr);
895         return ((db_rr->type != dns_rdatatype_soa &&
896                  db_rr->type != dns_rdatatype_ns) ?
897                 ISC_TRUE : ISC_FALSE);
898 }
899
900 /*
901  * Return true always.
902  */
903 static isc_boolean_t
904 true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
905         UNUSED(update_rr);
906         UNUSED(db_rr);
907         return (ISC_TRUE);
908 }
909
910 /*
911  * Return true iff the two RRs have identical rdata.
912  */
913 static isc_boolean_t
914 rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
915         /*
916          * XXXRTH  This is not a problem, but we should consider creating
917          *         dns_rdata_equal() (that used dns_name_equal()), since it
918          *         would be faster.  Not a priority.
919          */
920         return (dns_rdata_compare(update_rr, db_rr) == 0 ?
921                 ISC_TRUE : ISC_FALSE);
922 }
923
924 /*
925  * Return true iff 'update_rr' should replace 'db_rr' according
926  * to the special RFC2136 rules for CNAME, SOA, and WKS records.
927  *
928  * RFC2136 does not mention NXT or DNAME, but multiple NXTs or DNAMEs
929  * make little sense, so we replace those, too.
930  */
931 static isc_boolean_t
932 replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
933         if (db_rr->type != update_rr->type)
934                 return (ISC_FALSE);
935         if (db_rr->type == dns_rdatatype_cname)
936                 return (ISC_TRUE);
937         if (db_rr->type == dns_rdatatype_dname)
938                 return (ISC_TRUE);
939         if (db_rr->type == dns_rdatatype_soa)
940                 return (ISC_TRUE);
941         if (db_rr->type == dns_rdatatype_nxt)
942                 return (ISC_TRUE);
943         if (db_rr->type == dns_rdatatype_wks) {
944                 /*
945                  * Compare the address and protocol fields only.  These
946                  * form the first five bytes of the RR data.  Do a
947                  * raw binary comparison; unpacking the WKS RRs using
948                  * dns_rdata_tostruct() might be cleaner in some ways,
949                  * but it would require us to pass around an mctx.
950                  */
951                 INSIST(db_rr->length >= 5 && update_rr->length >= 5);
952                 return (memcmp(db_rr->data, update_rr->data, 5) == 0 ?
953                         ISC_TRUE : ISC_FALSE);
954         }
955         return (ISC_FALSE);
956 }
957
958 /*
959  * Internal helper function for delete_if().
960  */
961 static isc_result_t
962 delete_if_action(void *data, rr_t *rr) {
963         conditional_delete_ctx_t *ctx = data;
964         if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
965                 isc_result_t result;
966                 result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
967                                        DNS_DIFFOP_DEL, ctx->name,
968                                        rr->ttl, &rr->rdata);
969                 return (result);
970         } else {
971                 return (ISC_R_SUCCESS);
972         }
973 }
974
975 /*
976  * Conditionally delete RRs.  Apply 'predicate' to the RRs
977  * specified by 'db', 'ver', 'name', and 'type' (which can
978  * be dns_rdatatype_any to match any type).  Delete those
979  * RRs for which the predicate returns true, and log the
980  * deletions in 'diff'.
981  */
982 static isc_result_t
983 delete_if(rr_predicate *predicate,
984           dns_db_t *db,
985           dns_dbversion_t *ver,
986           dns_name_t *name,
987           dns_rdatatype_t type,
988           dns_rdatatype_t covers,
989           dns_rdata_t *update_rr,
990           dns_diff_t *diff)
991 {
992         conditional_delete_ctx_t ctx;
993         ctx.predicate = predicate;
994         ctx.db = db;
995         ctx.ver = ver;
996         ctx.diff = diff;
997         ctx.name = name;
998         ctx.update_rr = update_rr;
999         return (foreach_rr(db, ver, name, type, covers,
1000                            delete_if_action, &ctx));
1001 }
1002
1003 /**************************************************************************/
1004 /*
1005  * Prepare an RR for the addition of the new RR 'ctx->update_rr',
1006  * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
1007  * the RRs if it is replaced by the new RR or has a conflicting TTL.
1008  * The necessary changes are appended to ctx->del_diff and ctx->add_diff;
1009  * we need to do all deletions before any additions so that we don't run
1010  * into transient states with conflicting TTLs.
1011  */
1012
1013 typedef struct {
1014         dns_db_t *db;
1015         dns_dbversion_t *ver;
1016         dns_diff_t *diff;
1017         dns_name_t *name;
1018         dns_rdata_t *update_rr;
1019         dns_ttl_t update_rr_ttl;
1020         isc_boolean_t ignore_add;
1021         dns_diff_t del_diff;
1022         dns_diff_t add_diff;
1023 } add_rr_prepare_ctx_t;
1024
1025 static isc_result_t
1026 add_rr_prepare_action(void *data, rr_t *rr) {
1027         isc_result_t result = ISC_R_SUCCESS;    
1028         add_rr_prepare_ctx_t *ctx = data;
1029         dns_difftuple_t *tuple = NULL;
1030         isc_boolean_t equal;
1031
1032         /*
1033          * If the update RR is a "duplicate" of the update RR,
1034          * the update should be silently ignored.
1035          */
1036         equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0);
1037         if (equal && rr->ttl == ctx->update_rr_ttl) {
1038                 ctx->ignore_add = ISC_TRUE;
1039                 return (ISC_R_SUCCESS);
1040         }
1041
1042         /*
1043          * If this RR is "equal" to the update RR, it should
1044          * be deleted before the update RR is added.
1045          */
1046         if (replaces_p(ctx->update_rr, &rr->rdata)) {
1047                 CHECK(dns_difftuple_create(ctx->del_diff.mctx,
1048                                            DNS_DIFFOP_DEL, ctx->name,
1049                                            rr->ttl,
1050                                            &rr->rdata,
1051                                            &tuple));
1052                 dns_diff_append(&ctx->del_diff, &tuple);
1053                 return (ISC_R_SUCCESS);
1054         }
1055
1056         /*
1057          * If this RR differs in TTL from the update RR,
1058          * its TTL must be adjusted.
1059          */
1060         if (rr->ttl != ctx->update_rr_ttl) {
1061                 CHECK(dns_difftuple_create(ctx->del_diff.mctx,
1062                                            DNS_DIFFOP_DEL, ctx->name,
1063                                            rr->ttl,
1064                                            &rr->rdata,
1065                                            &tuple));
1066                 dns_diff_append(&ctx->del_diff, &tuple);
1067                 if (!equal) {
1068                         CHECK(dns_difftuple_create(ctx->add_diff.mctx,
1069                                                    DNS_DIFFOP_ADD, ctx->name,
1070                                                    ctx->update_rr_ttl,
1071                                                    &rr->rdata,
1072                                                    &tuple));
1073                         dns_diff_append(&ctx->add_diff, &tuple);
1074                 }
1075         }
1076  failure:
1077         return (result);
1078 }
1079
1080 /**************************************************************************/
1081 /*
1082  * Miscellaneous subroutines.
1083  */
1084
1085 /*
1086  * Extract a single update RR from 'section' of dynamic update message
1087  * 'msg', with consistency checking.
1088  *
1089  * Stores the owner name, rdata, and TTL of the update RR at 'name',
1090  * 'rdata', and 'ttl', respectively.
1091  */
1092 static void
1093 get_current_rr(dns_message_t *msg, dns_section_t section,
1094                dns_rdataclass_t zoneclass,
1095                dns_name_t **name, dns_rdata_t *rdata, dns_rdatatype_t *covers,
1096                dns_ttl_t *ttl,
1097                dns_rdataclass_t *update_class)
1098 {
1099         dns_rdataset_t *rdataset;
1100         isc_result_t result;
1101         dns_message_currentname(msg, section, name);
1102         rdataset = ISC_LIST_HEAD((*name)->list);
1103         INSIST(rdataset != NULL);
1104         INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
1105         *covers = rdataset->covers;
1106         *ttl = rdataset->ttl;
1107         result = dns_rdataset_first(rdataset);
1108         INSIST(result == ISC_R_SUCCESS);
1109         dns_rdataset_current(rdataset, rdata);
1110         INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
1111         *update_class = rdata->rdclass;
1112         rdata->rdclass = zoneclass;
1113 }
1114
1115 /*
1116  * Increment the SOA serial number of database 'db', version 'ver'.
1117  * Replace the SOA record in the database, and log the
1118  * change in 'diff'.
1119  */
1120
1121         /*
1122          * XXXRTH  Failures in this routine will be worth logging, when
1123          *         we have a logging system.  Failure to find the zonename
1124          *         or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
1125          */
1126
1127 static isc_result_t
1128 increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
1129                      dns_diff_t *diff, isc_mem_t *mctx)
1130 {
1131         dns_difftuple_t *deltuple = NULL;
1132         dns_difftuple_t *addtuple = NULL;
1133         isc_uint32_t serial;
1134         isc_result_t result;
1135
1136         CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
1137         CHECK(dns_difftuple_copy(deltuple, &addtuple));
1138         addtuple->op = DNS_DIFFOP_ADD;
1139
1140         serial = dns_soa_getserial(&addtuple->rdata);
1141
1142         /* RFC1982 */
1143         serial = (serial + 1) & 0xFFFFFFFF;
1144         if (serial == 0)
1145                 serial = 1;
1146
1147         dns_soa_setserial(serial, &addtuple->rdata);
1148         CHECK(do_one_tuple(&deltuple, db, ver, diff));
1149         CHECK(do_one_tuple(&addtuple, db, ver, diff));
1150         result = ISC_R_SUCCESS;
1151
1152  failure:
1153         if (addtuple != NULL)
1154                 dns_difftuple_free(&addtuple);
1155         if (deltuple != NULL)
1156                 dns_difftuple_free(&deltuple);
1157         return (result);
1158 }
1159
1160 /*
1161  * Check that the new SOA record at 'update_rdata' does not
1162  * illegally cause the SOA serial number to decrease or stay
1163  * unchanged relative to the existing SOA in 'db'.
1164  *
1165  * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not.
1166  *
1167  * William King points out that RFC2136 is inconsistent about
1168  * the case where the serial number stays unchanged:
1169  *
1170  *   section 3.4.2.2 requires a server to ignore a SOA update request
1171  *   if the serial number on the update SOA is less_than_or_equal to
1172  *   the zone SOA serial.
1173  *
1174  *   section 3.6 requires a server to ignore a SOA update request if
1175  *   the serial is less_than the zone SOA serial.
1176  *
1177  * Paul says 3.4.2.2 is correct.
1178  *
1179  */
1180 static isc_result_t
1181 check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
1182                     dns_rdata_t *update_rdata,
1183                     isc_boolean_t *ok)
1184 {
1185         isc_uint32_t db_serial;
1186         isc_uint32_t update_serial;
1187         isc_result_t result;
1188
1189         update_serial = dns_soa_getserial(update_rdata);
1190
1191         result = dns_db_getsoaserial(db, ver, &db_serial);
1192         if (result != ISC_R_SUCCESS)
1193                 return (result);
1194
1195         if (DNS_SERIAL_GE(db_serial, update_serial)) {
1196                 *ok = ISC_FALSE;
1197         } else {
1198                 *ok = ISC_TRUE;
1199         }
1200
1201         return (ISC_R_SUCCESS);
1202
1203 }
1204
1205 /**************************************************************************/
1206 /*
1207  * Incremental updating of NXTs and SIGs.
1208  */
1209
1210 #define MAXZONEKEYS 32  /* Maximum number of zone keys supported. */
1211
1212 /*
1213  * We abuse the dns_diff_t type to represent a set of domain names
1214  * affected by the update.
1215  */
1216 static isc_result_t
1217 namelist_append_name(dns_diff_t *list, dns_name_t *name) {
1218         isc_result_t result;
1219         dns_difftuple_t *tuple = NULL;
1220         static dns_rdata_t dummy_rdata = { NULL, 0, 0, 0, 0,
1221                                            { (void*)(-1), (void*)(-1) } };
1222         CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
1223                                    &dummy_rdata, &tuple));
1224         dns_diff_append(list, &tuple);
1225  failure:
1226         return (result);
1227 }
1228
1229 static isc_result_t
1230 namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
1231 {
1232         isc_result_t result;
1233         dns_fixedname_t fixedname;
1234         dns_name_t *child;
1235         dns_dbiterator_t *dbit = NULL;
1236
1237         dns_fixedname_init(&fixedname);
1238         child = dns_fixedname_name(&fixedname);
1239
1240         CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
1241
1242         for (result = dns_dbiterator_seek(dbit, name);
1243              result == ISC_R_SUCCESS;
1244              result = dns_dbiterator_next(dbit))
1245         {
1246                 dns_dbnode_t *node = NULL;
1247                 result = dns_dbiterator_current(dbit, &node, child);
1248                 dns_db_detachnode(db, &node);
1249                 CHECK(result);
1250                 if (! dns_name_issubdomain(child, name))
1251                         break;
1252                 CHECK(namelist_append_name(affected, child));
1253         }
1254         if (result == ISC_R_NOMORE)
1255                 result = ISC_R_SUCCESS;
1256  failure:
1257         if (dbit != NULL)
1258                 dns_dbiterator_destroy(&dbit);
1259         return (result);
1260 }
1261
1262
1263
1264 /*
1265  * Helper function for non_nxt_rrset_exists().
1266  */
1267 static isc_result_t
1268 is_non_nxt_action(void *data, dns_rdataset_t *rrset) {
1269         UNUSED(data);
1270         if (!(rrset->type == dns_rdatatype_nxt ||
1271               (rrset->type == dns_rdatatype_sig &&
1272                rrset->covers == dns_rdatatype_nxt)))
1273                 return (ISC_R_EXISTS);
1274         return (ISC_R_SUCCESS);
1275 }
1276
1277 /*
1278  * Check whether there is an rrset other than a NXT or SIG NXT,
1279  * i.e., anything that justifies the continued existence of a name
1280  * after a secure update.
1281  *
1282  * If such an rrset exists, set '*exists' to ISC_TRUE.
1283  * Otherwise, set it to ISC_FALSE.
1284  */
1285 static isc_result_t
1286 non_nxt_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
1287                      dns_name_t *name, isc_boolean_t *exists)
1288 {
1289         isc_result_t result;
1290         result = foreach_rrset(db, ver, name,
1291                                is_non_nxt_action, NULL);
1292         RETURN_EXISTENCE_FLAG;
1293 }
1294
1295 /*
1296  * A comparison function for sorting dns_diff_t:s by name.
1297  */
1298 static int
1299 name_order(const void *av, const void *bv) {
1300         dns_difftuple_t const * const *ap = av;
1301         dns_difftuple_t const * const *bp = bv;
1302         dns_difftuple_t const *a = *ap;
1303         dns_difftuple_t const *b = *bp;
1304         return (dns_name_compare(&a->name, &b->name));
1305 }
1306
1307 static isc_result_t
1308 uniqify_name_list(dns_diff_t *list) {
1309         isc_result_t result;
1310         dns_difftuple_t *p, *q;
1311
1312         CHECK(dns_diff_sort(list, name_order));
1313
1314         p = ISC_LIST_HEAD(list->tuples);
1315         while (p != NULL) {
1316                 do {
1317                         q = ISC_LIST_NEXT(p, link);
1318                         if (q == NULL || ! dns_name_equal(&p->name, &q->name))
1319                                 break;
1320                         ISC_LIST_UNLINK(list->tuples, q, link);
1321                         dns_difftuple_free(&q);
1322                 } while (1);
1323                 p = ISC_LIST_NEXT(p, link);
1324         }
1325  failure:
1326         return (result);
1327 }
1328
1329
1330 static isc_result_t
1331 is_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
1332         isc_boolean_t *flag)
1333 {
1334         isc_result_t result;
1335         dns_fixedname_t foundname;
1336         dns_fixedname_init(&foundname);
1337         result = dns_db_find(db, name, ver, dns_rdatatype_any,
1338                              DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
1339                              (isc_stdtime_t) 0, NULL,
1340                              dns_fixedname_name(&foundname),
1341                              NULL, NULL);
1342         if (result == ISC_R_SUCCESS) {
1343                 *flag = ISC_FALSE;
1344                 return (ISC_R_SUCCESS);
1345         } else if (result == DNS_R_ZONECUT) {
1346                 /*
1347                  * We are at the zonecut.  The name will have an NXT, but
1348                  * non-delegation will be omitted from the type bit map.
1349                  */
1350                 *flag = ISC_FALSE;
1351                 return (ISC_R_SUCCESS);
1352         } else if (result == DNS_R_GLUE || result == DNS_R_DNAME) {
1353                 *flag = ISC_TRUE;
1354                 return (ISC_R_SUCCESS);
1355         } else {
1356                 return (result);
1357         }
1358 }
1359
1360 /*
1361  * Find the next/previous name that has a NXT record.
1362  * In other words, skip empty database nodes and names that
1363  * have had their NXTs removed because they are obscured by
1364  * a zone cut.
1365  */
1366 static isc_result_t
1367 next_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *oldname,
1368      dns_name_t *newname, isc_boolean_t forward)
1369 {
1370         isc_result_t result;
1371         dns_dbiterator_t *dbit = NULL;
1372         isc_boolean_t has_nxt;
1373         unsigned int wraps = 0;
1374
1375         CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
1376
1377         CHECK(dns_dbiterator_seek(dbit, oldname));
1378         do {
1379                 dns_dbnode_t *node = NULL;
1380
1381                 if (forward)
1382                         result = dns_dbiterator_next(dbit);
1383                 else
1384                         result = dns_dbiterator_prev(dbit);
1385                 if (result == ISC_R_NOMORE) {
1386                         /*
1387                          * Wrap around.
1388                          */
1389                         if (forward)
1390                                 CHECK(dns_dbiterator_first(dbit));
1391                         else
1392                                 CHECK(dns_dbiterator_last(dbit));
1393                         wraps++;
1394                         if (wraps == 2) {
1395                                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE,
1396                                               NS_LOGMODULE_UPDATE,
1397                                               ISC_LOG_ERROR,
1398                                               "secure zone with no NXTs");
1399                                 result = DNS_R_BADZONE;
1400                                 goto failure;
1401                         }
1402                 }
1403                 dns_dbiterator_current(dbit, &node, newname);
1404                 dns_db_detachnode(db, &node);
1405
1406                 /*
1407                  * The iterator may hold the tree lock, and
1408                  * rrset_exists() calls dns_db_findnode() which
1409                  * may try to reacquire it.  To avoid deadlock
1410                  * we must pause the iterator first.
1411                  */
1412                 CHECK(dns_dbiterator_pause(dbit));
1413                 CHECK(rrset_exists(db, ver, newname,
1414                                    dns_rdatatype_nxt, 0, &has_nxt));
1415
1416         } while (! has_nxt);
1417  failure:
1418         if (dbit != NULL)
1419                 dns_dbiterator_destroy(&dbit);
1420
1421         return (result);
1422 }
1423
1424 /*
1425  * Add a NXT record for "name", recording the change in "diff".
1426  * The existing NXT is removed.
1427  */
1428 static isc_result_t
1429 add_nxt(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_diff_t *diff)
1430 {
1431         isc_result_t result;
1432         dns_dbnode_t *node = NULL;
1433         unsigned char buffer[DNS_NXT_BUFFERSIZE];
1434         dns_rdata_t rdata = DNS_RDATA_INIT;
1435         dns_difftuple_t *tuple = NULL;
1436         dns_fixedname_t fixedname;
1437         dns_name_t *target;
1438
1439         dns_fixedname_init(&fixedname);
1440         target = dns_fixedname_name(&fixedname);
1441
1442         /*
1443          * Find the successor name, aka NXT target.
1444          */
1445         CHECK(next_active(db, ver, name, target, ISC_TRUE));
1446
1447         /*
1448          * Create the NXT RDATA.
1449          */
1450         CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
1451         dns_rdata_init(&rdata);
1452         CHECK(dns_nxt_buildrdata(db, ver, node, target, buffer, &rdata));
1453         dns_db_detachnode(db, &node);
1454
1455         /*
1456          * Delete the old NXT and record the change.
1457          */
1458         CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nxt, 0,
1459                         NULL, diff));
1460         /*
1461          * Add the new NXT and record the change.
1462          */
1463         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
1464                                    3600,        /* XXXRTH */
1465                                    &rdata, &tuple));
1466         CHECK(do_one_tuple(&tuple, db, ver, diff));
1467         INSIST(tuple == NULL);
1468
1469  failure:
1470         if (node != NULL)
1471                 dns_db_detachnode(db, &node);
1472         return (result);
1473 }
1474
1475 /*
1476  * Add a placeholder NXT record for "name", recording the change in "diff".
1477  */
1478 static isc_result_t
1479 add_placeholder_nxt(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
1480                     dns_diff_t *diff) {
1481         isc_result_t result;
1482         dns_difftuple_t *tuple = NULL;
1483         isc_region_t r;
1484         unsigned char data[1] = { 0 }; /* The root domain, no bits. */
1485         dns_rdata_t rdata = DNS_RDATA_INIT;
1486
1487         r.base = data;
1488         r.length = sizeof data;
1489         dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nxt, &r);
1490         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
1491                                    &rdata, &tuple));
1492         CHECK(do_one_tuple(&tuple, db, ver, diff));
1493  failure:
1494         return (result);
1495 }
1496
1497 static isc_result_t
1498 find_zone_keys(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
1499                unsigned int maxkeys, dst_key_t **keys, unsigned int *nkeys)
1500 {
1501         isc_result_t result;
1502         dns_dbnode_t *node = NULL;
1503         CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
1504         CHECK(dns_dnssec_findzonekeys(db, ver, node, dns_db_origin(db),
1505                                       mctx, maxkeys, keys, nkeys));
1506  failure:
1507         if (node != NULL)
1508                 dns_db_detachnode(db, &node);
1509         return (result);
1510 }
1511
1512 /*
1513  * Add SIG records for an RRset, recording the change in "diff".
1514  */
1515 static isc_result_t
1516 add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
1517          dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
1518          unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception,
1519          isc_stdtime_t expire)
1520 {
1521         isc_result_t result;
1522         dns_dbnode_t *node = NULL;
1523         dns_rdataset_t rdataset;
1524         dns_rdata_t sig_rdata = DNS_RDATA_INIT;
1525         isc_buffer_t buffer;
1526         unsigned char data[1024]; /* XXX */
1527         unsigned int i;
1528
1529         dns_rdataset_init(&rdataset);
1530         isc_buffer_init(&buffer, data, sizeof(data));
1531
1532         /* Get the rdataset to sign. */
1533         CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
1534         CHECK(dns_db_findrdataset(db, node, ver, type, 0,
1535                                   (isc_stdtime_t) 0,
1536                                   &rdataset, NULL));
1537         dns_db_detachnode(db, &node);
1538
1539         for (i = 0; i < nkeys; i++) {
1540                 /* Calculate the signature, creating a SIG RDATA. */
1541                 CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
1542                                       &inception, &expire,
1543                                       mctx, &buffer, &sig_rdata));
1544
1545                 /* Update the database and journal with the SIG. */
1546                 /* XXX inefficient - will cause dataset merging */
1547                 CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name,
1548                                     rdataset.ttl, &sig_rdata));
1549                 dns_rdata_reset(&sig_rdata);
1550         }
1551
1552  failure:
1553         if (dns_rdataset_isassociated(&rdataset))
1554                 dns_rdataset_disassociate(&rdataset);
1555         if (node != NULL)
1556                 dns_db_detachnode(db, &node);
1557         return (result);
1558 }
1559
1560 /*
1561  * Update SIG and NXT records affected by an update.  The original
1562  * update, including the SOA serial update but exluding the SIG & NXT
1563  * changes, is in "diff" and has already been applied to "newver" of "db".
1564  * The database version prior to the update is "oldver".
1565  *
1566  * The necessary SIG and NXT changes will be applied to "newver"
1567  * and added (as a minimal diff) to "diff".
1568  *
1569  * The SIGs generated will be valid for 'sigvalidityinterval' seconds.
1570  */
1571 static isc_result_t
1572 update_signatures(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *oldver,
1573                   dns_dbversion_t *newver, dns_diff_t *diff,
1574                   isc_uint32_t sigvalidityinterval)
1575 {
1576         isc_result_t result;
1577         dns_difftuple_t *t;
1578         dns_diff_t diffnames;
1579         dns_diff_t affected;
1580         dns_diff_t sig_diff;
1581         dns_diff_t nxt_diff;
1582         dns_diff_t nxt_mindiff;
1583         isc_boolean_t flag;
1584         dst_key_t *zone_keys[MAXZONEKEYS];
1585         unsigned int nkeys = 0;
1586         unsigned int i;
1587         isc_stdtime_t now, inception, expire;
1588
1589         dns_diff_init(mctx, &diffnames);
1590         dns_diff_init(mctx, &affected);
1591
1592         dns_diff_init(mctx, &sig_diff);
1593         dns_diff_init(mctx, &nxt_diff);
1594         dns_diff_init(mctx, &nxt_mindiff);
1595
1596         result = find_zone_keys(db, newver, mctx,
1597                                 MAXZONEKEYS, zone_keys, &nkeys);
1598         if (result != ISC_R_SUCCESS) {
1599                 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE,
1600                               NS_LOGMODULE_UPDATE, ISC_LOG_ERROR,
1601                               "could not get zone keys for secure "
1602                               "dynamic update");
1603                 goto failure;
1604         }
1605
1606         isc_stdtime_get(&now);
1607         inception = now - 3600; /* Allow for some clock skew. */
1608         expire = now + sigvalidityinterval;
1609
1610         /*
1611          * Find all RRsets directly affected by the update, and
1612          * update their SIGs.  Also build a list of names affected
1613          * by the update in "diffnames".
1614          */
1615         CHECK(dns_diff_sort(diff, temp_order));
1616
1617         t = ISC_LIST_HEAD(diff->tuples);
1618         while (t != NULL) {
1619                 dns_name_t *name = &t->name;
1620                 /* Now "name" is a new, unique name affected by the update. */
1621
1622                 CHECK(namelist_append_name(&diffnames, name));
1623
1624                 while (t != NULL && dns_name_equal(&t->name, name)) {
1625                         dns_rdatatype_t type;
1626                         type = t->rdata.type;
1627
1628                         /*
1629                          * Now "name" and "type" denote a new unique RRset
1630                          * affected by the update.
1631                          */
1632
1633                         /* Don't sign SIGs. */
1634                         if (type == dns_rdatatype_sig)
1635                                 goto skip;
1636
1637                         /*
1638                          * Delete all old SIGs covering this type, since they
1639                          * are all invalid when the signed RRset has changed.
1640                          * We may not be able to recreate all of them - tough.
1641                          */
1642                         CHECK(delete_if(true_p, db, newver, name,
1643                                         dns_rdatatype_sig, type,
1644                                         NULL, &sig_diff));
1645
1646                         /*
1647                          * If this RRset still exists after the update,
1648                          * add a new signature for it.
1649                          */
1650                         CHECK(rrset_exists(db, newver, name, type, 0, &flag));
1651                         if (flag) {
1652                                 CHECK(add_sigs(db, newver, name, type,
1653                                                &sig_diff, zone_keys, nkeys,
1654                                                mctx, inception, expire));
1655                         }
1656                 skip:
1657                         /* Skip any other updates to the same RRset. */
1658                         while (t != NULL &&
1659                                dns_name_equal(&t->name, name) &&
1660                                t->rdata.type == type)
1661                         {
1662                                 t = ISC_LIST_NEXT(t, link);
1663                         }
1664                 }
1665         }
1666
1667         /* Remove orphaned NXTs and SIG NXTs. */
1668         for (t = ISC_LIST_HEAD(diffnames.tuples);
1669              t != NULL;
1670              t = ISC_LIST_NEXT(t, link))
1671         {
1672                 CHECK(non_nxt_rrset_exists(db, newver, &t->name, &flag));
1673                 if (! flag) {
1674                         CHECK(delete_if(true_p, db, newver, &t->name,
1675                                         dns_rdatatype_any, 0,
1676                                         NULL, &sig_diff));
1677                 }
1678         }
1679
1680         /*
1681          * When a name is created or deleted, its predecessor needs to
1682          * have its NXT updated.
1683          */
1684         for (t = ISC_LIST_HEAD(diffnames.tuples);
1685              t != NULL;
1686              t = ISC_LIST_NEXT(t, link))
1687         {
1688                 isc_boolean_t existed, exists;
1689                 dns_fixedname_t fixedname;
1690                 dns_name_t *prevname;
1691
1692                 dns_fixedname_init(&fixedname);
1693                 prevname = dns_fixedname_name(&fixedname);
1694
1695                 CHECK(name_exists(db, oldver, &t->name, &existed));
1696                 CHECK(name_exists(db, newver, &t->name, &exists));
1697                 if (exists == existed)
1698                         continue;
1699
1700                 /*
1701                  * Find the predecessor.
1702                  * When names become obscured or unobscured in this update
1703                  * transaction, we may find the wrong predecessor because
1704                  * the NXTs have not yet been updated to reflect the delegation
1705                  * change.  This should not matter because in this case,
1706                  * the correct predecessor is either the delegation node or
1707                  * a newly unobscured node, and those nodes are on the
1708                  * "affected" list in any case.
1709                  */
1710                 CHECK(next_active(db, newver, &t->name, prevname, ISC_FALSE));
1711                 CHECK(namelist_append_name(&affected, prevname));
1712         }
1713
1714         /*
1715          * Find names potentially affected by delegation changes
1716          * (obscured by adding an NS or DNAME, or unobscured by
1717          * removing one).
1718          */
1719         for (t = ISC_LIST_HEAD(diffnames.tuples);
1720              t != NULL;
1721              t = ISC_LIST_NEXT(t, link))
1722         {
1723                 isc_boolean_t ns_existed, dname_existed;
1724                 isc_boolean_t ns_exists, dname_exists;
1725
1726                 CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0,
1727                                    &ns_existed));
1728                 CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0,
1729                                    &dname_existed));
1730                 CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
1731                                    &ns_exists));
1732                 CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0,
1733                                    &dname_exists));
1734                 if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
1735                         continue;
1736                 /*
1737                  * There was a delegation change.  Mark all subdomains
1738                  * of t->name as potentially needing a NXT update.
1739                  */
1740                 CHECK(namelist_append_subdomain(db, &t->name, &affected));
1741         }
1742
1743         ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link);
1744         INSIST(ISC_LIST_EMPTY(diffnames.tuples));
1745
1746         CHECK(uniqify_name_list(&affected));
1747
1748         /*
1749          * Determine which names should have NXTs, and delete/create
1750          * NXTs to make it so.  We don't know the final NXT targets yet,
1751          * so we just create placeholder NXTs with arbitrary contents
1752          * to indicate that their respective owner names should be part of
1753          * the NXT chain.
1754          */
1755         for (t = ISC_LIST_HEAD(affected.tuples);
1756              t != NULL;
1757              t = ISC_LIST_NEXT(t, link))
1758         {
1759                 isc_boolean_t exists;
1760                 CHECK(name_exists(db, newver, &t->name, &exists));
1761                 if (! exists)
1762                         continue;
1763                 CHECK(is_glue(db, newver, &t->name, &flag));
1764                 if (flag) {
1765                         /*
1766                          * This name is obscured.  Delete any
1767                          * existing NXT record.
1768                          */
1769                         CHECK(delete_if(true_p, db, newver, &t->name,
1770                                         dns_rdatatype_nxt, 0,
1771                                         NULL, &nxt_diff));
1772                 } else {
1773                         /*
1774                          * This name is not obscured.  It should have a NXT.
1775                          */
1776                         CHECK(rrset_exists(db, newver, &t->name,
1777                                            dns_rdatatype_nxt, 0, &flag));
1778                         if (! flag) {
1779                                 add_placeholder_nxt(db, newver, &t->name,
1780                                                     diff);
1781                         }
1782                 }
1783         }
1784
1785         /*
1786          * Now we know which names are part of the NXT chain.
1787          * Make them all point at their correct targets.
1788          */
1789         for (t = ISC_LIST_HEAD(affected.tuples);
1790              t != NULL;
1791              t = ISC_LIST_NEXT(t, link))
1792         {
1793                 CHECK(rrset_exists(db, newver, &t->name,
1794                                    dns_rdatatype_nxt, 0, &flag));
1795                 if (flag) {
1796                         /*
1797                          * There is a NXT, but we don't know if it is correct.
1798                          * Delete it and create a correct one to be sure.
1799                          * If the update was unnecessary, the diff minimization
1800                          * will take care of eliminating it from the journal,
1801                          * IXFRs, etc.
1802                          *
1803                          * The SIG bit should always be set in the NXTs
1804                          * we generate, because they will all get SIG NXTs.
1805                          * (XXX what if the zone keys are missing?).
1806                          * Because the SIG NXTs have not necessarily been
1807                          * created yet, the correctness of the bit mask relies
1808                          * on the assumption that NXTs are only created if
1809                          * there is other data, and if there is other data,
1810                          * there are other SIGs.
1811                          */
1812                         CHECK(add_nxt(db, newver, &t->name, &nxt_diff));
1813                 }
1814         }
1815
1816         /*
1817          * Minimize the set of NXT updates so that we don't
1818          * have to regenerate the SIG NXTs for NXTs that were
1819          * replaced with identical ones.
1820          */
1821         while ((t = ISC_LIST_HEAD(nxt_diff.tuples)) != NULL) {
1822                 ISC_LIST_UNLINK(nxt_diff.tuples, t, link);
1823                 dns_diff_appendminimal(&nxt_mindiff, &t);
1824         }
1825
1826         /* Update SIG NXTs. */
1827         for (t = ISC_LIST_HEAD(nxt_mindiff.tuples);
1828              t != NULL;
1829              t = ISC_LIST_NEXT(t, link))
1830         {
1831                 if (t->op == DNS_DIFFOP_DEL) {
1832                         CHECK(delete_if(true_p, db, newver, &t->name,
1833                                         dns_rdatatype_sig, dns_rdatatype_nxt,
1834                                         NULL, &sig_diff));
1835                 } else if (t->op == DNS_DIFFOP_ADD) {
1836                         CHECK(add_sigs(db, newver, &t->name, dns_rdatatype_nxt,
1837                                        &sig_diff, zone_keys, nkeys, mctx,
1838                                        inception, expire));
1839                 } else {
1840                         INSIST(0);
1841                 }
1842         }
1843
1844         /* Record our changes for the journal. */
1845         while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
1846                 ISC_LIST_UNLINK(sig_diff.tuples, t, link);
1847                 dns_diff_appendminimal(diff, &t);
1848         }
1849         while ((t = ISC_LIST_HEAD(nxt_mindiff.tuples)) != NULL) {
1850                 ISC_LIST_UNLINK(nxt_mindiff.tuples, t, link);
1851                 dns_diff_appendminimal(diff, &t);
1852         }
1853
1854         INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
1855         INSIST(ISC_LIST_EMPTY(nxt_diff.tuples));
1856         INSIST(ISC_LIST_EMPTY(nxt_mindiff.tuples));
1857
1858  failure:
1859         dns_diff_clear(&sig_diff);
1860         dns_diff_clear(&nxt_diff);
1861         dns_diff_clear(&nxt_mindiff);
1862
1863         dns_diff_clear(&affected);
1864         dns_diff_clear(&diffnames);
1865
1866         for (i = 0; i < nkeys; i++)
1867                 dst_key_free(&zone_keys[i]);
1868
1869         return (result);
1870 }
1871
1872
1873 /**************************************************************************/
1874 /*
1875  * The actual update code in all its glory.  We try to follow
1876  * the RFC2136 pseudocode as closely as possible.
1877  */
1878
1879 static isc_result_t
1880 send_update_event(ns_client_t *client, dns_zone_t *zone) {
1881         isc_result_t result = ISC_R_SUCCESS;
1882         update_event_t *event = NULL;
1883         isc_task_t *zonetask = NULL;
1884         ns_client_t *evclient;
1885
1886         event = (update_event_t *)
1887                 isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
1888                                    update_action, NULL, sizeof(*event));
1889         if (event == NULL)
1890                 FAIL(ISC_R_NOMEMORY);
1891         event->zone = zone;
1892         event->result = ISC_R_SUCCESS;
1893
1894         evclient = NULL;
1895         ns_client_attach(client, &evclient);
1896         INSIST(client->nupdates == 0);
1897         client->nupdates++;
1898         event->ev_arg = evclient;
1899
1900         dns_zone_gettask(zone, &zonetask);
1901         isc_task_send(zonetask, ISC_EVENT_PTR(&event));
1902
1903  failure:
1904         if (event != NULL)
1905                 isc_event_free(ISC_EVENT_PTR(&event));
1906         return (result);
1907 }
1908
1909 static void
1910 respond(ns_client_t *client, isc_result_t result) {
1911         isc_result_t msg_result;
1912
1913         msg_result = dns_message_reply(client->message, ISC_TRUE);
1914         if (msg_result != ISC_R_SUCCESS)
1915                 goto msg_failure;
1916         client->message->rcode = dns_result_torcode(result);
1917
1918         ns_client_send(client);
1919         return;
1920
1921  msg_failure:
1922         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
1923                       ISC_LOG_ERROR,
1924                       "could not create update response message: %s",
1925                       isc_result_totext(msg_result));
1926         ns_client_next(client, msg_result);
1927 }
1928
1929 void
1930 ns_update_start(ns_client_t *client, isc_result_t sigresult) {
1931         dns_message_t *request = client->message;
1932         isc_result_t result;
1933         dns_name_t *zonename;
1934         dns_rdataset_t *zone_rdataset;
1935         dns_zone_t *zone = NULL;
1936
1937         /*
1938          * Interpret the zone section.
1939          */
1940         result = dns_message_firstname(request, DNS_SECTION_ZONE);
1941         if (result != ISC_R_SUCCESS)
1942                 FAILC(DNS_R_FORMERR,
1943                       "update zone section empty");
1944
1945         /*
1946          * The zone section must contain exactly one "question", and
1947          * it must be of type SOA.
1948          */
1949         zonename = NULL;
1950         dns_message_currentname(request, DNS_SECTION_ZONE, &zonename);
1951         zone_rdataset = ISC_LIST_HEAD(zonename->list);
1952         if (zone_rdataset->type != dns_rdatatype_soa)
1953                 FAILC(DNS_R_FORMERR,
1954                       "update zone section contains non-SOA");
1955         if (ISC_LIST_NEXT(zone_rdataset, link) != NULL)
1956                 FAILC(DNS_R_FORMERR,
1957                       "update zone section contains multiple RRs");
1958
1959         /* The zone section must have exactly one name. */
1960         result = dns_message_nextname(request, DNS_SECTION_ZONE);
1961         if (result != ISC_R_NOMORE)
1962                 FAILC(DNS_R_FORMERR,
1963                       "update zone section contains multiple RRs");
1964
1965         result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
1966                              &zone);
1967         if (result != ISC_R_SUCCESS)
1968                 FAILC(DNS_R_NOTAUTH,
1969                       "not authoritative for update zone");
1970
1971         switch(dns_zone_gettype(zone)) {
1972         case dns_zone_master:
1973                 /*
1974                  * We can now fail due to a bad signature as we now know
1975                  * that we are the master.
1976                  */
1977                 if (sigresult != ISC_R_SUCCESS)
1978                         FAIL(sigresult);
1979                 CHECK(send_update_event(client, zone));
1980                 break;
1981         case dns_zone_slave:
1982                 if (dns_zone_getforwardacl(zone) == NULL) {
1983                         result = DNS_R_NOTIMP;
1984                         ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
1985                                       NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
1986                                       "update forwarding denied");
1987                         goto failure;
1988                 }
1989                 CHECK(ns_client_checkacl(client, "update forwarding",
1990                                          dns_zone_getforwardacl(zone),
1991                                          ISC_FALSE, ISC_LOG_ERROR));
1992                 CHECK(send_forward_event(client, zone));
1993                 break;
1994         default:
1995                 FAILC(DNS_R_NOTAUTH,
1996                       "not authoritative for update zone");
1997         }
1998         return;
1999
2000  failure:
2001         /*
2002          * We failed without having sent an update event to the zone.
2003          * We are still in the client task context, so we can
2004          * simply give an error response without switching tasks.
2005          */
2006         respond(client, result);
2007         if (zone != NULL)
2008                 dns_zone_detach(&zone);
2009 }
2010
2011 static void
2012 update_action(isc_task_t *task, isc_event_t *event) {
2013         update_event_t *uev = (update_event_t *) event;
2014         dns_zone_t *zone = uev->zone;
2015         ns_client_t *client = (ns_client_t *)event->ev_arg;
2016
2017         isc_result_t result;
2018         dns_db_t *db = NULL;
2019         dns_dbversion_t *oldver = NULL;
2020         dns_dbversion_t *ver = NULL;
2021         dns_diff_t diff;        /* Pending updates. */
2022         dns_diff_t temp;        /* Pending RR existence assertions. */
2023         isc_boolean_t soa_serial_changed = ISC_FALSE;
2024         isc_mem_t *mctx = client->mctx;
2025         dns_rdatatype_t covers;
2026         dns_message_t *request = client->message;
2027         dns_rdataclass_t zoneclass;
2028         dns_name_t *zonename;
2029         dns_ssutable_t *ssutable = NULL;
2030
2031         INSIST(event->ev_type == DNS_EVENT_UPDATE);
2032
2033         dns_diff_init(mctx, &diff);
2034         dns_diff_init(mctx, &temp);
2035
2036         CHECK(dns_zone_getdb(zone, &db));
2037         zonename = dns_db_origin(db);
2038         zoneclass = dns_db_class(db);
2039         dns_zone_getssutable(zone, &ssutable);
2040         dns_db_currentversion(db, &oldver);
2041         CHECK(dns_db_newversion(db, &ver));
2042
2043         /*
2044          * Check prerequisites.
2045          */
2046
2047         for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE);
2048              result == ISC_R_SUCCESS;
2049              result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE))
2050         {
2051                 dns_name_t *name = NULL;
2052                 dns_rdata_t rdata = DNS_RDATA_INIT;
2053                 dns_ttl_t ttl;
2054                 dns_rdataclass_t update_class;
2055                 isc_boolean_t flag;
2056
2057                 get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass,
2058                                &name, &rdata, &covers, &ttl, &update_class);
2059
2060                 if (ttl != 0)
2061                         FAILC(DNS_R_FORMERR, "prerequisite TTL is not zero");
2062
2063                 if (! dns_name_issubdomain(name, zonename))
2064                         FAILC(DNS_R_NOTZONE,
2065                                 "prerequisite name is out of zone");
2066
2067                 if (update_class == dns_rdataclass_any) {
2068                         if (rdata.length != 0)
2069                                 FAILC(DNS_R_FORMERR,
2070                                       "class ANY prerequisite "
2071                                       "RDATA is not empty");
2072                         if (rdata.type == dns_rdatatype_any) {
2073                                 CHECK(name_exists(db, ver, name, &flag));
2074                                 if (! flag) {
2075                                         FAILC(DNS_R_NXDOMAIN,
2076                                               "'name in use' prerequisite "
2077                                               "not satisfied");
2078                                 }
2079                         } else {
2080                                 CHECK(rrset_exists(db, ver, name,
2081                                                    rdata.type, covers, &flag));
2082                                 if (! flag) {
2083                                         /* RRset does not exist. */
2084                                         FAILC(DNS_R_NXRRSET,
2085                                         "'rrset exists (value independent)' "
2086                                         "prerequisite not satisfied");
2087                                 }
2088                         }
2089                 } else if (update_class == dns_rdataclass_none) {
2090                         if (rdata.length != 0)
2091                                 FAILC(DNS_R_FORMERR,
2092                                       "class NONE prerequisite "
2093                                       "RDATA is not empty");
2094                         if (rdata.type == dns_rdatatype_any) {
2095                                 CHECK(name_exists(db, ver, name, &flag));
2096                                 if (flag) {
2097                                         FAILC(DNS_R_YXDOMAIN,
2098                                               "'name not in use' prerequisite "
2099                                               "not satisfied");
2100                                 }
2101                         } else {
2102                                 CHECK(rrset_exists(db, ver, name,
2103                                                    rdata.type, covers, &flag));
2104                                 if (flag) {
2105                                         /* RRset exists. */
2106                                         FAILC(DNS_R_YXRRSET,
2107                                               "'rrset does not exist' "
2108                                               "prerequisite not satisfied");
2109                                 }
2110                         }
2111                 } else if (update_class == zoneclass) {
2112                         /* "temp<rr.name, rr.type> += rr;" */
2113                         result = temp_append(&temp, name, &rdata);
2114                         if (result != ISC_R_SUCCESS) {
2115                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
2116                                          "temp entry creation failed: %s",
2117                                                  dns_result_totext(result));
2118                                 FAIL(ISC_R_UNEXPECTED);
2119                         }
2120                 } else {
2121                         FAILC(DNS_R_FORMERR, "malformed prerequisite");
2122                 }
2123         }
2124         if (result != ISC_R_NOMORE)
2125                 FAIL(result);
2126
2127         /*
2128          * Perform the final check of the "rrset exists (value dependent)"
2129          * prerequisites.
2130          */
2131         result = temp_check(mctx, &temp, db, ver);
2132         if (result != ISC_R_SUCCESS)
2133                 FAILC(result, "'RRset exists (value dependent)' "
2134                       "prerequisite not satisfied");
2135
2136         update_log(client, zone, LOGLEVEL_DEBUG,
2137                    "prerequisites are OK");
2138
2139         /*
2140          * Check Requestor's Permissions.  It seems a bit silly to do this
2141          * only after prerequisite testing, but that is what RFC2136 says.
2142          */
2143         if (ssutable == NULL) {
2144                 char msg[DNS_RDATACLASS_FORMATSIZE + DNS_NAME_FORMATSIZE
2145                          + sizeof("update '/'")];
2146                 ns_client_aclmsg("update", zonename, client->view->rdclass,
2147                                  msg, sizeof(msg));
2148                 CHECK(ns_client_checkacl(client, msg,
2149                                          dns_zone_getupdateacl(zone),
2150                                          ISC_FALSE, ISC_LOG_ERROR));
2151         } else if (client->signer == NULL) {
2152                 /* This gets us a free log message. */
2153                 char msg[DNS_RDATACLASS_FORMATSIZE + DNS_NAME_FORMATSIZE
2154                          + sizeof("update '/'")];
2155                 ns_client_aclmsg("update", zonename, client->view->rdclass,
2156                                  msg, sizeof(msg));
2157                 CHECK(ns_client_checkacl(client, msg, NULL, ISC_FALSE,
2158                                          ISC_LOG_ERROR));
2159         }
2160
2161         /*
2162          * Perform the Update Section Prescan.
2163          */
2164
2165         for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
2166              result == ISC_R_SUCCESS;
2167              result = dns_message_nextname(request, DNS_SECTION_UPDATE))
2168         {
2169                 dns_name_t *name = NULL;
2170                 dns_rdata_t rdata = DNS_RDATA_INIT;
2171                 dns_ttl_t ttl;
2172                 dns_rdataclass_t update_class;
2173                 get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
2174                                &name, &rdata, &covers, &ttl, &update_class);
2175
2176                 if (! dns_name_issubdomain(name, zonename))
2177                         FAILC(DNS_R_NOTZONE,
2178                               "update RR is outside zone");
2179                 if (update_class == zoneclass) {
2180                         /*
2181                          * Check for meta-RRs.  The RFC2136 pseudocode says
2182                          * check for ANY|AXFR|MAILA|MAILB, but the text adds
2183                          * "or any other QUERY metatype"
2184                          */
2185                         if (dns_rdatatype_ismeta(rdata.type)) {
2186                                 FAILC(DNS_R_FORMERR,
2187                                       "meta-RR in update");
2188                         }
2189                 } else if (update_class == dns_rdataclass_any) {
2190                         if (ttl != 0 || rdata.length != 0 ||
2191                             (dns_rdatatype_ismeta(rdata.type) &&
2192                              rdata.type != dns_rdatatype_any))
2193                                 FAILC(DNS_R_FORMERR,
2194                                       "meta-RR in update");
2195                 } else if (update_class == dns_rdataclass_none) {
2196                         if (ttl != 0 ||
2197                             dns_rdatatype_ismeta(rdata.type))
2198                                 FAILC(DNS_R_FORMERR,
2199                                       "meta-RR in update");
2200                 } else {
2201                         update_log(client, zone, ISC_LOG_WARNING,
2202                                    "update RR has incorrect class %d",
2203                                    update_class);
2204                         FAIL(DNS_R_FORMERR);
2205                 }
2206                 /*
2207                  * draft-ietf-dnsind-simple-secure-update-01 says
2208                  * "Unlike traditional dynamic update, the client
2209                  * is forbidden from updating NXT records."
2210                  */
2211                 if (dns_db_issecure(db)) {
2212                         if (rdata.type == dns_rdatatype_nxt) {
2213                                 FAILC(DNS_R_REFUSED,
2214                                       "explicit NXT updates are not allowed "
2215                                       "in secure zones");
2216                         }
2217                         else if (rdata.type == dns_rdatatype_sig) {
2218                                 FAILC(DNS_R_REFUSED,
2219                                       "explicit SIG updates are currently not "
2220                                       "supported in secure zones");
2221                         }
2222                 }
2223
2224                 if (ssutable != NULL && client->signer != NULL) {
2225                         if (rdata.type != dns_rdatatype_any) {
2226                                 if (!dns_ssutable_checkrules(ssutable,
2227                                                              client->signer,
2228                                                              name, rdata.type))
2229                                         FAILC(DNS_R_REFUSED,
2230                                               "rejected by secure update");
2231                         }
2232                         else {
2233                                 if (!ssu_checkall(db, ver, name, ssutable,
2234                                                   client->signer))
2235                                         FAILC(DNS_R_REFUSED,
2236                                               "rejected by secure update");
2237                         }
2238                 }
2239         }
2240         if (result != ISC_R_NOMORE)
2241                 FAIL(result);
2242
2243         update_log(client, zone, LOGLEVEL_DEBUG,
2244                    "update section prescan OK");
2245
2246         /*
2247          * Process the Update Section.
2248          */
2249
2250         for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
2251              result == ISC_R_SUCCESS;
2252              result = dns_message_nextname(request, DNS_SECTION_UPDATE))
2253         {
2254                 dns_name_t *name = NULL;
2255                 dns_rdata_t rdata = DNS_RDATA_INIT;
2256                 dns_ttl_t ttl;
2257                 dns_rdataclass_t update_class;
2258                 isc_boolean_t flag;
2259
2260                 get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
2261                                &name, &rdata, &covers, &ttl, &update_class);
2262
2263                 if (update_class == zoneclass) {
2264                         if (rdata.type == dns_rdatatype_cname) {
2265                                 CHECK(cname_incompatible_rrset_exists(db, ver,
2266                                                                       name,
2267                                                                       &flag));
2268                                 if (flag) {
2269                                         update_log(client, zone,
2270                                                    LOGLEVEL_PROTOCOL,
2271                                                    "attempt to add CNAME "
2272                                                    "alongside non-CNAME "
2273                                                    "ignored");
2274                                         continue;
2275                                 }
2276                         } else {
2277                                 CHECK(rrset_exists(db, ver, name,
2278                                                    dns_rdatatype_cname, 0,
2279                                                    &flag));
2280                                 if (flag &&
2281                                     ! dns_rdatatype_isdnssec(rdata.type))
2282                                 {
2283                                         update_log(client, zone,
2284                                                    LOGLEVEL_PROTOCOL,
2285                                                    "attempt to add non-CNAME "
2286                                                    "alongside CNAME ignored");
2287                                         continue;
2288                                 }
2289                         }
2290                         if (rdata.type == dns_rdatatype_soa) {
2291                                 isc_boolean_t ok;
2292                                 CHECK(rrset_exists(db, ver, name,
2293                                                    dns_rdatatype_soa, 0,
2294                                                    &flag));
2295                                 if (! flag) {
2296                                         update_log(client, zone,
2297                                                    LOGLEVEL_PROTOCOL,
2298                                                    "attempt to create 2nd "
2299                                                    "SOA ignored");
2300                                         continue;
2301                                 }
2302                                 CHECK(check_soa_increment(db, ver, &rdata,
2303                                                           &ok));
2304                                 if (! ok) {
2305                                         update_log(client, zone,
2306                                                    LOGLEVEL_PROTOCOL,
2307                                                    "SOA update failed to "
2308                                                    "increment serial, "
2309                                                    "ignoring it");
2310                                         continue;
2311                                 }
2312                                 soa_serial_changed = ISC_TRUE;
2313                         }
2314                         
2315                         update_log(client, zone,
2316                                    LOGLEVEL_PROTOCOL, "adding an RR");
2317
2318                         /* Prepare the affected RRset for the addition. */
2319                         {
2320                                 add_rr_prepare_ctx_t ctx;
2321                                 ctx.db = db;
2322                                 ctx.ver = ver;
2323                                 ctx.diff = &diff;
2324                                 ctx.name = name;
2325                                 ctx.update_rr = &rdata;
2326                                 ctx.update_rr_ttl = ttl;
2327                                 ctx.ignore_add = ISC_FALSE;
2328                                 dns_diff_init(mctx, &ctx.del_diff);
2329                                 dns_diff_init(mctx, &ctx.add_diff);
2330                                 CHECK(foreach_rr(db, ver, name, rdata.type, covers,
2331                                                  add_rr_prepare_action, &ctx));
2332
2333                                 if (ctx.ignore_add) {
2334                                         dns_diff_clear(&ctx.del_diff);
2335                                         dns_diff_clear(&ctx.add_diff);
2336                                 } else {
2337                                         CHECK(do_diff(&ctx.del_diff, db, ver, &diff));
2338                                         CHECK(do_diff(&ctx.add_diff, db, ver, &diff));
2339                                         CHECK(update_one_rr(db, ver, &diff,
2340                                                             DNS_DIFFOP_ADD,
2341                                                             name, ttl, &rdata));
2342                                 }
2343                         }
2344                 } else if (update_class == dns_rdataclass_any) {
2345                         if (rdata.type == dns_rdatatype_any) {
2346                                 update_log(client, zone,
2347                                            LOGLEVEL_PROTOCOL,
2348                                            "delete all rrsets from a name");
2349                                 if (dns_name_equal(name, zonename)) {
2350                                         CHECK(delete_if(type_not_soa_nor_ns_p,
2351                                                         db, ver, name,
2352                                                         dns_rdatatype_any, 0,
2353                                                         &rdata, &diff));
2354                                 } else {
2355                                         CHECK(delete_if(true_p, db, ver, name,
2356                                                         dns_rdatatype_any, 0,
2357                                                         &rdata, &diff));
2358                                 }
2359                         } else if (dns_name_equal(name, zonename) &&
2360                                    (rdata.type == dns_rdatatype_soa ||
2361                                     rdata.type == dns_rdatatype_ns)) {
2362                                 update_log(client, zone,
2363                                            LOGLEVEL_PROTOCOL,
2364                                            "attempt to delete all SOA "
2365                                            "or NS records ignored");
2366                                 continue;
2367                         } else {
2368                                 update_log(client, zone,
2369                                            LOGLEVEL_PROTOCOL,
2370                                            "deleting an rrset");
2371                                 CHECK(delete_if(true_p, db, ver, name,
2372                                                 rdata.type, covers, &rdata,
2373                                                 &diff));
2374                         }
2375                 } else if (update_class == dns_rdataclass_none) {
2376                         /*
2377                          * The (name == zonename) condition appears in
2378                          * RFC2136 3.4.2.4 but is missing from the pseudocode.
2379                          */
2380                         if (dns_name_equal(name, zonename)) {
2381                                 if (rdata.type == dns_rdatatype_soa) {
2382                                         update_log(client, zone,
2383                                                    LOGLEVEL_PROTOCOL,
2384                                                    "attempt to delete SOA "
2385                                                    "ignored");
2386                                         continue;
2387                                 }
2388                                 if (rdata.type == dns_rdatatype_ns) {
2389                                         int count;
2390                                         CHECK(rr_count(db, ver, name,
2391                                                        dns_rdatatype_ns,
2392                                                        0, &count));
2393                                         if (count == 1) {
2394                                                 update_log(client, zone,
2395                                                            LOGLEVEL_PROTOCOL,
2396                                                            "attempt to "
2397                                                            "delete last "
2398                                                            "NS ignored");
2399                                                 continue;
2400                                         }
2401                                 }
2402                         }
2403                         update_log(client, zone,
2404                                    LOGLEVEL_PROTOCOL,
2405                                    "deleting an RR");
2406                         CHECK(delete_if(rr_equal_p, db, ver, name,
2407                                         rdata.type, covers, &rdata, &diff));
2408                 }
2409         }
2410         if (result != ISC_R_NOMORE)
2411                 FAIL(result);
2412
2413         /*
2414          * If any changes were made, increment the SOA serial number,
2415          * update SIGs and NXTs (if zone is secure), and write the update
2416          * to the journal.
2417          */
2418         if (! ISC_LIST_EMPTY(diff.tuples)) {
2419                 char *journalfile;
2420                 dns_journal_t *journal;
2421
2422                 /*
2423                  * Increment the SOA serial, but only if it was not
2424                  * changed as a result of an update operation.
2425                  */
2426                 if (! soa_serial_changed) {
2427                         CHECK(increment_soa_serial(db, ver, &diff, mctx));
2428                 }
2429
2430                 if (dns_db_issecure(db)) {
2431                         result = update_signatures(mctx, db, oldver, ver,
2432                            &diff, dns_zone_getsigvalidityinterval(zone));
2433                         if (result != ISC_R_SUCCESS) {
2434                                 update_log(client, zone,
2435                                            ISC_LOG_ERROR,
2436                                            "SIG/NXT update failed: %s",
2437                                            isc_result_totext(result));
2438                                 goto failure;
2439                         }
2440                 }
2441
2442                 journalfile = dns_zone_getjournal(zone);
2443                 if (journalfile != NULL) {
2444                         update_log(client, zone, LOGLEVEL_DEBUG,
2445                                    "writing journal %s", journalfile);
2446
2447                         journal = NULL;
2448                         result = dns_journal_open(mctx, journalfile,
2449                                                   ISC_TRUE, &journal);
2450                         if (result != ISC_R_SUCCESS)
2451                                 FAILS(result, "journal open failed");
2452
2453                         result = dns_journal_write_transaction(journal, &diff);
2454                         if (result != ISC_R_SUCCESS) {
2455                                 dns_journal_destroy(&journal);
2456                                 FAILS(result, "journal write failed");
2457                         }
2458
2459                         dns_journal_destroy(&journal);
2460                 }
2461
2462                 /*
2463                  * XXXRTH  Just a note that this committing code will have
2464                  *         to change to handle databases that need two-phase
2465                  *         commit, but this isn't a priority.
2466                  */
2467                 update_log(client, zone, LOGLEVEL_DEBUG,
2468                            "committing update transaction");
2469                 dns_db_closeversion(db, &ver, ISC_TRUE);
2470
2471                 /*
2472                  * Mark the zone as dirty so that it will be written to disk.
2473                  */
2474                 dns_zone_markdirty(zone);
2475
2476                 /*
2477                  * Notify slaves of the change we just made.
2478                  */
2479                 dns_zone_notify(zone);
2480         } else {
2481                 update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
2482                 dns_db_closeversion(db, &ver, ISC_TRUE);
2483         }
2484         result = ISC_R_SUCCESS;
2485         goto common;
2486
2487  failure:
2488         /*
2489          * The reason for failure should have been logged at this point.
2490          */
2491         if (ver != NULL) {
2492                 update_log(client, zone, LOGLEVEL_DEBUG, 
2493                            "rolling back");
2494                 dns_db_closeversion(db, &ver, ISC_FALSE);
2495         }
2496
2497  common:
2498         dns_diff_clear(&temp);
2499         dns_diff_clear(&diff);
2500
2501         if (oldver != NULL)
2502                 dns_db_closeversion(db, &oldver, ISC_FALSE);
2503
2504         if (db != NULL)
2505                 dns_db_detach(&db);
2506
2507         if (ssutable != NULL)
2508                 dns_ssutable_detach(&ssutable);
2509
2510         if (zone != NULL)
2511                 dns_zone_detach(&zone);
2512
2513         isc_task_detach(&task);
2514         uev->result = result;
2515         uev->ev_type = DNS_EVENT_UPDATEDONE;
2516         uev->ev_action = updatedone_action;
2517         isc_task_send(client->task, &event);
2518         INSIST(event == NULL);
2519 }
2520
2521 static void
2522 updatedone_action(isc_task_t *task, isc_event_t *event) {
2523         update_event_t *uev = (update_event_t *) event;
2524         ns_client_t *client = (ns_client_t *) event->ev_arg;
2525
2526         UNUSED(task);
2527
2528         INSIST(event->ev_type == DNS_EVENT_UPDATEDONE);
2529         INSIST(task == client->task);
2530
2531         INSIST(client->nupdates > 0);
2532         client->nupdates--;
2533         respond(client, uev->result);
2534         ns_client_detach(&client);
2535         isc_event_free(&event);
2536 }
2537
2538 /*
2539  * Update forwarding support.
2540  */
2541
2542 static void
2543 forward_fail(isc_task_t *task, isc_event_t *event) {
2544         ns_client_t *client = (ns_client_t *)event->ev_arg;
2545
2546         UNUSED(task);
2547
2548         INSIST(client->nupdates > 0);
2549         client->nupdates--;
2550         respond(client, DNS_R_SERVFAIL);
2551         ns_client_detach(&client);
2552         isc_event_free(&event);
2553 }
2554
2555
2556 static void
2557 forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
2558         update_event_t *uev = arg;
2559         ns_client_t *client = uev->ev_arg;
2560
2561         if (result != ISC_R_SUCCESS) {
2562                 INSIST(answer == NULL);
2563                 uev->ev_type = DNS_EVENT_UPDATEDONE;
2564                 uev->ev_action = forward_fail;
2565         } else {
2566                 uev->ev_type = DNS_EVENT_UPDATEDONE;
2567                 uev->ev_action = forward_done;
2568                 uev->answer = answer;
2569         }
2570         isc_task_send(client->task, ISC_EVENT_PTR(&uev));
2571 }
2572
2573 static void
2574 forward_done(isc_task_t *task, isc_event_t *event) {
2575         update_event_t *uev = (update_event_t *) event;
2576         ns_client_t *client = (ns_client_t *)event->ev_arg;
2577
2578         UNUSED(task);
2579
2580         INSIST(client->nupdates > 0);
2581         client->nupdates--;
2582         ns_client_sendraw(client, uev->answer);
2583         dns_message_destroy(&uev->answer);
2584         isc_event_free(&event);
2585         ns_client_detach(&client);
2586 }
2587
2588 static void
2589 forward_action(isc_task_t *task, isc_event_t *event) {
2590         update_event_t *uev = (update_event_t *) event;
2591         dns_zone_t *zone = uev->zone;
2592         ns_client_t *client = (ns_client_t *)event->ev_arg;
2593         isc_result_t result;
2594
2595         result = dns_zone_forwardupdate(zone, client->message,
2596                                         forward_callback, event);
2597         if (result != ISC_R_SUCCESS) {
2598                 uev->ev_type = DNS_EVENT_UPDATEDONE;
2599                 uev->ev_action = forward_fail;
2600                 isc_task_send(client->task, &event);
2601         }
2602         dns_zone_detach(&zone);
2603         isc_task_detach(&task);
2604 }
2605
2606 static isc_result_t
2607 send_forward_event(ns_client_t *client, dns_zone_t *zone) {
2608         isc_result_t result = ISC_R_SUCCESS;
2609         update_event_t *event = NULL;
2610         isc_task_t *zonetask = NULL;
2611         ns_client_t *evclient;
2612
2613         event = (update_event_t *)
2614                 isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
2615                                    forward_action, NULL, sizeof(*event));
2616         if (event == NULL)
2617                 FAIL(ISC_R_NOMEMORY);
2618         event->zone = zone;
2619         event->result = ISC_R_SUCCESS;
2620
2621         evclient = NULL;
2622         ns_client_attach(client, &evclient);
2623         INSIST(client->nupdates == 0);
2624         client->nupdates++;
2625         event->ev_arg = evclient;
2626
2627         dns_zone_gettask(zone, &zonetask);
2628         isc_task_send(zonetask, ISC_EVENT_PTR(&event));
2629
2630  failure:
2631         if (event != NULL)
2632                 isc_event_free(ISC_EVENT_PTR(&event));
2633         return (result);
2634 }