update.c revision 165071
1135446Strhodes/* 2165071Sdougb * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18165071Sdougb/* $Id: update.c,v 1.88.2.5.2.29 2006/01/06 00:01:42 marka Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/print.h> 23135446Strhodes#include <isc/string.h> 24135446Strhodes#include <isc/taskpool.h> 25135446Strhodes#include <isc/util.h> 26135446Strhodes 27135446Strhodes#include <dns/db.h> 28135446Strhodes#include <dns/dbiterator.h> 29135446Strhodes#include <dns/diff.h> 30135446Strhodes#include <dns/dnssec.h> 31135446Strhodes#include <dns/events.h> 32135446Strhodes#include <dns/fixedname.h> 33135446Strhodes#include <dns/journal.h> 34135446Strhodes#include <dns/message.h> 35135446Strhodes#include <dns/nsec.h> 36135446Strhodes#include <dns/rdataclass.h> 37135446Strhodes#include <dns/rdataset.h> 38135446Strhodes#include <dns/rdatasetiter.h> 39165071Sdougb#include <dns/rdatastruct.h> 40135446Strhodes#include <dns/rdatatype.h> 41135446Strhodes#include <dns/soa.h> 42135446Strhodes#include <dns/ssu.h> 43135446Strhodes#include <dns/view.h> 44135446Strhodes#include <dns/zone.h> 45135446Strhodes#include <dns/zt.h> 46135446Strhodes 47135446Strhodes#include <named/client.h> 48135446Strhodes#include <named/log.h> 49135446Strhodes#include <named/update.h> 50135446Strhodes 51135446Strhodes/* 52135446Strhodes * This module implements dynamic update as in RFC2136. 53135446Strhodes */ 54135446Strhodes 55135446Strhodes/* 56135446Strhodes XXX TODO: 57135446Strhodes - document strict minimality 58135446Strhodes*/ 59135446Strhodes 60135446Strhodes/**************************************************************************/ 61135446Strhodes 62135446Strhodes/* 63135446Strhodes * Log level for tracing dynamic update protocol requests. 64135446Strhodes */ 65135446Strhodes#define LOGLEVEL_PROTOCOL ISC_LOG_INFO 66135446Strhodes 67135446Strhodes/* 68135446Strhodes * Log level for low-level debug tracing. 69135446Strhodes */ 70135446Strhodes#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) 71135446Strhodes 72135446Strhodes/* 73135446Strhodes * Check an operation for failure. These macros all assume that 74135446Strhodes * the function using them has a 'result' variable and a 'failure' 75135446Strhodes * label. 76135446Strhodes */ 77135446Strhodes#define CHECK(op) \ 78135446Strhodes do { result = (op); \ 79135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 80135446Strhodes } while (0) 81135446Strhodes 82135446Strhodes/* 83135446Strhodes * Fail unconditionally with result 'code', which must not 84135446Strhodes * be ISC_R_SUCCESS. The reason for failure presumably has 85135446Strhodes * been logged already. 86135446Strhodes * 87135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 88135446Strhodes * from complaining about "end-of-loop code not reached". 89135446Strhodes */ 90135446Strhodes 91135446Strhodes#define FAIL(code) \ 92135446Strhodes do { \ 93135446Strhodes result = (code); \ 94135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 95135446Strhodes } while (0) 96135446Strhodes 97135446Strhodes/* 98135446Strhodes * Fail unconditionally and log as a client error. 99135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 100135446Strhodes * from complaining about "end-of-loop code not reached". 101135446Strhodes */ 102135446Strhodes#define FAILC(code, msg) \ 103135446Strhodes do { \ 104135446Strhodes const char *_what = "failed"; \ 105135446Strhodes result = (code); \ 106135446Strhodes switch (result) { \ 107135446Strhodes case DNS_R_NXDOMAIN: \ 108135446Strhodes case DNS_R_YXDOMAIN: \ 109135446Strhodes case DNS_R_YXRRSET: \ 110135446Strhodes case DNS_R_NXRRSET: \ 111135446Strhodes _what = "unsuccessful"; \ 112135446Strhodes } \ 113135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 114135446Strhodes "update %s: %s (%s)", _what, \ 115135446Strhodes msg, isc_result_totext(result)); \ 116135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 117135446Strhodes } while (0) 118135446Strhodes 119135446Strhodes#define FAILN(code, name, msg) \ 120135446Strhodes do { \ 121135446Strhodes const char *_what = "failed"; \ 122135446Strhodes result = (code); \ 123135446Strhodes switch (result) { \ 124135446Strhodes case DNS_R_NXDOMAIN: \ 125135446Strhodes case DNS_R_YXDOMAIN: \ 126135446Strhodes case DNS_R_YXRRSET: \ 127135446Strhodes case DNS_R_NXRRSET: \ 128135446Strhodes _what = "unsuccessful"; \ 129135446Strhodes } \ 130135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 131135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 132135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 133135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 134135446Strhodes "update %s: %s: %s (%s)", _what, _nbuf, \ 135135446Strhodes msg, isc_result_totext(result)); \ 136135446Strhodes } \ 137135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 138135446Strhodes } while (0) 139135446Strhodes 140135446Strhodes#define FAILNT(code, name, type, msg) \ 141135446Strhodes do { \ 142135446Strhodes const char *_what = "failed"; \ 143135446Strhodes result = (code); \ 144135446Strhodes switch (result) { \ 145135446Strhodes case DNS_R_NXDOMAIN: \ 146135446Strhodes case DNS_R_YXDOMAIN: \ 147135446Strhodes case DNS_R_YXRRSET: \ 148135446Strhodes case DNS_R_NXRRSET: \ 149135446Strhodes _what = "unsuccessful"; \ 150135446Strhodes } \ 151135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 152135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 153135446Strhodes char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ 154135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 155135446Strhodes dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ 156135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 157135446Strhodes "update %s: %s/%s: %s (%s)", \ 158135446Strhodes _what, _nbuf, _tbuf, msg, \ 159135446Strhodes isc_result_totext(result)); \ 160135446Strhodes } \ 161135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 162135446Strhodes } while (0) 163135446Strhodes/* 164135446Strhodes * Fail unconditionally and log as a server error. 165135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 166135446Strhodes * from complaining about "end-of-loop code not reached". 167135446Strhodes */ 168135446Strhodes#define FAILS(code, msg) \ 169135446Strhodes do { \ 170135446Strhodes result = (code); \ 171135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 172135446Strhodes "error: %s: %s", \ 173135446Strhodes msg, isc_result_totext(result)); \ 174135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 175135446Strhodes } while (0) 176135446Strhodes 177135446Strhodes/**************************************************************************/ 178135446Strhodes 179135446Strhodestypedef struct rr rr_t; 180135446Strhodes 181135446Strhodesstruct rr { 182135446Strhodes /* dns_name_t name; */ 183135446Strhodes isc_uint32_t ttl; 184135446Strhodes dns_rdata_t rdata; 185135446Strhodes}; 186135446Strhodes 187135446Strhodestypedef struct update_event update_event_t; 188135446Strhodes 189135446Strhodesstruct update_event { 190135446Strhodes ISC_EVENT_COMMON(update_event_t); 191135446Strhodes dns_zone_t *zone; 192135446Strhodes isc_result_t result; 193135446Strhodes dns_message_t *answer; 194135446Strhodes}; 195135446Strhodes 196135446Strhodes/**************************************************************************/ 197135446Strhodes/* 198135446Strhodes * Forward declarations. 199135446Strhodes */ 200135446Strhodes 201135446Strhodesstatic void update_action(isc_task_t *task, isc_event_t *event); 202135446Strhodesstatic void updatedone_action(isc_task_t *task, isc_event_t *event); 203135446Strhodesstatic isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone); 204135446Strhodesstatic void forward_done(isc_task_t *task, isc_event_t *event); 205135446Strhodes 206135446Strhodes/**************************************************************************/ 207135446Strhodes 208135446Strhodesstatic void 209135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 210135446Strhodes int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); 211135446Strhodes 212135446Strhodesstatic void 213135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 214135446Strhodes int level, const char *fmt, ...) 215135446Strhodes{ 216135446Strhodes va_list ap; 217135446Strhodes char message[4096]; 218135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 219135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 220135446Strhodes 221135446Strhodes if (client == NULL || zone == NULL) 222135446Strhodes return; 223135446Strhodes 224135446Strhodes if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE) 225135446Strhodes return; 226135446Strhodes 227135446Strhodes dns_name_format(dns_zone_getorigin(zone), namebuf, 228135446Strhodes sizeof(namebuf)); 229135446Strhodes dns_rdataclass_format(dns_zone_getclass(zone), classbuf, 230135446Strhodes sizeof(classbuf)); 231135446Strhodes 232135446Strhodes va_start(ap, fmt); 233135446Strhodes vsnprintf(message, sizeof(message), fmt, ap); 234135446Strhodes va_end(ap); 235135446Strhodes 236135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 237135446Strhodes level, "updating zone '%s/%s': %s", 238135446Strhodes namebuf, classbuf, message); 239135446Strhodes} 240135446Strhodes 241135446Strhodesstatic isc_result_t 242135446Strhodescheckupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message, 243135446Strhodes dns_name_t *zonename, isc_boolean_t slave) 244135446Strhodes{ 245135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 246135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 247135446Strhodes int level = ISC_LOG_ERROR; 248135446Strhodes const char *msg = "denied"; 249135446Strhodes isc_result_t result; 250135446Strhodes 251135446Strhodes if (slave && acl == NULL) { 252135446Strhodes result = DNS_R_NOTIMP; 253135446Strhodes level = ISC_LOG_DEBUG(3); 254135446Strhodes msg = "disabled"; 255135446Strhodes } else 256135446Strhodes result = ns_client_checkaclsilent(client, acl, ISC_FALSE); 257135446Strhodes 258135446Strhodes if (result == ISC_R_SUCCESS) { 259135446Strhodes level = ISC_LOG_DEBUG(3); 260135446Strhodes msg = "approved"; 261135446Strhodes } 262135446Strhodes 263135446Strhodes dns_name_format(zonename, namebuf, sizeof(namebuf)); 264135446Strhodes dns_rdataclass_format(client->view->rdclass, classbuf, 265135446Strhodes sizeof(classbuf)); 266135446Strhodes 267135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 268135446Strhodes NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", 269135446Strhodes message, namebuf, classbuf, msg); 270135446Strhodes return (result); 271135446Strhodes} 272135446Strhodes 273135446Strhodes/* 274135446Strhodes * Update a single RR in version 'ver' of 'db' and log the 275135446Strhodes * update in 'diff'. 276135446Strhodes * 277135446Strhodes * Ensures: 278135446Strhodes * '*tuple' == NULL. Either the tuple is freed, or its 279135446Strhodes * ownership has been transferred to the diff. 280135446Strhodes */ 281135446Strhodesstatic isc_result_t 282135446Strhodesdo_one_tuple(dns_difftuple_t **tuple, 283135446Strhodes dns_db_t *db, dns_dbversion_t *ver, 284135446Strhodes dns_diff_t *diff) 285135446Strhodes{ 286135446Strhodes dns_diff_t temp_diff; 287135446Strhodes isc_result_t result; 288135446Strhodes 289135446Strhodes /* 290135446Strhodes * Create a singleton diff. 291135446Strhodes */ 292135446Strhodes dns_diff_init(diff->mctx, &temp_diff); 293135446Strhodes ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); 294135446Strhodes 295135446Strhodes /* 296135446Strhodes * Apply it to the database. 297135446Strhodes */ 298135446Strhodes result = dns_diff_apply(&temp_diff, db, ver); 299135446Strhodes ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); 300135446Strhodes if (result != ISC_R_SUCCESS) { 301135446Strhodes dns_difftuple_free(tuple); 302135446Strhodes return (result); 303135446Strhodes } 304135446Strhodes 305135446Strhodes /* 306135446Strhodes * Merge it into the current pending journal entry. 307135446Strhodes */ 308135446Strhodes dns_diff_appendminimal(diff, tuple); 309135446Strhodes 310135446Strhodes /* 311135446Strhodes * Do not clear temp_diff. 312135446Strhodes */ 313135446Strhodes return (ISC_R_SUCCESS); 314135446Strhodes} 315135446Strhodes 316135446Strhodes/* 317135446Strhodes * Perform the updates in 'updates' in version 'ver' of 'db' and log the 318135446Strhodes * update in 'diff'. 319135446Strhodes * 320135446Strhodes * Ensures: 321135446Strhodes * 'updates' is empty. 322135446Strhodes */ 323135446Strhodesstatic isc_result_t 324135446Strhodesdo_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver, 325135446Strhodes dns_diff_t *diff) 326135446Strhodes{ 327135446Strhodes isc_result_t result; 328135446Strhodes while (! ISC_LIST_EMPTY(updates->tuples)) { 329135446Strhodes dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples); 330135446Strhodes ISC_LIST_UNLINK(updates->tuples, t, link); 331135446Strhodes CHECK(do_one_tuple(&t, db, ver, diff)); 332135446Strhodes } 333135446Strhodes return (ISC_R_SUCCESS); 334135446Strhodes 335135446Strhodes failure: 336135446Strhodes dns_diff_clear(diff); 337135446Strhodes return (result); 338135446Strhodes} 339135446Strhodes 340135446Strhodesstatic isc_result_t 341135446Strhodesupdate_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, 342135446Strhodes dns_diffop_t op, dns_name_t *name, 343135446Strhodes dns_ttl_t ttl, dns_rdata_t *rdata) 344135446Strhodes{ 345135446Strhodes dns_difftuple_t *tuple = NULL; 346135446Strhodes isc_result_t result; 347135446Strhodes result = dns_difftuple_create(diff->mctx, op, 348135446Strhodes name, ttl, rdata, &tuple); 349135446Strhodes if (result != ISC_R_SUCCESS) 350135446Strhodes return (result); 351135446Strhodes return (do_one_tuple(&tuple, db, ver, diff)); 352135446Strhodes} 353135446Strhodes 354135446Strhodes/**************************************************************************/ 355135446Strhodes/* 356135446Strhodes * Callback-style iteration over rdatasets and rdatas. 357135446Strhodes * 358135446Strhodes * foreach_rrset() can be used to iterate over the RRsets 359135446Strhodes * of a name and call a callback function with each 360135446Strhodes * one. Similarly, foreach_rr() can be used to iterate 361135446Strhodes * over the individual RRs at name, optionally restricted 362135446Strhodes * to RRs of a given type. 363135446Strhodes * 364135446Strhodes * The callback functions are called "actions" and take 365135446Strhodes * two arguments: a void pointer for passing arbitrary 366135446Strhodes * context information, and a pointer to the current RRset 367135446Strhodes * or RR. By convention, their names end in "_action". 368135446Strhodes */ 369135446Strhodes 370135446Strhodes/* 371135446Strhodes * XXXRTH We might want to make this public somewhere in libdns. 372135446Strhodes */ 373135446Strhodes 374135446Strhodes/* 375135446Strhodes * Function type for foreach_rrset() iterator actions. 376135446Strhodes */ 377135446Strhodestypedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); 378135446Strhodes 379135446Strhodes/* 380135446Strhodes * Function type for foreach_rr() iterator actions. 381135446Strhodes */ 382135446Strhodestypedef isc_result_t rr_func(void *data, rr_t *rr); 383135446Strhodes 384135446Strhodes/* 385135446Strhodes * Internal context struct for foreach_node_rr(). 386135446Strhodes */ 387135446Strhodestypedef struct { 388135446Strhodes rr_func * rr_action; 389135446Strhodes void * rr_action_data; 390135446Strhodes} foreach_node_rr_ctx_t; 391135446Strhodes 392135446Strhodes/* 393135446Strhodes * Internal helper function for foreach_node_rr(). 394135446Strhodes */ 395135446Strhodesstatic isc_result_t 396135446Strhodesforeach_node_rr_action(void *data, dns_rdataset_t *rdataset) { 397135446Strhodes isc_result_t result; 398135446Strhodes foreach_node_rr_ctx_t *ctx = data; 399135446Strhodes for (result = dns_rdataset_first(rdataset); 400135446Strhodes result == ISC_R_SUCCESS; 401135446Strhodes result = dns_rdataset_next(rdataset)) 402135446Strhodes { 403135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 404135446Strhodes 405135446Strhodes dns_rdataset_current(rdataset, &rr.rdata); 406135446Strhodes rr.ttl = rdataset->ttl; 407135446Strhodes result = (*ctx->rr_action)(ctx->rr_action_data, &rr); 408135446Strhodes if (result != ISC_R_SUCCESS) 409135446Strhodes return (result); 410135446Strhodes } 411135446Strhodes if (result != ISC_R_NOMORE) 412135446Strhodes return (result); 413135446Strhodes return (ISC_R_SUCCESS); 414135446Strhodes} 415135446Strhodes 416135446Strhodes/* 417135446Strhodes * For each rdataset of 'name' in 'ver' of 'db', call 'action' 418135446Strhodes * with the rdataset and 'action_data' as arguments. If the name 419135446Strhodes * does not exist, do nothing. 420135446Strhodes * 421135446Strhodes * If 'action' returns an error, abort iteration and return the error. 422135446Strhodes */ 423135446Strhodesstatic isc_result_t 424135446Strhodesforeach_rrset(dns_db_t *db, 425135446Strhodes dns_dbversion_t *ver, 426135446Strhodes dns_name_t *name, 427135446Strhodes rrset_func *action, 428135446Strhodes void *action_data) 429135446Strhodes{ 430135446Strhodes isc_result_t result; 431135446Strhodes dns_dbnode_t *node; 432135446Strhodes dns_rdatasetiter_t *iter; 433135446Strhodes 434135446Strhodes node = NULL; 435135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 436135446Strhodes if (result == ISC_R_NOTFOUND) 437135446Strhodes return (ISC_R_SUCCESS); 438135446Strhodes if (result != ISC_R_SUCCESS) 439135446Strhodes return (result); 440135446Strhodes 441135446Strhodes iter = NULL; 442135446Strhodes result = dns_db_allrdatasets(db, node, ver, 443135446Strhodes (isc_stdtime_t) 0, &iter); 444135446Strhodes if (result != ISC_R_SUCCESS) 445135446Strhodes goto cleanup_node; 446135446Strhodes 447135446Strhodes for (result = dns_rdatasetiter_first(iter); 448135446Strhodes result == ISC_R_SUCCESS; 449135446Strhodes result = dns_rdatasetiter_next(iter)) 450135446Strhodes { 451135446Strhodes dns_rdataset_t rdataset; 452135446Strhodes 453135446Strhodes dns_rdataset_init(&rdataset); 454135446Strhodes dns_rdatasetiter_current(iter, &rdataset); 455135446Strhodes 456135446Strhodes result = (*action)(action_data, &rdataset); 457135446Strhodes 458135446Strhodes dns_rdataset_disassociate(&rdataset); 459135446Strhodes if (result != ISC_R_SUCCESS) 460135446Strhodes goto cleanup_iterator; 461135446Strhodes } 462135446Strhodes if (result == ISC_R_NOMORE) 463135446Strhodes result = ISC_R_SUCCESS; 464135446Strhodes 465135446Strhodes cleanup_iterator: 466135446Strhodes dns_rdatasetiter_destroy(&iter); 467135446Strhodes 468135446Strhodes cleanup_node: 469135446Strhodes dns_db_detachnode(db, &node); 470135446Strhodes 471135446Strhodes return (result); 472135446Strhodes} 473135446Strhodes 474135446Strhodes/* 475135446Strhodes * For each RR of 'name' in 'ver' of 'db', call 'action' 476135446Strhodes * with the RR and 'action_data' as arguments. If the name 477135446Strhodes * does not exist, do nothing. 478135446Strhodes * 479135446Strhodes * If 'action' returns an error, abort iteration 480135446Strhodes * and return the error. 481135446Strhodes */ 482135446Strhodesstatic isc_result_t 483135446Strhodesforeach_node_rr(dns_db_t *db, 484135446Strhodes dns_dbversion_t *ver, 485135446Strhodes dns_name_t *name, 486135446Strhodes rr_func *rr_action, 487135446Strhodes void *rr_action_data) 488135446Strhodes{ 489135446Strhodes foreach_node_rr_ctx_t ctx; 490135446Strhodes ctx.rr_action = rr_action; 491135446Strhodes ctx.rr_action_data = rr_action_data; 492135446Strhodes return (foreach_rrset(db, ver, name, 493135446Strhodes foreach_node_rr_action, &ctx)); 494135446Strhodes} 495135446Strhodes 496135446Strhodes 497135446Strhodes/* 498135446Strhodes * For each of the RRs specified by 'db', 'ver', 'name', 'type', 499135446Strhodes * (which can be dns_rdatatype_any to match any type), and 'covers', call 500135446Strhodes * 'action' with the RR and 'action_data' as arguments. If the name 501135446Strhodes * does not exist, or if no RRset of the given type exists at the name, 502135446Strhodes * do nothing. 503135446Strhodes * 504135446Strhodes * If 'action' returns an error, abort iteration and return the error. 505135446Strhodes */ 506135446Strhodesstatic isc_result_t 507135446Strhodesforeach_rr(dns_db_t *db, 508135446Strhodes dns_dbversion_t *ver, 509135446Strhodes dns_name_t *name, 510135446Strhodes dns_rdatatype_t type, 511135446Strhodes dns_rdatatype_t covers, 512135446Strhodes rr_func *rr_action, 513135446Strhodes void *rr_action_data) 514135446Strhodes{ 515135446Strhodes 516135446Strhodes isc_result_t result; 517135446Strhodes dns_dbnode_t *node; 518135446Strhodes dns_rdataset_t rdataset; 519135446Strhodes 520135446Strhodes if (type == dns_rdatatype_any) 521135446Strhodes return (foreach_node_rr(db, ver, name, 522135446Strhodes rr_action, rr_action_data)); 523135446Strhodes 524135446Strhodes node = NULL; 525135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 526135446Strhodes if (result == ISC_R_NOTFOUND) 527135446Strhodes return (ISC_R_SUCCESS); 528135446Strhodes if (result != ISC_R_SUCCESS) 529135446Strhodes return (result); 530135446Strhodes 531135446Strhodes dns_rdataset_init(&rdataset); 532135446Strhodes result = dns_db_findrdataset(db, node, ver, type, covers, 533135446Strhodes (isc_stdtime_t) 0, &rdataset, NULL); 534135446Strhodes if (result == ISC_R_NOTFOUND) { 535135446Strhodes result = ISC_R_SUCCESS; 536135446Strhodes goto cleanup_node; 537135446Strhodes } 538135446Strhodes if (result != ISC_R_SUCCESS) 539135446Strhodes goto cleanup_node; 540135446Strhodes 541135446Strhodes for (result = dns_rdataset_first(&rdataset); 542135446Strhodes result == ISC_R_SUCCESS; 543135446Strhodes result = dns_rdataset_next(&rdataset)) 544135446Strhodes { 545135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 546135446Strhodes dns_rdataset_current(&rdataset, &rr.rdata); 547135446Strhodes rr.ttl = rdataset.ttl; 548135446Strhodes result = (*rr_action)(rr_action_data, &rr); 549135446Strhodes if (result != ISC_R_SUCCESS) 550135446Strhodes goto cleanup_rdataset; 551135446Strhodes } 552135446Strhodes if (result != ISC_R_NOMORE) 553135446Strhodes goto cleanup_rdataset; 554135446Strhodes result = ISC_R_SUCCESS; 555135446Strhodes 556135446Strhodes cleanup_rdataset: 557135446Strhodes dns_rdataset_disassociate(&rdataset); 558135446Strhodes cleanup_node: 559135446Strhodes dns_db_detachnode(db, &node); 560135446Strhodes 561135446Strhodes return (result); 562135446Strhodes} 563135446Strhodes 564135446Strhodes/**************************************************************************/ 565135446Strhodes/* 566135446Strhodes * Various tests on the database contents (for prerequisites, etc). 567135446Strhodes */ 568135446Strhodes 569135446Strhodes/* 570135446Strhodes * Function type for predicate functions that compare a database RR 'db_rr' 571135446Strhodes * against an update RR 'update_rr'. 572135446Strhodes */ 573135446Strhodestypedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); 574135446Strhodes 575135446Strhodes/* 576135446Strhodes * Helper function for rrset_exists(). 577135446Strhodes */ 578135446Strhodesstatic isc_result_t 579135446Strhodesrrset_exists_action(void *data, rr_t *rr) { 580135446Strhodes UNUSED(data); 581135446Strhodes UNUSED(rr); 582135446Strhodes return (ISC_R_EXISTS); 583135446Strhodes} 584135446Strhodes 585135446Strhodes/* 586135446Strhodes * Utility macro for RR existence checking functions. 587135446Strhodes * 588135446Strhodes * If the variable 'result' has the value ISC_R_EXISTS or 589135446Strhodes * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, 590135446Strhodes * respectively, and return success. 591135446Strhodes * 592135446Strhodes * If 'result' has any other value, there was a failure. 593135446Strhodes * Return the failure result code and do not set *exists. 594135446Strhodes * 595135446Strhodes * This would be more readable as "do { if ... } while(0)", 596135446Strhodes * but that form generates tons of warnings on Solaris 2.6. 597135446Strhodes */ 598135446Strhodes#define RETURN_EXISTENCE_FLAG \ 599135446Strhodes return ((result == ISC_R_EXISTS) ? \ 600135446Strhodes (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ 601135446Strhodes ((result == ISC_R_SUCCESS) ? \ 602135446Strhodes (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ 603135446Strhodes result)) 604135446Strhodes 605135446Strhodes/* 606135446Strhodes * Set '*exists' to true iff an rrset of the given type exists, 607135446Strhodes * to false otherwise. 608135446Strhodes */ 609135446Strhodesstatic isc_result_t 610135446Strhodesrrset_exists(dns_db_t *db, dns_dbversion_t *ver, 611135446Strhodes dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, 612135446Strhodes isc_boolean_t *exists) 613135446Strhodes{ 614135446Strhodes isc_result_t result; 615135446Strhodes result = foreach_rr(db, ver, name, type, covers, 616135446Strhodes rrset_exists_action, NULL); 617135446Strhodes RETURN_EXISTENCE_FLAG; 618135446Strhodes} 619135446Strhodes 620135446Strhodes/* 621135446Strhodes * Helper function for cname_incompatible_rrset_exists. 622135446Strhodes */ 623135446Strhodesstatic isc_result_t 624135446Strhodescname_compatibility_action(void *data, dns_rdataset_t *rrset) { 625135446Strhodes UNUSED(data); 626135446Strhodes if (rrset->type != dns_rdatatype_cname && 627135446Strhodes ! dns_rdatatype_isdnssec(rrset->type)) 628135446Strhodes return (ISC_R_EXISTS); 629135446Strhodes return (ISC_R_SUCCESS); 630135446Strhodes} 631135446Strhodes 632135446Strhodes/* 633135446Strhodes * Check whether there is an rrset incompatible with adding a CNAME RR, 634135446Strhodes * i.e., anything but another CNAME (which can be replaced) or a 635135446Strhodes * DNSSEC RR (which can coexist). 636135446Strhodes * 637135446Strhodes * If such an incompatible rrset exists, set '*exists' to ISC_TRUE. 638135446Strhodes * Otherwise, set it to ISC_FALSE. 639135446Strhodes */ 640135446Strhodesstatic isc_result_t 641135446Strhodescname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, 642135446Strhodes dns_name_t *name, isc_boolean_t *exists) { 643135446Strhodes isc_result_t result; 644135446Strhodes result = foreach_rrset(db, ver, name, 645135446Strhodes cname_compatibility_action, NULL); 646135446Strhodes RETURN_EXISTENCE_FLAG; 647135446Strhodes} 648135446Strhodes 649135446Strhodes/* 650135446Strhodes * Helper function for rr_count(). 651135446Strhodes */ 652135446Strhodesstatic isc_result_t 653135446Strhodescount_rr_action(void *data, rr_t *rr) { 654135446Strhodes int *countp = data; 655135446Strhodes UNUSED(rr); 656135446Strhodes (*countp)++; 657135446Strhodes return (ISC_R_SUCCESS); 658135446Strhodes} 659135446Strhodes 660135446Strhodes/* 661135446Strhodes * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'. 662135446Strhodes */ 663135446Strhodesstatic isc_result_t 664135446Strhodesrr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 665135446Strhodes dns_rdatatype_t type, dns_rdatatype_t covers, int *countp) 666135446Strhodes{ 667135446Strhodes *countp = 0; 668135446Strhodes return (foreach_rr(db, ver, name, type, covers, 669135446Strhodes count_rr_action, countp)); 670135446Strhodes} 671135446Strhodes 672135446Strhodes/* 673135446Strhodes * Context struct and helper function for name_exists(). 674135446Strhodes */ 675135446Strhodes 676135446Strhodesstatic isc_result_t 677135446Strhodesname_exists_action(void *data, dns_rdataset_t *rrset) { 678135446Strhodes UNUSED(data); 679135446Strhodes UNUSED(rrset); 680135446Strhodes return (ISC_R_EXISTS); 681135446Strhodes} 682135446Strhodes 683135446Strhodes/* 684135446Strhodes * Set '*exists' to true iff the given name exists, to false otherwise. 685135446Strhodes */ 686135446Strhodesstatic isc_result_t 687135446Strhodesname_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 688135446Strhodes isc_boolean_t *exists) 689135446Strhodes{ 690135446Strhodes isc_result_t result; 691135446Strhodes result = foreach_rrset(db, ver, name, 692135446Strhodes name_exists_action, NULL); 693135446Strhodes RETURN_EXISTENCE_FLAG; 694135446Strhodes} 695135446Strhodes 696135446Strhodestypedef struct { 697135446Strhodes dns_name_t *name, *signer; 698135446Strhodes dns_ssutable_t *table; 699135446Strhodes} ssu_check_t; 700135446Strhodes 701135446Strhodesstatic isc_result_t 702135446Strhodesssu_checkrule(void *data, dns_rdataset_t *rrset) { 703135446Strhodes ssu_check_t *ssuinfo = data; 704135446Strhodes isc_boolean_t result; 705135446Strhodes 706135446Strhodes /* 707135446Strhodes * If we're deleting all records, it's ok to delete RRSIG and NSEC even 708135446Strhodes * if we're normally not allowed to. 709135446Strhodes */ 710135446Strhodes if (rrset->type == dns_rdatatype_rrsig || 711135446Strhodes rrset->type == dns_rdatatype_nsec) 712143731Sdougb return (ISC_R_SUCCESS); 713135446Strhodes result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer, 714135446Strhodes ssuinfo->name, rrset->type); 715135446Strhodes return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE); 716135446Strhodes} 717135446Strhodes 718135446Strhodesstatic isc_boolean_t 719135446Strhodesssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 720135446Strhodes dns_ssutable_t *ssutable, dns_name_t *signer) 721135446Strhodes{ 722135446Strhodes isc_result_t result; 723135446Strhodes ssu_check_t ssuinfo; 724135446Strhodes 725135446Strhodes ssuinfo.name = name; 726135446Strhodes ssuinfo.table = ssutable; 727135446Strhodes ssuinfo.signer = signer; 728135446Strhodes result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo); 729135446Strhodes return (ISC_TF(result == ISC_R_SUCCESS)); 730135446Strhodes} 731135446Strhodes 732135446Strhodes/**************************************************************************/ 733135446Strhodes/* 734135446Strhodes * Checking of "RRset exists (value dependent)" prerequisites. 735135446Strhodes * 736135446Strhodes * In the RFC2136 section 3.2.5, this is the pseudocode involving 737135446Strhodes * a variable called "temp", a mapping of <name, type> tuples to rrsets. 738135446Strhodes * 739135446Strhodes * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t" 740135446Strhodes * where each typle has op==DNS_DIFFOP_EXISTS. 741135446Strhodes */ 742135446Strhodes 743135446Strhodes 744135446Strhodes/* 745135446Strhodes * Append a tuple asserting the existence of the RR with 746135446Strhodes * 'name' and 'rdata' to 'diff'. 747135446Strhodes */ 748135446Strhodesstatic isc_result_t 749135446Strhodestemp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) { 750135446Strhodes isc_result_t result; 751135446Strhodes dns_difftuple_t *tuple = NULL; 752135446Strhodes 753135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 754135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, 755135446Strhodes name, 0, rdata, &tuple)); 756135446Strhodes ISC_LIST_APPEND(diff->tuples, tuple, link); 757135446Strhodes failure: 758135446Strhodes return (result); 759135446Strhodes} 760135446Strhodes 761135446Strhodes/* 762135446Strhodes * Compare two rdatasets represented as sorted lists of tuples. 763135446Strhodes * All list elements must have the same owner name and type. 764135446Strhodes * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset) 765135446Strhodes * if not. 766135446Strhodes */ 767135446Strhodesstatic isc_result_t 768135446Strhodestemp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) { 769135446Strhodes for (;;) { 770135446Strhodes if (a == NULL || b == NULL) 771135446Strhodes break; 772135446Strhodes INSIST(a->op == DNS_DIFFOP_EXISTS && 773135446Strhodes b->op == DNS_DIFFOP_EXISTS); 774135446Strhodes INSIST(a->rdata.type == b->rdata.type); 775135446Strhodes INSIST(dns_name_equal(&a->name, &b->name)); 776135446Strhodes if (dns_rdata_compare(&a->rdata, &b->rdata) != 0) 777135446Strhodes return (DNS_R_NXRRSET); 778135446Strhodes a = ISC_LIST_NEXT(a, link); 779135446Strhodes b = ISC_LIST_NEXT(b, link); 780135446Strhodes } 781135446Strhodes if (a != NULL || b != NULL) 782135446Strhodes return (DNS_R_NXRRSET); 783135446Strhodes return (ISC_R_SUCCESS); 784135446Strhodes} 785135446Strhodes 786135446Strhodes/* 787135446Strhodes * A comparison function defining the sorting order for the entries 788135446Strhodes * in the "temp" data structure. The major sort key is the owner name, 789135446Strhodes * followed by the type and rdata. 790135446Strhodes */ 791135446Strhodesstatic int 792135446Strhodestemp_order(const void *av, const void *bv) { 793135446Strhodes dns_difftuple_t const * const *ap = av; 794135446Strhodes dns_difftuple_t const * const *bp = bv; 795135446Strhodes dns_difftuple_t const *a = *ap; 796135446Strhodes dns_difftuple_t const *b = *bp; 797135446Strhodes int r; 798135446Strhodes r = dns_name_compare(&a->name, &b->name); 799135446Strhodes if (r != 0) 800135446Strhodes return (r); 801135446Strhodes r = (b->rdata.type - a->rdata.type); 802135446Strhodes if (r != 0) 803135446Strhodes return (r); 804135446Strhodes r = dns_rdata_compare(&a->rdata, &b->rdata); 805135446Strhodes return (r); 806135446Strhodes} 807135446Strhodes 808135446Strhodes/* 809135446Strhodes * Check the "RRset exists (value dependent)" prerequisite information 810135446Strhodes * in 'temp' against the contents of the database 'db'. 811135446Strhodes * 812135446Strhodes * Return ISC_R_SUCCESS if the prerequisites are satisfied, 813135446Strhodes * rcode(dns_rcode_nxrrset) if not. 814135446Strhodes * 815135446Strhodes * 'temp' must be pre-sorted. 816135446Strhodes */ 817135446Strhodes 818135446Strhodesstatic isc_result_t 819135446Strhodestemp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db, 820135446Strhodes dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep) 821135446Strhodes{ 822135446Strhodes isc_result_t result; 823135446Strhodes dns_name_t *name; 824135446Strhodes dns_dbnode_t *node; 825135446Strhodes dns_difftuple_t *t; 826135446Strhodes dns_diff_t trash; 827135446Strhodes 828135446Strhodes dns_diff_init(mctx, &trash); 829135446Strhodes 830135446Strhodes /* 831135446Strhodes * For each name and type in the prerequisites, 832135446Strhodes * construct a sorted rdata list of the corresponding 833135446Strhodes * database contents, and compare the lists. 834135446Strhodes */ 835135446Strhodes t = ISC_LIST_HEAD(temp->tuples); 836135446Strhodes while (t != NULL) { 837135446Strhodes name = &t->name; 838135446Strhodes (void)dns_name_copy(name, tmpname, NULL); 839135446Strhodes *typep = t->rdata.type; 840135446Strhodes 841135446Strhodes /* A new unique name begins here. */ 842135446Strhodes node = NULL; 843135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 844135446Strhodes if (result == ISC_R_NOTFOUND) 845135446Strhodes return (DNS_R_NXRRSET); 846135446Strhodes if (result != ISC_R_SUCCESS) 847135446Strhodes return (result); 848135446Strhodes 849135446Strhodes /* A new unique type begins here. */ 850135446Strhodes while (t != NULL && dns_name_equal(&t->name, name)) { 851135446Strhodes dns_rdatatype_t type, covers; 852135446Strhodes dns_rdataset_t rdataset; 853135446Strhodes dns_diff_t d_rrs; /* Database RRs with 854135446Strhodes this name and type */ 855135446Strhodes dns_diff_t u_rrs; /* Update RRs with 856135446Strhodes this name and type */ 857135446Strhodes 858135446Strhodes *typep = type = t->rdata.type; 859135446Strhodes if (type == dns_rdatatype_rrsig || 860135446Strhodes type == dns_rdatatype_sig) 861135446Strhodes covers = dns_rdata_covers(&t->rdata); 862135446Strhodes else 863135446Strhodes covers = 0; 864135446Strhodes 865135446Strhodes /* 866135446Strhodes * Collect all database RRs for this name and type 867135446Strhodes * onto d_rrs and sort them. 868135446Strhodes */ 869135446Strhodes dns_rdataset_init(&rdataset); 870135446Strhodes result = dns_db_findrdataset(db, node, ver, type, 871135446Strhodes covers, (isc_stdtime_t) 0, 872135446Strhodes &rdataset, NULL); 873135446Strhodes if (result != ISC_R_SUCCESS) { 874135446Strhodes dns_db_detachnode(db, &node); 875135446Strhodes return (DNS_R_NXRRSET); 876135446Strhodes } 877135446Strhodes 878135446Strhodes dns_diff_init(mctx, &d_rrs); 879135446Strhodes dns_diff_init(mctx, &u_rrs); 880135446Strhodes 881135446Strhodes for (result = dns_rdataset_first(&rdataset); 882135446Strhodes result == ISC_R_SUCCESS; 883135446Strhodes result = dns_rdataset_next(&rdataset)) 884135446Strhodes { 885135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 886135446Strhodes dns_rdataset_current(&rdataset, &rdata); 887135446Strhodes result = temp_append(&d_rrs, name, &rdata); 888135446Strhodes if (result != ISC_R_SUCCESS) 889135446Strhodes goto failure; 890135446Strhodes } 891135446Strhodes if (result != ISC_R_NOMORE) 892135446Strhodes goto failure; 893135446Strhodes result = dns_diff_sort(&d_rrs, temp_order); 894135446Strhodes if (result != ISC_R_SUCCESS) 895135446Strhodes goto failure; 896135446Strhodes 897135446Strhodes /* 898135446Strhodes * Collect all update RRs for this name and type 899135446Strhodes * onto u_rrs. No need to sort them here - 900135446Strhodes * they are already sorted. 901135446Strhodes */ 902135446Strhodes while (t != NULL && 903135446Strhodes dns_name_equal(&t->name, name) && 904135446Strhodes t->rdata.type == type) 905135446Strhodes { 906135446Strhodes dns_difftuple_t *next = 907135446Strhodes ISC_LIST_NEXT(t, link); 908135446Strhodes ISC_LIST_UNLINK(temp->tuples, t, link); 909135446Strhodes ISC_LIST_APPEND(u_rrs.tuples, t, link); 910135446Strhodes t = next; 911135446Strhodes } 912135446Strhodes 913135446Strhodes /* Compare the two sorted lists. */ 914135446Strhodes result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples), 915135446Strhodes ISC_LIST_HEAD(d_rrs.tuples)); 916135446Strhodes if (result != ISC_R_SUCCESS) 917135446Strhodes goto failure; 918135446Strhodes 919135446Strhodes /* 920135446Strhodes * We are done with the tuples, but we can't free 921135446Strhodes * them yet because "name" still points into one 922135446Strhodes * of them. Move them on a temporary list. 923135446Strhodes */ 924135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link); 925135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link); 926135446Strhodes dns_rdataset_disassociate(&rdataset); 927135446Strhodes 928135446Strhodes continue; 929135446Strhodes 930135446Strhodes failure: 931135446Strhodes dns_diff_clear(&d_rrs); 932135446Strhodes dns_diff_clear(&u_rrs); 933135446Strhodes dns_diff_clear(&trash); 934135446Strhodes dns_rdataset_disassociate(&rdataset); 935135446Strhodes dns_db_detachnode(db, &node); 936135446Strhodes return (result); 937135446Strhodes } 938135446Strhodes 939135446Strhodes dns_db_detachnode(db, &node); 940135446Strhodes } 941135446Strhodes 942135446Strhodes dns_diff_clear(&trash); 943135446Strhodes return (ISC_R_SUCCESS); 944135446Strhodes} 945135446Strhodes 946135446Strhodes/**************************************************************************/ 947135446Strhodes/* 948135446Strhodes * Conditional deletion of RRs. 949135446Strhodes */ 950135446Strhodes 951135446Strhodes/* 952135446Strhodes * Context structure for delete_if(). 953135446Strhodes */ 954135446Strhodes 955135446Strhodestypedef struct { 956135446Strhodes rr_predicate *predicate; 957135446Strhodes dns_db_t *db; 958135446Strhodes dns_dbversion_t *ver; 959135446Strhodes dns_diff_t *diff; 960135446Strhodes dns_name_t *name; 961135446Strhodes dns_rdata_t *update_rr; 962135446Strhodes} conditional_delete_ctx_t; 963135446Strhodes 964135446Strhodes/* 965135446Strhodes * Predicate functions for delete_if(). 966135446Strhodes */ 967135446Strhodes 968135446Strhodes/* 969143731Sdougb * Return true iff 'db_rr' is neither a SOA nor an NS RR nor 970143731Sdougb * an RRSIG nor a NSEC. 971135446Strhodes */ 972135446Strhodesstatic isc_boolean_t 973135446Strhodestype_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 974135446Strhodes UNUSED(update_rr); 975135446Strhodes return ((db_rr->type != dns_rdatatype_soa && 976143731Sdougb db_rr->type != dns_rdatatype_ns && 977143731Sdougb db_rr->type != dns_rdatatype_rrsig && 978143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 979135446Strhodes ISC_TRUE : ISC_FALSE); 980135446Strhodes} 981135446Strhodes 982135446Strhodes/* 983143731Sdougb * Return true iff 'db_rr' is neither a RRSIG nor a NSEC. 984143731Sdougb */ 985143731Sdougbstatic isc_boolean_t 986143731Sdougbtype_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 987143731Sdougb UNUSED(update_rr); 988143731Sdougb return ((db_rr->type != dns_rdatatype_rrsig && 989143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 990143731Sdougb ISC_TRUE : ISC_FALSE); 991143731Sdougb} 992143731Sdougb 993143731Sdougb/* 994135446Strhodes * Return true always. 995135446Strhodes */ 996135446Strhodesstatic isc_boolean_t 997135446Strhodestrue_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 998135446Strhodes UNUSED(update_rr); 999135446Strhodes UNUSED(db_rr); 1000135446Strhodes return (ISC_TRUE); 1001135446Strhodes} 1002135446Strhodes 1003135446Strhodes/* 1004135446Strhodes * Return true iff the two RRs have identical rdata. 1005135446Strhodes */ 1006135446Strhodesstatic isc_boolean_t 1007135446Strhodesrr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1008135446Strhodes /* 1009135446Strhodes * XXXRTH This is not a problem, but we should consider creating 1010135446Strhodes * dns_rdata_equal() (that used dns_name_equal()), since it 1011135446Strhodes * would be faster. Not a priority. 1012135446Strhodes */ 1013135446Strhodes return (dns_rdata_compare(update_rr, db_rr) == 0 ? 1014135446Strhodes ISC_TRUE : ISC_FALSE); 1015135446Strhodes} 1016135446Strhodes 1017135446Strhodes/* 1018135446Strhodes * Return true iff 'update_rr' should replace 'db_rr' according 1019135446Strhodes * to the special RFC2136 rules for CNAME, SOA, and WKS records. 1020135446Strhodes * 1021135446Strhodes * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs 1022135446Strhodes * make little sense, so we replace those, too. 1023135446Strhodes */ 1024135446Strhodesstatic isc_boolean_t 1025135446Strhodesreplaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1026135446Strhodes if (db_rr->type != update_rr->type) 1027135446Strhodes return (ISC_FALSE); 1028135446Strhodes if (db_rr->type == dns_rdatatype_cname) 1029135446Strhodes return (ISC_TRUE); 1030135446Strhodes if (db_rr->type == dns_rdatatype_dname) 1031135446Strhodes return (ISC_TRUE); 1032135446Strhodes if (db_rr->type == dns_rdatatype_soa) 1033135446Strhodes return (ISC_TRUE); 1034135446Strhodes if (db_rr->type == dns_rdatatype_nsec) 1035135446Strhodes return (ISC_TRUE); 1036135446Strhodes if (db_rr->type == dns_rdatatype_wks) { 1037135446Strhodes /* 1038135446Strhodes * Compare the address and protocol fields only. These 1039135446Strhodes * form the first five bytes of the RR data. Do a 1040135446Strhodes * raw binary comparison; unpacking the WKS RRs using 1041135446Strhodes * dns_rdata_tostruct() might be cleaner in some ways, 1042135446Strhodes * but it would require us to pass around an mctx. 1043135446Strhodes */ 1044135446Strhodes INSIST(db_rr->length >= 5 && update_rr->length >= 5); 1045135446Strhodes return (memcmp(db_rr->data, update_rr->data, 5) == 0 ? 1046135446Strhodes ISC_TRUE : ISC_FALSE); 1047135446Strhodes } 1048135446Strhodes return (ISC_FALSE); 1049135446Strhodes} 1050135446Strhodes 1051135446Strhodes/* 1052135446Strhodes * Internal helper function for delete_if(). 1053135446Strhodes */ 1054135446Strhodesstatic isc_result_t 1055135446Strhodesdelete_if_action(void *data, rr_t *rr) { 1056135446Strhodes conditional_delete_ctx_t *ctx = data; 1057135446Strhodes if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { 1058135446Strhodes isc_result_t result; 1059135446Strhodes result = update_one_rr(ctx->db, ctx->ver, ctx->diff, 1060135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1061135446Strhodes rr->ttl, &rr->rdata); 1062135446Strhodes return (result); 1063135446Strhodes } else { 1064135446Strhodes return (ISC_R_SUCCESS); 1065135446Strhodes } 1066135446Strhodes} 1067135446Strhodes 1068135446Strhodes/* 1069135446Strhodes * Conditionally delete RRs. Apply 'predicate' to the RRs 1070135446Strhodes * specified by 'db', 'ver', 'name', and 'type' (which can 1071135446Strhodes * be dns_rdatatype_any to match any type). Delete those 1072135446Strhodes * RRs for which the predicate returns true, and log the 1073135446Strhodes * deletions in 'diff'. 1074135446Strhodes */ 1075135446Strhodesstatic isc_result_t 1076135446Strhodesdelete_if(rr_predicate *predicate, 1077135446Strhodes dns_db_t *db, 1078135446Strhodes dns_dbversion_t *ver, 1079135446Strhodes dns_name_t *name, 1080135446Strhodes dns_rdatatype_t type, 1081135446Strhodes dns_rdatatype_t covers, 1082135446Strhodes dns_rdata_t *update_rr, 1083135446Strhodes dns_diff_t *diff) 1084135446Strhodes{ 1085135446Strhodes conditional_delete_ctx_t ctx; 1086135446Strhodes ctx.predicate = predicate; 1087135446Strhodes ctx.db = db; 1088135446Strhodes ctx.ver = ver; 1089135446Strhodes ctx.diff = diff; 1090135446Strhodes ctx.name = name; 1091135446Strhodes ctx.update_rr = update_rr; 1092135446Strhodes return (foreach_rr(db, ver, name, type, covers, 1093135446Strhodes delete_if_action, &ctx)); 1094135446Strhodes} 1095135446Strhodes 1096135446Strhodes/**************************************************************************/ 1097135446Strhodes/* 1098135446Strhodes * Prepare an RR for the addition of the new RR 'ctx->update_rr', 1099135446Strhodes * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting 1100135446Strhodes * the RRs if it is replaced by the new RR or has a conflicting TTL. 1101135446Strhodes * The necessary changes are appended to ctx->del_diff and ctx->add_diff; 1102135446Strhodes * we need to do all deletions before any additions so that we don't run 1103135446Strhodes * into transient states with conflicting TTLs. 1104135446Strhodes */ 1105135446Strhodes 1106135446Strhodestypedef struct { 1107135446Strhodes dns_db_t *db; 1108135446Strhodes dns_dbversion_t *ver; 1109135446Strhodes dns_diff_t *diff; 1110135446Strhodes dns_name_t *name; 1111135446Strhodes dns_rdata_t *update_rr; 1112135446Strhodes dns_ttl_t update_rr_ttl; 1113135446Strhodes isc_boolean_t ignore_add; 1114135446Strhodes dns_diff_t del_diff; 1115135446Strhodes dns_diff_t add_diff; 1116135446Strhodes} add_rr_prepare_ctx_t; 1117135446Strhodes 1118135446Strhodesstatic isc_result_t 1119135446Strhodesadd_rr_prepare_action(void *data, rr_t *rr) { 1120135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1121135446Strhodes add_rr_prepare_ctx_t *ctx = data; 1122135446Strhodes dns_difftuple_t *tuple = NULL; 1123135446Strhodes isc_boolean_t equal; 1124135446Strhodes 1125135446Strhodes /* 1126135446Strhodes * If the update RR is a "duplicate" of the update RR, 1127135446Strhodes * the update should be silently ignored. 1128135446Strhodes */ 1129135446Strhodes equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0); 1130135446Strhodes if (equal && rr->ttl == ctx->update_rr_ttl) { 1131135446Strhodes ctx->ignore_add = ISC_TRUE; 1132135446Strhodes return (ISC_R_SUCCESS); 1133135446Strhodes } 1134135446Strhodes 1135135446Strhodes /* 1136135446Strhodes * If this RR is "equal" to the update RR, it should 1137135446Strhodes * be deleted before the update RR is added. 1138135446Strhodes */ 1139135446Strhodes if (replaces_p(ctx->update_rr, &rr->rdata)) { 1140135446Strhodes CHECK(dns_difftuple_create(ctx->del_diff.mctx, 1141135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1142135446Strhodes rr->ttl, 1143135446Strhodes &rr->rdata, 1144135446Strhodes &tuple)); 1145135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1146135446Strhodes return (ISC_R_SUCCESS); 1147135446Strhodes } 1148135446Strhodes 1149135446Strhodes /* 1150135446Strhodes * If this RR differs in TTL from the update RR, 1151135446Strhodes * its TTL must be adjusted. 1152135446Strhodes */ 1153135446Strhodes if (rr->ttl != ctx->update_rr_ttl) { 1154135446Strhodes CHECK(dns_difftuple_create(ctx->del_diff.mctx, 1155135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1156135446Strhodes rr->ttl, 1157135446Strhodes &rr->rdata, 1158135446Strhodes &tuple)); 1159135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1160135446Strhodes if (!equal) { 1161135446Strhodes CHECK(dns_difftuple_create(ctx->add_diff.mctx, 1162135446Strhodes DNS_DIFFOP_ADD, ctx->name, 1163135446Strhodes ctx->update_rr_ttl, 1164135446Strhodes &rr->rdata, 1165135446Strhodes &tuple)); 1166135446Strhodes dns_diff_append(&ctx->add_diff, &tuple); 1167135446Strhodes } 1168135446Strhodes } 1169135446Strhodes failure: 1170135446Strhodes return (result); 1171135446Strhodes} 1172135446Strhodes 1173135446Strhodes/**************************************************************************/ 1174135446Strhodes/* 1175135446Strhodes * Miscellaneous subroutines. 1176135446Strhodes */ 1177135446Strhodes 1178135446Strhodes/* 1179135446Strhodes * Extract a single update RR from 'section' of dynamic update message 1180135446Strhodes * 'msg', with consistency checking. 1181135446Strhodes * 1182135446Strhodes * Stores the owner name, rdata, and TTL of the update RR at 'name', 1183135446Strhodes * 'rdata', and 'ttl', respectively. 1184135446Strhodes */ 1185135446Strhodesstatic void 1186135446Strhodesget_current_rr(dns_message_t *msg, dns_section_t section, 1187135446Strhodes dns_rdataclass_t zoneclass, 1188135446Strhodes dns_name_t **name, dns_rdata_t *rdata, dns_rdatatype_t *covers, 1189135446Strhodes dns_ttl_t *ttl, 1190135446Strhodes dns_rdataclass_t *update_class) 1191135446Strhodes{ 1192135446Strhodes dns_rdataset_t *rdataset; 1193135446Strhodes isc_result_t result; 1194135446Strhodes dns_message_currentname(msg, section, name); 1195135446Strhodes rdataset = ISC_LIST_HEAD((*name)->list); 1196135446Strhodes INSIST(rdataset != NULL); 1197135446Strhodes INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); 1198135446Strhodes *covers = rdataset->covers; 1199135446Strhodes *ttl = rdataset->ttl; 1200135446Strhodes result = dns_rdataset_first(rdataset); 1201135446Strhodes INSIST(result == ISC_R_SUCCESS); 1202135446Strhodes dns_rdataset_current(rdataset, rdata); 1203135446Strhodes INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); 1204135446Strhodes *update_class = rdata->rdclass; 1205135446Strhodes rdata->rdclass = zoneclass; 1206135446Strhodes} 1207135446Strhodes 1208135446Strhodes/* 1209135446Strhodes * Increment the SOA serial number of database 'db', version 'ver'. 1210135446Strhodes * Replace the SOA record in the database, and log the 1211135446Strhodes * change in 'diff'. 1212135446Strhodes */ 1213135446Strhodes 1214135446Strhodes /* 1215135446Strhodes * XXXRTH Failures in this routine will be worth logging, when 1216135446Strhodes * we have a logging system. Failure to find the zonename 1217135446Strhodes * or the SOA rdataset warrant at least an UNEXPECTED_ERROR(). 1218135446Strhodes */ 1219135446Strhodes 1220135446Strhodesstatic isc_result_t 1221135446Strhodesincrement_soa_serial(dns_db_t *db, dns_dbversion_t *ver, 1222135446Strhodes dns_diff_t *diff, isc_mem_t *mctx) 1223135446Strhodes{ 1224135446Strhodes dns_difftuple_t *deltuple = NULL; 1225135446Strhodes dns_difftuple_t *addtuple = NULL; 1226135446Strhodes isc_uint32_t serial; 1227135446Strhodes isc_result_t result; 1228135446Strhodes 1229135446Strhodes CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); 1230135446Strhodes CHECK(dns_difftuple_copy(deltuple, &addtuple)); 1231135446Strhodes addtuple->op = DNS_DIFFOP_ADD; 1232135446Strhodes 1233135446Strhodes serial = dns_soa_getserial(&addtuple->rdata); 1234135446Strhodes 1235135446Strhodes /* RFC1982 */ 1236135446Strhodes serial = (serial + 1) & 0xFFFFFFFF; 1237135446Strhodes if (serial == 0) 1238135446Strhodes serial = 1; 1239135446Strhodes 1240135446Strhodes dns_soa_setserial(serial, &addtuple->rdata); 1241135446Strhodes CHECK(do_one_tuple(&deltuple, db, ver, diff)); 1242135446Strhodes CHECK(do_one_tuple(&addtuple, db, ver, diff)); 1243135446Strhodes result = ISC_R_SUCCESS; 1244135446Strhodes 1245135446Strhodes failure: 1246135446Strhodes if (addtuple != NULL) 1247135446Strhodes dns_difftuple_free(&addtuple); 1248135446Strhodes if (deltuple != NULL) 1249135446Strhodes dns_difftuple_free(&deltuple); 1250135446Strhodes return (result); 1251135446Strhodes} 1252135446Strhodes 1253135446Strhodes/* 1254135446Strhodes * Check that the new SOA record at 'update_rdata' does not 1255135446Strhodes * illegally cause the SOA serial number to decrease or stay 1256135446Strhodes * unchanged relative to the existing SOA in 'db'. 1257135446Strhodes * 1258135446Strhodes * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not. 1259135446Strhodes * 1260135446Strhodes * William King points out that RFC2136 is inconsistent about 1261135446Strhodes * the case where the serial number stays unchanged: 1262135446Strhodes * 1263135446Strhodes * section 3.4.2.2 requires a server to ignore a SOA update request 1264135446Strhodes * if the serial number on the update SOA is less_than_or_equal to 1265135446Strhodes * the zone SOA serial. 1266135446Strhodes * 1267135446Strhodes * section 3.6 requires a server to ignore a SOA update request if 1268135446Strhodes * the serial is less_than the zone SOA serial. 1269135446Strhodes * 1270135446Strhodes * Paul says 3.4.2.2 is correct. 1271135446Strhodes * 1272135446Strhodes */ 1273135446Strhodesstatic isc_result_t 1274135446Strhodescheck_soa_increment(dns_db_t *db, dns_dbversion_t *ver, 1275135446Strhodes dns_rdata_t *update_rdata, 1276135446Strhodes isc_boolean_t *ok) 1277135446Strhodes{ 1278135446Strhodes isc_uint32_t db_serial; 1279135446Strhodes isc_uint32_t update_serial; 1280135446Strhodes isc_result_t result; 1281135446Strhodes 1282135446Strhodes update_serial = dns_soa_getserial(update_rdata); 1283135446Strhodes 1284135446Strhodes result = dns_db_getsoaserial(db, ver, &db_serial); 1285135446Strhodes if (result != ISC_R_SUCCESS) 1286135446Strhodes return (result); 1287135446Strhodes 1288135446Strhodes if (DNS_SERIAL_GE(db_serial, update_serial)) { 1289135446Strhodes *ok = ISC_FALSE; 1290135446Strhodes } else { 1291135446Strhodes *ok = ISC_TRUE; 1292135446Strhodes } 1293135446Strhodes 1294135446Strhodes return (ISC_R_SUCCESS); 1295135446Strhodes 1296135446Strhodes} 1297135446Strhodes 1298135446Strhodes/**************************************************************************/ 1299135446Strhodes/* 1300135446Strhodes * Incremental updating of NSECs and RRSIGs. 1301135446Strhodes */ 1302135446Strhodes 1303135446Strhodes#define MAXZONEKEYS 32 /* Maximum number of zone keys supported. */ 1304135446Strhodes 1305135446Strhodes/* 1306135446Strhodes * We abuse the dns_diff_t type to represent a set of domain names 1307135446Strhodes * affected by the update. 1308135446Strhodes */ 1309135446Strhodesstatic isc_result_t 1310135446Strhodesnamelist_append_name(dns_diff_t *list, dns_name_t *name) { 1311135446Strhodes isc_result_t result; 1312135446Strhodes dns_difftuple_t *tuple = NULL; 1313135446Strhodes static dns_rdata_t dummy_rdata = { NULL, 0, 0, 0, 0, 1314135446Strhodes { (void*)(-1), (void*)(-1) } }; 1315135446Strhodes CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, 1316135446Strhodes &dummy_rdata, &tuple)); 1317135446Strhodes dns_diff_append(list, &tuple); 1318135446Strhodes failure: 1319135446Strhodes return (result); 1320135446Strhodes} 1321135446Strhodes 1322135446Strhodesstatic isc_result_t 1323135446Strhodesnamelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) 1324135446Strhodes{ 1325135446Strhodes isc_result_t result; 1326135446Strhodes dns_fixedname_t fixedname; 1327135446Strhodes dns_name_t *child; 1328135446Strhodes dns_dbiterator_t *dbit = NULL; 1329135446Strhodes 1330135446Strhodes dns_fixedname_init(&fixedname); 1331135446Strhodes child = dns_fixedname_name(&fixedname); 1332135446Strhodes 1333135446Strhodes CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); 1334135446Strhodes 1335135446Strhodes for (result = dns_dbiterator_seek(dbit, name); 1336135446Strhodes result == ISC_R_SUCCESS; 1337135446Strhodes result = dns_dbiterator_next(dbit)) 1338135446Strhodes { 1339135446Strhodes dns_dbnode_t *node = NULL; 1340135446Strhodes CHECK(dns_dbiterator_current(dbit, &node, child)); 1341135446Strhodes dns_db_detachnode(db, &node); 1342135446Strhodes if (! dns_name_issubdomain(child, name)) 1343135446Strhodes break; 1344135446Strhodes CHECK(namelist_append_name(affected, child)); 1345135446Strhodes } 1346135446Strhodes if (result == ISC_R_NOMORE) 1347135446Strhodes result = ISC_R_SUCCESS; 1348135446Strhodes failure: 1349135446Strhodes if (dbit != NULL) 1350135446Strhodes dns_dbiterator_destroy(&dbit); 1351135446Strhodes return (result); 1352135446Strhodes} 1353135446Strhodes 1354135446Strhodes 1355135446Strhodes 1356135446Strhodes/* 1357135446Strhodes * Helper function for non_nsec_rrset_exists(). 1358135446Strhodes */ 1359135446Strhodesstatic isc_result_t 1360135446Strhodesis_non_nsec_action(void *data, dns_rdataset_t *rrset) { 1361135446Strhodes UNUSED(data); 1362135446Strhodes if (!(rrset->type == dns_rdatatype_nsec || 1363135446Strhodes (rrset->type == dns_rdatatype_rrsig && 1364135446Strhodes rrset->covers == dns_rdatatype_nsec))) 1365135446Strhodes return (ISC_R_EXISTS); 1366135446Strhodes return (ISC_R_SUCCESS); 1367135446Strhodes} 1368135446Strhodes 1369135446Strhodes/* 1370135446Strhodes * Check whether there is an rrset other than a NSEC or RRSIG NSEC, 1371135446Strhodes * i.e., anything that justifies the continued existence of a name 1372135446Strhodes * after a secure update. 1373135446Strhodes * 1374135446Strhodes * If such an rrset exists, set '*exists' to ISC_TRUE. 1375135446Strhodes * Otherwise, set it to ISC_FALSE. 1376135446Strhodes */ 1377135446Strhodesstatic isc_result_t 1378135446Strhodesnon_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, 1379135446Strhodes dns_name_t *name, isc_boolean_t *exists) 1380135446Strhodes{ 1381135446Strhodes isc_result_t result; 1382135446Strhodes result = foreach_rrset(db, ver, name, 1383135446Strhodes is_non_nsec_action, NULL); 1384135446Strhodes RETURN_EXISTENCE_FLAG; 1385135446Strhodes} 1386135446Strhodes 1387135446Strhodes/* 1388135446Strhodes * A comparison function for sorting dns_diff_t:s by name. 1389135446Strhodes */ 1390135446Strhodesstatic int 1391135446Strhodesname_order(const void *av, const void *bv) { 1392135446Strhodes dns_difftuple_t const * const *ap = av; 1393135446Strhodes dns_difftuple_t const * const *bp = bv; 1394135446Strhodes dns_difftuple_t const *a = *ap; 1395135446Strhodes dns_difftuple_t const *b = *bp; 1396135446Strhodes return (dns_name_compare(&a->name, &b->name)); 1397135446Strhodes} 1398135446Strhodes 1399135446Strhodesstatic isc_result_t 1400135446Strhodesuniqify_name_list(dns_diff_t *list) { 1401135446Strhodes isc_result_t result; 1402135446Strhodes dns_difftuple_t *p, *q; 1403135446Strhodes 1404135446Strhodes CHECK(dns_diff_sort(list, name_order)); 1405135446Strhodes 1406135446Strhodes p = ISC_LIST_HEAD(list->tuples); 1407135446Strhodes while (p != NULL) { 1408135446Strhodes do { 1409135446Strhodes q = ISC_LIST_NEXT(p, link); 1410135446Strhodes if (q == NULL || ! dns_name_equal(&p->name, &q->name)) 1411135446Strhodes break; 1412135446Strhodes ISC_LIST_UNLINK(list->tuples, q, link); 1413135446Strhodes dns_difftuple_free(&q); 1414135446Strhodes } while (1); 1415135446Strhodes p = ISC_LIST_NEXT(p, link); 1416135446Strhodes } 1417135446Strhodes failure: 1418135446Strhodes return (result); 1419135446Strhodes} 1420135446Strhodes 1421135446Strhodes 1422135446Strhodesstatic isc_result_t 1423135446Strhodesis_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1424135446Strhodes isc_boolean_t *flag) 1425135446Strhodes{ 1426135446Strhodes isc_result_t result; 1427135446Strhodes dns_fixedname_t foundname; 1428135446Strhodes dns_fixedname_init(&foundname); 1429135446Strhodes result = dns_db_find(db, name, ver, dns_rdatatype_any, 1430135446Strhodes DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, 1431135446Strhodes (isc_stdtime_t) 0, NULL, 1432135446Strhodes dns_fixedname_name(&foundname), 1433135446Strhodes NULL, NULL); 1434135446Strhodes if (result == ISC_R_SUCCESS) { 1435135446Strhodes *flag = ISC_FALSE; 1436135446Strhodes return (ISC_R_SUCCESS); 1437135446Strhodes } else if (result == DNS_R_ZONECUT) { 1438135446Strhodes /* 1439135446Strhodes * We are at the zonecut. The name will have an NSEC, but 1440135446Strhodes * non-delegation will be omitted from the type bit map. 1441135446Strhodes */ 1442135446Strhodes *flag = ISC_FALSE; 1443135446Strhodes return (ISC_R_SUCCESS); 1444135446Strhodes } else if (result == DNS_R_GLUE || result == DNS_R_DNAME) { 1445135446Strhodes *flag = ISC_TRUE; 1446135446Strhodes return (ISC_R_SUCCESS); 1447135446Strhodes } else { 1448135446Strhodes return (result); 1449135446Strhodes } 1450135446Strhodes} 1451135446Strhodes 1452135446Strhodes/* 1453135446Strhodes * Find the next/previous name that has a NSEC record. 1454135446Strhodes * In other words, skip empty database nodes and names that 1455135446Strhodes * have had their NSECs removed because they are obscured by 1456135446Strhodes * a zone cut. 1457135446Strhodes */ 1458135446Strhodesstatic isc_result_t 1459135446Strhodesnext_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1460135446Strhodes dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, 1461135446Strhodes isc_boolean_t forward) 1462135446Strhodes{ 1463135446Strhodes isc_result_t result; 1464135446Strhodes dns_dbiterator_t *dbit = NULL; 1465135446Strhodes isc_boolean_t has_nsec; 1466135446Strhodes unsigned int wraps = 0; 1467135446Strhodes 1468135446Strhodes CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); 1469135446Strhodes 1470135446Strhodes CHECK(dns_dbiterator_seek(dbit, oldname)); 1471135446Strhodes do { 1472135446Strhodes dns_dbnode_t *node = NULL; 1473135446Strhodes 1474135446Strhodes if (forward) 1475135446Strhodes result = dns_dbiterator_next(dbit); 1476135446Strhodes else 1477135446Strhodes result = dns_dbiterator_prev(dbit); 1478135446Strhodes if (result == ISC_R_NOMORE) { 1479135446Strhodes /* 1480135446Strhodes * Wrap around. 1481135446Strhodes */ 1482135446Strhodes if (forward) 1483135446Strhodes CHECK(dns_dbiterator_first(dbit)); 1484135446Strhodes else 1485135446Strhodes CHECK(dns_dbiterator_last(dbit)); 1486135446Strhodes wraps++; 1487135446Strhodes if (wraps == 2) { 1488135446Strhodes update_log(client, zone, ISC_LOG_ERROR, 1489135446Strhodes "secure zone with no NSECs"); 1490135446Strhodes result = DNS_R_BADZONE; 1491135446Strhodes goto failure; 1492135446Strhodes } 1493135446Strhodes } 1494135446Strhodes CHECK(dns_dbiterator_current(dbit, &node, newname)); 1495135446Strhodes dns_db_detachnode(db, &node); 1496135446Strhodes 1497135446Strhodes /* 1498135446Strhodes * The iterator may hold the tree lock, and 1499135446Strhodes * rrset_exists() calls dns_db_findnode() which 1500135446Strhodes * may try to reacquire it. To avoid deadlock 1501135446Strhodes * we must pause the iterator first. 1502135446Strhodes */ 1503135446Strhodes CHECK(dns_dbiterator_pause(dbit)); 1504135446Strhodes CHECK(rrset_exists(db, ver, newname, 1505135446Strhodes dns_rdatatype_nsec, 0, &has_nsec)); 1506135446Strhodes 1507135446Strhodes } while (! has_nsec); 1508135446Strhodes failure: 1509135446Strhodes if (dbit != NULL) 1510135446Strhodes dns_dbiterator_destroy(&dbit); 1511135446Strhodes 1512135446Strhodes return (result); 1513135446Strhodes} 1514135446Strhodes 1515135446Strhodes/* 1516135446Strhodes * Add a NSEC record for "name", recording the change in "diff". 1517135446Strhodes * The existing NSEC is removed. 1518135446Strhodes */ 1519135446Strhodesstatic isc_result_t 1520135446Strhodesadd_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1521165071Sdougb dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, 1522165071Sdougb dns_diff_t *diff) 1523135446Strhodes{ 1524135446Strhodes isc_result_t result; 1525135446Strhodes dns_dbnode_t *node = NULL; 1526135446Strhodes unsigned char buffer[DNS_NSEC_BUFFERSIZE]; 1527135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1528135446Strhodes dns_difftuple_t *tuple = NULL; 1529135446Strhodes dns_fixedname_t fixedname; 1530135446Strhodes dns_name_t *target; 1531135446Strhodes 1532135446Strhodes dns_fixedname_init(&fixedname); 1533135446Strhodes target = dns_fixedname_name(&fixedname); 1534135446Strhodes 1535135446Strhodes /* 1536135446Strhodes * Find the successor name, aka NSEC target. 1537135446Strhodes */ 1538135446Strhodes CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE)); 1539135446Strhodes 1540135446Strhodes /* 1541135446Strhodes * Create the NSEC RDATA. 1542135446Strhodes */ 1543135446Strhodes CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); 1544135446Strhodes dns_rdata_init(&rdata); 1545135446Strhodes CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); 1546135446Strhodes dns_db_detachnode(db, &node); 1547135446Strhodes 1548135446Strhodes /* 1549135446Strhodes * Delete the old NSEC and record the change. 1550135446Strhodes */ 1551135446Strhodes CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, 1552135446Strhodes NULL, diff)); 1553135446Strhodes /* 1554135446Strhodes * Add the new NSEC and record the change. 1555135446Strhodes */ 1556135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 1557165071Sdougb nsecttl, &rdata, &tuple)); 1558135446Strhodes CHECK(do_one_tuple(&tuple, db, ver, diff)); 1559135446Strhodes INSIST(tuple == NULL); 1560135446Strhodes 1561135446Strhodes failure: 1562135446Strhodes if (node != NULL) 1563135446Strhodes dns_db_detachnode(db, &node); 1564135446Strhodes return (result); 1565135446Strhodes} 1566135446Strhodes 1567135446Strhodes/* 1568135446Strhodes * Add a placeholder NSEC record for "name", recording the change in "diff". 1569135446Strhodes */ 1570135446Strhodesstatic isc_result_t 1571135446Strhodesadd_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1572135446Strhodes dns_diff_t *diff) { 1573135446Strhodes isc_result_t result; 1574135446Strhodes dns_difftuple_t *tuple = NULL; 1575135446Strhodes isc_region_t r; 1576135446Strhodes unsigned char data[1] = { 0 }; /* The root domain, no bits. */ 1577135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1578135446Strhodes 1579135446Strhodes r.base = data; 1580135446Strhodes r.length = sizeof(data); 1581135446Strhodes dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); 1582135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, 1583135446Strhodes &rdata, &tuple)); 1584135446Strhodes CHECK(do_one_tuple(&tuple, db, ver, diff)); 1585135446Strhodes failure: 1586135446Strhodes return (result); 1587135446Strhodes} 1588135446Strhodes 1589135446Strhodesstatic isc_result_t 1590135446Strhodesfind_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, 1591135446Strhodes isc_mem_t *mctx, unsigned int maxkeys, 1592135446Strhodes dst_key_t **keys, unsigned int *nkeys) 1593135446Strhodes{ 1594135446Strhodes isc_result_t result; 1595135446Strhodes dns_dbnode_t *node = NULL; 1596135446Strhodes const char *directory = dns_zone_getkeydirectory(zone); 1597135446Strhodes CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); 1598135446Strhodes CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), 1599135446Strhodes directory, mctx, maxkeys, keys, nkeys)); 1600135446Strhodes failure: 1601135446Strhodes if (node != NULL) 1602135446Strhodes dns_db_detachnode(db, &node); 1603135446Strhodes return (result); 1604135446Strhodes} 1605135446Strhodes 1606135446Strhodes/* 1607135446Strhodes * Add RRSIG records for an RRset, recording the change in "diff". 1608135446Strhodes */ 1609135446Strhodesstatic isc_result_t 1610135446Strhodesadd_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1611135446Strhodes dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys, 1612135446Strhodes unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception, 1613135446Strhodes isc_stdtime_t expire) 1614135446Strhodes{ 1615135446Strhodes isc_result_t result; 1616135446Strhodes dns_dbnode_t *node = NULL; 1617135446Strhodes dns_rdataset_t rdataset; 1618135446Strhodes dns_rdata_t sig_rdata = DNS_RDATA_INIT; 1619135446Strhodes isc_buffer_t buffer; 1620135446Strhodes unsigned char data[1024]; /* XXX */ 1621135446Strhodes unsigned int i; 1622135446Strhodes 1623135446Strhodes dns_rdataset_init(&rdataset); 1624135446Strhodes isc_buffer_init(&buffer, data, sizeof(data)); 1625135446Strhodes 1626135446Strhodes /* Get the rdataset to sign. */ 1627135446Strhodes CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); 1628135446Strhodes CHECK(dns_db_findrdataset(db, node, ver, type, 0, 1629135446Strhodes (isc_stdtime_t) 0, 1630135446Strhodes &rdataset, NULL)); 1631135446Strhodes dns_db_detachnode(db, &node); 1632135446Strhodes 1633135446Strhodes for (i = 0; i < nkeys; i++) { 1634135446Strhodes /* Calculate the signature, creating a RRSIG RDATA. */ 1635135446Strhodes CHECK(dns_dnssec_sign(name, &rdataset, keys[i], 1636135446Strhodes &inception, &expire, 1637135446Strhodes mctx, &buffer, &sig_rdata)); 1638135446Strhodes 1639135446Strhodes /* Update the database and journal with the RRSIG. */ 1640135446Strhodes /* XXX inefficient - will cause dataset merging */ 1641135446Strhodes CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, 1642135446Strhodes rdataset.ttl, &sig_rdata)); 1643135446Strhodes dns_rdata_reset(&sig_rdata); 1644135446Strhodes } 1645135446Strhodes 1646135446Strhodes failure: 1647135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 1648135446Strhodes dns_rdataset_disassociate(&rdataset); 1649135446Strhodes if (node != NULL) 1650135446Strhodes dns_db_detachnode(db, &node); 1651135446Strhodes return (result); 1652135446Strhodes} 1653135446Strhodes 1654135446Strhodes/* 1655135446Strhodes * Update RRSIG and NSEC records affected by an update. The original 1656135446Strhodes * update, including the SOA serial update but exluding the RRSIG & NSEC 1657135446Strhodes * changes, is in "diff" and has already been applied to "newver" of "db". 1658135446Strhodes * The database version prior to the update is "oldver". 1659135446Strhodes * 1660135446Strhodes * The necessary RRSIG and NSEC changes will be applied to "newver" 1661135446Strhodes * and added (as a minimal diff) to "diff". 1662135446Strhodes * 1663135446Strhodes * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. 1664135446Strhodes */ 1665135446Strhodesstatic isc_result_t 1666135446Strhodesupdate_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1667135446Strhodes dns_dbversion_t *oldver, dns_dbversion_t *newver, 1668135446Strhodes dns_diff_t *diff, isc_uint32_t sigvalidityinterval) 1669135446Strhodes{ 1670135446Strhodes isc_result_t result; 1671135446Strhodes dns_difftuple_t *t; 1672135446Strhodes dns_diff_t diffnames; 1673135446Strhodes dns_diff_t affected; 1674135446Strhodes dns_diff_t sig_diff; 1675135446Strhodes dns_diff_t nsec_diff; 1676135446Strhodes dns_diff_t nsec_mindiff; 1677135446Strhodes isc_boolean_t flag; 1678135446Strhodes dst_key_t *zone_keys[MAXZONEKEYS]; 1679135446Strhodes unsigned int nkeys = 0; 1680135446Strhodes unsigned int i; 1681135446Strhodes isc_stdtime_t now, inception, expire; 1682165071Sdougb dns_ttl_t nsecttl; 1683165071Sdougb dns_rdata_soa_t soa; 1684165071Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1685165071Sdougb dns_rdataset_t rdataset; 1686165071Sdougb dns_dbnode_t *node = NULL; 1687135446Strhodes 1688135446Strhodes dns_diff_init(client->mctx, &diffnames); 1689135446Strhodes dns_diff_init(client->mctx, &affected); 1690135446Strhodes 1691135446Strhodes dns_diff_init(client->mctx, &sig_diff); 1692135446Strhodes dns_diff_init(client->mctx, &nsec_diff); 1693135446Strhodes dns_diff_init(client->mctx, &nsec_mindiff); 1694135446Strhodes 1695135446Strhodes result = find_zone_keys(zone, db, newver, client->mctx, 1696135446Strhodes MAXZONEKEYS, zone_keys, &nkeys); 1697135446Strhodes if (result != ISC_R_SUCCESS) { 1698135446Strhodes update_log(client, zone, ISC_LOG_ERROR, 1699135446Strhodes "could not get zone keys for secure dynamic update"); 1700135446Strhodes goto failure; 1701135446Strhodes } 1702135446Strhodes 1703135446Strhodes isc_stdtime_get(&now); 1704135446Strhodes inception = now - 3600; /* Allow for some clock skew. */ 1705135446Strhodes expire = now + sigvalidityinterval; 1706135446Strhodes 1707135446Strhodes /* 1708165071Sdougb * Get the NSEC's TTL from the SOA MINIMUM field. 1709165071Sdougb */ 1710165071Sdougb CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); 1711165071Sdougb dns_rdataset_init(&rdataset); 1712165071Sdougb CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, 1713165071Sdougb (isc_stdtime_t) 0, &rdataset, NULL)); 1714165071Sdougb CHECK(dns_rdataset_first(&rdataset)); 1715165071Sdougb dns_rdataset_current(&rdataset, &rdata); 1716165071Sdougb CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); 1717165071Sdougb nsecttl = soa.minimum; 1718165071Sdougb dns_rdataset_disassociate(&rdataset); 1719165071Sdougb dns_db_detachnode(db, &node); 1720165071Sdougb 1721165071Sdougb /* 1722135446Strhodes * Find all RRsets directly affected by the update, and 1723135446Strhodes * update their RRSIGs. Also build a list of names affected 1724135446Strhodes * by the update in "diffnames". 1725135446Strhodes */ 1726135446Strhodes CHECK(dns_diff_sort(diff, temp_order)); 1727135446Strhodes 1728135446Strhodes t = ISC_LIST_HEAD(diff->tuples); 1729135446Strhodes while (t != NULL) { 1730135446Strhodes dns_name_t *name = &t->name; 1731135446Strhodes /* Now "name" is a new, unique name affected by the update. */ 1732135446Strhodes 1733135446Strhodes CHECK(namelist_append_name(&diffnames, name)); 1734135446Strhodes 1735135446Strhodes while (t != NULL && dns_name_equal(&t->name, name)) { 1736135446Strhodes dns_rdatatype_t type; 1737135446Strhodes type = t->rdata.type; 1738135446Strhodes 1739135446Strhodes /* 1740135446Strhodes * Now "name" and "type" denote a new unique RRset 1741135446Strhodes * affected by the update. 1742135446Strhodes */ 1743135446Strhodes 1744135446Strhodes /* Don't sign RRSIGs. */ 1745135446Strhodes if (type == dns_rdatatype_rrsig) 1746135446Strhodes goto skip; 1747135446Strhodes 1748135446Strhodes /* 1749135446Strhodes * Delete all old RRSIGs covering this type, since they 1750135446Strhodes * are all invalid when the signed RRset has changed. 1751135446Strhodes * We may not be able to recreate all of them - tough. 1752135446Strhodes */ 1753135446Strhodes CHECK(delete_if(true_p, db, newver, name, 1754135446Strhodes dns_rdatatype_rrsig, type, 1755135446Strhodes NULL, &sig_diff)); 1756135446Strhodes 1757135446Strhodes /* 1758135446Strhodes * If this RRset still exists after the update, 1759135446Strhodes * add a new signature for it. 1760135446Strhodes */ 1761135446Strhodes CHECK(rrset_exists(db, newver, name, type, 0, &flag)); 1762135446Strhodes if (flag) { 1763135446Strhodes CHECK(add_sigs(db, newver, name, type, 1764135446Strhodes &sig_diff, zone_keys, nkeys, 1765135446Strhodes client->mctx, inception, 1766135446Strhodes expire)); 1767135446Strhodes } 1768135446Strhodes skip: 1769135446Strhodes /* Skip any other updates to the same RRset. */ 1770135446Strhodes while (t != NULL && 1771135446Strhodes dns_name_equal(&t->name, name) && 1772135446Strhodes t->rdata.type == type) 1773135446Strhodes { 1774135446Strhodes t = ISC_LIST_NEXT(t, link); 1775135446Strhodes } 1776135446Strhodes } 1777135446Strhodes } 1778135446Strhodes 1779135446Strhodes /* Remove orphaned NSECs and RRSIG NSECs. */ 1780135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1781135446Strhodes t != NULL; 1782135446Strhodes t = ISC_LIST_NEXT(t, link)) 1783135446Strhodes { 1784135446Strhodes CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); 1785135446Strhodes if (! flag) { 1786135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 1787135446Strhodes dns_rdatatype_any, 0, 1788135446Strhodes NULL, &sig_diff)); 1789135446Strhodes } 1790135446Strhodes } 1791135446Strhodes 1792135446Strhodes /* 1793135446Strhodes * When a name is created or deleted, its predecessor needs to 1794135446Strhodes * have its NSEC updated. 1795135446Strhodes */ 1796135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1797135446Strhodes t != NULL; 1798135446Strhodes t = ISC_LIST_NEXT(t, link)) 1799135446Strhodes { 1800135446Strhodes isc_boolean_t existed, exists; 1801135446Strhodes dns_fixedname_t fixedname; 1802135446Strhodes dns_name_t *prevname; 1803135446Strhodes 1804135446Strhodes dns_fixedname_init(&fixedname); 1805135446Strhodes prevname = dns_fixedname_name(&fixedname); 1806135446Strhodes 1807135446Strhodes CHECK(name_exists(db, oldver, &t->name, &existed)); 1808135446Strhodes CHECK(name_exists(db, newver, &t->name, &exists)); 1809135446Strhodes if (exists == existed) 1810135446Strhodes continue; 1811135446Strhodes 1812135446Strhodes /* 1813135446Strhodes * Find the predecessor. 1814135446Strhodes * When names become obscured or unobscured in this update 1815135446Strhodes * transaction, we may find the wrong predecessor because 1816135446Strhodes * the NSECs have not yet been updated to reflect the delegation 1817135446Strhodes * change. This should not matter because in this case, 1818135446Strhodes * the correct predecessor is either the delegation node or 1819135446Strhodes * a newly unobscured node, and those nodes are on the 1820135446Strhodes * "affected" list in any case. 1821135446Strhodes */ 1822135446Strhodes CHECK(next_active(client, zone, db, newver, 1823135446Strhodes &t->name, prevname, ISC_FALSE)); 1824135446Strhodes CHECK(namelist_append_name(&affected, prevname)); 1825135446Strhodes } 1826135446Strhodes 1827135446Strhodes /* 1828135446Strhodes * Find names potentially affected by delegation changes 1829135446Strhodes * (obscured by adding an NS or DNAME, or unobscured by 1830135446Strhodes * removing one). 1831135446Strhodes */ 1832135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1833135446Strhodes t != NULL; 1834135446Strhodes t = ISC_LIST_NEXT(t, link)) 1835135446Strhodes { 1836135446Strhodes isc_boolean_t ns_existed, dname_existed; 1837135446Strhodes isc_boolean_t ns_exists, dname_exists; 1838135446Strhodes 1839135446Strhodes CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0, 1840135446Strhodes &ns_existed)); 1841135446Strhodes CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0, 1842135446Strhodes &dname_existed)); 1843135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, 1844135446Strhodes &ns_exists)); 1845135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, 1846135446Strhodes &dname_exists)); 1847135446Strhodes if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) 1848135446Strhodes continue; 1849135446Strhodes /* 1850135446Strhodes * There was a delegation change. Mark all subdomains 1851135446Strhodes * of t->name as potentially needing a NSEC update. 1852135446Strhodes */ 1853135446Strhodes CHECK(namelist_append_subdomain(db, &t->name, &affected)); 1854135446Strhodes } 1855135446Strhodes 1856135446Strhodes ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); 1857135446Strhodes INSIST(ISC_LIST_EMPTY(diffnames.tuples)); 1858135446Strhodes 1859135446Strhodes CHECK(uniqify_name_list(&affected)); 1860135446Strhodes 1861135446Strhodes /* 1862135446Strhodes * Determine which names should have NSECs, and delete/create 1863135446Strhodes * NSECs to make it so. We don't know the final NSEC targets yet, 1864135446Strhodes * so we just create placeholder NSECs with arbitrary contents 1865135446Strhodes * to indicate that their respective owner names should be part of 1866135446Strhodes * the NSEC chain. 1867135446Strhodes */ 1868135446Strhodes for (t = ISC_LIST_HEAD(affected.tuples); 1869135446Strhodes t != NULL; 1870135446Strhodes t = ISC_LIST_NEXT(t, link)) 1871135446Strhodes { 1872135446Strhodes isc_boolean_t exists; 1873135446Strhodes CHECK(name_exists(db, newver, &t->name, &exists)); 1874135446Strhodes if (! exists) 1875135446Strhodes continue; 1876135446Strhodes CHECK(is_glue(db, newver, &t->name, &flag)); 1877135446Strhodes if (flag) { 1878135446Strhodes /* 1879135446Strhodes * This name is obscured. Delete any 1880135446Strhodes * existing NSEC record. 1881135446Strhodes */ 1882135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 1883135446Strhodes dns_rdatatype_nsec, 0, 1884135446Strhodes NULL, &nsec_diff)); 1885135446Strhodes } else { 1886135446Strhodes /* 1887135446Strhodes * This name is not obscured. It should have a NSEC. 1888135446Strhodes */ 1889135446Strhodes CHECK(rrset_exists(db, newver, &t->name, 1890135446Strhodes dns_rdatatype_nsec, 0, &flag)); 1891135446Strhodes if (! flag) 1892135446Strhodes CHECK(add_placeholder_nsec(db, newver, &t->name, 1893135446Strhodes diff)); 1894135446Strhodes } 1895135446Strhodes } 1896135446Strhodes 1897135446Strhodes /* 1898135446Strhodes * Now we know which names are part of the NSEC chain. 1899135446Strhodes * Make them all point at their correct targets. 1900135446Strhodes */ 1901135446Strhodes for (t = ISC_LIST_HEAD(affected.tuples); 1902135446Strhodes t != NULL; 1903135446Strhodes t = ISC_LIST_NEXT(t, link)) 1904135446Strhodes { 1905135446Strhodes CHECK(rrset_exists(db, newver, &t->name, 1906135446Strhodes dns_rdatatype_nsec, 0, &flag)); 1907135446Strhodes if (flag) { 1908135446Strhodes /* 1909135446Strhodes * There is a NSEC, but we don't know if it is correct. 1910135446Strhodes * Delete it and create a correct one to be sure. 1911135446Strhodes * If the update was unnecessary, the diff minimization 1912135446Strhodes * will take care of eliminating it from the journal, 1913135446Strhodes * IXFRs, etc. 1914135446Strhodes * 1915135446Strhodes * The RRSIG bit should always be set in the NSECs 1916135446Strhodes * we generate, because they will all get RRSIG NSECs. 1917135446Strhodes * (XXX what if the zone keys are missing?). 1918135446Strhodes * Because the RRSIG NSECs have not necessarily been 1919135446Strhodes * created yet, the correctness of the bit mask relies 1920135446Strhodes * on the assumption that NSECs are only created if 1921135446Strhodes * there is other data, and if there is other data, 1922135446Strhodes * there are other RRSIGs. 1923135446Strhodes */ 1924165071Sdougb CHECK(add_nsec(client, zone, db, newver, &t->name, 1925165071Sdougb nsecttl, &nsec_diff)); 1926135446Strhodes } 1927135446Strhodes } 1928135446Strhodes 1929135446Strhodes /* 1930135446Strhodes * Minimize the set of NSEC updates so that we don't 1931135446Strhodes * have to regenerate the RRSIG NSECs for NSECs that were 1932135446Strhodes * replaced with identical ones. 1933135446Strhodes */ 1934135446Strhodes while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { 1935135446Strhodes ISC_LIST_UNLINK(nsec_diff.tuples, t, link); 1936135446Strhodes dns_diff_appendminimal(&nsec_mindiff, &t); 1937135446Strhodes } 1938135446Strhodes 1939135446Strhodes /* Update RRSIG NSECs. */ 1940135446Strhodes for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); 1941135446Strhodes t != NULL; 1942135446Strhodes t = ISC_LIST_NEXT(t, link)) 1943135446Strhodes { 1944135446Strhodes if (t->op == DNS_DIFFOP_DEL) { 1945135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 1946135446Strhodes dns_rdatatype_rrsig, dns_rdatatype_nsec, 1947135446Strhodes NULL, &sig_diff)); 1948135446Strhodes } else if (t->op == DNS_DIFFOP_ADD) { 1949135446Strhodes CHECK(add_sigs(db, newver, &t->name, dns_rdatatype_nsec, 1950135446Strhodes &sig_diff, zone_keys, nkeys, 1951135446Strhodes client->mctx, inception, expire)); 1952135446Strhodes } else { 1953135446Strhodes INSIST(0); 1954135446Strhodes } 1955135446Strhodes } 1956135446Strhodes 1957135446Strhodes /* Record our changes for the journal. */ 1958135446Strhodes while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { 1959135446Strhodes ISC_LIST_UNLINK(sig_diff.tuples, t, link); 1960135446Strhodes dns_diff_appendminimal(diff, &t); 1961135446Strhodes } 1962135446Strhodes while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { 1963135446Strhodes ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); 1964135446Strhodes dns_diff_appendminimal(diff, &t); 1965135446Strhodes } 1966135446Strhodes 1967135446Strhodes INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); 1968135446Strhodes INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); 1969135446Strhodes INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); 1970135446Strhodes 1971135446Strhodes failure: 1972135446Strhodes dns_diff_clear(&sig_diff); 1973135446Strhodes dns_diff_clear(&nsec_diff); 1974135446Strhodes dns_diff_clear(&nsec_mindiff); 1975135446Strhodes 1976135446Strhodes dns_diff_clear(&affected); 1977135446Strhodes dns_diff_clear(&diffnames); 1978135446Strhodes 1979135446Strhodes for (i = 0; i < nkeys; i++) 1980135446Strhodes dst_key_free(&zone_keys[i]); 1981135446Strhodes 1982135446Strhodes return (result); 1983135446Strhodes} 1984135446Strhodes 1985135446Strhodes 1986135446Strhodes/**************************************************************************/ 1987135446Strhodes/* 1988135446Strhodes * The actual update code in all its glory. We try to follow 1989135446Strhodes * the RFC2136 pseudocode as closely as possible. 1990135446Strhodes */ 1991135446Strhodes 1992135446Strhodesstatic isc_result_t 1993135446Strhodessend_update_event(ns_client_t *client, dns_zone_t *zone) { 1994135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1995135446Strhodes update_event_t *event = NULL; 1996135446Strhodes isc_task_t *zonetask = NULL; 1997135446Strhodes ns_client_t *evclient; 1998135446Strhodes 1999135446Strhodes event = (update_event_t *) 2000135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 2001135446Strhodes update_action, NULL, sizeof(*event)); 2002135446Strhodes if (event == NULL) 2003135446Strhodes FAIL(ISC_R_NOMEMORY); 2004135446Strhodes event->zone = zone; 2005135446Strhodes event->result = ISC_R_SUCCESS; 2006135446Strhodes 2007135446Strhodes evclient = NULL; 2008135446Strhodes ns_client_attach(client, &evclient); 2009135446Strhodes INSIST(client->nupdates == 0); 2010135446Strhodes client->nupdates++; 2011135446Strhodes event->ev_arg = evclient; 2012135446Strhodes 2013135446Strhodes dns_zone_gettask(zone, &zonetask); 2014135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 2015135446Strhodes 2016135446Strhodes failure: 2017135446Strhodes if (event != NULL) 2018135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 2019135446Strhodes return (result); 2020135446Strhodes} 2021135446Strhodes 2022135446Strhodesstatic void 2023135446Strhodesrespond(ns_client_t *client, isc_result_t result) { 2024135446Strhodes isc_result_t msg_result; 2025135446Strhodes 2026135446Strhodes msg_result = dns_message_reply(client->message, ISC_TRUE); 2027135446Strhodes if (msg_result != ISC_R_SUCCESS) 2028135446Strhodes goto msg_failure; 2029135446Strhodes client->message->rcode = dns_result_torcode(result); 2030135446Strhodes 2031135446Strhodes ns_client_send(client); 2032135446Strhodes return; 2033135446Strhodes 2034135446Strhodes msg_failure: 2035135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 2036135446Strhodes ISC_LOG_ERROR, 2037135446Strhodes "could not create update response message: %s", 2038135446Strhodes isc_result_totext(msg_result)); 2039135446Strhodes ns_client_next(client, msg_result); 2040135446Strhodes} 2041135446Strhodes 2042135446Strhodesvoid 2043135446Strhodesns_update_start(ns_client_t *client, isc_result_t sigresult) { 2044135446Strhodes dns_message_t *request = client->message; 2045135446Strhodes isc_result_t result; 2046135446Strhodes dns_name_t *zonename; 2047135446Strhodes dns_rdataset_t *zone_rdataset; 2048135446Strhodes dns_zone_t *zone = NULL; 2049135446Strhodes 2050135446Strhodes /* 2051135446Strhodes * Interpret the zone section. 2052135446Strhodes */ 2053135446Strhodes result = dns_message_firstname(request, DNS_SECTION_ZONE); 2054135446Strhodes if (result != ISC_R_SUCCESS) 2055135446Strhodes FAILC(DNS_R_FORMERR, 2056135446Strhodes "update zone section empty"); 2057135446Strhodes 2058135446Strhodes /* 2059135446Strhodes * The zone section must contain exactly one "question", and 2060135446Strhodes * it must be of type SOA. 2061135446Strhodes */ 2062135446Strhodes zonename = NULL; 2063135446Strhodes dns_message_currentname(request, DNS_SECTION_ZONE, &zonename); 2064135446Strhodes zone_rdataset = ISC_LIST_HEAD(zonename->list); 2065135446Strhodes if (zone_rdataset->type != dns_rdatatype_soa) 2066135446Strhodes FAILC(DNS_R_FORMERR, 2067135446Strhodes "update zone section contains non-SOA"); 2068135446Strhodes if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) 2069135446Strhodes FAILC(DNS_R_FORMERR, 2070135446Strhodes "update zone section contains multiple RRs"); 2071135446Strhodes 2072135446Strhodes /* The zone section must have exactly one name. */ 2073135446Strhodes result = dns_message_nextname(request, DNS_SECTION_ZONE); 2074135446Strhodes if (result != ISC_R_NOMORE) 2075135446Strhodes FAILC(DNS_R_FORMERR, 2076135446Strhodes "update zone section contains multiple RRs"); 2077135446Strhodes 2078135446Strhodes result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, 2079135446Strhodes &zone); 2080135446Strhodes if (result != ISC_R_SUCCESS) 2081135446Strhodes FAILC(DNS_R_NOTAUTH, 2082135446Strhodes "not authoritative for update zone"); 2083135446Strhodes 2084135446Strhodes switch(dns_zone_gettype(zone)) { 2085135446Strhodes case dns_zone_master: 2086135446Strhodes /* 2087135446Strhodes * We can now fail due to a bad signature as we now know 2088135446Strhodes * that we are the master. 2089135446Strhodes */ 2090135446Strhodes if (sigresult != ISC_R_SUCCESS) 2091135446Strhodes FAIL(sigresult); 2092135446Strhodes CHECK(send_update_event(client, zone)); 2093135446Strhodes break; 2094135446Strhodes case dns_zone_slave: 2095135446Strhodes CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone), 2096135446Strhodes "update forwarding", zonename, ISC_TRUE)); 2097135446Strhodes CHECK(send_forward_event(client, zone)); 2098135446Strhodes break; 2099135446Strhodes default: 2100135446Strhodes FAILC(DNS_R_NOTAUTH, 2101135446Strhodes "not authoritative for update zone"); 2102135446Strhodes } 2103135446Strhodes return; 2104135446Strhodes 2105135446Strhodes failure: 2106135446Strhodes /* 2107135446Strhodes * We failed without having sent an update event to the zone. 2108135446Strhodes * We are still in the client task context, so we can 2109135446Strhodes * simply give an error response without switching tasks. 2110135446Strhodes */ 2111135446Strhodes respond(client, result); 2112135446Strhodes if (zone != NULL) 2113135446Strhodes dns_zone_detach(&zone); 2114135446Strhodes} 2115135446Strhodes 2116135446Strhodes/* 2117135446Strhodes * DS records are not allowed to exist without corresponding NS records, 2118135446Strhodes * draft-ietf-dnsext-delegation-signer-11.txt, 2.2 Protocol Change, 2119135446Strhodes * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex". 2120135446Strhodes */ 2121135446Strhodes 2122135446Strhodesstatic isc_result_t 2123135446Strhodesremove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) { 2124135446Strhodes isc_result_t result; 2125135446Strhodes isc_boolean_t ns_exists, ds_exists; 2126135446Strhodes dns_difftuple_t *t; 2127135446Strhodes 2128135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); 2129135446Strhodes t != NULL; 2130135446Strhodes t = ISC_LIST_NEXT(t, link)) { 2131135446Strhodes if (t->op != DNS_DIFFOP_DEL || 2132135446Strhodes t->rdata.type != dns_rdatatype_ns) 2133135446Strhodes continue; 2134135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, 2135135446Strhodes &ns_exists)); 2136135446Strhodes if (ns_exists) 2137135446Strhodes continue; 2138135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ds, 0, 2139135446Strhodes &ds_exists)); 2140135446Strhodes if (!ds_exists) 2141135446Strhodes continue; 2142135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 2143135446Strhodes dns_rdatatype_ds, 0, NULL, diff)); 2144135446Strhodes } 2145135446Strhodes return (ISC_R_SUCCESS); 2146135446Strhodes 2147135446Strhodes failure: 2148135446Strhodes return (result); 2149135446Strhodes} 2150135446Strhodes 2151135446Strhodesstatic void 2152135446Strhodesupdate_action(isc_task_t *task, isc_event_t *event) { 2153135446Strhodes update_event_t *uev = (update_event_t *) event; 2154135446Strhodes dns_zone_t *zone = uev->zone; 2155135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2156135446Strhodes 2157135446Strhodes isc_result_t result; 2158135446Strhodes dns_db_t *db = NULL; 2159135446Strhodes dns_dbversion_t *oldver = NULL; 2160135446Strhodes dns_dbversion_t *ver = NULL; 2161135446Strhodes dns_diff_t diff; /* Pending updates. */ 2162135446Strhodes dns_diff_t temp; /* Pending RR existence assertions. */ 2163135446Strhodes isc_boolean_t soa_serial_changed = ISC_FALSE; 2164135446Strhodes isc_mem_t *mctx = client->mctx; 2165135446Strhodes dns_rdatatype_t covers; 2166135446Strhodes dns_message_t *request = client->message; 2167135446Strhodes dns_rdataclass_t zoneclass; 2168135446Strhodes dns_name_t *zonename; 2169135446Strhodes dns_ssutable_t *ssutable = NULL; 2170135446Strhodes dns_fixedname_t tmpnamefixed; 2171135446Strhodes dns_name_t *tmpname = NULL; 2172135446Strhodes 2173135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATE); 2174135446Strhodes 2175135446Strhodes dns_diff_init(mctx, &diff); 2176135446Strhodes dns_diff_init(mctx, &temp); 2177135446Strhodes 2178135446Strhodes CHECK(dns_zone_getdb(zone, &db)); 2179135446Strhodes zonename = dns_db_origin(db); 2180135446Strhodes zoneclass = dns_db_class(db); 2181135446Strhodes dns_zone_getssutable(zone, &ssutable); 2182135446Strhodes dns_db_currentversion(db, &oldver); 2183135446Strhodes CHECK(dns_db_newversion(db, &ver)); 2184135446Strhodes 2185135446Strhodes /* 2186135446Strhodes * Check prerequisites. 2187135446Strhodes */ 2188135446Strhodes 2189135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE); 2190135446Strhodes result == ISC_R_SUCCESS; 2191135446Strhodes result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE)) 2192135446Strhodes { 2193135446Strhodes dns_name_t *name = NULL; 2194135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2195135446Strhodes dns_ttl_t ttl; 2196135446Strhodes dns_rdataclass_t update_class; 2197135446Strhodes isc_boolean_t flag; 2198135446Strhodes 2199135446Strhodes get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass, 2200135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2201135446Strhodes 2202135446Strhodes if (ttl != 0) 2203135446Strhodes FAILC(DNS_R_FORMERR, "prerequisite TTL is not zero"); 2204135446Strhodes 2205135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2206135446Strhodes FAILN(DNS_R_NOTZONE, name, 2207135446Strhodes "prerequisite name is out of zone"); 2208135446Strhodes 2209135446Strhodes if (update_class == dns_rdataclass_any) { 2210135446Strhodes if (rdata.length != 0) 2211135446Strhodes FAILC(DNS_R_FORMERR, 2212135446Strhodes "class ANY prerequisite " 2213135446Strhodes "RDATA is not empty"); 2214135446Strhodes if (rdata.type == dns_rdatatype_any) { 2215135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2216135446Strhodes if (! flag) { 2217135446Strhodes FAILN(DNS_R_NXDOMAIN, name, 2218135446Strhodes "'name in use' prerequisite " 2219135446Strhodes "not satisfied"); 2220135446Strhodes } 2221135446Strhodes } else { 2222135446Strhodes CHECK(rrset_exists(db, ver, name, 2223135446Strhodes rdata.type, covers, &flag)); 2224135446Strhodes if (! flag) { 2225135446Strhodes /* RRset does not exist. */ 2226135446Strhodes FAILNT(DNS_R_NXRRSET, name, rdata.type, 2227135446Strhodes "'rrset exists (value independent)' " 2228135446Strhodes "prerequisite not satisfied"); 2229135446Strhodes } 2230135446Strhodes } 2231135446Strhodes } else if (update_class == dns_rdataclass_none) { 2232135446Strhodes if (rdata.length != 0) 2233135446Strhodes FAILC(DNS_R_FORMERR, 2234135446Strhodes "class NONE prerequisite " 2235135446Strhodes "RDATA is not empty"); 2236135446Strhodes if (rdata.type == dns_rdatatype_any) { 2237135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2238135446Strhodes if (flag) { 2239135446Strhodes FAILN(DNS_R_YXDOMAIN, name, 2240135446Strhodes "'name not in use' prerequisite " 2241135446Strhodes "not satisfied"); 2242135446Strhodes } 2243135446Strhodes } else { 2244135446Strhodes CHECK(rrset_exists(db, ver, name, 2245135446Strhodes rdata.type, covers, &flag)); 2246135446Strhodes if (flag) { 2247135446Strhodes /* RRset exists. */ 2248135446Strhodes FAILNT(DNS_R_YXRRSET, name, rdata.type, 2249135446Strhodes "'rrset does not exist' " 2250135446Strhodes "prerequisite not satisfied"); 2251135446Strhodes } 2252135446Strhodes } 2253135446Strhodes } else if (update_class == zoneclass) { 2254135446Strhodes /* "temp<rr.name, rr.type> += rr;" */ 2255135446Strhodes result = temp_append(&temp, name, &rdata); 2256135446Strhodes if (result != ISC_R_SUCCESS) { 2257135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 2258135446Strhodes "temp entry creation failed: %s", 2259135446Strhodes dns_result_totext(result)); 2260135446Strhodes FAIL(ISC_R_UNEXPECTED); 2261135446Strhodes } 2262135446Strhodes } else { 2263135446Strhodes FAILC(DNS_R_FORMERR, "malformed prerequisite"); 2264135446Strhodes } 2265135446Strhodes } 2266135446Strhodes if (result != ISC_R_NOMORE) 2267135446Strhodes FAIL(result); 2268135446Strhodes 2269135446Strhodes 2270135446Strhodes /* 2271135446Strhodes * Perform the final check of the "rrset exists (value dependent)" 2272135446Strhodes * prerequisites. 2273135446Strhodes */ 2274135446Strhodes if (ISC_LIST_HEAD(temp.tuples) != NULL) { 2275135446Strhodes dns_rdatatype_t type; 2276135446Strhodes 2277135446Strhodes /* 2278135446Strhodes * Sort the prerequisite records by owner name, 2279135446Strhodes * type, and rdata. 2280135446Strhodes */ 2281135446Strhodes result = dns_diff_sort(&temp, temp_order); 2282135446Strhodes if (result != ISC_R_SUCCESS) 2283135446Strhodes FAILC(result, "'RRset exists (value dependent)' " 2284135446Strhodes "prerequisite not satisfied"); 2285135446Strhodes 2286135446Strhodes dns_fixedname_init(&tmpnamefixed); 2287135446Strhodes tmpname = dns_fixedname_name(&tmpnamefixed); 2288135446Strhodes result = temp_check(mctx, &temp, db, ver, tmpname, &type); 2289135446Strhodes if (result != ISC_R_SUCCESS) 2290135446Strhodes FAILNT(result, tmpname, type, 2291135446Strhodes "'RRset exists (value dependent)' " 2292135446Strhodes "prerequisite not satisfied"); 2293135446Strhodes } 2294135446Strhodes 2295135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2296135446Strhodes "prerequisites are OK"); 2297135446Strhodes 2298135446Strhodes /* 2299135446Strhodes * Check Requestor's Permissions. It seems a bit silly to do this 2300135446Strhodes * only after prerequisite testing, but that is what RFC2136 says. 2301135446Strhodes */ 2302135446Strhodes result = ISC_R_SUCCESS; 2303135446Strhodes if (ssutable == NULL) 2304135446Strhodes CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone), 2305135446Strhodes "update", zonename, ISC_FALSE)); 2306135446Strhodes else if (client->signer == NULL) 2307135446Strhodes CHECK(checkupdateacl(client, NULL, "update", zonename, 2308135446Strhodes ISC_FALSE)); 2309135446Strhodes 2310135446Strhodes if (dns_zone_getupdatedisabled(zone)) 2311135446Strhodes FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled"); 2312135446Strhodes 2313135446Strhodes /* 2314135446Strhodes * Perform the Update Section Prescan. 2315135446Strhodes */ 2316135446Strhodes 2317135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2318135446Strhodes result == ISC_R_SUCCESS; 2319135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2320135446Strhodes { 2321135446Strhodes dns_name_t *name = NULL; 2322135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2323135446Strhodes dns_ttl_t ttl; 2324135446Strhodes dns_rdataclass_t update_class; 2325135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2326135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2327135446Strhodes 2328135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2329135446Strhodes FAILC(DNS_R_NOTZONE, 2330135446Strhodes "update RR is outside zone"); 2331135446Strhodes if (update_class == zoneclass) { 2332135446Strhodes /* 2333135446Strhodes * Check for meta-RRs. The RFC2136 pseudocode says 2334135446Strhodes * check for ANY|AXFR|MAILA|MAILB, but the text adds 2335135446Strhodes * "or any other QUERY metatype" 2336135446Strhodes */ 2337135446Strhodes if (dns_rdatatype_ismeta(rdata.type)) { 2338135446Strhodes FAILC(DNS_R_FORMERR, 2339135446Strhodes "meta-RR in update"); 2340135446Strhodes } 2341135446Strhodes result = dns_zone_checknames(zone, name, &rdata); 2342135446Strhodes if (result != ISC_R_SUCCESS) 2343135446Strhodes FAIL(DNS_R_REFUSED); 2344135446Strhodes } else if (update_class == dns_rdataclass_any) { 2345135446Strhodes if (ttl != 0 || rdata.length != 0 || 2346135446Strhodes (dns_rdatatype_ismeta(rdata.type) && 2347135446Strhodes rdata.type != dns_rdatatype_any)) 2348135446Strhodes FAILC(DNS_R_FORMERR, 2349135446Strhodes "meta-RR in update"); 2350135446Strhodes } else if (update_class == dns_rdataclass_none) { 2351135446Strhodes if (ttl != 0 || 2352135446Strhodes dns_rdatatype_ismeta(rdata.type)) 2353135446Strhodes FAILC(DNS_R_FORMERR, 2354135446Strhodes "meta-RR in update"); 2355135446Strhodes } else { 2356135446Strhodes update_log(client, zone, ISC_LOG_WARNING, 2357135446Strhodes "update RR has incorrect class %d", 2358135446Strhodes update_class); 2359135446Strhodes FAIL(DNS_R_FORMERR); 2360135446Strhodes } 2361135446Strhodes /* 2362135446Strhodes * draft-ietf-dnsind-simple-secure-update-01 says 2363135446Strhodes * "Unlike traditional dynamic update, the client 2364135446Strhodes * is forbidden from updating NSEC records." 2365135446Strhodes */ 2366135446Strhodes if (dns_db_issecure(db)) { 2367135446Strhodes if (rdata.type == dns_rdatatype_nsec) { 2368135446Strhodes FAILC(DNS_R_REFUSED, 2369135446Strhodes "explicit NSEC updates are not allowed " 2370135446Strhodes "in secure zones"); 2371135446Strhodes } 2372135446Strhodes else if (rdata.type == dns_rdatatype_rrsig) { 2373135446Strhodes FAILC(DNS_R_REFUSED, 2374135446Strhodes "explicit RRSIG updates are currently not " 2375135446Strhodes "supported in secure zones"); 2376135446Strhodes } 2377135446Strhodes } 2378135446Strhodes 2379135446Strhodes if (ssutable != NULL && client->signer != NULL) { 2380135446Strhodes if (rdata.type != dns_rdatatype_any) { 2381135446Strhodes if (!dns_ssutable_checkrules(ssutable, 2382135446Strhodes client->signer, 2383135446Strhodes name, rdata.type)) 2384135446Strhodes FAILC(DNS_R_REFUSED, 2385135446Strhodes "rejected by secure update"); 2386135446Strhodes } 2387135446Strhodes else { 2388135446Strhodes if (!ssu_checkall(db, ver, name, ssutable, 2389135446Strhodes client->signer)) 2390135446Strhodes FAILC(DNS_R_REFUSED, 2391135446Strhodes "rejected by secure update"); 2392135446Strhodes } 2393135446Strhodes } 2394135446Strhodes } 2395135446Strhodes if (result != ISC_R_NOMORE) 2396135446Strhodes FAIL(result); 2397135446Strhodes 2398135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2399135446Strhodes "update section prescan OK"); 2400135446Strhodes 2401135446Strhodes /* 2402135446Strhodes * Process the Update Section. 2403135446Strhodes */ 2404135446Strhodes 2405135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2406135446Strhodes result == ISC_R_SUCCESS; 2407135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2408135446Strhodes { 2409135446Strhodes dns_name_t *name = NULL; 2410135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2411135446Strhodes dns_ttl_t ttl; 2412135446Strhodes dns_rdataclass_t update_class; 2413135446Strhodes isc_boolean_t flag; 2414135446Strhodes 2415135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2416135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2417135446Strhodes 2418135446Strhodes if (update_class == zoneclass) { 2419135446Strhodes 2420135446Strhodes /* 2421135446Strhodes * RFC 1123 doesn't allow MF and MD in master zones. */ 2422135446Strhodes if (rdata.type == dns_rdatatype_md || 2423135446Strhodes rdata.type == dns_rdatatype_mf) { 2424135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2425135446Strhodes 2426135446Strhodes dns_rdatatype_format(rdata.type, typebuf, 2427135446Strhodes sizeof(typebuf)); 2428135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, 2429135446Strhodes "attempt to add %s ignored", 2430135446Strhodes typebuf); 2431135446Strhodes continue; 2432135446Strhodes } 2433135446Strhodes if (rdata.type == dns_rdatatype_ns && 2434135446Strhodes dns_name_iswildcard(name)) { 2435135446Strhodes update_log(client, zone, 2436135446Strhodes LOGLEVEL_PROTOCOL, 2437135446Strhodes "attempt to add wildcard NS record" 2438135446Strhodes "ignored"); 2439135446Strhodes continue; 2440135446Strhodes } 2441135446Strhodes if (rdata.type == dns_rdatatype_cname) { 2442135446Strhodes CHECK(cname_incompatible_rrset_exists(db, ver, 2443135446Strhodes name, 2444135446Strhodes &flag)); 2445135446Strhodes if (flag) { 2446135446Strhodes update_log(client, zone, 2447135446Strhodes LOGLEVEL_PROTOCOL, 2448135446Strhodes "attempt to add CNAME " 2449135446Strhodes "alongside non-CNAME " 2450135446Strhodes "ignored"); 2451135446Strhodes continue; 2452135446Strhodes } 2453135446Strhodes } else { 2454135446Strhodes CHECK(rrset_exists(db, ver, name, 2455135446Strhodes dns_rdatatype_cname, 0, 2456135446Strhodes &flag)); 2457135446Strhodes if (flag && 2458135446Strhodes ! dns_rdatatype_isdnssec(rdata.type)) 2459135446Strhodes { 2460135446Strhodes update_log(client, zone, 2461135446Strhodes LOGLEVEL_PROTOCOL, 2462135446Strhodes "attempt to add non-CNAME " 2463135446Strhodes "alongside CNAME ignored"); 2464135446Strhodes continue; 2465135446Strhodes } 2466135446Strhodes } 2467135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2468135446Strhodes isc_boolean_t ok; 2469135446Strhodes CHECK(rrset_exists(db, ver, name, 2470135446Strhodes dns_rdatatype_soa, 0, 2471135446Strhodes &flag)); 2472135446Strhodes if (! flag) { 2473135446Strhodes update_log(client, zone, 2474135446Strhodes LOGLEVEL_PROTOCOL, 2475135446Strhodes "attempt to create 2nd " 2476135446Strhodes "SOA ignored"); 2477135446Strhodes continue; 2478135446Strhodes } 2479135446Strhodes CHECK(check_soa_increment(db, ver, &rdata, 2480135446Strhodes &ok)); 2481135446Strhodes if (! ok) { 2482135446Strhodes update_log(client, zone, 2483135446Strhodes LOGLEVEL_PROTOCOL, 2484135446Strhodes "SOA update failed to " 2485135446Strhodes "increment serial, " 2486135446Strhodes "ignoring it"); 2487135446Strhodes continue; 2488135446Strhodes } 2489135446Strhodes soa_serial_changed = ISC_TRUE; 2490135446Strhodes } 2491135446Strhodes 2492135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { 2493135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2494135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2495135446Strhodes dns_name_format(name, namestr, 2496135446Strhodes sizeof(namestr)); 2497135446Strhodes dns_rdatatype_format(rdata.type, typestr, 2498135446Strhodes sizeof(typestr)); 2499135446Strhodes update_log(client, zone, 2500135446Strhodes LOGLEVEL_PROTOCOL, 2501135446Strhodes "adding an RR at '%s' %s", 2502135446Strhodes namestr, typestr); 2503135446Strhodes } 2504135446Strhodes 2505135446Strhodes /* Prepare the affected RRset for the addition. */ 2506135446Strhodes { 2507135446Strhodes add_rr_prepare_ctx_t ctx; 2508135446Strhodes ctx.db = db; 2509135446Strhodes ctx.ver = ver; 2510135446Strhodes ctx.diff = &diff; 2511135446Strhodes ctx.name = name; 2512135446Strhodes ctx.update_rr = &rdata; 2513135446Strhodes ctx.update_rr_ttl = ttl; 2514135446Strhodes ctx.ignore_add = ISC_FALSE; 2515135446Strhodes dns_diff_init(mctx, &ctx.del_diff); 2516135446Strhodes dns_diff_init(mctx, &ctx.add_diff); 2517135446Strhodes CHECK(foreach_rr(db, ver, name, rdata.type, 2518135446Strhodes covers, add_rr_prepare_action, 2519135446Strhodes &ctx)); 2520135446Strhodes 2521135446Strhodes if (ctx.ignore_add) { 2522135446Strhodes dns_diff_clear(&ctx.del_diff); 2523135446Strhodes dns_diff_clear(&ctx.add_diff); 2524135446Strhodes } else { 2525135446Strhodes CHECK(do_diff(&ctx.del_diff, db, ver, &diff)); 2526135446Strhodes CHECK(do_diff(&ctx.add_diff, db, ver, &diff)); 2527135446Strhodes CHECK(update_one_rr(db, ver, &diff, 2528135446Strhodes DNS_DIFFOP_ADD, 2529135446Strhodes name, ttl, &rdata)); 2530135446Strhodes } 2531135446Strhodes } 2532135446Strhodes } else if (update_class == dns_rdataclass_any) { 2533135446Strhodes if (rdata.type == dns_rdatatype_any) { 2534135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2535135446Strhodes LOGLEVEL_PROTOCOL)) 2536135446Strhodes { 2537135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2538135446Strhodes dns_name_format(name, namestr, 2539135446Strhodes sizeof(namestr)); 2540135446Strhodes update_log(client, zone, 2541135446Strhodes LOGLEVEL_PROTOCOL, 2542135446Strhodes "delete all rrsets from " 2543135446Strhodes "name '%s'", namestr); 2544135446Strhodes } 2545135446Strhodes if (dns_name_equal(name, zonename)) { 2546135446Strhodes CHECK(delete_if(type_not_soa_nor_ns_p, 2547135446Strhodes db, ver, name, 2548135446Strhodes dns_rdatatype_any, 0, 2549135446Strhodes &rdata, &diff)); 2550135446Strhodes } else { 2551143731Sdougb CHECK(delete_if(type_not_dnssec, 2552143731Sdougb db, ver, name, 2553135446Strhodes dns_rdatatype_any, 0, 2554135446Strhodes &rdata, &diff)); 2555135446Strhodes } 2556135446Strhodes } else if (dns_name_equal(name, zonename) && 2557135446Strhodes (rdata.type == dns_rdatatype_soa || 2558135446Strhodes rdata.type == dns_rdatatype_ns)) { 2559135446Strhodes update_log(client, zone, 2560135446Strhodes LOGLEVEL_PROTOCOL, 2561135446Strhodes "attempt to delete all SOA " 2562135446Strhodes "or NS records ignored"); 2563135446Strhodes continue; 2564135446Strhodes } else { 2565135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2566135446Strhodes LOGLEVEL_PROTOCOL)) 2567135446Strhodes { 2568135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2569135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2570135446Strhodes dns_name_format(name, namestr, 2571135446Strhodes sizeof(namestr)); 2572135446Strhodes dns_rdatatype_format(rdata.type, 2573135446Strhodes typestr, 2574135446Strhodes sizeof(typestr)); 2575135446Strhodes update_log(client, zone, 2576135446Strhodes LOGLEVEL_PROTOCOL, 2577135446Strhodes "deleting rrset at '%s' %s", 2578135446Strhodes namestr, typestr); 2579135446Strhodes } 2580135446Strhodes CHECK(delete_if(true_p, db, ver, name, 2581135446Strhodes rdata.type, covers, &rdata, 2582135446Strhodes &diff)); 2583135446Strhodes } 2584135446Strhodes } else if (update_class == dns_rdataclass_none) { 2585135446Strhodes /* 2586135446Strhodes * The (name == zonename) condition appears in 2587135446Strhodes * RFC2136 3.4.2.4 but is missing from the pseudocode. 2588135446Strhodes */ 2589135446Strhodes if (dns_name_equal(name, zonename)) { 2590135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2591135446Strhodes update_log(client, zone, 2592135446Strhodes LOGLEVEL_PROTOCOL, 2593135446Strhodes "attempt to delete SOA " 2594135446Strhodes "ignored"); 2595135446Strhodes continue; 2596135446Strhodes } 2597135446Strhodes if (rdata.type == dns_rdatatype_ns) { 2598135446Strhodes int count; 2599135446Strhodes CHECK(rr_count(db, ver, name, 2600135446Strhodes dns_rdatatype_ns, 2601135446Strhodes 0, &count)); 2602135446Strhodes if (count == 1) { 2603135446Strhodes update_log(client, zone, 2604135446Strhodes LOGLEVEL_PROTOCOL, 2605135446Strhodes "attempt to " 2606135446Strhodes "delete last " 2607135446Strhodes "NS ignored"); 2608135446Strhodes continue; 2609135446Strhodes } 2610135446Strhodes } 2611135446Strhodes } 2612135446Strhodes update_log(client, zone, 2613135446Strhodes LOGLEVEL_PROTOCOL, 2614135446Strhodes "deleting an RR"); 2615135446Strhodes CHECK(delete_if(rr_equal_p, db, ver, name, 2616135446Strhodes rdata.type, covers, &rdata, &diff)); 2617135446Strhodes } 2618135446Strhodes } 2619135446Strhodes if (result != ISC_R_NOMORE) 2620135446Strhodes FAIL(result); 2621135446Strhodes 2622135446Strhodes /* 2623135446Strhodes * If any changes were made, increment the SOA serial number, 2624135446Strhodes * update RRSIGs and NSECs (if zone is secure), and write the update 2625135446Strhodes * to the journal. 2626135446Strhodes */ 2627135446Strhodes if (! ISC_LIST_EMPTY(diff.tuples)) { 2628135446Strhodes char *journalfile; 2629135446Strhodes dns_journal_t *journal; 2630135446Strhodes 2631135446Strhodes /* 2632135446Strhodes * Increment the SOA serial, but only if it was not 2633135446Strhodes * changed as a result of an update operation. 2634135446Strhodes */ 2635135446Strhodes if (! soa_serial_changed) { 2636135446Strhodes CHECK(increment_soa_serial(db, ver, &diff, mctx)); 2637135446Strhodes } 2638135446Strhodes 2639135446Strhodes CHECK(remove_orphaned_ds(db, ver, &diff)); 2640135446Strhodes 2641135446Strhodes if (dns_db_issecure(db)) { 2642135446Strhodes result = update_signatures(client, zone, db, oldver, 2643135446Strhodes ver, &diff, 2644135446Strhodes dns_zone_getsigvalidityinterval(zone)); 2645135446Strhodes if (result != ISC_R_SUCCESS) { 2646135446Strhodes update_log(client, zone, 2647135446Strhodes ISC_LOG_ERROR, 2648135446Strhodes "RRSIG/NSEC update failed: %s", 2649135446Strhodes isc_result_totext(result)); 2650135446Strhodes goto failure; 2651135446Strhodes } 2652135446Strhodes } 2653135446Strhodes 2654135446Strhodes journalfile = dns_zone_getjournal(zone); 2655135446Strhodes if (journalfile != NULL) { 2656135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2657135446Strhodes "writing journal %s", journalfile); 2658135446Strhodes 2659135446Strhodes journal = NULL; 2660135446Strhodes result = dns_journal_open(mctx, journalfile, 2661135446Strhodes ISC_TRUE, &journal); 2662135446Strhodes if (result != ISC_R_SUCCESS) 2663135446Strhodes FAILS(result, "journal open failed"); 2664135446Strhodes 2665135446Strhodes result = dns_journal_write_transaction(journal, &diff); 2666135446Strhodes if (result != ISC_R_SUCCESS) { 2667135446Strhodes dns_journal_destroy(&journal); 2668135446Strhodes FAILS(result, "journal write failed"); 2669135446Strhodes } 2670135446Strhodes 2671135446Strhodes dns_journal_destroy(&journal); 2672135446Strhodes } 2673135446Strhodes 2674135446Strhodes /* 2675135446Strhodes * XXXRTH Just a note that this committing code will have 2676135446Strhodes * to change to handle databases that need two-phase 2677135446Strhodes * commit, but this isn't a priority. 2678135446Strhodes */ 2679135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2680135446Strhodes "committing update transaction"); 2681135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 2682135446Strhodes 2683135446Strhodes /* 2684135446Strhodes * Mark the zone as dirty so that it will be written to disk. 2685135446Strhodes */ 2686135446Strhodes dns_zone_markdirty(zone); 2687135446Strhodes 2688135446Strhodes /* 2689135446Strhodes * Notify slaves of the change we just made. 2690135446Strhodes */ 2691135446Strhodes dns_zone_notify(zone); 2692135446Strhodes } else { 2693135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); 2694135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 2695135446Strhodes } 2696135446Strhodes result = ISC_R_SUCCESS; 2697135446Strhodes goto common; 2698135446Strhodes 2699135446Strhodes failure: 2700135446Strhodes /* 2701135446Strhodes * The reason for failure should have been logged at this point. 2702135446Strhodes */ 2703135446Strhodes if (ver != NULL) { 2704135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2705135446Strhodes "rolling back"); 2706135446Strhodes dns_db_closeversion(db, &ver, ISC_FALSE); 2707135446Strhodes } 2708135446Strhodes 2709135446Strhodes common: 2710135446Strhodes dns_diff_clear(&temp); 2711135446Strhodes dns_diff_clear(&diff); 2712135446Strhodes 2713135446Strhodes if (oldver != NULL) 2714135446Strhodes dns_db_closeversion(db, &oldver, ISC_FALSE); 2715135446Strhodes 2716135446Strhodes if (db != NULL) 2717135446Strhodes dns_db_detach(&db); 2718135446Strhodes 2719135446Strhodes if (ssutable != NULL) 2720135446Strhodes dns_ssutable_detach(&ssutable); 2721135446Strhodes 2722135446Strhodes if (zone != NULL) 2723135446Strhodes dns_zone_detach(&zone); 2724135446Strhodes 2725135446Strhodes isc_task_detach(&task); 2726135446Strhodes uev->result = result; 2727135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2728135446Strhodes uev->ev_action = updatedone_action; 2729135446Strhodes isc_task_send(client->task, &event); 2730135446Strhodes INSIST(event == NULL); 2731135446Strhodes} 2732135446Strhodes 2733135446Strhodesstatic void 2734135446Strhodesupdatedone_action(isc_task_t *task, isc_event_t *event) { 2735135446Strhodes update_event_t *uev = (update_event_t *) event; 2736135446Strhodes ns_client_t *client = (ns_client_t *) event->ev_arg; 2737135446Strhodes 2738135446Strhodes UNUSED(task); 2739135446Strhodes 2740135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATEDONE); 2741135446Strhodes INSIST(task == client->task); 2742135446Strhodes 2743135446Strhodes INSIST(client->nupdates > 0); 2744135446Strhodes client->nupdates--; 2745135446Strhodes respond(client, uev->result); 2746153816Sdougb isc_event_free(&event); 2747135446Strhodes ns_client_detach(&client); 2748135446Strhodes} 2749135446Strhodes 2750135446Strhodes/* 2751135446Strhodes * Update forwarding support. 2752135446Strhodes */ 2753135446Strhodes 2754135446Strhodesstatic void 2755135446Strhodesforward_fail(isc_task_t *task, isc_event_t *event) { 2756135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2757135446Strhodes 2758135446Strhodes UNUSED(task); 2759135446Strhodes 2760135446Strhodes INSIST(client->nupdates > 0); 2761135446Strhodes client->nupdates--; 2762135446Strhodes respond(client, DNS_R_SERVFAIL); 2763153816Sdougb isc_event_free(&event); 2764135446Strhodes ns_client_detach(&client); 2765135446Strhodes} 2766135446Strhodes 2767135446Strhodes 2768135446Strhodesstatic void 2769135446Strhodesforward_callback(void *arg, isc_result_t result, dns_message_t *answer) { 2770135446Strhodes update_event_t *uev = arg; 2771135446Strhodes ns_client_t *client = uev->ev_arg; 2772135446Strhodes 2773135446Strhodes if (result != ISC_R_SUCCESS) { 2774135446Strhodes INSIST(answer == NULL); 2775135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2776135446Strhodes uev->ev_action = forward_fail; 2777135446Strhodes } else { 2778135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2779135446Strhodes uev->ev_action = forward_done; 2780135446Strhodes uev->answer = answer; 2781135446Strhodes } 2782135446Strhodes isc_task_send(client->task, ISC_EVENT_PTR(&uev)); 2783135446Strhodes} 2784135446Strhodes 2785135446Strhodesstatic void 2786135446Strhodesforward_done(isc_task_t *task, isc_event_t *event) { 2787135446Strhodes update_event_t *uev = (update_event_t *) event; 2788135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2789135446Strhodes 2790135446Strhodes UNUSED(task); 2791135446Strhodes 2792135446Strhodes INSIST(client->nupdates > 0); 2793135446Strhodes client->nupdates--; 2794135446Strhodes ns_client_sendraw(client, uev->answer); 2795135446Strhodes dns_message_destroy(&uev->answer); 2796135446Strhodes isc_event_free(&event); 2797135446Strhodes ns_client_detach(&client); 2798135446Strhodes} 2799135446Strhodes 2800135446Strhodesstatic void 2801135446Strhodesforward_action(isc_task_t *task, isc_event_t *event) { 2802135446Strhodes update_event_t *uev = (update_event_t *) event; 2803135446Strhodes dns_zone_t *zone = uev->zone; 2804135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2805135446Strhodes isc_result_t result; 2806135446Strhodes 2807135446Strhodes result = dns_zone_forwardupdate(zone, client->message, 2808135446Strhodes forward_callback, event); 2809135446Strhodes if (result != ISC_R_SUCCESS) { 2810135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2811135446Strhodes uev->ev_action = forward_fail; 2812135446Strhodes isc_task_send(client->task, &event); 2813135446Strhodes } 2814135446Strhodes dns_zone_detach(&zone); 2815135446Strhodes isc_task_detach(&task); 2816135446Strhodes} 2817135446Strhodes 2818135446Strhodesstatic isc_result_t 2819135446Strhodessend_forward_event(ns_client_t *client, dns_zone_t *zone) { 2820135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2821135446Strhodes update_event_t *event = NULL; 2822135446Strhodes isc_task_t *zonetask = NULL; 2823135446Strhodes ns_client_t *evclient; 2824135446Strhodes 2825135446Strhodes event = (update_event_t *) 2826135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 2827135446Strhodes forward_action, NULL, sizeof(*event)); 2828135446Strhodes if (event == NULL) 2829135446Strhodes FAIL(ISC_R_NOMEMORY); 2830135446Strhodes event->zone = zone; 2831135446Strhodes event->result = ISC_R_SUCCESS; 2832135446Strhodes 2833135446Strhodes evclient = NULL; 2834135446Strhodes ns_client_attach(client, &evclient); 2835135446Strhodes INSIST(client->nupdates == 0); 2836135446Strhodes client->nupdates++; 2837135446Strhodes event->ev_arg = evclient; 2838135446Strhodes 2839135446Strhodes dns_zone_gettask(zone, &zonetask); 2840135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 2841135446Strhodes 2842135446Strhodes failure: 2843135446Strhodes if (event != NULL) 2844135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 2845135446Strhodes return (result); 2846135446Strhodes} 2847