update.c revision 186462
1135446Strhodes/* 2186462Sdougb * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or 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 18186462Sdougb/* $Id: update.c,v 1.109.18.27 2008/02/07 03:16:08 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> 34170222Sdougb#include <dns/keyvalues.h> 35135446Strhodes#include <dns/message.h> 36135446Strhodes#include <dns/nsec.h> 37135446Strhodes#include <dns/rdataclass.h> 38135446Strhodes#include <dns/rdataset.h> 39135446Strhodes#include <dns/rdatasetiter.h> 40165071Sdougb#include <dns/rdatastruct.h> 41135446Strhodes#include <dns/rdatatype.h> 42135446Strhodes#include <dns/soa.h> 43135446Strhodes#include <dns/ssu.h> 44135446Strhodes#include <dns/view.h> 45135446Strhodes#include <dns/zone.h> 46135446Strhodes#include <dns/zt.h> 47135446Strhodes 48135446Strhodes#include <named/client.h> 49135446Strhodes#include <named/log.h> 50135446Strhodes#include <named/update.h> 51135446Strhodes 52170222Sdougb/*! \file 53170222Sdougb * \brief 54135446Strhodes * This module implements dynamic update as in RFC2136. 55135446Strhodes */ 56135446Strhodes 57135446Strhodes/* 58135446Strhodes XXX TODO: 59135446Strhodes - document strict minimality 60135446Strhodes*/ 61135446Strhodes 62135446Strhodes/**************************************************************************/ 63135446Strhodes 64170222Sdougb/*% 65135446Strhodes * Log level for tracing dynamic update protocol requests. 66135446Strhodes */ 67135446Strhodes#define LOGLEVEL_PROTOCOL ISC_LOG_INFO 68135446Strhodes 69170222Sdougb/*% 70135446Strhodes * Log level for low-level debug tracing. 71135446Strhodes */ 72135446Strhodes#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) 73135446Strhodes 74170222Sdougb/*% 75135446Strhodes * Check an operation for failure. These macros all assume that 76135446Strhodes * the function using them has a 'result' variable and a 'failure' 77135446Strhodes * label. 78135446Strhodes */ 79135446Strhodes#define CHECK(op) \ 80135446Strhodes do { result = (op); \ 81135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 82135446Strhodes } while (0) 83135446Strhodes 84170222Sdougb/*% 85135446Strhodes * Fail unconditionally with result 'code', which must not 86135446Strhodes * be ISC_R_SUCCESS. The reason for failure presumably has 87135446Strhodes * been logged already. 88135446Strhodes * 89135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 90135446Strhodes * from complaining about "end-of-loop code not reached". 91135446Strhodes */ 92135446Strhodes 93135446Strhodes#define FAIL(code) \ 94135446Strhodes do { \ 95135446Strhodes result = (code); \ 96135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 97135446Strhodes } while (0) 98135446Strhodes 99170222Sdougb/*% 100135446Strhodes * Fail unconditionally and log as a client error. 101135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 102135446Strhodes * from complaining about "end-of-loop code not reached". 103135446Strhodes */ 104135446Strhodes#define FAILC(code, msg) \ 105135446Strhodes do { \ 106135446Strhodes const char *_what = "failed"; \ 107135446Strhodes result = (code); \ 108135446Strhodes switch (result) { \ 109135446Strhodes case DNS_R_NXDOMAIN: \ 110135446Strhodes case DNS_R_YXDOMAIN: \ 111135446Strhodes case DNS_R_YXRRSET: \ 112135446Strhodes case DNS_R_NXRRSET: \ 113135446Strhodes _what = "unsuccessful"; \ 114135446Strhodes } \ 115135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 116135446Strhodes "update %s: %s (%s)", _what, \ 117186462Sdougb msg, isc_result_totext(result)); \ 118135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 119135446Strhodes } while (0) 120135446Strhodes 121135446Strhodes#define FAILN(code, name, msg) \ 122135446Strhodes do { \ 123135446Strhodes const char *_what = "failed"; \ 124135446Strhodes result = (code); \ 125135446Strhodes switch (result) { \ 126135446Strhodes case DNS_R_NXDOMAIN: \ 127135446Strhodes case DNS_R_YXDOMAIN: \ 128135446Strhodes case DNS_R_YXRRSET: \ 129135446Strhodes case DNS_R_NXRRSET: \ 130135446Strhodes _what = "unsuccessful"; \ 131135446Strhodes } \ 132135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 133135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 134135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 135135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 136135446Strhodes "update %s: %s: %s (%s)", _what, _nbuf, \ 137135446Strhodes msg, isc_result_totext(result)); \ 138135446Strhodes } \ 139135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 140135446Strhodes } while (0) 141135446Strhodes 142135446Strhodes#define FAILNT(code, name, type, msg) \ 143135446Strhodes do { \ 144135446Strhodes const char *_what = "failed"; \ 145135446Strhodes result = (code); \ 146135446Strhodes switch (result) { \ 147135446Strhodes case DNS_R_NXDOMAIN: \ 148135446Strhodes case DNS_R_YXDOMAIN: \ 149135446Strhodes case DNS_R_YXRRSET: \ 150135446Strhodes case DNS_R_NXRRSET: \ 151135446Strhodes _what = "unsuccessful"; \ 152135446Strhodes } \ 153135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 154135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 155135446Strhodes char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ 156135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 157135446Strhodes dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ 158135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 159135446Strhodes "update %s: %s/%s: %s (%s)", \ 160135446Strhodes _what, _nbuf, _tbuf, msg, \ 161135446Strhodes isc_result_totext(result)); \ 162135446Strhodes } \ 163135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 164135446Strhodes } while (0) 165170222Sdougb/*% 166135446Strhodes * Fail unconditionally and log as a server error. 167135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 168135446Strhodes * from complaining about "end-of-loop code not reached". 169135446Strhodes */ 170135446Strhodes#define FAILS(code, msg) \ 171135446Strhodes do { \ 172135446Strhodes result = (code); \ 173135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 174135446Strhodes "error: %s: %s", \ 175135446Strhodes msg, isc_result_totext(result)); \ 176135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 177135446Strhodes } while (0) 178135446Strhodes 179135446Strhodes/**************************************************************************/ 180135446Strhodes 181135446Strhodestypedef struct rr rr_t; 182135446Strhodes 183135446Strhodesstruct rr { 184135446Strhodes /* dns_name_t name; */ 185135446Strhodes isc_uint32_t ttl; 186135446Strhodes dns_rdata_t rdata; 187135446Strhodes}; 188135446Strhodes 189135446Strhodestypedef struct update_event update_event_t; 190135446Strhodes 191135446Strhodesstruct update_event { 192135446Strhodes ISC_EVENT_COMMON(update_event_t); 193135446Strhodes dns_zone_t *zone; 194135446Strhodes isc_result_t result; 195135446Strhodes dns_message_t *answer; 196135446Strhodes}; 197135446Strhodes 198135446Strhodes/**************************************************************************/ 199135446Strhodes/* 200135446Strhodes * Forward declarations. 201135446Strhodes */ 202135446Strhodes 203135446Strhodesstatic void update_action(isc_task_t *task, isc_event_t *event); 204135446Strhodesstatic void updatedone_action(isc_task_t *task, isc_event_t *event); 205135446Strhodesstatic isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone); 206135446Strhodesstatic void forward_done(isc_task_t *task, isc_event_t *event); 207135446Strhodes 208135446Strhodes/**************************************************************************/ 209135446Strhodes 210135446Strhodesstatic void 211135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 212135446Strhodes int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); 213135446Strhodes 214135446Strhodesstatic void 215135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 216135446Strhodes int level, const char *fmt, ...) 217135446Strhodes{ 218135446Strhodes va_list ap; 219135446Strhodes char message[4096]; 220135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 221135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 222135446Strhodes 223135446Strhodes if (client == NULL || zone == NULL) 224135446Strhodes return; 225135446Strhodes 226135446Strhodes if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE) 227135446Strhodes return; 228135446Strhodes 229135446Strhodes dns_name_format(dns_zone_getorigin(zone), namebuf, 230135446Strhodes sizeof(namebuf)); 231135446Strhodes dns_rdataclass_format(dns_zone_getclass(zone), classbuf, 232135446Strhodes sizeof(classbuf)); 233135446Strhodes 234135446Strhodes va_start(ap, fmt); 235135446Strhodes vsnprintf(message, sizeof(message), fmt, ap); 236135446Strhodes va_end(ap); 237135446Strhodes 238135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 239135446Strhodes level, "updating zone '%s/%s': %s", 240135446Strhodes namebuf, classbuf, message); 241135446Strhodes} 242135446Strhodes 243135446Strhodesstatic isc_result_t 244135446Strhodescheckupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message, 245135446Strhodes dns_name_t *zonename, isc_boolean_t slave) 246135446Strhodes{ 247135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 248135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 249135446Strhodes int level = ISC_LOG_ERROR; 250135446Strhodes const char *msg = "denied"; 251135446Strhodes isc_result_t result; 252135446Strhodes 253135446Strhodes if (slave && acl == NULL) { 254135446Strhodes result = DNS_R_NOTIMP; 255135446Strhodes level = ISC_LOG_DEBUG(3); 256135446Strhodes msg = "disabled"; 257135446Strhodes } else 258135446Strhodes result = ns_client_checkaclsilent(client, acl, ISC_FALSE); 259135446Strhodes 260135446Strhodes if (result == ISC_R_SUCCESS) { 261135446Strhodes level = ISC_LOG_DEBUG(3); 262135446Strhodes msg = "approved"; 263135446Strhodes } 264135446Strhodes 265135446Strhodes dns_name_format(zonename, namebuf, sizeof(namebuf)); 266135446Strhodes dns_rdataclass_format(client->view->rdclass, classbuf, 267135446Strhodes sizeof(classbuf)); 268135446Strhodes 269135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 270135446Strhodes NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", 271135446Strhodes message, namebuf, classbuf, msg); 272135446Strhodes return (result); 273135446Strhodes} 274135446Strhodes 275170222Sdougb/*% 276135446Strhodes * Update a single RR in version 'ver' of 'db' and log the 277135446Strhodes * update in 'diff'. 278135446Strhodes * 279135446Strhodes * Ensures: 280170222Sdougb * \li '*tuple' == NULL. Either the tuple is freed, or its 281135446Strhodes * ownership has been transferred to the diff. 282135446Strhodes */ 283135446Strhodesstatic isc_result_t 284135446Strhodesdo_one_tuple(dns_difftuple_t **tuple, 285135446Strhodes dns_db_t *db, dns_dbversion_t *ver, 286135446Strhodes dns_diff_t *diff) 287135446Strhodes{ 288135446Strhodes dns_diff_t temp_diff; 289135446Strhodes isc_result_t result; 290135446Strhodes 291135446Strhodes /* 292135446Strhodes * Create a singleton diff. 293135446Strhodes */ 294135446Strhodes dns_diff_init(diff->mctx, &temp_diff); 295135446Strhodes ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); 296135446Strhodes 297135446Strhodes /* 298135446Strhodes * Apply it to the database. 299135446Strhodes */ 300135446Strhodes result = dns_diff_apply(&temp_diff, db, ver); 301135446Strhodes ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); 302135446Strhodes if (result != ISC_R_SUCCESS) { 303135446Strhodes dns_difftuple_free(tuple); 304135446Strhodes return (result); 305135446Strhodes } 306135446Strhodes 307135446Strhodes /* 308135446Strhodes * Merge it into the current pending journal entry. 309135446Strhodes */ 310135446Strhodes dns_diff_appendminimal(diff, tuple); 311135446Strhodes 312135446Strhodes /* 313135446Strhodes * Do not clear temp_diff. 314135446Strhodes */ 315135446Strhodes return (ISC_R_SUCCESS); 316135446Strhodes} 317135446Strhodes 318170222Sdougb/*% 319135446Strhodes * Perform the updates in 'updates' in version 'ver' of 'db' and log the 320135446Strhodes * update in 'diff'. 321135446Strhodes * 322135446Strhodes * Ensures: 323170222Sdougb * \li 'updates' is empty. 324135446Strhodes */ 325135446Strhodesstatic isc_result_t 326135446Strhodesdo_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver, 327135446Strhodes dns_diff_t *diff) 328135446Strhodes{ 329135446Strhodes isc_result_t result; 330135446Strhodes while (! ISC_LIST_EMPTY(updates->tuples)) { 331135446Strhodes dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples); 332135446Strhodes ISC_LIST_UNLINK(updates->tuples, t, link); 333135446Strhodes CHECK(do_one_tuple(&t, db, ver, diff)); 334135446Strhodes } 335135446Strhodes return (ISC_R_SUCCESS); 336135446Strhodes 337135446Strhodes failure: 338135446Strhodes dns_diff_clear(diff); 339135446Strhodes return (result); 340135446Strhodes} 341135446Strhodes 342135446Strhodesstatic isc_result_t 343135446Strhodesupdate_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, 344135446Strhodes dns_diffop_t op, dns_name_t *name, 345135446Strhodes dns_ttl_t ttl, dns_rdata_t *rdata) 346135446Strhodes{ 347135446Strhodes dns_difftuple_t *tuple = NULL; 348135446Strhodes isc_result_t result; 349135446Strhodes result = dns_difftuple_create(diff->mctx, op, 350135446Strhodes name, ttl, rdata, &tuple); 351135446Strhodes if (result != ISC_R_SUCCESS) 352135446Strhodes return (result); 353135446Strhodes return (do_one_tuple(&tuple, db, ver, diff)); 354135446Strhodes} 355135446Strhodes 356135446Strhodes/**************************************************************************/ 357135446Strhodes/* 358135446Strhodes * Callback-style iteration over rdatasets and rdatas. 359135446Strhodes * 360135446Strhodes * foreach_rrset() can be used to iterate over the RRsets 361135446Strhodes * of a name and call a callback function with each 362135446Strhodes * one. Similarly, foreach_rr() can be used to iterate 363135446Strhodes * over the individual RRs at name, optionally restricted 364135446Strhodes * to RRs of a given type. 365135446Strhodes * 366135446Strhodes * The callback functions are called "actions" and take 367135446Strhodes * two arguments: a void pointer for passing arbitrary 368135446Strhodes * context information, and a pointer to the current RRset 369135446Strhodes * or RR. By convention, their names end in "_action". 370135446Strhodes */ 371135446Strhodes 372135446Strhodes/* 373135446Strhodes * XXXRTH We might want to make this public somewhere in libdns. 374135446Strhodes */ 375135446Strhodes 376170222Sdougb/*% 377135446Strhodes * Function type for foreach_rrset() iterator actions. 378135446Strhodes */ 379135446Strhodestypedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); 380135446Strhodes 381170222Sdougb/*% 382135446Strhodes * Function type for foreach_rr() iterator actions. 383135446Strhodes */ 384135446Strhodestypedef isc_result_t rr_func(void *data, rr_t *rr); 385135446Strhodes 386170222Sdougb/*% 387135446Strhodes * Internal context struct for foreach_node_rr(). 388135446Strhodes */ 389135446Strhodestypedef struct { 390135446Strhodes rr_func * rr_action; 391135446Strhodes void * rr_action_data; 392135446Strhodes} foreach_node_rr_ctx_t; 393135446Strhodes 394170222Sdougb/*% 395135446Strhodes * Internal helper function for foreach_node_rr(). 396135446Strhodes */ 397135446Strhodesstatic isc_result_t 398135446Strhodesforeach_node_rr_action(void *data, dns_rdataset_t *rdataset) { 399135446Strhodes isc_result_t result; 400135446Strhodes foreach_node_rr_ctx_t *ctx = data; 401135446Strhodes for (result = dns_rdataset_first(rdataset); 402135446Strhodes result == ISC_R_SUCCESS; 403135446Strhodes result = dns_rdataset_next(rdataset)) 404135446Strhodes { 405135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 406186462Sdougb 407135446Strhodes dns_rdataset_current(rdataset, &rr.rdata); 408135446Strhodes rr.ttl = rdataset->ttl; 409135446Strhodes result = (*ctx->rr_action)(ctx->rr_action_data, &rr); 410135446Strhodes if (result != ISC_R_SUCCESS) 411135446Strhodes return (result); 412135446Strhodes } 413135446Strhodes if (result != ISC_R_NOMORE) 414135446Strhodes return (result); 415135446Strhodes return (ISC_R_SUCCESS); 416135446Strhodes} 417135446Strhodes 418170222Sdougb/*% 419135446Strhodes * For each rdataset of 'name' in 'ver' of 'db', call 'action' 420135446Strhodes * with the rdataset and 'action_data' as arguments. If the name 421135446Strhodes * does not exist, do nothing. 422135446Strhodes * 423135446Strhodes * If 'action' returns an error, abort iteration and return the error. 424135446Strhodes */ 425135446Strhodesstatic isc_result_t 426135446Strhodesforeach_rrset(dns_db_t *db, 427135446Strhodes dns_dbversion_t *ver, 428135446Strhodes dns_name_t *name, 429135446Strhodes rrset_func *action, 430135446Strhodes void *action_data) 431135446Strhodes{ 432135446Strhodes isc_result_t result; 433135446Strhodes dns_dbnode_t *node; 434135446Strhodes dns_rdatasetiter_t *iter; 435135446Strhodes 436135446Strhodes node = NULL; 437135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 438135446Strhodes if (result == ISC_R_NOTFOUND) 439135446Strhodes return (ISC_R_SUCCESS); 440135446Strhodes if (result != ISC_R_SUCCESS) 441135446Strhodes return (result); 442135446Strhodes 443135446Strhodes iter = NULL; 444135446Strhodes result = dns_db_allrdatasets(db, node, ver, 445135446Strhodes (isc_stdtime_t) 0, &iter); 446135446Strhodes if (result != ISC_R_SUCCESS) 447135446Strhodes goto cleanup_node; 448135446Strhodes 449135446Strhodes for (result = dns_rdatasetiter_first(iter); 450135446Strhodes result == ISC_R_SUCCESS; 451135446Strhodes result = dns_rdatasetiter_next(iter)) 452135446Strhodes { 453135446Strhodes dns_rdataset_t rdataset; 454135446Strhodes 455135446Strhodes dns_rdataset_init(&rdataset); 456135446Strhodes dns_rdatasetiter_current(iter, &rdataset); 457135446Strhodes 458135446Strhodes result = (*action)(action_data, &rdataset); 459135446Strhodes 460135446Strhodes dns_rdataset_disassociate(&rdataset); 461135446Strhodes if (result != ISC_R_SUCCESS) 462135446Strhodes goto cleanup_iterator; 463135446Strhodes } 464135446Strhodes if (result == ISC_R_NOMORE) 465135446Strhodes result = ISC_R_SUCCESS; 466135446Strhodes 467135446Strhodes cleanup_iterator: 468135446Strhodes dns_rdatasetiter_destroy(&iter); 469135446Strhodes 470135446Strhodes cleanup_node: 471135446Strhodes dns_db_detachnode(db, &node); 472135446Strhodes 473135446Strhodes return (result); 474135446Strhodes} 475135446Strhodes 476170222Sdougb/*% 477135446Strhodes * For each RR of 'name' in 'ver' of 'db', call 'action' 478135446Strhodes * with the RR and 'action_data' as arguments. If the name 479135446Strhodes * does not exist, do nothing. 480135446Strhodes * 481135446Strhodes * If 'action' returns an error, abort iteration 482135446Strhodes * and return the error. 483135446Strhodes */ 484135446Strhodesstatic isc_result_t 485135446Strhodesforeach_node_rr(dns_db_t *db, 486135446Strhodes dns_dbversion_t *ver, 487135446Strhodes dns_name_t *name, 488135446Strhodes rr_func *rr_action, 489135446Strhodes void *rr_action_data) 490135446Strhodes{ 491135446Strhodes foreach_node_rr_ctx_t ctx; 492135446Strhodes ctx.rr_action = rr_action; 493135446Strhodes ctx.rr_action_data = rr_action_data; 494135446Strhodes return (foreach_rrset(db, ver, name, 495135446Strhodes foreach_node_rr_action, &ctx)); 496135446Strhodes} 497135446Strhodes 498135446Strhodes 499170222Sdougb/*% 500135446Strhodes * For each of the RRs specified by 'db', 'ver', 'name', 'type', 501135446Strhodes * (which can be dns_rdatatype_any to match any type), and 'covers', call 502135446Strhodes * 'action' with the RR and 'action_data' as arguments. If the name 503135446Strhodes * does not exist, or if no RRset of the given type exists at the name, 504135446Strhodes * do nothing. 505135446Strhodes * 506135446Strhodes * If 'action' returns an error, abort iteration and return the error. 507135446Strhodes */ 508135446Strhodesstatic isc_result_t 509135446Strhodesforeach_rr(dns_db_t *db, 510135446Strhodes dns_dbversion_t *ver, 511135446Strhodes dns_name_t *name, 512135446Strhodes dns_rdatatype_t type, 513135446Strhodes dns_rdatatype_t covers, 514135446Strhodes rr_func *rr_action, 515135446Strhodes void *rr_action_data) 516135446Strhodes{ 517135446Strhodes 518135446Strhodes isc_result_t result; 519135446Strhodes dns_dbnode_t *node; 520135446Strhodes dns_rdataset_t rdataset; 521135446Strhodes 522135446Strhodes if (type == dns_rdatatype_any) 523135446Strhodes return (foreach_node_rr(db, ver, name, 524135446Strhodes rr_action, rr_action_data)); 525135446Strhodes 526135446Strhodes node = NULL; 527135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 528135446Strhodes if (result == ISC_R_NOTFOUND) 529135446Strhodes return (ISC_R_SUCCESS); 530135446Strhodes if (result != ISC_R_SUCCESS) 531135446Strhodes return (result); 532135446Strhodes 533135446Strhodes dns_rdataset_init(&rdataset); 534135446Strhodes result = dns_db_findrdataset(db, node, ver, type, covers, 535135446Strhodes (isc_stdtime_t) 0, &rdataset, NULL); 536135446Strhodes if (result == ISC_R_NOTFOUND) { 537135446Strhodes result = ISC_R_SUCCESS; 538135446Strhodes goto cleanup_node; 539135446Strhodes } 540135446Strhodes if (result != ISC_R_SUCCESS) 541135446Strhodes goto cleanup_node; 542135446Strhodes 543135446Strhodes for (result = dns_rdataset_first(&rdataset); 544135446Strhodes result == ISC_R_SUCCESS; 545135446Strhodes result = dns_rdataset_next(&rdataset)) 546135446Strhodes { 547135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 548135446Strhodes dns_rdataset_current(&rdataset, &rr.rdata); 549135446Strhodes rr.ttl = rdataset.ttl; 550135446Strhodes result = (*rr_action)(rr_action_data, &rr); 551135446Strhodes if (result != ISC_R_SUCCESS) 552135446Strhodes goto cleanup_rdataset; 553135446Strhodes } 554135446Strhodes if (result != ISC_R_NOMORE) 555135446Strhodes goto cleanup_rdataset; 556135446Strhodes result = ISC_R_SUCCESS; 557135446Strhodes 558135446Strhodes cleanup_rdataset: 559135446Strhodes dns_rdataset_disassociate(&rdataset); 560135446Strhodes cleanup_node: 561135446Strhodes dns_db_detachnode(db, &node); 562135446Strhodes 563135446Strhodes return (result); 564135446Strhodes} 565135446Strhodes 566135446Strhodes/**************************************************************************/ 567135446Strhodes/* 568135446Strhodes * Various tests on the database contents (for prerequisites, etc). 569135446Strhodes */ 570135446Strhodes 571170222Sdougb/*% 572135446Strhodes * Function type for predicate functions that compare a database RR 'db_rr' 573135446Strhodes * against an update RR 'update_rr'. 574135446Strhodes */ 575135446Strhodestypedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); 576135446Strhodes 577170222Sdougb/*% 578135446Strhodes * Helper function for rrset_exists(). 579135446Strhodes */ 580135446Strhodesstatic isc_result_t 581135446Strhodesrrset_exists_action(void *data, rr_t *rr) { 582135446Strhodes UNUSED(data); 583135446Strhodes UNUSED(rr); 584135446Strhodes return (ISC_R_EXISTS); 585135446Strhodes} 586135446Strhodes 587170222Sdougb/*% 588135446Strhodes * Utility macro for RR existence checking functions. 589135446Strhodes * 590135446Strhodes * If the variable 'result' has the value ISC_R_EXISTS or 591135446Strhodes * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, 592135446Strhodes * respectively, and return success. 593135446Strhodes * 594135446Strhodes * If 'result' has any other value, there was a failure. 595135446Strhodes * Return the failure result code and do not set *exists. 596135446Strhodes * 597135446Strhodes * This would be more readable as "do { if ... } while(0)", 598135446Strhodes * but that form generates tons of warnings on Solaris 2.6. 599135446Strhodes */ 600135446Strhodes#define RETURN_EXISTENCE_FLAG \ 601135446Strhodes return ((result == ISC_R_EXISTS) ? \ 602135446Strhodes (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ 603135446Strhodes ((result == ISC_R_SUCCESS) ? \ 604135446Strhodes (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ 605135446Strhodes result)) 606135446Strhodes 607170222Sdougb/*% 608135446Strhodes * Set '*exists' to true iff an rrset of the given type exists, 609135446Strhodes * to false otherwise. 610135446Strhodes */ 611135446Strhodesstatic isc_result_t 612135446Strhodesrrset_exists(dns_db_t *db, dns_dbversion_t *ver, 613135446Strhodes dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, 614135446Strhodes isc_boolean_t *exists) 615135446Strhodes{ 616135446Strhodes isc_result_t result; 617135446Strhodes result = foreach_rr(db, ver, name, type, covers, 618135446Strhodes rrset_exists_action, NULL); 619135446Strhodes RETURN_EXISTENCE_FLAG; 620135446Strhodes} 621135446Strhodes 622170222Sdougb/*% 623135446Strhodes * Helper function for cname_incompatible_rrset_exists. 624135446Strhodes */ 625135446Strhodesstatic isc_result_t 626135446Strhodescname_compatibility_action(void *data, dns_rdataset_t *rrset) { 627135446Strhodes UNUSED(data); 628135446Strhodes if (rrset->type != dns_rdatatype_cname && 629135446Strhodes ! dns_rdatatype_isdnssec(rrset->type)) 630135446Strhodes return (ISC_R_EXISTS); 631135446Strhodes return (ISC_R_SUCCESS); 632135446Strhodes} 633135446Strhodes 634170222Sdougb/*% 635135446Strhodes * Check whether there is an rrset incompatible with adding a CNAME RR, 636135446Strhodes * i.e., anything but another CNAME (which can be replaced) or a 637135446Strhodes * DNSSEC RR (which can coexist). 638135446Strhodes * 639135446Strhodes * If such an incompatible rrset exists, set '*exists' to ISC_TRUE. 640135446Strhodes * Otherwise, set it to ISC_FALSE. 641135446Strhodes */ 642135446Strhodesstatic isc_result_t 643135446Strhodescname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, 644135446Strhodes dns_name_t *name, isc_boolean_t *exists) { 645135446Strhodes isc_result_t result; 646135446Strhodes result = foreach_rrset(db, ver, name, 647135446Strhodes cname_compatibility_action, NULL); 648135446Strhodes RETURN_EXISTENCE_FLAG; 649135446Strhodes} 650135446Strhodes 651170222Sdougb/*% 652135446Strhodes * Helper function for rr_count(). 653135446Strhodes */ 654135446Strhodesstatic isc_result_t 655135446Strhodescount_rr_action(void *data, rr_t *rr) { 656135446Strhodes int *countp = data; 657135446Strhodes UNUSED(rr); 658135446Strhodes (*countp)++; 659135446Strhodes return (ISC_R_SUCCESS); 660135446Strhodes} 661135446Strhodes 662170222Sdougb/*% 663135446Strhodes * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'. 664135446Strhodes */ 665135446Strhodesstatic isc_result_t 666135446Strhodesrr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 667135446Strhodes dns_rdatatype_t type, dns_rdatatype_t covers, int *countp) 668135446Strhodes{ 669135446Strhodes *countp = 0; 670135446Strhodes return (foreach_rr(db, ver, name, type, covers, 671135446Strhodes count_rr_action, countp)); 672135446Strhodes} 673135446Strhodes 674170222Sdougb/*% 675135446Strhodes * Context struct and helper function for name_exists(). 676135446Strhodes */ 677135446Strhodes 678135446Strhodesstatic isc_result_t 679135446Strhodesname_exists_action(void *data, dns_rdataset_t *rrset) { 680135446Strhodes UNUSED(data); 681135446Strhodes UNUSED(rrset); 682135446Strhodes return (ISC_R_EXISTS); 683135446Strhodes} 684135446Strhodes 685170222Sdougb/*% 686135446Strhodes * Set '*exists' to true iff the given name exists, to false otherwise. 687135446Strhodes */ 688135446Strhodesstatic isc_result_t 689135446Strhodesname_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 690135446Strhodes isc_boolean_t *exists) 691135446Strhodes{ 692135446Strhodes isc_result_t result; 693135446Strhodes result = foreach_rrset(db, ver, name, 694135446Strhodes name_exists_action, NULL); 695135446Strhodes RETURN_EXISTENCE_FLAG; 696135446Strhodes} 697135446Strhodes 698135446Strhodestypedef struct { 699135446Strhodes dns_name_t *name, *signer; 700135446Strhodes dns_ssutable_t *table; 701135446Strhodes} ssu_check_t; 702135446Strhodes 703135446Strhodesstatic isc_result_t 704135446Strhodesssu_checkrule(void *data, dns_rdataset_t *rrset) { 705135446Strhodes ssu_check_t *ssuinfo = data; 706135446Strhodes isc_boolean_t result; 707135446Strhodes 708135446Strhodes /* 709135446Strhodes * If we're deleting all records, it's ok to delete RRSIG and NSEC even 710135446Strhodes * if we're normally not allowed to. 711135446Strhodes */ 712135446Strhodes if (rrset->type == dns_rdatatype_rrsig || 713135446Strhodes rrset->type == dns_rdatatype_nsec) 714143731Sdougb return (ISC_R_SUCCESS); 715135446Strhodes result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer, 716135446Strhodes ssuinfo->name, rrset->type); 717135446Strhodes return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE); 718135446Strhodes} 719135446Strhodes 720135446Strhodesstatic isc_boolean_t 721135446Strhodesssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 722135446Strhodes dns_ssutable_t *ssutable, dns_name_t *signer) 723135446Strhodes{ 724135446Strhodes isc_result_t result; 725135446Strhodes ssu_check_t ssuinfo; 726135446Strhodes 727135446Strhodes ssuinfo.name = name; 728135446Strhodes ssuinfo.table = ssutable; 729135446Strhodes ssuinfo.signer = signer; 730135446Strhodes result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo); 731135446Strhodes return (ISC_TF(result == ISC_R_SUCCESS)); 732135446Strhodes} 733135446Strhodes 734135446Strhodes/**************************************************************************/ 735135446Strhodes/* 736135446Strhodes * Checking of "RRset exists (value dependent)" prerequisites. 737135446Strhodes * 738135446Strhodes * In the RFC2136 section 3.2.5, this is the pseudocode involving 739135446Strhodes * a variable called "temp", a mapping of <name, type> tuples to rrsets. 740135446Strhodes * 741135446Strhodes * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t" 742135446Strhodes * where each typle has op==DNS_DIFFOP_EXISTS. 743135446Strhodes */ 744135446Strhodes 745135446Strhodes 746170222Sdougb/*% 747135446Strhodes * Append a tuple asserting the existence of the RR with 748135446Strhodes * 'name' and 'rdata' to 'diff'. 749135446Strhodes */ 750135446Strhodesstatic isc_result_t 751135446Strhodestemp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) { 752135446Strhodes isc_result_t result; 753135446Strhodes dns_difftuple_t *tuple = NULL; 754135446Strhodes 755135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 756135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, 757135446Strhodes name, 0, rdata, &tuple)); 758135446Strhodes ISC_LIST_APPEND(diff->tuples, tuple, link); 759135446Strhodes failure: 760135446Strhodes return (result); 761135446Strhodes} 762135446Strhodes 763170222Sdougb/*% 764135446Strhodes * Compare two rdatasets represented as sorted lists of tuples. 765135446Strhodes * All list elements must have the same owner name and type. 766135446Strhodes * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset) 767135446Strhodes * if not. 768135446Strhodes */ 769135446Strhodesstatic isc_result_t 770135446Strhodestemp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) { 771135446Strhodes for (;;) { 772135446Strhodes if (a == NULL || b == NULL) 773135446Strhodes break; 774135446Strhodes INSIST(a->op == DNS_DIFFOP_EXISTS && 775135446Strhodes b->op == DNS_DIFFOP_EXISTS); 776135446Strhodes INSIST(a->rdata.type == b->rdata.type); 777135446Strhodes INSIST(dns_name_equal(&a->name, &b->name)); 778135446Strhodes if (dns_rdata_compare(&a->rdata, &b->rdata) != 0) 779135446Strhodes return (DNS_R_NXRRSET); 780135446Strhodes a = ISC_LIST_NEXT(a, link); 781135446Strhodes b = ISC_LIST_NEXT(b, link); 782135446Strhodes } 783135446Strhodes if (a != NULL || b != NULL) 784135446Strhodes return (DNS_R_NXRRSET); 785135446Strhodes return (ISC_R_SUCCESS); 786135446Strhodes} 787135446Strhodes 788170222Sdougb/*% 789135446Strhodes * A comparison function defining the sorting order for the entries 790135446Strhodes * in the "temp" data structure. The major sort key is the owner name, 791135446Strhodes * followed by the type and rdata. 792135446Strhodes */ 793135446Strhodesstatic int 794135446Strhodestemp_order(const void *av, const void *bv) { 795135446Strhodes dns_difftuple_t const * const *ap = av; 796135446Strhodes dns_difftuple_t const * const *bp = bv; 797135446Strhodes dns_difftuple_t const *a = *ap; 798135446Strhodes dns_difftuple_t const *b = *bp; 799135446Strhodes int r; 800135446Strhodes r = dns_name_compare(&a->name, &b->name); 801135446Strhodes if (r != 0) 802135446Strhodes return (r); 803135446Strhodes r = (b->rdata.type - a->rdata.type); 804135446Strhodes if (r != 0) 805135446Strhodes return (r); 806135446Strhodes r = dns_rdata_compare(&a->rdata, &b->rdata); 807135446Strhodes return (r); 808135446Strhodes} 809135446Strhodes 810170222Sdougb/*% 811135446Strhodes * Check the "RRset exists (value dependent)" prerequisite information 812135446Strhodes * in 'temp' against the contents of the database 'db'. 813135446Strhodes * 814135446Strhodes * Return ISC_R_SUCCESS if the prerequisites are satisfied, 815135446Strhodes * rcode(dns_rcode_nxrrset) if not. 816135446Strhodes * 817135446Strhodes * 'temp' must be pre-sorted. 818135446Strhodes */ 819135446Strhodes 820135446Strhodesstatic isc_result_t 821135446Strhodestemp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db, 822135446Strhodes dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep) 823135446Strhodes{ 824135446Strhodes isc_result_t result; 825135446Strhodes dns_name_t *name; 826135446Strhodes dns_dbnode_t *node; 827135446Strhodes dns_difftuple_t *t; 828135446Strhodes dns_diff_t trash; 829135446Strhodes 830135446Strhodes dns_diff_init(mctx, &trash); 831135446Strhodes 832135446Strhodes /* 833135446Strhodes * For each name and type in the prerequisites, 834135446Strhodes * construct a sorted rdata list of the corresponding 835135446Strhodes * database contents, and compare the lists. 836135446Strhodes */ 837135446Strhodes t = ISC_LIST_HEAD(temp->tuples); 838135446Strhodes while (t != NULL) { 839135446Strhodes name = &t->name; 840135446Strhodes (void)dns_name_copy(name, tmpname, NULL); 841135446Strhodes *typep = t->rdata.type; 842135446Strhodes 843135446Strhodes /* A new unique name begins here. */ 844135446Strhodes node = NULL; 845135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 846186462Sdougb if (result == ISC_R_NOTFOUND) { 847186462Sdougb dns_diff_clear(&trash); 848135446Strhodes return (DNS_R_NXRRSET); 849186462Sdougb } 850186462Sdougb if (result != ISC_R_SUCCESS) { 851186462Sdougb dns_diff_clear(&trash); 852135446Strhodes return (result); 853186462Sdougb } 854135446Strhodes 855135446Strhodes /* A new unique type begins here. */ 856135446Strhodes while (t != NULL && dns_name_equal(&t->name, name)) { 857135446Strhodes dns_rdatatype_t type, covers; 858135446Strhodes dns_rdataset_t rdataset; 859135446Strhodes dns_diff_t d_rrs; /* Database RRs with 860135446Strhodes this name and type */ 861186462Sdougb dns_diff_t u_rrs; /* Update RRs with 862135446Strhodes this name and type */ 863135446Strhodes 864135446Strhodes *typep = type = t->rdata.type; 865135446Strhodes if (type == dns_rdatatype_rrsig || 866135446Strhodes type == dns_rdatatype_sig) 867135446Strhodes covers = dns_rdata_covers(&t->rdata); 868135446Strhodes else 869135446Strhodes covers = 0; 870135446Strhodes 871135446Strhodes /* 872135446Strhodes * Collect all database RRs for this name and type 873135446Strhodes * onto d_rrs and sort them. 874135446Strhodes */ 875135446Strhodes dns_rdataset_init(&rdataset); 876135446Strhodes result = dns_db_findrdataset(db, node, ver, type, 877135446Strhodes covers, (isc_stdtime_t) 0, 878135446Strhodes &rdataset, NULL); 879135446Strhodes if (result != ISC_R_SUCCESS) { 880135446Strhodes dns_db_detachnode(db, &node); 881186462Sdougb dns_diff_clear(&trash); 882135446Strhodes return (DNS_R_NXRRSET); 883135446Strhodes } 884135446Strhodes 885135446Strhodes dns_diff_init(mctx, &d_rrs); 886135446Strhodes dns_diff_init(mctx, &u_rrs); 887135446Strhodes 888135446Strhodes for (result = dns_rdataset_first(&rdataset); 889135446Strhodes result == ISC_R_SUCCESS; 890135446Strhodes result = dns_rdataset_next(&rdataset)) 891135446Strhodes { 892135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 893135446Strhodes dns_rdataset_current(&rdataset, &rdata); 894135446Strhodes result = temp_append(&d_rrs, name, &rdata); 895135446Strhodes if (result != ISC_R_SUCCESS) 896135446Strhodes goto failure; 897135446Strhodes } 898135446Strhodes if (result != ISC_R_NOMORE) 899135446Strhodes goto failure; 900135446Strhodes result = dns_diff_sort(&d_rrs, temp_order); 901135446Strhodes if (result != ISC_R_SUCCESS) 902135446Strhodes goto failure; 903135446Strhodes 904135446Strhodes /* 905135446Strhodes * Collect all update RRs for this name and type 906135446Strhodes * onto u_rrs. No need to sort them here - 907135446Strhodes * they are already sorted. 908135446Strhodes */ 909135446Strhodes while (t != NULL && 910135446Strhodes dns_name_equal(&t->name, name) && 911135446Strhodes t->rdata.type == type) 912135446Strhodes { 913135446Strhodes dns_difftuple_t *next = 914135446Strhodes ISC_LIST_NEXT(t, link); 915135446Strhodes ISC_LIST_UNLINK(temp->tuples, t, link); 916135446Strhodes ISC_LIST_APPEND(u_rrs.tuples, t, link); 917135446Strhodes t = next; 918135446Strhodes } 919135446Strhodes 920135446Strhodes /* Compare the two sorted lists. */ 921135446Strhodes result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples), 922135446Strhodes ISC_LIST_HEAD(d_rrs.tuples)); 923135446Strhodes if (result != ISC_R_SUCCESS) 924135446Strhodes goto failure; 925135446Strhodes 926135446Strhodes /* 927135446Strhodes * We are done with the tuples, but we can't free 928135446Strhodes * them yet because "name" still points into one 929135446Strhodes * of them. Move them on a temporary list. 930135446Strhodes */ 931135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link); 932135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link); 933135446Strhodes dns_rdataset_disassociate(&rdataset); 934135446Strhodes 935135446Strhodes continue; 936135446Strhodes 937135446Strhodes failure: 938135446Strhodes dns_diff_clear(&d_rrs); 939135446Strhodes dns_diff_clear(&u_rrs); 940135446Strhodes dns_diff_clear(&trash); 941135446Strhodes dns_rdataset_disassociate(&rdataset); 942135446Strhodes dns_db_detachnode(db, &node); 943135446Strhodes return (result); 944135446Strhodes } 945135446Strhodes 946135446Strhodes dns_db_detachnode(db, &node); 947135446Strhodes } 948135446Strhodes 949135446Strhodes dns_diff_clear(&trash); 950135446Strhodes return (ISC_R_SUCCESS); 951135446Strhodes} 952135446Strhodes 953135446Strhodes/**************************************************************************/ 954135446Strhodes/* 955135446Strhodes * Conditional deletion of RRs. 956135446Strhodes */ 957135446Strhodes 958170222Sdougb/*% 959135446Strhodes * Context structure for delete_if(). 960135446Strhodes */ 961135446Strhodes 962135446Strhodestypedef struct { 963135446Strhodes rr_predicate *predicate; 964135446Strhodes dns_db_t *db; 965135446Strhodes dns_dbversion_t *ver; 966135446Strhodes dns_diff_t *diff; 967135446Strhodes dns_name_t *name; 968135446Strhodes dns_rdata_t *update_rr; 969135446Strhodes} conditional_delete_ctx_t; 970135446Strhodes 971170222Sdougb/*% 972135446Strhodes * Predicate functions for delete_if(). 973135446Strhodes */ 974135446Strhodes 975170222Sdougb/*% 976143731Sdougb * Return true iff 'db_rr' is neither a SOA nor an NS RR nor 977143731Sdougb * an RRSIG nor a NSEC. 978135446Strhodes */ 979135446Strhodesstatic isc_boolean_t 980135446Strhodestype_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 981135446Strhodes UNUSED(update_rr); 982135446Strhodes return ((db_rr->type != dns_rdatatype_soa && 983143731Sdougb db_rr->type != dns_rdatatype_ns && 984143731Sdougb db_rr->type != dns_rdatatype_rrsig && 985143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 986135446Strhodes ISC_TRUE : ISC_FALSE); 987135446Strhodes} 988135446Strhodes 989170222Sdougb/*% 990143731Sdougb * Return true iff 'db_rr' is neither a RRSIG nor a NSEC. 991143731Sdougb */ 992143731Sdougbstatic isc_boolean_t 993143731Sdougbtype_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 994143731Sdougb UNUSED(update_rr); 995143731Sdougb return ((db_rr->type != dns_rdatatype_rrsig && 996143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 997143731Sdougb ISC_TRUE : ISC_FALSE); 998143731Sdougb} 999143731Sdougb 1000170222Sdougb/*% 1001135446Strhodes * Return true always. 1002135446Strhodes */ 1003135446Strhodesstatic isc_boolean_t 1004135446Strhodestrue_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1005135446Strhodes UNUSED(update_rr); 1006135446Strhodes UNUSED(db_rr); 1007135446Strhodes return (ISC_TRUE); 1008135446Strhodes} 1009135446Strhodes 1010170222Sdougb/*% 1011135446Strhodes * Return true iff the two RRs have identical rdata. 1012135446Strhodes */ 1013135446Strhodesstatic isc_boolean_t 1014135446Strhodesrr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1015135446Strhodes /* 1016135446Strhodes * XXXRTH This is not a problem, but we should consider creating 1017135446Strhodes * dns_rdata_equal() (that used dns_name_equal()), since it 1018135446Strhodes * would be faster. Not a priority. 1019135446Strhodes */ 1020135446Strhodes return (dns_rdata_compare(update_rr, db_rr) == 0 ? 1021135446Strhodes ISC_TRUE : ISC_FALSE); 1022135446Strhodes} 1023135446Strhodes 1024170222Sdougb/*% 1025135446Strhodes * Return true iff 'update_rr' should replace 'db_rr' according 1026135446Strhodes * to the special RFC2136 rules for CNAME, SOA, and WKS records. 1027135446Strhodes * 1028135446Strhodes * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs 1029135446Strhodes * make little sense, so we replace those, too. 1030135446Strhodes */ 1031135446Strhodesstatic isc_boolean_t 1032135446Strhodesreplaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1033135446Strhodes if (db_rr->type != update_rr->type) 1034135446Strhodes return (ISC_FALSE); 1035135446Strhodes if (db_rr->type == dns_rdatatype_cname) 1036135446Strhodes return (ISC_TRUE); 1037135446Strhodes if (db_rr->type == dns_rdatatype_dname) 1038135446Strhodes return (ISC_TRUE); 1039135446Strhodes if (db_rr->type == dns_rdatatype_soa) 1040135446Strhodes return (ISC_TRUE); 1041135446Strhodes if (db_rr->type == dns_rdatatype_nsec) 1042135446Strhodes return (ISC_TRUE); 1043135446Strhodes if (db_rr->type == dns_rdatatype_wks) { 1044135446Strhodes /* 1045135446Strhodes * Compare the address and protocol fields only. These 1046135446Strhodes * form the first five bytes of the RR data. Do a 1047135446Strhodes * raw binary comparison; unpacking the WKS RRs using 1048135446Strhodes * dns_rdata_tostruct() might be cleaner in some ways, 1049135446Strhodes * but it would require us to pass around an mctx. 1050135446Strhodes */ 1051135446Strhodes INSIST(db_rr->length >= 5 && update_rr->length >= 5); 1052135446Strhodes return (memcmp(db_rr->data, update_rr->data, 5) == 0 ? 1053135446Strhodes ISC_TRUE : ISC_FALSE); 1054135446Strhodes } 1055135446Strhodes return (ISC_FALSE); 1056135446Strhodes} 1057135446Strhodes 1058170222Sdougb/*% 1059135446Strhodes * Internal helper function for delete_if(). 1060135446Strhodes */ 1061135446Strhodesstatic isc_result_t 1062135446Strhodesdelete_if_action(void *data, rr_t *rr) { 1063135446Strhodes conditional_delete_ctx_t *ctx = data; 1064135446Strhodes if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { 1065135446Strhodes isc_result_t result; 1066135446Strhodes result = update_one_rr(ctx->db, ctx->ver, ctx->diff, 1067135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1068135446Strhodes rr->ttl, &rr->rdata); 1069135446Strhodes return (result); 1070135446Strhodes } else { 1071135446Strhodes return (ISC_R_SUCCESS); 1072135446Strhodes } 1073135446Strhodes} 1074135446Strhodes 1075170222Sdougb/*% 1076135446Strhodes * Conditionally delete RRs. Apply 'predicate' to the RRs 1077135446Strhodes * specified by 'db', 'ver', 'name', and 'type' (which can 1078135446Strhodes * be dns_rdatatype_any to match any type). Delete those 1079135446Strhodes * RRs for which the predicate returns true, and log the 1080135446Strhodes * deletions in 'diff'. 1081135446Strhodes */ 1082135446Strhodesstatic isc_result_t 1083135446Strhodesdelete_if(rr_predicate *predicate, 1084135446Strhodes dns_db_t *db, 1085135446Strhodes dns_dbversion_t *ver, 1086135446Strhodes dns_name_t *name, 1087135446Strhodes dns_rdatatype_t type, 1088135446Strhodes dns_rdatatype_t covers, 1089135446Strhodes dns_rdata_t *update_rr, 1090135446Strhodes dns_diff_t *diff) 1091135446Strhodes{ 1092135446Strhodes conditional_delete_ctx_t ctx; 1093135446Strhodes ctx.predicate = predicate; 1094135446Strhodes ctx.db = db; 1095135446Strhodes ctx.ver = ver; 1096135446Strhodes ctx.diff = diff; 1097135446Strhodes ctx.name = name; 1098135446Strhodes ctx.update_rr = update_rr; 1099135446Strhodes return (foreach_rr(db, ver, name, type, covers, 1100135446Strhodes delete_if_action, &ctx)); 1101135446Strhodes} 1102135446Strhodes 1103135446Strhodes/**************************************************************************/ 1104170222Sdougb/*% 1105135446Strhodes * Prepare an RR for the addition of the new RR 'ctx->update_rr', 1106135446Strhodes * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting 1107135446Strhodes * the RRs if it is replaced by the new RR or has a conflicting TTL. 1108135446Strhodes * The necessary changes are appended to ctx->del_diff and ctx->add_diff; 1109135446Strhodes * we need to do all deletions before any additions so that we don't run 1110135446Strhodes * into transient states with conflicting TTLs. 1111135446Strhodes */ 1112135446Strhodes 1113135446Strhodestypedef struct { 1114135446Strhodes dns_db_t *db; 1115135446Strhodes dns_dbversion_t *ver; 1116135446Strhodes dns_diff_t *diff; 1117135446Strhodes dns_name_t *name; 1118135446Strhodes dns_rdata_t *update_rr; 1119135446Strhodes dns_ttl_t update_rr_ttl; 1120135446Strhodes isc_boolean_t ignore_add; 1121135446Strhodes dns_diff_t del_diff; 1122135446Strhodes dns_diff_t add_diff; 1123135446Strhodes} add_rr_prepare_ctx_t; 1124135446Strhodes 1125135446Strhodesstatic isc_result_t 1126135446Strhodesadd_rr_prepare_action(void *data, rr_t *rr) { 1127186462Sdougb isc_result_t result = ISC_R_SUCCESS; 1128135446Strhodes add_rr_prepare_ctx_t *ctx = data; 1129135446Strhodes dns_difftuple_t *tuple = NULL; 1130135446Strhodes isc_boolean_t equal; 1131135446Strhodes 1132135446Strhodes /* 1133135446Strhodes * If the update RR is a "duplicate" of the update RR, 1134135446Strhodes * the update should be silently ignored. 1135135446Strhodes */ 1136135446Strhodes equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0); 1137135446Strhodes if (equal && rr->ttl == ctx->update_rr_ttl) { 1138135446Strhodes ctx->ignore_add = ISC_TRUE; 1139135446Strhodes return (ISC_R_SUCCESS); 1140135446Strhodes } 1141135446Strhodes 1142135446Strhodes /* 1143135446Strhodes * If this RR is "equal" to the update RR, it should 1144135446Strhodes * be deleted before the update RR is added. 1145135446Strhodes */ 1146135446Strhodes if (replaces_p(ctx->update_rr, &rr->rdata)) { 1147135446Strhodes CHECK(dns_difftuple_create(ctx->del_diff.mctx, 1148135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1149135446Strhodes rr->ttl, 1150135446Strhodes &rr->rdata, 1151135446Strhodes &tuple)); 1152135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1153135446Strhodes return (ISC_R_SUCCESS); 1154135446Strhodes } 1155135446Strhodes 1156135446Strhodes /* 1157135446Strhodes * If this RR differs in TTL from the update RR, 1158135446Strhodes * its TTL must be adjusted. 1159135446Strhodes */ 1160135446Strhodes if (rr->ttl != ctx->update_rr_ttl) { 1161135446Strhodes CHECK(dns_difftuple_create(ctx->del_diff.mctx, 1162135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1163135446Strhodes rr->ttl, 1164135446Strhodes &rr->rdata, 1165135446Strhodes &tuple)); 1166135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1167135446Strhodes if (!equal) { 1168135446Strhodes CHECK(dns_difftuple_create(ctx->add_diff.mctx, 1169135446Strhodes DNS_DIFFOP_ADD, ctx->name, 1170135446Strhodes ctx->update_rr_ttl, 1171135446Strhodes &rr->rdata, 1172135446Strhodes &tuple)); 1173135446Strhodes dns_diff_append(&ctx->add_diff, &tuple); 1174135446Strhodes } 1175135446Strhodes } 1176135446Strhodes failure: 1177135446Strhodes return (result); 1178135446Strhodes} 1179135446Strhodes 1180135446Strhodes/**************************************************************************/ 1181135446Strhodes/* 1182135446Strhodes * Miscellaneous subroutines. 1183135446Strhodes */ 1184135446Strhodes 1185170222Sdougb/*% 1186135446Strhodes * Extract a single update RR from 'section' of dynamic update message 1187135446Strhodes * 'msg', with consistency checking. 1188135446Strhodes * 1189135446Strhodes * Stores the owner name, rdata, and TTL of the update RR at 'name', 1190135446Strhodes * 'rdata', and 'ttl', respectively. 1191135446Strhodes */ 1192135446Strhodesstatic void 1193135446Strhodesget_current_rr(dns_message_t *msg, dns_section_t section, 1194135446Strhodes dns_rdataclass_t zoneclass, 1195135446Strhodes dns_name_t **name, dns_rdata_t *rdata, dns_rdatatype_t *covers, 1196135446Strhodes dns_ttl_t *ttl, 1197135446Strhodes dns_rdataclass_t *update_class) 1198135446Strhodes{ 1199135446Strhodes dns_rdataset_t *rdataset; 1200135446Strhodes isc_result_t result; 1201135446Strhodes dns_message_currentname(msg, section, name); 1202135446Strhodes rdataset = ISC_LIST_HEAD((*name)->list); 1203135446Strhodes INSIST(rdataset != NULL); 1204135446Strhodes INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); 1205135446Strhodes *covers = rdataset->covers; 1206135446Strhodes *ttl = rdataset->ttl; 1207135446Strhodes result = dns_rdataset_first(rdataset); 1208135446Strhodes INSIST(result == ISC_R_SUCCESS); 1209135446Strhodes dns_rdataset_current(rdataset, rdata); 1210135446Strhodes INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); 1211135446Strhodes *update_class = rdata->rdclass; 1212135446Strhodes rdata->rdclass = zoneclass; 1213135446Strhodes} 1214135446Strhodes 1215170222Sdougb/*% 1216135446Strhodes * Increment the SOA serial number of database 'db', version 'ver'. 1217135446Strhodes * Replace the SOA record in the database, and log the 1218135446Strhodes * change in 'diff'. 1219135446Strhodes */ 1220135446Strhodes 1221135446Strhodes /* 1222135446Strhodes * XXXRTH Failures in this routine will be worth logging, when 1223135446Strhodes * we have a logging system. Failure to find the zonename 1224135446Strhodes * or the SOA rdataset warrant at least an UNEXPECTED_ERROR(). 1225135446Strhodes */ 1226135446Strhodes 1227135446Strhodesstatic isc_result_t 1228135446Strhodesincrement_soa_serial(dns_db_t *db, dns_dbversion_t *ver, 1229135446Strhodes dns_diff_t *diff, isc_mem_t *mctx) 1230135446Strhodes{ 1231135446Strhodes dns_difftuple_t *deltuple = NULL; 1232135446Strhodes dns_difftuple_t *addtuple = NULL; 1233135446Strhodes isc_uint32_t serial; 1234135446Strhodes isc_result_t result; 1235135446Strhodes 1236135446Strhodes CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); 1237135446Strhodes CHECK(dns_difftuple_copy(deltuple, &addtuple)); 1238135446Strhodes addtuple->op = DNS_DIFFOP_ADD; 1239135446Strhodes 1240135446Strhodes serial = dns_soa_getserial(&addtuple->rdata); 1241135446Strhodes 1242135446Strhodes /* RFC1982 */ 1243135446Strhodes serial = (serial + 1) & 0xFFFFFFFF; 1244135446Strhodes if (serial == 0) 1245135446Strhodes serial = 1; 1246135446Strhodes 1247135446Strhodes dns_soa_setserial(serial, &addtuple->rdata); 1248135446Strhodes CHECK(do_one_tuple(&deltuple, db, ver, diff)); 1249135446Strhodes CHECK(do_one_tuple(&addtuple, db, ver, diff)); 1250135446Strhodes result = ISC_R_SUCCESS; 1251135446Strhodes 1252135446Strhodes failure: 1253135446Strhodes if (addtuple != NULL) 1254135446Strhodes dns_difftuple_free(&addtuple); 1255135446Strhodes if (deltuple != NULL) 1256135446Strhodes dns_difftuple_free(&deltuple); 1257135446Strhodes return (result); 1258135446Strhodes} 1259135446Strhodes 1260170222Sdougb/*% 1261135446Strhodes * Check that the new SOA record at 'update_rdata' does not 1262135446Strhodes * illegally cause the SOA serial number to decrease or stay 1263135446Strhodes * unchanged relative to the existing SOA in 'db'. 1264135446Strhodes * 1265135446Strhodes * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not. 1266135446Strhodes * 1267135446Strhodes * William King points out that RFC2136 is inconsistent about 1268135446Strhodes * the case where the serial number stays unchanged: 1269135446Strhodes * 1270135446Strhodes * section 3.4.2.2 requires a server to ignore a SOA update request 1271135446Strhodes * if the serial number on the update SOA is less_than_or_equal to 1272135446Strhodes * the zone SOA serial. 1273135446Strhodes * 1274135446Strhodes * section 3.6 requires a server to ignore a SOA update request if 1275135446Strhodes * the serial is less_than the zone SOA serial. 1276135446Strhodes * 1277135446Strhodes * Paul says 3.4.2.2 is correct. 1278135446Strhodes * 1279135446Strhodes */ 1280135446Strhodesstatic isc_result_t 1281135446Strhodescheck_soa_increment(dns_db_t *db, dns_dbversion_t *ver, 1282135446Strhodes dns_rdata_t *update_rdata, 1283135446Strhodes isc_boolean_t *ok) 1284135446Strhodes{ 1285135446Strhodes isc_uint32_t db_serial; 1286135446Strhodes isc_uint32_t update_serial; 1287135446Strhodes isc_result_t result; 1288135446Strhodes 1289135446Strhodes update_serial = dns_soa_getserial(update_rdata); 1290135446Strhodes 1291135446Strhodes result = dns_db_getsoaserial(db, ver, &db_serial); 1292135446Strhodes if (result != ISC_R_SUCCESS) 1293135446Strhodes return (result); 1294135446Strhodes 1295135446Strhodes if (DNS_SERIAL_GE(db_serial, update_serial)) { 1296135446Strhodes *ok = ISC_FALSE; 1297135446Strhodes } else { 1298135446Strhodes *ok = ISC_TRUE; 1299135446Strhodes } 1300135446Strhodes 1301135446Strhodes return (ISC_R_SUCCESS); 1302135446Strhodes 1303135446Strhodes} 1304135446Strhodes 1305135446Strhodes/**************************************************************************/ 1306135446Strhodes/* 1307135446Strhodes * Incremental updating of NSECs and RRSIGs. 1308135446Strhodes */ 1309135446Strhodes 1310170222Sdougb#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */ 1311135446Strhodes 1312170222Sdougb/*% 1313135446Strhodes * We abuse the dns_diff_t type to represent a set of domain names 1314135446Strhodes * affected by the update. 1315135446Strhodes */ 1316135446Strhodesstatic isc_result_t 1317135446Strhodesnamelist_append_name(dns_diff_t *list, dns_name_t *name) { 1318135446Strhodes isc_result_t result; 1319135446Strhodes dns_difftuple_t *tuple = NULL; 1320170222Sdougb static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; 1321170222Sdougb 1322135446Strhodes CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, 1323135446Strhodes &dummy_rdata, &tuple)); 1324135446Strhodes dns_diff_append(list, &tuple); 1325135446Strhodes failure: 1326135446Strhodes return (result); 1327135446Strhodes} 1328135446Strhodes 1329135446Strhodesstatic isc_result_t 1330135446Strhodesnamelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected) 1331135446Strhodes{ 1332135446Strhodes isc_result_t result; 1333135446Strhodes dns_fixedname_t fixedname; 1334135446Strhodes dns_name_t *child; 1335135446Strhodes dns_dbiterator_t *dbit = NULL; 1336135446Strhodes 1337135446Strhodes dns_fixedname_init(&fixedname); 1338135446Strhodes child = dns_fixedname_name(&fixedname); 1339135446Strhodes 1340135446Strhodes CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); 1341135446Strhodes 1342135446Strhodes for (result = dns_dbiterator_seek(dbit, name); 1343135446Strhodes result == ISC_R_SUCCESS; 1344135446Strhodes result = dns_dbiterator_next(dbit)) 1345135446Strhodes { 1346135446Strhodes dns_dbnode_t *node = NULL; 1347135446Strhodes CHECK(dns_dbiterator_current(dbit, &node, child)); 1348135446Strhodes dns_db_detachnode(db, &node); 1349135446Strhodes if (! dns_name_issubdomain(child, name)) 1350135446Strhodes break; 1351135446Strhodes CHECK(namelist_append_name(affected, child)); 1352135446Strhodes } 1353135446Strhodes if (result == ISC_R_NOMORE) 1354135446Strhodes result = ISC_R_SUCCESS; 1355135446Strhodes failure: 1356135446Strhodes if (dbit != NULL) 1357135446Strhodes dns_dbiterator_destroy(&dbit); 1358135446Strhodes return (result); 1359135446Strhodes} 1360135446Strhodes 1361135446Strhodes 1362135446Strhodes 1363170222Sdougb/*% 1364135446Strhodes * Helper function for non_nsec_rrset_exists(). 1365135446Strhodes */ 1366135446Strhodesstatic isc_result_t 1367135446Strhodesis_non_nsec_action(void *data, dns_rdataset_t *rrset) { 1368135446Strhodes UNUSED(data); 1369135446Strhodes if (!(rrset->type == dns_rdatatype_nsec || 1370135446Strhodes (rrset->type == dns_rdatatype_rrsig && 1371135446Strhodes rrset->covers == dns_rdatatype_nsec))) 1372135446Strhodes return (ISC_R_EXISTS); 1373135446Strhodes return (ISC_R_SUCCESS); 1374135446Strhodes} 1375135446Strhodes 1376170222Sdougb/*% 1377135446Strhodes * Check whether there is an rrset other than a NSEC or RRSIG NSEC, 1378135446Strhodes * i.e., anything that justifies the continued existence of a name 1379135446Strhodes * after a secure update. 1380135446Strhodes * 1381135446Strhodes * If such an rrset exists, set '*exists' to ISC_TRUE. 1382135446Strhodes * Otherwise, set it to ISC_FALSE. 1383135446Strhodes */ 1384135446Strhodesstatic isc_result_t 1385135446Strhodesnon_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, 1386135446Strhodes dns_name_t *name, isc_boolean_t *exists) 1387135446Strhodes{ 1388135446Strhodes isc_result_t result; 1389135446Strhodes result = foreach_rrset(db, ver, name, 1390135446Strhodes is_non_nsec_action, NULL); 1391135446Strhodes RETURN_EXISTENCE_FLAG; 1392135446Strhodes} 1393135446Strhodes 1394170222Sdougb/*% 1395135446Strhodes * A comparison function for sorting dns_diff_t:s by name. 1396135446Strhodes */ 1397135446Strhodesstatic int 1398135446Strhodesname_order(const void *av, const void *bv) { 1399135446Strhodes dns_difftuple_t const * const *ap = av; 1400135446Strhodes dns_difftuple_t const * const *bp = bv; 1401135446Strhodes dns_difftuple_t const *a = *ap; 1402135446Strhodes dns_difftuple_t const *b = *bp; 1403135446Strhodes return (dns_name_compare(&a->name, &b->name)); 1404135446Strhodes} 1405135446Strhodes 1406135446Strhodesstatic isc_result_t 1407135446Strhodesuniqify_name_list(dns_diff_t *list) { 1408135446Strhodes isc_result_t result; 1409135446Strhodes dns_difftuple_t *p, *q; 1410135446Strhodes 1411135446Strhodes CHECK(dns_diff_sort(list, name_order)); 1412135446Strhodes 1413135446Strhodes p = ISC_LIST_HEAD(list->tuples); 1414135446Strhodes while (p != NULL) { 1415135446Strhodes do { 1416135446Strhodes q = ISC_LIST_NEXT(p, link); 1417135446Strhodes if (q == NULL || ! dns_name_equal(&p->name, &q->name)) 1418135446Strhodes break; 1419135446Strhodes ISC_LIST_UNLINK(list->tuples, q, link); 1420135446Strhodes dns_difftuple_free(&q); 1421135446Strhodes } while (1); 1422135446Strhodes p = ISC_LIST_NEXT(p, link); 1423135446Strhodes } 1424135446Strhodes failure: 1425135446Strhodes return (result); 1426135446Strhodes} 1427135446Strhodes 1428135446Strhodes 1429135446Strhodesstatic isc_result_t 1430135446Strhodesis_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1431135446Strhodes isc_boolean_t *flag) 1432135446Strhodes{ 1433135446Strhodes isc_result_t result; 1434135446Strhodes dns_fixedname_t foundname; 1435135446Strhodes dns_fixedname_init(&foundname); 1436135446Strhodes result = dns_db_find(db, name, ver, dns_rdatatype_any, 1437135446Strhodes DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, 1438135446Strhodes (isc_stdtime_t) 0, NULL, 1439135446Strhodes dns_fixedname_name(&foundname), 1440135446Strhodes NULL, NULL); 1441135446Strhodes if (result == ISC_R_SUCCESS) { 1442135446Strhodes *flag = ISC_FALSE; 1443135446Strhodes return (ISC_R_SUCCESS); 1444135446Strhodes } else if (result == DNS_R_ZONECUT) { 1445135446Strhodes /* 1446135446Strhodes * We are at the zonecut. The name will have an NSEC, but 1447135446Strhodes * non-delegation will be omitted from the type bit map. 1448135446Strhodes */ 1449135446Strhodes *flag = ISC_FALSE; 1450135446Strhodes return (ISC_R_SUCCESS); 1451135446Strhodes } else if (result == DNS_R_GLUE || result == DNS_R_DNAME) { 1452135446Strhodes *flag = ISC_TRUE; 1453135446Strhodes return (ISC_R_SUCCESS); 1454135446Strhodes } else { 1455135446Strhodes return (result); 1456135446Strhodes } 1457135446Strhodes} 1458135446Strhodes 1459170222Sdougb/*% 1460135446Strhodes * Find the next/previous name that has a NSEC record. 1461135446Strhodes * In other words, skip empty database nodes and names that 1462135446Strhodes * have had their NSECs removed because they are obscured by 1463135446Strhodes * a zone cut. 1464135446Strhodes */ 1465135446Strhodesstatic isc_result_t 1466135446Strhodesnext_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1467135446Strhodes dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, 1468135446Strhodes isc_boolean_t forward) 1469135446Strhodes{ 1470135446Strhodes isc_result_t result; 1471135446Strhodes dns_dbiterator_t *dbit = NULL; 1472135446Strhodes isc_boolean_t has_nsec; 1473135446Strhodes unsigned int wraps = 0; 1474135446Strhodes 1475135446Strhodes CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); 1476135446Strhodes 1477135446Strhodes CHECK(dns_dbiterator_seek(dbit, oldname)); 1478135446Strhodes do { 1479135446Strhodes dns_dbnode_t *node = NULL; 1480135446Strhodes 1481135446Strhodes if (forward) 1482135446Strhodes result = dns_dbiterator_next(dbit); 1483135446Strhodes else 1484135446Strhodes result = dns_dbiterator_prev(dbit); 1485135446Strhodes if (result == ISC_R_NOMORE) { 1486135446Strhodes /* 1487135446Strhodes * Wrap around. 1488135446Strhodes */ 1489135446Strhodes if (forward) 1490135446Strhodes CHECK(dns_dbiterator_first(dbit)); 1491135446Strhodes else 1492135446Strhodes CHECK(dns_dbiterator_last(dbit)); 1493135446Strhodes wraps++; 1494135446Strhodes if (wraps == 2) { 1495135446Strhodes update_log(client, zone, ISC_LOG_ERROR, 1496135446Strhodes "secure zone with no NSECs"); 1497135446Strhodes result = DNS_R_BADZONE; 1498135446Strhodes goto failure; 1499135446Strhodes } 1500135446Strhodes } 1501135446Strhodes CHECK(dns_dbiterator_current(dbit, &node, newname)); 1502135446Strhodes dns_db_detachnode(db, &node); 1503135446Strhodes 1504135446Strhodes /* 1505135446Strhodes * The iterator may hold the tree lock, and 1506135446Strhodes * rrset_exists() calls dns_db_findnode() which 1507135446Strhodes * may try to reacquire it. To avoid deadlock 1508135446Strhodes * we must pause the iterator first. 1509135446Strhodes */ 1510135446Strhodes CHECK(dns_dbiterator_pause(dbit)); 1511135446Strhodes CHECK(rrset_exists(db, ver, newname, 1512135446Strhodes dns_rdatatype_nsec, 0, &has_nsec)); 1513135446Strhodes 1514135446Strhodes } while (! has_nsec); 1515135446Strhodes failure: 1516135446Strhodes if (dbit != NULL) 1517135446Strhodes dns_dbiterator_destroy(&dbit); 1518135446Strhodes 1519135446Strhodes return (result); 1520135446Strhodes} 1521135446Strhodes 1522170222Sdougb/*% 1523135446Strhodes * Add a NSEC record for "name", recording the change in "diff". 1524135446Strhodes * The existing NSEC is removed. 1525135446Strhodes */ 1526135446Strhodesstatic isc_result_t 1527135446Strhodesadd_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1528165071Sdougb dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, 1529165071Sdougb dns_diff_t *diff) 1530135446Strhodes{ 1531135446Strhodes isc_result_t result; 1532135446Strhodes dns_dbnode_t *node = NULL; 1533135446Strhodes unsigned char buffer[DNS_NSEC_BUFFERSIZE]; 1534135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1535135446Strhodes dns_difftuple_t *tuple = NULL; 1536135446Strhodes dns_fixedname_t fixedname; 1537135446Strhodes dns_name_t *target; 1538135446Strhodes 1539135446Strhodes dns_fixedname_init(&fixedname); 1540135446Strhodes target = dns_fixedname_name(&fixedname); 1541135446Strhodes 1542135446Strhodes /* 1543135446Strhodes * Find the successor name, aka NSEC target. 1544135446Strhodes */ 1545135446Strhodes CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE)); 1546135446Strhodes 1547135446Strhodes /* 1548135446Strhodes * Create the NSEC RDATA. 1549135446Strhodes */ 1550135446Strhodes CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); 1551135446Strhodes dns_rdata_init(&rdata); 1552135446Strhodes CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); 1553135446Strhodes dns_db_detachnode(db, &node); 1554135446Strhodes 1555135446Strhodes /* 1556135446Strhodes * Delete the old NSEC and record the change. 1557135446Strhodes */ 1558135446Strhodes CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, 1559135446Strhodes NULL, diff)); 1560135446Strhodes /* 1561135446Strhodes * Add the new NSEC and record the change. 1562135446Strhodes */ 1563135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 1564165071Sdougb nsecttl, &rdata, &tuple)); 1565135446Strhodes CHECK(do_one_tuple(&tuple, db, ver, diff)); 1566135446Strhodes INSIST(tuple == NULL); 1567135446Strhodes 1568135446Strhodes failure: 1569135446Strhodes if (node != NULL) 1570135446Strhodes dns_db_detachnode(db, &node); 1571135446Strhodes return (result); 1572135446Strhodes} 1573135446Strhodes 1574170222Sdougb/*% 1575135446Strhodes * Add a placeholder NSEC record for "name", recording the change in "diff". 1576135446Strhodes */ 1577135446Strhodesstatic isc_result_t 1578135446Strhodesadd_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1579135446Strhodes dns_diff_t *diff) { 1580135446Strhodes isc_result_t result; 1581135446Strhodes dns_difftuple_t *tuple = NULL; 1582135446Strhodes isc_region_t r; 1583135446Strhodes unsigned char data[1] = { 0 }; /* The root domain, no bits. */ 1584135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1585135446Strhodes 1586135446Strhodes r.base = data; 1587135446Strhodes r.length = sizeof(data); 1588135446Strhodes dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); 1589135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, 1590135446Strhodes &rdata, &tuple)); 1591135446Strhodes CHECK(do_one_tuple(&tuple, db, ver, diff)); 1592135446Strhodes failure: 1593135446Strhodes return (result); 1594135446Strhodes} 1595135446Strhodes 1596135446Strhodesstatic isc_result_t 1597135446Strhodesfind_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, 1598135446Strhodes isc_mem_t *mctx, unsigned int maxkeys, 1599135446Strhodes dst_key_t **keys, unsigned int *nkeys) 1600135446Strhodes{ 1601135446Strhodes isc_result_t result; 1602135446Strhodes dns_dbnode_t *node = NULL; 1603135446Strhodes const char *directory = dns_zone_getkeydirectory(zone); 1604135446Strhodes CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); 1605135446Strhodes CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), 1606135446Strhodes directory, mctx, maxkeys, keys, nkeys)); 1607135446Strhodes failure: 1608135446Strhodes if (node != NULL) 1609135446Strhodes dns_db_detachnode(db, &node); 1610135446Strhodes return (result); 1611135446Strhodes} 1612135446Strhodes 1613170222Sdougbstatic isc_boolean_t 1614170222Sdougbksk_sanity(dns_db_t *db, dns_dbversion_t *ver) { 1615170222Sdougb isc_boolean_t ret = ISC_FALSE; 1616170222Sdougb isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE; 1617170222Sdougb isc_result_t result; 1618170222Sdougb dns_dbnode_t *node = NULL; 1619170222Sdougb dns_rdataset_t rdataset; 1620170222Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1621170222Sdougb dns_rdata_dnskey_t dnskey; 1622170222Sdougb 1623170222Sdougb dns_rdataset_init(&rdataset); 1624170222Sdougb CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); 1625170222Sdougb CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, 1626170222Sdougb &rdataset, NULL)); 1627170222Sdougb CHECK(dns_rdataset_first(&rdataset)); 1628170222Sdougb while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) { 1629170222Sdougb dns_rdataset_current(&rdataset, &rdata); 1630170222Sdougb CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL)); 1631170222Sdougb if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) 1632170222Sdougb == DNS_KEYOWNER_ZONE) { 1633170222Sdougb if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) 1634170222Sdougb have_ksk = ISC_TRUE; 1635170222Sdougb else 1636170222Sdougb have_nonksk = ISC_TRUE; 1637170222Sdougb } 1638170222Sdougb dns_rdata_reset(&rdata); 1639170222Sdougb result = dns_rdataset_next(&rdataset); 1640170222Sdougb } 1641170222Sdougb if (have_ksk && have_nonksk) 1642170222Sdougb ret = ISC_TRUE; 1643170222Sdougb failure: 1644170222Sdougb if (dns_rdataset_isassociated(&rdataset)) 1645170222Sdougb dns_rdataset_disassociate(&rdataset); 1646170222Sdougb if (node != NULL) 1647170222Sdougb dns_db_detachnode(db, &node); 1648170222Sdougb return (ret); 1649170222Sdougb} 1650170222Sdougb 1651170222Sdougb/*% 1652135446Strhodes * Add RRSIG records for an RRset, recording the change in "diff". 1653135446Strhodes */ 1654135446Strhodesstatic isc_result_t 1655186462Sdougbadd_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1656186462Sdougb dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type, 1657186462Sdougb dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, 1658186462Sdougb isc_mem_t *mctx, isc_stdtime_t inception, isc_stdtime_t expire, 1659186462Sdougb isc_boolean_t check_ksk) 1660135446Strhodes{ 1661135446Strhodes isc_result_t result; 1662135446Strhodes dns_dbnode_t *node = NULL; 1663135446Strhodes dns_rdataset_t rdataset; 1664135446Strhodes dns_rdata_t sig_rdata = DNS_RDATA_INIT; 1665135446Strhodes isc_buffer_t buffer; 1666135446Strhodes unsigned char data[1024]; /* XXX */ 1667135446Strhodes unsigned int i; 1668186462Sdougb isc_boolean_t added_sig = ISC_FALSE; 1669135446Strhodes 1670135446Strhodes dns_rdataset_init(&rdataset); 1671135446Strhodes isc_buffer_init(&buffer, data, sizeof(data)); 1672135446Strhodes 1673135446Strhodes /* Get the rdataset to sign. */ 1674135446Strhodes CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); 1675135446Strhodes CHECK(dns_db_findrdataset(db, node, ver, type, 0, 1676135446Strhodes (isc_stdtime_t) 0, 1677135446Strhodes &rdataset, NULL)); 1678135446Strhodes dns_db_detachnode(db, &node); 1679135446Strhodes 1680135446Strhodes for (i = 0; i < nkeys; i++) { 1681186462Sdougb 1682170222Sdougb if (check_ksk && type != dns_rdatatype_dnskey && 1683170222Sdougb (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0) 1684170222Sdougb continue; 1685174187Sdougb 1686174187Sdougb if (!dst_key_isprivate(keys[i])) 1687174187Sdougb continue; 1688186462Sdougb 1689135446Strhodes /* Calculate the signature, creating a RRSIG RDATA. */ 1690135446Strhodes CHECK(dns_dnssec_sign(name, &rdataset, keys[i], 1691135446Strhodes &inception, &expire, 1692135446Strhodes mctx, &buffer, &sig_rdata)); 1693135446Strhodes 1694135446Strhodes /* Update the database and journal with the RRSIG. */ 1695135446Strhodes /* XXX inefficient - will cause dataset merging */ 1696135446Strhodes CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, 1697135446Strhodes rdataset.ttl, &sig_rdata)); 1698135446Strhodes dns_rdata_reset(&sig_rdata); 1699186462Sdougb added_sig = ISC_TRUE; 1700135446Strhodes } 1701186462Sdougb if (!added_sig) { 1702186462Sdougb update_log(client, zone, ISC_LOG_ERROR, 1703186462Sdougb "found no private keys, " 1704186462Sdougb "unable to generate any signatures"); 1705186462Sdougb result = ISC_R_NOTFOUND; 1706186462Sdougb } 1707135446Strhodes 1708135446Strhodes failure: 1709135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 1710135446Strhodes dns_rdataset_disassociate(&rdataset); 1711135446Strhodes if (node != NULL) 1712135446Strhodes dns_db_detachnode(db, &node); 1713135446Strhodes return (result); 1714135446Strhodes} 1715135446Strhodes 1716170222Sdougb/*% 1717135446Strhodes * Update RRSIG and NSEC records affected by an update. The original 1718135446Strhodes * update, including the SOA serial update but exluding the RRSIG & NSEC 1719135446Strhodes * changes, is in "diff" and has already been applied to "newver" of "db". 1720135446Strhodes * The database version prior to the update is "oldver". 1721135446Strhodes * 1722135446Strhodes * The necessary RRSIG and NSEC changes will be applied to "newver" 1723135446Strhodes * and added (as a minimal diff) to "diff". 1724135446Strhodes * 1725135446Strhodes * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. 1726135446Strhodes */ 1727135446Strhodesstatic isc_result_t 1728135446Strhodesupdate_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1729135446Strhodes dns_dbversion_t *oldver, dns_dbversion_t *newver, 1730135446Strhodes dns_diff_t *diff, isc_uint32_t sigvalidityinterval) 1731135446Strhodes{ 1732135446Strhodes isc_result_t result; 1733135446Strhodes dns_difftuple_t *t; 1734135446Strhodes dns_diff_t diffnames; 1735135446Strhodes dns_diff_t affected; 1736135446Strhodes dns_diff_t sig_diff; 1737135446Strhodes dns_diff_t nsec_diff; 1738135446Strhodes dns_diff_t nsec_mindiff; 1739135446Strhodes isc_boolean_t flag; 1740135446Strhodes dst_key_t *zone_keys[MAXZONEKEYS]; 1741135446Strhodes unsigned int nkeys = 0; 1742135446Strhodes unsigned int i; 1743135446Strhodes isc_stdtime_t now, inception, expire; 1744165071Sdougb dns_ttl_t nsecttl; 1745165071Sdougb dns_rdata_soa_t soa; 1746165071Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1747165071Sdougb dns_rdataset_t rdataset; 1748165071Sdougb dns_dbnode_t *node = NULL; 1749170222Sdougb isc_boolean_t check_ksk; 1750135446Strhodes 1751135446Strhodes dns_diff_init(client->mctx, &diffnames); 1752135446Strhodes dns_diff_init(client->mctx, &affected); 1753135446Strhodes 1754135446Strhodes dns_diff_init(client->mctx, &sig_diff); 1755135446Strhodes dns_diff_init(client->mctx, &nsec_diff); 1756135446Strhodes dns_diff_init(client->mctx, &nsec_mindiff); 1757135446Strhodes 1758135446Strhodes result = find_zone_keys(zone, db, newver, client->mctx, 1759135446Strhodes MAXZONEKEYS, zone_keys, &nkeys); 1760135446Strhodes if (result != ISC_R_SUCCESS) { 1761135446Strhodes update_log(client, zone, ISC_LOG_ERROR, 1762135446Strhodes "could not get zone keys for secure dynamic update"); 1763135446Strhodes goto failure; 1764135446Strhodes } 1765135446Strhodes 1766135446Strhodes isc_stdtime_get(&now); 1767135446Strhodes inception = now - 3600; /* Allow for some clock skew. */ 1768135446Strhodes expire = now + sigvalidityinterval; 1769135446Strhodes 1770135446Strhodes /* 1771170222Sdougb * Do we look at the KSK flag on the DNSKEY to determining which 1772170222Sdougb * keys sign which RRsets? First check the zone option then 1773170222Sdougb * check the keys flags to make sure atleast one has a ksk set 1774170222Sdougb * and one doesn't. 1775170222Sdougb */ 1776170222Sdougb check_ksk = ISC_TF((dns_zone_getoptions(zone) & 1777170222Sdougb DNS_ZONEOPT_UPDATECHECKKSK) != 0); 1778170222Sdougb if (check_ksk) 1779170222Sdougb check_ksk = ksk_sanity(db, newver); 1780170222Sdougb 1781170222Sdougb /* 1782165071Sdougb * Get the NSEC's TTL from the SOA MINIMUM field. 1783165071Sdougb */ 1784165071Sdougb CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); 1785165071Sdougb dns_rdataset_init(&rdataset); 1786165071Sdougb CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0, 1787186462Sdougb (isc_stdtime_t) 0, &rdataset, NULL)); 1788165071Sdougb CHECK(dns_rdataset_first(&rdataset)); 1789165071Sdougb dns_rdataset_current(&rdataset, &rdata); 1790165071Sdougb CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); 1791165071Sdougb nsecttl = soa.minimum; 1792165071Sdougb dns_rdataset_disassociate(&rdataset); 1793165071Sdougb dns_db_detachnode(db, &node); 1794165071Sdougb 1795165071Sdougb /* 1796135446Strhodes * Find all RRsets directly affected by the update, and 1797135446Strhodes * update their RRSIGs. Also build a list of names affected 1798135446Strhodes * by the update in "diffnames". 1799135446Strhodes */ 1800135446Strhodes CHECK(dns_diff_sort(diff, temp_order)); 1801135446Strhodes 1802135446Strhodes t = ISC_LIST_HEAD(diff->tuples); 1803135446Strhodes while (t != NULL) { 1804135446Strhodes dns_name_t *name = &t->name; 1805135446Strhodes /* Now "name" is a new, unique name affected by the update. */ 1806135446Strhodes 1807135446Strhodes CHECK(namelist_append_name(&diffnames, name)); 1808135446Strhodes 1809135446Strhodes while (t != NULL && dns_name_equal(&t->name, name)) { 1810135446Strhodes dns_rdatatype_t type; 1811135446Strhodes type = t->rdata.type; 1812135446Strhodes 1813135446Strhodes /* 1814135446Strhodes * Now "name" and "type" denote a new unique RRset 1815135446Strhodes * affected by the update. 1816135446Strhodes */ 1817135446Strhodes 1818135446Strhodes /* Don't sign RRSIGs. */ 1819135446Strhodes if (type == dns_rdatatype_rrsig) 1820135446Strhodes goto skip; 1821135446Strhodes 1822135446Strhodes /* 1823135446Strhodes * Delete all old RRSIGs covering this type, since they 1824135446Strhodes * are all invalid when the signed RRset has changed. 1825135446Strhodes * We may not be able to recreate all of them - tough. 1826135446Strhodes */ 1827135446Strhodes CHECK(delete_if(true_p, db, newver, name, 1828135446Strhodes dns_rdatatype_rrsig, type, 1829135446Strhodes NULL, &sig_diff)); 1830135446Strhodes 1831135446Strhodes /* 1832135446Strhodes * If this RRset still exists after the update, 1833135446Strhodes * add a new signature for it. 1834135446Strhodes */ 1835135446Strhodes CHECK(rrset_exists(db, newver, name, type, 0, &flag)); 1836135446Strhodes if (flag) { 1837186462Sdougb CHECK(add_sigs(client, zone, db, newver, name, 1838186462Sdougb type, &sig_diff, zone_keys, 1839186462Sdougb nkeys, client->mctx, inception, 1840170222Sdougb expire, check_ksk)); 1841135446Strhodes } 1842135446Strhodes skip: 1843135446Strhodes /* Skip any other updates to the same RRset. */ 1844135446Strhodes while (t != NULL && 1845135446Strhodes dns_name_equal(&t->name, name) && 1846135446Strhodes t->rdata.type == type) 1847135446Strhodes { 1848135446Strhodes t = ISC_LIST_NEXT(t, link); 1849135446Strhodes } 1850135446Strhodes } 1851135446Strhodes } 1852135446Strhodes 1853135446Strhodes /* Remove orphaned NSECs and RRSIG NSECs. */ 1854135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1855135446Strhodes t != NULL; 1856135446Strhodes t = ISC_LIST_NEXT(t, link)) 1857135446Strhodes { 1858135446Strhodes CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag)); 1859135446Strhodes if (! flag) { 1860135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 1861135446Strhodes dns_rdatatype_any, 0, 1862135446Strhodes NULL, &sig_diff)); 1863135446Strhodes } 1864135446Strhodes } 1865135446Strhodes 1866135446Strhodes /* 1867135446Strhodes * When a name is created or deleted, its predecessor needs to 1868135446Strhodes * have its NSEC updated. 1869135446Strhodes */ 1870135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1871135446Strhodes t != NULL; 1872135446Strhodes t = ISC_LIST_NEXT(t, link)) 1873135446Strhodes { 1874135446Strhodes isc_boolean_t existed, exists; 1875135446Strhodes dns_fixedname_t fixedname; 1876135446Strhodes dns_name_t *prevname; 1877135446Strhodes 1878135446Strhodes dns_fixedname_init(&fixedname); 1879135446Strhodes prevname = dns_fixedname_name(&fixedname); 1880135446Strhodes 1881135446Strhodes CHECK(name_exists(db, oldver, &t->name, &existed)); 1882135446Strhodes CHECK(name_exists(db, newver, &t->name, &exists)); 1883135446Strhodes if (exists == existed) 1884135446Strhodes continue; 1885135446Strhodes 1886135446Strhodes /* 1887135446Strhodes * Find the predecessor. 1888135446Strhodes * When names become obscured or unobscured in this update 1889135446Strhodes * transaction, we may find the wrong predecessor because 1890135446Strhodes * the NSECs have not yet been updated to reflect the delegation 1891135446Strhodes * change. This should not matter because in this case, 1892135446Strhodes * the correct predecessor is either the delegation node or 1893135446Strhodes * a newly unobscured node, and those nodes are on the 1894135446Strhodes * "affected" list in any case. 1895135446Strhodes */ 1896135446Strhodes CHECK(next_active(client, zone, db, newver, 1897135446Strhodes &t->name, prevname, ISC_FALSE)); 1898135446Strhodes CHECK(namelist_append_name(&affected, prevname)); 1899135446Strhodes } 1900135446Strhodes 1901135446Strhodes /* 1902135446Strhodes * Find names potentially affected by delegation changes 1903135446Strhodes * (obscured by adding an NS or DNAME, or unobscured by 1904135446Strhodes * removing one). 1905135446Strhodes */ 1906135446Strhodes for (t = ISC_LIST_HEAD(diffnames.tuples); 1907135446Strhodes t != NULL; 1908135446Strhodes t = ISC_LIST_NEXT(t, link)) 1909135446Strhodes { 1910135446Strhodes isc_boolean_t ns_existed, dname_existed; 1911135446Strhodes isc_boolean_t ns_exists, dname_exists; 1912135446Strhodes 1913135446Strhodes CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0, 1914135446Strhodes &ns_existed)); 1915135446Strhodes CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0, 1916135446Strhodes &dname_existed)); 1917135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, 1918135446Strhodes &ns_exists)); 1919135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0, 1920135446Strhodes &dname_exists)); 1921135446Strhodes if ((ns_exists || dname_exists) == (ns_existed || dname_existed)) 1922135446Strhodes continue; 1923135446Strhodes /* 1924135446Strhodes * There was a delegation change. Mark all subdomains 1925135446Strhodes * of t->name as potentially needing a NSEC update. 1926135446Strhodes */ 1927135446Strhodes CHECK(namelist_append_subdomain(db, &t->name, &affected)); 1928135446Strhodes } 1929135446Strhodes 1930135446Strhodes ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link); 1931135446Strhodes INSIST(ISC_LIST_EMPTY(diffnames.tuples)); 1932135446Strhodes 1933135446Strhodes CHECK(uniqify_name_list(&affected)); 1934135446Strhodes 1935135446Strhodes /* 1936135446Strhodes * Determine which names should have NSECs, and delete/create 1937135446Strhodes * NSECs to make it so. We don't know the final NSEC targets yet, 1938135446Strhodes * so we just create placeholder NSECs with arbitrary contents 1939135446Strhodes * to indicate that their respective owner names should be part of 1940135446Strhodes * the NSEC chain. 1941135446Strhodes */ 1942135446Strhodes for (t = ISC_LIST_HEAD(affected.tuples); 1943135446Strhodes t != NULL; 1944135446Strhodes t = ISC_LIST_NEXT(t, link)) 1945135446Strhodes { 1946135446Strhodes isc_boolean_t exists; 1947135446Strhodes CHECK(name_exists(db, newver, &t->name, &exists)); 1948135446Strhodes if (! exists) 1949135446Strhodes continue; 1950135446Strhodes CHECK(is_glue(db, newver, &t->name, &flag)); 1951135446Strhodes if (flag) { 1952135446Strhodes /* 1953135446Strhodes * This name is obscured. Delete any 1954135446Strhodes * existing NSEC record. 1955135446Strhodes */ 1956135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 1957135446Strhodes dns_rdatatype_nsec, 0, 1958135446Strhodes NULL, &nsec_diff)); 1959135446Strhodes } else { 1960135446Strhodes /* 1961135446Strhodes * This name is not obscured. It should have a NSEC. 1962135446Strhodes */ 1963135446Strhodes CHECK(rrset_exists(db, newver, &t->name, 1964135446Strhodes dns_rdatatype_nsec, 0, &flag)); 1965135446Strhodes if (! flag) 1966135446Strhodes CHECK(add_placeholder_nsec(db, newver, &t->name, 1967135446Strhodes diff)); 1968135446Strhodes } 1969135446Strhodes } 1970135446Strhodes 1971135446Strhodes /* 1972135446Strhodes * Now we know which names are part of the NSEC chain. 1973135446Strhodes * Make them all point at their correct targets. 1974135446Strhodes */ 1975135446Strhodes for (t = ISC_LIST_HEAD(affected.tuples); 1976135446Strhodes t != NULL; 1977135446Strhodes t = ISC_LIST_NEXT(t, link)) 1978135446Strhodes { 1979135446Strhodes CHECK(rrset_exists(db, newver, &t->name, 1980135446Strhodes dns_rdatatype_nsec, 0, &flag)); 1981135446Strhodes if (flag) { 1982135446Strhodes /* 1983135446Strhodes * There is a NSEC, but we don't know if it is correct. 1984135446Strhodes * Delete it and create a correct one to be sure. 1985135446Strhodes * If the update was unnecessary, the diff minimization 1986135446Strhodes * will take care of eliminating it from the journal, 1987135446Strhodes * IXFRs, etc. 1988135446Strhodes * 1989135446Strhodes * The RRSIG bit should always be set in the NSECs 1990135446Strhodes * we generate, because they will all get RRSIG NSECs. 1991135446Strhodes * (XXX what if the zone keys are missing?). 1992135446Strhodes * Because the RRSIG NSECs have not necessarily been 1993135446Strhodes * created yet, the correctness of the bit mask relies 1994135446Strhodes * on the assumption that NSECs are only created if 1995135446Strhodes * there is other data, and if there is other data, 1996135446Strhodes * there are other RRSIGs. 1997135446Strhodes */ 1998165071Sdougb CHECK(add_nsec(client, zone, db, newver, &t->name, 1999165071Sdougb nsecttl, &nsec_diff)); 2000135446Strhodes } 2001135446Strhodes } 2002135446Strhodes 2003135446Strhodes /* 2004135446Strhodes * Minimize the set of NSEC updates so that we don't 2005135446Strhodes * have to regenerate the RRSIG NSECs for NSECs that were 2006135446Strhodes * replaced with identical ones. 2007135446Strhodes */ 2008135446Strhodes while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) { 2009135446Strhodes ISC_LIST_UNLINK(nsec_diff.tuples, t, link); 2010135446Strhodes dns_diff_appendminimal(&nsec_mindiff, &t); 2011135446Strhodes } 2012135446Strhodes 2013135446Strhodes /* Update RRSIG NSECs. */ 2014135446Strhodes for (t = ISC_LIST_HEAD(nsec_mindiff.tuples); 2015135446Strhodes t != NULL; 2016135446Strhodes t = ISC_LIST_NEXT(t, link)) 2017135446Strhodes { 2018135446Strhodes if (t->op == DNS_DIFFOP_DEL) { 2019135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 2020135446Strhodes dns_rdatatype_rrsig, dns_rdatatype_nsec, 2021135446Strhodes NULL, &sig_diff)); 2022135446Strhodes } else if (t->op == DNS_DIFFOP_ADD) { 2023186462Sdougb CHECK(add_sigs(client, zone, db, newver, &t->name, 2024186462Sdougb dns_rdatatype_nsec, &sig_diff, 2025186462Sdougb zone_keys, nkeys, client->mctx, 2026186462Sdougb inception, expire, check_ksk)); 2027135446Strhodes } else { 2028135446Strhodes INSIST(0); 2029135446Strhodes } 2030135446Strhodes } 2031135446Strhodes 2032135446Strhodes /* Record our changes for the journal. */ 2033135446Strhodes while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) { 2034135446Strhodes ISC_LIST_UNLINK(sig_diff.tuples, t, link); 2035135446Strhodes dns_diff_appendminimal(diff, &t); 2036135446Strhodes } 2037135446Strhodes while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) { 2038135446Strhodes ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link); 2039135446Strhodes dns_diff_appendminimal(diff, &t); 2040135446Strhodes } 2041135446Strhodes 2042135446Strhodes INSIST(ISC_LIST_EMPTY(sig_diff.tuples)); 2043135446Strhodes INSIST(ISC_LIST_EMPTY(nsec_diff.tuples)); 2044135446Strhodes INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples)); 2045135446Strhodes 2046135446Strhodes failure: 2047135446Strhodes dns_diff_clear(&sig_diff); 2048135446Strhodes dns_diff_clear(&nsec_diff); 2049135446Strhodes dns_diff_clear(&nsec_mindiff); 2050135446Strhodes 2051135446Strhodes dns_diff_clear(&affected); 2052135446Strhodes dns_diff_clear(&diffnames); 2053135446Strhodes 2054135446Strhodes for (i = 0; i < nkeys; i++) 2055135446Strhodes dst_key_free(&zone_keys[i]); 2056135446Strhodes 2057135446Strhodes return (result); 2058135446Strhodes} 2059135446Strhodes 2060135446Strhodes 2061135446Strhodes/**************************************************************************/ 2062170222Sdougb/*% 2063135446Strhodes * The actual update code in all its glory. We try to follow 2064135446Strhodes * the RFC2136 pseudocode as closely as possible. 2065135446Strhodes */ 2066135446Strhodes 2067135446Strhodesstatic isc_result_t 2068135446Strhodessend_update_event(ns_client_t *client, dns_zone_t *zone) { 2069135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2070135446Strhodes update_event_t *event = NULL; 2071135446Strhodes isc_task_t *zonetask = NULL; 2072135446Strhodes ns_client_t *evclient; 2073135446Strhodes 2074135446Strhodes event = (update_event_t *) 2075135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 2076135446Strhodes update_action, NULL, sizeof(*event)); 2077135446Strhodes if (event == NULL) 2078135446Strhodes FAIL(ISC_R_NOMEMORY); 2079135446Strhodes event->zone = zone; 2080135446Strhodes event->result = ISC_R_SUCCESS; 2081135446Strhodes 2082135446Strhodes evclient = NULL; 2083135446Strhodes ns_client_attach(client, &evclient); 2084135446Strhodes INSIST(client->nupdates == 0); 2085135446Strhodes client->nupdates++; 2086135446Strhodes event->ev_arg = evclient; 2087135446Strhodes 2088135446Strhodes dns_zone_gettask(zone, &zonetask); 2089135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 2090135446Strhodes 2091135446Strhodes failure: 2092135446Strhodes if (event != NULL) 2093135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 2094135446Strhodes return (result); 2095135446Strhodes} 2096135446Strhodes 2097135446Strhodesstatic void 2098135446Strhodesrespond(ns_client_t *client, isc_result_t result) { 2099135446Strhodes isc_result_t msg_result; 2100135446Strhodes 2101135446Strhodes msg_result = dns_message_reply(client->message, ISC_TRUE); 2102135446Strhodes if (msg_result != ISC_R_SUCCESS) 2103135446Strhodes goto msg_failure; 2104135446Strhodes client->message->rcode = dns_result_torcode(result); 2105135446Strhodes 2106135446Strhodes ns_client_send(client); 2107135446Strhodes return; 2108135446Strhodes 2109135446Strhodes msg_failure: 2110135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 2111135446Strhodes ISC_LOG_ERROR, 2112135446Strhodes "could not create update response message: %s", 2113135446Strhodes isc_result_totext(msg_result)); 2114135446Strhodes ns_client_next(client, msg_result); 2115135446Strhodes} 2116135446Strhodes 2117135446Strhodesvoid 2118135446Strhodesns_update_start(ns_client_t *client, isc_result_t sigresult) { 2119135446Strhodes dns_message_t *request = client->message; 2120135446Strhodes isc_result_t result; 2121135446Strhodes dns_name_t *zonename; 2122135446Strhodes dns_rdataset_t *zone_rdataset; 2123135446Strhodes dns_zone_t *zone = NULL; 2124135446Strhodes 2125135446Strhodes /* 2126135446Strhodes * Interpret the zone section. 2127135446Strhodes */ 2128135446Strhodes result = dns_message_firstname(request, DNS_SECTION_ZONE); 2129135446Strhodes if (result != ISC_R_SUCCESS) 2130135446Strhodes FAILC(DNS_R_FORMERR, 2131135446Strhodes "update zone section empty"); 2132135446Strhodes 2133135446Strhodes /* 2134135446Strhodes * The zone section must contain exactly one "question", and 2135135446Strhodes * it must be of type SOA. 2136135446Strhodes */ 2137135446Strhodes zonename = NULL; 2138135446Strhodes dns_message_currentname(request, DNS_SECTION_ZONE, &zonename); 2139135446Strhodes zone_rdataset = ISC_LIST_HEAD(zonename->list); 2140135446Strhodes if (zone_rdataset->type != dns_rdatatype_soa) 2141135446Strhodes FAILC(DNS_R_FORMERR, 2142135446Strhodes "update zone section contains non-SOA"); 2143135446Strhodes if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) 2144135446Strhodes FAILC(DNS_R_FORMERR, 2145135446Strhodes "update zone section contains multiple RRs"); 2146135446Strhodes 2147135446Strhodes /* The zone section must have exactly one name. */ 2148135446Strhodes result = dns_message_nextname(request, DNS_SECTION_ZONE); 2149135446Strhodes if (result != ISC_R_NOMORE) 2150135446Strhodes FAILC(DNS_R_FORMERR, 2151135446Strhodes "update zone section contains multiple RRs"); 2152135446Strhodes 2153135446Strhodes result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, 2154135446Strhodes &zone); 2155135446Strhodes if (result != ISC_R_SUCCESS) 2156135446Strhodes FAILC(DNS_R_NOTAUTH, 2157135446Strhodes "not authoritative for update zone"); 2158135446Strhodes 2159135446Strhodes switch(dns_zone_gettype(zone)) { 2160135446Strhodes case dns_zone_master: 2161135446Strhodes /* 2162135446Strhodes * We can now fail due to a bad signature as we now know 2163135446Strhodes * that we are the master. 2164135446Strhodes */ 2165135446Strhodes if (sigresult != ISC_R_SUCCESS) 2166135446Strhodes FAIL(sigresult); 2167135446Strhodes CHECK(send_update_event(client, zone)); 2168135446Strhodes break; 2169135446Strhodes case dns_zone_slave: 2170135446Strhodes CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone), 2171135446Strhodes "update forwarding", zonename, ISC_TRUE)); 2172135446Strhodes CHECK(send_forward_event(client, zone)); 2173135446Strhodes break; 2174135446Strhodes default: 2175135446Strhodes FAILC(DNS_R_NOTAUTH, 2176135446Strhodes "not authoritative for update zone"); 2177135446Strhodes } 2178135446Strhodes return; 2179135446Strhodes 2180135446Strhodes failure: 2181135446Strhodes /* 2182135446Strhodes * We failed without having sent an update event to the zone. 2183135446Strhodes * We are still in the client task context, so we can 2184135446Strhodes * simply give an error response without switching tasks. 2185135446Strhodes */ 2186135446Strhodes respond(client, result); 2187135446Strhodes if (zone != NULL) 2188135446Strhodes dns_zone_detach(&zone); 2189135446Strhodes} 2190135446Strhodes 2191170222Sdougb/*% 2192135446Strhodes * DS records are not allowed to exist without corresponding NS records, 2193135446Strhodes * draft-ietf-dnsext-delegation-signer-11.txt, 2.2 Protocol Change, 2194135446Strhodes * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex". 2195135446Strhodes */ 2196135446Strhodes 2197135446Strhodesstatic isc_result_t 2198135446Strhodesremove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) { 2199135446Strhodes isc_result_t result; 2200135446Strhodes isc_boolean_t ns_exists, ds_exists; 2201135446Strhodes dns_difftuple_t *t; 2202135446Strhodes 2203135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); 2204135446Strhodes t != NULL; 2205135446Strhodes t = ISC_LIST_NEXT(t, link)) { 2206174187Sdougb if (t->op != DNS_DIFFOP_ADD || 2207135446Strhodes t->rdata.type != dns_rdatatype_ns) 2208135446Strhodes continue; 2209135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0, 2210135446Strhodes &ns_exists)); 2211135446Strhodes if (ns_exists) 2212135446Strhodes continue; 2213135446Strhodes CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ds, 0, 2214135446Strhodes &ds_exists)); 2215135446Strhodes if (!ds_exists) 2216135446Strhodes continue; 2217135446Strhodes CHECK(delete_if(true_p, db, newver, &t->name, 2218135446Strhodes dns_rdatatype_ds, 0, NULL, diff)); 2219135446Strhodes } 2220135446Strhodes return (ISC_R_SUCCESS); 2221135446Strhodes 2222135446Strhodes failure: 2223135446Strhodes return (result); 2224135446Strhodes} 2225135446Strhodes 2226170222Sdougb/* 2227170222Sdougb * This implements the post load integrity checks for mx records. 2228170222Sdougb */ 2229170222Sdougbstatic isc_result_t 2230170222Sdougbcheck_mx(ns_client_t *client, dns_zone_t *zone, 2231170222Sdougb dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) 2232170222Sdougb{ 2233170222Sdougb char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")]; 2234170222Sdougb char ownerbuf[DNS_NAME_FORMATSIZE]; 2235170222Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 2236170222Sdougb char altbuf[DNS_NAME_FORMATSIZE]; 2237170222Sdougb dns_difftuple_t *t; 2238170222Sdougb dns_fixedname_t fixed; 2239170222Sdougb dns_name_t *foundname; 2240170222Sdougb dns_rdata_mx_t mx; 2241170222Sdougb dns_rdata_t rdata; 2242170222Sdougb isc_boolean_t ok = ISC_TRUE; 2243170222Sdougb isc_boolean_t isaddress; 2244170222Sdougb isc_result_t result; 2245170222Sdougb struct in6_addr addr6; 2246170222Sdougb struct in_addr addr; 2247170222Sdougb unsigned int options; 2248170222Sdougb 2249170222Sdougb dns_fixedname_init(&fixed); 2250170222Sdougb foundname = dns_fixedname_name(&fixed); 2251170222Sdougb dns_rdata_init(&rdata); 2252170222Sdougb options = dns_zone_getoptions(zone); 2253170222Sdougb 2254170222Sdougb for (t = ISC_LIST_HEAD(diff->tuples); 2255170222Sdougb t != NULL; 2256170222Sdougb t = ISC_LIST_NEXT(t, link)) { 2257174187Sdougb if (t->op != DNS_DIFFOP_ADD || 2258170222Sdougb t->rdata.type != dns_rdatatype_mx) 2259170222Sdougb continue; 2260170222Sdougb 2261170222Sdougb result = dns_rdata_tostruct(&t->rdata, &mx, NULL); 2262170222Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2263170222Sdougb /* 2264170222Sdougb * Check if we will error out if we attempt to reload the 2265170222Sdougb * zone. 2266170222Sdougb */ 2267170222Sdougb dns_name_format(&mx.mx, namebuf, sizeof(namebuf)); 2268170222Sdougb dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf)); 2269170222Sdougb isaddress = ISC_FALSE; 2270170222Sdougb if ((options & DNS_RDATA_CHECKMX) != 0 && 2271170222Sdougb strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) { 2272170222Sdougb if (tmp[strlen(tmp) - 1] == '.') 2273170222Sdougb tmp[strlen(tmp) - 1] = '\0'; 2274170222Sdougb if (inet_aton(tmp, &addr) == 1 || 2275170222Sdougb inet_pton(AF_INET6, tmp, &addr6) == 1) 2276170222Sdougb isaddress = ISC_TRUE; 2277170222Sdougb } 2278170222Sdougb 2279170222Sdougb if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) { 2280170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 2281170222Sdougb "%s/MX: '%s': %s", 2282170222Sdougb ownerbuf, namebuf, 2283170222Sdougb dns_result_totext(DNS_R_MXISADDRESS)); 2284170222Sdougb ok = ISC_FALSE; 2285170222Sdougb } else if (isaddress) { 2286170222Sdougb update_log(client, zone, ISC_LOG_WARNING, 2287170222Sdougb "%s/MX: warning: '%s': %s", 2288170222Sdougb ownerbuf, namebuf, 2289170222Sdougb dns_result_totext(DNS_R_MXISADDRESS)); 2290170222Sdougb } 2291186462Sdougb 2292170222Sdougb /* 2293170222Sdougb * Check zone integrity checks. 2294170222Sdougb */ 2295170222Sdougb if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0) 2296170222Sdougb continue; 2297170222Sdougb result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a, 2298170222Sdougb 0, 0, NULL, foundname, NULL, NULL); 2299170222Sdougb if (result == ISC_R_SUCCESS) 2300170222Sdougb continue; 2301170222Sdougb 2302170222Sdougb if (result == DNS_R_NXRRSET) { 2303170222Sdougb result = dns_db_find(db, &mx.mx, newver, 2304170222Sdougb dns_rdatatype_aaaa, 2305170222Sdougb 0, 0, NULL, foundname, 2306170222Sdougb NULL, NULL); 2307170222Sdougb if (result == ISC_R_SUCCESS) 2308170222Sdougb continue; 2309170222Sdougb } 2310170222Sdougb 2311170222Sdougb if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) { 2312170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 2313170222Sdougb "%s/MX '%s' has no address records " 2314170222Sdougb "(A or AAAA)", ownerbuf, namebuf); 2315170222Sdougb ok = ISC_FALSE; 2316170222Sdougb } else if (result == DNS_R_CNAME) { 2317170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 2318170222Sdougb "%s/MX '%s' is a CNAME (illegal)", 2319170222Sdougb ownerbuf, namebuf); 2320170222Sdougb ok = ISC_FALSE; 2321170222Sdougb } else if (result == DNS_R_DNAME) { 2322170222Sdougb dns_name_format(foundname, altbuf, sizeof altbuf); 2323170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 2324170222Sdougb "%s/MX '%s' is below a DNAME '%s' (illegal)", 2325170222Sdougb ownerbuf, namebuf, altbuf); 2326170222Sdougb ok = ISC_FALSE; 2327170222Sdougb } 2328170222Sdougb } 2329170222Sdougb return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED); 2330170222Sdougb} 2331170222Sdougb 2332135446Strhodesstatic void 2333135446Strhodesupdate_action(isc_task_t *task, isc_event_t *event) { 2334135446Strhodes update_event_t *uev = (update_event_t *) event; 2335135446Strhodes dns_zone_t *zone = uev->zone; 2336135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2337135446Strhodes 2338135446Strhodes isc_result_t result; 2339135446Strhodes dns_db_t *db = NULL; 2340135446Strhodes dns_dbversion_t *oldver = NULL; 2341135446Strhodes dns_dbversion_t *ver = NULL; 2342135446Strhodes dns_diff_t diff; /* Pending updates. */ 2343135446Strhodes dns_diff_t temp; /* Pending RR existence assertions. */ 2344135446Strhodes isc_boolean_t soa_serial_changed = ISC_FALSE; 2345135446Strhodes isc_mem_t *mctx = client->mctx; 2346135446Strhodes dns_rdatatype_t covers; 2347135446Strhodes dns_message_t *request = client->message; 2348135446Strhodes dns_rdataclass_t zoneclass; 2349135446Strhodes dns_name_t *zonename; 2350135446Strhodes dns_ssutable_t *ssutable = NULL; 2351135446Strhodes dns_fixedname_t tmpnamefixed; 2352135446Strhodes dns_name_t *tmpname = NULL; 2353170222Sdougb unsigned int options; 2354135446Strhodes 2355135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATE); 2356135446Strhodes 2357135446Strhodes dns_diff_init(mctx, &diff); 2358135446Strhodes dns_diff_init(mctx, &temp); 2359135446Strhodes 2360135446Strhodes CHECK(dns_zone_getdb(zone, &db)); 2361135446Strhodes zonename = dns_db_origin(db); 2362135446Strhodes zoneclass = dns_db_class(db); 2363135446Strhodes dns_zone_getssutable(zone, &ssutable); 2364135446Strhodes dns_db_currentversion(db, &oldver); 2365135446Strhodes CHECK(dns_db_newversion(db, &ver)); 2366135446Strhodes 2367135446Strhodes /* 2368135446Strhodes * Check prerequisites. 2369135446Strhodes */ 2370135446Strhodes 2371135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE); 2372135446Strhodes result == ISC_R_SUCCESS; 2373135446Strhodes result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE)) 2374135446Strhodes { 2375135446Strhodes dns_name_t *name = NULL; 2376135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2377135446Strhodes dns_ttl_t ttl; 2378135446Strhodes dns_rdataclass_t update_class; 2379135446Strhodes isc_boolean_t flag; 2380135446Strhodes 2381135446Strhodes get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass, 2382135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2383135446Strhodes 2384135446Strhodes if (ttl != 0) 2385135446Strhodes FAILC(DNS_R_FORMERR, "prerequisite TTL is not zero"); 2386135446Strhodes 2387135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2388135446Strhodes FAILN(DNS_R_NOTZONE, name, 2389135446Strhodes "prerequisite name is out of zone"); 2390135446Strhodes 2391135446Strhodes if (update_class == dns_rdataclass_any) { 2392135446Strhodes if (rdata.length != 0) 2393135446Strhodes FAILC(DNS_R_FORMERR, 2394135446Strhodes "class ANY prerequisite " 2395135446Strhodes "RDATA is not empty"); 2396135446Strhodes if (rdata.type == dns_rdatatype_any) { 2397135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2398135446Strhodes if (! flag) { 2399135446Strhodes FAILN(DNS_R_NXDOMAIN, name, 2400135446Strhodes "'name in use' prerequisite " 2401135446Strhodes "not satisfied"); 2402135446Strhodes } 2403135446Strhodes } else { 2404135446Strhodes CHECK(rrset_exists(db, ver, name, 2405135446Strhodes rdata.type, covers, &flag)); 2406135446Strhodes if (! flag) { 2407135446Strhodes /* RRset does not exist. */ 2408135446Strhodes FAILNT(DNS_R_NXRRSET, name, rdata.type, 2409135446Strhodes "'rrset exists (value independent)' " 2410135446Strhodes "prerequisite not satisfied"); 2411135446Strhodes } 2412135446Strhodes } 2413135446Strhodes } else if (update_class == dns_rdataclass_none) { 2414135446Strhodes if (rdata.length != 0) 2415135446Strhodes FAILC(DNS_R_FORMERR, 2416135446Strhodes "class NONE prerequisite " 2417135446Strhodes "RDATA is not empty"); 2418135446Strhodes if (rdata.type == dns_rdatatype_any) { 2419135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2420135446Strhodes if (flag) { 2421135446Strhodes FAILN(DNS_R_YXDOMAIN, name, 2422135446Strhodes "'name not in use' prerequisite " 2423135446Strhodes "not satisfied"); 2424135446Strhodes } 2425135446Strhodes } else { 2426135446Strhodes CHECK(rrset_exists(db, ver, name, 2427135446Strhodes rdata.type, covers, &flag)); 2428135446Strhodes if (flag) { 2429135446Strhodes /* RRset exists. */ 2430135446Strhodes FAILNT(DNS_R_YXRRSET, name, rdata.type, 2431135446Strhodes "'rrset does not exist' " 2432135446Strhodes "prerequisite not satisfied"); 2433135446Strhodes } 2434135446Strhodes } 2435135446Strhodes } else if (update_class == zoneclass) { 2436135446Strhodes /* "temp<rr.name, rr.type> += rr;" */ 2437135446Strhodes result = temp_append(&temp, name, &rdata); 2438135446Strhodes if (result != ISC_R_SUCCESS) { 2439135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 2440135446Strhodes "temp entry creation failed: %s", 2441135446Strhodes dns_result_totext(result)); 2442135446Strhodes FAIL(ISC_R_UNEXPECTED); 2443135446Strhodes } 2444135446Strhodes } else { 2445135446Strhodes FAILC(DNS_R_FORMERR, "malformed prerequisite"); 2446135446Strhodes } 2447135446Strhodes } 2448135446Strhodes if (result != ISC_R_NOMORE) 2449135446Strhodes FAIL(result); 2450135446Strhodes 2451135446Strhodes 2452135446Strhodes /* 2453135446Strhodes * Perform the final check of the "rrset exists (value dependent)" 2454135446Strhodes * prerequisites. 2455135446Strhodes */ 2456135446Strhodes if (ISC_LIST_HEAD(temp.tuples) != NULL) { 2457135446Strhodes dns_rdatatype_t type; 2458135446Strhodes 2459135446Strhodes /* 2460135446Strhodes * Sort the prerequisite records by owner name, 2461135446Strhodes * type, and rdata. 2462135446Strhodes */ 2463135446Strhodes result = dns_diff_sort(&temp, temp_order); 2464135446Strhodes if (result != ISC_R_SUCCESS) 2465135446Strhodes FAILC(result, "'RRset exists (value dependent)' " 2466135446Strhodes "prerequisite not satisfied"); 2467135446Strhodes 2468135446Strhodes dns_fixedname_init(&tmpnamefixed); 2469135446Strhodes tmpname = dns_fixedname_name(&tmpnamefixed); 2470135446Strhodes result = temp_check(mctx, &temp, db, ver, tmpname, &type); 2471135446Strhodes if (result != ISC_R_SUCCESS) 2472135446Strhodes FAILNT(result, tmpname, type, 2473135446Strhodes "'RRset exists (value dependent)' " 2474135446Strhodes "prerequisite not satisfied"); 2475135446Strhodes } 2476135446Strhodes 2477135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2478135446Strhodes "prerequisites are OK"); 2479135446Strhodes 2480135446Strhodes /* 2481135446Strhodes * Check Requestor's Permissions. It seems a bit silly to do this 2482135446Strhodes * only after prerequisite testing, but that is what RFC2136 says. 2483135446Strhodes */ 2484135446Strhodes result = ISC_R_SUCCESS; 2485135446Strhodes if (ssutable == NULL) 2486135446Strhodes CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone), 2487135446Strhodes "update", zonename, ISC_FALSE)); 2488135446Strhodes else if (client->signer == NULL) 2489135446Strhodes CHECK(checkupdateacl(client, NULL, "update", zonename, 2490135446Strhodes ISC_FALSE)); 2491186462Sdougb 2492135446Strhodes if (dns_zone_getupdatedisabled(zone)) 2493135446Strhodes FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled"); 2494135446Strhodes 2495135446Strhodes /* 2496135446Strhodes * Perform the Update Section Prescan. 2497135446Strhodes */ 2498135446Strhodes 2499135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2500135446Strhodes result == ISC_R_SUCCESS; 2501135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2502135446Strhodes { 2503135446Strhodes dns_name_t *name = NULL; 2504135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2505135446Strhodes dns_ttl_t ttl; 2506135446Strhodes dns_rdataclass_t update_class; 2507135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2508135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2509135446Strhodes 2510135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2511135446Strhodes FAILC(DNS_R_NOTZONE, 2512135446Strhodes "update RR is outside zone"); 2513135446Strhodes if (update_class == zoneclass) { 2514135446Strhodes /* 2515135446Strhodes * Check for meta-RRs. The RFC2136 pseudocode says 2516135446Strhodes * check for ANY|AXFR|MAILA|MAILB, but the text adds 2517135446Strhodes * "or any other QUERY metatype" 2518135446Strhodes */ 2519135446Strhodes if (dns_rdatatype_ismeta(rdata.type)) { 2520135446Strhodes FAILC(DNS_R_FORMERR, 2521135446Strhodes "meta-RR in update"); 2522135446Strhodes } 2523135446Strhodes result = dns_zone_checknames(zone, name, &rdata); 2524135446Strhodes if (result != ISC_R_SUCCESS) 2525135446Strhodes FAIL(DNS_R_REFUSED); 2526135446Strhodes } else if (update_class == dns_rdataclass_any) { 2527135446Strhodes if (ttl != 0 || rdata.length != 0 || 2528135446Strhodes (dns_rdatatype_ismeta(rdata.type) && 2529135446Strhodes rdata.type != dns_rdatatype_any)) 2530135446Strhodes FAILC(DNS_R_FORMERR, 2531135446Strhodes "meta-RR in update"); 2532135446Strhodes } else if (update_class == dns_rdataclass_none) { 2533135446Strhodes if (ttl != 0 || 2534135446Strhodes dns_rdatatype_ismeta(rdata.type)) 2535135446Strhodes FAILC(DNS_R_FORMERR, 2536135446Strhodes "meta-RR in update"); 2537135446Strhodes } else { 2538135446Strhodes update_log(client, zone, ISC_LOG_WARNING, 2539135446Strhodes "update RR has incorrect class %d", 2540135446Strhodes update_class); 2541135446Strhodes FAIL(DNS_R_FORMERR); 2542135446Strhodes } 2543135446Strhodes /* 2544135446Strhodes * draft-ietf-dnsind-simple-secure-update-01 says 2545135446Strhodes * "Unlike traditional dynamic update, the client 2546135446Strhodes * is forbidden from updating NSEC records." 2547135446Strhodes */ 2548135446Strhodes if (dns_db_issecure(db)) { 2549135446Strhodes if (rdata.type == dns_rdatatype_nsec) { 2550135446Strhodes FAILC(DNS_R_REFUSED, 2551135446Strhodes "explicit NSEC updates are not allowed " 2552135446Strhodes "in secure zones"); 2553135446Strhodes } 2554135446Strhodes else if (rdata.type == dns_rdatatype_rrsig) { 2555135446Strhodes FAILC(DNS_R_REFUSED, 2556135446Strhodes "explicit RRSIG updates are currently not " 2557135446Strhodes "supported in secure zones"); 2558135446Strhodes } 2559135446Strhodes } 2560135446Strhodes 2561135446Strhodes if (ssutable != NULL && client->signer != NULL) { 2562135446Strhodes if (rdata.type != dns_rdatatype_any) { 2563135446Strhodes if (!dns_ssutable_checkrules(ssutable, 2564135446Strhodes client->signer, 2565135446Strhodes name, rdata.type)) 2566135446Strhodes FAILC(DNS_R_REFUSED, 2567135446Strhodes "rejected by secure update"); 2568135446Strhodes } 2569135446Strhodes else { 2570135446Strhodes if (!ssu_checkall(db, ver, name, ssutable, 2571135446Strhodes client->signer)) 2572135446Strhodes FAILC(DNS_R_REFUSED, 2573135446Strhodes "rejected by secure update"); 2574135446Strhodes } 2575135446Strhodes } 2576135446Strhodes } 2577135446Strhodes if (result != ISC_R_NOMORE) 2578135446Strhodes FAIL(result); 2579135446Strhodes 2580135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2581135446Strhodes "update section prescan OK"); 2582135446Strhodes 2583135446Strhodes /* 2584135446Strhodes * Process the Update Section. 2585135446Strhodes */ 2586135446Strhodes 2587170222Sdougb options = dns_zone_getoptions(zone); 2588135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2589135446Strhodes result == ISC_R_SUCCESS; 2590135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2591135446Strhodes { 2592135446Strhodes dns_name_t *name = NULL; 2593135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2594135446Strhodes dns_ttl_t ttl; 2595135446Strhodes dns_rdataclass_t update_class; 2596135446Strhodes isc_boolean_t flag; 2597135446Strhodes 2598135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2599135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2600135446Strhodes 2601135446Strhodes if (update_class == zoneclass) { 2602135446Strhodes 2603135446Strhodes /* 2604170222Sdougb * RFC1123 doesn't allow MF and MD in master zones. */ 2605135446Strhodes if (rdata.type == dns_rdatatype_md || 2606135446Strhodes rdata.type == dns_rdatatype_mf) { 2607135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2608135446Strhodes 2609135446Strhodes dns_rdatatype_format(rdata.type, typebuf, 2610135446Strhodes sizeof(typebuf)); 2611135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, 2612135446Strhodes "attempt to add %s ignored", 2613135446Strhodes typebuf); 2614135446Strhodes continue; 2615135446Strhodes } 2616135446Strhodes if (rdata.type == dns_rdatatype_ns && 2617135446Strhodes dns_name_iswildcard(name)) { 2618135446Strhodes update_log(client, zone, 2619135446Strhodes LOGLEVEL_PROTOCOL, 2620135446Strhodes "attempt to add wildcard NS record" 2621135446Strhodes "ignored"); 2622135446Strhodes continue; 2623135446Strhodes } 2624135446Strhodes if (rdata.type == dns_rdatatype_cname) { 2625135446Strhodes CHECK(cname_incompatible_rrset_exists(db, ver, 2626135446Strhodes name, 2627135446Strhodes &flag)); 2628135446Strhodes if (flag) { 2629135446Strhodes update_log(client, zone, 2630135446Strhodes LOGLEVEL_PROTOCOL, 2631135446Strhodes "attempt to add CNAME " 2632135446Strhodes "alongside non-CNAME " 2633135446Strhodes "ignored"); 2634135446Strhodes continue; 2635135446Strhodes } 2636135446Strhodes } else { 2637135446Strhodes CHECK(rrset_exists(db, ver, name, 2638135446Strhodes dns_rdatatype_cname, 0, 2639135446Strhodes &flag)); 2640135446Strhodes if (flag && 2641135446Strhodes ! dns_rdatatype_isdnssec(rdata.type)) 2642135446Strhodes { 2643135446Strhodes update_log(client, zone, 2644135446Strhodes LOGLEVEL_PROTOCOL, 2645135446Strhodes "attempt to add non-CNAME " 2646135446Strhodes "alongside CNAME ignored"); 2647135446Strhodes continue; 2648135446Strhodes } 2649135446Strhodes } 2650135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2651135446Strhodes isc_boolean_t ok; 2652135446Strhodes CHECK(rrset_exists(db, ver, name, 2653135446Strhodes dns_rdatatype_soa, 0, 2654135446Strhodes &flag)); 2655135446Strhodes if (! flag) { 2656135446Strhodes update_log(client, zone, 2657135446Strhodes LOGLEVEL_PROTOCOL, 2658135446Strhodes "attempt to create 2nd " 2659135446Strhodes "SOA ignored"); 2660135446Strhodes continue; 2661135446Strhodes } 2662135446Strhodes CHECK(check_soa_increment(db, ver, &rdata, 2663135446Strhodes &ok)); 2664135446Strhodes if (! ok) { 2665135446Strhodes update_log(client, zone, 2666135446Strhodes LOGLEVEL_PROTOCOL, 2667135446Strhodes "SOA update failed to " 2668135446Strhodes "increment serial, " 2669135446Strhodes "ignoring it"); 2670135446Strhodes continue; 2671135446Strhodes } 2672135446Strhodes soa_serial_changed = ISC_TRUE; 2673135446Strhodes } 2674170222Sdougb if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 && 2675170222Sdougb dns_name_internalwildcard(name)) { 2676170222Sdougb char namestr[DNS_NAME_FORMATSIZE]; 2677170222Sdougb dns_name_format(name, namestr, 2678170222Sdougb sizeof(namestr)); 2679170222Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2680170222Sdougb "warning: ownername '%s' contains " 2681170222Sdougb "a non-terminal wildcard", namestr); 2682170222Sdougb } 2683135446Strhodes 2684135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { 2685135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2686135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2687135446Strhodes dns_name_format(name, namestr, 2688135446Strhodes sizeof(namestr)); 2689135446Strhodes dns_rdatatype_format(rdata.type, typestr, 2690135446Strhodes sizeof(typestr)); 2691135446Strhodes update_log(client, zone, 2692135446Strhodes LOGLEVEL_PROTOCOL, 2693135446Strhodes "adding an RR at '%s' %s", 2694135446Strhodes namestr, typestr); 2695135446Strhodes } 2696135446Strhodes 2697135446Strhodes /* Prepare the affected RRset for the addition. */ 2698135446Strhodes { 2699135446Strhodes add_rr_prepare_ctx_t ctx; 2700135446Strhodes ctx.db = db; 2701135446Strhodes ctx.ver = ver; 2702135446Strhodes ctx.diff = &diff; 2703135446Strhodes ctx.name = name; 2704135446Strhodes ctx.update_rr = &rdata; 2705135446Strhodes ctx.update_rr_ttl = ttl; 2706135446Strhodes ctx.ignore_add = ISC_FALSE; 2707135446Strhodes dns_diff_init(mctx, &ctx.del_diff); 2708135446Strhodes dns_diff_init(mctx, &ctx.add_diff); 2709135446Strhodes CHECK(foreach_rr(db, ver, name, rdata.type, 2710135446Strhodes covers, add_rr_prepare_action, 2711135446Strhodes &ctx)); 2712135446Strhodes 2713135446Strhodes if (ctx.ignore_add) { 2714135446Strhodes dns_diff_clear(&ctx.del_diff); 2715135446Strhodes dns_diff_clear(&ctx.add_diff); 2716135446Strhodes } else { 2717135446Strhodes CHECK(do_diff(&ctx.del_diff, db, ver, &diff)); 2718135446Strhodes CHECK(do_diff(&ctx.add_diff, db, ver, &diff)); 2719135446Strhodes CHECK(update_one_rr(db, ver, &diff, 2720135446Strhodes DNS_DIFFOP_ADD, 2721135446Strhodes name, ttl, &rdata)); 2722135446Strhodes } 2723135446Strhodes } 2724135446Strhodes } else if (update_class == dns_rdataclass_any) { 2725135446Strhodes if (rdata.type == dns_rdatatype_any) { 2726135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2727135446Strhodes LOGLEVEL_PROTOCOL)) 2728135446Strhodes { 2729135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2730135446Strhodes dns_name_format(name, namestr, 2731135446Strhodes sizeof(namestr)); 2732135446Strhodes update_log(client, zone, 2733135446Strhodes LOGLEVEL_PROTOCOL, 2734135446Strhodes "delete all rrsets from " 2735135446Strhodes "name '%s'", namestr); 2736135446Strhodes } 2737135446Strhodes if (dns_name_equal(name, zonename)) { 2738135446Strhodes CHECK(delete_if(type_not_soa_nor_ns_p, 2739135446Strhodes db, ver, name, 2740135446Strhodes dns_rdatatype_any, 0, 2741135446Strhodes &rdata, &diff)); 2742135446Strhodes } else { 2743143731Sdougb CHECK(delete_if(type_not_dnssec, 2744143731Sdougb db, ver, name, 2745135446Strhodes dns_rdatatype_any, 0, 2746135446Strhodes &rdata, &diff)); 2747135446Strhodes } 2748135446Strhodes } else if (dns_name_equal(name, zonename) && 2749135446Strhodes (rdata.type == dns_rdatatype_soa || 2750135446Strhodes rdata.type == dns_rdatatype_ns)) { 2751135446Strhodes update_log(client, zone, 2752135446Strhodes LOGLEVEL_PROTOCOL, 2753135446Strhodes "attempt to delete all SOA " 2754135446Strhodes "or NS records ignored"); 2755135446Strhodes continue; 2756135446Strhodes } else { 2757135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2758135446Strhodes LOGLEVEL_PROTOCOL)) 2759135446Strhodes { 2760135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2761135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2762135446Strhodes dns_name_format(name, namestr, 2763135446Strhodes sizeof(namestr)); 2764135446Strhodes dns_rdatatype_format(rdata.type, 2765135446Strhodes typestr, 2766135446Strhodes sizeof(typestr)); 2767135446Strhodes update_log(client, zone, 2768135446Strhodes LOGLEVEL_PROTOCOL, 2769135446Strhodes "deleting rrset at '%s' %s", 2770135446Strhodes namestr, typestr); 2771135446Strhodes } 2772135446Strhodes CHECK(delete_if(true_p, db, ver, name, 2773135446Strhodes rdata.type, covers, &rdata, 2774135446Strhodes &diff)); 2775135446Strhodes } 2776135446Strhodes } else if (update_class == dns_rdataclass_none) { 2777135446Strhodes /* 2778135446Strhodes * The (name == zonename) condition appears in 2779135446Strhodes * RFC2136 3.4.2.4 but is missing from the pseudocode. 2780135446Strhodes */ 2781135446Strhodes if (dns_name_equal(name, zonename)) { 2782135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2783135446Strhodes update_log(client, zone, 2784135446Strhodes LOGLEVEL_PROTOCOL, 2785135446Strhodes "attempt to delete SOA " 2786135446Strhodes "ignored"); 2787135446Strhodes continue; 2788135446Strhodes } 2789135446Strhodes if (rdata.type == dns_rdatatype_ns) { 2790135446Strhodes int count; 2791135446Strhodes CHECK(rr_count(db, ver, name, 2792135446Strhodes dns_rdatatype_ns, 2793135446Strhodes 0, &count)); 2794135446Strhodes if (count == 1) { 2795135446Strhodes update_log(client, zone, 2796135446Strhodes LOGLEVEL_PROTOCOL, 2797135446Strhodes "attempt to " 2798135446Strhodes "delete last " 2799135446Strhodes "NS ignored"); 2800135446Strhodes continue; 2801135446Strhodes } 2802135446Strhodes } 2803135446Strhodes } 2804135446Strhodes update_log(client, zone, 2805135446Strhodes LOGLEVEL_PROTOCOL, 2806135446Strhodes "deleting an RR"); 2807135446Strhodes CHECK(delete_if(rr_equal_p, db, ver, name, 2808135446Strhodes rdata.type, covers, &rdata, &diff)); 2809135446Strhodes } 2810135446Strhodes } 2811135446Strhodes if (result != ISC_R_NOMORE) 2812135446Strhodes FAIL(result); 2813135446Strhodes 2814135446Strhodes /* 2815135446Strhodes * If any changes were made, increment the SOA serial number, 2816135446Strhodes * update RRSIGs and NSECs (if zone is secure), and write the update 2817135446Strhodes * to the journal. 2818135446Strhodes */ 2819135446Strhodes if (! ISC_LIST_EMPTY(diff.tuples)) { 2820135446Strhodes char *journalfile; 2821135446Strhodes dns_journal_t *journal; 2822135446Strhodes 2823135446Strhodes /* 2824135446Strhodes * Increment the SOA serial, but only if it was not 2825135446Strhodes * changed as a result of an update operation. 2826135446Strhodes */ 2827135446Strhodes if (! soa_serial_changed) { 2828135446Strhodes CHECK(increment_soa_serial(db, ver, &diff, mctx)); 2829135446Strhodes } 2830135446Strhodes 2831170222Sdougb CHECK(check_mx(client, zone, db, ver, &diff)); 2832170222Sdougb 2833135446Strhodes CHECK(remove_orphaned_ds(db, ver, &diff)); 2834135446Strhodes 2835135446Strhodes if (dns_db_issecure(db)) { 2836135446Strhodes result = update_signatures(client, zone, db, oldver, 2837135446Strhodes ver, &diff, 2838135446Strhodes dns_zone_getsigvalidityinterval(zone)); 2839135446Strhodes if (result != ISC_R_SUCCESS) { 2840135446Strhodes update_log(client, zone, 2841135446Strhodes ISC_LOG_ERROR, 2842135446Strhodes "RRSIG/NSEC update failed: %s", 2843135446Strhodes isc_result_totext(result)); 2844135446Strhodes goto failure; 2845135446Strhodes } 2846135446Strhodes } 2847135446Strhodes 2848135446Strhodes journalfile = dns_zone_getjournal(zone); 2849135446Strhodes if (journalfile != NULL) { 2850135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2851135446Strhodes "writing journal %s", journalfile); 2852135446Strhodes 2853135446Strhodes journal = NULL; 2854135446Strhodes result = dns_journal_open(mctx, journalfile, 2855135446Strhodes ISC_TRUE, &journal); 2856135446Strhodes if (result != ISC_R_SUCCESS) 2857135446Strhodes FAILS(result, "journal open failed"); 2858135446Strhodes 2859135446Strhodes result = dns_journal_write_transaction(journal, &diff); 2860135446Strhodes if (result != ISC_R_SUCCESS) { 2861135446Strhodes dns_journal_destroy(&journal); 2862135446Strhodes FAILS(result, "journal write failed"); 2863135446Strhodes } 2864135446Strhodes 2865135446Strhodes dns_journal_destroy(&journal); 2866135446Strhodes } 2867135446Strhodes 2868135446Strhodes /* 2869135446Strhodes * XXXRTH Just a note that this committing code will have 2870135446Strhodes * to change to handle databases that need two-phase 2871135446Strhodes * commit, but this isn't a priority. 2872135446Strhodes */ 2873135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2874135446Strhodes "committing update transaction"); 2875135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 2876135446Strhodes 2877135446Strhodes /* 2878135446Strhodes * Mark the zone as dirty so that it will be written to disk. 2879135446Strhodes */ 2880135446Strhodes dns_zone_markdirty(zone); 2881135446Strhodes 2882135446Strhodes /* 2883135446Strhodes * Notify slaves of the change we just made. 2884135446Strhodes */ 2885135446Strhodes dns_zone_notify(zone); 2886135446Strhodes } else { 2887135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); 2888135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 2889135446Strhodes } 2890135446Strhodes result = ISC_R_SUCCESS; 2891135446Strhodes goto common; 2892135446Strhodes 2893135446Strhodes failure: 2894135446Strhodes /* 2895135446Strhodes * The reason for failure should have been logged at this point. 2896135446Strhodes */ 2897135446Strhodes if (ver != NULL) { 2898186462Sdougb update_log(client, zone, LOGLEVEL_DEBUG, 2899135446Strhodes "rolling back"); 2900135446Strhodes dns_db_closeversion(db, &ver, ISC_FALSE); 2901135446Strhodes } 2902135446Strhodes 2903135446Strhodes common: 2904135446Strhodes dns_diff_clear(&temp); 2905135446Strhodes dns_diff_clear(&diff); 2906135446Strhodes 2907135446Strhodes if (oldver != NULL) 2908135446Strhodes dns_db_closeversion(db, &oldver, ISC_FALSE); 2909135446Strhodes 2910135446Strhodes if (db != NULL) 2911135446Strhodes dns_db_detach(&db); 2912135446Strhodes 2913135446Strhodes if (ssutable != NULL) 2914135446Strhodes dns_ssutable_detach(&ssutable); 2915135446Strhodes 2916135446Strhodes if (zone != NULL) 2917135446Strhodes dns_zone_detach(&zone); 2918135446Strhodes 2919135446Strhodes isc_task_detach(&task); 2920135446Strhodes uev->result = result; 2921135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2922135446Strhodes uev->ev_action = updatedone_action; 2923135446Strhodes isc_task_send(client->task, &event); 2924135446Strhodes INSIST(event == NULL); 2925135446Strhodes} 2926135446Strhodes 2927135446Strhodesstatic void 2928135446Strhodesupdatedone_action(isc_task_t *task, isc_event_t *event) { 2929135446Strhodes update_event_t *uev = (update_event_t *) event; 2930135446Strhodes ns_client_t *client = (ns_client_t *) event->ev_arg; 2931135446Strhodes 2932135446Strhodes UNUSED(task); 2933135446Strhodes 2934135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATEDONE); 2935135446Strhodes INSIST(task == client->task); 2936135446Strhodes 2937135446Strhodes INSIST(client->nupdates > 0); 2938135446Strhodes client->nupdates--; 2939135446Strhodes respond(client, uev->result); 2940153816Sdougb isc_event_free(&event); 2941135446Strhodes ns_client_detach(&client); 2942135446Strhodes} 2943135446Strhodes 2944170222Sdougb/*% 2945135446Strhodes * Update forwarding support. 2946135446Strhodes */ 2947135446Strhodes 2948135446Strhodesstatic void 2949135446Strhodesforward_fail(isc_task_t *task, isc_event_t *event) { 2950186462Sdougb ns_client_t *client = (ns_client_t *)event->ev_arg; 2951135446Strhodes 2952135446Strhodes UNUSED(task); 2953135446Strhodes 2954135446Strhodes INSIST(client->nupdates > 0); 2955135446Strhodes client->nupdates--; 2956135446Strhodes respond(client, DNS_R_SERVFAIL); 2957153816Sdougb isc_event_free(&event); 2958135446Strhodes ns_client_detach(&client); 2959135446Strhodes} 2960135446Strhodes 2961135446Strhodes 2962135446Strhodesstatic void 2963135446Strhodesforward_callback(void *arg, isc_result_t result, dns_message_t *answer) { 2964135446Strhodes update_event_t *uev = arg; 2965135446Strhodes ns_client_t *client = uev->ev_arg; 2966135446Strhodes 2967135446Strhodes if (result != ISC_R_SUCCESS) { 2968135446Strhodes INSIST(answer == NULL); 2969135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2970135446Strhodes uev->ev_action = forward_fail; 2971135446Strhodes } else { 2972135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 2973135446Strhodes uev->ev_action = forward_done; 2974135446Strhodes uev->answer = answer; 2975135446Strhodes } 2976135446Strhodes isc_task_send(client->task, ISC_EVENT_PTR(&uev)); 2977135446Strhodes} 2978135446Strhodes 2979135446Strhodesstatic void 2980135446Strhodesforward_done(isc_task_t *task, isc_event_t *event) { 2981135446Strhodes update_event_t *uev = (update_event_t *) event; 2982135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2983135446Strhodes 2984135446Strhodes UNUSED(task); 2985135446Strhodes 2986135446Strhodes INSIST(client->nupdates > 0); 2987135446Strhodes client->nupdates--; 2988135446Strhodes ns_client_sendraw(client, uev->answer); 2989135446Strhodes dns_message_destroy(&uev->answer); 2990135446Strhodes isc_event_free(&event); 2991135446Strhodes ns_client_detach(&client); 2992135446Strhodes} 2993135446Strhodes 2994135446Strhodesstatic void 2995135446Strhodesforward_action(isc_task_t *task, isc_event_t *event) { 2996135446Strhodes update_event_t *uev = (update_event_t *) event; 2997135446Strhodes dns_zone_t *zone = uev->zone; 2998135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2999135446Strhodes isc_result_t result; 3000135446Strhodes 3001135446Strhodes result = dns_zone_forwardupdate(zone, client->message, 3002135446Strhodes forward_callback, event); 3003135446Strhodes if (result != ISC_R_SUCCESS) { 3004135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 3005135446Strhodes uev->ev_action = forward_fail; 3006135446Strhodes isc_task_send(client->task, &event); 3007135446Strhodes } 3008135446Strhodes dns_zone_detach(&zone); 3009135446Strhodes isc_task_detach(&task); 3010135446Strhodes} 3011135446Strhodes 3012135446Strhodesstatic isc_result_t 3013135446Strhodessend_forward_event(ns_client_t *client, dns_zone_t *zone) { 3014135446Strhodes isc_result_t result = ISC_R_SUCCESS; 3015135446Strhodes update_event_t *event = NULL; 3016135446Strhodes isc_task_t *zonetask = NULL; 3017135446Strhodes ns_client_t *evclient; 3018135446Strhodes 3019135446Strhodes event = (update_event_t *) 3020135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 3021135446Strhodes forward_action, NULL, sizeof(*event)); 3022135446Strhodes if (event == NULL) 3023135446Strhodes FAIL(ISC_R_NOMEMORY); 3024135446Strhodes event->zone = zone; 3025135446Strhodes event->result = ISC_R_SUCCESS; 3026135446Strhodes 3027135446Strhodes evclient = NULL; 3028135446Strhodes ns_client_attach(client, &evclient); 3029135446Strhodes INSIST(client->nupdates == 0); 3030135446Strhodes client->nupdates++; 3031135446Strhodes event->ev_arg = evclient; 3032135446Strhodes 3033135446Strhodes dns_zone_gettask(zone, &zonetask); 3034135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 3035135446Strhodes 3036135446Strhodes failure: 3037135446Strhodes if (event != NULL) 3038135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 3039135446Strhodes return (result); 3040135446Strhodes} 3041