1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2013 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 18254897Serwin/* $Id: update.c,v 1.199 2011/12/22 07:32:40 each Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22193149Sdougb#include <isc/netaddr.h> 23135446Strhodes#include <isc/print.h> 24193149Sdougb#include <isc/serial.h> 25193149Sdougb#include <isc/stats.h> 26135446Strhodes#include <isc/string.h> 27135446Strhodes#include <isc/taskpool.h> 28135446Strhodes#include <isc/util.h> 29135446Strhodes 30135446Strhodes#include <dns/db.h> 31135446Strhodes#include <dns/dbiterator.h> 32135446Strhodes#include <dns/diff.h> 33135446Strhodes#include <dns/dnssec.h> 34135446Strhodes#include <dns/events.h> 35135446Strhodes#include <dns/fixedname.h> 36135446Strhodes#include <dns/journal.h> 37170222Sdougb#include <dns/keyvalues.h> 38135446Strhodes#include <dns/message.h> 39135446Strhodes#include <dns/nsec.h> 40193149Sdougb#include <dns/nsec3.h> 41224092Sdougb#include <dns/private.h> 42135446Strhodes#include <dns/rdataclass.h> 43135446Strhodes#include <dns/rdataset.h> 44135446Strhodes#include <dns/rdatasetiter.h> 45165071Sdougb#include <dns/rdatastruct.h> 46135446Strhodes#include <dns/rdatatype.h> 47135446Strhodes#include <dns/soa.h> 48135446Strhodes#include <dns/ssu.h> 49224092Sdougb#include <dns/tsig.h> 50254897Serwin#include <dns/update.h> 51135446Strhodes#include <dns/view.h> 52135446Strhodes#include <dns/zone.h> 53135446Strhodes#include <dns/zt.h> 54135446Strhodes 55135446Strhodes#include <named/client.h> 56135446Strhodes#include <named/log.h> 57193149Sdougb#include <named/server.h> 58135446Strhodes#include <named/update.h> 59135446Strhodes 60170222Sdougb/*! \file 61170222Sdougb * \brief 62135446Strhodes * This module implements dynamic update as in RFC2136. 63135446Strhodes */ 64135446Strhodes 65135446Strhodes/* 66193149Sdougb * XXX TODO: 67193149Sdougb * - document strict minimality 68193149Sdougb */ 69135446Strhodes 70135446Strhodes/**************************************************************************/ 71135446Strhodes 72170222Sdougb/*% 73135446Strhodes * Log level for tracing dynamic update protocol requests. 74135446Strhodes */ 75135446Strhodes#define LOGLEVEL_PROTOCOL ISC_LOG_INFO 76135446Strhodes 77170222Sdougb/*% 78135446Strhodes * Log level for low-level debug tracing. 79135446Strhodes */ 80193149Sdougb#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) 81135446Strhodes 82170222Sdougb/*% 83135446Strhodes * Check an operation for failure. These macros all assume that 84135446Strhodes * the function using them has a 'result' variable and a 'failure' 85135446Strhodes * label. 86135446Strhodes */ 87135446Strhodes#define CHECK(op) \ 88193149Sdougb do { result = (op); \ 89193149Sdougb if (result != ISC_R_SUCCESS) goto failure; \ 90135446Strhodes } while (0) 91135446Strhodes 92170222Sdougb/*% 93135446Strhodes * Fail unconditionally with result 'code', which must not 94135446Strhodes * be ISC_R_SUCCESS. The reason for failure presumably has 95135446Strhodes * been logged already. 96135446Strhodes * 97135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 98135446Strhodes * from complaining about "end-of-loop code not reached". 99135446Strhodes */ 100135446Strhodes 101135446Strhodes#define FAIL(code) \ 102135446Strhodes do { \ 103135446Strhodes result = (code); \ 104135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 105135446Strhodes } while (0) 106135446Strhodes 107170222Sdougb/*% 108135446Strhodes * Fail unconditionally and log as a client error. 109135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 110135446Strhodes * from complaining about "end-of-loop code not reached". 111135446Strhodes */ 112135446Strhodes#define FAILC(code, msg) \ 113135446Strhodes do { \ 114135446Strhodes const char *_what = "failed"; \ 115135446Strhodes result = (code); \ 116135446Strhodes switch (result) { \ 117135446Strhodes case DNS_R_NXDOMAIN: \ 118135446Strhodes case DNS_R_YXDOMAIN: \ 119135446Strhodes case DNS_R_YXRRSET: \ 120135446Strhodes case DNS_R_NXRRSET: \ 121135446Strhodes _what = "unsuccessful"; \ 122135446Strhodes } \ 123193149Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, \ 124193149Sdougb "update %s: %s (%s)", _what, \ 125193149Sdougb msg, isc_result_totext(result)); \ 126135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 127135446Strhodes } while (0) 128193149Sdougb#define PREREQFAILC(code, msg) \ 129193149Sdougb do { \ 130193149Sdougb inc_stats(zone, dns_nsstatscounter_updatebadprereq); \ 131193149Sdougb FAILC(code, msg); \ 132193149Sdougb } while (0) 133135446Strhodes 134135446Strhodes#define FAILN(code, name, msg) \ 135135446Strhodes do { \ 136135446Strhodes const char *_what = "failed"; \ 137135446Strhodes result = (code); \ 138135446Strhodes switch (result) { \ 139135446Strhodes case DNS_R_NXDOMAIN: \ 140135446Strhodes case DNS_R_YXDOMAIN: \ 141135446Strhodes case DNS_R_YXRRSET: \ 142135446Strhodes case DNS_R_NXRRSET: \ 143135446Strhodes _what = "unsuccessful"; \ 144135446Strhodes } \ 145135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 146135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 147135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 148193149Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, \ 149135446Strhodes "update %s: %s: %s (%s)", _what, _nbuf, \ 150135446Strhodes msg, isc_result_totext(result)); \ 151135446Strhodes } \ 152135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 153135446Strhodes } while (0) 154193149Sdougb#define PREREQFAILN(code, name, msg) \ 155193149Sdougb do { \ 156193149Sdougb inc_stats(zone, dns_nsstatscounter_updatebadprereq); \ 157193149Sdougb FAILN(code, name, msg); \ 158193149Sdougb } while (0) 159135446Strhodes 160135446Strhodes#define FAILNT(code, name, type, msg) \ 161135446Strhodes do { \ 162135446Strhodes const char *_what = "failed"; \ 163135446Strhodes result = (code); \ 164135446Strhodes switch (result) { \ 165135446Strhodes case DNS_R_NXDOMAIN: \ 166135446Strhodes case DNS_R_YXDOMAIN: \ 167135446Strhodes case DNS_R_YXRRSET: \ 168135446Strhodes case DNS_R_NXRRSET: \ 169135446Strhodes _what = "unsuccessful"; \ 170135446Strhodes } \ 171135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ 172135446Strhodes char _nbuf[DNS_NAME_FORMATSIZE]; \ 173135446Strhodes char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ 174135446Strhodes dns_name_format(name, _nbuf, sizeof(_nbuf)); \ 175135446Strhodes dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ 176193149Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, \ 177135446Strhodes "update %s: %s/%s: %s (%s)", \ 178135446Strhodes _what, _nbuf, _tbuf, msg, \ 179135446Strhodes isc_result_totext(result)); \ 180135446Strhodes } \ 181135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 182135446Strhodes } while (0) 183193149Sdougb#define PREREQFAILNT(code, name, type, msg) \ 184193149Sdougb do { \ 185193149Sdougb inc_stats(zone, dns_nsstatscounter_updatebadprereq); \ 186193149Sdougb FAILNT(code, name, type, msg); \ 187193149Sdougb } while (0) 188193149Sdougb 189170222Sdougb/*% 190135446Strhodes * Fail unconditionally and log as a server error. 191135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 192135446Strhodes * from complaining about "end-of-loop code not reached". 193135446Strhodes */ 194135446Strhodes#define FAILS(code, msg) \ 195135446Strhodes do { \ 196135446Strhodes result = (code); \ 197135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, \ 198193149Sdougb "error: %s: %s", \ 199193149Sdougb msg, isc_result_totext(result)); \ 200135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 201135446Strhodes } while (0) 202135446Strhodes 203193149Sdougb/* 204193149Sdougb * Return TRUE if NS_CLIENTATTR_TCP is set in the attributes other FALSE. 205193149Sdougb */ 206193149Sdougb#define TCPCLIENT(client) (((client)->attributes & NS_CLIENTATTR_TCP) != 0) 207193149Sdougb 208135446Strhodes/**************************************************************************/ 209135446Strhodes 210135446Strhodestypedef struct rr rr_t; 211135446Strhodes 212135446Strhodesstruct rr { 213135446Strhodes /* dns_name_t name; */ 214193149Sdougb isc_uint32_t ttl; 215193149Sdougb dns_rdata_t rdata; 216135446Strhodes}; 217135446Strhodes 218135446Strhodestypedef struct update_event update_event_t; 219135446Strhodes 220135446Strhodesstruct update_event { 221135446Strhodes ISC_EVENT_COMMON(update_event_t); 222193149Sdougb dns_zone_t *zone; 223135446Strhodes isc_result_t result; 224135446Strhodes dns_message_t *answer; 225135446Strhodes}; 226135446Strhodes 227135446Strhodes/**************************************************************************/ 228135446Strhodes/* 229135446Strhodes * Forward declarations. 230135446Strhodes */ 231135446Strhodes 232135446Strhodesstatic void update_action(isc_task_t *task, isc_event_t *event); 233135446Strhodesstatic void updatedone_action(isc_task_t *task, isc_event_t *event); 234135446Strhodesstatic isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone); 235135446Strhodesstatic void forward_done(isc_task_t *task, isc_event_t *event); 236135446Strhodes 237135446Strhodes/**************************************************************************/ 238135446Strhodes 239135446Strhodesstatic void 240135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 241135446Strhodes int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); 242135446Strhodes 243135446Strhodesstatic void 244135446Strhodesupdate_log(ns_client_t *client, dns_zone_t *zone, 245135446Strhodes int level, const char *fmt, ...) 246135446Strhodes{ 247135446Strhodes va_list ap; 248135446Strhodes char message[4096]; 249135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 250135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 251135446Strhodes 252135446Strhodes if (client == NULL || zone == NULL) 253135446Strhodes return; 254135446Strhodes 255135446Strhodes if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE) 256135446Strhodes return; 257135446Strhodes 258135446Strhodes dns_name_format(dns_zone_getorigin(zone), namebuf, 259135446Strhodes sizeof(namebuf)); 260135446Strhodes dns_rdataclass_format(dns_zone_getclass(zone), classbuf, 261135446Strhodes sizeof(classbuf)); 262135446Strhodes 263135446Strhodes va_start(ap, fmt); 264135446Strhodes vsnprintf(message, sizeof(message), fmt, ap); 265135446Strhodes va_end(ap); 266135446Strhodes 267135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 268135446Strhodes level, "updating zone '%s/%s': %s", 269135446Strhodes namebuf, classbuf, message); 270135446Strhodes} 271135446Strhodes 272254897Serwinstatic void 273254897Serwinupdate_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) { 274254897Serwin update_log(arg, zone, level, "%s", message); 275254897Serwin} 276254897Serwin 277193149Sdougb/*% 278193149Sdougb * Increment updated-related statistics counters. 279193149Sdougb */ 280193149Sdougbstatic inline void 281193149Sdougbinc_stats(dns_zone_t *zone, isc_statscounter_t counter) { 282193149Sdougb isc_stats_increment(ns_g_server->nsstats, counter); 283193149Sdougb 284193149Sdougb if (zone != NULL) { 285193149Sdougb isc_stats_t *zonestats = dns_zone_getrequeststats(zone); 286193149Sdougb if (zonestats != NULL) 287193149Sdougb isc_stats_increment(zonestats, counter); 288193149Sdougb } 289193149Sdougb} 290193149Sdougb 291193149Sdougb/*% 292224092Sdougb * Check if we could have queried for the contents of this zone or 293224092Sdougb * if the zone is potentially updateable. 294224092Sdougb * If the zone can potentially be updated and the check failed then 295224092Sdougb * log a error otherwise we log a informational message. 296224092Sdougb */ 297224092Sdougbstatic isc_result_t 298224092Sdougbcheckqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename, 299224092Sdougb dns_acl_t *updateacl, dns_ssutable_t *ssutable) 300224092Sdougb{ 301224092Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 302224092Sdougb char classbuf[DNS_RDATACLASS_FORMATSIZE]; 303224092Sdougb int level; 304224092Sdougb isc_result_t result; 305224092Sdougb 306224092Sdougb result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE); 307224092Sdougb if (result != ISC_R_SUCCESS) { 308224092Sdougb dns_name_format(zonename, namebuf, sizeof(namebuf)); 309224092Sdougb dns_rdataclass_format(client->view->rdclass, classbuf, 310224092Sdougb sizeof(classbuf)); 311224092Sdougb 312224092Sdougb level = (updateacl == NULL && ssutable == NULL) ? 313224092Sdougb ISC_LOG_INFO : ISC_LOG_ERROR; 314224092Sdougb 315224092Sdougb ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 316224092Sdougb NS_LOGMODULE_UPDATE, level, 317224092Sdougb "update '%s/%s' denied due to allow-query", 318224092Sdougb namebuf, classbuf); 319224092Sdougb } else if (updateacl == NULL && ssutable == NULL) { 320224092Sdougb dns_name_format(zonename, namebuf, sizeof(namebuf)); 321224092Sdougb dns_rdataclass_format(client->view->rdclass, classbuf, 322224092Sdougb sizeof(classbuf)); 323224092Sdougb 324224092Sdougb result = DNS_R_REFUSED; 325224092Sdougb ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 326224092Sdougb NS_LOGMODULE_UPDATE, ISC_LOG_INFO, 327224092Sdougb "update '%s/%s' denied", namebuf, classbuf); 328224092Sdougb } 329224092Sdougb return (result); 330224092Sdougb} 331224092Sdougb 332224092Sdougb/*% 333193149Sdougb * Override the default acl logging when checking whether a client 334193149Sdougb * can update the zone or whether we can forward the request to the 335193149Sdougb * master based on IP address. 336193149Sdougb * 337193149Sdougb * 'message' contains the type of operation that is being attempted. 338193149Sdougb * 'slave' indicates if this is a slave zone. If 'acl' is NULL then 339193149Sdougb * log at debug=3. 340193149Sdougb * If the zone has no access controls configured ('acl' == NULL && 341193149Sdougb * 'has_ssutable == ISC_FALS) log the attempt at info, otherwise 342193149Sdougb * at error. 343193149Sdougb * 344193149Sdougb * If the request was signed log that we received it. 345193149Sdougb */ 346135446Strhodesstatic isc_result_t 347135446Strhodescheckupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message, 348193149Sdougb dns_name_t *zonename, isc_boolean_t slave, 349193149Sdougb isc_boolean_t has_ssutable) 350135446Strhodes{ 351135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 352135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 353135446Strhodes int level = ISC_LOG_ERROR; 354135446Strhodes const char *msg = "denied"; 355135446Strhodes isc_result_t result; 356135446Strhodes 357135446Strhodes if (slave && acl == NULL) { 358135446Strhodes result = DNS_R_NOTIMP; 359135446Strhodes level = ISC_LOG_DEBUG(3); 360135446Strhodes msg = "disabled"; 361193149Sdougb } else { 362193149Sdougb result = ns_client_checkaclsilent(client, NULL, acl, ISC_FALSE); 363193149Sdougb if (result == ISC_R_SUCCESS) { 364193149Sdougb level = ISC_LOG_DEBUG(3); 365193149Sdougb msg = "approved"; 366193149Sdougb } else if (acl == NULL && !has_ssutable) { 367193149Sdougb level = ISC_LOG_INFO; 368193149Sdougb } 369193149Sdougb } 370135446Strhodes 371193149Sdougb if (client->signer != NULL) { 372193149Sdougb dns_name_format(client->signer, namebuf, sizeof(namebuf)); 373193149Sdougb ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 374193149Sdougb NS_LOGMODULE_UPDATE, ISC_LOG_INFO, 375193149Sdougb "signer \"%s\" %s", namebuf, msg); 376135446Strhodes } 377135446Strhodes 378135446Strhodes dns_name_format(zonename, namebuf, sizeof(namebuf)); 379135446Strhodes dns_rdataclass_format(client->view->rdclass, classbuf, 380135446Strhodes sizeof(classbuf)); 381135446Strhodes 382135446Strhodes ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, 383193149Sdougb NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", 384193149Sdougb message, namebuf, classbuf, msg); 385135446Strhodes return (result); 386135446Strhodes} 387135446Strhodes 388170222Sdougb/*% 389135446Strhodes * Update a single RR in version 'ver' of 'db' and log the 390135446Strhodes * update in 'diff'. 391135446Strhodes * 392135446Strhodes * Ensures: 393193149Sdougb * \li '*tuple' == NULL. Either the tuple is freed, or its 394193149Sdougb * ownership has been transferred to the diff. 395135446Strhodes */ 396135446Strhodesstatic isc_result_t 397193149Sdougbdo_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver, 398135446Strhodes dns_diff_t *diff) 399135446Strhodes{ 400135446Strhodes dns_diff_t temp_diff; 401135446Strhodes isc_result_t result; 402135446Strhodes 403135446Strhodes /* 404135446Strhodes * Create a singleton diff. 405135446Strhodes */ 406135446Strhodes dns_diff_init(diff->mctx, &temp_diff); 407135446Strhodes ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); 408135446Strhodes 409135446Strhodes /* 410135446Strhodes * Apply it to the database. 411135446Strhodes */ 412135446Strhodes result = dns_diff_apply(&temp_diff, db, ver); 413135446Strhodes ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); 414135446Strhodes if (result != ISC_R_SUCCESS) { 415135446Strhodes dns_difftuple_free(tuple); 416135446Strhodes return (result); 417135446Strhodes } 418135446Strhodes 419135446Strhodes /* 420135446Strhodes * Merge it into the current pending journal entry. 421135446Strhodes */ 422135446Strhodes dns_diff_appendminimal(diff, tuple); 423135446Strhodes 424135446Strhodes /* 425135446Strhodes * Do not clear temp_diff. 426135446Strhodes */ 427135446Strhodes return (ISC_R_SUCCESS); 428135446Strhodes} 429135446Strhodes 430170222Sdougb/*% 431135446Strhodes * Perform the updates in 'updates' in version 'ver' of 'db' and log the 432135446Strhodes * update in 'diff'. 433135446Strhodes * 434135446Strhodes * Ensures: 435193149Sdougb * \li 'updates' is empty. 436135446Strhodes */ 437135446Strhodesstatic isc_result_t 438135446Strhodesdo_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver, 439135446Strhodes dns_diff_t *diff) 440135446Strhodes{ 441135446Strhodes isc_result_t result; 442135446Strhodes while (! ISC_LIST_EMPTY(updates->tuples)) { 443135446Strhodes dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples); 444135446Strhodes ISC_LIST_UNLINK(updates->tuples, t, link); 445135446Strhodes CHECK(do_one_tuple(&t, db, ver, diff)); 446135446Strhodes } 447135446Strhodes return (ISC_R_SUCCESS); 448135446Strhodes 449135446Strhodes failure: 450135446Strhodes dns_diff_clear(diff); 451135446Strhodes return (result); 452135446Strhodes} 453135446Strhodes 454135446Strhodesstatic isc_result_t 455135446Strhodesupdate_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, 456193149Sdougb dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, 457193149Sdougb dns_rdata_t *rdata) 458135446Strhodes{ 459135446Strhodes dns_difftuple_t *tuple = NULL; 460135446Strhodes isc_result_t result; 461135446Strhodes result = dns_difftuple_create(diff->mctx, op, 462135446Strhodes name, ttl, rdata, &tuple); 463135446Strhodes if (result != ISC_R_SUCCESS) 464135446Strhodes return (result); 465135446Strhodes return (do_one_tuple(&tuple, db, ver, diff)); 466135446Strhodes} 467135446Strhodes 468135446Strhodes/**************************************************************************/ 469135446Strhodes/* 470135446Strhodes * Callback-style iteration over rdatasets and rdatas. 471135446Strhodes * 472135446Strhodes * foreach_rrset() can be used to iterate over the RRsets 473135446Strhodes * of a name and call a callback function with each 474135446Strhodes * one. Similarly, foreach_rr() can be used to iterate 475135446Strhodes * over the individual RRs at name, optionally restricted 476135446Strhodes * to RRs of a given type. 477135446Strhodes * 478135446Strhodes * The callback functions are called "actions" and take 479135446Strhodes * two arguments: a void pointer for passing arbitrary 480135446Strhodes * context information, and a pointer to the current RRset 481135446Strhodes * or RR. By convention, their names end in "_action". 482135446Strhodes */ 483135446Strhodes 484135446Strhodes/* 485135446Strhodes * XXXRTH We might want to make this public somewhere in libdns. 486135446Strhodes */ 487135446Strhodes 488170222Sdougb/*% 489135446Strhodes * Function type for foreach_rrset() iterator actions. 490135446Strhodes */ 491135446Strhodestypedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset); 492135446Strhodes 493170222Sdougb/*% 494135446Strhodes * Function type for foreach_rr() iterator actions. 495135446Strhodes */ 496135446Strhodestypedef isc_result_t rr_func(void *data, rr_t *rr); 497135446Strhodes 498170222Sdougb/*% 499135446Strhodes * Internal context struct for foreach_node_rr(). 500135446Strhodes */ 501135446Strhodestypedef struct { 502135446Strhodes rr_func * rr_action; 503135446Strhodes void * rr_action_data; 504135446Strhodes} foreach_node_rr_ctx_t; 505135446Strhodes 506170222Sdougb/*% 507135446Strhodes * Internal helper function for foreach_node_rr(). 508135446Strhodes */ 509135446Strhodesstatic isc_result_t 510135446Strhodesforeach_node_rr_action(void *data, dns_rdataset_t *rdataset) { 511135446Strhodes isc_result_t result; 512135446Strhodes foreach_node_rr_ctx_t *ctx = data; 513135446Strhodes for (result = dns_rdataset_first(rdataset); 514135446Strhodes result == ISC_R_SUCCESS; 515135446Strhodes result = dns_rdataset_next(rdataset)) 516135446Strhodes { 517135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 518186462Sdougb 519135446Strhodes dns_rdataset_current(rdataset, &rr.rdata); 520135446Strhodes rr.ttl = rdataset->ttl; 521135446Strhodes result = (*ctx->rr_action)(ctx->rr_action_data, &rr); 522135446Strhodes if (result != ISC_R_SUCCESS) 523135446Strhodes return (result); 524135446Strhodes } 525135446Strhodes if (result != ISC_R_NOMORE) 526135446Strhodes return (result); 527135446Strhodes return (ISC_R_SUCCESS); 528135446Strhodes} 529135446Strhodes 530170222Sdougb/*% 531135446Strhodes * For each rdataset of 'name' in 'ver' of 'db', call 'action' 532135446Strhodes * with the rdataset and 'action_data' as arguments. If the name 533135446Strhodes * does not exist, do nothing. 534135446Strhodes * 535135446Strhodes * If 'action' returns an error, abort iteration and return the error. 536135446Strhodes */ 537135446Strhodesstatic isc_result_t 538193149Sdougbforeach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 539193149Sdougb rrset_func *action, void *action_data) 540135446Strhodes{ 541135446Strhodes isc_result_t result; 542135446Strhodes dns_dbnode_t *node; 543135446Strhodes dns_rdatasetiter_t *iter; 544135446Strhodes 545135446Strhodes node = NULL; 546135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 547135446Strhodes if (result == ISC_R_NOTFOUND) 548135446Strhodes return (ISC_R_SUCCESS); 549135446Strhodes if (result != ISC_R_SUCCESS) 550135446Strhodes return (result); 551135446Strhodes 552135446Strhodes iter = NULL; 553135446Strhodes result = dns_db_allrdatasets(db, node, ver, 554135446Strhodes (isc_stdtime_t) 0, &iter); 555135446Strhodes if (result != ISC_R_SUCCESS) 556135446Strhodes goto cleanup_node; 557135446Strhodes 558135446Strhodes for (result = dns_rdatasetiter_first(iter); 559135446Strhodes result == ISC_R_SUCCESS; 560135446Strhodes result = dns_rdatasetiter_next(iter)) 561135446Strhodes { 562135446Strhodes dns_rdataset_t rdataset; 563135446Strhodes 564135446Strhodes dns_rdataset_init(&rdataset); 565135446Strhodes dns_rdatasetiter_current(iter, &rdataset); 566135446Strhodes 567135446Strhodes result = (*action)(action_data, &rdataset); 568135446Strhodes 569135446Strhodes dns_rdataset_disassociate(&rdataset); 570135446Strhodes if (result != ISC_R_SUCCESS) 571135446Strhodes goto cleanup_iterator; 572135446Strhodes } 573135446Strhodes if (result == ISC_R_NOMORE) 574135446Strhodes result = ISC_R_SUCCESS; 575135446Strhodes 576135446Strhodes cleanup_iterator: 577135446Strhodes dns_rdatasetiter_destroy(&iter); 578135446Strhodes 579135446Strhodes cleanup_node: 580135446Strhodes dns_db_detachnode(db, &node); 581135446Strhodes 582135446Strhodes return (result); 583135446Strhodes} 584135446Strhodes 585170222Sdougb/*% 586135446Strhodes * For each RR of 'name' in 'ver' of 'db', call 'action' 587135446Strhodes * with the RR and 'action_data' as arguments. If the name 588135446Strhodes * does not exist, do nothing. 589135446Strhodes * 590135446Strhodes * If 'action' returns an error, abort iteration 591135446Strhodes * and return the error. 592135446Strhodes */ 593135446Strhodesstatic isc_result_t 594193149Sdougbforeach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 595193149Sdougb rr_func *rr_action, void *rr_action_data) 596135446Strhodes{ 597135446Strhodes foreach_node_rr_ctx_t ctx; 598135446Strhodes ctx.rr_action = rr_action; 599135446Strhodes ctx.rr_action_data = rr_action_data; 600135446Strhodes return (foreach_rrset(db, ver, name, 601135446Strhodes foreach_node_rr_action, &ctx)); 602135446Strhodes} 603135446Strhodes 604135446Strhodes 605170222Sdougb/*% 606135446Strhodes * For each of the RRs specified by 'db', 'ver', 'name', 'type', 607135446Strhodes * (which can be dns_rdatatype_any to match any type), and 'covers', call 608135446Strhodes * 'action' with the RR and 'action_data' as arguments. If the name 609135446Strhodes * does not exist, or if no RRset of the given type exists at the name, 610135446Strhodes * do nothing. 611135446Strhodes * 612135446Strhodes * If 'action' returns an error, abort iteration and return the error. 613135446Strhodes */ 614135446Strhodesstatic isc_result_t 615193149Sdougbforeach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 616193149Sdougb dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action, 617135446Strhodes void *rr_action_data) 618135446Strhodes{ 619135446Strhodes 620135446Strhodes isc_result_t result; 621135446Strhodes dns_dbnode_t *node; 622135446Strhodes dns_rdataset_t rdataset; 623135446Strhodes 624135446Strhodes if (type == dns_rdatatype_any) 625135446Strhodes return (foreach_node_rr(db, ver, name, 626135446Strhodes rr_action, rr_action_data)); 627135446Strhodes 628135446Strhodes node = NULL; 629193149Sdougb if (type == dns_rdatatype_nsec3 || 630193149Sdougb (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3)) 631193149Sdougb result = dns_db_findnsec3node(db, name, ISC_FALSE, &node); 632193149Sdougb else 633193149Sdougb result = dns_db_findnode(db, name, ISC_FALSE, &node); 634135446Strhodes if (result == ISC_R_NOTFOUND) 635135446Strhodes return (ISC_R_SUCCESS); 636135446Strhodes if (result != ISC_R_SUCCESS) 637135446Strhodes return (result); 638135446Strhodes 639135446Strhodes dns_rdataset_init(&rdataset); 640135446Strhodes result = dns_db_findrdataset(db, node, ver, type, covers, 641135446Strhodes (isc_stdtime_t) 0, &rdataset, NULL); 642135446Strhodes if (result == ISC_R_NOTFOUND) { 643135446Strhodes result = ISC_R_SUCCESS; 644135446Strhodes goto cleanup_node; 645135446Strhodes } 646135446Strhodes if (result != ISC_R_SUCCESS) 647135446Strhodes goto cleanup_node; 648135446Strhodes 649135446Strhodes for (result = dns_rdataset_first(&rdataset); 650135446Strhodes result == ISC_R_SUCCESS; 651135446Strhodes result = dns_rdataset_next(&rdataset)) 652135446Strhodes { 653135446Strhodes rr_t rr = { 0, DNS_RDATA_INIT }; 654135446Strhodes dns_rdataset_current(&rdataset, &rr.rdata); 655135446Strhodes rr.ttl = rdataset.ttl; 656135446Strhodes result = (*rr_action)(rr_action_data, &rr); 657135446Strhodes if (result != ISC_R_SUCCESS) 658135446Strhodes goto cleanup_rdataset; 659135446Strhodes } 660135446Strhodes if (result != ISC_R_NOMORE) 661135446Strhodes goto cleanup_rdataset; 662135446Strhodes result = ISC_R_SUCCESS; 663135446Strhodes 664135446Strhodes cleanup_rdataset: 665135446Strhodes dns_rdataset_disassociate(&rdataset); 666135446Strhodes cleanup_node: 667135446Strhodes dns_db_detachnode(db, &node); 668135446Strhodes 669135446Strhodes return (result); 670135446Strhodes} 671135446Strhodes 672135446Strhodes/**************************************************************************/ 673135446Strhodes/* 674135446Strhodes * Various tests on the database contents (for prerequisites, etc). 675135446Strhodes */ 676135446Strhodes 677170222Sdougb/*% 678135446Strhodes * Function type for predicate functions that compare a database RR 'db_rr' 679135446Strhodes * against an update RR 'update_rr'. 680135446Strhodes */ 681135446Strhodestypedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); 682135446Strhodes 683170222Sdougb/*% 684135446Strhodes * Helper function for rrset_exists(). 685135446Strhodes */ 686135446Strhodesstatic isc_result_t 687135446Strhodesrrset_exists_action(void *data, rr_t *rr) { 688135446Strhodes UNUSED(data); 689135446Strhodes UNUSED(rr); 690135446Strhodes return (ISC_R_EXISTS); 691135446Strhodes} 692135446Strhodes 693170222Sdougb/*% 694135446Strhodes * Utility macro for RR existence checking functions. 695135446Strhodes * 696135446Strhodes * If the variable 'result' has the value ISC_R_EXISTS or 697135446Strhodes * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE, 698135446Strhodes * respectively, and return success. 699135446Strhodes * 700135446Strhodes * If 'result' has any other value, there was a failure. 701135446Strhodes * Return the failure result code and do not set *exists. 702135446Strhodes * 703135446Strhodes * This would be more readable as "do { if ... } while(0)", 704135446Strhodes * but that form generates tons of warnings on Solaris 2.6. 705135446Strhodes */ 706193149Sdougb#define RETURN_EXISTENCE_FLAG \ 707193149Sdougb return ((result == ISC_R_EXISTS) ? \ 708193149Sdougb (*exists = ISC_TRUE, ISC_R_SUCCESS) : \ 709135446Strhodes ((result == ISC_R_SUCCESS) ? \ 710135446Strhodes (*exists = ISC_FALSE, ISC_R_SUCCESS) : \ 711135446Strhodes result)) 712135446Strhodes 713170222Sdougb/*% 714135446Strhodes * Set '*exists' to true iff an rrset of the given type exists, 715135446Strhodes * to false otherwise. 716135446Strhodes */ 717135446Strhodesstatic isc_result_t 718193149Sdougbrrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 719193149Sdougb dns_rdatatype_t type, dns_rdatatype_t covers, 720135446Strhodes isc_boolean_t *exists) 721135446Strhodes{ 722135446Strhodes isc_result_t result; 723135446Strhodes result = foreach_rr(db, ver, name, type, covers, 724135446Strhodes rrset_exists_action, NULL); 725135446Strhodes RETURN_EXISTENCE_FLAG; 726135446Strhodes} 727135446Strhodes 728170222Sdougb/*% 729135446Strhodes * Helper function for cname_incompatible_rrset_exists. 730135446Strhodes */ 731135446Strhodesstatic isc_result_t 732135446Strhodescname_compatibility_action(void *data, dns_rdataset_t *rrset) { 733135446Strhodes UNUSED(data); 734135446Strhodes if (rrset->type != dns_rdatatype_cname && 735135446Strhodes ! dns_rdatatype_isdnssec(rrset->type)) 736135446Strhodes return (ISC_R_EXISTS); 737135446Strhodes return (ISC_R_SUCCESS); 738135446Strhodes} 739135446Strhodes 740170222Sdougb/*% 741135446Strhodes * Check whether there is an rrset incompatible with adding a CNAME RR, 742135446Strhodes * i.e., anything but another CNAME (which can be replaced) or a 743135446Strhodes * DNSSEC RR (which can coexist). 744135446Strhodes * 745135446Strhodes * If such an incompatible rrset exists, set '*exists' to ISC_TRUE. 746135446Strhodes * Otherwise, set it to ISC_FALSE. 747135446Strhodes */ 748135446Strhodesstatic isc_result_t 749135446Strhodescname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, 750135446Strhodes dns_name_t *name, isc_boolean_t *exists) { 751135446Strhodes isc_result_t result; 752135446Strhodes result = foreach_rrset(db, ver, name, 753135446Strhodes cname_compatibility_action, NULL); 754135446Strhodes RETURN_EXISTENCE_FLAG; 755135446Strhodes} 756135446Strhodes 757170222Sdougb/*% 758135446Strhodes * Helper function for rr_count(). 759135446Strhodes */ 760135446Strhodesstatic isc_result_t 761135446Strhodescount_rr_action(void *data, rr_t *rr) { 762135446Strhodes int *countp = data; 763135446Strhodes UNUSED(rr); 764135446Strhodes (*countp)++; 765135446Strhodes return (ISC_R_SUCCESS); 766135446Strhodes} 767135446Strhodes 768170222Sdougb/*% 769135446Strhodes * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'. 770135446Strhodes */ 771135446Strhodesstatic isc_result_t 772135446Strhodesrr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 773135446Strhodes dns_rdatatype_t type, dns_rdatatype_t covers, int *countp) 774135446Strhodes{ 775135446Strhodes *countp = 0; 776135446Strhodes return (foreach_rr(db, ver, name, type, covers, 777135446Strhodes count_rr_action, countp)); 778135446Strhodes} 779135446Strhodes 780170222Sdougb/*% 781135446Strhodes * Context struct and helper function for name_exists(). 782135446Strhodes */ 783135446Strhodes 784135446Strhodesstatic isc_result_t 785135446Strhodesname_exists_action(void *data, dns_rdataset_t *rrset) { 786135446Strhodes UNUSED(data); 787135446Strhodes UNUSED(rrset); 788135446Strhodes return (ISC_R_EXISTS); 789135446Strhodes} 790135446Strhodes 791170222Sdougb/*% 792135446Strhodes * Set '*exists' to true iff the given name exists, to false otherwise. 793135446Strhodes */ 794135446Strhodesstatic isc_result_t 795135446Strhodesname_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 796135446Strhodes isc_boolean_t *exists) 797135446Strhodes{ 798135446Strhodes isc_result_t result; 799135446Strhodes result = foreach_rrset(db, ver, name, 800135446Strhodes name_exists_action, NULL); 801135446Strhodes RETURN_EXISTENCE_FLAG; 802135446Strhodes} 803135446Strhodes 804193149Sdougb/* 805193149Sdougb * 'ssu_check_t' is used to pass the arguments to 806193149Sdougb * dns_ssutable_checkrules() to the callback function 807193149Sdougb * ssu_checkrule(). 808193149Sdougb */ 809135446Strhodestypedef struct { 810193149Sdougb /* The ownername of the record to be updated. */ 811193149Sdougb dns_name_t *name; 812193149Sdougb 813193149Sdougb /* The signature's name if the request was signed. */ 814193149Sdougb dns_name_t *signer; 815193149Sdougb 816193149Sdougb /* The address of the client if the request was received via TCP. */ 817193149Sdougb isc_netaddr_t *tcpaddr; 818193149Sdougb 819193149Sdougb /* The ssu table to check against. */ 820135446Strhodes dns_ssutable_t *table; 821224092Sdougb 822224092Sdougb /* the key used for TKEY requests */ 823224092Sdougb dst_key_t *key; 824135446Strhodes} ssu_check_t; 825135446Strhodes 826135446Strhodesstatic isc_result_t 827135446Strhodesssu_checkrule(void *data, dns_rdataset_t *rrset) { 828135446Strhodes ssu_check_t *ssuinfo = data; 829135446Strhodes isc_boolean_t result; 830135446Strhodes 831135446Strhodes /* 832135446Strhodes * If we're deleting all records, it's ok to delete RRSIG and NSEC even 833135446Strhodes * if we're normally not allowed to. 834135446Strhodes */ 835135446Strhodes if (rrset->type == dns_rdatatype_rrsig || 836135446Strhodes rrset->type == dns_rdatatype_nsec) 837143731Sdougb return (ISC_R_SUCCESS); 838135446Strhodes result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer, 839193149Sdougb ssuinfo->name, ssuinfo->tcpaddr, 840224092Sdougb rrset->type, ssuinfo->key); 841135446Strhodes return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE); 842135446Strhodes} 843135446Strhodes 844135446Strhodesstatic isc_boolean_t 845135446Strhodesssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 846193149Sdougb dns_ssutable_t *ssutable, dns_name_t *signer, 847224092Sdougb isc_netaddr_t *tcpaddr, dst_key_t *key) 848135446Strhodes{ 849135446Strhodes isc_result_t result; 850135446Strhodes ssu_check_t ssuinfo; 851135446Strhodes 852135446Strhodes ssuinfo.name = name; 853135446Strhodes ssuinfo.table = ssutable; 854135446Strhodes ssuinfo.signer = signer; 855193149Sdougb ssuinfo.tcpaddr = tcpaddr; 856224092Sdougb ssuinfo.key = key; 857135446Strhodes result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo); 858135446Strhodes return (ISC_TF(result == ISC_R_SUCCESS)); 859135446Strhodes} 860135446Strhodes 861135446Strhodes/**************************************************************************/ 862135446Strhodes/* 863135446Strhodes * Checking of "RRset exists (value dependent)" prerequisites. 864135446Strhodes * 865135446Strhodes * In the RFC2136 section 3.2.5, this is the pseudocode involving 866135446Strhodes * a variable called "temp", a mapping of <name, type> tuples to rrsets. 867135446Strhodes * 868193149Sdougb * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t" 869193149Sdougb * where each tuple has op==DNS_DIFFOP_EXISTS. 870135446Strhodes */ 871135446Strhodes 872135446Strhodes 873170222Sdougb/*% 874135446Strhodes * Append a tuple asserting the existence of the RR with 875135446Strhodes * 'name' and 'rdata' to 'diff'. 876135446Strhodes */ 877135446Strhodesstatic isc_result_t 878135446Strhodestemp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) { 879135446Strhodes isc_result_t result; 880135446Strhodes dns_difftuple_t *tuple = NULL; 881135446Strhodes 882135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 883135446Strhodes CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, 884193149Sdougb name, 0, rdata, &tuple)); 885135446Strhodes ISC_LIST_APPEND(diff->tuples, tuple, link); 886135446Strhodes failure: 887135446Strhodes return (result); 888135446Strhodes} 889135446Strhodes 890170222Sdougb/*% 891135446Strhodes * Compare two rdatasets represented as sorted lists of tuples. 892135446Strhodes * All list elements must have the same owner name and type. 893135446Strhodes * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset) 894135446Strhodes * if not. 895135446Strhodes */ 896135446Strhodesstatic isc_result_t 897135446Strhodestemp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) { 898135446Strhodes for (;;) { 899135446Strhodes if (a == NULL || b == NULL) 900135446Strhodes break; 901135446Strhodes INSIST(a->op == DNS_DIFFOP_EXISTS && 902135446Strhodes b->op == DNS_DIFFOP_EXISTS); 903135446Strhodes INSIST(a->rdata.type == b->rdata.type); 904135446Strhodes INSIST(dns_name_equal(&a->name, &b->name)); 905224092Sdougb if (dns_rdata_casecompare(&a->rdata, &b->rdata) != 0) 906135446Strhodes return (DNS_R_NXRRSET); 907135446Strhodes a = ISC_LIST_NEXT(a, link); 908135446Strhodes b = ISC_LIST_NEXT(b, link); 909135446Strhodes } 910135446Strhodes if (a != NULL || b != NULL) 911135446Strhodes return (DNS_R_NXRRSET); 912135446Strhodes return (ISC_R_SUCCESS); 913135446Strhodes} 914135446Strhodes 915170222Sdougb/*% 916135446Strhodes * A comparison function defining the sorting order for the entries 917135446Strhodes * in the "temp" data structure. The major sort key is the owner name, 918135446Strhodes * followed by the type and rdata. 919135446Strhodes */ 920135446Strhodesstatic int 921135446Strhodestemp_order(const void *av, const void *bv) { 922135446Strhodes dns_difftuple_t const * const *ap = av; 923135446Strhodes dns_difftuple_t const * const *bp = bv; 924135446Strhodes dns_difftuple_t const *a = *ap; 925135446Strhodes dns_difftuple_t const *b = *bp; 926135446Strhodes int r; 927135446Strhodes r = dns_name_compare(&a->name, &b->name); 928135446Strhodes if (r != 0) 929135446Strhodes return (r); 930135446Strhodes r = (b->rdata.type - a->rdata.type); 931135446Strhodes if (r != 0) 932135446Strhodes return (r); 933224092Sdougb r = dns_rdata_casecompare(&a->rdata, &b->rdata); 934135446Strhodes return (r); 935135446Strhodes} 936135446Strhodes 937170222Sdougb/*% 938135446Strhodes * Check the "RRset exists (value dependent)" prerequisite information 939135446Strhodes * in 'temp' against the contents of the database 'db'. 940135446Strhodes * 941135446Strhodes * Return ISC_R_SUCCESS if the prerequisites are satisfied, 942135446Strhodes * rcode(dns_rcode_nxrrset) if not. 943135446Strhodes * 944135446Strhodes * 'temp' must be pre-sorted. 945135446Strhodes */ 946135446Strhodes 947135446Strhodesstatic isc_result_t 948135446Strhodestemp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db, 949135446Strhodes dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep) 950135446Strhodes{ 951135446Strhodes isc_result_t result; 952135446Strhodes dns_name_t *name; 953135446Strhodes dns_dbnode_t *node; 954135446Strhodes dns_difftuple_t *t; 955135446Strhodes dns_diff_t trash; 956135446Strhodes 957135446Strhodes dns_diff_init(mctx, &trash); 958135446Strhodes 959135446Strhodes /* 960135446Strhodes * For each name and type in the prerequisites, 961135446Strhodes * construct a sorted rdata list of the corresponding 962135446Strhodes * database contents, and compare the lists. 963135446Strhodes */ 964135446Strhodes t = ISC_LIST_HEAD(temp->tuples); 965135446Strhodes while (t != NULL) { 966135446Strhodes name = &t->name; 967135446Strhodes (void)dns_name_copy(name, tmpname, NULL); 968135446Strhodes *typep = t->rdata.type; 969135446Strhodes 970135446Strhodes /* A new unique name begins here. */ 971135446Strhodes node = NULL; 972135446Strhodes result = dns_db_findnode(db, name, ISC_FALSE, &node); 973186462Sdougb if (result == ISC_R_NOTFOUND) { 974186462Sdougb dns_diff_clear(&trash); 975135446Strhodes return (DNS_R_NXRRSET); 976186462Sdougb } 977186462Sdougb if (result != ISC_R_SUCCESS) { 978186462Sdougb dns_diff_clear(&trash); 979135446Strhodes return (result); 980186462Sdougb } 981135446Strhodes 982135446Strhodes /* A new unique type begins here. */ 983135446Strhodes while (t != NULL && dns_name_equal(&t->name, name)) { 984135446Strhodes dns_rdatatype_t type, covers; 985135446Strhodes dns_rdataset_t rdataset; 986135446Strhodes dns_diff_t d_rrs; /* Database RRs with 987135446Strhodes this name and type */ 988186462Sdougb dns_diff_t u_rrs; /* Update RRs with 989135446Strhodes this name and type */ 990135446Strhodes 991135446Strhodes *typep = type = t->rdata.type; 992135446Strhodes if (type == dns_rdatatype_rrsig || 993135446Strhodes type == dns_rdatatype_sig) 994135446Strhodes covers = dns_rdata_covers(&t->rdata); 995195936Sdougb else if (type == dns_rdatatype_any) { 996195936Sdougb dns_db_detachnode(db, &node); 997195936Sdougb dns_diff_clear(&trash); 998195936Sdougb return (DNS_R_NXRRSET); 999195936Sdougb } else 1000135446Strhodes covers = 0; 1001135446Strhodes 1002135446Strhodes /* 1003135446Strhodes * Collect all database RRs for this name and type 1004135446Strhodes * onto d_rrs and sort them. 1005135446Strhodes */ 1006135446Strhodes dns_rdataset_init(&rdataset); 1007135446Strhodes result = dns_db_findrdataset(db, node, ver, type, 1008135446Strhodes covers, (isc_stdtime_t) 0, 1009135446Strhodes &rdataset, NULL); 1010135446Strhodes if (result != ISC_R_SUCCESS) { 1011135446Strhodes dns_db_detachnode(db, &node); 1012186462Sdougb dns_diff_clear(&trash); 1013135446Strhodes return (DNS_R_NXRRSET); 1014135446Strhodes } 1015135446Strhodes 1016135446Strhodes dns_diff_init(mctx, &d_rrs); 1017135446Strhodes dns_diff_init(mctx, &u_rrs); 1018135446Strhodes 1019135446Strhodes for (result = dns_rdataset_first(&rdataset); 1020135446Strhodes result == ISC_R_SUCCESS; 1021135446Strhodes result = dns_rdataset_next(&rdataset)) 1022135446Strhodes { 1023135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1024135446Strhodes dns_rdataset_current(&rdataset, &rdata); 1025135446Strhodes result = temp_append(&d_rrs, name, &rdata); 1026135446Strhodes if (result != ISC_R_SUCCESS) 1027135446Strhodes goto failure; 1028135446Strhodes } 1029135446Strhodes if (result != ISC_R_NOMORE) 1030135446Strhodes goto failure; 1031135446Strhodes result = dns_diff_sort(&d_rrs, temp_order); 1032135446Strhodes if (result != ISC_R_SUCCESS) 1033135446Strhodes goto failure; 1034135446Strhodes 1035135446Strhodes /* 1036135446Strhodes * Collect all update RRs for this name and type 1037135446Strhodes * onto u_rrs. No need to sort them here - 1038135446Strhodes * they are already sorted. 1039135446Strhodes */ 1040135446Strhodes while (t != NULL && 1041135446Strhodes dns_name_equal(&t->name, name) && 1042135446Strhodes t->rdata.type == type) 1043135446Strhodes { 1044135446Strhodes dns_difftuple_t *next = 1045135446Strhodes ISC_LIST_NEXT(t, link); 1046135446Strhodes ISC_LIST_UNLINK(temp->tuples, t, link); 1047135446Strhodes ISC_LIST_APPEND(u_rrs.tuples, t, link); 1048135446Strhodes t = next; 1049135446Strhodes } 1050135446Strhodes 1051135446Strhodes /* Compare the two sorted lists. */ 1052135446Strhodes result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples), 1053135446Strhodes ISC_LIST_HEAD(d_rrs.tuples)); 1054135446Strhodes if (result != ISC_R_SUCCESS) 1055135446Strhodes goto failure; 1056135446Strhodes 1057135446Strhodes /* 1058135446Strhodes * We are done with the tuples, but we can't free 1059135446Strhodes * them yet because "name" still points into one 1060135446Strhodes * of them. Move them on a temporary list. 1061135446Strhodes */ 1062135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link); 1063135446Strhodes ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link); 1064135446Strhodes dns_rdataset_disassociate(&rdataset); 1065135446Strhodes 1066135446Strhodes continue; 1067135446Strhodes 1068135446Strhodes failure: 1069135446Strhodes dns_diff_clear(&d_rrs); 1070135446Strhodes dns_diff_clear(&u_rrs); 1071135446Strhodes dns_diff_clear(&trash); 1072135446Strhodes dns_rdataset_disassociate(&rdataset); 1073135446Strhodes dns_db_detachnode(db, &node); 1074135446Strhodes return (result); 1075135446Strhodes } 1076135446Strhodes 1077135446Strhodes dns_db_detachnode(db, &node); 1078135446Strhodes } 1079135446Strhodes 1080135446Strhodes dns_diff_clear(&trash); 1081135446Strhodes return (ISC_R_SUCCESS); 1082135446Strhodes} 1083135446Strhodes 1084135446Strhodes/**************************************************************************/ 1085135446Strhodes/* 1086135446Strhodes * Conditional deletion of RRs. 1087135446Strhodes */ 1088135446Strhodes 1089170222Sdougb/*% 1090135446Strhodes * Context structure for delete_if(). 1091135446Strhodes */ 1092135446Strhodes 1093135446Strhodestypedef struct { 1094135446Strhodes rr_predicate *predicate; 1095135446Strhodes dns_db_t *db; 1096135446Strhodes dns_dbversion_t *ver; 1097135446Strhodes dns_diff_t *diff; 1098135446Strhodes dns_name_t *name; 1099135446Strhodes dns_rdata_t *update_rr; 1100135446Strhodes} conditional_delete_ctx_t; 1101135446Strhodes 1102170222Sdougb/*% 1103135446Strhodes * Predicate functions for delete_if(). 1104135446Strhodes */ 1105135446Strhodes 1106170222Sdougb/*% 1107143731Sdougb * Return true iff 'db_rr' is neither a SOA nor an NS RR nor 1108193149Sdougb * an RRSIG nor an NSEC3PARAM nor a NSEC. 1109135446Strhodes */ 1110135446Strhodesstatic isc_boolean_t 1111135446Strhodestype_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1112135446Strhodes UNUSED(update_rr); 1113135446Strhodes return ((db_rr->type != dns_rdatatype_soa && 1114143731Sdougb db_rr->type != dns_rdatatype_ns && 1115193149Sdougb db_rr->type != dns_rdatatype_nsec3param && 1116143731Sdougb db_rr->type != dns_rdatatype_rrsig && 1117143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 1118135446Strhodes ISC_TRUE : ISC_FALSE); 1119135446Strhodes} 1120135446Strhodes 1121170222Sdougb/*% 1122143731Sdougb * Return true iff 'db_rr' is neither a RRSIG nor a NSEC. 1123143731Sdougb */ 1124143731Sdougbstatic isc_boolean_t 1125143731Sdougbtype_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1126143731Sdougb UNUSED(update_rr); 1127143731Sdougb return ((db_rr->type != dns_rdatatype_rrsig && 1128143731Sdougb db_rr->type != dns_rdatatype_nsec) ? 1129143731Sdougb ISC_TRUE : ISC_FALSE); 1130143731Sdougb} 1131143731Sdougb 1132170222Sdougb/*% 1133135446Strhodes * Return true always. 1134135446Strhodes */ 1135135446Strhodesstatic isc_boolean_t 1136135446Strhodestrue_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1137135446Strhodes UNUSED(update_rr); 1138135446Strhodes UNUSED(db_rr); 1139135446Strhodes return (ISC_TRUE); 1140135446Strhodes} 1141135446Strhodes 1142170222Sdougb/*% 1143135446Strhodes * Return true iff the two RRs have identical rdata. 1144135446Strhodes */ 1145135446Strhodesstatic isc_boolean_t 1146135446Strhodesrr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1147135446Strhodes /* 1148135446Strhodes * XXXRTH This is not a problem, but we should consider creating 1149135446Strhodes * dns_rdata_equal() (that used dns_name_equal()), since it 1150135446Strhodes * would be faster. Not a priority. 1151135446Strhodes */ 1152224092Sdougb return (dns_rdata_casecompare(update_rr, db_rr) == 0 ? 1153135446Strhodes ISC_TRUE : ISC_FALSE); 1154135446Strhodes} 1155135446Strhodes 1156170222Sdougb/*% 1157135446Strhodes * Return true iff 'update_rr' should replace 'db_rr' according 1158135446Strhodes * to the special RFC2136 rules for CNAME, SOA, and WKS records. 1159135446Strhodes * 1160135446Strhodes * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs 1161135446Strhodes * make little sense, so we replace those, too. 1162193149Sdougb * 1163193149Sdougb * Additionally replace RRSIG that have been generated by the same key 1164193149Sdougb * for the same type. This simplifies refreshing a offline KSK by not 1165193149Sdougb * requiring that the old RRSIG be deleted. It also simplifies key 1166193149Sdougb * rollover by only requiring that the new RRSIG be added. 1167135446Strhodes */ 1168135446Strhodesstatic isc_boolean_t 1169135446Strhodesreplaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { 1170193149Sdougb dns_rdata_rrsig_t updatesig, dbsig; 1171193149Sdougb isc_result_t result; 1172193149Sdougb 1173135446Strhodes if (db_rr->type != update_rr->type) 1174135446Strhodes return (ISC_FALSE); 1175135446Strhodes if (db_rr->type == dns_rdatatype_cname) 1176135446Strhodes return (ISC_TRUE); 1177135446Strhodes if (db_rr->type == dns_rdatatype_dname) 1178135446Strhodes return (ISC_TRUE); 1179135446Strhodes if (db_rr->type == dns_rdatatype_soa) 1180135446Strhodes return (ISC_TRUE); 1181135446Strhodes if (db_rr->type == dns_rdatatype_nsec) 1182135446Strhodes return (ISC_TRUE); 1183193149Sdougb if (db_rr->type == dns_rdatatype_rrsig) { 1184193149Sdougb /* 1185193149Sdougb * Replace existing RRSIG with the same keyid, 1186193149Sdougb * covered and algorithm. 1187193149Sdougb */ 1188193149Sdougb result = dns_rdata_tostruct(db_rr, &dbsig, NULL); 1189193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1190193149Sdougb result = dns_rdata_tostruct(update_rr, &updatesig, NULL); 1191193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1192193149Sdougb if (dbsig.keyid == updatesig.keyid && 1193193149Sdougb dbsig.covered == updatesig.covered && 1194193149Sdougb dbsig.algorithm == updatesig.algorithm) 1195193149Sdougb return (ISC_TRUE); 1196193149Sdougb } 1197135446Strhodes if (db_rr->type == dns_rdatatype_wks) { 1198135446Strhodes /* 1199135446Strhodes * Compare the address and protocol fields only. These 1200135446Strhodes * form the first five bytes of the RR data. Do a 1201135446Strhodes * raw binary comparison; unpacking the WKS RRs using 1202193149Sdougb * dns_rdata_tostruct() might be cleaner in some ways. 1203135446Strhodes */ 1204135446Strhodes INSIST(db_rr->length >= 5 && update_rr->length >= 5); 1205135446Strhodes return (memcmp(db_rr->data, update_rr->data, 5) == 0 ? 1206135446Strhodes ISC_TRUE : ISC_FALSE); 1207135446Strhodes } 1208193149Sdougb 1209193149Sdougb if (db_rr->type == dns_rdatatype_nsec3param) { 1210193149Sdougb if (db_rr->length != update_rr->length) 1211193149Sdougb return (ISC_FALSE); 1212193149Sdougb INSIST(db_rr->length >= 4 && update_rr->length >= 4); 1213193149Sdougb /* 1214224092Sdougb * Replace NSEC3PARAM records that only differ by the 1215224092Sdougb * flags field. 1216193149Sdougb */ 1217193149Sdougb if (db_rr->data[0] == update_rr->data[0] && 1218193149Sdougb memcmp(db_rr->data+2, update_rr->data+2, 1219193149Sdougb update_rr->length - 2) == 0) 1220193149Sdougb return (ISC_TRUE); 1221193149Sdougb } 1222135446Strhodes return (ISC_FALSE); 1223135446Strhodes} 1224135446Strhodes 1225170222Sdougb/*% 1226135446Strhodes * Internal helper function for delete_if(). 1227135446Strhodes */ 1228135446Strhodesstatic isc_result_t 1229135446Strhodesdelete_if_action(void *data, rr_t *rr) { 1230135446Strhodes conditional_delete_ctx_t *ctx = data; 1231135446Strhodes if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { 1232135446Strhodes isc_result_t result; 1233135446Strhodes result = update_one_rr(ctx->db, ctx->ver, ctx->diff, 1234135446Strhodes DNS_DIFFOP_DEL, ctx->name, 1235135446Strhodes rr->ttl, &rr->rdata); 1236135446Strhodes return (result); 1237135446Strhodes } else { 1238135446Strhodes return (ISC_R_SUCCESS); 1239135446Strhodes } 1240135446Strhodes} 1241135446Strhodes 1242170222Sdougb/*% 1243135446Strhodes * Conditionally delete RRs. Apply 'predicate' to the RRs 1244135446Strhodes * specified by 'db', 'ver', 'name', and 'type' (which can 1245135446Strhodes * be dns_rdatatype_any to match any type). Delete those 1246135446Strhodes * RRs for which the predicate returns true, and log the 1247135446Strhodes * deletions in 'diff'. 1248135446Strhodes */ 1249135446Strhodesstatic isc_result_t 1250193149Sdougbdelete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, 1251193149Sdougb dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, 1252193149Sdougb dns_rdata_t *update_rr, dns_diff_t *diff) 1253135446Strhodes{ 1254135446Strhodes conditional_delete_ctx_t ctx; 1255135446Strhodes ctx.predicate = predicate; 1256135446Strhodes ctx.db = db; 1257135446Strhodes ctx.ver = ver; 1258135446Strhodes ctx.diff = diff; 1259135446Strhodes ctx.name = name; 1260135446Strhodes ctx.update_rr = update_rr; 1261135446Strhodes return (foreach_rr(db, ver, name, type, covers, 1262135446Strhodes delete_if_action, &ctx)); 1263135446Strhodes} 1264135446Strhodes 1265135446Strhodes/**************************************************************************/ 1266170222Sdougb/*% 1267135446Strhodes * Prepare an RR for the addition of the new RR 'ctx->update_rr', 1268135446Strhodes * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting 1269135446Strhodes * the RRs if it is replaced by the new RR or has a conflicting TTL. 1270135446Strhodes * The necessary changes are appended to ctx->del_diff and ctx->add_diff; 1271135446Strhodes * we need to do all deletions before any additions so that we don't run 1272135446Strhodes * into transient states with conflicting TTLs. 1273135446Strhodes */ 1274135446Strhodes 1275135446Strhodestypedef struct { 1276135446Strhodes dns_db_t *db; 1277135446Strhodes dns_dbversion_t *ver; 1278135446Strhodes dns_diff_t *diff; 1279135446Strhodes dns_name_t *name; 1280135446Strhodes dns_rdata_t *update_rr; 1281135446Strhodes dns_ttl_t update_rr_ttl; 1282135446Strhodes isc_boolean_t ignore_add; 1283135446Strhodes dns_diff_t del_diff; 1284135446Strhodes dns_diff_t add_diff; 1285135446Strhodes} add_rr_prepare_ctx_t; 1286135446Strhodes 1287135446Strhodesstatic isc_result_t 1288135446Strhodesadd_rr_prepare_action(void *data, rr_t *rr) { 1289186462Sdougb isc_result_t result = ISC_R_SUCCESS; 1290135446Strhodes add_rr_prepare_ctx_t *ctx = data; 1291135446Strhodes dns_difftuple_t *tuple = NULL; 1292135446Strhodes isc_boolean_t equal; 1293135446Strhodes 1294135446Strhodes /* 1295135446Strhodes * If the update RR is a "duplicate" of the update RR, 1296135446Strhodes * the update should be silently ignored. 1297135446Strhodes */ 1298224092Sdougb equal = ISC_TF(dns_rdata_casecompare(&rr->rdata, ctx->update_rr) == 0); 1299135446Strhodes if (equal && rr->ttl == ctx->update_rr_ttl) { 1300135446Strhodes ctx->ignore_add = ISC_TRUE; 1301135446Strhodes return (ISC_R_SUCCESS); 1302135446Strhodes } 1303135446Strhodes 1304135446Strhodes /* 1305135446Strhodes * If this RR is "equal" to the update RR, it should 1306135446Strhodes * be deleted before the update RR is added. 1307135446Strhodes */ 1308135446Strhodes if (replaces_p(ctx->update_rr, &rr->rdata)) { 1309193149Sdougb CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, 1310193149Sdougb ctx->name, rr->ttl, &rr->rdata, 1311135446Strhodes &tuple)); 1312135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1313135446Strhodes return (ISC_R_SUCCESS); 1314135446Strhodes } 1315135446Strhodes 1316135446Strhodes /* 1317135446Strhodes * If this RR differs in TTL from the update RR, 1318135446Strhodes * its TTL must be adjusted. 1319135446Strhodes */ 1320135446Strhodes if (rr->ttl != ctx->update_rr_ttl) { 1321193149Sdougb CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL, 1322193149Sdougb ctx->name, rr->ttl, &rr->rdata, 1323135446Strhodes &tuple)); 1324135446Strhodes dns_diff_append(&ctx->del_diff, &tuple); 1325135446Strhodes if (!equal) { 1326135446Strhodes CHECK(dns_difftuple_create(ctx->add_diff.mctx, 1327135446Strhodes DNS_DIFFOP_ADD, ctx->name, 1328135446Strhodes ctx->update_rr_ttl, 1329193149Sdougb &rr->rdata, &tuple)); 1330135446Strhodes dns_diff_append(&ctx->add_diff, &tuple); 1331135446Strhodes } 1332135446Strhodes } 1333135446Strhodes failure: 1334135446Strhodes return (result); 1335135446Strhodes} 1336135446Strhodes 1337135446Strhodes/**************************************************************************/ 1338135446Strhodes/* 1339135446Strhodes * Miscellaneous subroutines. 1340135446Strhodes */ 1341135446Strhodes 1342170222Sdougb/*% 1343135446Strhodes * Extract a single update RR from 'section' of dynamic update message 1344135446Strhodes * 'msg', with consistency checking. 1345135446Strhodes * 1346135446Strhodes * Stores the owner name, rdata, and TTL of the update RR at 'name', 1347135446Strhodes * 'rdata', and 'ttl', respectively. 1348135446Strhodes */ 1349135446Strhodesstatic void 1350135446Strhodesget_current_rr(dns_message_t *msg, dns_section_t section, 1351193149Sdougb dns_rdataclass_t zoneclass, dns_name_t **name, 1352193149Sdougb dns_rdata_t *rdata, dns_rdatatype_t *covers, 1353193149Sdougb dns_ttl_t *ttl, dns_rdataclass_t *update_class) 1354135446Strhodes{ 1355135446Strhodes dns_rdataset_t *rdataset; 1356135446Strhodes isc_result_t result; 1357135446Strhodes dns_message_currentname(msg, section, name); 1358135446Strhodes rdataset = ISC_LIST_HEAD((*name)->list); 1359135446Strhodes INSIST(rdataset != NULL); 1360135446Strhodes INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); 1361135446Strhodes *covers = rdataset->covers; 1362135446Strhodes *ttl = rdataset->ttl; 1363135446Strhodes result = dns_rdataset_first(rdataset); 1364135446Strhodes INSIST(result == ISC_R_SUCCESS); 1365135446Strhodes dns_rdataset_current(rdataset, rdata); 1366135446Strhodes INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE); 1367135446Strhodes *update_class = rdata->rdclass; 1368135446Strhodes rdata->rdclass = zoneclass; 1369135446Strhodes} 1370135446Strhodes 1371170222Sdougb/*% 1372135446Strhodes * Increment the SOA serial number of database 'db', version 'ver'. 1373135446Strhodes * Replace the SOA record in the database, and log the 1374135446Strhodes * change in 'diff'. 1375135446Strhodes */ 1376135446Strhodes 1377135446Strhodes /* 1378135446Strhodes * XXXRTH Failures in this routine will be worth logging, when 1379135446Strhodes * we have a logging system. Failure to find the zonename 1380135446Strhodes * or the SOA rdataset warrant at least an UNEXPECTED_ERROR(). 1381135446Strhodes */ 1382135446Strhodes 1383135446Strhodesstatic isc_result_t 1384254897Serwinupdate_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, 1385254897Serwin isc_mem_t *mctx, dns_updatemethod_t method) 1386135446Strhodes{ 1387135446Strhodes dns_difftuple_t *deltuple = NULL; 1388135446Strhodes dns_difftuple_t *addtuple = NULL; 1389135446Strhodes isc_uint32_t serial; 1390135446Strhodes isc_result_t result; 1391135446Strhodes 1392135446Strhodes CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); 1393135446Strhodes CHECK(dns_difftuple_copy(deltuple, &addtuple)); 1394135446Strhodes addtuple->op = DNS_DIFFOP_ADD; 1395135446Strhodes 1396135446Strhodes serial = dns_soa_getserial(&addtuple->rdata); 1397254897Serwin serial = dns_update_soaserial(serial, method); 1398135446Strhodes dns_soa_setserial(serial, &addtuple->rdata); 1399135446Strhodes CHECK(do_one_tuple(&deltuple, db, ver, diff)); 1400135446Strhodes CHECK(do_one_tuple(&addtuple, db, ver, diff)); 1401135446Strhodes result = ISC_R_SUCCESS; 1402135446Strhodes 1403135446Strhodes failure: 1404135446Strhodes if (addtuple != NULL) 1405135446Strhodes dns_difftuple_free(&addtuple); 1406135446Strhodes if (deltuple != NULL) 1407135446Strhodes dns_difftuple_free(&deltuple); 1408135446Strhodes return (result); 1409135446Strhodes} 1410135446Strhodes 1411170222Sdougb/*% 1412135446Strhodes * Check that the new SOA record at 'update_rdata' does not 1413135446Strhodes * illegally cause the SOA serial number to decrease or stay 1414135446Strhodes * unchanged relative to the existing SOA in 'db'. 1415135446Strhodes * 1416135446Strhodes * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not. 1417135446Strhodes * 1418135446Strhodes * William King points out that RFC2136 is inconsistent about 1419135446Strhodes * the case where the serial number stays unchanged: 1420135446Strhodes * 1421135446Strhodes * section 3.4.2.2 requires a server to ignore a SOA update request 1422135446Strhodes * if the serial number on the update SOA is less_than_or_equal to 1423135446Strhodes * the zone SOA serial. 1424135446Strhodes * 1425135446Strhodes * section 3.6 requires a server to ignore a SOA update request if 1426135446Strhodes * the serial is less_than the zone SOA serial. 1427135446Strhodes * 1428135446Strhodes * Paul says 3.4.2.2 is correct. 1429135446Strhodes * 1430135446Strhodes */ 1431135446Strhodesstatic isc_result_t 1432135446Strhodescheck_soa_increment(dns_db_t *db, dns_dbversion_t *ver, 1433193149Sdougb dns_rdata_t *update_rdata, isc_boolean_t *ok) 1434135446Strhodes{ 1435135446Strhodes isc_uint32_t db_serial; 1436135446Strhodes isc_uint32_t update_serial; 1437135446Strhodes isc_result_t result; 1438135446Strhodes 1439135446Strhodes update_serial = dns_soa_getserial(update_rdata); 1440135446Strhodes 1441135446Strhodes result = dns_db_getsoaserial(db, ver, &db_serial); 1442135446Strhodes if (result != ISC_R_SUCCESS) 1443135446Strhodes return (result); 1444135446Strhodes 1445135446Strhodes if (DNS_SERIAL_GE(db_serial, update_serial)) { 1446135446Strhodes *ok = ISC_FALSE; 1447135446Strhodes } else { 1448135446Strhodes *ok = ISC_TRUE; 1449135446Strhodes } 1450135446Strhodes 1451135446Strhodes return (ISC_R_SUCCESS); 1452135446Strhodes 1453135446Strhodes} 1454135446Strhodes 1455135446Strhodes/**************************************************************************/ 1456170222Sdougb/*% 1457135446Strhodes * The actual update code in all its glory. We try to follow 1458135446Strhodes * the RFC2136 pseudocode as closely as possible. 1459135446Strhodes */ 1460135446Strhodes 1461135446Strhodesstatic isc_result_t 1462135446Strhodessend_update_event(ns_client_t *client, dns_zone_t *zone) { 1463135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1464135446Strhodes update_event_t *event = NULL; 1465135446Strhodes isc_task_t *zonetask = NULL; 1466135446Strhodes ns_client_t *evclient; 1467135446Strhodes 1468135446Strhodes event = (update_event_t *) 1469135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 1470135446Strhodes update_action, NULL, sizeof(*event)); 1471135446Strhodes if (event == NULL) 1472135446Strhodes FAIL(ISC_R_NOMEMORY); 1473135446Strhodes event->zone = zone; 1474135446Strhodes event->result = ISC_R_SUCCESS; 1475135446Strhodes 1476135446Strhodes evclient = NULL; 1477135446Strhodes ns_client_attach(client, &evclient); 1478135446Strhodes INSIST(client->nupdates == 0); 1479135446Strhodes client->nupdates++; 1480135446Strhodes event->ev_arg = evclient; 1481135446Strhodes 1482135446Strhodes dns_zone_gettask(zone, &zonetask); 1483135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 1484135446Strhodes 1485135446Strhodes failure: 1486135446Strhodes if (event != NULL) 1487135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 1488135446Strhodes return (result); 1489135446Strhodes} 1490135446Strhodes 1491135446Strhodesstatic void 1492135446Strhodesrespond(ns_client_t *client, isc_result_t result) { 1493135446Strhodes isc_result_t msg_result; 1494135446Strhodes 1495135446Strhodes msg_result = dns_message_reply(client->message, ISC_TRUE); 1496135446Strhodes if (msg_result != ISC_R_SUCCESS) 1497135446Strhodes goto msg_failure; 1498135446Strhodes client->message->rcode = dns_result_torcode(result); 1499135446Strhodes 1500135446Strhodes ns_client_send(client); 1501135446Strhodes return; 1502135446Strhodes 1503135446Strhodes msg_failure: 1504135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 1505135446Strhodes ISC_LOG_ERROR, 1506135446Strhodes "could not create update response message: %s", 1507135446Strhodes isc_result_totext(msg_result)); 1508135446Strhodes ns_client_next(client, msg_result); 1509135446Strhodes} 1510135446Strhodes 1511135446Strhodesvoid 1512135446Strhodesns_update_start(ns_client_t *client, isc_result_t sigresult) { 1513135446Strhodes dns_message_t *request = client->message; 1514135446Strhodes isc_result_t result; 1515135446Strhodes dns_name_t *zonename; 1516135446Strhodes dns_rdataset_t *zone_rdataset; 1517254897Serwin dns_zone_t *zone = NULL, *raw = NULL; 1518135446Strhodes 1519135446Strhodes /* 1520135446Strhodes * Interpret the zone section. 1521135446Strhodes */ 1522135446Strhodes result = dns_message_firstname(request, DNS_SECTION_ZONE); 1523135446Strhodes if (result != ISC_R_SUCCESS) 1524193149Sdougb FAILC(DNS_R_FORMERR, "update zone section empty"); 1525135446Strhodes 1526135446Strhodes /* 1527135446Strhodes * The zone section must contain exactly one "question", and 1528135446Strhodes * it must be of type SOA. 1529135446Strhodes */ 1530135446Strhodes zonename = NULL; 1531135446Strhodes dns_message_currentname(request, DNS_SECTION_ZONE, &zonename); 1532135446Strhodes zone_rdataset = ISC_LIST_HEAD(zonename->list); 1533135446Strhodes if (zone_rdataset->type != dns_rdatatype_soa) 1534135446Strhodes FAILC(DNS_R_FORMERR, 1535135446Strhodes "update zone section contains non-SOA"); 1536135446Strhodes if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) 1537135446Strhodes FAILC(DNS_R_FORMERR, 1538135446Strhodes "update zone section contains multiple RRs"); 1539135446Strhodes 1540135446Strhodes /* The zone section must have exactly one name. */ 1541135446Strhodes result = dns_message_nextname(request, DNS_SECTION_ZONE); 1542135446Strhodes if (result != ISC_R_NOMORE) 1543135446Strhodes FAILC(DNS_R_FORMERR, 1544135446Strhodes "update zone section contains multiple RRs"); 1545135446Strhodes 1546135446Strhodes result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, 1547135446Strhodes &zone); 1548135446Strhodes if (result != ISC_R_SUCCESS) 1549193149Sdougb FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); 1550135446Strhodes 1551254897Serwin /* 1552254897Serwin * If there is a raw (unsigned) zone associated with this 1553254897Serwin * zone then it processes the UPDATE request. 1554254897Serwin */ 1555254897Serwin dns_zone_getraw(zone, &raw); 1556254897Serwin if (raw != NULL) { 1557254897Serwin dns_zone_detach(&zone); 1558254897Serwin dns_zone_attach(raw, &zone); 1559254897Serwin dns_zone_detach(&raw); 1560254897Serwin } 1561254897Serwin 1562135446Strhodes switch(dns_zone_gettype(zone)) { 1563135446Strhodes case dns_zone_master: 1564224092Sdougb case dns_zone_dlz: 1565135446Strhodes /* 1566135446Strhodes * We can now fail due to a bad signature as we now know 1567135446Strhodes * that we are the master. 1568135446Strhodes */ 1569135446Strhodes if (sigresult != ISC_R_SUCCESS) 1570135446Strhodes FAIL(sigresult); 1571135446Strhodes CHECK(send_update_event(client, zone)); 1572135446Strhodes break; 1573135446Strhodes case dns_zone_slave: 1574135446Strhodes CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone), 1575193149Sdougb "update forwarding", zonename, ISC_TRUE, 1576193149Sdougb ISC_FALSE)); 1577135446Strhodes CHECK(send_forward_event(client, zone)); 1578135446Strhodes break; 1579135446Strhodes default: 1580193149Sdougb FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); 1581135446Strhodes } 1582135446Strhodes return; 1583135446Strhodes 1584135446Strhodes failure: 1585193149Sdougb if (result == DNS_R_REFUSED) { 1586193149Sdougb INSIST(dns_zone_gettype(zone) == dns_zone_slave); 1587193149Sdougb inc_stats(zone, dns_nsstatscounter_updaterej); 1588193149Sdougb } 1589135446Strhodes /* 1590135446Strhodes * We failed without having sent an update event to the zone. 1591135446Strhodes * We are still in the client task context, so we can 1592135446Strhodes * simply give an error response without switching tasks. 1593135446Strhodes */ 1594135446Strhodes respond(client, result); 1595135446Strhodes if (zone != NULL) 1596135446Strhodes dns_zone_detach(&zone); 1597135446Strhodes} 1598135446Strhodes 1599170222Sdougb/*% 1600135446Strhodes * DS records are not allowed to exist without corresponding NS records, 1601193149Sdougb * RFC 3658, 2.2 Protocol Change, 1602135446Strhodes * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex". 1603135446Strhodes */ 1604135446Strhodes 1605135446Strhodesstatic isc_result_t 1606135446Strhodesremove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) { 1607135446Strhodes isc_result_t result; 1608193149Sdougb isc_boolean_t ns_exists; 1609193149Sdougb dns_difftuple_t *tupple; 1610193149Sdougb dns_diff_t temp_diff; 1611135446Strhodes 1612193149Sdougb dns_diff_init(diff->mctx, &temp_diff); 1613193149Sdougb 1614193149Sdougb for (tupple = ISC_LIST_HEAD(diff->tuples); 1615193149Sdougb tupple != NULL; 1616193149Sdougb tupple = ISC_LIST_NEXT(tupple, link)) { 1617193149Sdougb if (!((tupple->op == DNS_DIFFOP_DEL && 1618193149Sdougb tupple->rdata.type == dns_rdatatype_ns) || 1619193149Sdougb (tupple->op == DNS_DIFFOP_ADD && 1620193149Sdougb tupple->rdata.type == dns_rdatatype_ds))) 1621135446Strhodes continue; 1622193149Sdougb CHECK(rrset_exists(db, newver, &tupple->name, 1623193149Sdougb dns_rdatatype_ns, 0, &ns_exists)); 1624193149Sdougb if (ns_exists && 1625193149Sdougb !dns_name_equal(&tupple->name, dns_db_origin(db))) 1626135446Strhodes continue; 1627193149Sdougb CHECK(delete_if(true_p, db, newver, &tupple->name, 1628193149Sdougb dns_rdatatype_ds, 0, NULL, &temp_diff)); 1629135446Strhodes } 1630193149Sdougb result = ISC_R_SUCCESS; 1631135446Strhodes 1632135446Strhodes failure: 1633193149Sdougb for (tupple = ISC_LIST_HEAD(temp_diff.tuples); 1634193149Sdougb tupple != NULL; 1635193149Sdougb tupple = ISC_LIST_HEAD(temp_diff.tuples)) { 1636193149Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tupple, link); 1637193149Sdougb dns_diff_appendminimal(diff, &tupple); 1638193149Sdougb } 1639135446Strhodes return (result); 1640135446Strhodes} 1641135446Strhodes 1642170222Sdougb/* 1643170222Sdougb * This implements the post load integrity checks for mx records. 1644170222Sdougb */ 1645170222Sdougbstatic isc_result_t 1646170222Sdougbcheck_mx(ns_client_t *client, dns_zone_t *zone, 1647170222Sdougb dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) 1648170222Sdougb{ 1649170222Sdougb char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")]; 1650170222Sdougb char ownerbuf[DNS_NAME_FORMATSIZE]; 1651170222Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 1652170222Sdougb char altbuf[DNS_NAME_FORMATSIZE]; 1653170222Sdougb dns_difftuple_t *t; 1654170222Sdougb dns_fixedname_t fixed; 1655170222Sdougb dns_name_t *foundname; 1656170222Sdougb dns_rdata_mx_t mx; 1657170222Sdougb dns_rdata_t rdata; 1658170222Sdougb isc_boolean_t ok = ISC_TRUE; 1659170222Sdougb isc_boolean_t isaddress; 1660170222Sdougb isc_result_t result; 1661170222Sdougb struct in6_addr addr6; 1662170222Sdougb struct in_addr addr; 1663170222Sdougb unsigned int options; 1664170222Sdougb 1665170222Sdougb dns_fixedname_init(&fixed); 1666170222Sdougb foundname = dns_fixedname_name(&fixed); 1667170222Sdougb dns_rdata_init(&rdata); 1668170222Sdougb options = dns_zone_getoptions(zone); 1669170222Sdougb 1670170222Sdougb for (t = ISC_LIST_HEAD(diff->tuples); 1671170222Sdougb t != NULL; 1672170222Sdougb t = ISC_LIST_NEXT(t, link)) { 1673174187Sdougb if (t->op != DNS_DIFFOP_ADD || 1674170222Sdougb t->rdata.type != dns_rdatatype_mx) 1675170222Sdougb continue; 1676170222Sdougb 1677170222Sdougb result = dns_rdata_tostruct(&t->rdata, &mx, NULL); 1678170222Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1679170222Sdougb /* 1680170222Sdougb * Check if we will error out if we attempt to reload the 1681170222Sdougb * zone. 1682170222Sdougb */ 1683170222Sdougb dns_name_format(&mx.mx, namebuf, sizeof(namebuf)); 1684170222Sdougb dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf)); 1685170222Sdougb isaddress = ISC_FALSE; 1686170222Sdougb if ((options & DNS_RDATA_CHECKMX) != 0 && 1687170222Sdougb strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) { 1688170222Sdougb if (tmp[strlen(tmp) - 1] == '.') 1689170222Sdougb tmp[strlen(tmp) - 1] = '\0'; 1690170222Sdougb if (inet_aton(tmp, &addr) == 1 || 1691170222Sdougb inet_pton(AF_INET6, tmp, &addr6) == 1) 1692170222Sdougb isaddress = ISC_TRUE; 1693170222Sdougb } 1694170222Sdougb 1695170222Sdougb if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) { 1696170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 1697170222Sdougb "%s/MX: '%s': %s", 1698170222Sdougb ownerbuf, namebuf, 1699170222Sdougb dns_result_totext(DNS_R_MXISADDRESS)); 1700170222Sdougb ok = ISC_FALSE; 1701170222Sdougb } else if (isaddress) { 1702170222Sdougb update_log(client, zone, ISC_LOG_WARNING, 1703170222Sdougb "%s/MX: warning: '%s': %s", 1704170222Sdougb ownerbuf, namebuf, 1705170222Sdougb dns_result_totext(DNS_R_MXISADDRESS)); 1706170222Sdougb } 1707186462Sdougb 1708170222Sdougb /* 1709170222Sdougb * Check zone integrity checks. 1710170222Sdougb */ 1711170222Sdougb if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0) 1712170222Sdougb continue; 1713170222Sdougb result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a, 1714170222Sdougb 0, 0, NULL, foundname, NULL, NULL); 1715170222Sdougb if (result == ISC_R_SUCCESS) 1716170222Sdougb continue; 1717170222Sdougb 1718170222Sdougb if (result == DNS_R_NXRRSET) { 1719170222Sdougb result = dns_db_find(db, &mx.mx, newver, 1720170222Sdougb dns_rdatatype_aaaa, 1721170222Sdougb 0, 0, NULL, foundname, 1722170222Sdougb NULL, NULL); 1723170222Sdougb if (result == ISC_R_SUCCESS) 1724170222Sdougb continue; 1725170222Sdougb } 1726170222Sdougb 1727170222Sdougb if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) { 1728170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 1729170222Sdougb "%s/MX '%s' has no address records " 1730170222Sdougb "(A or AAAA)", ownerbuf, namebuf); 1731170222Sdougb ok = ISC_FALSE; 1732170222Sdougb } else if (result == DNS_R_CNAME) { 1733170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 1734170222Sdougb "%s/MX '%s' is a CNAME (illegal)", 1735170222Sdougb ownerbuf, namebuf); 1736170222Sdougb ok = ISC_FALSE; 1737170222Sdougb } else if (result == DNS_R_DNAME) { 1738170222Sdougb dns_name_format(foundname, altbuf, sizeof altbuf); 1739170222Sdougb update_log(client, zone, ISC_LOG_ERROR, 1740170222Sdougb "%s/MX '%s' is below a DNAME '%s' (illegal)", 1741170222Sdougb ownerbuf, namebuf, altbuf); 1742170222Sdougb ok = ISC_FALSE; 1743170222Sdougb } 1744170222Sdougb } 1745170222Sdougb return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED); 1746170222Sdougb} 1747170222Sdougb 1748193149Sdougbstatic isc_result_t 1749193149Sdougbrr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, 1750193149Sdougb const dns_rdata_t *rdata, isc_boolean_t *flag) 1751193149Sdougb{ 1752193149Sdougb dns_rdataset_t rdataset; 1753193149Sdougb dns_dbnode_t *node = NULL; 1754193149Sdougb isc_result_t result; 1755193149Sdougb 1756193149Sdougb dns_rdataset_init(&rdataset); 1757193149Sdougb if (rdata->type == dns_rdatatype_nsec3) 1758193149Sdougb CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node)); 1759193149Sdougb else 1760193149Sdougb CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); 1761193149Sdougb result = dns_db_findrdataset(db, node, ver, rdata->type, 0, 1762193149Sdougb (isc_stdtime_t) 0, &rdataset, NULL); 1763193149Sdougb if (result == ISC_R_NOTFOUND) { 1764193149Sdougb *flag = ISC_FALSE; 1765193149Sdougb result = ISC_R_SUCCESS; 1766193149Sdougb goto failure; 1767193149Sdougb } 1768193149Sdougb 1769193149Sdougb for (result = dns_rdataset_first(&rdataset); 1770193149Sdougb result == ISC_R_SUCCESS; 1771193149Sdougb result = dns_rdataset_next(&rdataset)) { 1772193149Sdougb dns_rdata_t myrdata = DNS_RDATA_INIT; 1773193149Sdougb dns_rdataset_current(&rdataset, &myrdata); 1774224092Sdougb if (!dns_rdata_casecompare(&myrdata, rdata)) 1775193149Sdougb break; 1776193149Sdougb } 1777193149Sdougb dns_rdataset_disassociate(&rdataset); 1778193149Sdougb if (result == ISC_R_SUCCESS) { 1779193149Sdougb *flag = ISC_TRUE; 1780193149Sdougb } else if (result == ISC_R_NOMORE) { 1781193149Sdougb *flag = ISC_FALSE; 1782193149Sdougb result = ISC_R_SUCCESS; 1783193149Sdougb } 1784193149Sdougb 1785193149Sdougb failure: 1786193149Sdougb if (node != NULL) 1787193149Sdougb dns_db_detachnode(db, &node); 1788193149Sdougb return (result); 1789193149Sdougb} 1790193149Sdougb 1791193149Sdougbstatic isc_result_t 1792224092Sdougbget_iterations(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype, 1793224092Sdougb unsigned int *iterationsp) 1794224092Sdougb{ 1795193149Sdougb dns_dbnode_t *node = NULL; 1796193149Sdougb dns_rdata_nsec3param_t nsec3param; 1797193149Sdougb dns_rdataset_t rdataset; 1798193149Sdougb isc_result_t result; 1799193149Sdougb unsigned int iterations = 0; 1800193149Sdougb 1801193149Sdougb dns_rdataset_init(&rdataset); 1802193149Sdougb 1803193149Sdougb result = dns_db_getoriginnode(db, &node); 1804193149Sdougb if (result != ISC_R_SUCCESS) 1805193149Sdougb return (result); 1806193149Sdougb result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 1807193149Sdougb 0, (isc_stdtime_t) 0, &rdataset, NULL); 1808193149Sdougb if (result == ISC_R_NOTFOUND) 1809224092Sdougb goto try_private; 1810224092Sdougb if (result != ISC_R_SUCCESS) 1811224092Sdougb goto failure; 1812224092Sdougb 1813224092Sdougb for (result = dns_rdataset_first(&rdataset); 1814224092Sdougb result == ISC_R_SUCCESS; 1815224092Sdougb result = dns_rdataset_next(&rdataset)) { 1816224092Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1817224092Sdougb dns_rdataset_current(&rdataset, &rdata); 1818224092Sdougb CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); 1819224092Sdougb if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) 1820224092Sdougb continue; 1821224092Sdougb if (nsec3param.iterations > iterations) 1822224092Sdougb iterations = nsec3param.iterations; 1823224092Sdougb } 1824224092Sdougb if (result != ISC_R_NOMORE) 1825224092Sdougb goto failure; 1826224092Sdougb 1827224092Sdougb dns_rdataset_disassociate(&rdataset); 1828224092Sdougb 1829224092Sdougb try_private: 1830224092Sdougb if (privatetype == 0) 1831193149Sdougb goto success; 1832224092Sdougb 1833224092Sdougb result = dns_db_findrdataset(db, node, ver, privatetype, 1834224092Sdougb 0, (isc_stdtime_t) 0, &rdataset, NULL); 1835224092Sdougb if (result == ISC_R_NOTFOUND) 1836224092Sdougb goto success; 1837193149Sdougb if (result != ISC_R_SUCCESS) 1838193149Sdougb goto failure; 1839193149Sdougb 1840193149Sdougb for (result = dns_rdataset_first(&rdataset); 1841193149Sdougb result == ISC_R_SUCCESS; 1842193149Sdougb result = dns_rdataset_next(&rdataset)) { 1843224092Sdougb unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; 1844224092Sdougb dns_rdata_t private = DNS_RDATA_INIT; 1845193149Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1846224092Sdougb 1847193149Sdougb dns_rdataset_current(&rdataset, &rdata); 1848224092Sdougb if (!dns_nsec3param_fromprivate(&private, &rdata, 1849224092Sdougb buf, sizeof(buf))) 1850224092Sdougb continue; 1851193149Sdougb CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); 1852193149Sdougb if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) 1853193149Sdougb continue; 1854193149Sdougb if (nsec3param.iterations > iterations) 1855193149Sdougb iterations = nsec3param.iterations; 1856193149Sdougb } 1857193149Sdougb if (result != ISC_R_NOMORE) 1858193149Sdougb goto failure; 1859193149Sdougb 1860193149Sdougb success: 1861193149Sdougb *iterationsp = iterations; 1862193149Sdougb result = ISC_R_SUCCESS; 1863193149Sdougb 1864193149Sdougb failure: 1865224092Sdougb if (node != NULL) 1866224092Sdougb dns_db_detachnode(db, &node); 1867193149Sdougb if (dns_rdataset_isassociated(&rdataset)) 1868193149Sdougb dns_rdataset_disassociate(&rdataset); 1869193149Sdougb return (result); 1870193149Sdougb} 1871193149Sdougb 1872193149Sdougb/* 1873193149Sdougb * Prevent the zone entering a inconsistent state where 1874193149Sdougb * NSEC only DNSKEYs are present with NSEC3 chains. 1875193149Sdougb */ 1876193149Sdougbstatic isc_result_t 1877193149Sdougbcheck_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1878193149Sdougb dns_dbversion_t *ver, dns_diff_t *diff) 1879193149Sdougb{ 1880224092Sdougb dns_difftuple_t *tuple; 1881224092Sdougb isc_boolean_t nseconly = ISC_FALSE, nsec3 = ISC_FALSE; 1882193149Sdougb isc_result_t result; 1883193149Sdougb unsigned int iterations = 0, max; 1884224092Sdougb dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); 1885193149Sdougb 1886224092Sdougb /* Scan the tuples for an NSEC-only DNSKEY or an NSEC3PARAM */ 1887224092Sdougb for (tuple = ISC_LIST_HEAD(diff->tuples); 1888224092Sdougb tuple != NULL; 1889224092Sdougb tuple = ISC_LIST_NEXT(tuple, link)) { 1890224092Sdougb if (tuple->op != DNS_DIFFOP_ADD) 1891224092Sdougb continue; 1892193149Sdougb 1893224092Sdougb if (tuple->rdata.type == dns_rdatatype_dnskey) { 1894224092Sdougb isc_uint8_t alg; 1895224092Sdougb alg = tuple->rdata.data[3]; 1896224092Sdougb if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 || 1897224092Sdougb alg == DST_ALG_DSA || alg == DST_ALG_ECC) { 1898224092Sdougb nseconly = ISC_TRUE; 1899224092Sdougb break; 1900224092Sdougb } 1901224092Sdougb } else if (tuple->rdata.type == dns_rdatatype_nsec3param) { 1902224092Sdougb nsec3 = ISC_TRUE; 1903224092Sdougb break; 1904224092Sdougb } 1905224092Sdougb } 1906193149Sdougb 1907224092Sdougb /* Check existing DB for NSEC-only DNSKEY */ 1908254897Serwin if (!nseconly) { 1909254897Serwin result = dns_nsec_nseconly(db, ver, &nseconly); 1910224092Sdougb 1911254897Serwin /* 1912254897Serwin * An NSEC3PARAM update can proceed without a DNSKEY (it 1913254897Serwin * will trigger a delayed change), so we can ignore 1914254897Serwin * ISC_R_NOTFOUND here. 1915254897Serwin */ 1916254897Serwin if (result == ISC_R_NOTFOUND) 1917254897Serwin result = ISC_R_SUCCESS; 1918254897Serwin 1919254897Serwin CHECK(result); 1920254897Serwin } 1921254897Serwin 1922224092Sdougb /* Check existing DB for NSEC3 */ 1923224092Sdougb if (!nsec3) 1924224092Sdougb CHECK(dns_nsec3_activex(db, ver, ISC_FALSE, 1925224092Sdougb privatetype, &nsec3)); 1926224092Sdougb 1927224092Sdougb /* Refuse to allow NSEC3 with NSEC-only keys */ 1928224092Sdougb if (nseconly && nsec3) { 1929224092Sdougb update_log(client, zone, ISC_LOG_ERROR, 1930193149Sdougb "NSEC only DNSKEYs and NSEC3 chains not allowed"); 1931224092Sdougb result = DNS_R_REFUSED; 1932224092Sdougb goto failure; 1933193149Sdougb } 1934224092Sdougb 1935224092Sdougb /* Verify NSEC3 params */ 1936224092Sdougb CHECK(get_iterations(db, ver, privatetype, &iterations)); 1937224092Sdougb CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max)); 1938224092Sdougb if (max != 0 && iterations > max) { 1939224092Sdougb update_log(client, zone, ISC_LOG_ERROR, 1940224092Sdougb "too many NSEC3 iterations (%u) for " 1941224092Sdougb "weakest DNSKEY (%u)", iterations, max); 1942224092Sdougb result = DNS_R_REFUSED; 1943224092Sdougb goto failure; 1944193149Sdougb } 1945193149Sdougb 1946193149Sdougb failure: 1947193149Sdougb return (result); 1948193149Sdougb} 1949193149Sdougb 1950193149Sdougb/* 1951193149Sdougb * Delay NSEC3PARAM changes as they need to be applied to the whole zone. 1952193149Sdougb */ 1953193149Sdougbstatic isc_result_t 1954193149Sdougbadd_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, 1955224092Sdougb dns_dbversion_t *ver, dns_diff_t *diff) 1956193149Sdougb{ 1957193149Sdougb isc_result_t result = ISC_R_SUCCESS; 1958193149Sdougb dns_difftuple_t *tuple, *newtuple = NULL, *next; 1959193149Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1960224092Sdougb unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE + 1]; 1961193149Sdougb dns_diff_t temp_diff; 1962193149Sdougb dns_diffop_t op; 1963193149Sdougb isc_boolean_t flag; 1964224092Sdougb dns_name_t *name = dns_zone_getorigin(zone); 1965224092Sdougb dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); 1966224092Sdougb isc_uint32_t ttl = 0; 1967224092Sdougb isc_boolean_t ttl_good = ISC_FALSE; 1968193149Sdougb 1969193149Sdougb update_log(client, zone, ISC_LOG_DEBUG(3), 1970193149Sdougb "checking for NSEC3PARAM changes"); 1971193149Sdougb 1972193149Sdougb dns_diff_init(diff->mctx, &temp_diff); 1973193149Sdougb 1974193149Sdougb /* 1975193149Sdougb * Extract NSEC3PARAM tuples from list. 1976193149Sdougb */ 1977193149Sdougb for (tuple = ISC_LIST_HEAD(diff->tuples); 1978193149Sdougb tuple != NULL; 1979193149Sdougb tuple = next) { 1980193149Sdougb 1981193149Sdougb next = ISC_LIST_NEXT(tuple, link); 1982193149Sdougb 1983193149Sdougb if (tuple->rdata.type != dns_rdatatype_nsec3param || 1984193149Sdougb !dns_name_equal(name, &tuple->name)) 1985193149Sdougb continue; 1986193149Sdougb ISC_LIST_UNLINK(diff->tuples, tuple, link); 1987193149Sdougb ISC_LIST_APPEND(temp_diff.tuples, tuple, link); 1988193149Sdougb } 1989193149Sdougb 1990224092Sdougb /* 1991224092Sdougb * Extract TTL changes pairs, we don't need to convert these to 1992224092Sdougb * delayed changes. 1993224092Sdougb */ 1994193149Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 1995193149Sdougb tuple != NULL; tuple = next) { 1996224092Sdougb if (tuple->op == DNS_DIFFOP_ADD) { 1997224092Sdougb if (!ttl_good) { 1998224092Sdougb /* 1999224092Sdougb * Any adds here will contain the final 2000224092Sdougb * NSEC3PARAM RRset TTL. 2001224092Sdougb */ 2002224092Sdougb ttl = tuple->ttl; 2003224092Sdougb ttl_good = ISC_TRUE; 2004224092Sdougb } 2005224092Sdougb /* 2006224092Sdougb * Walk the temp_diff list looking for the 2007224092Sdougb * corresponding delete. 2008224092Sdougb */ 2009224092Sdougb next = ISC_LIST_HEAD(temp_diff.tuples); 2010224092Sdougb while (next != NULL) { 2011224092Sdougb unsigned char *next_data = next->rdata.data; 2012224092Sdougb unsigned char *tuple_data = tuple->rdata.data; 2013224092Sdougb if (next->op == DNS_DIFFOP_DEL && 2014224092Sdougb next->rdata.length == tuple->rdata.length && 2015224092Sdougb !memcmp(next_data, tuple_data, 2016224092Sdougb next->rdata.length)) { 2017224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, next, 2018224092Sdougb link); 2019224092Sdougb ISC_LIST_APPEND(diff->tuples, next, 2020224092Sdougb link); 2021224092Sdougb break; 2022224092Sdougb } 2023224092Sdougb next = ISC_LIST_NEXT(next, link); 2024224092Sdougb } 2025224092Sdougb /* 2026224092Sdougb * If we have not found a pair move onto the next 2027224092Sdougb * tuple. 2028224092Sdougb */ 2029224092Sdougb if (next == NULL) { 2030224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2031224092Sdougb continue; 2032224092Sdougb } 2033224092Sdougb /* 2034224092Sdougb * Find the next tuple to be processed before 2035224092Sdougb * unlinking then complete moving the pair to 'diff'. 2036224092Sdougb */ 2037224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2038224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2039224092Sdougb ISC_LIST_APPEND(diff->tuples, tuple, link); 2040224092Sdougb } else 2041224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2042224092Sdougb } 2043193149Sdougb 2044224092Sdougb /* 2045224092Sdougb * Preserve any ongoing changes from a BIND 9.6.x upgrade. 2046224092Sdougb * 2047224092Sdougb * Any NSEC3PARAM records with flags other than OPTOUT named 2048224092Sdougb * in managing and should not be touched so revert such changes 2049224092Sdougb * taking into account any TTL change of the NSEC3PARAM RRset. 2050224092Sdougb */ 2051224092Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 2052224092Sdougb tuple != NULL; tuple = next) { 2053224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2054224092Sdougb if ((tuple->rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) { 2055224092Sdougb /* 2056224092Sdougb * If we havn't had any adds then the tuple->ttl must 2057224092Sdougb * be the original ttl and should be used for any 2058224092Sdougb * future changes. 2059224092Sdougb */ 2060224092Sdougb if (!ttl_good) { 2061224092Sdougb ttl = tuple->ttl; 2062224092Sdougb ttl_good = ISC_TRUE; 2063224092Sdougb } 2064224092Sdougb op = (tuple->op == DNS_DIFFOP_DEL) ? 2065224092Sdougb DNS_DIFFOP_ADD : DNS_DIFFOP_DEL; 2066224092Sdougb CHECK(dns_difftuple_create(diff->mctx, op, name, 2067224092Sdougb ttl, &tuple->rdata, 2068224092Sdougb &newtuple)); 2069224092Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2070224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2071224092Sdougb dns_diff_appendminimal(diff, &tuple); 2072224092Sdougb } 2073224092Sdougb } 2074224092Sdougb 2075224092Sdougb /* 2076224092Sdougb * We now have just the actual changes to the NSEC3PARAM RRset. 2077224092Sdougb * Convert the adds to delayed adds and the deletions into delayed 2078224092Sdougb * deletions. 2079224092Sdougb */ 2080224092Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 2081224092Sdougb tuple != NULL; tuple = next) { 2082224092Sdougb /* 2083224092Sdougb * If we havn't had any adds then the tuple->ttl must be the 2084224092Sdougb * original ttl and should be used for any future changes. 2085224092Sdougb */ 2086224092Sdougb if (!ttl_good) { 2087224092Sdougb ttl = tuple->ttl; 2088224092Sdougb ttl_good = ISC_TRUE; 2089224092Sdougb } 2090193149Sdougb if (tuple->op == DNS_DIFFOP_ADD) { 2091254897Serwin isc_boolean_t nseconly = ISC_FALSE; 2092254897Serwin 2093224092Sdougb /* 2094224092Sdougb * Look for any deletes which match this ADD ignoring 2095254897Serwin * flags. We don't need to explictly remove them as 2096224092Sdougb * they will be removed a side effect of processing 2097224092Sdougb * the add. 2098224092Sdougb */ 2099224092Sdougb next = ISC_LIST_HEAD(temp_diff.tuples); 2100193149Sdougb while (next != NULL) { 2101193149Sdougb unsigned char *next_data = next->rdata.data; 2102193149Sdougb unsigned char *tuple_data = tuple->rdata.data; 2103224092Sdougb if (next->op != DNS_DIFFOP_DEL || 2104224092Sdougb next->rdata.length != tuple->rdata.length || 2105224092Sdougb next_data[0] != tuple_data[0] || 2106193149Sdougb next_data[2] != tuple_data[2] || 2107193149Sdougb next_data[3] != tuple_data[3] || 2108224092Sdougb memcmp(next_data + 4, tuple_data + 4, 2109224092Sdougb tuple->rdata.length - 4)) { 2110193149Sdougb next = ISC_LIST_NEXT(next, link); 2111193149Sdougb continue; 2112193149Sdougb } 2113193149Sdougb ISC_LIST_UNLINK(temp_diff.tuples, next, link); 2114224092Sdougb ISC_LIST_APPEND(diff->tuples, next, link); 2115224092Sdougb next = ISC_LIST_HEAD(temp_diff.tuples); 2116193149Sdougb } 2117254897Serwin 2118193149Sdougb /* 2119254897Serwin * Create a private-type record to signal that 2120254897Serwin * we want a delayed NSEC3 chain add/delete 2121193149Sdougb */ 2122224092Sdougb dns_nsec3param_toprivate(&tuple->rdata, &rdata, 2123224092Sdougb privatetype, buf, sizeof(buf)); 2124224092Sdougb buf[2] |= DNS_NSEC3FLAG_CREATE; 2125254897Serwin 2126254897Serwin /* 2127254897Serwin * If the zone is not currently capable of 2128254897Serwin * supporting an NSEC3 chain, then we set the 2129254897Serwin * INITIAL flag to indicate that these parameters 2130254897Serwin * are to be used later. 2131254897Serwin */ 2132254897Serwin result = dns_nsec_nseconly(db, ver, &nseconly); 2133254897Serwin if (result == ISC_R_NOTFOUND || nseconly) 2134254897Serwin buf[2] |= DNS_NSEC3FLAG_INITIAL; 2135254897Serwin 2136254897Serwin /* 2137254897Serwin * See if this CREATE request already exists. 2138254897Serwin */ 2139193149Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2140193149Sdougb 2141193149Sdougb if (!flag) { 2142193149Sdougb CHECK(dns_difftuple_create(diff->mctx, 2143193149Sdougb DNS_DIFFOP_ADD, 2144224092Sdougb name, 0, &rdata, 2145193149Sdougb &newtuple)); 2146193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2147193149Sdougb } 2148204619Sdougb 2149193149Sdougb /* 2150204619Sdougb * Remove any existing CREATE request to add an 2151204619Sdougb * otherwise indentical chain with a reversed 2152204619Sdougb * OPTOUT state. 2153204619Sdougb */ 2154224092Sdougb buf[2] ^= DNS_NSEC3FLAG_OPTOUT; 2155204619Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2156204619Sdougb 2157204619Sdougb if (flag) { 2158204619Sdougb CHECK(dns_difftuple_create(diff->mctx, 2159204619Sdougb DNS_DIFFOP_DEL, 2160224092Sdougb name, 0, &rdata, 2161204619Sdougb &newtuple)); 2162204619Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2163204619Sdougb } 2164204619Sdougb 2165204619Sdougb /* 2166224092Sdougb * Find the next tuple to be processed and remove the 2167224092Sdougb * temporary add record. 2168193149Sdougb */ 2169224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2170193149Sdougb CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, 2171224092Sdougb name, ttl, &tuple->rdata, 2172224092Sdougb &newtuple)); 2173193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2174193149Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2175193149Sdougb dns_diff_appendminimal(diff, &tuple); 2176193149Sdougb dns_rdata_reset(&rdata); 2177193149Sdougb } else 2178193149Sdougb next = ISC_LIST_NEXT(tuple, link); 2179193149Sdougb } 2180193149Sdougb 2181193149Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 2182193149Sdougb tuple != NULL; tuple = next) { 2183193149Sdougb 2184224092Sdougb INSIST(ttl_good); 2185224092Sdougb 2186193149Sdougb next = ISC_LIST_NEXT(tuple, link); 2187193149Sdougb /* 2188193149Sdougb * See if we already have a REMOVE request in progress. 2189193149Sdougb */ 2190224092Sdougb dns_nsec3param_toprivate(&tuple->rdata, &rdata, privatetype, 2191224092Sdougb buf, sizeof(buf)); 2192193149Sdougb 2193224092Sdougb buf[2] |= DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC; 2194224092Sdougb 2195193149Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2196224092Sdougb if (!flag) { 2197224092Sdougb buf[2] &= ~DNS_NSEC3FLAG_NONSEC; 2198224092Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2199224092Sdougb } 2200193149Sdougb 2201193149Sdougb if (!flag) { 2202193149Sdougb CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, 2203224092Sdougb name, 0, &rdata, &newtuple)); 2204193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2205193149Sdougb } 2206193149Sdougb CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 2207224092Sdougb ttl, &tuple->rdata, &newtuple)); 2208193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2209193149Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2210193149Sdougb dns_diff_appendminimal(diff, &tuple); 2211193149Sdougb dns_rdata_reset(&rdata); 2212193149Sdougb } 2213193149Sdougb 2214193149Sdougb result = ISC_R_SUCCESS; 2215193149Sdougb failure: 2216193149Sdougb dns_diff_clear(&temp_diff); 2217193149Sdougb return (result); 2218193149Sdougb} 2219193149Sdougb 2220224092Sdougbstatic isc_result_t 2221224092Sdougbrollback_private(dns_db_t *db, dns_rdatatype_t privatetype, 2222224092Sdougb dns_dbversion_t *ver, dns_diff_t *diff) 2223224092Sdougb{ 2224224092Sdougb dns_diff_t temp_diff; 2225224092Sdougb dns_diffop_t op; 2226224092Sdougb dns_difftuple_t *tuple, *newtuple = NULL, *next; 2227224092Sdougb dns_name_t *name = dns_db_origin(db); 2228224092Sdougb isc_mem_t *mctx = diff->mctx; 2229224092Sdougb isc_result_t result; 2230224092Sdougb 2231224092Sdougb if (privatetype == 0) 2232224092Sdougb return (ISC_R_SUCCESS); 2233224092Sdougb 2234224092Sdougb dns_diff_init(mctx, &temp_diff); 2235224092Sdougb 2236224092Sdougb /* 2237224092Sdougb * Extract the changes to be rolled back. 2238224092Sdougb */ 2239224092Sdougb for (tuple = ISC_LIST_HEAD(diff->tuples); 2240224092Sdougb tuple != NULL; tuple = next) { 2241224092Sdougb 2242224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2243224092Sdougb 2244224092Sdougb if (tuple->rdata.type != privatetype || 2245224092Sdougb !dns_name_equal(name, &tuple->name)) 2246224092Sdougb continue; 2247224092Sdougb 2248224092Sdougb /* 2249224092Sdougb * Allow records which indicate that a zone has been 2250254897Serwin * signed with a DNSKEY to be removed. 2251224092Sdougb */ 2252224092Sdougb if (tuple->op == DNS_DIFFOP_DEL && 2253224092Sdougb tuple->rdata.length == 5 && 2254224092Sdougb tuple->rdata.data[0] != 0 && 2255224092Sdougb tuple->rdata.data[4] != 0) 2256224092Sdougb continue; 2257224092Sdougb 2258224092Sdougb ISC_LIST_UNLINK(diff->tuples, tuple, link); 2259224092Sdougb ISC_LIST_PREPEND(temp_diff.tuples, tuple, link); 2260224092Sdougb } 2261224092Sdougb 2262224092Sdougb /* 2263224092Sdougb * Rollback the changes. 2264224092Sdougb */ 2265224092Sdougb while ((tuple = ISC_LIST_HEAD(temp_diff.tuples)) != NULL) { 2266224092Sdougb op = (tuple->op == DNS_DIFFOP_DEL) ? 2267224092Sdougb DNS_DIFFOP_ADD : DNS_DIFFOP_DEL; 2268224092Sdougb CHECK(dns_difftuple_create(mctx, op, name, tuple->ttl, 2269224092Sdougb &tuple->rdata, &newtuple)); 2270224092Sdougb CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff)); 2271224092Sdougb } 2272224092Sdougb result = ISC_R_SUCCESS; 2273224092Sdougb 2274224092Sdougb failure: 2275224092Sdougb dns_diff_clear(&temp_diff); 2276224092Sdougb return (result); 2277224092Sdougb} 2278224092Sdougb 2279193149Sdougb/* 2280193149Sdougb * Add records to cause the delayed signing of the zone by added DNSKEY 2281193149Sdougb * to remove the RRSIG records generated by a deleted DNSKEY. 2282193149Sdougb */ 2283193149Sdougbstatic isc_result_t 2284224092Sdougbadd_signing_records(dns_db_t *db, dns_rdatatype_t privatetype, 2285224092Sdougb dns_dbversion_t *ver, dns_diff_t *diff) 2286193149Sdougb{ 2287224092Sdougb dns_difftuple_t *tuple, *newtuple = NULL, *next; 2288193149Sdougb dns_rdata_dnskey_t dnskey; 2289193149Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 2290193149Sdougb isc_boolean_t flag; 2291193149Sdougb isc_region_t r; 2292193149Sdougb isc_result_t result = ISC_R_SUCCESS; 2293193149Sdougb isc_uint16_t keyid; 2294193149Sdougb unsigned char buf[5]; 2295224092Sdougb dns_name_t *name = dns_db_origin(db); 2296224092Sdougb dns_diff_t temp_diff; 2297193149Sdougb 2298224092Sdougb dns_diff_init(diff->mctx, &temp_diff); 2299224092Sdougb 2300224092Sdougb /* 2301224092Sdougb * Extract the DNSKEY tuples from the list. 2302224092Sdougb */ 2303193149Sdougb for (tuple = ISC_LIST_HEAD(diff->tuples); 2304224092Sdougb tuple != NULL; tuple = next) { 2305224092Sdougb 2306224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2307224092Sdougb 2308193149Sdougb if (tuple->rdata.type != dns_rdatatype_dnskey) 2309193149Sdougb continue; 2310193149Sdougb 2311224092Sdougb ISC_LIST_UNLINK(diff->tuples, tuple, link); 2312224092Sdougb ISC_LIST_APPEND(temp_diff.tuples, tuple, link); 2313224092Sdougb } 2314224092Sdougb 2315224092Sdougb /* 2316224092Sdougb * Extract TTL changes pairs, we don't need signing records for these. 2317224092Sdougb */ 2318224092Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 2319224092Sdougb tuple != NULL; tuple = next) { 2320224092Sdougb if (tuple->op == DNS_DIFFOP_ADD) { 2321224092Sdougb /* 2322224092Sdougb * Walk the temp_diff list looking for the 2323224092Sdougb * corresponding delete. 2324224092Sdougb */ 2325224092Sdougb next = ISC_LIST_HEAD(temp_diff.tuples); 2326224092Sdougb while (next != NULL) { 2327224092Sdougb unsigned char *next_data = next->rdata.data; 2328224092Sdougb unsigned char *tuple_data = tuple->rdata.data; 2329224092Sdougb if (next->op == DNS_DIFFOP_DEL && 2330224092Sdougb dns_name_equal(&tuple->name, &next->name) && 2331224092Sdougb next->rdata.length == tuple->rdata.length && 2332224092Sdougb !memcmp(next_data, tuple_data, 2333224092Sdougb next->rdata.length)) { 2334224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, next, 2335224092Sdougb link); 2336224092Sdougb ISC_LIST_APPEND(diff->tuples, next, 2337224092Sdougb link); 2338224092Sdougb break; 2339224092Sdougb } 2340224092Sdougb next = ISC_LIST_NEXT(next, link); 2341224092Sdougb } 2342224092Sdougb /* 2343224092Sdougb * If we have not found a pair move onto the next 2344224092Sdougb * tuple. 2345224092Sdougb */ 2346224092Sdougb if (next == NULL) { 2347224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2348224092Sdougb continue; 2349224092Sdougb } 2350224092Sdougb /* 2351224092Sdougb * Find the next tuple to be processed before 2352224092Sdougb * unlinking then complete moving the pair to 'diff'. 2353224092Sdougb */ 2354224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2355224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2356224092Sdougb ISC_LIST_APPEND(diff->tuples, tuple, link); 2357224092Sdougb } else 2358224092Sdougb next = ISC_LIST_NEXT(tuple, link); 2359224092Sdougb } 2360224092Sdougb 2361224092Sdougb /* 2362224092Sdougb * Process the remaining DNSKEY entries. 2363224092Sdougb */ 2364224092Sdougb for (tuple = ISC_LIST_HEAD(temp_diff.tuples); 2365224092Sdougb tuple != NULL; 2366224092Sdougb tuple = ISC_LIST_HEAD(temp_diff.tuples)) { 2367224092Sdougb 2368224092Sdougb ISC_LIST_UNLINK(temp_diff.tuples, tuple, link); 2369224092Sdougb ISC_LIST_APPEND(diff->tuples, tuple, link); 2370224092Sdougb 2371254402Serwin result = dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL); 2372254402Serwin RUNTIME_CHECK(result == ISC_R_SUCCESS); 2373193149Sdougb if ((dnskey.flags & 2374193149Sdougb (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) 2375193149Sdougb != DNS_KEYOWNER_ZONE) 2376193149Sdougb continue; 2377193149Sdougb 2378193149Sdougb dns_rdata_toregion(&tuple->rdata, &r); 2379224092Sdougb 2380193149Sdougb keyid = dst_region_computeid(&r, dnskey.algorithm); 2381193149Sdougb 2382193149Sdougb buf[0] = dnskey.algorithm; 2383193149Sdougb buf[1] = (keyid & 0xff00) >> 8; 2384193149Sdougb buf[2] = (keyid & 0xff); 2385193149Sdougb buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1; 2386193149Sdougb buf[4] = 0; 2387193149Sdougb rdata.data = buf; 2388193149Sdougb rdata.length = sizeof(buf); 2389193149Sdougb rdata.type = privatetype; 2390193149Sdougb rdata.rdclass = tuple->rdata.rdclass; 2391193149Sdougb 2392193149Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2393193149Sdougb if (flag) 2394193149Sdougb continue; 2395193149Sdougb CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, 2396193149Sdougb name, 0, &rdata, &newtuple)); 2397193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2398193149Sdougb INSIST(newtuple == NULL); 2399193149Sdougb /* 2400193149Sdougb * Remove any record which says this operation has already 2401193149Sdougb * completed. 2402193149Sdougb */ 2403193149Sdougb buf[4] = 1; 2404193149Sdougb CHECK(rr_exists(db, ver, name, &rdata, &flag)); 2405193149Sdougb if (flag) { 2406193149Sdougb CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, 2407193149Sdougb name, 0, &rdata, &newtuple)); 2408193149Sdougb CHECK(do_one_tuple(&newtuple, db, ver, diff)); 2409193149Sdougb INSIST(newtuple == NULL); 2410193149Sdougb } 2411193149Sdougb } 2412224092Sdougb 2413193149Sdougb failure: 2414224092Sdougb dns_diff_clear(&temp_diff); 2415193149Sdougb return (result); 2416193149Sdougb} 2417193149Sdougb 2418224092Sdougbstatic isc_boolean_t 2419224092Sdougbisdnssec(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype) { 2420224092Sdougb isc_result_t result; 2421224092Sdougb isc_boolean_t build_nsec, build_nsec3; 2422193149Sdougb 2423224092Sdougb if (dns_db_issecure(db)) 2424224092Sdougb return (ISC_TRUE); 2425193149Sdougb 2426224092Sdougb result = dns_private_chains(db, ver, privatetype, 2427224092Sdougb &build_nsec, &build_nsec3); 2428224092Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2429224092Sdougb return (build_nsec || build_nsec3); 2430193149Sdougb} 2431193149Sdougb 2432135446Strhodesstatic void 2433135446Strhodesupdate_action(isc_task_t *task, isc_event_t *event) { 2434135446Strhodes update_event_t *uev = (update_event_t *) event; 2435135446Strhodes dns_zone_t *zone = uev->zone; 2436135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 2437135446Strhodes 2438135446Strhodes isc_result_t result; 2439135446Strhodes dns_db_t *db = NULL; 2440135446Strhodes dns_dbversion_t *oldver = NULL; 2441135446Strhodes dns_dbversion_t *ver = NULL; 2442193149Sdougb dns_diff_t diff; /* Pending updates. */ 2443193149Sdougb dns_diff_t temp; /* Pending RR existence assertions. */ 2444135446Strhodes isc_boolean_t soa_serial_changed = ISC_FALSE; 2445135446Strhodes isc_mem_t *mctx = client->mctx; 2446135446Strhodes dns_rdatatype_t covers; 2447135446Strhodes dns_message_t *request = client->message; 2448135446Strhodes dns_rdataclass_t zoneclass; 2449135446Strhodes dns_name_t *zonename; 2450135446Strhodes dns_ssutable_t *ssutable = NULL; 2451135446Strhodes dns_fixedname_t tmpnamefixed; 2452135446Strhodes dns_name_t *tmpname = NULL; 2453170222Sdougb unsigned int options; 2454193149Sdougb dns_difftuple_t *tuple; 2455193149Sdougb dns_rdata_dnskey_t dnskey; 2456193149Sdougb isc_boolean_t had_dnskey; 2457224092Sdougb dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); 2458135446Strhodes 2459135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATE); 2460135446Strhodes 2461135446Strhodes dns_diff_init(mctx, &diff); 2462135446Strhodes dns_diff_init(mctx, &temp); 2463135446Strhodes 2464135446Strhodes CHECK(dns_zone_getdb(zone, &db)); 2465135446Strhodes zonename = dns_db_origin(db); 2466135446Strhodes zoneclass = dns_db_class(db); 2467135446Strhodes dns_zone_getssutable(zone, &ssutable); 2468224092Sdougb 2469224092Sdougb /* 2470224092Sdougb * Update message processing can leak record existance information 2471224092Sdougb * so check that we are allowed to query this zone. Additionally 2472224092Sdougb * if we would refuse all updates for this zone we bail out here. 2473224092Sdougb */ 2474224092Sdougb CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone), zonename, 2475224092Sdougb dns_zone_getupdateacl(zone), ssutable)); 2476224092Sdougb 2477224092Sdougb /* 2478224092Sdougb * Get old and new versions now that queryacl has been checked. 2479224092Sdougb */ 2480135446Strhodes dns_db_currentversion(db, &oldver); 2481135446Strhodes CHECK(dns_db_newversion(db, &ver)); 2482135446Strhodes 2483135446Strhodes /* 2484135446Strhodes * Check prerequisites. 2485135446Strhodes */ 2486135446Strhodes 2487135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE); 2488135446Strhodes result == ISC_R_SUCCESS; 2489135446Strhodes result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE)) 2490135446Strhodes { 2491135446Strhodes dns_name_t *name = NULL; 2492135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2493135446Strhodes dns_ttl_t ttl; 2494135446Strhodes dns_rdataclass_t update_class; 2495135446Strhodes isc_boolean_t flag; 2496135446Strhodes 2497135446Strhodes get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass, 2498135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2499135446Strhodes 2500135446Strhodes if (ttl != 0) 2501193149Sdougb PREREQFAILC(DNS_R_FORMERR, 2502193149Sdougb "prerequisite TTL is not zero"); 2503135446Strhodes 2504135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2505193149Sdougb PREREQFAILN(DNS_R_NOTZONE, name, 2506193149Sdougb "prerequisite name is out of zone"); 2507135446Strhodes 2508135446Strhodes if (update_class == dns_rdataclass_any) { 2509135446Strhodes if (rdata.length != 0) 2510193149Sdougb PREREQFAILC(DNS_R_FORMERR, 2511135446Strhodes "class ANY prerequisite " 2512135446Strhodes "RDATA is not empty"); 2513135446Strhodes if (rdata.type == dns_rdatatype_any) { 2514135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2515135446Strhodes if (! flag) { 2516193149Sdougb PREREQFAILN(DNS_R_NXDOMAIN, name, 2517193149Sdougb "'name in use' " 2518193149Sdougb "prerequisite not " 2519193149Sdougb "satisfied"); 2520135446Strhodes } 2521135446Strhodes } else { 2522135446Strhodes CHECK(rrset_exists(db, ver, name, 2523135446Strhodes rdata.type, covers, &flag)); 2524135446Strhodes if (! flag) { 2525135446Strhodes /* RRset does not exist. */ 2526193149Sdougb PREREQFAILNT(DNS_R_NXRRSET, name, rdata.type, 2527135446Strhodes "'rrset exists (value independent)' " 2528135446Strhodes "prerequisite not satisfied"); 2529135446Strhodes } 2530135446Strhodes } 2531135446Strhodes } else if (update_class == dns_rdataclass_none) { 2532135446Strhodes if (rdata.length != 0) 2533193149Sdougb PREREQFAILC(DNS_R_FORMERR, 2534193149Sdougb "class NONE prerequisite " 2535193149Sdougb "RDATA is not empty"); 2536135446Strhodes if (rdata.type == dns_rdatatype_any) { 2537135446Strhodes CHECK(name_exists(db, ver, name, &flag)); 2538135446Strhodes if (flag) { 2539193149Sdougb PREREQFAILN(DNS_R_YXDOMAIN, name, 2540193149Sdougb "'name not in use' " 2541193149Sdougb "prerequisite not " 2542193149Sdougb "satisfied"); 2543135446Strhodes } 2544135446Strhodes } else { 2545135446Strhodes CHECK(rrset_exists(db, ver, name, 2546135446Strhodes rdata.type, covers, &flag)); 2547135446Strhodes if (flag) { 2548135446Strhodes /* RRset exists. */ 2549193149Sdougb PREREQFAILNT(DNS_R_YXRRSET, name, 2550193149Sdougb rdata.type, 2551193149Sdougb "'rrset does not exist' " 2552193149Sdougb "prerequisite not " 2553193149Sdougb "satisfied"); 2554135446Strhodes } 2555135446Strhodes } 2556135446Strhodes } else if (update_class == zoneclass) { 2557135446Strhodes /* "temp<rr.name, rr.type> += rr;" */ 2558135446Strhodes result = temp_append(&temp, name, &rdata); 2559135446Strhodes if (result != ISC_R_SUCCESS) { 2560135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 2561135446Strhodes "temp entry creation failed: %s", 2562135446Strhodes dns_result_totext(result)); 2563135446Strhodes FAIL(ISC_R_UNEXPECTED); 2564135446Strhodes } 2565135446Strhodes } else { 2566193149Sdougb PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite"); 2567135446Strhodes } 2568135446Strhodes } 2569135446Strhodes if (result != ISC_R_NOMORE) 2570135446Strhodes FAIL(result); 2571135446Strhodes 2572135446Strhodes /* 2573135446Strhodes * Perform the final check of the "rrset exists (value dependent)" 2574135446Strhodes * prerequisites. 2575135446Strhodes */ 2576135446Strhodes if (ISC_LIST_HEAD(temp.tuples) != NULL) { 2577135446Strhodes dns_rdatatype_t type; 2578135446Strhodes 2579135446Strhodes /* 2580135446Strhodes * Sort the prerequisite records by owner name, 2581135446Strhodes * type, and rdata. 2582135446Strhodes */ 2583135446Strhodes result = dns_diff_sort(&temp, temp_order); 2584135446Strhodes if (result != ISC_R_SUCCESS) 2585135446Strhodes FAILC(result, "'RRset exists (value dependent)' " 2586135446Strhodes "prerequisite not satisfied"); 2587135446Strhodes 2588135446Strhodes dns_fixedname_init(&tmpnamefixed); 2589135446Strhodes tmpname = dns_fixedname_name(&tmpnamefixed); 2590135446Strhodes result = temp_check(mctx, &temp, db, ver, tmpname, &type); 2591135446Strhodes if (result != ISC_R_SUCCESS) 2592135446Strhodes FAILNT(result, tmpname, type, 2593135446Strhodes "'RRset exists (value dependent)' " 2594135446Strhodes "prerequisite not satisfied"); 2595135446Strhodes } 2596135446Strhodes 2597135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2598135446Strhodes "prerequisites are OK"); 2599135446Strhodes 2600135446Strhodes /* 2601135446Strhodes * Check Requestor's Permissions. It seems a bit silly to do this 2602135446Strhodes * only after prerequisite testing, but that is what RFC2136 says. 2603135446Strhodes */ 2604135446Strhodes if (ssutable == NULL) 2605135446Strhodes CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone), 2606193149Sdougb "update", zonename, ISC_FALSE, ISC_FALSE)); 2607193149Sdougb else if (client->signer == NULL && !TCPCLIENT(client)) 2608135446Strhodes CHECK(checkupdateacl(client, NULL, "update", zonename, 2609193149Sdougb ISC_FALSE, ISC_TRUE)); 2610186462Sdougb 2611135446Strhodes if (dns_zone_getupdatedisabled(zone)) 2612193149Sdougb FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled " 2613193149Sdougb "because the zone is frozen. Use " 2614193149Sdougb "'rndc thaw' to re-enable updates."); 2615135446Strhodes 2616135446Strhodes /* 2617135446Strhodes * Perform the Update Section Prescan. 2618135446Strhodes */ 2619135446Strhodes 2620135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2621135446Strhodes result == ISC_R_SUCCESS; 2622135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2623135446Strhodes { 2624135446Strhodes dns_name_t *name = NULL; 2625135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2626135446Strhodes dns_ttl_t ttl; 2627135446Strhodes dns_rdataclass_t update_class; 2628135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2629135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2630135446Strhodes 2631135446Strhodes if (! dns_name_issubdomain(name, zonename)) 2632135446Strhodes FAILC(DNS_R_NOTZONE, 2633135446Strhodes "update RR is outside zone"); 2634135446Strhodes if (update_class == zoneclass) { 2635135446Strhodes /* 2636135446Strhodes * Check for meta-RRs. The RFC2136 pseudocode says 2637135446Strhodes * check for ANY|AXFR|MAILA|MAILB, but the text adds 2638135446Strhodes * "or any other QUERY metatype" 2639135446Strhodes */ 2640135446Strhodes if (dns_rdatatype_ismeta(rdata.type)) { 2641135446Strhodes FAILC(DNS_R_FORMERR, 2642135446Strhodes "meta-RR in update"); 2643135446Strhodes } 2644135446Strhodes result = dns_zone_checknames(zone, name, &rdata); 2645135446Strhodes if (result != ISC_R_SUCCESS) 2646135446Strhodes FAIL(DNS_R_REFUSED); 2647135446Strhodes } else if (update_class == dns_rdataclass_any) { 2648135446Strhodes if (ttl != 0 || rdata.length != 0 || 2649135446Strhodes (dns_rdatatype_ismeta(rdata.type) && 2650135446Strhodes rdata.type != dns_rdatatype_any)) 2651135446Strhodes FAILC(DNS_R_FORMERR, 2652135446Strhodes "meta-RR in update"); 2653135446Strhodes } else if (update_class == dns_rdataclass_none) { 2654135446Strhodes if (ttl != 0 || 2655135446Strhodes dns_rdatatype_ismeta(rdata.type)) 2656135446Strhodes FAILC(DNS_R_FORMERR, 2657135446Strhodes "meta-RR in update"); 2658135446Strhodes } else { 2659135446Strhodes update_log(client, zone, ISC_LOG_WARNING, 2660135446Strhodes "update RR has incorrect class %d", 2661135446Strhodes update_class); 2662135446Strhodes FAIL(DNS_R_FORMERR); 2663135446Strhodes } 2664224092Sdougb 2665135446Strhodes /* 2666135446Strhodes * draft-ietf-dnsind-simple-secure-update-01 says 2667135446Strhodes * "Unlike traditional dynamic update, the client 2668135446Strhodes * is forbidden from updating NSEC records." 2669135446Strhodes */ 2670224092Sdougb if (rdata.type == dns_rdatatype_nsec3) { 2671224092Sdougb FAILC(DNS_R_REFUSED, 2672224092Sdougb "explicit NSEC3 updates are not allowed " 2673224092Sdougb "in secure zones"); 2674224092Sdougb } else if (rdata.type == dns_rdatatype_nsec) { 2675224092Sdougb FAILC(DNS_R_REFUSED, 2676224092Sdougb "explicit NSEC updates are not allowed " 2677224092Sdougb "in secure zones"); 2678224092Sdougb } else if (rdata.type == dns_rdatatype_rrsig && 2679224092Sdougb !dns_name_equal(name, zonename)) { 2680224092Sdougb FAILC(DNS_R_REFUSED, 2681224092Sdougb "explicit RRSIG updates are currently " 2682224092Sdougb "not supported in secure zones except " 2683224092Sdougb "at the apex"); 2684135446Strhodes } 2685135446Strhodes 2686193149Sdougb if (ssutable != NULL) { 2687193149Sdougb isc_netaddr_t *tcpaddr, netaddr; 2688224092Sdougb dst_key_t *tsigkey = NULL; 2689193149Sdougb /* 2690193149Sdougb * If this is a TCP connection then pass the 2691193149Sdougb * address of the client through for tcp-self 2692193149Sdougb * and 6to4-self otherwise pass NULL. This 2693193149Sdougb * provides weak address based authentication. 2694193149Sdougb */ 2695193149Sdougb if (TCPCLIENT(client)) { 2696193149Sdougb isc_netaddr_fromsockaddr(&netaddr, 2697193149Sdougb &client->peeraddr); 2698193149Sdougb tcpaddr = &netaddr; 2699193149Sdougb } else 2700193149Sdougb tcpaddr = NULL; 2701224092Sdougb 2702224092Sdougb if (client->message->tsigkey != NULL) 2703224092Sdougb tsigkey = client->message->tsigkey->key; 2704224092Sdougb 2705135446Strhodes if (rdata.type != dns_rdatatype_any) { 2706135446Strhodes if (!dns_ssutable_checkrules(ssutable, 2707135446Strhodes client->signer, 2708193149Sdougb name, tcpaddr, 2709224092Sdougb rdata.type, 2710224092Sdougb tsigkey)) 2711135446Strhodes FAILC(DNS_R_REFUSED, 2712135446Strhodes "rejected by secure update"); 2713193149Sdougb } else { 2714135446Strhodes if (!ssu_checkall(db, ver, name, ssutable, 2715224092Sdougb client->signer, tcpaddr, 2716224092Sdougb tsigkey)) 2717135446Strhodes FAILC(DNS_R_REFUSED, 2718135446Strhodes "rejected by secure update"); 2719135446Strhodes } 2720135446Strhodes } 2721135446Strhodes } 2722135446Strhodes if (result != ISC_R_NOMORE) 2723135446Strhodes FAIL(result); 2724135446Strhodes 2725135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 2726135446Strhodes "update section prescan OK"); 2727135446Strhodes 2728135446Strhodes /* 2729135446Strhodes * Process the Update Section. 2730135446Strhodes */ 2731135446Strhodes 2732170222Sdougb options = dns_zone_getoptions(zone); 2733135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); 2734135446Strhodes result == ISC_R_SUCCESS; 2735135446Strhodes result = dns_message_nextname(request, DNS_SECTION_UPDATE)) 2736135446Strhodes { 2737135446Strhodes dns_name_t *name = NULL; 2738135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2739135446Strhodes dns_ttl_t ttl; 2740135446Strhodes dns_rdataclass_t update_class; 2741135446Strhodes isc_boolean_t flag; 2742135446Strhodes 2743135446Strhodes get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, 2744135446Strhodes &name, &rdata, &covers, &ttl, &update_class); 2745135446Strhodes 2746135446Strhodes if (update_class == zoneclass) { 2747135446Strhodes 2748135446Strhodes /* 2749170222Sdougb * RFC1123 doesn't allow MF and MD in master zones. */ 2750135446Strhodes if (rdata.type == dns_rdatatype_md || 2751135446Strhodes rdata.type == dns_rdatatype_mf) { 2752135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2753135446Strhodes 2754135446Strhodes dns_rdatatype_format(rdata.type, typebuf, 2755135446Strhodes sizeof(typebuf)); 2756135446Strhodes update_log(client, zone, LOGLEVEL_PROTOCOL, 2757135446Strhodes "attempt to add %s ignored", 2758135446Strhodes typebuf); 2759135446Strhodes continue; 2760135446Strhodes } 2761193149Sdougb if ((rdata.type == dns_rdatatype_ns || 2762193149Sdougb rdata.type == dns_rdatatype_dname) && 2763135446Strhodes dns_name_iswildcard(name)) { 2764193149Sdougb char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2765193149Sdougb 2766193149Sdougb dns_rdatatype_format(rdata.type, typebuf, 2767193149Sdougb sizeof(typebuf)); 2768135446Strhodes update_log(client, zone, 2769135446Strhodes LOGLEVEL_PROTOCOL, 2770193149Sdougb "attempt to add wildcard %s record " 2771193149Sdougb "ignored", typebuf); 2772135446Strhodes continue; 2773135446Strhodes } 2774135446Strhodes if (rdata.type == dns_rdatatype_cname) { 2775135446Strhodes CHECK(cname_incompatible_rrset_exists(db, ver, 2776135446Strhodes name, 2777135446Strhodes &flag)); 2778135446Strhodes if (flag) { 2779135446Strhodes update_log(client, zone, 2780135446Strhodes LOGLEVEL_PROTOCOL, 2781135446Strhodes "attempt to add CNAME " 2782135446Strhodes "alongside non-CNAME " 2783135446Strhodes "ignored"); 2784135446Strhodes continue; 2785135446Strhodes } 2786135446Strhodes } else { 2787135446Strhodes CHECK(rrset_exists(db, ver, name, 2788135446Strhodes dns_rdatatype_cname, 0, 2789135446Strhodes &flag)); 2790135446Strhodes if (flag && 2791135446Strhodes ! dns_rdatatype_isdnssec(rdata.type)) 2792135446Strhodes { 2793135446Strhodes update_log(client, zone, 2794135446Strhodes LOGLEVEL_PROTOCOL, 2795135446Strhodes "attempt to add non-CNAME " 2796135446Strhodes "alongside CNAME ignored"); 2797135446Strhodes continue; 2798135446Strhodes } 2799135446Strhodes } 2800135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2801135446Strhodes isc_boolean_t ok; 2802135446Strhodes CHECK(rrset_exists(db, ver, name, 2803135446Strhodes dns_rdatatype_soa, 0, 2804135446Strhodes &flag)); 2805135446Strhodes if (! flag) { 2806135446Strhodes update_log(client, zone, 2807135446Strhodes LOGLEVEL_PROTOCOL, 2808135446Strhodes "attempt to create 2nd " 2809135446Strhodes "SOA ignored"); 2810135446Strhodes continue; 2811135446Strhodes } 2812135446Strhodes CHECK(check_soa_increment(db, ver, &rdata, 2813135446Strhodes &ok)); 2814135446Strhodes if (! ok) { 2815135446Strhodes update_log(client, zone, 2816135446Strhodes LOGLEVEL_PROTOCOL, 2817135446Strhodes "SOA update failed to " 2818135446Strhodes "increment serial, " 2819135446Strhodes "ignoring it"); 2820135446Strhodes continue; 2821135446Strhodes } 2822135446Strhodes soa_serial_changed = ISC_TRUE; 2823135446Strhodes } 2824193149Sdougb 2825224092Sdougb if (rdata.type == privatetype) { 2826224092Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2827224092Sdougb "attempt to add a private type " 2828224092Sdougb "(%u) record rejected internal " 2829224092Sdougb "use only", privatetype); 2830224092Sdougb continue; 2831224092Sdougb } 2832224092Sdougb 2833193149Sdougb if (rdata.type == dns_rdatatype_nsec3param) { 2834193149Sdougb /* 2835193149Sdougb * Ignore attempts to add NSEC3PARAM records 2836193149Sdougb * with any flags other than OPTOUT. 2837193149Sdougb */ 2838193149Sdougb if ((rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) { 2839193149Sdougb update_log(client, zone, 2840193149Sdougb LOGLEVEL_PROTOCOL, 2841193149Sdougb "attempt to add NSEC3PARAM " 2842193149Sdougb "record with non OPTOUT " 2843193149Sdougb "flag"); 2844193149Sdougb continue; 2845193149Sdougb } 2846193149Sdougb } 2847193149Sdougb 2848170222Sdougb if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 && 2849170222Sdougb dns_name_internalwildcard(name)) { 2850170222Sdougb char namestr[DNS_NAME_FORMATSIZE]; 2851170222Sdougb dns_name_format(name, namestr, 2852170222Sdougb sizeof(namestr)); 2853170222Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2854170222Sdougb "warning: ownername '%s' contains " 2855170222Sdougb "a non-terminal wildcard", namestr); 2856170222Sdougb } 2857135446Strhodes 2858135446Strhodes if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { 2859135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2860135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2861135446Strhodes dns_name_format(name, namestr, 2862135446Strhodes sizeof(namestr)); 2863135446Strhodes dns_rdatatype_format(rdata.type, typestr, 2864135446Strhodes sizeof(typestr)); 2865193149Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2866135446Strhodes "adding an RR at '%s' %s", 2867135446Strhodes namestr, typestr); 2868135446Strhodes } 2869135446Strhodes 2870135446Strhodes /* Prepare the affected RRset for the addition. */ 2871135446Strhodes { 2872135446Strhodes add_rr_prepare_ctx_t ctx; 2873135446Strhodes ctx.db = db; 2874135446Strhodes ctx.ver = ver; 2875135446Strhodes ctx.diff = &diff; 2876135446Strhodes ctx.name = name; 2877135446Strhodes ctx.update_rr = &rdata; 2878135446Strhodes ctx.update_rr_ttl = ttl; 2879135446Strhodes ctx.ignore_add = ISC_FALSE; 2880135446Strhodes dns_diff_init(mctx, &ctx.del_diff); 2881135446Strhodes dns_diff_init(mctx, &ctx.add_diff); 2882135446Strhodes CHECK(foreach_rr(db, ver, name, rdata.type, 2883135446Strhodes covers, add_rr_prepare_action, 2884135446Strhodes &ctx)); 2885135446Strhodes 2886135446Strhodes if (ctx.ignore_add) { 2887135446Strhodes dns_diff_clear(&ctx.del_diff); 2888135446Strhodes dns_diff_clear(&ctx.add_diff); 2889135446Strhodes } else { 2890193149Sdougb CHECK(do_diff(&ctx.del_diff, db, ver, 2891193149Sdougb &diff)); 2892193149Sdougb CHECK(do_diff(&ctx.add_diff, db, ver, 2893193149Sdougb &diff)); 2894135446Strhodes CHECK(update_one_rr(db, ver, &diff, 2895135446Strhodes DNS_DIFFOP_ADD, 2896135446Strhodes name, ttl, &rdata)); 2897135446Strhodes } 2898135446Strhodes } 2899135446Strhodes } else if (update_class == dns_rdataclass_any) { 2900135446Strhodes if (rdata.type == dns_rdatatype_any) { 2901135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2902135446Strhodes LOGLEVEL_PROTOCOL)) 2903135446Strhodes { 2904135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2905135446Strhodes dns_name_format(name, namestr, 2906135446Strhodes sizeof(namestr)); 2907135446Strhodes update_log(client, zone, 2908135446Strhodes LOGLEVEL_PROTOCOL, 2909135446Strhodes "delete all rrsets from " 2910135446Strhodes "name '%s'", namestr); 2911135446Strhodes } 2912135446Strhodes if (dns_name_equal(name, zonename)) { 2913135446Strhodes CHECK(delete_if(type_not_soa_nor_ns_p, 2914135446Strhodes db, ver, name, 2915135446Strhodes dns_rdatatype_any, 0, 2916135446Strhodes &rdata, &diff)); 2917135446Strhodes } else { 2918143731Sdougb CHECK(delete_if(type_not_dnssec, 2919143731Sdougb db, ver, name, 2920135446Strhodes dns_rdatatype_any, 0, 2921135446Strhodes &rdata, &diff)); 2922135446Strhodes } 2923135446Strhodes } else if (dns_name_equal(name, zonename) && 2924135446Strhodes (rdata.type == dns_rdatatype_soa || 2925135446Strhodes rdata.type == dns_rdatatype_ns)) { 2926193149Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2927135446Strhodes "attempt to delete all SOA " 2928135446Strhodes "or NS records ignored"); 2929135446Strhodes continue; 2930135446Strhodes } else { 2931135446Strhodes if (isc_log_wouldlog(ns_g_lctx, 2932135446Strhodes LOGLEVEL_PROTOCOL)) 2933135446Strhodes { 2934135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 2935135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 2936135446Strhodes dns_name_format(name, namestr, 2937135446Strhodes sizeof(namestr)); 2938135446Strhodes dns_rdatatype_format(rdata.type, 2939135446Strhodes typestr, 2940135446Strhodes sizeof(typestr)); 2941135446Strhodes update_log(client, zone, 2942135446Strhodes LOGLEVEL_PROTOCOL, 2943135446Strhodes "deleting rrset at '%s' %s", 2944135446Strhodes namestr, typestr); 2945135446Strhodes } 2946135446Strhodes CHECK(delete_if(true_p, db, ver, name, 2947135446Strhodes rdata.type, covers, &rdata, 2948135446Strhodes &diff)); 2949135446Strhodes } 2950135446Strhodes } else if (update_class == dns_rdataclass_none) { 2951224092Sdougb char namestr[DNS_NAME_FORMATSIZE]; 2952224092Sdougb char typestr[DNS_RDATATYPE_FORMATSIZE]; 2953224092Sdougb 2954135446Strhodes /* 2955135446Strhodes * The (name == zonename) condition appears in 2956135446Strhodes * RFC2136 3.4.2.4 but is missing from the pseudocode. 2957135446Strhodes */ 2958135446Strhodes if (dns_name_equal(name, zonename)) { 2959135446Strhodes if (rdata.type == dns_rdatatype_soa) { 2960135446Strhodes update_log(client, zone, 2961135446Strhodes LOGLEVEL_PROTOCOL, 2962135446Strhodes "attempt to delete SOA " 2963135446Strhodes "ignored"); 2964135446Strhodes continue; 2965135446Strhodes } 2966135446Strhodes if (rdata.type == dns_rdatatype_ns) { 2967135446Strhodes int count; 2968135446Strhodes CHECK(rr_count(db, ver, name, 2969135446Strhodes dns_rdatatype_ns, 2970135446Strhodes 0, &count)); 2971135446Strhodes if (count == 1) { 2972135446Strhodes update_log(client, zone, 2973135446Strhodes LOGLEVEL_PROTOCOL, 2974135446Strhodes "attempt to " 2975135446Strhodes "delete last " 2976135446Strhodes "NS ignored"); 2977135446Strhodes continue; 2978135446Strhodes } 2979135446Strhodes } 2980135446Strhodes } 2981224092Sdougb dns_name_format(name, namestr, sizeof(namestr)); 2982224092Sdougb dns_rdatatype_format(rdata.type, typestr, 2983224092Sdougb sizeof(typestr)); 2984224092Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 2985224092Sdougb "deleting an RR at %s %s", namestr, typestr); 2986224092Sdougb CHECK(delete_if(rr_equal_p, db, ver, name, rdata.type, 2987224092Sdougb covers, &rdata, &diff)); 2988135446Strhodes } 2989135446Strhodes } 2990135446Strhodes if (result != ISC_R_NOMORE) 2991135446Strhodes FAIL(result); 2992135446Strhodes 2993135446Strhodes /* 2994193149Sdougb * Check that any changes to DNSKEY/NSEC3PARAM records make sense. 2995193149Sdougb * If they don't then back out all changes to DNSKEY/NSEC3PARAM 2996193149Sdougb * records. 2997193149Sdougb */ 2998193149Sdougb if (! ISC_LIST_EMPTY(diff.tuples)) 2999193149Sdougb CHECK(check_dnssec(client, zone, db, ver, &diff)); 3000193149Sdougb 3001224092Sdougb if (! ISC_LIST_EMPTY(diff.tuples)) { 3002224092Sdougb unsigned int errors = 0; 3003224092Sdougb CHECK(dns_zone_nscheck(zone, db, ver, &errors)); 3004224092Sdougb if (errors != 0) { 3005224092Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 3006224092Sdougb "update rejected: post update name server " 3007224092Sdougb "sanity check failed"); 3008224092Sdougb result = DNS_R_REFUSED; 3009224092Sdougb goto failure; 3010224092Sdougb } 3011224092Sdougb } 3012224092Sdougb 3013193149Sdougb /* 3014135446Strhodes * If any changes were made, increment the SOA serial number, 3015135446Strhodes * update RRSIGs and NSECs (if zone is secure), and write the update 3016135446Strhodes * to the journal. 3017135446Strhodes */ 3018135446Strhodes if (! ISC_LIST_EMPTY(diff.tuples)) { 3019135446Strhodes char *journalfile; 3020135446Strhodes dns_journal_t *journal; 3021193149Sdougb isc_boolean_t has_dnskey; 3022135446Strhodes 3023135446Strhodes /* 3024135446Strhodes * Increment the SOA serial, but only if it was not 3025135446Strhodes * changed as a result of an update operation. 3026135446Strhodes */ 3027135446Strhodes if (! soa_serial_changed) { 3028254897Serwin CHECK(update_soa_serial(db, ver, &diff, mctx, 3029254897Serwin dns_zone_getserialupdatemethod(zone))); 3030135446Strhodes } 3031135446Strhodes 3032170222Sdougb CHECK(check_mx(client, zone, db, ver, &diff)); 3033170222Sdougb 3034135446Strhodes CHECK(remove_orphaned_ds(db, ver, &diff)); 3035135446Strhodes 3036193149Sdougb CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey, 3037193149Sdougb 0, &has_dnskey)); 3038193149Sdougb 3039224092Sdougb#define ALLOW_SECURE_TO_INSECURE(zone) \ 3040224092Sdougb ((dns_zone_getoptions(zone) & DNS_ZONEOPT_SECURETOINSECURE) != 0) 3041193149Sdougb 3042224092Sdougb if (!ALLOW_SECURE_TO_INSECURE(zone)) { 3043224092Sdougb CHECK(rrset_exists(db, oldver, zonename, 3044224092Sdougb dns_rdatatype_dnskey, 0, 3045224092Sdougb &had_dnskey)); 3046224092Sdougb if (had_dnskey && !has_dnskey) { 3047224092Sdougb update_log(client, zone, LOGLEVEL_PROTOCOL, 3048224092Sdougb "update rejected: all DNSKEY " 3049224092Sdougb "records removed and " 3050224092Sdougb "'dnssec-secure-to-insecure' " 3051224092Sdougb "not set"); 3052224092Sdougb result = DNS_R_REFUSED; 3053224092Sdougb goto failure; 3054224092Sdougb } 3055193149Sdougb } 3056193149Sdougb 3057224092Sdougb CHECK(rollback_private(db, privatetype, ver, &diff)); 3058193149Sdougb 3059224092Sdougb CHECK(add_signing_records(db, privatetype, ver, &diff)); 3060193149Sdougb 3061224092Sdougb CHECK(add_nsec3param_records(client, zone, db, ver, &diff)); 3062224092Sdougb 3063254897Serwin if (had_dnskey && !has_dnskey) { 3064193149Sdougb /* 3065193149Sdougb * We are transitioning from secure to insecure. 3066193149Sdougb * Cause all NSEC3 chains to be deleted. When the 3067193149Sdougb * the last signature for the DNSKEY records are 3068193149Sdougb * remove any NSEC chain present will also be removed. 3069193149Sdougb */ 3070224092Sdougb CHECK(dns_nsec3param_deletechains(db, ver, zone, 3071254897Serwin ISC_TRUE, &diff)); 3072224092Sdougb } else if (has_dnskey && isdnssec(db, ver, privatetype)) { 3073193149Sdougb isc_uint32_t interval; 3074254897Serwin dns_update_log_t log; 3075254897Serwin 3076193149Sdougb interval = dns_zone_getsigvalidityinterval(zone); 3077254897Serwin log.func = update_log_cb; 3078254897Serwin log.arg = client; 3079254897Serwin result = dns_update_signatures(&log, zone, db, oldver, 3080254897Serwin ver, &diff, interval); 3081254897Serwin 3082135446Strhodes if (result != ISC_R_SUCCESS) { 3083135446Strhodes update_log(client, zone, 3084135446Strhodes ISC_LOG_ERROR, 3085193149Sdougb "RRSIG/NSEC/NSEC3 update failed: %s", 3086135446Strhodes isc_result_totext(result)); 3087135446Strhodes goto failure; 3088135446Strhodes } 3089135446Strhodes } 3090135446Strhodes 3091135446Strhodes journalfile = dns_zone_getjournal(zone); 3092135446Strhodes if (journalfile != NULL) { 3093135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 3094135446Strhodes "writing journal %s", journalfile); 3095135446Strhodes 3096135446Strhodes journal = NULL; 3097135446Strhodes result = dns_journal_open(mctx, journalfile, 3098254897Serwin DNS_JOURNAL_CREATE, &journal); 3099135446Strhodes if (result != ISC_R_SUCCESS) 3100135446Strhodes FAILS(result, "journal open failed"); 3101135446Strhodes 3102135446Strhodes result = dns_journal_write_transaction(journal, &diff); 3103135446Strhodes if (result != ISC_R_SUCCESS) { 3104135446Strhodes dns_journal_destroy(&journal); 3105135446Strhodes FAILS(result, "journal write failed"); 3106135446Strhodes } 3107135446Strhodes 3108135446Strhodes dns_journal_destroy(&journal); 3109135446Strhodes } 3110135446Strhodes 3111135446Strhodes /* 3112135446Strhodes * XXXRTH Just a note that this committing code will have 3113135446Strhodes * to change to handle databases that need two-phase 3114135446Strhodes * commit, but this isn't a priority. 3115135446Strhodes */ 3116135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, 3117135446Strhodes "committing update transaction"); 3118193149Sdougb 3119135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 3120135446Strhodes 3121135446Strhodes /* 3122135446Strhodes * Mark the zone as dirty so that it will be written to disk. 3123135446Strhodes */ 3124135446Strhodes dns_zone_markdirty(zone); 3125135446Strhodes 3126135446Strhodes /* 3127135446Strhodes * Notify slaves of the change we just made. 3128135446Strhodes */ 3129135446Strhodes dns_zone_notify(zone); 3130193149Sdougb 3131193149Sdougb /* 3132193149Sdougb * Cause the zone to be signed with the key that we 3133193149Sdougb * have just added or have the corresponding signatures 3134193149Sdougb * deleted. 3135193149Sdougb * 3136193149Sdougb * Note: we are already committed to this course of action. 3137193149Sdougb */ 3138193149Sdougb for (tuple = ISC_LIST_HEAD(diff.tuples); 3139193149Sdougb tuple != NULL; 3140193149Sdougb tuple = ISC_LIST_NEXT(tuple, link)) { 3141193149Sdougb isc_region_t r; 3142193149Sdougb dns_secalg_t algorithm; 3143193149Sdougb isc_uint16_t keyid; 3144193149Sdougb 3145193149Sdougb if (tuple->rdata.type != dns_rdatatype_dnskey) 3146193149Sdougb continue; 3147193149Sdougb 3148193149Sdougb dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL); 3149193149Sdougb if ((dnskey.flags & 3150193149Sdougb (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) 3151193149Sdougb != DNS_KEYOWNER_ZONE) 3152193149Sdougb continue; 3153193149Sdougb 3154193149Sdougb dns_rdata_toregion(&tuple->rdata, &r); 3155193149Sdougb algorithm = dnskey.algorithm; 3156193149Sdougb keyid = dst_region_computeid(&r, algorithm); 3157193149Sdougb 3158193149Sdougb result = dns_zone_signwithkey(zone, algorithm, keyid, 3159193149Sdougb ISC_TF(tuple->op == DNS_DIFFOP_DEL)); 3160193149Sdougb if (result != ISC_R_SUCCESS) { 3161193149Sdougb update_log(client, zone, ISC_LOG_ERROR, 3162193149Sdougb "dns_zone_signwithkey failed: %s", 3163193149Sdougb dns_result_totext(result)); 3164193149Sdougb } 3165193149Sdougb } 3166193149Sdougb 3167193149Sdougb /* 3168193149Sdougb * Cause the zone to add/delete NSEC3 chains for the 3169193149Sdougb * deferred NSEC3PARAM changes. 3170193149Sdougb * 3171193149Sdougb * Note: we are already committed to this course of action. 3172193149Sdougb */ 3173193149Sdougb for (tuple = ISC_LIST_HEAD(diff.tuples); 3174193149Sdougb tuple != NULL; 3175193149Sdougb tuple = ISC_LIST_NEXT(tuple, link)) { 3176224092Sdougb unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; 3177224092Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 3178193149Sdougb dns_rdata_nsec3param_t nsec3param; 3179193149Sdougb 3180224092Sdougb if (tuple->rdata.type != privatetype || 3181193149Sdougb tuple->op != DNS_DIFFOP_ADD) 3182193149Sdougb continue; 3183193149Sdougb 3184224092Sdougb if (!dns_nsec3param_fromprivate(&tuple->rdata, &rdata, 3185224092Sdougb buf, sizeof(buf))) 3186224092Sdougb continue; 3187224092Sdougb dns_rdata_tostruct(&rdata, &nsec3param, NULL); 3188193149Sdougb if (nsec3param.flags == 0) 3189193149Sdougb continue; 3190193149Sdougb 3191193149Sdougb result = dns_zone_addnsec3chain(zone, &nsec3param); 3192193149Sdougb if (result != ISC_R_SUCCESS) { 3193193149Sdougb update_log(client, zone, ISC_LOG_ERROR, 3194193149Sdougb "dns_zone_addnsec3chain failed: %s", 3195193149Sdougb dns_result_totext(result)); 3196193149Sdougb } 3197193149Sdougb } 3198135446Strhodes } else { 3199135446Strhodes update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); 3200135446Strhodes dns_db_closeversion(db, &ver, ISC_TRUE); 3201135446Strhodes } 3202135446Strhodes result = ISC_R_SUCCESS; 3203135446Strhodes goto common; 3204135446Strhodes 3205135446Strhodes failure: 3206135446Strhodes /* 3207135446Strhodes * The reason for failure should have been logged at this point. 3208135446Strhodes */ 3209135446Strhodes if (ver != NULL) { 3210186462Sdougb update_log(client, zone, LOGLEVEL_DEBUG, 3211135446Strhodes "rolling back"); 3212135446Strhodes dns_db_closeversion(db, &ver, ISC_FALSE); 3213135446Strhodes } 3214135446Strhodes 3215135446Strhodes common: 3216135446Strhodes dns_diff_clear(&temp); 3217135446Strhodes dns_diff_clear(&diff); 3218135446Strhodes 3219135446Strhodes if (oldver != NULL) 3220135446Strhodes dns_db_closeversion(db, &oldver, ISC_FALSE); 3221135446Strhodes 3222135446Strhodes if (db != NULL) 3223135446Strhodes dns_db_detach(&db); 3224135446Strhodes 3225135446Strhodes if (ssutable != NULL) 3226135446Strhodes dns_ssutable_detach(&ssutable); 3227135446Strhodes 3228135446Strhodes isc_task_detach(&task); 3229135446Strhodes uev->result = result; 3230193149Sdougb if (zone != NULL) 3231193149Sdougb INSIST(uev->zone == zone); /* we use this later */ 3232135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 3233135446Strhodes uev->ev_action = updatedone_action; 3234135446Strhodes isc_task_send(client->task, &event); 3235135446Strhodes INSIST(event == NULL); 3236135446Strhodes} 3237135446Strhodes 3238135446Strhodesstatic void 3239135446Strhodesupdatedone_action(isc_task_t *task, isc_event_t *event) { 3240135446Strhodes update_event_t *uev = (update_event_t *) event; 3241135446Strhodes ns_client_t *client = (ns_client_t *) event->ev_arg; 3242135446Strhodes 3243135446Strhodes UNUSED(task); 3244135446Strhodes 3245135446Strhodes INSIST(event->ev_type == DNS_EVENT_UPDATEDONE); 3246135446Strhodes INSIST(task == client->task); 3247135446Strhodes 3248135446Strhodes INSIST(client->nupdates > 0); 3249193149Sdougb switch (uev->result) { 3250193149Sdougb case ISC_R_SUCCESS: 3251193149Sdougb inc_stats(uev->zone, dns_nsstatscounter_updatedone); 3252193149Sdougb break; 3253193149Sdougb case DNS_R_REFUSED: 3254193149Sdougb inc_stats(uev->zone, dns_nsstatscounter_updaterej); 3255193149Sdougb break; 3256193149Sdougb default: 3257193149Sdougb inc_stats(uev->zone, dns_nsstatscounter_updatefail); 3258193149Sdougb break; 3259193149Sdougb } 3260193149Sdougb if (uev->zone != NULL) 3261193149Sdougb dns_zone_detach(&uev->zone); 3262135446Strhodes client->nupdates--; 3263135446Strhodes respond(client, uev->result); 3264153816Sdougb isc_event_free(&event); 3265135446Strhodes ns_client_detach(&client); 3266135446Strhodes} 3267135446Strhodes 3268170222Sdougb/*% 3269135446Strhodes * Update forwarding support. 3270135446Strhodes */ 3271135446Strhodes 3272135446Strhodesstatic void 3273135446Strhodesforward_fail(isc_task_t *task, isc_event_t *event) { 3274186462Sdougb ns_client_t *client = (ns_client_t *)event->ev_arg; 3275135446Strhodes 3276135446Strhodes UNUSED(task); 3277135446Strhodes 3278135446Strhodes INSIST(client->nupdates > 0); 3279135446Strhodes client->nupdates--; 3280135446Strhodes respond(client, DNS_R_SERVFAIL); 3281153816Sdougb isc_event_free(&event); 3282135446Strhodes ns_client_detach(&client); 3283135446Strhodes} 3284135446Strhodes 3285135446Strhodes 3286135446Strhodesstatic void 3287135446Strhodesforward_callback(void *arg, isc_result_t result, dns_message_t *answer) { 3288135446Strhodes update_event_t *uev = arg; 3289135446Strhodes ns_client_t *client = uev->ev_arg; 3290193149Sdougb dns_zone_t *zone = uev->zone; 3291135446Strhodes 3292135446Strhodes if (result != ISC_R_SUCCESS) { 3293135446Strhodes INSIST(answer == NULL); 3294135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 3295135446Strhodes uev->ev_action = forward_fail; 3296193149Sdougb inc_stats(zone, dns_nsstatscounter_updatefwdfail); 3297135446Strhodes } else { 3298135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 3299135446Strhodes uev->ev_action = forward_done; 3300135446Strhodes uev->answer = answer; 3301193149Sdougb inc_stats(zone, dns_nsstatscounter_updaterespfwd); 3302135446Strhodes } 3303135446Strhodes isc_task_send(client->task, ISC_EVENT_PTR(&uev)); 3304193149Sdougb dns_zone_detach(&zone); 3305135446Strhodes} 3306135446Strhodes 3307135446Strhodesstatic void 3308135446Strhodesforward_done(isc_task_t *task, isc_event_t *event) { 3309135446Strhodes update_event_t *uev = (update_event_t *) event; 3310135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 3311135446Strhodes 3312135446Strhodes UNUSED(task); 3313135446Strhodes 3314135446Strhodes INSIST(client->nupdates > 0); 3315135446Strhodes client->nupdates--; 3316135446Strhodes ns_client_sendraw(client, uev->answer); 3317135446Strhodes dns_message_destroy(&uev->answer); 3318135446Strhodes isc_event_free(&event); 3319135446Strhodes ns_client_detach(&client); 3320135446Strhodes} 3321135446Strhodes 3322135446Strhodesstatic void 3323135446Strhodesforward_action(isc_task_t *task, isc_event_t *event) { 3324135446Strhodes update_event_t *uev = (update_event_t *) event; 3325135446Strhodes dns_zone_t *zone = uev->zone; 3326135446Strhodes ns_client_t *client = (ns_client_t *)event->ev_arg; 3327135446Strhodes isc_result_t result; 3328135446Strhodes 3329135446Strhodes result = dns_zone_forwardupdate(zone, client->message, 3330135446Strhodes forward_callback, event); 3331135446Strhodes if (result != ISC_R_SUCCESS) { 3332135446Strhodes uev->ev_type = DNS_EVENT_UPDATEDONE; 3333135446Strhodes uev->ev_action = forward_fail; 3334135446Strhodes isc_task_send(client->task, &event); 3335193149Sdougb inc_stats(zone, dns_nsstatscounter_updatefwdfail); 3336193149Sdougb dns_zone_detach(&zone); 3337193149Sdougb } else 3338193149Sdougb inc_stats(zone, dns_nsstatscounter_updatereqfwd); 3339135446Strhodes isc_task_detach(&task); 3340135446Strhodes} 3341135446Strhodes 3342135446Strhodesstatic isc_result_t 3343135446Strhodessend_forward_event(ns_client_t *client, dns_zone_t *zone) { 3344262706Serwin char namebuf[DNS_NAME_FORMATSIZE]; 3345262706Serwin char classbuf[DNS_RDATACLASS_FORMATSIZE]; 3346135446Strhodes isc_result_t result = ISC_R_SUCCESS; 3347135446Strhodes update_event_t *event = NULL; 3348135446Strhodes isc_task_t *zonetask = NULL; 3349135446Strhodes ns_client_t *evclient; 3350135446Strhodes 3351234010Sdougb /* 3352234010Sdougb * This may take some time so replace this client. 3353234010Sdougb */ 3354234010Sdougb if (!client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0) 3355234010Sdougb CHECK(ns_client_replace(client)); 3356234010Sdougb 3357135446Strhodes event = (update_event_t *) 3358135446Strhodes isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, 3359135446Strhodes forward_action, NULL, sizeof(*event)); 3360135446Strhodes if (event == NULL) 3361135446Strhodes FAIL(ISC_R_NOMEMORY); 3362135446Strhodes event->zone = zone; 3363135446Strhodes event->result = ISC_R_SUCCESS; 3364135446Strhodes 3365135446Strhodes evclient = NULL; 3366135446Strhodes ns_client_attach(client, &evclient); 3367135446Strhodes INSIST(client->nupdates == 0); 3368135446Strhodes client->nupdates++; 3369135446Strhodes event->ev_arg = evclient; 3370135446Strhodes 3371262706Serwin dns_name_format(dns_zone_getorigin(zone), namebuf, 3372262706Serwin sizeof(namebuf)); 3373262706Serwin dns_rdataclass_format(dns_zone_getclass(zone), classbuf, 3374262706Serwin sizeof(classbuf)); 3375262706Serwin 3376262706Serwin ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, 3377262706Serwin LOGLEVEL_PROTOCOL, "forwarding update for zone '%s/%s'", 3378262706Serwin namebuf, classbuf); 3379262706Serwin 3380135446Strhodes dns_zone_gettask(zone, &zonetask); 3381135446Strhodes isc_task_send(zonetask, ISC_EVENT_PTR(&event)); 3382135446Strhodes 3383135446Strhodes failure: 3384135446Strhodes if (event != NULL) 3385135446Strhodes isc_event_free(ISC_EVENT_PTR(&event)); 3386135446Strhodes return (result); 3387135446Strhodes} 3388