Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / bin / named / update.c
1 /*
2  * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: update.c,v 1.138.2.13 2009/07/28 15:54:31 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/print.h>
23 #include <isc/stats.h>
24 #include <isc/string.h>
25 #include <isc/taskpool.h>
26 #include <isc/util.h>
27
28 #include <dns/db.h>
29 #include <dns/dbiterator.h>
30 #include <dns/diff.h>
31 #include <dns/dnssec.h>
32 #include <dns/events.h>
33 #include <dns/fixedname.h>
34 #include <dns/journal.h>
35 #include <dns/keyvalues.h>
36 #include <dns/message.h>
37 #include <dns/nsec.h>
38 #include <dns/rdataclass.h>
39 #include <dns/rdataset.h>
40 #include <dns/rdatasetiter.h>
41 #include <dns/rdatastruct.h>
42 #include <dns/rdatatype.h>
43 #include <dns/soa.h>
44 #include <dns/ssu.h>
45 #include <dns/view.h>
46 #include <dns/zone.h>
47 #include <dns/zt.h>
48
49 #include <named/client.h>
50 #include <named/log.h>
51 #include <named/server.h>
52 #include <named/update.h>
53
54 /*! \file
55  * \brief
56  * This module implements dynamic update as in RFC2136.
57  */
58
59 /*
60  *  XXX TODO:
61  * - document strict minimality
62  */
63
64 /**************************************************************************/
65
66 /*%
67  * Log level for tracing dynamic update protocol requests.
68  */
69 #define LOGLEVEL_PROTOCOL       ISC_LOG_INFO
70
71 /*%
72  * Log level for low-level debug tracing.
73  */
74 #define LOGLEVEL_DEBUG          ISC_LOG_DEBUG(8)
75
76 /*%
77  * Check an operation for failure.  These macros all assume that
78  * the function using them has a 'result' variable and a 'failure'
79  * label.
80  */
81 #define CHECK(op) \
82         do { result = (op); \
83                 if (result != ISC_R_SUCCESS) goto failure; \
84         } while (0)
85
86 /*%
87  * Fail unconditionally with result 'code', which must not
88  * be ISC_R_SUCCESS.  The reason for failure presumably has
89  * been logged already.
90  *
91  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
92  * from complaining about "end-of-loop code not reached".
93  */
94
95 #define FAIL(code) \
96         do {                                                    \
97                 result = (code);                                \
98                 if (result != ISC_R_SUCCESS) goto failure;      \
99         } while (0)
100
101 /*%
102  * Fail unconditionally and log as a client error.
103  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
104  * from complaining about "end-of-loop code not reached".
105  */
106 #define FAILC(code, msg) \
107         do {                                                    \
108                 const char *_what = "failed";                   \
109                 result = (code);                                \
110                 switch (result) {                               \
111                 case DNS_R_NXDOMAIN:                            \
112                 case DNS_R_YXDOMAIN:                            \
113                 case DNS_R_YXRRSET:                             \
114                 case DNS_R_NXRRSET:                             \
115                         _what = "unsuccessful";                 \
116                 }                                               \
117                 update_log(client, zone, LOGLEVEL_PROTOCOL,     \
118                            "update %s: %s (%s)", _what,         \
119                            msg, isc_result_totext(result));     \
120                 if (result != ISC_R_SUCCESS) goto failure;      \
121         } while (0)
122 #define PREREQFAILC(code, msg) \
123         do {                                                    \
124                 inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
125                 FAILC(code, msg);                               \
126         } while (0)
127
128 #define FAILN(code, name, msg) \
129         do {                                                            \
130                 const char *_what = "failed";                           \
131                 result = (code);                                        \
132                 switch (result) {                                       \
133                 case DNS_R_NXDOMAIN:                                    \
134                 case DNS_R_YXDOMAIN:                                    \
135                 case DNS_R_YXRRSET:                                     \
136                 case DNS_R_NXRRSET:                                     \
137                         _what = "unsuccessful";                         \
138                 }                                                       \
139                 if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {   \
140                         char _nbuf[DNS_NAME_FORMATSIZE];                \
141                         dns_name_format(name, _nbuf, sizeof(_nbuf));    \
142                         update_log(client, zone, LOGLEVEL_PROTOCOL,     \
143                                    "update %s: %s: %s (%s)", _what, _nbuf, \
144                                    msg, isc_result_totext(result));     \
145                 }                                                       \
146                 if (result != ISC_R_SUCCESS) goto failure;              \
147         } while (0)
148 #define PREREQFAILN(code, name, msg) \
149         do {                                                            \
150                 inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
151                 FAILN(code, name, msg);                                 \
152         } while (0)
153
154 #define FAILNT(code, name, type, msg) \
155         do {                                                            \
156                 const char *_what = "failed";                           \
157                 result = (code);                                        \
158                 switch (result) {                                       \
159                 case DNS_R_NXDOMAIN:                                    \
160                 case DNS_R_YXDOMAIN:                                    \
161                 case DNS_R_YXRRSET:                                     \
162                 case DNS_R_NXRRSET:                                     \
163                         _what = "unsuccessful";                         \
164                 }                                                       \
165                 if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {   \
166                         char _nbuf[DNS_NAME_FORMATSIZE];                \
167                         char _tbuf[DNS_RDATATYPE_FORMATSIZE];           \
168                         dns_name_format(name, _nbuf, sizeof(_nbuf));    \
169                         dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
170                         update_log(client, zone, LOGLEVEL_PROTOCOL,     \
171                                    "update %s: %s/%s: %s (%s)",         \
172                                    _what, _nbuf, _tbuf, msg,            \
173                                    isc_result_totext(result));          \
174                 }                                                       \
175                 if (result != ISC_R_SUCCESS) goto failure;              \
176         } while (0)
177 #define PREREQFAILNT(code, name, type, msg)                             \
178         do {                                                            \
179                 inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
180                 FAILNT(code, name, type, msg);                          \
181         } while (0)
182
183 /*%
184  * Fail unconditionally and log as a server error.
185  * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
186  * from complaining about "end-of-loop code not reached".
187  */
188 #define FAILS(code, msg) \
189         do {                                                    \
190                 result = (code);                                \
191                 update_log(client, zone, LOGLEVEL_PROTOCOL,     \
192                            "error: %s: %s",                     \
193                            msg, isc_result_totext(result));     \
194                 if (result != ISC_R_SUCCESS) goto failure;      \
195         } while (0)
196
197 /**************************************************************************/
198
199 typedef struct rr rr_t;
200
201 struct rr {
202         /* dns_name_t name; */
203         isc_uint32_t            ttl;
204         dns_rdata_t             rdata;
205 };
206
207 typedef struct update_event update_event_t;
208
209 struct update_event {
210         ISC_EVENT_COMMON(update_event_t);
211         dns_zone_t              *zone;
212         isc_result_t            result;
213         dns_message_t           *answer;
214 };
215
216 /**************************************************************************/
217 /*
218  * Forward declarations.
219  */
220
221 static void update_action(isc_task_t *task, isc_event_t *event);
222 static void updatedone_action(isc_task_t *task, isc_event_t *event);
223 static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
224 static void forward_done(isc_task_t *task, isc_event_t *event);
225
226 /**************************************************************************/
227
228 static void
229 update_log(ns_client_t *client, dns_zone_t *zone,
230            int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);
231
232 static void
233 update_log(ns_client_t *client, dns_zone_t *zone,
234            int level, const char *fmt, ...)
235 {
236         va_list ap;
237         char message[4096];
238         char namebuf[DNS_NAME_FORMATSIZE];
239         char classbuf[DNS_RDATACLASS_FORMATSIZE];
240
241         if (client == NULL || zone == NULL)
242                 return;
243
244         if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE)
245                 return;
246
247         dns_name_format(dns_zone_getorigin(zone), namebuf,
248                         sizeof(namebuf));
249         dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
250                               sizeof(classbuf));
251
252         va_start(ap, fmt);
253         vsnprintf(message, sizeof(message), fmt, ap);
254         va_end(ap);
255
256         ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
257                       level, "updating zone '%s/%s': %s",
258                       namebuf, classbuf, message);
259 }
260
261 /*%
262  * Increment updated-related statistics counters.
263  */
264 static inline void
265 inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
266         isc_stats_increment(ns_g_server->nsstats, counter);
267
268         if (zone != NULL) {
269                 isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
270                 if (zonestats != NULL)
271                         isc_stats_increment(zonestats, counter);
272         }
273 }
274
275 /*%
276  * Override the default acl logging when checking whether a client
277  * can update the zone or whether we can forward the request to the
278  * master based on IP address.
279  *
280  * 'message' contains the type of operation that is being attempted.
281  * 'slave' indicates if this is a slave zone.  If 'acl' is NULL then
282  * log at debug=3.
283  * If the zone has no access controls configured ('acl' == NULL &&
284  * 'has_ssutable == ISC_FALS) log the attempt at info, otherwise
285  * at error.
286  *
287  * If the request was signed log that we received it.
288  */
289 static isc_result_t
290 checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
291                dns_name_t *zonename, isc_boolean_t slave,
292                isc_boolean_t has_ssutable)
293 {
294         char namebuf[DNS_NAME_FORMATSIZE];
295         char classbuf[DNS_RDATACLASS_FORMATSIZE];
296         int level = ISC_LOG_ERROR;
297         const char *msg = "denied";
298         isc_result_t result;
299
300         if (slave && acl == NULL) {
301                 result = DNS_R_NOTIMP;
302                 level = ISC_LOG_DEBUG(3);
303                 msg = "disabled";
304         } else {
305                 result = ns_client_checkaclsilent(client, NULL, acl, ISC_FALSE);
306                 if (result == ISC_R_SUCCESS) {
307                         level = ISC_LOG_DEBUG(3);
308                         msg = "approved";
309                 } else if (acl == NULL && !has_ssutable) {
310                         level = ISC_LOG_INFO;
311                 }
312         }
313
314         if (client->signer != NULL) {
315                 dns_name_format(client->signer, namebuf, sizeof(namebuf));
316                 ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
317                               NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
318                               "signer \"%s\" %s", namebuf, msg);
319         }
320
321         dns_name_format(zonename, namebuf, sizeof(namebuf));
322         dns_rdataclass_format(client->view->rdclass, classbuf,
323                               sizeof(classbuf));
324
325         ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
326                       NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s",
327                       message, namebuf, classbuf, msg);
328         return (result);
329 }
330
331 /*%
332  * Update a single RR in version 'ver' of 'db' and log the
333  * update in 'diff'.
334  *
335  * Ensures:
336  * \li  '*tuple' == NULL.  Either the tuple is freed, or its
337  *      ownership has been transferred to the diff.
338  */
339 static isc_result_t
340 do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
341              dns_diff_t *diff)
342 {
343         dns_diff_t temp_diff;
344         isc_result_t result;
345
346         /*
347          * Create a singleton diff.
348          */
349         dns_diff_init(diff->mctx, &temp_diff);
350         ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
351
352         /*
353          * Apply it to the database.
354          */
355         result = dns_diff_apply(&temp_diff, db, ver);
356         ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
357         if (result != ISC_R_SUCCESS) {
358                 dns_difftuple_free(tuple);
359                 return (result);
360         }
361
362         /*
363          * Merge it into the current pending journal entry.
364          */
365         dns_diff_appendminimal(diff, tuple);
366
367         /*
368          * Do not clear temp_diff.
369          */
370         return (ISC_R_SUCCESS);
371 }
372
373 /*%
374  * Perform the updates in 'updates' in version 'ver' of 'db' and log the
375  * update in 'diff'.
376  *
377  * Ensures:
378  * \li  'updates' is empty.
379  */
380 static isc_result_t
381 do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
382         dns_diff_t *diff)
383 {
384         isc_result_t result;
385         while (! ISC_LIST_EMPTY(updates->tuples)) {
386                 dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
387                 ISC_LIST_UNLINK(updates->tuples, t, link);
388                 CHECK(do_one_tuple(&t, db, ver, diff));
389         }
390         return (ISC_R_SUCCESS);
391
392  failure:
393         dns_diff_clear(diff);
394         return (result);
395 }
396
397 static isc_result_t
398 update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
399               dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
400               dns_rdata_t *rdata)
401 {
402         dns_difftuple_t *tuple = NULL;
403         isc_result_t result;
404         result = dns_difftuple_create(diff->mctx, op,
405                                       name, ttl, rdata, &tuple);
406         if (result != ISC_R_SUCCESS)
407                 return (result);
408         return (do_one_tuple(&tuple, db, ver, diff));
409 }
410
411 /**************************************************************************/
412 /*
413  * Callback-style iteration over rdatasets and rdatas.
414  *
415  * foreach_rrset() can be used to iterate over the RRsets
416  * of a name and call a callback function with each
417  * one.  Similarly, foreach_rr() can be used to iterate
418  * over the individual RRs at name, optionally restricted
419  * to RRs of a given type.
420  *
421  * The callback functions are called "actions" and take
422  * two arguments: a void pointer for passing arbitrary
423  * context information, and a pointer to the current RRset
424  * or RR.  By convention, their names end in "_action".
425  */
426
427 /*
428  * XXXRTH  We might want to make this public somewhere in libdns.
429  */
430
431 /*%
432  * Function type for foreach_rrset() iterator actions.
433  */
434 typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
435
436 /*%
437  * Function type for foreach_rr() iterator actions.
438  */
439 typedef isc_result_t rr_func(void *data, rr_t *rr);
440
441 /*%
442  * Internal context struct for foreach_node_rr().
443  */
444 typedef struct {
445         rr_func *       rr_action;
446         void *          rr_action_data;
447 } foreach_node_rr_ctx_t;
448
449 /*%
450  * Internal helper function for foreach_node_rr().
451  */
452 static isc_result_t
453 foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
454         isc_result_t result;
455         foreach_node_rr_ctx_t *ctx = data;
456         for (result = dns_rdataset_first(rdataset);
457              result == ISC_R_SUCCESS;
458              result = dns_rdataset_next(rdataset))
459         {
460                 rr_t rr = { 0, DNS_RDATA_INIT };
461
462                 dns_rdataset_current(rdataset, &rr.rdata);
463                 rr.ttl = rdataset->ttl;
464                 result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
465                 if (result != ISC_R_SUCCESS)
466                         return (result);
467         }
468         if (result != ISC_R_NOMORE)
469                 return (result);
470         return (ISC_R_SUCCESS);
471 }
472
473 /*%
474  * For each rdataset of 'name' in 'ver' of 'db', call 'action'
475  * with the rdataset and 'action_data' as arguments.  If the name
476  * does not exist, do nothing.
477  *
478  * If 'action' returns an error, abort iteration and return the error.
479  */
480 static isc_result_t
481 foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
482               rrset_func *action, void *action_data)
483 {
484         isc_result_t result;
485         dns_dbnode_t *node;
486         dns_rdatasetiter_t *iter;
487
488         node = NULL;
489         result = dns_db_findnode(db, name, ISC_FALSE, &node);
490         if (result == ISC_R_NOTFOUND)
491                 return (ISC_R_SUCCESS);
492         if (result != ISC_R_SUCCESS)
493                 return (result);
494
495         iter = NULL;
496         result = dns_db_allrdatasets(db, node, ver,
497                                      (isc_stdtime_t) 0, &iter);
498         if (result != ISC_R_SUCCESS)
499                 goto cleanup_node;
500
501         for (result = dns_rdatasetiter_first(iter);
502              result == ISC_R_SUCCESS;
503              result = dns_rdatasetiter_next(iter))
504         {
505                 dns_rdataset_t rdataset;
506
507                 dns_rdataset_init(&rdataset);
508                 dns_rdatasetiter_current(iter, &rdataset);
509
510                 result = (*action)(action_data, &rdataset);
511
512                 dns_rdataset_disassociate(&rdataset);
513                 if (result != ISC_R_SUCCESS)
514                         goto cleanup_iterator;
515         }
516         if (result == ISC_R_NOMORE)
517                 result = ISC_R_SUCCESS;
518
519  cleanup_iterator:
520         dns_rdatasetiter_destroy(&iter);
521
522  cleanup_node:
523         dns_db_detachnode(db, &node);
524
525         return (result);
526 }
527
528 /*%
529  * For each RR of 'name' in 'ver' of 'db', call 'action'
530  * with the RR and 'action_data' as arguments.  If the name
531  * does not exist, do nothing.
532  *
533  * If 'action' returns an error, abort iteration
534  * and return the error.
535  */
536 static isc_result_t
537 foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
538                 rr_func *rr_action, void *rr_action_data)
539 {
540         foreach_node_rr_ctx_t ctx;
541         ctx.rr_action = rr_action;
542         ctx.rr_action_data = rr_action_data;
543         return (foreach_rrset(db, ver, name,
544                               foreach_node_rr_action, &ctx));
545 }
546
547
548 /*%
549  * For each of the RRs specified by 'db', 'ver', 'name', 'type',
550  * (which can be dns_rdatatype_any to match any type), and 'covers', call
551  * 'action' with the RR and 'action_data' as arguments. If the name
552  * does not exist, or if no RRset of the given type exists at the name,
553  * do nothing.
554  *
555  * If 'action' returns an error, abort iteration and return the error.
556  */
557 static isc_result_t
558 foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
559            dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action,
560            void *rr_action_data)
561 {
562
563         isc_result_t result;
564         dns_dbnode_t *node;
565         dns_rdataset_t rdataset;
566
567         if (type == dns_rdatatype_any)
568                 return (foreach_node_rr(db, ver, name,
569                                         rr_action, rr_action_data));
570
571         node = NULL;
572         result = dns_db_findnode(db, name, ISC_FALSE, &node);
573         if (result == ISC_R_NOTFOUND)
574                 return (ISC_R_SUCCESS);
575         if (result != ISC_R_SUCCESS)
576                 return (result);
577
578         dns_rdataset_init(&rdataset);
579         result = dns_db_findrdataset(db, node, ver, type, covers,
580                                      (isc_stdtime_t) 0, &rdataset, NULL);
581         if (result == ISC_R_NOTFOUND) {
582                 result = ISC_R_SUCCESS;
583                 goto cleanup_node;
584         }
585         if (result != ISC_R_SUCCESS)
586                 goto cleanup_node;
587
588         for (result = dns_rdataset_first(&rdataset);
589              result == ISC_R_SUCCESS;
590              result = dns_rdataset_next(&rdataset))
591         {
592                 rr_t rr = { 0, DNS_RDATA_INIT };
593                 dns_rdataset_current(&rdataset, &rr.rdata);
594                 rr.ttl = rdataset.ttl;
595                 result = (*rr_action)(rr_action_data, &rr);
596                 if (result != ISC_R_SUCCESS)
597                         goto cleanup_rdataset;
598         }
599         if (result != ISC_R_NOMORE)
600                 goto cleanup_rdataset;
601         result = ISC_R_SUCCESS;
602
603  cleanup_rdataset:
604         dns_rdataset_disassociate(&rdataset);
605  cleanup_node:
606         dns_db_detachnode(db, &node);
607
608         return (result);
609 }
610
611 /**************************************************************************/
612 /*
613  * Various tests on the database contents (for prerequisites, etc).
614  */
615
616 /*%
617  * Function type for predicate functions that compare a database RR 'db_rr'
618  * against an update RR 'update_rr'.
619  */
620 typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
621
622 /*%
623  * Helper function for rrset_exists().
624  */
625 static isc_result_t
626 rrset_exists_action(void *data, rr_t *rr) {
627         UNUSED(data);
628         UNUSED(rr);
629         return (ISC_R_EXISTS);
630 }
631
632 /*%
633  * Utility macro for RR existence checking functions.
634  *
635  * If the variable 'result' has the value ISC_R_EXISTS or
636  * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
637  * respectively, and return success.
638  *
639  * If 'result' has any other value, there was a failure.
640  * Return the failure result code and do not set *exists.
641  *
642  * This would be more readable as "do { if ... } while(0)",
643  * but that form generates tons of warnings on Solaris 2.6.
644  */
645 #define RETURN_EXISTENCE_FLAG                           \
646         return ((result == ISC_R_EXISTS) ?              \
647                 (*exists = ISC_TRUE, ISC_R_SUCCESS) :   \
648                 ((result == ISC_R_SUCCESS) ?            \
649                  (*exists = ISC_FALSE, ISC_R_SUCCESS) : \
650                  result))
651
652 /*%
653  * Set '*exists' to true iff an rrset of the given type exists,
654  * to false otherwise.
655  */
656 static isc_result_t
657 rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
658              dns_rdatatype_t type, dns_rdatatype_t covers,
659              isc_boolean_t *exists)
660 {
661         isc_result_t result;
662         result = foreach_rr(db, ver, name, type, covers,
663                             rrset_exists_action, NULL);
664         RETURN_EXISTENCE_FLAG;
665 }
666
667 /*%
668  * Set '*visible' to true if the RRset exists and is part of the
669  * visible zone.  Otherwise '*visible' is set to false unless a
670  * error occurs.
671  */
672 static isc_result_t
673 rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
674               dns_rdatatype_t type, isc_boolean_t *visible)
675 {
676         isc_result_t result;
677         dns_fixedname_t fixed;
678
679         dns_fixedname_init(&fixed);
680         result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD,
681                              (isc_stdtime_t) 0, NULL,
682                              dns_fixedname_name(&fixed), NULL, NULL);
683         switch (result) {
684         case ISC_R_SUCCESS:
685                 *visible = ISC_TRUE;
686                 break;
687         /*
688          * Glue, obscured, deleted or replaced records.
689          */
690         case DNS_R_DELEGATION:
691         case DNS_R_DNAME:
692         case DNS_R_CNAME:
693         case DNS_R_NXDOMAIN:
694         case DNS_R_NXRRSET:
695         case DNS_R_EMPTYNAME:
696         case DNS_R_COVERINGNSEC:
697                 *visible = ISC_FALSE;
698                 result = ISC_R_SUCCESS;
699                 break;
700         default:
701                 break;
702         }
703         return (result);
704 }
705
706 /*%
707  * Helper function for cname_incompatible_rrset_exists.
708  */
709 static isc_result_t
710 cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
711         UNUSED(data);
712         if (rrset->type != dns_rdatatype_cname &&
713             ! dns_rdatatype_isdnssec(rrset->type))
714                 return (ISC_R_EXISTS);
715         return (ISC_R_SUCCESS);
716 }
717
718 /*%
719  * Check whether there is an rrset incompatible with adding a CNAME RR,
720  * i.e., anything but another CNAME (which can be replaced) or a
721  * DNSSEC RR (which can coexist).
722  *
723  * If such an incompatible rrset exists, set '*exists' to ISC_TRUE.
724  * Otherwise, set it to ISC_FALSE.
725  */
726 static isc_result_t
727 cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
728                                 dns_name_t *name, isc_boolean_t *exists) {
729         isc_result_t result;
730         result = foreach_rrset(db, ver, name,
731                                cname_compatibility_action, NULL);
732         RETURN_EXISTENCE_FLAG;
733 }
734
735 /*%
736  * Helper function for rr_count().
737  */
738 static isc_result_t
739 count_rr_action(void *data, rr_t *rr) {
740         int *countp = data;
741         UNUSED(rr);
742         (*countp)++;
743         return (ISC_R_SUCCESS);
744 }
745
746 /*%
747  * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
748  */
749 static isc_result_t
750 rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
751          dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
752 {
753         *countp = 0;
754         return (foreach_rr(db, ver, name, type, covers,
755                            count_rr_action, countp));
756 }
757
758 /*%
759  * Context struct and helper function for name_exists().
760  */
761
762 static isc_result_t
763 name_exists_action(void *data, dns_rdataset_t *rrset) {
764         UNUSED(data);
765         UNUSED(rrset);
766         return (ISC_R_EXISTS);
767 }
768
769 /*%
770  * Set '*exists' to true iff the given name exists, to false otherwise.
771  */
772 static isc_result_t
773 name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
774             isc_boolean_t *exists)
775 {
776         isc_result_t result;
777         result = foreach_rrset(db, ver, name,
778                                name_exists_action, NULL);
779         RETURN_EXISTENCE_FLAG;
780 }
781
782 typedef struct {
783         dns_name_t *name;
784         dns_name_t *signer;
785         dns_ssutable_t *table;
786 } ssu_check_t;
787
788 static isc_result_t
789 ssu_checkrule(void *data, dns_rdataset_t *rrset) {
790         ssu_check_t *ssuinfo = data;
791         isc_boolean_t result;
792
793         /*
794          * If we're deleting all records, it's ok to delete RRSIG and NSEC even
795          * if we're normally not allowed to.
796          */
797         if (rrset->type == dns_rdatatype_rrsig ||
798             rrset->type == dns_rdatatype_nsec)
799                 return (ISC_R_SUCCESS);
800         result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
801                                          ssuinfo->name, rrset->type);
802         return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
803 }
804
805 static isc_boolean_t
806 ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
807              dns_ssutable_t *ssutable, dns_name_t *signer)
808 {
809         isc_result_t result;
810         ssu_check_t ssuinfo;
811
812         ssuinfo.name = name;
813         ssuinfo.table = ssutable;
814         ssuinfo.signer = signer;
815         result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
816         return (ISC_TF(result == ISC_R_SUCCESS));
817 }
818
819 /**************************************************************************/
820 /*
821  * Checking of "RRset exists (value dependent)" prerequisites.
822  *
823  * In the RFC2136 section 3.2.5, this is the pseudocode involving
824  * a variable called "temp", a mapping of <name, type> tuples to rrsets.
825  *
826  * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t"
827  * where each tuple has op==DNS_DIFFOP_EXISTS.
828  */
829
830
831 /*%
832  * Append a tuple asserting the existence of the RR with
833  * 'name' and 'rdata' to 'diff'.
834  */
835 static isc_result_t
836 temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
837         isc_result_t result;
838         dns_difftuple_t *tuple = NULL;
839
840         REQUIRE(DNS_DIFF_VALID(diff));
841         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
842                                    name, 0, rdata, &tuple));
843         ISC_LIST_APPEND(diff->tuples, tuple, link);
844  failure:
845         return (result);
846 }
847
848 /*%
849  * Compare two rdatasets represented as sorted lists of tuples.
850  * All list elements must have the same owner name and type.
851  * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
852  * if not.
853  */
854 static isc_result_t
855 temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
856         for (;;) {
857                 if (a == NULL || b == NULL)
858                         break;
859                 INSIST(a->op == DNS_DIFFOP_EXISTS &&
860                        b->op == DNS_DIFFOP_EXISTS);
861                 INSIST(a->rdata.type == b->rdata.type);
862                 INSIST(dns_name_equal(&a->name, &b->name));
863                 if (dns_rdata_compare(&a->rdata, &b->rdata) != 0)
864                         return (DNS_R_NXRRSET);
865                 a = ISC_LIST_NEXT(a, link);
866                 b = ISC_LIST_NEXT(b, link);
867         }
868         if (a != NULL || b != NULL)
869                 return (DNS_R_NXRRSET);
870         return (ISC_R_SUCCESS);
871 }
872
873 /*%
874  * A comparison function defining the sorting order for the entries
875  * in the "temp" data structure.  The major sort key is the owner name,
876  * followed by the type and rdata.
877  */
878 static int
879 temp_order(const void *av, const void *bv) {
880         dns_difftuple_t const * const *ap = av;
881         dns_difftuple_t const * const *bp = bv;
882         dns_difftuple_t const *a = *ap;
883         dns_difftuple_t const *b = *bp;
884         int r;
885         r = dns_name_compare(&a->name, &b->name);
886         if (r != 0)
887                 return (r);
888         r = (b->rdata.type - a->rdata.type);
889         if (r != 0)
890                 return (r);
891         r = dns_rdata_compare(&a->rdata, &b->rdata);
892         return (r);
893 }
894
895 /*%
896  * Check the "RRset exists (value dependent)" prerequisite information
897  * in 'temp' against the contents of the database 'db'.
898  *
899  * Return ISC_R_SUCCESS if the prerequisites are satisfied,
900  * rcode(dns_rcode_nxrrset) if not.
901  *
902  * 'temp' must be pre-sorted.
903  */
904
905 static isc_result_t
906 temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
907            dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep)
908 {
909         isc_result_t result;
910         dns_name_t *name;
911         dns_dbnode_t *node;
912         dns_difftuple_t *t;
913         dns_diff_t trash;
914
915         dns_diff_init(mctx, &trash);
916
917         /*
918          * For each name and type in the prerequisites,
919          * construct a sorted rdata list of the corresponding
920          * database contents, and compare the lists.
921          */
922         t = ISC_LIST_HEAD(temp->tuples);
923         while (t != NULL) {
924                 name = &t->name;
925                 (void)dns_name_copy(name, tmpname, NULL);
926                 *typep = t->rdata.type;
927
928                 /* A new unique name begins here. */
929                 node = NULL;
930                 result = dns_db_findnode(db, name, ISC_FALSE, &node);
931                 if (result == ISC_R_NOTFOUND) {
932                         dns_diff_clear(&trash);
933                         return (DNS_R_NXRRSET);
934                 }
935                 if (result != ISC_R_SUCCESS) {
936                         dns_diff_clear(&trash);
937                         return (result);
938                 }
939
940                 /* A new unique type begins here. */
941                 while (t != NULL && dns_name_equal(&t->name, name)) {
942                         dns_rdatatype_t type, covers;
943                         dns_rdataset_t rdataset;
944                         dns_diff_t d_rrs; /* Database RRs with
945                                                 this name and type */
946                         dns_diff_t u_rrs; /* Update RRs with
947                                                 this name and type */
948
949                         *typep = type = t->rdata.type;
950                         if (type == dns_rdatatype_rrsig ||
951                             type == dns_rdatatype_sig)
952                                 covers = dns_rdata_covers(&t->rdata);
953                         else if (type == dns_rdatatype_any) {
954                                 dns_db_detachnode(db, &node);
955                                 dns_diff_clear(&trash);
956                                 return (DNS_R_NXRRSET);
957                         } else
958                                 covers = 0;
959
960                         /*
961                          * Collect all database RRs for this name and type
962                          * onto d_rrs and sort them.
963                          */
964                         dns_rdataset_init(&rdataset);
965                         result = dns_db_findrdataset(db, node, ver, type,
966                                                      covers, (isc_stdtime_t) 0,
967                                                      &rdataset, NULL);
968                         if (result != ISC_R_SUCCESS) {
969                                 dns_db_detachnode(db, &node);
970                                 dns_diff_clear(&trash);
971                                 return (DNS_R_NXRRSET);
972                         }
973
974                         dns_diff_init(mctx, &d_rrs);
975                         dns_diff_init(mctx, &u_rrs);
976
977                         for (result = dns_rdataset_first(&rdataset);
978                              result == ISC_R_SUCCESS;
979                              result = dns_rdataset_next(&rdataset))
980                         {
981                                 dns_rdata_t rdata = DNS_RDATA_INIT;
982                                 dns_rdataset_current(&rdataset, &rdata);
983                                 result = temp_append(&d_rrs, name, &rdata);
984                                 if (result != ISC_R_SUCCESS)
985                                         goto failure;
986                         }
987                         if (result != ISC_R_NOMORE)
988                                 goto failure;
989                         result = dns_diff_sort(&d_rrs, temp_order);
990                         if (result != ISC_R_SUCCESS)
991                                 goto failure;
992
993                         /*
994                          * Collect all update RRs for this name and type
995                          * onto u_rrs.  No need to sort them here -
996                          * they are already sorted.
997                          */
998                         while (t != NULL &&
999                                dns_name_equal(&t->name, name) &&
1000                                t->rdata.type == type)
1001                         {
1002                                 dns_difftuple_t *next =
1003                                         ISC_LIST_NEXT(t, link);
1004                                 ISC_LIST_UNLINK(temp->tuples, t, link);
1005                                 ISC_LIST_APPEND(u_rrs.tuples, t, link);
1006                                 t = next;
1007                         }
1008
1009                         /* Compare the two sorted lists. */
1010                         result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
1011                                                   ISC_LIST_HEAD(d_rrs.tuples));
1012                         if (result != ISC_R_SUCCESS)
1013                                 goto failure;
1014
1015                         /*
1016                          * We are done with the tuples, but we can't free
1017                          * them yet because "name" still points into one
1018                          * of them.  Move them on a temporary list.
1019                          */
1020                         ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
1021                         ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
1022                         dns_rdataset_disassociate(&rdataset);
1023
1024                         continue;
1025
1026                     failure:
1027                         dns_diff_clear(&d_rrs);
1028                         dns_diff_clear(&u_rrs);
1029                         dns_diff_clear(&trash);
1030                         dns_rdataset_disassociate(&rdataset);
1031                         dns_db_detachnode(db, &node);
1032                         return (result);
1033                 }
1034
1035                 dns_db_detachnode(db, &node);
1036         }
1037
1038         dns_diff_clear(&trash);
1039         return (ISC_R_SUCCESS);
1040 }
1041
1042 /**************************************************************************/
1043 /*
1044  * Conditional deletion of RRs.
1045  */
1046
1047 /*%
1048  * Context structure for delete_if().
1049  */
1050
1051 typedef struct {
1052         rr_predicate *predicate;
1053         dns_db_t *db;
1054         dns_dbversion_t *ver;
1055         dns_diff_t *diff;
1056         dns_name_t *name;
1057         dns_rdata_t *update_rr;
1058 } conditional_delete_ctx_t;
1059
1060 /*%
1061  * Predicate functions for delete_if().
1062  */
1063
1064 /*%
1065  * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
1066  * an RRSIG nor a NSEC.
1067  */
1068 static isc_boolean_t
1069 type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1070         UNUSED(update_rr);
1071         return ((db_rr->type != dns_rdatatype_soa &&
1072                  db_rr->type != dns_rdatatype_ns &&
1073                  db_rr->type != dns_rdatatype_rrsig &&
1074                  db_rr->type != dns_rdatatype_nsec) ?
1075                 ISC_TRUE : ISC_FALSE);
1076 }
1077
1078 /*%
1079  * Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
1080  */
1081 static isc_boolean_t
1082 type_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1083         UNUSED(update_rr);
1084         return ((db_rr->type != dns_rdatatype_rrsig &&
1085                  db_rr->type != dns_rdatatype_nsec) ?
1086                 ISC_TRUE : ISC_FALSE);
1087 }
1088
1089 /*%
1090  * Return true always.
1091  */
1092 static isc_boolean_t
1093 true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1094         UNUSED(update_rr);
1095         UNUSED(db_rr);
1096         return (ISC_TRUE);
1097 }
1098
1099 /*%
1100  * Return true if the record is a RRSIG.
1101  */
1102 static isc_boolean_t
1103 rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1104         UNUSED(update_rr);
1105         return ((db_rr->type == dns_rdatatype_rrsig) ?
1106                 ISC_TRUE : ISC_FALSE);
1107 }
1108
1109 /*%
1110  * Return true iff the two RRs have identical rdata.
1111  */
1112 static isc_boolean_t
1113 rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1114         /*
1115          * XXXRTH  This is not a problem, but we should consider creating
1116          *         dns_rdata_equal() (that used dns_name_equal()), since it
1117          *         would be faster.  Not a priority.
1118          */
1119         return (dns_rdata_compare(update_rr, db_rr) == 0 ?
1120                 ISC_TRUE : ISC_FALSE);
1121 }
1122
1123 /*%
1124  * Return true iff 'update_rr' should replace 'db_rr' according
1125  * to the special RFC2136 rules for CNAME, SOA, and WKS records.
1126  *
1127  * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs
1128  * make little sense, so we replace those, too.
1129  */
1130 static isc_boolean_t
1131 replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
1132         if (db_rr->type != update_rr->type)
1133                 return (ISC_FALSE);
1134         if (db_rr->type == dns_rdatatype_cname)
1135                 return (ISC_TRUE);
1136         if (db_rr->type == dns_rdatatype_dname)
1137                 return (ISC_TRUE);
1138         if (db_rr->type == dns_rdatatype_soa)
1139                 return (ISC_TRUE);
1140         if (db_rr->type == dns_rdatatype_nsec)
1141                 return (ISC_TRUE);
1142         if (db_rr->type == dns_rdatatype_wks) {
1143                 /*
1144                  * Compare the address and protocol fields only.  These
1145                  * form the first five bytes of the RR data.  Do a
1146                  * raw binary comparison; unpacking the WKS RRs using
1147                  * dns_rdata_tostruct() might be cleaner in some ways,
1148                  * but it would require us to pass around an mctx.
1149                  */
1150                 INSIST(db_rr->length >= 5 && update_rr->length >= 5);
1151                 return (memcmp(db_rr->data, update_rr->data, 5) == 0 ?
1152                         ISC_TRUE : ISC_FALSE);
1153         }
1154         return (ISC_FALSE);
1155 }
1156
1157 /*%
1158  * Internal helper function for delete_if().
1159  */
1160 static isc_result_t
1161 delete_if_action(void *data, rr_t *rr) {
1162         conditional_delete_ctx_t *ctx = data;
1163         if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
1164                 isc_result_t result;
1165                 result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
1166                                        DNS_DIFFOP_DEL, ctx->name,
1167                                        rr->ttl, &rr->rdata);
1168                 return (result);
1169         } else {
1170                 return (ISC_R_SUCCESS);
1171         }
1172 }
1173
1174 /*%
1175  * Conditionally delete RRs.  Apply 'predicate' to the RRs
1176  * specified by 'db', 'ver', 'name', and 'type' (which can
1177  * be dns_rdatatype_any to match any type).  Delete those
1178  * RRs for which the predicate returns true, and log the
1179  * deletions in 'diff'.
1180  */
1181 static isc_result_t
1182 delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver,
1183           dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
1184           dns_rdata_t *update_rr, dns_diff_t *diff)
1185 {
1186         conditional_delete_ctx_t ctx;
1187         ctx.predicate = predicate;
1188         ctx.db = db;
1189         ctx.ver = ver;
1190         ctx.diff = diff;
1191         ctx.name = name;
1192         ctx.update_rr = update_rr;
1193         return (foreach_rr(db, ver, name, type, covers,
1194                            delete_if_action, &ctx));
1195 }
1196
1197 /**************************************************************************/
1198 /*%
1199  * Prepare an RR for the addition of the new RR 'ctx->update_rr',
1200  * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
1201  * the RRs if it is replaced by the new RR or has a conflicting TTL.
1202  * The necessary changes are appended to ctx->del_diff and ctx->add_diff;
1203  * we need to do all deletions before any additions so that we don't run
1204  * into transient states with conflicting TTLs.
1205  */
1206
1207 typedef struct {
1208         dns_db_t *db;
1209         dns_dbversion_t *ver;
1210         dns_diff_t *diff;
1211         dns_name_t *name;
1212         dns_rdata_t *update_rr;
1213         dns_ttl_t update_rr_ttl;
1214         isc_boolean_t ignore_add;
1215         dns_diff_t del_diff;
1216         dns_diff_t add_diff;
1217 } add_rr_prepare_ctx_t;
1218
1219 static isc_result_t
1220 add_rr_prepare_action(void *data, rr_t *rr) {
1221         isc_result_t result = ISC_R_SUCCESS;
1222         add_rr_prepare_ctx_t *ctx = data;
1223         dns_difftuple_t *tuple = NULL;
1224         isc_boolean_t equal;
1225
1226         /*
1227          * If the update RR is a "duplicate" of the update RR,
1228          * the update should be silently ignored.
1229          */
1230         equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0);
1231         if (equal && rr->ttl == ctx->update_rr_ttl) {
1232                 ctx->ignore_add = ISC_TRUE;
1233                 return (ISC_R_SUCCESS);
1234         }
1235
1236         /*
1237          * If this RR is "equal" to the update RR, it should
1238          * be deleted before the update RR is added.
1239          */
1240         if (replaces_p(ctx->update_rr, &rr->rdata)) {
1241                 CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
1242                                            ctx->name, rr->ttl, &rr->rdata,
1243                                            &tuple));
1244                 dns_diff_append(&ctx->del_diff, &tuple);
1245                 return (ISC_R_SUCCESS);
1246         }
1247
1248         /*
1249          * If this RR differs in TTL from the update RR,
1250          * its TTL must be adjusted.
1251          */
1252         if (rr->ttl != ctx->update_rr_ttl) {
1253                 CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
1254                                            ctx->name, rr->ttl, &rr->rdata,
1255                                            &tuple));
1256                 dns_diff_append(&ctx->del_diff, &tuple);
1257                 if (!equal) {
1258                         CHECK(dns_difftuple_create(ctx->add_diff.mctx,
1259                                                    DNS_DIFFOP_ADD, ctx->name,
1260                                                    ctx->update_rr_ttl,
1261                                                    &rr->rdata, &tuple));
1262                         dns_diff_append(&ctx->add_diff, &tuple);
1263                 }
1264         }
1265  failure:
1266         return (result);
1267 }
1268
1269 /**************************************************************************/
1270 /*
1271  * Miscellaneous subroutines.
1272  */
1273
1274 /*%
1275  * Extract a single update RR from 'section' of dynamic update message
1276  * 'msg', with consistency checking.
1277  *
1278  * Stores the owner name, rdata, and TTL of the update RR at 'name',
1279  * 'rdata', and 'ttl', respectively.
1280  */
1281 static void
1282 get_current_rr(dns_message_t *msg, dns_section_t section,
1283                dns_rdataclass_t zoneclass, dns_name_t **name,
1284                dns_rdata_t *rdata, dns_rdatatype_t *covers,
1285                dns_ttl_t *ttl, dns_rdataclass_t *update_class)
1286 {
1287         dns_rdataset_t *rdataset;
1288         isc_result_t result;
1289         dns_message_currentname(msg, section, name);
1290         rdataset = ISC_LIST_HEAD((*name)->list);
1291         INSIST(rdataset != NULL);
1292         INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
1293         *covers = rdataset->covers;
1294         *ttl = rdataset->ttl;
1295         result = dns_rdataset_first(rdataset);
1296         INSIST(result == ISC_R_SUCCESS);
1297         dns_rdataset_current(rdataset, rdata);
1298         INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
1299         *update_class = rdata->rdclass;
1300         rdata->rdclass = zoneclass;
1301 }
1302
1303 /*%
1304  * Increment the SOA serial number of database 'db', version 'ver'.
1305  * Replace the SOA record in the database, and log the
1306  * change in 'diff'.
1307  */
1308
1309         /*
1310          * XXXRTH  Failures in this routine will be worth logging, when
1311          *         we have a logging system.  Failure to find the zonename
1312          *         or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
1313          */
1314
1315 static isc_result_t
1316 increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
1317                      dns_diff_t *diff, isc_mem_t *mctx)
1318 {
1319         dns_difftuple_t *deltuple = NULL;
1320         dns_difftuple_t *addtuple = NULL;
1321         isc_uint32_t serial;
1322         isc_result_t result;
1323
1324         CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
1325         CHECK(dns_difftuple_copy(deltuple, &addtuple));
1326         addtuple->op = DNS_DIFFOP_ADD;
1327
1328         serial = dns_soa_getserial(&addtuple->rdata);
1329
1330         /* RFC1982 */
1331         serial = (serial + 1) & 0xFFFFFFFF;
1332         if (serial == 0)
1333                 serial = 1;
1334
1335         dns_soa_setserial(serial, &addtuple->rdata);
1336         CHECK(do_one_tuple(&deltuple, db, ver, diff));
1337         CHECK(do_one_tuple(&addtuple, db, ver, diff));
1338         result = ISC_R_SUCCESS;
1339
1340  failure:
1341         if (addtuple != NULL)
1342                 dns_difftuple_free(&addtuple);
1343         if (deltuple != NULL)
1344                 dns_difftuple_free(&deltuple);
1345         return (result);
1346 }
1347
1348 /*%
1349  * Check that the new SOA record at 'update_rdata' does not
1350  * illegally cause the SOA serial number to decrease or stay
1351  * unchanged relative to the existing SOA in 'db'.
1352  *
1353  * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not.
1354  *
1355  * William King points out that RFC2136 is inconsistent about
1356  * the case where the serial number stays unchanged:
1357  *
1358  *   section 3.4.2.2 requires a server to ignore a SOA update request
1359  *   if the serial number on the update SOA is less_than_or_equal to
1360  *   the zone SOA serial.
1361  *
1362  *   section 3.6 requires a server to ignore a SOA update request if
1363  *   the serial is less_than the zone SOA serial.
1364  *
1365  * Paul says 3.4.2.2 is correct.
1366  *
1367  */
1368 static isc_result_t
1369 check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
1370                     dns_rdata_t *update_rdata, isc_boolean_t *ok)
1371 {
1372         isc_uint32_t db_serial;
1373         isc_uint32_t update_serial;
1374         isc_result_t result;
1375
1376         update_serial = dns_soa_getserial(update_rdata);
1377
1378         result = dns_db_getsoaserial(db, ver, &db_serial);
1379         if (result != ISC_R_SUCCESS)
1380                 return (result);
1381
1382         if (DNS_SERIAL_GE(db_serial, update_serial)) {
1383                 *ok = ISC_FALSE;
1384         } else {
1385                 *ok = ISC_TRUE;
1386         }
1387
1388         return (ISC_R_SUCCESS);
1389
1390 }
1391
1392 /**************************************************************************/
1393 /*
1394  * Incremental updating of NSECs and RRSIGs.
1395  */
1396
1397 #define MAXZONEKEYS 32  /*%< Maximum number of zone keys supported. */
1398
1399 /*%
1400  * We abuse the dns_diff_t type to represent a set of domain names
1401  * affected by the update.
1402  */
1403 static isc_result_t
1404 namelist_append_name(dns_diff_t *list, dns_name_t *name) {
1405         isc_result_t result;
1406         dns_difftuple_t *tuple = NULL;
1407         static dns_rdata_t dummy_rdata = DNS_RDATA_INIT;
1408
1409         CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
1410                                    &dummy_rdata, &tuple));
1411         dns_diff_append(list, &tuple);
1412  failure:
1413         return (result);
1414 }
1415
1416 static isc_result_t
1417 namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
1418 {
1419         isc_result_t result;
1420         dns_fixedname_t fixedname;
1421         dns_name_t *child;
1422         dns_dbiterator_t *dbit = NULL;
1423
1424         dns_fixedname_init(&fixedname);
1425         child = dns_fixedname_name(&fixedname);
1426
1427         CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
1428
1429         for (result = dns_dbiterator_seek(dbit, name);
1430              result == ISC_R_SUCCESS;
1431              result = dns_dbiterator_next(dbit))
1432         {
1433                 dns_dbnode_t *node = NULL;
1434                 CHECK(dns_dbiterator_current(dbit, &node, child));
1435                 dns_db_detachnode(db, &node);
1436                 if (! dns_name_issubdomain(child, name))
1437                         break;
1438                 CHECK(namelist_append_name(affected, child));
1439         }
1440         if (result == ISC_R_NOMORE)
1441                 result = ISC_R_SUCCESS;
1442  failure:
1443         if (dbit != NULL)
1444                 dns_dbiterator_destroy(&dbit);
1445         return (result);
1446 }
1447
1448
1449
1450 /*%
1451  * Helper function for non_nsec_rrset_exists().
1452  */
1453 static isc_result_t
1454 is_non_nsec_action(void *data, dns_rdataset_t *rrset) {
1455         UNUSED(data);
1456         if (!(rrset->type == dns_rdatatype_nsec ||
1457               (rrset->type == dns_rdatatype_rrsig &&
1458                rrset->covers == dns_rdatatype_nsec)))
1459                 return (ISC_R_EXISTS);
1460         return (ISC_R_SUCCESS);
1461 }
1462
1463 /*%
1464  * Check whether there is an rrset other than a NSEC or RRSIG NSEC,
1465  * i.e., anything that justifies the continued existence of a name
1466  * after a secure update.
1467  *
1468  * If such an rrset exists, set '*exists' to ISC_TRUE.
1469  * Otherwise, set it to ISC_FALSE.
1470  */
1471 static isc_result_t
1472 non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
1473                      dns_name_t *name, isc_boolean_t *exists)
1474 {
1475         isc_result_t result;
1476         result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL);
1477         RETURN_EXISTENCE_FLAG;
1478 }
1479
1480 /*%
1481  * A comparison function for sorting dns_diff_t:s by name.
1482  */
1483 static int
1484 name_order(const void *av, const void *bv) {
1485         dns_difftuple_t const * const *ap = av;
1486         dns_difftuple_t const * const *bp = bv;
1487         dns_difftuple_t const *a = *ap;
1488         dns_difftuple_t const *b = *bp;
1489         return (dns_name_compare(&a->name, &b->name));
1490 }
1491
1492 static isc_result_t
1493 uniqify_name_list(dns_diff_t *list) {
1494         isc_result_t result;
1495         dns_difftuple_t *p, *q;
1496
1497         CHECK(dns_diff_sort(list, name_order));
1498
1499         p = ISC_LIST_HEAD(list->tuples);
1500         while (p != NULL) {
1501                 do {
1502                         q = ISC_LIST_NEXT(p, link);
1503                         if (q == NULL || ! dns_name_equal(&p->name, &q->name))
1504                                 break;
1505                         ISC_LIST_UNLINK(list->tuples, q, link);
1506                         dns_difftuple_free(&q);
1507                 } while (1);
1508                 p = ISC_LIST_NEXT(p, link);
1509         }
1510  failure:
1511         return (result);
1512 }
1513
1514 static isc_result_t
1515 is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
1516           isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure)
1517 {
1518         isc_result_t result;
1519         dns_fixedname_t foundname;
1520         dns_fixedname_init(&foundname);
1521         result = dns_db_find(db, name, ver, dns_rdatatype_any,
1522                              DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
1523                              (isc_stdtime_t) 0, NULL,
1524                              dns_fixedname_name(&foundname),
1525                              NULL, NULL);
1526         if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) {
1527                 *flag = ISC_TRUE;
1528                 *cut = ISC_FALSE;
1529                 if (unsecure != NULL)
1530                         *unsecure = ISC_FALSE;
1531                 return (ISC_R_SUCCESS);
1532         } else if (result == DNS_R_ZONECUT) {
1533                 *flag = ISC_TRUE;
1534                 *cut = ISC_TRUE;
1535                 if (unsecure != NULL) {
1536                         /*
1537                          * We are at the zonecut.  Check to see if there
1538                          * is a DS RRset.
1539                          */
1540                         if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0,
1541                                         (isc_stdtime_t) 0, NULL,
1542                                         dns_fixedname_name(&foundname),
1543                                         NULL, NULL) == DNS_R_NXRRSET)
1544                                 *unsecure = ISC_TRUE;
1545                         else
1546                                 *unsecure = ISC_FALSE;
1547                 }
1548                 return (ISC_R_SUCCESS);
1549         } else if (result == DNS_R_GLUE || result == DNS_R_DNAME ||
1550                    result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) {
1551                 *flag = ISC_FALSE;
1552                 *cut = ISC_FALSE;
1553                 if (unsecure != NULL)
1554                         *unsecure = ISC_FALSE;
1555                 return (ISC_R_SUCCESS);
1556         } else {
1557                 /*
1558                  * Silence compiler.
1559                  */
1560                 *flag = ISC_FALSE;
1561                 *cut = ISC_FALSE;
1562                 if (unsecure != NULL)
1563                         *unsecure = ISC_FALSE;
1564                 return (result);
1565         }
1566 }
1567
1568 /*%
1569  * Find the next/previous name that has a NSEC record.
1570  * In other words, skip empty database nodes and names that
1571  * have had their NSECs removed because they are obscured by
1572  * a zone cut.
1573  */
1574 static isc_result_t
1575 next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
1576             dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname,
1577             isc_boolean_t forward)
1578 {
1579         isc_result_t result;
1580         dns_dbiterator_t *dbit = NULL;
1581         isc_boolean_t has_nsec;
1582         unsigned int wraps = 0;
1583
1584         CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
1585
1586         CHECK(dns_dbiterator_seek(dbit, oldname));
1587         do {
1588                 dns_dbnode_t *node = NULL;
1589
1590                 if (forward)
1591                         result = dns_dbiterator_next(dbit);
1592                 else
1593                         result = dns_dbiterator_prev(dbit);
1594                 if (result == ISC_R_NOMORE) {
1595                         /*
1596                          * Wrap around.
1597                          */
1598                         if (forward)
1599                                 CHECK(dns_dbiterator_first(dbit));
1600                         else
1601                                 CHECK(dns_dbiterator_last(dbit));
1602                         wraps++;
1603                         if (wraps == 2) {
1604                                 update_log(client, zone, ISC_LOG_ERROR,
1605                                            "secure zone with no NSECs");
1606                                 result = DNS_R_BADZONE;
1607                                 goto failure;
1608                         }
1609                 }
1610                 CHECK(dns_dbiterator_current(dbit, &node, newname));
1611                 dns_db_detachnode(db, &node);
1612
1613                 /*
1614                  * The iterator may hold the tree lock, and
1615                  * rrset_exists() calls dns_db_findnode() which
1616                  * may try to reacquire it.  To avoid deadlock
1617                  * we must pause the iterator first.
1618                  */
1619                 CHECK(dns_dbiterator_pause(dbit));
1620                 CHECK(rrset_exists(db, ver, newname,
1621                                    dns_rdatatype_nsec, 0, &has_nsec));
1622
1623         } while (! has_nsec);
1624  failure:
1625         if (dbit != NULL)
1626                 dns_dbiterator_destroy(&dbit);
1627
1628         return (result);
1629 }
1630
1631 /*%
1632  * Add a NSEC record for "name", recording the change in "diff".
1633  * The existing NSEC is removed.
1634  */
1635 static isc_result_t
1636 add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
1637          dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl,
1638          dns_diff_t *diff)
1639 {
1640         isc_result_t result;
1641         dns_dbnode_t *node = NULL;
1642         unsigned char buffer[DNS_NSEC_BUFFERSIZE];
1643         dns_rdata_t rdata = DNS_RDATA_INIT;
1644         dns_difftuple_t *tuple = NULL;
1645         dns_fixedname_t fixedname;
1646         dns_name_t *target;
1647
1648         dns_fixedname_init(&fixedname);
1649         target = dns_fixedname_name(&fixedname);
1650
1651         /*
1652          * Find the successor name, aka NSEC target.
1653          */
1654         CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE));
1655
1656         /*
1657          * Create the NSEC RDATA.
1658          */
1659         CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
1660         dns_rdata_init(&rdata);
1661         CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata));
1662         dns_db_detachnode(db, &node);
1663
1664         /*
1665          * Delete the old NSEC and record the change.
1666          */
1667         CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0,
1668                         NULL, diff));
1669         /*
1670          * Add the new NSEC and record the change.
1671          */
1672         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
1673                                    nsecttl, &rdata, &tuple));
1674         CHECK(do_one_tuple(&tuple, db, ver, diff));
1675         INSIST(tuple == NULL);
1676
1677  failure:
1678         if (node != NULL)
1679                 dns_db_detachnode(db, &node);
1680         return (result);
1681 }
1682
1683 /*%
1684  * Add a placeholder NSEC record for "name", recording the change in "diff".
1685  */
1686 static isc_result_t
1687 add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
1688                      dns_diff_t *diff)
1689 {
1690         isc_result_t result;
1691         dns_difftuple_t *tuple = NULL;
1692         isc_region_t r;
1693         unsigned char data[1] = { 0 }; /* The root domain, no bits. */
1694         dns_rdata_t rdata = DNS_RDATA_INIT;
1695
1696         r.base = data;
1697         r.length = sizeof(data);
1698         dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r);
1699         CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
1700                                    &rdata, &tuple));
1701         CHECK(do_one_tuple(&tuple, db, ver, diff));
1702  failure:
1703         return (result);
1704 }
1705
1706 static isc_result_t
1707 find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
1708                isc_mem_t *mctx, unsigned int maxkeys,
1709                dst_key_t **keys, unsigned int *nkeys)
1710 {
1711         isc_result_t result;
1712         dns_dbnode_t *node = NULL;
1713         const char *directory = dns_zone_getkeydirectory(zone);
1714         CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
1715         CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
1716                                        directory, mctx, maxkeys, keys, nkeys));
1717  failure:
1718         if (node != NULL)
1719                 dns_db_detachnode(db, &node);
1720         return (result);
1721 }
1722
1723 static isc_boolean_t
1724 ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) {
1725         isc_boolean_t ret = ISC_FALSE;
1726         isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE;
1727         isc_result_t result;
1728         dns_dbnode_t *node = NULL;
1729         dns_rdataset_t rdataset;
1730         dns_rdata_t rdata = DNS_RDATA_INIT;
1731         dns_rdata_dnskey_t dnskey;
1732
1733         dns_rdataset_init(&rdataset);
1734         CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
1735         CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
1736                                    &rdataset, NULL));
1737         CHECK(dns_rdataset_first(&rdataset));
1738         while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) {
1739                 dns_rdataset_current(&rdataset, &rdata);
1740                 CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
1741                 if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
1742                                  == DNS_KEYOWNER_ZONE) {
1743                         if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0)
1744                                 have_ksk = ISC_TRUE;
1745                         else
1746                                 have_nonksk = ISC_TRUE;
1747                 }
1748                 dns_rdata_reset(&rdata);
1749                 result = dns_rdataset_next(&rdataset);
1750         }
1751         if (have_ksk && have_nonksk)
1752                 ret = ISC_TRUE;
1753  failure:
1754         if (dns_rdataset_isassociated(&rdataset))
1755                 dns_rdataset_disassociate(&rdataset);
1756         if (node != NULL)
1757                 dns_db_detachnode(db, &node);
1758         return (ret);
1759 }
1760
1761 /*%
1762  * Add RRSIG records for an RRset, recording the change in "diff".
1763  */
1764 static isc_result_t
1765 add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
1766          dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type,
1767          dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys,
1768          isc_stdtime_t inception, isc_stdtime_t expire,
1769          isc_boolean_t check_ksk)
1770 {
1771         isc_result_t result;
1772         dns_dbnode_t *node = NULL;
1773         dns_rdataset_t rdataset;
1774         dns_rdata_t sig_rdata = DNS_RDATA_INIT;
1775         isc_buffer_t buffer;
1776         unsigned char data[1024]; /* XXX */
1777         unsigned int i;
1778         isc_boolean_t added_sig = ISC_FALSE;
1779         isc_mem_t *mctx = client->mctx;
1780
1781         dns_rdataset_init(&rdataset);
1782         isc_buffer_init(&buffer, data, sizeof(data));
1783
1784         /* Get the rdataset to sign. */
1785         CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
1786         CHECK(dns_db_findrdataset(db, node, ver, type, 0,
1787                                   (isc_stdtime_t) 0, &rdataset, NULL));
1788         dns_db_detachnode(db, &node);
1789
1790         for (i = 0; i < nkeys; i++) {
1791
1792                 if (check_ksk && type != dns_rdatatype_dnskey &&
1793                     (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0)
1794                         continue;
1795
1796                 if (!dst_key_isprivate(keys[i]))
1797                         continue;
1798
1799                 /* Calculate the signature, creating a RRSIG RDATA. */
1800                 CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
1801                                       &inception, &expire,
1802                                       mctx, &buffer, &sig_rdata));
1803
1804                 /* Update the database and journal with the RRSIG. */
1805                 /* XXX inefficient - will cause dataset merging */
1806                 CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name,
1807                                     rdataset.ttl, &sig_rdata));
1808                 dns_rdata_reset(&sig_rdata);
1809                 added_sig = ISC_TRUE;
1810         }
1811         if (!added_sig) {
1812                 update_log(client, zone, ISC_LOG_ERROR,
1813                            "found no private keys, "
1814                            "unable to generate any signatures");
1815                 result = ISC_R_NOTFOUND;
1816         }
1817
1818  failure:
1819         if (dns_rdataset_isassociated(&rdataset))
1820                 dns_rdataset_disassociate(&rdataset);
1821         if (node != NULL)
1822                 dns_db_detachnode(db, &node);
1823         return (result);
1824 }
1825
1826 static isc_result_t
1827 add_exposed_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
1828                  dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut,
1829                  dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys,
1830                  isc_stdtime_t inception, isc_stdtime_t expire,
1831                  isc_boolean_t check_ksk)
1832 {
1833         isc_result_t result;
1834         dns_dbnode_t *node;
1835         dns_rdatasetiter_t *iter;
1836
1837         node = NULL;
1838         result = dns_db_findnode(db, name, ISC_FALSE, &node);
1839         if (result == ISC_R_NOTFOUND)
1840                 return (ISC_R_SUCCESS);
1841         if (result != ISC_R_SUCCESS)
1842                 return (result);
1843
1844         iter = NULL;
1845         result = dns_db_allrdatasets(db, node, ver,
1846                                      (isc_stdtime_t) 0, &iter);
1847         if (result != ISC_R_SUCCESS)
1848                 goto cleanup_node;
1849
1850         for (result = dns_rdatasetiter_first(iter);
1851              result == ISC_R_SUCCESS;
1852              result = dns_rdatasetiter_next(iter))
1853         {
1854                 dns_rdataset_t rdataset;
1855                 dns_rdatatype_t type;
1856                 isc_boolean_t flag;
1857
1858                 dns_rdataset_init(&rdataset);
1859                 dns_rdatasetiter_current(iter, &rdataset);
1860                 type = rdataset.type;
1861                 dns_rdataset_disassociate(&rdataset);
1862
1863                 /*
1864                  * We don't need to sign unsigned NSEC records at the cut
1865                  * as they are handled elsewhere.
1866                  */
1867                 if ((type == dns_rdatatype_rrsig) ||
1868                     (cut && type != dns_rdatatype_ds))
1869                         continue;
1870                 result = rrset_exists(db, ver, name, dns_rdatatype_rrsig,
1871                                       type, &flag);
1872                 if (result != ISC_R_SUCCESS)
1873                         goto cleanup_iterator;
1874                 if (flag)
1875                         continue;;
1876                 result = add_sigs(client, zone, db, ver, name, type, diff,
1877                                   keys, nkeys, inception, expire, check_ksk);
1878                 if (result != ISC_R_SUCCESS)
1879                         goto cleanup_iterator;
1880         }
1881         if (result == ISC_R_NOMORE)
1882                 result = ISC_R_SUCCESS;
1883
1884  cleanup_iterator:
1885         dns_rdatasetiter_destroy(&iter);
1886
1887  cleanup_node:
1888         dns_db_detachnode(db, &node);
1889
1890         return (result);
1891 }
1892
1893 /*%
1894  * Update RRSIG and NSEC records affected by an update.  The original
1895  * update, including the SOA serial update but excluding the RRSIG & NSEC
1896  * changes, is in "diff" and has already been applied to "newver" of "db".
1897  * The database version prior to the update is "oldver".
1898  *
1899  * The necessary RRSIG and NSEC changes will be applied to "newver"
1900  * and added (as a minimal diff) to "diff".
1901  *
1902  * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds.
1903  */
1904 static isc_result_t
1905 update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
1906                   dns_dbversion_t *oldver, dns_dbversion_t *newver,
1907                   dns_diff_t *diff, isc_uint32_t sigvalidityinterval)
1908 {
1909         isc_result_t result;
1910         dns_difftuple_t *t;
1911         dns_diff_t diffnames;
1912         dns_diff_t affected;
1913         dns_diff_t sig_diff;
1914         dns_diff_t nsec_diff;
1915         dns_diff_t nsec_mindiff;
1916         isc_boolean_t flag;
1917         dst_key_t *zone_keys[MAXZONEKEYS];
1918         unsigned int nkeys = 0;
1919         unsigned int i;
1920         isc_stdtime_t now, inception, expire;
1921         dns_ttl_t nsecttl;
1922         dns_rdata_soa_t soa;
1923         dns_rdata_t rdata = DNS_RDATA_INIT;
1924         dns_rdataset_t rdataset;
1925         dns_dbnode_t *node = NULL;
1926         isc_boolean_t check_ksk;
1927         isc_boolean_t cut;
1928
1929         dns_diff_init(client->mctx, &diffnames);
1930         dns_diff_init(client->mctx, &affected);
1931
1932         dns_diff_init(client->mctx, &sig_diff);
1933         dns_diff_init(client->mctx, &nsec_diff);
1934         dns_diff_init(client->mctx, &nsec_mindiff);
1935
1936         result = find_zone_keys(zone, db, newver, client->mctx,
1937                                 MAXZONEKEYS, zone_keys, &nkeys);
1938         if (result != ISC_R_SUCCESS) {
1939                 update_log(client, zone, ISC_LOG_ERROR,
1940                            "could not get zone keys for secure dynamic update");
1941                 goto failure;
1942         }
1943
1944         isc_stdtime_get(&now);
1945         inception = now - 3600; /* Allow for some clock skew. */
1946         expire = now + sigvalidityinterval;
1947
1948         /*
1949          * Do we look at the KSK flag on the DNSKEY to determining which
1950          * keys sign which RRsets?  First check the zone option then
1951          * check the keys flags to make sure at least one has a ksk set
1952          * and one doesn't.
1953          */
1954         check_ksk = ISC_TF((dns_zone_getoptions(zone) &
1955                             DNS_ZONEOPT_UPDATECHECKKSK) != 0);
1956         if (check_ksk)
1957                 check_ksk = ksk_sanity(db, newver);
1958
1959         /*
1960          * Get the NSEC's TTL from the SOA MINIMUM field.
1961          */
1962         CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
1963         dns_rdataset_init(&rdataset);
1964         CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0,
1965                                   (isc_stdtime_t) 0, &rdataset, NULL));
1966         CHECK(dns_rdataset_first(&rdataset));
1967         dns_rdataset_current(&rdataset, &rdata);
1968         CHECK(dns_rdata_tostruct(&rdata, &soa, NULL));
1969         nsecttl = soa.minimum;
1970         dns_rdataset_disassociate(&rdataset);
1971         dns_db_detachnode(db, &node);
1972
1973         /*
1974          * Find all RRsets directly affected by the update, and
1975          * update their RRSIGs.  Also build a list of names affected
1976          * by the update in "diffnames".
1977          */
1978         CHECK(dns_diff_sort(diff, temp_order));
1979
1980         t = ISC_LIST_HEAD(diff->tuples);
1981         while (t != NULL) {
1982                 dns_name_t *name = &t->name;
1983                 /* Now "name" is a new, unique name affected by the update. */
1984
1985                 CHECK(namelist_append_name(&diffnames, name));
1986
1987                 while (t != NULL && dns_name_equal(&t->name, name)) {
1988                         dns_rdatatype_t type;
1989                         type = t->rdata.type;
1990
1991                         /*
1992                          * Now "name" and "type" denote a new unique RRset
1993                          * affected by the update.
1994                          */
1995
1996                         /* Don't sign RRSIGs. */
1997                         if (type == dns_rdatatype_rrsig)
1998                                 goto skip;
1999
2000                         /*
2001                          * Delete all old RRSIGs covering this type, since they
2002                          * are all invalid when the signed RRset has changed.
2003                          * We may not be able to recreate all of them - tough.
2004                          */
2005                         CHECK(delete_if(true_p, db, newver, name,
2006                                         dns_rdatatype_rrsig, type,
2007                                         NULL, &sig_diff));
2008
2009                         /*
2010                          * If this RRset is still visible after the update,
2011                          * add a new signature for it.
2012                          */
2013                         CHECK(rrset_visible(db, newver, name, type, &flag));
2014                         if (flag) {
2015                                 CHECK(add_sigs(client, zone, db, newver, name,
2016                                                type, &sig_diff, zone_keys,
2017                                                nkeys, inception, expire,
2018                                                check_ksk));
2019                         }
2020                 skip:
2021                         /* Skip any other updates to the same RRset. */
2022                         while (t != NULL &&
2023                                dns_name_equal(&t->name, name) &&
2024                                t->rdata.type == type)
2025                         {
2026                                 t = ISC_LIST_NEXT(t, link);
2027                         }
2028                 }
2029         }
2030
2031         /* Remove orphaned NSECs and RRSIG NSECs. */
2032         for (t = ISC_LIST_HEAD(diffnames.tuples);
2033              t != NULL;
2034              t = ISC_LIST_NEXT(t, link))
2035         {
2036                 CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag));
2037                 if (! flag) {
2038                         CHECK(delete_if(true_p, db, newver, &t->name,
2039                                         dns_rdatatype_any, 0,
2040                                         NULL, &sig_diff));
2041                 }
2042         }
2043
2044         /*
2045          * When a name is created or deleted, its predecessor needs to
2046          * have its NSEC updated.
2047          */
2048         for (t = ISC_LIST_HEAD(diffnames.tuples);
2049              t != NULL;
2050              t = ISC_LIST_NEXT(t, link))
2051         {
2052                 isc_boolean_t existed, exists;
2053                 dns_fixedname_t fixedname;
2054                 dns_name_t *prevname;
2055
2056                 dns_fixedname_init(&fixedname);
2057                 prevname = dns_fixedname_name(&fixedname);
2058
2059                 CHECK(name_exists(db, oldver, &t->name, &existed));
2060                 CHECK(name_exists(db, newver, &t->name, &exists));
2061                 if (exists == existed)
2062                         continue;
2063
2064                 /*
2065                  * Find the predecessor.
2066                  * When names become obscured or unobscured in this update
2067                  * transaction, we may find the wrong predecessor because
2068                  * the NSECs have not yet been updated to reflect the delegation
2069                  * change.  This should not matter because in this case,
2070                  * the correct predecessor is either the delegation node or
2071                  * a newly unobscured node, and those nodes are on the
2072                  * "affected" list in any case.
2073                  */
2074                 CHECK(next_active(client, zone, db, newver,
2075                                   &t->name, prevname, ISC_FALSE));
2076                 CHECK(namelist_append_name(&affected, prevname));
2077         }
2078
2079         /*
2080          * Find names potentially affected by delegation changes
2081          * (obscured by adding an NS or DNAME, or unobscured by
2082          * removing one).
2083          */
2084         for (t = ISC_LIST_HEAD(diffnames.tuples);
2085              t != NULL;
2086              t = ISC_LIST_NEXT(t, link))
2087         {
2088                 isc_boolean_t ns_existed, dname_existed;
2089                 isc_boolean_t ns_exists, dname_exists;
2090
2091                 CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0,
2092                                    &ns_existed));
2093                 CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0,
2094                                    &dname_existed));
2095                 CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
2096                                    &ns_exists));
2097                 CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0,
2098                                    &dname_exists));
2099                 if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
2100                         continue;
2101                 /*
2102                  * There was a delegation change.  Mark all subdomains
2103                  * of t->name as potentially needing a NSEC update.
2104                  */
2105                 CHECK(namelist_append_subdomain(db, &t->name, &affected));
2106         }
2107
2108         ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link);
2109         INSIST(ISC_LIST_EMPTY(diffnames.tuples));
2110
2111         CHECK(uniqify_name_list(&affected));
2112
2113         /*
2114          * Determine which names should have NSECs, and delete/create
2115          * NSECs to make it so.  We don't know the final NSEC targets yet,
2116          * so we just create placeholder NSECs with arbitrary contents
2117          * to indicate that their respective owner names should be part of
2118          * the NSEC chain.
2119          */
2120         for (t = ISC_LIST_HEAD(affected.tuples);
2121              t != NULL;
2122              t = ISC_LIST_NEXT(t, link))
2123         {
2124                 isc_boolean_t exists;
2125                 dns_name_t *name = &t->name;
2126
2127                 CHECK(name_exists(db, newver, name, &exists));
2128                 if (! exists)
2129                         continue;
2130                 CHECK(is_active(db, newver, name, &flag, &cut, NULL));
2131                 if (!flag) {
2132                         /*
2133                          * This name is obscured.  Delete any
2134                          * existing NSEC record.
2135                          */
2136                         CHECK(delete_if(true_p, db, newver, name,
2137                                         dns_rdatatype_nsec, 0,
2138                                         NULL, &nsec_diff));
2139                         CHECK(delete_if(rrsig_p, db, newver, name,
2140                                         dns_rdatatype_any, 0, NULL, diff));
2141                 } else {
2142                         /*
2143                          * This name is not obscured.  It should have a NSEC.
2144                          */
2145                         CHECK(rrset_exists(db, newver, name,
2146                                            dns_rdatatype_nsec, 0, &flag));
2147                         if (! flag)
2148                                 CHECK(add_placeholder_nsec(db, newver, &t->name,
2149                                                           diff));
2150                         CHECK(add_exposed_sigs(client, zone, db, newver, name,
2151                                                cut, diff, zone_keys, nkeys,
2152                                                inception, expire, check_ksk));
2153                 }
2154         }
2155
2156         /*
2157          * Now we know which names are part of the NSEC chain.
2158          * Make them all point at their correct targets.
2159          */
2160         for (t = ISC_LIST_HEAD(affected.tuples);
2161              t != NULL;
2162              t = ISC_LIST_NEXT(t, link))
2163         {
2164                 CHECK(rrset_exists(db, newver, &t->name,
2165                                    dns_rdatatype_nsec, 0, &flag));
2166                 if (flag) {
2167                         /*
2168                          * There is a NSEC, but we don't know if it is correct.
2169                          * Delete it and create a correct one to be sure.
2170                          * If the update was unnecessary, the diff minimization
2171                          * will take care of eliminating it from the journal,
2172                          * IXFRs, etc.
2173                          *
2174                          * The RRSIG bit should always be set in the NSECs
2175                          * we generate, because they will all get RRSIG NSECs.
2176                          * (XXX what if the zone keys are missing?).
2177                          * Because the RRSIG NSECs have not necessarily been
2178                          * created yet, the correctness of the bit mask relies
2179                          * on the assumption that NSECs are only created if
2180                          * there is other data, and if there is other data,
2181                          * there are other RRSIGs.
2182                          */
2183                         CHECK(add_nsec(client, zone, db, newver, &t->name,
2184                                        nsecttl, &nsec_diff));
2185                 }
2186         }
2187
2188         /*
2189          * Minimize the set of NSEC updates so that we don't
2190          * have to regenerate the RRSIG NSECs for NSECs that were
2191          * replaced with identical ones.
2192          */
2193         while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) {
2194                 ISC_LIST_UNLINK(nsec_diff.tuples, t, link);
2195                 dns_diff_appendminimal(&nsec_mindiff, &t);
2196         }
2197
2198         /* Update RRSIG NSECs. */
2199         for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
2200              t != NULL;
2201              t = ISC_LIST_NEXT(t, link))
2202         {
2203                 if (t->op == DNS_DIFFOP_DEL) {
2204                         CHECK(delete_if(true_p, db, newver, &t->name,
2205                                         dns_rdatatype_rrsig, dns_rdatatype_nsec,
2206                                         NULL, &sig_diff));
2207                 } else if (t->op == DNS_DIFFOP_ADD) {
2208                         CHECK(add_sigs(client, zone, db, newver, &t->name,
2209                                        dns_rdatatype_nsec, &sig_diff,
2210                                        zone_keys, nkeys, inception, expire,
2211                                        check_ksk));
2212                 } else {
2213                         INSIST(0);
2214                 }
2215         }
2216
2217         /* Record our changes for the journal. */
2218         while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
2219                 ISC_LIST_UNLINK(sig_diff.tuples, t, link);
2220                 dns_diff_appendminimal(diff, &t);
2221         }
2222         while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) {
2223                 ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link);
2224                 dns_diff_appendminimal(diff, &t);
2225         }
2226
2227         INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
2228         INSIST(ISC_LIST_EMPTY(nsec_diff.tuples));
2229         INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples));
2230
2231  failure:
2232         dns_diff_clear(&sig_diff);
2233         dns_diff_clear(&nsec_diff);
2234         dns_diff_clear(&nsec_mindiff);
2235
2236         dns_diff_clear(&affected);
2237         dns_diff_clear(&diffnames);
2238
2239         for (i = 0; i < nkeys; i++)
2240                 dst_key_free(&zone_keys[i]);
2241
2242         return (result);
2243 }
2244
2245
2246 /**************************************************************************/
2247 /*%
2248  * The actual update code in all its glory.  We try to follow
2249  * the RFC2136 pseudocode as closely as possible.
2250  */
2251
2252 static isc_result_t
2253 send_update_event(ns_client_t *client, dns_zone_t *zone) {
2254         isc_result_t result = ISC_R_SUCCESS;
2255         update_event_t *event = NULL;
2256         isc_task_t *zonetask = NULL;
2257         ns_client_t *evclient;
2258
2259         event = (update_event_t *)
2260                 isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
2261                                    update_action, NULL, sizeof(*event));
2262         if (event == NULL)
2263                 FAIL(ISC_R_NOMEMORY);
2264         event->zone = zone;
2265         event->result = ISC_R_SUCCESS;
2266
2267         evclient = NULL;
2268         ns_client_attach(client, &evclient);
2269         INSIST(client->nupdates == 0);
2270         client->nupdates++;
2271         event->ev_arg = evclient;
2272
2273         dns_zone_gettask(zone, &zonetask);
2274         isc_task_send(zonetask, ISC_EVENT_PTR(&event));
2275
2276  failure:
2277         if (event != NULL)
2278                 isc_event_free(ISC_EVENT_PTR(&event));
2279         return (result);
2280 }
2281
2282 static void
2283 respond(ns_client_t *client, isc_result_t result) {
2284         isc_result_t msg_result;
2285
2286         msg_result = dns_message_reply(client->message, ISC_TRUE);
2287         if (msg_result != ISC_R_SUCCESS)
2288                 goto msg_failure;
2289         client->message->rcode = dns_result_torcode(result);
2290
2291         ns_client_send(client);
2292         return;
2293
2294  msg_failure:
2295         isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
2296                       ISC_LOG_ERROR,
2297                       "could not create update response message: %s",
2298                       isc_result_totext(msg_result));
2299         ns_client_next(client, msg_result);
2300 }
2301
2302 void
2303 ns_update_start(ns_client_t *client, isc_result_t sigresult) {
2304         dns_message_t *request = client->message;
2305         isc_result_t result;
2306         dns_name_t *zonename;
2307         dns_rdataset_t *zone_rdataset;
2308         dns_zone_t *zone = NULL;
2309
2310         /*
2311          * Interpret the zone section.
2312          */
2313         result = dns_message_firstname(request, DNS_SECTION_ZONE);
2314         if (result != ISC_R_SUCCESS)
2315                 FAILC(DNS_R_FORMERR, "update zone section empty");
2316
2317         /*
2318          * The zone section must contain exactly one "question", and
2319          * it must be of type SOA.
2320          */
2321         zonename = NULL;
2322         dns_message_currentname(request, DNS_SECTION_ZONE, &zonename);
2323         zone_rdataset = ISC_LIST_HEAD(zonename->list);
2324         if (zone_rdataset->type != dns_rdatatype_soa)
2325                 FAILC(DNS_R_FORMERR,
2326                       "update zone section contains non-SOA");
2327         if (ISC_LIST_NEXT(zone_rdataset, link) != NULL)
2328                 FAILC(DNS_R_FORMERR,
2329                       "update zone section contains multiple RRs");
2330
2331         /* The zone section must have exactly one name. */
2332         result = dns_message_nextname(request, DNS_SECTION_ZONE);
2333         if (result != ISC_R_NOMORE)
2334                 FAILC(DNS_R_FORMERR,
2335                       "update zone section contains multiple RRs");
2336
2337         result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
2338                              &zone);
2339         if (result != ISC_R_SUCCESS)
2340                 FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
2341
2342         switch(dns_zone_gettype(zone)) {
2343         case dns_zone_master:
2344                 /*
2345                  * We can now fail due to a bad signature as we now know
2346                  * that we are the master.
2347                  */
2348                 if (sigresult != ISC_R_SUCCESS)
2349                         FAIL(sigresult);
2350                 CHECK(send_update_event(client, zone));
2351                 break;
2352         case dns_zone_slave:
2353                 CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone),
2354                                      "update forwarding", zonename, ISC_TRUE,
2355                                      ISC_FALSE));
2356                 CHECK(send_forward_event(client, zone));
2357                 break;
2358         default:
2359                 FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
2360         }
2361         return;
2362
2363  failure:
2364         if (result == DNS_R_REFUSED) {
2365                 INSIST(dns_zone_gettype(zone) == dns_zone_slave);
2366                 inc_stats(zone, dns_nsstatscounter_updaterej);
2367         }
2368         /*
2369          * We failed without having sent an update event to the zone.
2370          * We are still in the client task context, so we can
2371          * simply give an error response without switching tasks.
2372          */
2373         respond(client, result);
2374         if (zone != NULL)
2375                 dns_zone_detach(&zone);
2376 }
2377
2378 /*%
2379  * DS records are not allowed to exist without corresponding NS records,
2380  * draft-ietf-dnsext-delegation-signer-11.txt, 2.2 Protocol Change,
2381  * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
2382  */
2383
2384 static isc_result_t
2385 remove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) {
2386         isc_result_t result;
2387         isc_boolean_t ns_exists;
2388         dns_difftuple_t *tupple;
2389         dns_diff_t temp_diff;
2390
2391         dns_diff_init(diff->mctx, &temp_diff);
2392
2393         for (tupple = ISC_LIST_HEAD(diff->tuples);
2394              tupple != NULL;
2395              tupple = ISC_LIST_NEXT(tupple, link)) {
2396                 if (!((tupple->op == DNS_DIFFOP_DEL &&
2397                        tupple->rdata.type == dns_rdatatype_ns) ||
2398                       (tupple->op == DNS_DIFFOP_ADD &&
2399                        tupple->rdata.type == dns_rdatatype_ds)))
2400                         continue;
2401                 CHECK(rrset_exists(db, newver, &tupple->name,
2402                                    dns_rdatatype_ns, 0, &ns_exists));
2403                 if (ns_exists &&
2404                     !dns_name_equal(&tupple->name, dns_db_origin(db)))
2405                         continue;
2406                 CHECK(delete_if(true_p, db, newver, &tupple->name,
2407                                 dns_rdatatype_ds, 0, NULL, &temp_diff));
2408         }
2409         result = ISC_R_SUCCESS;
2410
2411  failure:
2412         for (tupple = ISC_LIST_HEAD(temp_diff.tuples);
2413              tupple != NULL;
2414              tupple = ISC_LIST_HEAD(temp_diff.tuples)) {
2415                 ISC_LIST_UNLINK(temp_diff.tuples, tupple, link);
2416                 dns_diff_appendminimal(diff, &tupple);
2417         }
2418         return (result);
2419 }
2420
2421 /*
2422  * This implements the post load integrity checks for mx records.
2423  */
2424 static isc_result_t
2425 check_mx(ns_client_t *client, dns_zone_t *zone,
2426          dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff)
2427 {
2428         char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
2429         char ownerbuf[DNS_NAME_FORMATSIZE];
2430         char namebuf[DNS_NAME_FORMATSIZE];
2431         char altbuf[DNS_NAME_FORMATSIZE];
2432         dns_difftuple_t *t;
2433         dns_fixedname_t fixed;
2434         dns_name_t *foundname;
2435         dns_rdata_mx_t mx;
2436         dns_rdata_t rdata;
2437         isc_boolean_t ok = ISC_TRUE;
2438         isc_boolean_t isaddress;
2439         isc_result_t result;
2440         struct in6_addr addr6;
2441         struct in_addr addr;
2442         unsigned int options;
2443
2444         dns_fixedname_init(&fixed);
2445         foundname = dns_fixedname_name(&fixed);
2446         dns_rdata_init(&rdata);
2447         options = dns_zone_getoptions(zone);
2448
2449         for (t = ISC_LIST_HEAD(diff->tuples);
2450              t != NULL;
2451              t = ISC_LIST_NEXT(t, link)) {
2452                 if (t->op != DNS_DIFFOP_ADD ||
2453                     t->rdata.type != dns_rdatatype_mx)
2454                         continue;
2455
2456                 result = dns_rdata_tostruct(&t->rdata, &mx, NULL);
2457                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
2458                 /*
2459                  * Check if we will error out if we attempt to reload the
2460                  * zone.
2461                  */
2462                 dns_name_format(&mx.mx, namebuf, sizeof(namebuf));
2463                 dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf));
2464                 isaddress = ISC_FALSE;
2465                 if ((options & DNS_RDATA_CHECKMX) != 0 &&
2466                     strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) {
2467                         if (tmp[strlen(tmp) - 1] == '.')
2468                                 tmp[strlen(tmp) - 1] = '\0';
2469                         if (inet_aton(tmp, &addr) == 1 ||
2470                             inet_pton(AF_INET6, tmp, &addr6) == 1)
2471                                 isaddress = ISC_TRUE;
2472                 }
2473
2474                 if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) {
2475                         update_log(client, zone, ISC_LOG_ERROR,
2476                                    "%s/MX: '%s': %s",
2477                                    ownerbuf, namebuf,
2478                                    dns_result_totext(DNS_R_MXISADDRESS));
2479                         ok = ISC_FALSE;
2480                 } else if (isaddress) {
2481                         update_log(client, zone, ISC_LOG_WARNING,
2482                                    "%s/MX: warning: '%s': %s",
2483                                    ownerbuf, namebuf,
2484                                    dns_result_totext(DNS_R_MXISADDRESS));
2485                 }
2486
2487                 /*
2488                  * Check zone integrity checks.
2489                  */
2490                 if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0)
2491                         continue;
2492                 result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a,
2493                                      0, 0, NULL, foundname, NULL, NULL);
2494                 if (result == ISC_R_SUCCESS)
2495                         continue;
2496
2497                 if (result == DNS_R_NXRRSET) {
2498                         result = dns_db_find(db, &mx.mx, newver,
2499                                              dns_rdatatype_aaaa,
2500                                              0, 0, NULL, foundname,
2501                                              NULL, NULL);
2502                         if (result == ISC_R_SUCCESS)
2503                                 continue;
2504                 }
2505
2506                 if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) {
2507                         update_log(client, zone, ISC_LOG_ERROR,
2508                                    "%s/MX '%s' has no address records "
2509                                    "(A or AAAA)", ownerbuf, namebuf);
2510                         ok = ISC_FALSE;
2511                 } else if (result == DNS_R_CNAME) {
2512                         update_log(client, zone, ISC_LOG_ERROR,
2513                                    "%s/MX '%s' is a CNAME (illegal)",
2514                                    ownerbuf, namebuf);
2515                         ok = ISC_FALSE;
2516                 } else if (result == DNS_R_DNAME) {
2517                         dns_name_format(foundname, altbuf, sizeof altbuf);
2518                         update_log(client, zone, ISC_LOG_ERROR,
2519                                    "%s/MX '%s' is below a DNAME '%s' (illegal)",
2520                                    ownerbuf, namebuf, altbuf);
2521                         ok = ISC_FALSE;
2522                 }
2523         }
2524         return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED);
2525 }
2526
2527 static void
2528 update_action(isc_task_t *task, isc_event_t *event) {
2529         update_event_t *uev = (update_event_t *) event;
2530         dns_zone_t *zone = uev->zone;
2531         ns_client_t *client = (ns_client_t *)event->ev_arg;
2532
2533         isc_result_t result;
2534         dns_db_t *db = NULL;
2535         dns_dbversion_t *oldver = NULL;
2536         dns_dbversion_t *ver = NULL;
2537         dns_diff_t diff;        /* Pending updates. */
2538         dns_diff_t temp;        /* Pending RR existence assertions. */
2539         isc_boolean_t soa_serial_changed = ISC_FALSE;
2540         isc_mem_t *mctx = client->mctx;
2541         dns_rdatatype_t covers;
2542         dns_message_t *request = client->message;
2543         dns_rdataclass_t zoneclass;
2544         dns_name_t *zonename;
2545         dns_ssutable_t *ssutable = NULL;
2546         dns_fixedname_t tmpnamefixed;
2547         dns_name_t *tmpname = NULL;
2548         unsigned int options;
2549
2550         INSIST(event->ev_type == DNS_EVENT_UPDATE);
2551
2552         dns_diff_init(mctx, &diff);
2553         dns_diff_init(mctx, &temp);
2554
2555         CHECK(dns_zone_getdb(zone, &db));
2556         zonename = dns_db_origin(db);
2557         zoneclass = dns_db_class(db);
2558         dns_zone_getssutable(zone, &ssutable);
2559         dns_db_currentversion(db, &oldver);
2560         CHECK(dns_db_newversion(db, &ver));
2561
2562         /*
2563          * Check prerequisites.
2564          */
2565
2566         for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE);
2567              result == ISC_R_SUCCESS;
2568              result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE))
2569         {
2570                 dns_name_t *name = NULL;
2571                 dns_rdata_t rdata = DNS_RDATA_INIT;
2572                 dns_ttl_t ttl;
2573                 dns_rdataclass_t update_class;
2574                 isc_boolean_t flag;
2575
2576                 get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass,
2577                                &name, &rdata, &covers, &ttl, &update_class);
2578
2579                 if (ttl != 0)
2580                         PREREQFAILC(DNS_R_FORMERR,
2581                                     "prerequisite TTL is not zero");
2582
2583                 if (! dns_name_issubdomain(name, zonename))
2584                         PREREQFAILN(DNS_R_NOTZONE, name,
2585                                     "prerequisite name is out of zone");
2586
2587                 if (update_class == dns_rdataclass_any) {
2588                         if (rdata.length != 0)
2589                                 PREREQFAILC(DNS_R_FORMERR,
2590                                       "class ANY prerequisite "
2591                                       "RDATA is not empty");
2592                         if (rdata.type == dns_rdatatype_any) {
2593                                 CHECK(name_exists(db, ver, name, &flag));
2594                                 if (! flag) {
2595                                         PREREQFAILN(DNS_R_NXDOMAIN, name,
2596                                                     "'name in use' "
2597                                                     "prerequisite not "
2598                                                     "satisfied");
2599                                 }
2600                         } else {
2601                                 CHECK(rrset_exists(db, ver, name,
2602                                                    rdata.type, covers, &flag));
2603                                 if (! flag) {
2604                                         /* RRset does not exist. */
2605                                         PREREQFAILNT(DNS_R_NXRRSET, name, rdata.type,
2606                                         "'rrset exists (value independent)' "
2607                                         "prerequisite not satisfied");
2608                                 }
2609                         }
2610                 } else if (update_class == dns_rdataclass_none) {
2611                         if (rdata.length != 0)
2612                                 PREREQFAILC(DNS_R_FORMERR,
2613                                             "class NONE prerequisite "
2614                                             "RDATA is not empty");
2615                         if (rdata.type == dns_rdatatype_any) {
2616                                 CHECK(name_exists(db, ver, name, &flag));
2617                                 if (flag) {
2618                                         PREREQFAILN(DNS_R_YXDOMAIN, name,
2619                                                     "'name not in use' "
2620                                                     "prerequisite not "
2621                                                     "satisfied");
2622                                 }
2623                         } else {
2624                                 CHECK(rrset_exists(db, ver, name,
2625                                                    rdata.type, covers, &flag));
2626                                 if (flag) {
2627                                         /* RRset exists. */
2628                                         PREREQFAILNT(DNS_R_YXRRSET, name,
2629                                                      rdata.type,
2630                                                      "'rrset does not exist' "
2631                                                      "prerequisite not "
2632                                                      "satisfied");
2633                                 }
2634                         }
2635                 } else if (update_class == zoneclass) {
2636                         /* "temp<rr.name, rr.type> += rr;" */
2637                         result = temp_append(&temp, name, &rdata);
2638                         if (result != ISC_R_SUCCESS) {
2639                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
2640                                          "temp entry creation failed: %s",
2641                                                  dns_result_totext(result));
2642                                 FAIL(ISC_R_UNEXPECTED);
2643                         }
2644                 } else {
2645                         PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite");
2646                 }
2647         }
2648         if (result != ISC_R_NOMORE)
2649                 FAIL(result);
2650
2651
2652         /*
2653          * Perform the final check of the "rrset exists (value dependent)"
2654          * prerequisites.
2655          */
2656         if (ISC_LIST_HEAD(temp.tuples) != NULL) {
2657                 dns_rdatatype_t type;
2658
2659                 /*
2660                  * Sort the prerequisite records by owner name,
2661                  * type, and rdata.
2662                  */
2663                 result = dns_diff_sort(&temp, temp_order);
2664                 if (result != ISC_R_SUCCESS)
2665                         FAILC(result, "'RRset exists (value dependent)' "
2666                               "prerequisite not satisfied");
2667
2668                 dns_fixedname_init(&tmpnamefixed);
2669                 tmpname = dns_fixedname_name(&tmpnamefixed);
2670                 result = temp_check(mctx, &temp, db, ver, tmpname, &type);
2671                 if (result != ISC_R_SUCCESS)
2672                         FAILNT(result, tmpname, type,
2673                                "'RRset exists (value dependent)' "
2674                                "prerequisite not satisfied");
2675         }
2676
2677         update_log(client, zone, LOGLEVEL_DEBUG,
2678                    "prerequisites are OK");
2679
2680         /*
2681          * Check Requestor's Permissions.  It seems a bit silly to do this
2682          * only after prerequisite testing, but that is what RFC2136 says.
2683          */
2684         result = ISC_R_SUCCESS;
2685         if (ssutable == NULL)
2686                 CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
2687                                      "update", zonename, ISC_FALSE, ISC_FALSE));
2688         else if (client->signer == NULL)
2689                 CHECK(checkupdateacl(client, NULL, "update", zonename,
2690                                      ISC_FALSE, ISC_TRUE));
2691
2692         if (dns_zone_getupdatedisabled(zone))
2693                 FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled "
2694                                      "because the zone is frozen.  Use "
2695                                      "'rndc thaw' to re-enable updates.");
2696
2697         /*
2698          * Perform the Update Section Prescan.
2699          */
2700
2701         for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
2702              result == ISC_R_SUCCESS;
2703              result = dns_message_nextname(request, DNS_SECTION_UPDATE))
2704         {
2705                 dns_name_t *name = NULL;
2706                 dns_rdata_t rdata = DNS_RDATA_INIT;
2707                 dns_ttl_t ttl;
2708                 dns_rdataclass_t update_class;
2709                 get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
2710                                &name, &rdata, &covers, &ttl, &update_class);
2711
2712                 if (! dns_name_issubdomain(name, zonename))
2713                         FAILC(DNS_R_NOTZONE,
2714                               "update RR is outside zone");
2715                 if (update_class == zoneclass) {
2716                         /*
2717                          * Check for meta-RRs.  The RFC2136 pseudocode says
2718                          * check for ANY|AXFR|MAILA|MAILB, but the text adds
2719                          * "or any other QUERY metatype"
2720                          */
2721                         if (dns_rdatatype_ismeta(rdata.type)) {
2722                                 FAILC(DNS_R_FORMERR,
2723                                       "meta-RR in update");
2724                         }
2725                         result = dns_zone_checknames(zone, name, &rdata);
2726                         if (result != ISC_R_SUCCESS)
2727                                 FAIL(DNS_R_REFUSED);
2728                 } else if (update_class == dns_rdataclass_any) {
2729                         if (ttl != 0 || rdata.length != 0 ||
2730                             (dns_rdatatype_ismeta(rdata.type) &&
2731                              rdata.type != dns_rdatatype_any))
2732                                 FAILC(DNS_R_FORMERR,
2733                                       "meta-RR in update");
2734                 } else if (update_class == dns_rdataclass_none) {
2735                         if (ttl != 0 ||
2736                             dns_rdatatype_ismeta(rdata.type))
2737                                 FAILC(DNS_R_FORMERR,
2738                                       "meta-RR in update");
2739                 } else {
2740                         update_log(client, zone, ISC_LOG_WARNING,
2741                                    "update RR has incorrect class %d",
2742                                    update_class);
2743                         FAIL(DNS_R_FORMERR);
2744                 }
2745                 /*
2746                  * draft-ietf-dnsind-simple-secure-update-01 says
2747                  * "Unlike traditional dynamic update, the client
2748                  * is forbidden from updating NSEC records."
2749                  */
2750                 if (dns_db_issecure(db)) {
2751                         if (rdata.type == dns_rdatatype_nsec) {
2752                                 FAILC(DNS_R_REFUSED,
2753                                       "explicit NSEC updates are not allowed "
2754                                       "in secure zones");
2755                         }
2756                         else if (rdata.type == dns_rdatatype_rrsig) {
2757                                 FAILC(DNS_R_REFUSED,
2758                                       "explicit RRSIG updates are currently not "
2759                                       "supported in secure zones");
2760                         }
2761                 }
2762
2763                 if (ssutable != NULL && client->signer != NULL) {
2764                         if (rdata.type != dns_rdatatype_any) {
2765                                 if (!dns_ssutable_checkrules(ssutable,
2766                                                              client->signer,
2767                                                              name, rdata.type))
2768                                         FAILC(DNS_R_REFUSED,
2769                                               "rejected by secure update");
2770                         }
2771                         else {
2772                                 if (!ssu_checkall(db, ver, name, ssutable,
2773                                                   client->signer))
2774                                         FAILC(DNS_R_REFUSED,
2775                                               "rejected by secure update");
2776                         }
2777                 }
2778         }
2779         if (result != ISC_R_NOMORE)
2780                 FAIL(result);
2781
2782         update_log(client, zone, LOGLEVEL_DEBUG,
2783                    "update section prescan OK");
2784
2785         /*
2786          * Process the Update Section.
2787          */
2788
2789         options = dns_zone_getoptions(zone);
2790         for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
2791              result == ISC_R_SUCCESS;
2792              result = dns_message_nextname(request, DNS_SECTION_UPDATE))
2793         {
2794                 dns_name_t *name = NULL;
2795                 dns_rdata_t rdata = DNS_RDATA_INIT;
2796                 dns_ttl_t ttl;
2797                 dns_rdataclass_t update_class;
2798                 isc_boolean_t flag;
2799
2800                 get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
2801                                &name, &rdata, &covers, &ttl, &update_class);
2802
2803                 if (update_class == zoneclass) {
2804
2805                         /*
2806                          * RFC1123 doesn't allow MF and MD in master zones.                              */
2807                         if (rdata.type == dns_rdatatype_md ||
2808                             rdata.type == dns_rdatatype_mf) {
2809                                 char typebuf[DNS_RDATATYPE_FORMATSIZE];
2810
2811                                 dns_rdatatype_format(rdata.type, typebuf,
2812                                                      sizeof(typebuf));
2813                                 update_log(client, zone, LOGLEVEL_PROTOCOL,
2814                                            "attempt to add %s ignored",
2815                                            typebuf);
2816                                 continue;
2817                         }
2818                         if (rdata.type == dns_rdatatype_ns &&
2819                             dns_name_iswildcard(name)) {
2820                                 update_log(client, zone,
2821                                            LOGLEVEL_PROTOCOL,
2822                                            "attempt to add wildcard NS record"
2823                                            "ignored");
2824                                 continue;
2825                         }
2826                         if (rdata.type == dns_rdatatype_cname) {
2827                                 CHECK(cname_incompatible_rrset_exists(db, ver,
2828                                                                       name,
2829                                                                       &flag));
2830                                 if (flag) {
2831                                         update_log(client, zone,
2832                                                    LOGLEVEL_PROTOCOL,
2833                                                    "attempt to add CNAME "
2834                                                    "alongside non-CNAME "
2835                                                    "ignored");
2836                                         continue;
2837                                 }
2838                         } else {
2839                                 CHECK(rrset_exists(db, ver, name,
2840                                                    dns_rdatatype_cname, 0,
2841                                                    &flag));
2842                                 if (flag &&
2843                                     ! dns_rdatatype_isdnssec(rdata.type))
2844                                 {
2845                                         update_log(client, zone,
2846                                                    LOGLEVEL_PROTOCOL,
2847                                                    "attempt to add non-CNAME "
2848                                                    "alongside CNAME ignored");
2849                                         continue;
2850                                 }
2851                         }
2852                         if (rdata.type == dns_rdatatype_soa) {
2853                                 isc_boolean_t ok;
2854                                 CHECK(rrset_exists(db, ver, name,
2855                                                    dns_rdatatype_soa, 0,
2856                                                    &flag));
2857                                 if (! flag) {
2858                                         update_log(client, zone,
2859                                                    LOGLEVEL_PROTOCOL,
2860                                                    "attempt to create 2nd "
2861                                                    "SOA ignored");
2862                                         continue;
2863                                 }
2864                                 CHECK(check_soa_increment(db, ver, &rdata,
2865                                                           &ok));
2866                                 if (! ok) {
2867                                         update_log(client, zone,
2868                                                    LOGLEVEL_PROTOCOL,
2869                                                    "SOA update failed to "
2870                                                    "increment serial, "
2871                                                    "ignoring it");
2872                                         continue;
2873                                 }
2874                                 soa_serial_changed = ISC_TRUE;
2875                         }
2876                         if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
2877                             dns_name_internalwildcard(name)) {
2878                                 char namestr[DNS_NAME_FORMATSIZE];
2879                                 dns_name_format(name, namestr,
2880                                                 sizeof(namestr));
2881                                 update_log(client, zone, LOGLEVEL_PROTOCOL,
2882                                            "warning: ownername '%s' contains "
2883                                            "a non-terminal wildcard", namestr);
2884                         }
2885
2886                         if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {
2887                                 char namestr[DNS_NAME_FORMATSIZE];
2888                                 char typestr[DNS_RDATATYPE_FORMATSIZE];
2889                                 dns_name_format(name, namestr,
2890                                                 sizeof(namestr));
2891                                 dns_rdatatype_format(rdata.type, typestr,
2892                                                      sizeof(typestr));
2893                                 update_log(client, zone, LOGLEVEL_PROTOCOL,
2894                                            "adding an RR at '%s' %s",
2895                                            namestr, typestr);
2896                         }
2897
2898                         /* Prepare the affected RRset for the addition. */
2899                         {
2900                                 add_rr_prepare_ctx_t ctx;
2901                                 ctx.db = db;
2902                                 ctx.ver = ver;
2903                                 ctx.diff = &diff;
2904                                 ctx.name = name;
2905                                 ctx.update_rr = &rdata;
2906                                 ctx.update_rr_ttl = ttl;
2907                                 ctx.ignore_add = ISC_FALSE;
2908                                 dns_diff_init(mctx, &ctx.del_diff);
2909                                 dns_diff_init(mctx, &ctx.add_diff);
2910                                 CHECK(foreach_rr(db, ver, name, rdata.type,
2911                                                  covers, add_rr_prepare_action,
2912                                                  &ctx));
2913
2914                                 if (ctx.ignore_add) {
2915                                         dns_diff_clear(&ctx.del_diff);
2916                                         dns_diff_clear(&ctx.add_diff);
2917                                 } else {
2918                                         CHECK(do_diff(&ctx.del_diff, db, ver, &diff));
2919                                         CHECK(do_diff(&ctx.add_diff, db, ver, &diff));
2920                                         CHECK(update_one_rr(db, ver, &diff,
2921                                                             DNS_DIFFOP_ADD,
2922                                                             name, ttl, &rdata));
2923                                 }
2924                         }
2925                 } else if (update_class == dns_rdataclass_any) {
2926                         if (rdata.type == dns_rdatatype_any) {
2927                                 if (isc_log_wouldlog(ns_g_lctx,
2928                                                      LOGLEVEL_PROTOCOL))
2929                                 {
2930                                         char namestr[DNS_NAME_FORMATSIZE];
2931                                         dns_name_format(name, namestr,
2932                                                         sizeof(namestr));
2933                                         update_log(client, zone,
2934                                                    LOGLEVEL_PROTOCOL,
2935                                                    "delete all rrsets from "
2936                                                    "name '%s'", namestr);
2937                                 }
2938                                 if (dns_name_equal(name, zonename)) {
2939                                         CHECK(delete_if(type_not_soa_nor_ns_p,
2940                                                         db, ver, name,
2941                                                         dns_rdatatype_any, 0,
2942                                                         &rdata, &diff));
2943                                 } else {
2944                                         CHECK(delete_if(type_not_dnssec,
2945                                                         db, ver, name,
2946                                                         dns_rdatatype_any, 0,
2947                                                         &rdata, &diff));
2948                                 }
2949                         } else if (dns_name_equal(name, zonename) &&
2950                                    (rdata.type == dns_rdatatype_soa ||
2951                                     rdata.type == dns_rdatatype_ns)) {
2952                                 update_log(client, zone, LOGLEVEL_PROTOCOL,
2953                                            "attempt to delete all SOA "
2954                                            "or NS records ignored");
2955                                 continue;
2956                         } else {
2957                                 if (isc_log_wouldlog(ns_g_lctx,
2958                                                      LOGLEVEL_PROTOCOL))
2959                                 {
2960                                         char namestr[DNS_NAME_FORMATSIZE];
2961                                         char typestr[DNS_RDATATYPE_FORMATSIZE];
2962                                         dns_name_format(name, namestr,
2963                                                         sizeof(namestr));
2964                                         dns_rdatatype_format(rdata.type,
2965                                                              typestr,
2966                                                              sizeof(typestr));
2967                                         update_log(client, zone,
2968                                                    LOGLEVEL_PROTOCOL,
2969                                                    "deleting rrset at '%s' %s",
2970                                                    namestr, typestr);
2971                                 }
2972                                 CHECK(delete_if(true_p, db, ver, name,
2973                                                 rdata.type, covers, &rdata,
2974                                                 &diff));
2975                         }
2976                 } else if (update_class == dns_rdataclass_none) {
2977                         /*
2978                          * The (name == zonename) condition appears in
2979                          * RFC2136 3.4.2.4 but is missing from the pseudocode.
2980                          */
2981                         if (dns_name_equal(name, zonename)) {
2982                                 if (rdata.type == dns_rdatatype_soa) {
2983                                         update_log(client, zone,
2984                                                    LOGLEVEL_PROTOCOL,
2985                                                    "attempt to delete SOA "
2986                                                    "ignored");
2987                                         continue;
2988                                 }
2989                                 if (rdata.type == dns_rdatatype_ns) {
2990                                         int count;
2991                                         CHECK(rr_count(db, ver, name,
2992                                                        dns_rdatatype_ns,
2993                                                        0, &count));
2994                                         if (count == 1) {
2995                                                 update_log(client, zone,
2996                                                            LOGLEVEL_PROTOCOL,
2997                                                            "attempt to "
2998                                                            "delete last "
2999                                                            "NS ignored");
3000                                                 continue;
3001                                         }
3002                                 }
3003                         }
3004                         update_log(client, zone,
3005                                    LOGLEVEL_PROTOCOL,
3006                                    "deleting an RR");
3007                         CHECK(delete_if(rr_equal_p, db, ver, name,
3008                                         rdata.type, covers, &rdata, &diff));
3009                 }
3010         }
3011         if (result != ISC_R_NOMORE)
3012                 FAIL(result);
3013
3014         /*
3015          * If any changes were made, increment the SOA serial number,
3016          * update RRSIGs and NSECs (if zone is secure), and write the update
3017          * to the journal.
3018          */
3019         if (! ISC_LIST_EMPTY(diff.tuples)) {
3020                 char *journalfile;
3021                 dns_journal_t *journal;
3022
3023                 /*
3024                  * Increment the SOA serial, but only if it was not
3025                  * changed as a result of an update operation.
3026                  */
3027                 if (! soa_serial_changed) {
3028                         CHECK(increment_soa_serial(db, ver, &diff, mctx));
3029                 }
3030
3031                 CHECK(check_mx(client, zone, db, ver, &diff));
3032
3033                 CHECK(remove_orphaned_ds(db, ver, &diff));
3034
3035                 if (dns_db_issecure(db)) {
3036                         result = update_signatures(client, zone, db, oldver,
3037                                                    ver, &diff,
3038                                          dns_zone_getsigvalidityinterval(zone));
3039                         if (result != ISC_R_SUCCESS) {
3040                                 update_log(client, zone,
3041                                            ISC_LOG_ERROR,
3042                                            "RRSIG/NSEC update failed: %s",
3043                                            isc_result_totext(result));
3044                                 goto failure;
3045                         }
3046                 }
3047
3048                 journalfile = dns_zone_getjournal(zone);
3049                 if (journalfile != NULL) {
3050                         update_log(client, zone, LOGLEVEL_DEBUG,
3051                                    "writing journal %s", journalfile);
3052
3053                         journal = NULL;
3054                         result = dns_journal_open(mctx, journalfile,
3055                                                   ISC_TRUE, &journal);
3056                         if (result != ISC_R_SUCCESS)
3057                                 FAILS(result, "journal open failed");
3058
3059                         result = dns_journal_write_transaction(journal, &diff);
3060                         if (result != ISC_R_SUCCESS) {
3061                                 dns_journal_destroy(&journal);
3062                                 FAILS(result, "journal write failed");
3063                         }
3064
3065                         dns_journal_destroy(&journal);
3066                 }
3067
3068                 /*
3069                  * XXXRTH  Just a note that this committing code will have
3070                  *         to change to handle databases that need two-phase
3071                  *         commit, but this isn't a priority.
3072                  */
3073                 update_log(client, zone, LOGLEVEL_DEBUG,
3074                            "committing update transaction");
3075                 dns_db_closeversion(db, &ver, ISC_TRUE);
3076
3077                 /*
3078                  * Mark the zone as dirty so that it will be written to disk.
3079                  */
3080                 dns_zone_markdirty(zone);
3081
3082                 /*
3083                  * Notify slaves of the change we just made.
3084                  */
3085                 dns_zone_notify(zone);
3086         } else {
3087                 update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
3088                 dns_db_closeversion(db, &ver, ISC_TRUE);
3089         }
3090         result = ISC_R_SUCCESS;
3091         goto common;
3092
3093  failure:
3094         /*
3095          * The reason for failure should have been logged at this point.
3096          */
3097         if (ver != NULL) {
3098                 update_log(client, zone, LOGLEVEL_DEBUG,
3099                            "rolling back");
3100                 dns_db_closeversion(db, &ver, ISC_FALSE);
3101         }
3102
3103  common:
3104         dns_diff_clear(&temp);
3105         dns_diff_clear(&diff);
3106
3107         if (oldver != NULL)
3108                 dns_db_closeversion(db, &oldver, ISC_FALSE);
3109
3110         if (db != NULL)
3111                 dns_db_detach(&db);
3112
3113         if (ssutable != NULL)
3114                 dns_ssutable_detach(&ssutable);
3115
3116         isc_task_detach(&task);
3117         uev->result = result;
3118         if (zone != NULL)
3119                 INSIST(uev->zone == zone); /* we use this later */
3120         uev->ev_type = DNS_EVENT_UPDATEDONE;
3121         uev->ev_action = updatedone_action;
3122         isc_task_send(client->task, &event);
3123         INSIST(event == NULL);
3124 }
3125
3126 static void
3127 updatedone_action(isc_task_t *task, isc_event_t *event) {
3128         update_event_t *uev = (update_event_t *) event;
3129         ns_client_t *client = (ns_client_t *) event->ev_arg;
3130
3131         UNUSED(task);
3132
3133         INSIST(event->ev_type == DNS_EVENT_UPDATEDONE);
3134         INSIST(task == client->task);
3135
3136         INSIST(client->nupdates > 0);
3137         switch (uev->result) {
3138         case ISC_R_SUCCESS:
3139                 inc_stats(uev->zone, dns_nsstatscounter_updatedone);
3140                 break;
3141         case DNS_R_REFUSED:
3142                 inc_stats(uev->zone, dns_nsstatscounter_updaterej);
3143                 break;
3144         default:
3145                 inc_stats(uev->zone, dns_nsstatscounter_updatefail);
3146                 break;
3147         }
3148         if (uev->zone != NULL)
3149                 dns_zone_detach(&uev->zone);
3150         client->nupdates--;
3151         respond(client, uev->result);
3152         isc_event_free(&event);
3153         ns_client_detach(&client);
3154 }
3155
3156 /*%
3157  * Update forwarding support.
3158  */
3159
3160 static void
3161 forward_fail(isc_task_t *task, isc_event_t *event) {
3162         ns_client_t *client = (ns_client_t *)event->ev_arg;
3163
3164         UNUSED(task);
3165
3166         INSIST(client->nupdates > 0);
3167         client->nupdates--;
3168         respond(client, DNS_R_SERVFAIL);
3169         isc_event_free(&event);
3170         ns_client_detach(&client);
3171 }
3172
3173
3174 static void
3175 forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
3176         update_event_t *uev = arg;
3177         ns_client_t *client = uev->ev_arg;
3178         dns_zone_t *zone = uev->zone;
3179
3180         if (result != ISC_R_SUCCESS) {
3181                 INSIST(answer == NULL);
3182                 uev->ev_type = DNS_EVENT_UPDATEDONE;
3183                 uev->ev_action = forward_fail;
3184                 inc_stats(zone, dns_nsstatscounter_updatefwdfail);
3185         } else {
3186                 uev->ev_type = DNS_EVENT_UPDATEDONE;
3187                 uev->ev_action = forward_done;
3188                 uev->answer = answer;
3189                 inc_stats(zone, dns_nsstatscounter_updaterespfwd);
3190         }
3191         isc_task_send(client->task, ISC_EVENT_PTR(&uev));
3192         dns_zone_detach(&zone);
3193 }
3194
3195 static void
3196 forward_done(isc_task_t *task, isc_event_t *event) {
3197         update_event_t *uev = (update_event_t *) event;
3198         ns_client_t *client = (ns_client_t *)event->ev_arg;
3199
3200         UNUSED(task);
3201
3202         INSIST(client->nupdates > 0);
3203         client->nupdates--;
3204         ns_client_sendraw(client, uev->answer);
3205         dns_message_destroy(&uev->answer);
3206         isc_event_free(&event);
3207         ns_client_detach(&client);
3208 }
3209
3210 static void
3211 forward_action(isc_task_t *task, isc_event_t *event) {
3212         update_event_t *uev = (update_event_t *) event;
3213         dns_zone_t *zone = uev->zone;
3214         ns_client_t *client = (ns_client_t *)event->ev_arg;
3215         isc_result_t result;
3216
3217         result = dns_zone_forwardupdate(zone, client->message,
3218                                         forward_callback, event);
3219         if (result != ISC_R_SUCCESS) {
3220                 uev->ev_type = DNS_EVENT_UPDATEDONE;
3221                 uev->ev_action = forward_fail;
3222                 isc_task_send(client->task, &event);
3223                 inc_stats(zone, dns_nsstatscounter_updatefwdfail);
3224                 dns_zone_detach(&zone);
3225         } else
3226                 inc_stats(zone, dns_nsstatscounter_updatereqfwd);
3227         isc_task_detach(&task);
3228 }
3229
3230 static isc_result_t
3231 send_forward_event(ns_client_t *client, dns_zone_t *zone) {
3232         isc_result_t result = ISC_R_SUCCESS;
3233         update_event_t *event = NULL;
3234         isc_task_t *zonetask = NULL;
3235         ns_client_t *evclient;
3236
3237         event = (update_event_t *)
3238                 isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
3239                                    forward_action, NULL, sizeof(*event));
3240         if (event == NULL)
3241                 FAIL(ISC_R_NOMEMORY);
3242         event->zone = zone;
3243         event->result = ISC_R_SUCCESS;
3244
3245         evclient = NULL;
3246         ns_client_attach(client, &evclient);
3247         INSIST(client->nupdates == 0);
3248         client->nupdates++;
3249         event->ev_arg = evclient;
3250
3251         dns_zone_gettask(zone, &zonetask);
3252         isc_task_send(zonetask, ISC_EVENT_PTR(&event));
3253
3254  failure:
3255         if (event != NULL)
3256                 isc_event_free(ISC_EVENT_PTR(&event));
3257         return (result);
3258 }