diff --git a/CHANGES b/CHANGES index 2c39ffd347..c2c470fd86 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +2349. [func] Provide incremental re-signing support for secure + dynamic zones. [RT #1091] + 2348. [func] Use the EVP interface to OpenSSL. Add PKCS#11 support. Documentation is in the new README.pkcs11 file. [RT #16844] diff --git a/bin/named/query.c b/bin/named/query.c index 507a634f37..21c47f5ffc 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.303 2008/01/24 02:00:43 jinmei Exp $ */ +/* $Id: query.c,v 1.304 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -1087,8 +1087,12 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { result = dns_db_find(db, name, version, type, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { + if (sigrdataset != NULL && !dns_db_issecure(db) && + dns_rdataset_isassociated(sigrdataset)) + dns_rdataset_disassociate(sigrdataset); goto found; + } if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); @@ -2025,7 +2029,7 @@ query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, eresult = DNS_R_SERVFAIL; goto cleanup; } - if (WANTDNSSEC(client)) { + if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { eresult = DNS_R_SERVFAIL; @@ -2143,7 +2147,7 @@ query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) { eresult = DNS_R_SERVFAIL; goto cleanup; } - if (WANTDNSSEC(client)) { + if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { CTRACE("query_addns: query_newrdataset failed"); @@ -3534,7 +3538,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } - if (WANTDNSSEC(client)) { + if (WANTDNSSEC(client) && (!is_zone || dns_db_issecure(db))) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); @@ -4173,7 +4177,16 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) result = dns_rdatasetiter_first(rdsiter); while (result == ISC_R_SUCCESS) { dns_rdatasetiter_current(rdsiter, rdataset); - if ((qtype == dns_rdatatype_any || + if (is_zone && qtype == dns_rdatatype_any && + !dns_db_issecure(db) && + dns_rdatatype_isdnssec(rdataset->type)) { + /* + * The zone is transitioning from insecure + * to secure. Hide the dnssec records from + * ANY queries. + */ + dns_rdataset_disassociate(rdataset); + } else if ((qtype == dns_rdatatype_any || rdataset->type == qtype) && rdataset->type != 0) { query_addrrset(client, fname != NULL ? &fname : &tname, diff --git a/bin/named/update.c b/bin/named/update.c index 10c7682d73..b26acccec3 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,12 +15,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.142 2008/02/07 03:12:15 marka Exp $ */ +/* $Id: update.c,v 1.143 2008/04/01 01:37:24 marka Exp $ */ #include #include #include +#include #include #include #include @@ -1063,9 +1064,17 @@ rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { * * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs * make little sense, so we replace those, too. + * + * Additionally replace RRSIG that have been generated by the same key + * for the same type. This simplifies refreshing a offline KSK by not + * requiring that the old RRSIG be deleted. It also simpifies key + * rollover by only requiring that the new RRSIG be added. */ static isc_boolean_t replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { + dns_rdata_rrsig_t updatesig, dbsig; + isc_result_t result; + if (db_rr->type != update_rr->type) return (ISC_FALSE); if (db_rr->type == dns_rdatatype_cname) @@ -1076,6 +1085,20 @@ replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { return (ISC_TRUE); if (db_rr->type == dns_rdatatype_nsec) return (ISC_TRUE); + if (db_rr->type == dns_rdatatype_rrsig) { + /* + * Replace existing RRSIG with the same keyid, + * covered and algorithm. + */ + result = dns_rdata_tostruct(db_rr, &dbsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = dns_rdata_tostruct(update_rr, &updatesig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (dbsig.keyid == updatesig.keyid && + dbsig.covered == updatesig.covered && + dbsig.algorithm == updatesig.algorithm) + return (ISC_TRUE); + } if (db_rr->type == dns_rdatatype_wks) { /* * Compare the address and protocol fields only. These @@ -1493,6 +1516,7 @@ next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, dns_dbiterator_t *dbit = NULL; isc_boolean_t has_nsec; unsigned int wraps = 0; + isc_boolean_t secure = dns_db_issecure(db); CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); @@ -1530,9 +1554,29 @@ next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, * we must pause the iterator first. */ CHECK(dns_dbiterator_pause(dbit)); - CHECK(rrset_exists(db, ver, newname, - dns_rdatatype_nsec, 0, &has_nsec)); - + if (secure) { + CHECK(rrset_exists(db, ver, newname, + dns_rdatatype_nsec, 0, &has_nsec)); + } else { + dns_fixedname_t ffound; + dns_name_t *found; + dns_fixedname_init(&ffound); + found = dns_fixedname_name(&ffound); + result = dns_db_find(db, newname, ver, + dns_rdatatype_soa, + DNS_DBFIND_NOWILD, 0, NULL, found, + NULL, NULL); + if (result == ISC_R_SUCCESS || + result == DNS_R_EMPTYNAME || + result == DNS_R_NXRRSET || + result == DNS_R_CNAME || + (result == DNS_R_DELEGATION && + dns_name_equal(newname, found))) { + has_nsec = ISC_TRUE; + result = ISC_R_SUCCESS; + } else if (result != DNS_R_NXDOMAIN) + break; + } } while (! has_nsec); failure: if (dbit != NULL) @@ -1541,6 +1585,35 @@ next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, return (result); } +static isc_boolean_t +has_opt_bit(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) { + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + isc_boolean_t has_bit = ISC_FALSE; + + dns_rdataset_init(&rdataset); + CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, + dns_rdatatype_none, 0, &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + dns_rdataset_current(&rdataset, &rdata); + has_bit = dns_nsec_typepresent(&rdata, dns_rdatatype_opt); + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + return (has_bit); +} + +static void +set_bit(unsigned char *array, unsigned int index) { + unsigned int shift, mask; + + shift = 7 - (index % 8); + mask = 1 << shift; + + array[index / 8] |= mask; +} + /*% * Add a NSEC record for "name", recording the change in "diff". * The existing NSEC is removed. @@ -1572,6 +1645,24 @@ add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, CHECK(dns_db_findnode(db, name, ISC_FALSE, &node)); dns_rdata_init(&rdata); CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); + /* + * Preserve the status of the OPT bit in the origin's NSEC record. + */ + if (dns_name_equal(dns_db_origin(db), name) && + has_opt_bit(db, ver, node)) + { + isc_region_t region; + dns_name_t next; + + dns_name_init(&next, NULL); + dns_rdata_toregion(&rdata, ®ion); + dns_name_fromregion(&next, ®ion); + isc_region_consume(®ion, next.length); + INSIST(region.length > (2 + dns_rdatatype_opt / 8) && + region.base[0] == 0 && + region.base[1] > dns_rdatatype_opt / 8); + set_bit(region.base + 2, dns_rdatatype_opt); + } dns_db_detachnode(db, &node); /* @@ -1715,7 +1806,7 @@ add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, /* Update the database and journal with the RRSIG. */ /* XXX inefficient - will cause dataset merging */ - CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name, rdataset.ttl, &sig_rdata)); dns_rdata_reset(&sig_rdata); added_sig = ISC_TRUE; @@ -1735,6 +1826,84 @@ add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, return (result); } +/* + * Delete expired RRsigs and any RRsigs we are about to re-sign. + * See also zone.c:del_sigs(). + */ +static isc_result_t +del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i; + dns_rdata_rrsig_t rrsig; + isc_boolean_t found; + +fprintf(stderr, "del_keysigs\n"); + + dns_rdataset_init(&rdataset); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, + dns_rdatatype_dnskey, (isc_stdtime_t) 0, + &rdataset, NULL); + dns_db_detachnode(db, &node); + + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + found = ISC_FALSE; + for (i = 0; i < nkeys; i++) { + if (rrsig.keyid == dst_key_id(keys[i])) { + found = ISC_TRUE; + if (!dst_key_isprivate(keys[i])) { + /* + * The re-signing code in zone.c + * will mark this as offline. + * Just skip the record for now. + */ + break; + } + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata); + break; + } + } + /* + * If there is not a matching DNSKEY then delete the RRSIG. + */ + if (!found) + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; +failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + /*% * Update RRSIG and NSEC records affected by an update. The original * update, including the SOA serial update but exluding the RRSIG & NSEC @@ -1749,7 +1918,8 @@ add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, static isc_result_t update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *oldver, dns_dbversion_t *newver, - dns_diff_t *diff, isc_uint32_t sigvalidityinterval) + dns_diff_t *diff, isc_uint32_t sigvalidityinterval, + isc_boolean_t *deleted_zsk) { isc_result_t result; dns_difftuple_t *t; @@ -1797,8 +1967,27 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, */ check_ksk = ISC_TF((dns_zone_getoptions(zone) & DNS_ZONEOPT_UPDATECHECKKSK) != 0); - if (check_ksk) + /* + * If we are not checking the ZSK flag then all DNSKEY's are + * already signing all RRsets so we don't need to trigger special + * changes. + */ + if (*deleted_zsk && (!check_ksk || !ksk_sanity(db, oldver))) + *deleted_zsk = ISC_FALSE; + + if (check_ksk) { check_ksk = ksk_sanity(db, newver); + if (!check_ksk && ksk_sanity(db, oldver)) + update_log(client, zone, ISC_LOG_WARNING, + "disabling update-check-ksk"); + } + + /* + * If we have deleted a ZSK and we we still have some ZSK's + * we don't need to convert the KSK's to a ZSK's. + */ + if (*deleted_zsk && check_ksk) + *deleted_zsk = ISC_FALSE; /* * Get the NSEC's TTL from the SOA MINIMUM field. @@ -1845,10 +2034,17 @@ update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db, * Delete all old RRSIGs covering this type, since they * are all invalid when the signed RRset has changed. * We may not be able to recreate all of them - tough. + * Special case changes to the zone's DNSKEY records + * to support offline KSKs. */ - CHECK(delete_if(true_p, db, newver, name, - dns_rdatatype_rrsig, type, - NULL, &sig_diff)); +fprintf(stderr, "delete signatures %u\n", type); + if (type == dns_rdatatype_dnskey) + del_keysigs(db, newver, name, &sig_diff, + zone_keys, nkeys); + else + CHECK(delete_if(true_p, db, newver, name, + dns_rdatatype_rrsig, type, + NULL, &sig_diff)); /* * If this RRset still exists after the update, @@ -2349,6 +2545,52 @@ check_mx(ns_client_t *client, dns_zone_t *zone, return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED); } +static isc_result_t +add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver, + dns_diff_t *diff) +{ + isc_result_t result = ISC_R_SUCCESS; + dns_difftuple_t *tuple, *newtuple = NULL; + dns_rdata_dnskey_t dnskey; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned char buf[4]; + isc_region_t r; + isc_uint16_t keyid; + + for (tuple = ISC_LIST_HEAD(diff->tuples); + tuple != NULL; + tuple = ISC_LIST_NEXT(tuple, link)) { + if (tuple->rdata.type != dns_rdatatype_dnskey || + tuple->op != DNS_DIFFOP_ADD) + continue; + + dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL); + if ((dnskey.flags & + (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) + != DNS_KEYOWNER_ZONE) + continue; + + dns_rdata_toregion(&tuple->rdata, &r); + keyid = dst_region_computeid(&r, dnskey.algorithm); + + buf[0] = dnskey.algorithm; + buf[1] = (keyid & 0xff00) >> 8; + buf[2] = (keyid & 0xff); + buf[3] = 0; + rdata.data = buf; + rdata.length = sizeof(buf); + rdata.type = 0xFFFF; /* XXXMPA make user settable */ + rdata.rdclass = tuple->rdata.rdclass; + + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, + 0, &rdata, &newtuple)); + CHECK(do_one_tuple(&newtuple, db, ver, diff)); + INSIST(newtuple == NULL); + } + failure: + return (result); +} + static void update_action(isc_task_t *task, isc_event_t *event) { update_event_t *uev = (update_event_t *) event; @@ -2371,6 +2613,9 @@ update_action(isc_task_t *task, isc_event_t *event) { dns_fixedname_t tmpnamefixed; dns_name_t *tmpname = NULL; unsigned int options; + isc_boolean_t deleted_zsk; + dns_difftuple_t *tuple; + dns_rdata_dnskey_t dnskey; INSIST(event->ev_type == DNS_EVENT_UPDATE); @@ -2567,16 +2812,18 @@ update_action(isc_task_t *task, isc_event_t *event) { * "Unlike traditional dynamic update, the client * is forbidden from updating NSEC records." */ - if (dns_db_issecure(db)) { + if (dns_db_isdnssec(db)) { if (rdata.type == dns_rdatatype_nsec) { FAILC(DNS_R_REFUSED, "explicit NSEC updates are not allowed " "in secure zones"); } - else if (rdata.type == dns_rdatatype_rrsig) { + else if (rdata.type == dns_rdatatype_rrsig && + !dns_name_equal(name, zonename)) { FAILC(DNS_R_REFUSED, "explicit RRSIG updates are currently " - "not supported in secure zones"); + "not supported in secure zones except " + "at the apex."); } } @@ -2852,6 +3099,7 @@ update_action(isc_task_t *task, isc_event_t *event) { if (! ISC_LIST_EMPTY(diff.tuples)) { char *journalfile; dns_journal_t *journal; + isc_boolean_t has_dnskey; /* * Increment the SOA serial, but only if it was not @@ -2865,10 +3113,17 @@ update_action(isc_task_t *task, isc_event_t *event) { CHECK(remove_orphaned_ds(db, ver, &diff)); - if (dns_db_issecure(db)) { + CHECK(add_signing_records(db, zonename, ver, &diff)); + + CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey, + 0, &has_dnskey)); + + if (has_dnskey && dns_db_isdnssec(db)) { + isc_uint32_t interval; + interval = dns_zone_getsigvalidityinterval(zone); result = update_signatures(client, zone, db, oldver, - ver, &diff, - dns_zone_getsigvalidityinterval(zone)); + ver, &diff, interval, + &deleted_zsk); if (result != ISC_R_SUCCESS) { update_log(client, zone, ISC_LOG_ERROR, @@ -2905,6 +3160,7 @@ update_action(isc_task_t *task, isc_event_t *event) { */ update_log(client, zone, LOGLEVEL_DEBUG, "committing update transaction"); + dns_db_closeversion(db, &ver, ISC_TRUE); /* @@ -2916,6 +3172,35 @@ update_action(isc_task_t *task, isc_event_t *event) { * Notify slaves of the change we just made. */ dns_zone_notify(zone); + + for (tuple = ISC_LIST_HEAD(diff.tuples); + tuple != NULL; + tuple = ISC_LIST_NEXT(tuple, link)) { + isc_region_t r; + dns_secalg_t algorithm; + isc_uint16_t keyid; + + if (tuple->rdata.type != dns_rdatatype_dnskey || + tuple->op != DNS_DIFFOP_ADD) + continue; + + dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL); + if ((dnskey.flags & + (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) + != DNS_KEYOWNER_ZONE) + continue; + + dns_rdata_toregion(&tuple->rdata, &r); + algorithm = dnskey.algorithm; + keyid = dst_region_computeid(&r, algorithm); + + result = dns_zone_signwithkey(zone, algorithm, keyid); + if (result != ISC_R_SUCCESS) { + update_log(client, zone, ISC_LOG_ERROR, + "dns_zone_signwithkey failed: %s", + dns_result_totext(result)); + } + } } else { update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); dns_db_closeversion(db, &ver, ISC_TRUE); diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index 44f98d29b3..3779ccc123 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: nsupdate.c,v 1.157 2008/01/18 23:46:57 tbox Exp $ */ +/* $Id: nsupdate.c,v 1.158 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -1032,7 +1032,7 @@ parse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass, check_result(result, "isc_lex_openbuffer"); result = isc_buffer_allocate(mctx, &buf, MAXWIRE); check_result(result, "isc_buffer_allocate"); - result = dns_rdata_fromtext(rdata, rdataclass, rdatatype, lex, + result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex, dns_rootname, 0, mctx, buf, &callbacks); isc_lex_destroy(&lex); diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 99aea2d556..3d880d222c 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check.c,v 1.89 2008/03/29 23:47:08 tbox Exp $ */ +/* $Id: check.c,v 1.90 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -654,6 +654,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) { if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) result = tresult; + goto trust_anchor; } /* * XXXMPA to be removed when multiple lookaside @@ -666,6 +667,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) { if (result == ISC_R_SUCCESS) result = ISC_R_FAILURE; } + trust_anchor: dlv = cfg_obj_asstring(cfg_tuple_get(obj, "trust-anchor")); isc_buffer_init(&b, dlv, strlen(dlv)); diff --git a/lib/dns/db.c b/lib/dns/db.c index 64327651de..23ebde344c 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.c,v 1.83 2007/06/18 23:47:40 tbox Exp $ */ +/* $Id: db.c,v 1.84 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -228,6 +228,21 @@ dns_db_isstub(dns_db_t *db) { return (ISC_FALSE); } +isc_boolean_t +dns_db_isdnssec(dns_db_t *db) { + + /* + * Is 'db' secure or partially secure? + */ + + REQUIRE(DNS_DB_VALID(db)); + REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0); + + if (db->methods->isdnssec != NULL) + return ((db->methods->isdnssec)(db)); + return ((db->methods->issecure)(db)); +} + isc_boolean_t dns_db_issecure(dns_db_t *db) { @@ -843,3 +858,27 @@ dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { return (ISC_R_NOTFOUND); } + +isc_result_t +dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, + isc_stdtime_t resign) +{ + if (db->methods->setsigningtime != NULL) + return ((db->methods->setsigningtime)(db, rdataset, resign)); + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name) +{ + if (db->methods->getsigningtime != NULL) + return ((db->methods->getsigningtime)(db, rdataset, name)); + return (ISC_R_NOTFOUND); +} + +void +dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) +{ + if (db->methods->resigned != NULL) + return ((db->methods->resigned)(db, rdataset, version)); +} diff --git a/lib/dns/diff.c b/lib/dns/diff.c index 188f7ad849..1cd4ced8d2 100644 --- a/lib/dns/diff.c +++ b/lib/dns/diff.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: diff.c,v 1.14 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: diff.c,v 1.15 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -192,6 +193,52 @@ dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) ENSURE(*tuplep == NULL); } +static isc_stdtime_t +setresign(dns_rdataset_t *modified, dns_diffop_t op) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t sig; + isc_stdtime_t when; + unsigned int delta; + isc_result_t result; + + result = dns_rdataset_first(modified); + INSIST(result == ISC_R_SUCCESS); + dns_rdataset_current(modified, &rdata); + (void)dns_rdata_tostruct(&rdata, &sig, NULL); + if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) { + when = 0; + } else { + delta = (sig.timeexpire - sig.timesigned) * 3 / 4; + when = sig.timesigned + delta; + } + dns_rdata_reset(&rdata); + + result = dns_rdataset_next(modified); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(modified, &rdata); + (void)dns_rdata_tostruct(&rdata, &sig, NULL); + if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) { + goto next_rr; + } + delta = (sig.timeexpire - sig.timesigned)* 3 / 4; + switch (op) { + case DNS_DIFFOP_ADDRESIGN: + case DNS_DIFFOP_DELRESIGN: + if (when == 0 || sig.timesigned + delta < when) + when = sig.timesigned + delta; + break; + default: + INSIST(0); + } + next_rr: + dns_rdata_reset(&rdata); + result = dns_rdataset_next(modified); + } + INSIST(result == ISC_R_NOMORE); +fprintf(stderr, "setresign %u %u\n", modified->covers, when); + return (when); +} + static isc_result_t diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, isc_boolean_t warn) @@ -228,6 +275,9 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, dns_diffop_t op; dns_rdatalist_t rdl; dns_rdataset_t rds; + dns_rdataset_t ardataset; + dns_rdataset_t *modified = NULL; + isc_boolean_t offline; op = t->op; type = t->rdata.type; @@ -255,6 +305,7 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, ISC_LIST_INIT(rdl.rdata); ISC_LINK_INIT(&rdl, link); + offline = ISC_FALSE; while (t != NULL && dns_name_equal(&t->name, name) && t->op == op && @@ -276,6 +327,10 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, namebuf, typebuf, classbuf, (unsigned long) t->ttl, (unsigned long) rdl.ttl); + if (t->rdata.flags &DNS_RDATA_OFFLINE) { + fprintf(stderr, "diff_apply offline\n"); + offline = ISC_TRUE; + } ISC_LIST_APPEND(rdl.rdata, &t->rdata, link); t = ISC_LIST_NEXT(t, link); } @@ -285,27 +340,50 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, */ dns_rdataset_init(&rds); CHECK(dns_rdatalist_tordataset(&rdl, &rds)); + if (rds.type == dns_rdatatype_rrsig) + switch (op) { + case DNS_DIFFOP_ADDRESIGN: + case DNS_DIFFOP_DELRESIGN: + modified = &ardataset; + dns_rdataset_init(modified); + break; + default: + break; + } rds.trust = dns_trust_ultimate; /* * Merge the rdataset into the database. */ - if (op == DNS_DIFFOP_ADD) { + switch (op) { + case DNS_DIFFOP_ADD: + case DNS_DIFFOP_ADDRESIGN: result = dns_db_addrdataset(db, node, ver, 0, &rds, DNS_DBADD_MERGE| DNS_DBADD_EXACT| DNS_DBADD_EXACTTTL, - NULL); - } else if (op == DNS_DIFFOP_DEL) { + modified); + break; + case DNS_DIFFOP_DEL: + case DNS_DIFFOP_DELRESIGN: result = dns_db_subtractrdataset(db, node, ver, &rds, DNS_DBSUB_EXACT, - NULL); - } else { + modified); + break; + default: INSIST(0); } - if (result == DNS_R_UNCHANGED) { + + if (result == ISC_R_SUCCESS) { + if (modified != NULL) { + isc_stdtime_t resign; + resign = setresign(modified, op); + dns_db_setsigningtime(db, modified, + resign); + } + } else if (result == DNS_R_UNCHANGED) { /* * This will not happen when executing a * dynamic update, because that code will @@ -318,14 +396,21 @@ diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver, isc_log_write(DIFF_COMMON_LOGARGS, ISC_LOG_WARNING, "update with no effect"); - } else if (result == ISC_R_SUCCESS || - result == DNS_R_NXRRSET) { + } else if (result == DNS_R_NXRRSET) { /* * OK. */ } else { + if (modified != NULL && + dns_rdataset_isassociated(modified)) + dns_rdataset_disassociate(modified); CHECK(result); } + if (modified != NULL && + dns_rdataset_isassociated(modified)) + dns_rdataset_disassociate(modified); + if (offline) + fprintf(stderr, "end offline\n"); } dns_db_detachnode(db, &node); } @@ -485,6 +570,7 @@ dns_diff_print(dns_diff_t *diff, FILE *file) { dns_difftuple_t *t; char *mem = NULL; unsigned int size = 2048; + const char *op = NULL; REQUIRE(DNS_DIFF_VALID(diff)); @@ -536,15 +622,20 @@ dns_diff_print(dns_diff_t *diff, FILE *file) { buf.used--; isc_buffer_usedregion(&buf, &r); + switch (t->op) { + case DNS_DIFFOP_EXISTS: op = "exists"; break; + case DNS_DIFFOP_ADD: op = "add"; break; + case DNS_DIFFOP_DEL: op = "del"; break; + case DNS_DIFFOP_ADDRESIGN: op = "add re-sign"; break; + case DNS_DIFFOP_DELRESIGN: op = "del re-sign"; break; + } if (file != NULL) - fprintf(file, "%s %.*s\n", - t->op == DNS_DIFFOP_ADD ? "add" : "del", - (int) r.length, (char *) r.base); + fprintf(file, "%s %.*s\n", op, (int) r.length, + (char *) r.base); else isc_log_write(DIFF_COMMON_LOGARGS, ISC_LOG_DEBUG(7), - "%s %.*s", - t->op == DNS_DIFFOP_ADD ? "add" : "del", - (int) r.length, (char *) r.base); + "%s %.*s", op, (int) r.length, + (char *) r.base); } result = ISC_R_SUCCESS; cleanup: diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index fb87cf66f0..64c2f2eae5 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.h,v 1.89 2007/06/18 23:47:42 tbox Exp $ */ +/* $Id: db.h,v 1.90 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -147,7 +147,16 @@ typedef struct dns_dbmethods { void (*settask)(dns_db_t *db, isc_task_t *); isc_result_t (*getoriginnode)(dns_db_t *db, dns_dbnode_t **nodep); void (*transfernode)(dns_db_t *db, dns_dbnode_t **sourcep, - dns_dbnode_t **targetp); + dns_dbnode_t **targetp); + isc_result_t (*setsigningtime)(dns_db_t *db, + dns_rdataset_t *rdataset, + isc_stdtime_t resign); + isc_result_t (*getsigningtime)(dns_db_t *db, + dns_rdataset_t *rdataset, + dns_name_t *name); + void (*resigned)(dns_db_t *db, dns_rdataset_t *rdataset, + dns_dbversion_t *version); + isc_boolean_t (*isdnssec)(dns_db_t *db); } dns_dbmethods_t; typedef isc_result_t @@ -357,6 +366,20 @@ dns_db_issecure(dns_db_t *db); * \li #ISC_FALSE 'db' is not secure. */ +isc_boolean_t +dns_db_isdnssec(dns_db_t *db); +/*%< + * Is 'db' secure or partially secure? + * + * Requires: + * + * \li 'db' is a valid database with zone semantics. + * + * Returns: + * \li #ISC_TRUE 'db' is secure or is partially. + * \li #ISC_FALSE 'db' is not secure. + */ + dns_name_t * dns_db_origin(dns_db_t *db); /*%< @@ -628,7 +651,7 @@ dns_db_findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, * * \li #ISC_R_SUCCESS * \li #ISC_R_NOTFOUND If !create and name not found. - * \li #ISC_R_NOMEMORY Can only happen if create is ISC_TRUE. + * \li #ISC_R_NOMEMORY Can only happen if create is ISC_TRUE. * * \li Other results are possible, depending upon the database * implementation used. @@ -887,7 +910,7 @@ dns_db_detachnode(dns_db_t *db, dns_dbnode_t **nodep); void dns_db_transfernode(dns_db_t *db, dns_dbnode_t **sourcep, - dns_dbnode_t **targetp); + dns_dbnode_t **targetp); /*%< * Transfer a node between pointer. * @@ -1317,6 +1340,54 @@ dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep); * \li #ISC_R_NOTFOUND - the DB implementation does not support this feature. */ +isc_result_t +dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, + isc_stdtime_t resign); +/*%< + * Sets the re-signing time associated with 'rdataset' to 'resign'. + * + * Requires: + * \li 'db' is a valid zone database. + * \li 'rdataset' to be associated with 'db'. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + * \li #ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation. + */ + +isc_result_t +dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name); +/*%< + * Return the rdataset with the earliest signing time in the zone. + * Note: the rdataset is version agnostic. + * + * Requires: + * \li 'db' is a valid zone database. + * \li 'rdataset' to be initialized but not associated. + * \li 'name' to be NULL or have a buffer associated with it. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOTFOUND - No dataset exists. + */ + +void +dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, + dns_dbversion_t *version); +/*%< + * Mark 'rdataset' as not being available to be returned by + * dns_db_getsigningtime(). If the changes associated with 'version' + * are committed this will be permanent. If the version is not committed + * this change will be rolled back when the version is closed. + * + * Requires: + * \li 'db' is a valid zone database. + * \li 'rdataset' to be associated with 'db'. + * \li 'version' to be open for writing. + */ + + ISC_LANG_ENDDECLS #endif /* DNS_DB_H */ diff --git a/lib/dns/include/dns/diff.h b/lib/dns/include/dns/diff.h index f9b8988a7a..34ff64cf5a 100644 --- a/lib/dns/include/dns/diff.h +++ b/lib/dns/include/dns/diff.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: diff.h,v 1.12 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: diff.h,v 1.13 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_DIFF_H #define DNS_DIFF_H 1 @@ -59,12 +59,16 @@ * individual RRs of a "RRset exists (value dependent)" * prerequisite set. In this case, op==DNS_DIFFOP_EXISTS, * and the TTL is ignored. + * + * DNS_DIFFOP_*RESIGN will cause the 'resign' attribute to be recomputed. */ typedef enum { - DNS_DIFFOP_ADD, /*%< Add an RR. */ - DNS_DIFFOP_DEL, /*%< Delete an RR. */ - DNS_DIFFOP_EXISTS /*%< Assert RR existence. */ + DNS_DIFFOP_ADD = 0, /*%< Add an RR. */ + DNS_DIFFOP_DEL = 1, /*%< Delete an RR. */ + DNS_DIFFOP_EXISTS = 2, /*%< Assert RR existence. */ + DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */ + DNS_DIFFOP_DELRESIGN = 5, /*%< DEL + RESIGN. */ } dns_diffop_t; typedef struct dns_difftuple dns_difftuple_t; @@ -73,7 +77,7 @@ typedef struct dns_difftuple dns_difftuple_t; #define DNS_DIFFTUPLE_VALID(t) ISC_MAGIC_VALID(t, DNS_DIFFTUPLE_MAGIC) struct dns_difftuple { - unsigned int magic; + unsigned int magic; isc_mem_t *mctx; dns_diffop_t op; dns_name_t name; diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h index 8f2e32a740..2f4601c283 100644 --- a/lib/dns/include/dns/journal.h +++ b/lib/dns/include/dns/journal.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.h,v 1.31 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: journal.h,v 1.32 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_JOURNAL_H #define DNS_JOURNAL_H 1 @@ -41,6 +41,11 @@ #include #include +/*** + *** Defines. + ***/ +#define DNS_JOURNALOPT_RESIGN 0x00000001 + /*** *** Types ***/ @@ -225,17 +230,18 @@ dns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl, */ isc_result_t -dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, const char *filename); +dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, unsigned int options, + const char *filename); /*%< * Roll forward (play back) the journal file "filename" into the * database "db". This should be called when the server starts * after a shutdown or crash. * * Requires: - *\li 'mctx' is a valid memory context. + *\li 'mctx' is a valid memory context. *\li 'db' is a valid database which does not have a version * open for writing. - * \li 'filename' is the name of the journal file belonging to 'db'. + *\li 'filename' is the name of the journal file belonging to 'db'. * * Returns: *\li DNS_R_NOJOURNAL when journal does not exist. @@ -264,7 +270,7 @@ dns_db_diff(isc_mem_t *mctx, isc_result_t dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, - isc_uint32_t target_size); + isc_uint32_t target_size); /*%< * Attempt to compact the journal if it is greater that 'target_size'. * Changes from 'serial' onwards will be preserved. If the journal diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h index 481e3fe1d2..787788a87c 100644 --- a/lib/dns/include/dns/master.h +++ b/lib/dns/include/dns/master.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.h,v 1.48 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: master.h,v 1.49 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_MASTER_H #define DNS_MASTER_H 1 @@ -55,6 +55,8 @@ #define DNS_MASTER_CHECKMX 0x00000800 #define DNS_MASTER_CHECKMXFAIL 0x00001000 +#define DNS_MASTER_RESIGN 0x00002000 + ISC_LANG_BEGINDECLS /* diff --git a/lib/dns/include/dns/masterdump.h b/lib/dns/include/dns/masterdump.h index 4ffa2197e7..53fec12393 100644 --- a/lib/dns/include/dns/masterdump.h +++ b/lib/dns/include/dns/masterdump.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: masterdump.h,v 1.39 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: masterdump.h,v 1.40 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_MASTERDUMP_H #define DNS_MASTERDUMP_H 1 @@ -91,12 +91,15 @@ typedef struct dns_master_style dns_master_style_t; /*% Print negative caching entries. */ #define DNS_STYLEFLAG_NCACHE 0x00800000U -/*% Never print the TTL */ +/*% Never print the TTL. */ #define DNS_STYLEFLAG_NO_TTL 0x01000000U - -/*% Never print the CLASS */ + +/*% Never print the CLASS. */ #define DNS_STYLEFLAG_NO_CLASS 0x02000000U +/*% Report re-signing time. */ +#define DNS_STYLEFLAG_RESIGN 0x04000000U + ISC_LANG_BEGINDECLS /*** diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 5a6e88a373..24fce03ac7 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.h,v 1.67 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: rdata.h,v 1.68 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_RDATA_H #define DNS_RDATA_H 1 @@ -124,7 +124,13 @@ struct dns_rdata { #define DNS_RDATA_INIT { NULL, 0, 0, 0, 0, {(void*)(-1), (void *)(-1)}} -#define DNS_RDATA_UPDATE 0x0001 /*%< update pseudo record */ +#define DNS_RDATA_UPDATE 0x0001 /*%< update pseudo record. */ +#define DNS_RDATA_OFFLINE 0x0002 /*%< RRSIG has a offline key. */ +#define DNS_RDATA_WARNMASK 0x001c /*%< RRSIG(DNSKEY) expired + warnings number mask. */ +#define DNS_RDATA_WARNSHIFT 2 /*%< How many bits to shift + to find remaining + expired warning number. */ /* * Flags affecting rdata formatting style. Flags 0xFFFF0000 diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index e7f38cee73..92dfc12add 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataset.h,v 1.62 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: rdataset.h,v 1.63 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_RDATASET_H #define DNS_RDATASET_H 1 @@ -140,6 +140,11 @@ struct dns_rdataset { * increment the counter. */ isc_uint32_t count; + /* + * This RRSIG RRset should be re-generated around this time. + * Only valid if DNS_RDATASETATTR_RESIGN is set in attributes. + */ + isc_stdtime_t resign; /*@{*/ /*% * These are for use by the rdataset implementation, and MUST NOT @@ -152,6 +157,7 @@ struct dns_rdataset { void * private5; void * private6; /*@}*/ + }; /*! @@ -184,6 +190,7 @@ struct dns_rdataset { #define DNS_RDATASETATTR_CHECKNAMES 0x00008000 /*%< Used by resolver. */ #define DNS_RDATASETATTR_REQUIREDGLUE 0x00010000 #define DNS_RDATASETATTR_LOADORDER 0x00020000 +#define DNS_RDATASETATTR_RESIGN 0x00040000 /*% * _OMITDNSSEC: diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h index 9308073842..a76546b8f0 100644 --- a/lib/dns/include/dns/rdataslab.h +++ b/lib/dns/include/dns/rdataslab.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataslab.h,v 1.31 2007/06/19 23:47:17 tbox Exp $ */ +/* $Id: rdataslab.h,v 1.32 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_RDATASLAB_H #define DNS_RDATASLAB_H 1 @@ -57,6 +57,13 @@ ISC_LANG_BEGINDECLS #define DNS_RDATASLAB_FORCE 0x1 #define DNS_RDATASLAB_EXACT 0x2 +#define DNS_RDATASLAB_OFFLINE 0x01 /* RRSIG is for offline DNSKEY */ +#define DNS_RDATASLAB_WARNMASK 0x0E /*%< RRSIG(DNSKEY) expired + * warnings number mask. */ +#define DNS_RDATASLAB_WARNSHIFT 1 /*%< How many bits to shift to find + * remaining expired warning number. */ + + /*** *** Functions ***/ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index d0c06591d1..c5c26087a0 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.h,v 1.155 2008/01/24 23:47:00 tbox Exp $ */ +/* $Id: zone.h,v 1.156 2008/04/01 01:37:25 marka Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -879,12 +879,16 @@ isc_boolean_t dns_zone_getupdatedisabled(dns_zone_t *zone); /*%< * Return update disabled. + * Transient unless called when running in isc_task_exclusive() mode. */ void dns_zone_setupdatedisabled(dns_zone_t *zone, isc_boolean_t state); /*%< * Set update disabled. + * Should only be called only when running in isc_task_exclusive() mode. + * Failure to do so may result in updates being committed after the + * call has been made. */ isc_boolean_t @@ -1636,6 +1640,9 @@ dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg); * delivered to 'myview'. */ +isc_result_t +dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid); + #ifdef HAVE_LIBXML2 isc_result_t diff --git a/lib/dns/journal.c b/lib/dns/journal.c index 05b73e480b..9c540426e7 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.99 2007/09/07 05:14:33 marka Exp $ */ +/* $Id: journal.c,v 1.100 2008/04/01 01:37:24 marka Exp $ */ #include @@ -709,8 +709,35 @@ ixfr_order(const void *av, const void *bv) { dns_difftuple_t const *a = *ap; dns_difftuple_t const *b = *bp; int r; + int bop = 0, aop = 0; - r = (b->op == DNS_DIFFOP_DEL) - (a->op == DNS_DIFFOP_DEL); + switch (a->op) { + case DNS_DIFFOP_DEL: + case DNS_DIFFOP_DELRESIGN: + aop = 1; + break; + case DNS_DIFFOP_ADD: + case DNS_DIFFOP_ADDRESIGN: + aop = 0; + break; + default: + INSIST(0); + } + + switch (b->op) { + case DNS_DIFFOP_DEL: + case DNS_DIFFOP_DELRESIGN: + bop = 1; + break; + case DNS_DIFFOP_ADD: + case DNS_DIFFOP_ADDRESIGN: + bop = 0; + break; + default: + INSIST(0); + } + + r = bop - aop; if (r != 0) return (r); @@ -1191,7 +1218,7 @@ dns_journal_destroy(dns_journal_t **journalp) { /* XXX Share code with incoming IXFR? */ static isc_result_t -roll_forward(dns_journal_t *j, dns_db_t *db) { +roll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) { isc_buffer_t source; /* Transaction data from disk */ isc_buffer_t target; /* Ditto after _fromwire check */ isc_uint32_t db_serial; /* Database SOA serial */ @@ -1202,6 +1229,7 @@ roll_forward(dns_journal_t *j, dns_db_t *db) { dns_diff_t diff; unsigned int n_soa = 0; unsigned int n_put = 0; + dns_diffop_t op; REQUIRE(DNS_JOURNAL_VALID(j)); REQUIRE(DNS_DB_VALID(db)); @@ -1273,9 +1301,14 @@ roll_forward(dns_journal_t *j, dns_db_t *db) { "initial SOA", j->filename); FAIL(ISC_R_UNEXPECTED); } - CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ? - DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, - name, ttl, rdata, &tuple)); + if ((options & DNS_JOURNALOPT_RESIGN) != 0) + op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN : + DNS_DIFFOP_ADDRESIGN; + else + op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; + + CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, + &tuple)); dns_diff_append(&diff, &tuple); if (++n_put > 100) { @@ -1317,7 +1350,9 @@ roll_forward(dns_journal_t *j, dns_db_t *db) { } isc_result_t -dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, const char *filename) { +dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, + unsigned int options, const char *filename) +{ dns_journal_t *j; isc_result_t result; @@ -1336,7 +1371,7 @@ dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, const char *filename) { if (JOURNAL_EMPTY(&j->header)) result = DNS_R_UPTODATE; else - result = roll_forward(j, db); + result = roll_forward(j, db, options); dns_journal_destroy(&j); diff --git a/lib/dns/master.c b/lib/dns/master.c index e9dbffce3d..fe41ab8396 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.c,v 1.169 2008/01/18 23:46:58 tbox Exp $ */ +/* $Id: master.c,v 1.170 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -1738,8 +1738,7 @@ load_text(dns_loadctx_t *lctx) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(ictx->current, namebuf, sizeof(namebuf)); - (*callbacks->error)(callbacks, - "%s:%lu: SOA " + (*callbacks->error)(callbacks, "%s:%lu: SOA " "record not at top of zone (%s)", source, line, namebuf); result = DNS_R_NOTZONETOP; @@ -2700,6 +2699,29 @@ grow_rdata(int new_len, dns_rdata_t *old, int old_len, return (new); } +static isc_uint32_t +resign_fromlist(dns_rdatalist_t *this) { + dns_rdata_t *rdata; + dns_rdata_rrsig_t sig; + isc_uint32_t when, delta; + + rdata = ISC_LIST_HEAD(this->rdata); + INSIST(rdata != NULL); + (void)dns_rdata_tostruct(rdata, &sig, NULL); + delta = (sig.timeexpire - sig.timesigned)* 3 / 4; + when = sig.timesigned + delta; + + rdata = ISC_LIST_NEXT(rdata, link); + while (rdata != NULL) { + (void)dns_rdata_tostruct(rdata, &sig, NULL); + delta = (sig.timeexpire - sig.timesigned)* 3 / 4; + if (sig.timesigned + delta < when) + when = sig.timesigned + delta; + rdata = ISC_LIST_NEXT(rdata, link); + } + return (when); +} + /* * Convert each element from a rdatalist_t to rdataset then call commit. * Unlink each element as we go. @@ -2726,14 +2748,22 @@ commit(dns_rdatacallbacks_t *callbacks, dns_loadctx_t *lctx, RUNTIME_CHECK(dns_rdatalist_tordataset(this, &dataset) == ISC_R_SUCCESS); dataset.trust = dns_trust_ultimate; + /* + * If this is a secure dynamic zone set the re-signing time. + */ + if (dataset.type == dns_rdatatype_rrsig && + (lctx->options & DNS_MASTER_RESIGN) != 0) { + dataset.attributes |= DNS_RDATASETATTR_RESIGN; + dns_name_format(owner, namebuf, sizeof(namebuf)); + dataset.resign = resign_fromlist(this); + } result = ((*callbacks->add)(callbacks->add_private, owner, &dataset)); if (result == ISC_R_NOMEMORY) { (*error)(callbacks, "dns_master_load: %s", dns_result_totext(result)); } else if (result != ISC_R_SUCCESS) { - dns_name_format(owner, namebuf, - sizeof(namebuf)); + dns_name_format(owner, namebuf, sizeof(namebuf)); if (source != NULL) { (*error)(callbacks, "%s: %s:%lu: %s: %s", "dns_master_load", source, line, diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c index 8b3ebd660d..e8a1895dd1 100644 --- a/lib/dns/masterdump.c +++ b/lib/dns/masterdump.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: masterdump.c,v 1.89 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: masterdump.c,v 1.90 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -108,7 +108,8 @@ dns_master_style_default = { LIBDNS_EXTERNAL_DATA const dns_master_style_t dns_master_style_full = { - DNS_STYLEFLAG_COMMENT, + DNS_STYLEFLAG_COMMENT | + DNS_STYLEFLAG_RESIGN, 46, 46, 46, 64, 120, 8 }; @@ -840,6 +841,15 @@ dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name, if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0) name = NULL; } + if (ctx->style.flags & DNS_STYLEFLAG_RESIGN && + rds->attributes & DNS_RDATASETATTR_RESIGN) { + isc_buffer_t b; + char buf[sizeof("YYYYMMDDHHMMSS")]; + memset(buf, 0, sizeof(buf)); + isc_buffer_init(&b, buf, sizeof(buf) - 1); + dns_time64_totext((isc_uint64_t)rds->resign, &b); + fprintf(f, "; resign=%s\n", buf); + } dns_rdataset_disassociate(rds); } @@ -1077,7 +1087,7 @@ dns_dumpctx_version(dns_dumpctx_t *dctx) { dns_db_t * dns_dumpctx_db(dns_dumpctx_t *dctx) { - REQUIRE(DNS_DCTX_VALID(dctx)); + REQUIRE(DNS_DCTX_VALID(dctx)); return (dctx->db); } @@ -1703,10 +1713,10 @@ dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, isc_result_t dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags, - unsigned int ttl_column, unsigned int class_column, - unsigned int type_column, unsigned int rdata_column, - unsigned int line_length, unsigned int tab_width, - isc_mem_t *mctx) + unsigned int ttl_column, unsigned int class_column, + unsigned int type_column, unsigned int rdata_column, + unsigned int line_length, unsigned int tab_width, + isc_mem_t *mctx) { dns_master_style_t *style; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 477a5175fd..a4460a9b71 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.252 2008/02/01 23:46:58 tbox Exp $ */ +/* $Id: rbtdb.c,v 1.253 2008/04/01 01:37:24 marka Exp $ */ /*! \file */ @@ -25,15 +25,16 @@ #include -#include #include +#include #include +#include #include #include -#include #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -261,6 +263,7 @@ typedef struct rdatasetheader { /*%< * Used for TTL-based cache cleaning. */ + isc_stdtime_t resign; } rdatasetheader_t; typedef ISC_LIST(rdatasetheader_t) rdatasetheaderlist_t; @@ -271,6 +274,7 @@ typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t; #define RDATASET_ATTR_IGNORE 0x0004 #define RDATASET_ATTR_RETAIN 0x0008 #define RDATASET_ATTR_NXDOMAIN 0x0010 +#define RDATASET_ATTR_RESIGN 0x0020 #define RDATASET_ATTR_CACHE 0x1000 /* for debug */ #define RDATASET_ATTR_CANCELED 0x2000 /* for debug */ @@ -307,6 +311,8 @@ struct acachectl { (((header)->attributes & RDATASET_ATTR_RETAIN) != 0) #define NXDOMAIN(header) \ (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0) +#define RESIGN(header) \ + (((header)->attributes & RDATASET_ATTR_RESIGN) != 0) #define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */ #define DEFAULT_CACHE_NODE_LOCK_COUNT 1009 /*%< Should be prime. */ @@ -340,6 +346,7 @@ typedef struct rbtdb_version { isc_boolean_t writer; isc_boolean_t commit_ok; rbtdb_changedlist_t changed_list; + rdatasetheaderlist_t resigned_list; ISC_LINK(struct rbtdb_version) link; } rbtdb_version_t; @@ -372,6 +379,12 @@ struct cachestat { }; #endif +typedef enum { + dns_db_insecure, + dns_db_partial, + dns_db_secure +} dns_db_secure_t; + typedef struct { /* Unlocked. */ dns_db_t common; @@ -419,7 +432,7 @@ typedef struct { /* Locked by tree_lock. */ dns_rbt_t * tree; - isc_boolean_t secure; + dns_db_secure_t secure; /* Unlocked */ unsigned int quantum; @@ -498,6 +511,8 @@ static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_stdtime_t now); static void check_stale_cache(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, isc_stdtime_t now, isc_boolean_t tree_locked); +static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx, + rdatasetheader_t *newheader); static dns_rdatasetmethods_t rdataset_methods = { rdataset_disassociate, @@ -653,6 +668,9 @@ set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) { oldttl = header->rdh_ttl; header->rdh_ttl = newttl; + if (!IS_CACHE(rbtdb)) + return; + /* * It's possible the rbtdb is not a cache. If this is the case, * we will not have a heap, and we move on. If we do, though, @@ -672,7 +690,7 @@ set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) { } /*% - * This function allows the heap code to rank the priority of each + * These functions allows the heap code to rank the priority of each * element. It returns ISC_TRUE if v1 happens "sooner" than v2. */ static isc_boolean_t @@ -685,11 +703,21 @@ ttl_sooner(void *v1, void *v2) { return (ISC_FALSE); } +static isc_boolean_t +resign_sooner(void *v1, void *v2) { + rdatasetheader_t *h1 = v1; + rdatasetheader_t *h2 = v2; + + if (h1->resign < h2->resign) + return (ISC_TRUE); + return (ISC_FALSE); +} + /*% * This function sets the heap index into the header. */ static void -ttl_set_index(void *what, unsigned int index) { +set_index(void *what, unsigned int index) { rdatasetheader_t *h = what; h->heap_index = index; @@ -828,7 +856,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { } /* - * Clean up LRU cache objects. + * Clean up LRU / re-signing order lists. */ if (rbtdb->rdatasets != NULL) { for (i = 0; i < rbtdb->node_lock_count; i++) @@ -847,7 +875,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { rbtdb->node_lock_count * sizeof(rbtnodelist_t)); } /* - * Clean up TTL heap cache objects. + * Clean up heap objects. */ if (rbtdb->heaps != NULL) { for (i = 0; i < rbtdb->node_lock_count; i++) @@ -1005,6 +1033,7 @@ allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial, version->writer = writer; version->commit_ok = ISC_FALSE; ISC_LIST_INIT(version->changed_list); + ISC_LIST_INIT(version->resigned_list); ISC_LINK_INIT(version, link); return (version); @@ -1165,6 +1194,7 @@ static inline void free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset) { unsigned int size; + int idx; #ifdef LRU_DEBUG /* @@ -1229,15 +1259,12 @@ free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset) } #endif - if (IS_CACHE(rbtdb) && ISC_LINK_LINKED(rdataset, lru_link)) { - int idx = rdataset->node->locknum; + idx = rdataset->node->locknum; + if (ISC_LINK_LINKED(rdataset, lru_link)) ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, lru_link); - if (rdataset->heap_index != 0) { - isc_heap_delete(rbtdb->heaps[idx], - rdataset->heap_index); - } - rdataset->heap_index = 0; - } + if (rdataset->heap_index != 0) + isc_heap_delete(rbtdb->heaps[idx], rdataset->heap_index); + rdataset->heap_index = 0; if (rdataset->noqname != NULL) free_noqname(mctx, &rdataset->noqname); @@ -1703,12 +1730,14 @@ cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) { } } -static isc_boolean_t +static dns_db_secure_t iszonesecure(dns_db_t *db, dns_dbnode_t *origin) { dns_rdataset_t keyset; dns_rdataset_t nsecset, signsecset; + dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t haszonekey = ISC_FALSE; isc_boolean_t hasnsec = ISC_FALSE; + isc_boolean_t hasoptbit = ISC_FALSE; isc_result_t result; dns_rdataset_init(&keyset); @@ -1728,7 +1757,7 @@ iszonesecure(dns_db_t *db, dns_dbnode_t *origin) { dns_rdataset_disassociate(&keyset); } if (!haszonekey) - return (ISC_FALSE); + return (dns_db_insecure); dns_rdataset_init(&nsecset); dns_rdataset_init(&signsecset); @@ -1737,11 +1766,20 @@ iszonesecure(dns_db_t *db, dns_dbnode_t *origin) { if (result == ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&signsecset)) { hasnsec = ISC_TRUE; + result = dns_rdataset_first(&nsecset); + if (result == ISC_R_SUCCESS) { + dns_rdataset_current(&nsecset, &rdata); + hasoptbit = dns_nsec_typepresent(&rdata, + dns_rdatatype_opt); + } dns_rdataset_disassociate(&signsecset); } dns_rdataset_disassociate(&nsecset); } - return (hasnsec); + + if (hasnsec && hasoptbit) + return (dns_db_partial); + return (hasnsec ? dns_db_secure : dns_db_insecure); } static void @@ -1750,10 +1788,12 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { rbtdb_version_t *version, *cleanup_version, *least_greater; isc_boolean_t rollback = ISC_FALSE; rbtdb_changedlist_t cleanup_list; + rdatasetheaderlist_t resigned_list; rbtdb_changed_t *changed, *next_changed; rbtdb_serial_t serial, least_serial; dns_rbtnode_t *rbtnode; unsigned int refs; + rdatasetheader_t *header; isc_boolean_t writer; REQUIRE(VALID_RBTDB(rbtdb)); @@ -1761,6 +1801,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { cleanup_version = NULL; ISC_LIST_INIT(cleanup_list); + ISC_LIST_INIT(resigned_list); isc_refcount_decrement(&version->references, &refs); if (refs > 0) { /* typical and easy case first */ @@ -1850,12 +1891,16 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { INSIST(cur_ref == 1); PREPEND(rbtdb->open_versions, rbtdb->current_version, link); + resigned_list = version->resigned_list; + ISC_LIST_INIT(version->resigned_list); } else { /* * We're rolling back this transaction. */ cleanup_list = version->changed_list; ISC_LIST_INIT(version->changed_list); + resigned_list = version->resigned_list; + ISC_LIST_INIT(version->resigned_list); rollback = ISC_TRUE; cleanup_version = version; rbtdb->future_version = NULL; @@ -1916,6 +1961,25 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) { sizeof(*cleanup_version)); } + /* + * Commit/rollback re-signed headers. + */ + for (header = HEAD(resigned_list); + header != NULL; + header = HEAD(resigned_list)) { + ISC_LIST_UNLINK(resigned_list, header, lru_link); + if (rollback) { + nodelock_t *lock; + lock = &rbtdb->node_locks[header->node->locknum].lock; + NODE_LOCK(lock, isc_rwlocktype_write); + resign_insert(rbtdb, header->node->locknum, header); + NODE_UNLOCK(lock, isc_rwlocktype_write); + } + decrement_reference(rbtdb, header->node, least_serial, + isc_rwlocktype_write, + isc_rwlocktype_none); + } + if (!EMPTY(cleanup_list)) { /* * We acquire a tree write lock here in order to make sure @@ -2306,6 +2370,14 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdataset->private6 = header->noqname; if (rdataset->private6 != NULL) rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; + /* + * Copy out re-signing information. + */ + if (RESIGN(header)) { + rdataset->attributes |= DNS_RDATASETATTR_RESIGN; + rdataset->resign = header->resign; + } else + rdataset->resign = 0; } static inline isc_result_t @@ -2993,12 +3065,12 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * If we're here, then the name does not exist, is not * beneath a zonecut, and there's no matching wildcard. */ - if (search.rbtdb->secure || + if (search.rbtdb->secure == dns_db_secure || (search.options & DNS_DBFIND_FORCENSEC) != 0) { result = find_closest_nsec(&search, nodep, foundname, - rdataset, sigrdataset, - search.rbtdb->secure); + rdataset, sigrdataset, + ISC_TRUE); if (result == ISC_R_SUCCESS) result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN; @@ -3222,7 +3294,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * The desired type doesn't exist. */ result = DNS_R_NXRRSET; - if (search.rbtdb->secure && + if (search.rbtdb->secure == dns_db_secure && (nsecheader == NULL || nsecsig == NULL)) { /* * The zone is secure but there's no NSEC, @@ -3237,7 +3309,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, NODE_UNLOCK(lock, isc_rwlocktype_read); result = find_closest_nsec(&search, nodep, foundname, rdataset, sigrdataset, - search.rbtdb->secure); + search.rbtdb->secure == + dns_db_secure); if (result == ISC_R_SUCCESS) result = DNS_R_EMPTYWILD; goto tree_exit; @@ -3256,7 +3329,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, new_reference(search.rbtdb, node); *nodep = node; } - if (search.rbtdb->secure || + if (search.rbtdb->secure == dns_db_secure || (search.options & DNS_DBFIND_FORCENSEC) != 0) { bind_rdataset(search.rbtdb, node, nsecheader, @@ -4545,11 +4618,13 @@ printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { first = ISC_FALSE; fprintf(out, "\tserial = %lu, ttl = %u, " - "trust = %u, attributes = %u\n", + "trust = %u, attributes = %u, " + "resign = %u\n", (unsigned long)current->serial, current->rdh_ttl, current->trust, - current->attributes); + current->attributes, + current->resign); current = current->down; } while (current != NULL); } @@ -4941,6 +5016,16 @@ cachestat_update(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { } #endif +static isc_result_t +resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) { + isc_result_t result; + + INSIST(newheader->heap_index == 0); + INSIST(!ISC_LINK_LINKED(newheader, lru_link)); + result = isc_heap_insert(rbtdb->heaps[idx], newheader); + return (result); +} + static isc_result_t add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, rdatasetheader_t *newheader, unsigned int options, isc_boolean_t loading, @@ -4956,6 +5041,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, dns_rdatatype_t rdtype, covers; rbtdb_rdatatype_t negtype; dns_trust_t trust; + int idx; /* * Add an rdatasetheader_t to a node. @@ -5158,6 +5244,10 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, free_rdataset(rbtdb, rbtdb->common.mctx, newheader); newheader = (rdatasetheader_t *)merged; + if (loading && RESIGN(newheader) && + RESIGN(header) && + header->resign < newheader->resign) + newheader->resign = header->resign; } else { free_rdataset(rbtdb, rbtdb->common.mctx, newheader); @@ -5250,12 +5340,10 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, set_ttl(rbtdb, header, 0); header->attributes |= RDATASET_ATTR_STALE; } + idx = newheader->node->locknum; if (IS_CACHE(rbtdb)) { - int idx = newheader->node->locknum; - ISC_LIST_PREPEND(rbtdb->rdatasets[idx], newheader, lru_link); - /* * XXXMLG We don't check the return value * here. If it fails, we will not do TTL @@ -5264,16 +5352,17 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, * will not leak... for long. */ isc_heap_insert(rbtdb->heaps[idx], newheader); - } #ifdef LRU_DEBUG - cachestat_update(rbtdb, newheader); + cachestat_update(rbtdb, newheader); #endif - } - } else { - /* - * No non-IGNORED rdatasets of the given type exist at - * this node. - */ + } else if (RESIGN(newheader)) + resign_insert(rbtdb, idx, newheader); + } + } else { + /* + * No non-IGNORED rdatasets of the given type exist at + * this node. + */ /* * If we're trying to delete the type, don't bother. @@ -5313,15 +5402,17 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, newheader->down = NULL; rbtnode->data = newheader; } + idx = newheader->node->locknum; if (IS_CACHE(rbtdb)) { - int idx = newheader->node->locknum; ISC_LIST_PREPEND(rbtdb->rdatasets[idx], newheader, lru_link); isc_heap_insert(rbtdb->heaps[idx], newheader); - } #ifdef LRU_DEBUG - cachestat_update(rbtdb, newheader); + cachestat_update(rbtdb, newheader); #endif + } else if (RESIGN(newheader)) { + resign_insert(rbtdb, idx, newheader); + } } /* @@ -5446,8 +5537,14 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, if (rbtversion != NULL) { newheader->serial = rbtversion->serial; now = 0; + if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { + newheader->attributes |= RDATASET_ATTR_RESIGN; + newheader->resign = rdataset->resign; + } else + newheader->resign = 0; } else { newheader->serial = 1; + newheader->resign = 0; if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) newheader->attributes |= RDATASET_ATTR_NXDOMAIN; if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { @@ -5595,6 +5692,11 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, newheader->additional_glue = NULL; newheader->last_used = 0; newheader->node = rbtnode; + if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { + newheader->attributes |= RDATASET_ATTR_RESIGN; + newheader->resign = rdataset->resign; + } else + newheader->resign = 0; NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, isc_rwlocktype_write); @@ -5644,6 +5746,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, if (result == ISC_R_SUCCESS) { free_rdataset(rbtdb, rbtdb->common.mctx, newheader); newheader = (rdatasetheader_t *)subresult; + init_rdataset(rbtdb, newheader); /* * We have to set the serial since the rdataslab * subtraction routine copies the reserved portion of @@ -5849,9 +5952,13 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { newheader->count = init_count++; newheader->additional_auth = NULL; newheader->additional_glue = NULL; - /* won't be used, but initialize anyway */ newheader->last_used = 0; newheader->node = node; + if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { + newheader->attributes |= RDATASET_ATTR_RESIGN; + newheader->resign = rdataset->resign; + } else + newheader->resign = 0; result = add(rbtdb, node, rbtdb->current_version, newheader, DNS_DBADD_MERGE, ISC_TRUE, NULL, 0); @@ -5966,12 +6073,28 @@ issecure(dns_db_t *db) { REQUIRE(VALID_RBTDB(rbtdb)); RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - secure = rbtdb->secure; + secure = ISC_TF(rbtdb->secure == dns_db_secure); RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); return (secure); } +static isc_boolean_t +isdnssec(dns_db_t *db) { + dns_rbtdb_t *rbtdb; + isc_boolean_t dnssec; + + rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(rbtdb)); + + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + dnssec = ISC_TF(rbtdb->secure != dns_db_insecure); + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + + return (dnssec); +} + static unsigned int nodecount(dns_db_t *db) { dns_rbtdb_t *rbtdb; @@ -6035,6 +6158,125 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { return (result); } +static isc_result_t +setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + isc_stdtime_t oldresign; + isc_result_t result = ISC_R_SUCCESS; + rdatasetheader_t *header; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(!IS_CACHE(rbtdb)); + REQUIRE(rdataset != NULL); + + header = rdataset->private3; + header--; + + NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock, + isc_rwlocktype_write); + + oldresign = header->resign; + header->resign = resign; + if (header->heap_index != 0) { + INSIST(RESIGN(header)); + if (resign == 0) { + isc_heap_delete(rbtdb->heaps[header->node->locknum], + header->heap_index); + header->heap_index = 0; + } else if (resign < oldresign) + isc_heap_increased(rbtdb->heaps[header->node->locknum], + header->heap_index); + else + isc_heap_decreased(rbtdb->heaps[header->node->locknum], + header->heap_index); + } else if (resign && header->heap_index == 0) { + header->attributes |= RDATASET_ATTR_RESIGN; + result = resign_insert(rbtdb, header->node->locknum, header); + } + NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock, + isc_rwlocktype_write); + return (result); +} + +static isc_result_t +getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, + dns_name_t *foundname) +{ + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + rdatasetheader_t *header = NULL, *this; + unsigned int i; + isc_result_t result = ISC_R_NOTFOUND; + + REQUIRE(VALID_RBTDB(rbtdb)); + + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); + + for (i = 0; i < rbtdb->node_lock_count; i++) { + this = isc_heap_element(rbtdb->heaps[i], 1); + if (this == NULL) + continue; + if (header == NULL) + header = this; + else if (isc_serial_lt(this->resign, header->resign)) + header = this; + } + + if (header == NULL) + goto unlock; + + NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock, + isc_rwlocktype_read); + + bind_rdataset(rbtdb, header->node, header, 0, rdataset); + + if (foundname != NULL) + dns_rbt_fullnamefromnode(header->node, foundname); + + NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock, + isc_rwlocktype_read); + + result = ISC_R_SUCCESS; + + unlock: + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); + + return (result); +} + +static void +resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) +{ + rbtdb_version_t *rbtversion = (rbtdb_version_t *)version; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + dns_rbtnode_t *node; + rdatasetheader_t *header; + + REQUIRE(VALID_RBTDB(rbtdb)); + REQUIRE(rdataset != NULL); + REQUIRE(rbtdb->future_version == rbtversion); + REQUIRE(rbtversion->writer); + + node = rdataset->private2; + header = rdataset->private3; + header--; + + RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read); + NODE_LOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_write); + /* + * Delete from heap and save to re-signed list so that it can + * be restored if we backout of this change. + */ + new_reference(rbtdb, node); + isc_heap_delete(rbtdb->heaps[node->locknum], header->heap_index); + header->heap_index = 0; + ISC_LIST_APPEND(rbtversion->resigned_list, header, lru_link); + + NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock, + isc_rwlocktype_write); + RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); +} + static dns_dbmethods_t zone_methods = { attach, detach, @@ -6065,6 +6307,10 @@ static dns_dbmethods_t zone_methods = { settask, getoriginnode, NULL, + setsigningtime, + getsigningtime, + resigned, + isdnssec, }; static dns_dbmethods_t cache_methods = { @@ -6096,7 +6342,11 @@ static dns_dbmethods_t cache_methods = { overmem, settask, getoriginnode, - NULL + NULL, + NULL, + NULL, + NULL, + isdnssec }; isc_result_t @@ -6168,28 +6418,32 @@ dns_rbtdb_create } for (i = 0; i < (int)rbtdb->node_lock_count; i++) ISC_LIST_INIT(rbtdb->rdatasets[i]); - - /* - * Create the heaps. - */ - rbtdb->heaps = isc_mem_get(mctx, rbtdb->node_lock_count * - sizeof(isc_heap_t *)); - if (rbtdb->heaps == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup_rdatasets; - } - for (i = 0; i < (int)rbtdb->node_lock_count; i++) - rbtdb->heaps[i] = NULL; - for (i = 0; i < (int)rbtdb->node_lock_count; i++) { - result = isc_heap_create(mctx, ttl_sooner, - ttl_set_index, 0, - &rbtdb->heaps[i]); - if (result != ISC_R_SUCCESS) - goto cleanup_heaps; - } } else { rbtdb->rdatasets = NULL; - rbtdb->heaps = NULL; + } + + /* + * Create the heaps. + */ + rbtdb->heaps = isc_mem_get(mctx, rbtdb->node_lock_count * + sizeof(isc_heap_t *)); + if (rbtdb->heaps == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_rdatasets; + } + for (i = 0; i < (int)rbtdb->node_lock_count; i++) + rbtdb->heaps[i] = NULL; + + for (i = 0; i < (int)rbtdb->node_lock_count; i++) { + if (IS_CACHE(rbtdb)) + result = isc_heap_create(mctx, ttl_sooner, set_index, + 0, &rbtdb->heaps[i]); + else + result = isc_heap_create(mctx, resign_sooner, + set_index, 0, + &rbtdb->heaps[i]); + if (result != ISC_R_SUCCESS) + goto cleanup_heaps; } rbtdb->deadnodes = isc_mem_get(mctx, rbtdb->node_lock_count * @@ -6297,7 +6551,7 @@ dns_rbtdb_create return (result); } rbtdb->attributes = 0; - rbtdb->secure = ISC_FALSE; + rbtdb->secure = dns_db_insecure; rbtdb->overmem = ISC_FALSE; rbtdb->task = NULL; @@ -6446,7 +6700,9 @@ rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { #if DNS_RDATASET_FIXED unsigned int offset; #endif + unsigned int length; isc_region_t r; + unsigned int flags = 0; REQUIRE(raw != NULL); @@ -6462,15 +6718,25 @@ rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { raw += offset; } #endif - r.length = raw[0] * 256 + raw[1]; - + length = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED raw += 4; #else raw += 2; #endif + if (rdataset->type == dns_rdatatype_rrsig) { + flags = ((*raw & DNS_RDATASLAB_WARNMASK) + >> DNS_RDATASLAB_WARNSHIFT) + << DNS_RDATA_WARNSHIFT; + if (*raw & DNS_RDATASLAB_OFFLINE) + flags |= DNS_RDATA_OFFLINE; + length--; + raw++; + } + r.length = length; r.base = raw; dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); + rdata->flags |= flags; } static void @@ -7539,6 +7805,8 @@ static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_stdtime_t now) { + INSIST(IS_CACHE(rbtdb)); + /* To be checked: can we really assume this? XXXMLG */ INSIST(ISC_LINK_LINKED(header, lru_link)); diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 0f1f080c17..2f1e934f80 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.c,v 1.196 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: rdata.c,v 1.197 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -269,7 +269,7 @@ dns_rdata_init(dns_rdata_t *rdata) { /* ISC_LIST_INIT(rdata->list); */ } -#if 0 +#if 1 #define DNS_RDATA_INITIALIZED(rdata) \ ((rdata)->data == NULL && (rdata)->length == 0 && \ (rdata)->rdclass == 0 && (rdata)->type == 0 && (rdata)->flags == 0 && \ @@ -282,8 +282,9 @@ dns_rdata_init(dns_rdata_t *rdata) { #define DNS_RDATA_INITIALIZED(rdata) ISC_TRUE #endif #endif + #define DNS_RDATA_VALIDFLAGS(rdata) \ - (((rdata)->flags & ~DNS_RDATA_UPDATE) == 0) + (((rdata)->flags & ~(DNS_RDATA_UPDATE|DNS_RDATA_OFFLINE)) == 0) void dns_rdata_reset(dns_rdata_t *rdata) { diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index e5c017e7a9..810535f5ae 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataset.c,v 1.79 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: rdataset.c,v 1.80 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -59,6 +59,7 @@ dns_rdataset_init(dns_rdataset_t *rdataset) { rdataset->privateuint4 = 0; rdataset->private5 = NULL; rdataset->private6 = NULL; + rdataset->resign = 0; } void @@ -620,7 +621,7 @@ dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) { isc_result_t dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, - dns_rdataset_t *nsec, dns_rdataset_t *nsecsig) + dns_rdataset_t *nsec, dns_rdataset_t *nsecsig) { REQUIRE(DNS_RDATASET_VALID(rdataset)); REQUIRE(rdataset->methods != NULL); diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c index 6de77bac8d..d69714e2c6 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataslab.c,v 1.43 2007/06/18 23:47:41 tbox Exp $ */ +/* $Id: rdataslab.c,v 1.44 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -47,6 +47,7 @@ * data records * data length (2 bytes) * order (2 bytes) + * meta data (1 byte for RRSIG's) * data (data length bytes) * * If DNS_RDATASET_FIXED is defined to be zero (0) the format of a @@ -141,6 +142,7 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, #if DNS_RDATASET_FIXED unsigned int *offsettable; #endif + unsigned int length; buflen = reservelen + 2; @@ -169,6 +171,8 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, #if DNS_RDATASET_FIXED x[i].order = i; #endif + if (x[i].rdata.flags & DNS_RDATA_OFFLINE) + fprintf(stderr, "OFFLINE\n"); result = dns_rdataset_next(rdataset); } if (result != ISC_R_NOMORE) @@ -209,12 +213,18 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, x[i].order = x[i-1].order; #endif nitems--; - } else + } else { #if DNS_RDATASET_FIXED buflen += (8 + x[i-1].rdata.length); #else buflen += (2 + x[i-1].rdata.length); #endif + /* + * Provide space to store the per RR meta data. + */ + if (rdataset->type == dns_rdatatype_rrsig) + buflen++; + } } /* * Don't forget the last item! @@ -224,6 +234,11 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, #else buflen += (2 + x[i-1].rdata.length); #endif + /* + * Provide space to store the per RR meta data. + */ + if (rdataset->type == dns_rdatatype_rrsig) + buflen++; /* * Ensure that singleton types are actually singletons. @@ -280,11 +295,26 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, #if DNS_RDATASET_FIXED offsettable[x[i].order] = rawbuf - offsetbase; #endif - *rawbuf++ = (x[i].rdata.length & 0xff00) >> 8; - *rawbuf++ = (x[i].rdata.length & 0x00ff); + length = x[i].rdata.length; + if (rdataset->type == dns_rdatatype_rrsig) + length++; + *rawbuf++ = (length & 0xff00) >> 8; + *rawbuf++ = (length & 0x00ff); #if DNS_RDATASET_FIXED rawbuf += 2; /* filled in later */ #endif + /* + * Store the per RR meta data. + */ + if (rdataset->type == dns_rdatatype_rrsig) { + *rawbuf = ((x[i].rdata.flags & DNS_RDATA_WARNMASK) + >> DNS_RDATA_WARNSHIFT) + << DNS_RDATASLAB_WARNSHIFT; + *rawbuf++ |= (x[i].rdata.flags & DNS_RDATA_OFFLINE) ? + DNS_RDATASLAB_OFFLINE : 0; + if (x[i].rdata.flags & DNS_RDATA_OFFLINE) + fprintf(stderr, "set DNS_RDATASLAB_OFFLINE\n"); + } memcpy(rawbuf, x[i].rdata.data, x[i].rdata.length); rawbuf += x[i].rdata.length; } @@ -360,17 +390,30 @@ static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { unsigned char *raw = rdataset->private5; isc_region_t r; + unsigned int length; + unsigned int flags = 0; REQUIRE(raw != NULL); - r.length = raw[0] * 256 + raw[1]; + length = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED raw += 4; #else raw += 2; #endif + if (rdataset->type == dns_rdatatype_rrsig) { + flags = ((*raw & DNS_RDATASLAB_WARNMASK) + >> DNS_RDATASLAB_WARNSHIFT) + << DNS_RDATA_WARNSHIFT; + if (*raw & DNS_RDATASLAB_OFFLINE) + flags |= DNS_RDATA_OFFLINE; + length--; + raw++; + } + r.length = length; r.base = raw; dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); + rdata->flags |= flags; } static void @@ -474,15 +517,29 @@ rdata_from_slab(unsigned char **current, { unsigned char *tcurrent = *current; isc_region_t region; + unsigned int length; + isc_boolean_t offline = ISC_FALSE; - region.length = *tcurrent++ * 256; - region.length += *tcurrent++; + length = *tcurrent++ * 256; + length += *tcurrent++; + + if (type == dns_rdatatype_rrsig) { + if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0) + offline = ISC_TRUE; + length--; + tcurrent++; + } + region.length = length; #if DNS_RDATASET_FIXED tcurrent += 2; #endif region.base = tcurrent; tcurrent += region.length; dns_rdata_fromregion(rdata, rdclass, type, ®ion); + if (offline) { + rdata->flags |= DNS_RDATA_OFFLINE; + fprintf(stderr, "rdata_from_slab: DNS_RDATA_OFFLINE\n"); + } *current = tcurrent; } @@ -528,9 +585,8 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, dns_rdataclass_t rdclass, dns_rdatatype_t type, unsigned int flags, unsigned char **tslabp) { - unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent; + unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data; unsigned int ocount, ncount, count, olength, tlength, tcount, length; - isc_region_t nregion; dns_rdata_t ordata = DNS_RDATA_INIT; dns_rdata_t nrdata = DNS_RDATA_INIT; isc_boolean_t added_something = ISC_FALSE; @@ -603,29 +659,24 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, * the old slab. */ do { - nregion.length = *ncurrent++ * 256; - nregion.length += *ncurrent++; -#if DNS_RDATASET_FIXED - ncurrent += 2; /* Skip order. */ -#endif - nregion.base = ncurrent; dns_rdata_init(&nrdata); - dns_rdata_fromregion(&nrdata, rdclass, type, &nregion); + rdata_from_slab(&ncurrent, rdclass, type, &nrdata); if (!rdata_in_slab(oslab, reservelen, rdclass, type, &nrdata)) { /* * This rdata isn't in the old slab. */ #if DNS_RDATASET_FIXED - tlength += nregion.length + 8; + tlength += nrdata.length + 8; #else - tlength += nregion.length + 2; + tlength += nrdata.length + 2; #endif + if (type == dns_rdatatype_rrsig) + tlength++; tcount++; nncount++; added_something = ISC_TRUE; } - ncurrent += nregion.length; ncount--; } while (ncount > 0); ncount = nncount; @@ -726,12 +777,17 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, offsettable[oorder] = tcurrent - offsetbase; #endif length = ordata.length; + data = ordata.data; + if (type == dns_rdatatype_rrsig) { + length++; + data--; + } *tcurrent++ = (length & 0xff00) >> 8; *tcurrent++ = (length & 0x00ff); #if DNS_RDATASET_FIXED tcurrent += 2; /* fill in later */ #endif - memcpy(tcurrent, ordata.data, length); + memcpy(tcurrent, data, length); tcurrent += length; oadded++; if (oadded < ocount) { @@ -748,12 +804,17 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, offsettable[ocount + norder] = tcurrent - offsetbase; #endif length = nrdata.length; + data = nrdata.data; + if (type == dns_rdatatype_rrsig) { + length++; + data--; + } *tcurrent++ = (length & 0xff00) >> 8; *tcurrent++ = (length & 0x00ff); #if DNS_RDATASET_FIXED tcurrent += 2; /* fill in later */ #endif - memcpy(tcurrent, nrdata.data, length); + memcpy(tcurrent, data, length); tcurrent += length; nadded++; if (nadded < ncount) { diff --git a/lib/dns/rootns.c b/lib/dns/rootns.c index 94770bcb80..01d23b5d4b 100644 --- a/lib/dns/rootns.c +++ b/lib/dns/rootns.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rootns.c,v 1.34 2008/02/05 23:47:08 tbox Exp $ */ +/* $Id: rootns.c,v 1.35 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -97,6 +97,7 @@ in_rootns(dns_rdataset_t *rootns, dns_name_t *name) { if (dns_name_compare(name, &ns.name) == 0) return (ISC_R_SUCCESS); result = dns_rdataset_next(rootns); + dns_rdata_reset(&rdata); } if (result == ISC_R_NOMORE) result = ISC_R_NOTFOUND; @@ -338,6 +339,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) { result = dns_rdataset_first(&rootrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rootrrset, &rdata); if (!inrrset(&hintrrset, &rdata)) report(view, name, ISC_TRUE, &rdata); @@ -345,6 +347,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, } result = dns_rdataset_first(&hintrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&hintrrset, &rdata); if (!inrrset(&rootrrset, &rdata)) report(view, name, ISC_FALSE, &rdata); @@ -355,6 +358,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) { result = dns_rdataset_first(&rootrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rootrrset, &rdata); report(view, name, ISC_TRUE, &rdata); result = dns_rdataset_next(&rootrrset); @@ -377,6 +381,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) { result = dns_rdataset_first(&rootrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rootrrset, &rdata); if (!inrrset(&hintrrset, &rdata)) report(view, name, ISC_TRUE, &rdata); @@ -385,6 +390,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, } result = dns_rdataset_first(&hintrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&hintrrset, &rdata); if (!inrrset(&rootrrset, &rdata)) report(view, name, ISC_FALSE, &rdata); @@ -396,6 +402,7 @@ check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db, (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) { result = dns_rdataset_first(&rootrrset); while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); dns_rdataset_current(&rootrrset, &rdata); report(view, name, ISC_TRUE, &rdata); dns_rdata_reset(&rdata); diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index ea7702b564..c3284803d6 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: sdb.c,v 1.62 2008/01/18 23:46:58 tbox Exp $ */ +/* $Id: sdb.c,v 1.63 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -1247,6 +1247,10 @@ static dns_dbmethods_t sdb_methods = { overmem, settask, NULL, + NULL, + NULL, + NULL, + NULL, NULL }; diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index 627ea2e193..22e8eb8105 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -50,7 +50,7 @@ * USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: sdlz.c,v 1.14 2007/08/27 03:32:27 marka Exp $ */ +/* $Id: sdlz.c,v 1.15 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -1051,6 +1051,10 @@ static dns_dbmethods_t sdlzdb_methods = { overmem, settask, NULL, + NULL, + NULL, + NULL, + NULL, NULL }; diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c index c64aa81b1b..5f22c54cb2 100644 --- a/lib/dns/tsig.c +++ b/lib/dns/tsig.c @@ -16,7 +16,7 @@ */ /* - * $Id: tsig.c,v 1.133 2008/01/18 23:46:58 tbox Exp $ + * $Id: tsig.c,v 1.134 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ #include @@ -445,7 +445,7 @@ cleanup_ring(dns_tsig_keyring_t *ring) dns_rbtnodechain_current(&chain, &foundname, origin, &node); tkey = node->data; if (tkey != NULL) { - tsig_log(tkey, 2, "tsig expire: generated=%d, refs=%d, expire=%d)", tkey->generated, isc_refcount_current(&tkey->refs), now - tkey->expire); + tsig_log(tkey, 2, "tsig expire: generated=%d, refs=%d, expire=%d", tkey->generated, isc_refcount_current(&tkey->refs), now - tkey->expire); if (tkey->generated && isc_refcount_current(&tkey->refs) == 1 && tkey->inception != tkey->expire diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 66302b6a25..dd1f672c03 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.472 2008/01/24 23:47:00 tbox Exp $ */ +/* $Id: zone.c,v 1.473 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -41,29 +42,36 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include -#include +#include #include +#include #include #include #include +#include + #define ZONE_MAGIC ISC_MAGIC('Z', 'O', 'N', 'E') #define DNS_ZONE_VALID(zone) ISC_MAGIC_VALID(zone, ZONE_MAGIC) @@ -112,6 +120,8 @@ typedef struct dns_load dns_load_t; typedef struct dns_forward dns_forward_t; typedef struct dns_io dns_io_t; typedef ISC_LIST(dns_io_t) dns_iolist_t; +typedef struct dns_signing dns_signing_t; +typedef ISC_LIST(dns_signing_t) dns_signinglist_t; #define DNS_ZONE_CHECKLOCK #ifdef DNS_ZONE_CHECKLOCK @@ -179,11 +189,15 @@ struct dns_zone { isc_time_t dumptime; isc_time_t loadtime; isc_time_t notifytime; + isc_time_t resigntime; + isc_time_t keywarntime; + isc_time_t signingtime; isc_uint32_t serial; isc_uint32_t refresh; isc_uint32_t retry; isc_uint32_t expire; isc_uint32_t minimum; + isc_stdtime_t key_expiry; char *keydirectory; isc_uint32_t maxrefresh; @@ -265,6 +279,10 @@ struct dns_zone { * Serial number for deferred journal compaction. */ isc_uint32_t compact_serial; + /*% + * Keys that are signing the zone for the first time. + */ + ISC_LIST(dns_signing_t) signing; }; #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0)) @@ -320,6 +338,11 @@ struct dns_zone { #define UNREACH_CHACHE_SIZE 10U #define UNREACH_HOLD_TIME 600 /* 10 minutes */ +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto failure; \ + } while (0) + struct dns_unreachable { isc_sockaddr_t remote; isc_sockaddr_t local; @@ -431,6 +454,19 @@ struct dns_io { isc_event_t *event; }; +/*% + * Hold state for when we are signing a zone with a new + * DNSKEY as result of a update. + */ +struct dns_signing { + unsigned int magic; + dns_db_t *db; + dns_dbiterator_t *dbiterator; + dns_secalg_t algorithm; + isc_uint16_t keyid; + ISC_LINK(dns_signing_t) link; +}; + #define SEND_BUFFER_SIZE 2048 static void zone_settimer(dns_zone_t *, isc_time_t *); @@ -513,6 +549,8 @@ static isc_boolean_t dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote, isc_sockaddr_t *local, isc_time_t *now); +static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, + uint16_t keyid); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -608,6 +646,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { isc_time_settoepoch(&zone->dumptime); isc_time_settoepoch(&zone->loadtime); zone->notifytime = now; + isc_time_settoepoch(&zone->resigntime); + isc_time_settoepoch(&zone->keywarntime); + isc_time_settoepoch(&zone->signingtime); zone->serial = 0; zone->refresh = DNS_ZONE_DEFAULTREFRESH; zone->retry = DNS_ZONE_DEFAULTRETRY; @@ -656,6 +697,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->maxxfrout = MAX_XFER_TIME; zone->ssutable = NULL; zone->sigvalidityinterval = 30 * 24 * 3600; + zone->sigvalidityinterval = 3600; /* XXMPA */ zone->view = NULL; zone->acache = NULL; zone->checkmx = NULL; @@ -667,6 +709,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->notifydelay = 5; zone->isself = NULL; zone->isselfarg = NULL; + ISC_LIST_INIT(zone->signing); zone->magic = ZONE_MAGIC; @@ -703,6 +746,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { static void zone_free(dns_zone_t *zone) { isc_mem_t *mctx = NULL; + dns_signing_t *signing; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(isc_refcount_current(&zone->erefs) == 0); @@ -725,6 +769,14 @@ zone_free(dns_zone_t *zone) { dns_zonemgr_releasezone(zone->zmgr, zone); /* Unmanaged objects */ + for (signing = ISC_LIST_HEAD(zone->signing); + signing != NULL; + signing = ISC_LIST_HEAD(zone->signing)) { + ISC_LIST_UNLINK(zone->signing, signing, link); + dns_db_detach(&signing->db); + dns_dbiterator_destroy(&signing->dbiterator); + isc_mem_put(zone->mctx, signing, sizeof *signing); + } if (zone->masterfile != NULL) isc_mem_free(zone->mctx, zone->masterfile); zone->masterfile = NULL; @@ -1319,6 +1371,33 @@ dns_zone_loadnew(dns_zone_t *zone) { return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT)); } +static unsigned int +get_master_options(dns_zone_t *zone) { + unsigned int options; + + options = DNS_MASTER_ZONE; + if (zone->type == dns_zone_slave) + options |= DNS_MASTER_SLAVE; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS)) + options |= DNS_MASTER_CHECKNS; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS)) + options |= DNS_MASTER_FATALNS; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES)) + options |= DNS_MASTER_CHECKNAMES; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) + options |= DNS_MASTER_CHECKNAMESFAIL; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMX)) + options |= DNS_MASTER_CHECKMX; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL)) + options |= DNS_MASTER_CHECKMXFAIL; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) + options |= DNS_MASTER_CHECKWILDCARD; + if (zone->type == dns_zone_master && + (zone->update_acl != NULL || zone->ssutable != NULL)) + options |= DNS_MASTER_RESIGN; + return (options); +} + static void zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { dns_load_t *load = event->ev_arg; @@ -1333,23 +1412,8 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { if (result == ISC_R_CANCELED) goto fail; - options = DNS_MASTER_ZONE; - if (load->zone->type == dns_zone_slave) - options |= DNS_MASTER_SLAVE; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKNS)) - options |= DNS_MASTER_CHECKNS; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_FATALNS)) - options |= DNS_MASTER_FATALNS; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKNAMES)) - options |= DNS_MASTER_CHECKNAMES; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKNAMESFAIL)) - options |= DNS_MASTER_CHECKNAMESFAIL; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMX)) - options |= DNS_MASTER_CHECKMX; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMXFAIL)) - options |= DNS_MASTER_CHECKMXFAIL; - if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKWILDCARD)) - options |= DNS_MASTER_CHECKWILDCARD; + options = get_master_options(load->zone); + result = dns_master_loadfileinc2(load->zone->masterfile, dns_db_origin(load->db), dns_db_origin(load->db), @@ -1410,25 +1474,10 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { isc_result_t tresult; unsigned int options; - options = DNS_MASTER_ZONE; + options = get_master_options(zone); + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS)) options |= DNS_MASTER_MANYERRORS; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS)) - options |= DNS_MASTER_CHECKNS; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS)) - options |= DNS_MASTER_FATALNS; - if (zone->type == dns_zone_slave) - options |= DNS_MASTER_SLAVE; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES)) - options |= DNS_MASTER_CHECKNAMES; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) - options |= DNS_MASTER_CHECKNAMESFAIL; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMX)) - options |= DNS_MASTER_CHECKMX; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL)) - options |= DNS_MASTER_CHECKMXFAIL; - if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) - options |= DNS_MASTER_CHECKWILDCARD; if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) { load = isc_mem_get(zone->mctx, sizeof(*load)); @@ -1964,6 +2013,82 @@ zone_check_dnskeys(dns_zone_t *zone, dns_db_t *db) { } +static void +set_signingtime(dns_zone_t *zone) { + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_t rdataset; + isc_result_t result; + + result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) + goto cleanup; + + dns_db_currentversion(zone->db, &version); + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(zone->db, node, version, + (dns_rdatatype_t)0xffff, + dns_rdatatype_none, 0, + &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdataset_current(&rdataset, &rdata); + if (rdata.length != 4 || rdata.data[3] != 0) { + dns_rdata_reset(&rdata); + continue; + } + + result = zone_signwithkey(zone, rdata.data[0], + (rdata.data[1] << 8) | rdata.data[2]); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "dns_zone_signwithkey failed: %s", + dns_result_totext(result)); + } + dns_rdata_reset(&rdata); + } + dns_rdataset_disassociate(&rdataset); + + cleanup: + if (node != NULL) + dns_db_detachnode(zone->db, &node); + if (version != NULL) + dns_db_closeversion(zone->db, &version, ISC_FALSE); + +} + +static void +set_resigntime(dns_zone_t *zone) { + dns_rdataset_t rdataset; + dns_fixedname_t fixed; + char namebuf[DNS_NAME_FORMATSIZE]; + unsigned int resign; + isc_result_t result; + isc_uint32_t nanosecs; + + dns_rdataset_init(&rdataset); + dns_fixedname_init(&fixed); + result = dns_db_getsigningtime(zone->db, &rdataset, + dns_fixedname_name(&fixed)); + if (result != ISC_R_SUCCESS) { + isc_time_settoepoch(&zone->resigntime); + return; + } + resign = rdataset.resign; +fprintf(stderr, "set_resigntime %u\n", resign); + dns_name_format(dns_fixedname_name(&fixed), namebuf, sizeof(namebuf)); + dns_rdataset_disassociate(&rdataset); + isc_random_get(&nanosecs); + nanosecs %= 1000000000; + isc_time_set(&zone->resigntime, resign, nanosecs); +} + static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result) @@ -1975,6 +2100,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_time_t now; isc_boolean_t needdump = ISC_FALSE; isc_boolean_t hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE); + unsigned int options; TIME_NOW(&now); @@ -2019,7 +2145,12 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOMERGE) && ! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) { - result = dns_journal_rollforward(zone->mctx, db, + if (zone->type == dns_zone_master && + (zone->update_acl != NULL || zone->ssutable != NULL)) + options = DNS_JOURNALOPT_RESIGN; + else + options = 0; + result = dns_journal_rollforward(zone->mctx, db, options, zone->journal); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND && result != DNS_R_UPTODATE && result != DNS_R_NOJOURNAL && @@ -2046,7 +2177,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, zone->loadtime = loadtime; dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded"); - /* * Obtain ns, soa and cname counts for top of zone. */ @@ -2121,6 +2251,17 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, "zone may fail to transfer " "to slaves."); } + + if (zone->type == dns_zone_master && + (zone->update_acl != NULL || zone->ssutable != NULL) && + (zone->sigvalidityinterval / 4) < (3 * refresh) && + dns_db_issecure(db)) + { + dns_zone_log(zone, ISC_LOG_WARNING, + "1/4 sig-validity-interval less than " + "3 * refresh."); + } + zone->serial = serial; zone->refresh = RANGE(refresh, zone->minrefresh, zone->maxrefresh); @@ -2195,8 +2336,13 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = ISC_R_SUCCESS; if (needdump) zone_needdump(zone, DNS_DUMP_DELAY); - if (zone->task != NULL) + if (zone->task != NULL) { + if (zone->type == dns_zone_master) { + set_resigntime(zone); + set_signingtime(zone); + } zone_settimer(zone, &now); + } if (! dns_db_ispersistent(db)) dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s", @@ -3009,6 +3155,1275 @@ was_dumping(dns_zone_t *zone) { return (dumping); } +#define MAXZONEKEYS 10 + +static isc_result_t +do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver, + dns_diff_t *diff) +{ + dns_diff_t temp_diff; + isc_result_t result; + + /* + * Create a singleton diff. + */ + dns_diff_init(diff->mctx, &temp_diff); + ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); + + /* + * Apply it to the database. + */ + result = dns_diff_apply(&temp_diff, db, ver); + ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); + if (result != ISC_R_SUCCESS) { + dns_difftuple_free(tuple); + return (result); + } + + /* + * Merge it into the current pending journal entry. + */ + dns_diff_appendminimal(diff, tuple); + + /* + * Do not clear temp_diff. + */ + return (ISC_R_SUCCESS); +} + +static isc_result_t +increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver, + dns_diff_t *diff, isc_mem_t *mctx) +{ + dns_difftuple_t *deltuple = NULL; + dns_difftuple_t *addtuple = NULL; + isc_uint32_t serial; + isc_result_t result; + + CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple)); + CHECK(dns_difftuple_copy(deltuple, &addtuple)); + addtuple->op = DNS_DIFFOP_ADD; + + serial = dns_soa_getserial(&addtuple->rdata); + + /* RFC1982 */ + serial = (serial + 1) & 0xFFFFFFFF; + if (serial == 0) + serial = 1; + + dns_soa_setserial(serial, &addtuple->rdata); + CHECK(do_one_tuple(&deltuple, db, ver, diff)); + CHECK(do_one_tuple(&addtuple, db, ver, diff)); + result = ISC_R_SUCCESS; + + failure: + if (addtuple != NULL) + dns_difftuple_free(&addtuple); + if (deltuple != NULL) + dns_difftuple_free(&deltuple); + return (result); +} + +static isc_result_t +update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, + dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, + dns_rdata_t *rdata) +{ + dns_difftuple_t *tuple = NULL; + isc_result_t result; + result = dns_difftuple_create(diff->mctx, op, + name, ttl, rdata, &tuple); + if (result != ISC_R_SUCCESS) + return (result); + return (do_one_tuple(&tuple, db, ver, diff)); +} + +static isc_boolean_t +ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) { + isc_boolean_t ret = ISC_FALSE; + isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE; + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_dnskey_t dnskey; + + dns_rdataset_init(&rdataset); + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, + &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) { + dns_rdataset_current(&rdataset, &rdata); + CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL)); + if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH)) + == DNS_KEYOWNER_ZONE) { + if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0) + have_ksk = ISC_TRUE; + else + have_nonksk = ISC_TRUE; + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + if (have_ksk && have_nonksk) + ret = ISC_TRUE; + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + return (ret); +} + +static isc_result_t +find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, + isc_mem_t *mctx, unsigned int maxkeys, + dst_key_t **keys, unsigned int *nkeys) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + const char *directory = dns_zone_getkeydirectory(zone); + CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node)); + result = dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db), + directory, mctx, maxkeys, keys, + nkeys); + if (result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +offline(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, dns_name_t *name, + dns_ttl_t ttl, dns_rdata_t *rdata) +{ + isc_result_t result; + + if ((rdata->flags & DNS_RDATA_OFFLINE) != 0) { +fprintf(stderr, "rdata is already marked as offline\n"); + return (ISC_R_SUCCESS); + } +fprintf(stderr, "mark as offline\n"); + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DELRESIGN, + name, ttl, rdata); + if (result != ISC_R_SUCCESS) + return (result); + rdata->flags |= DNS_RDATA_OFFLINE; + result = update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, + name, ttl, rdata); + return (result); +} + +static void +set_key_expiry_warning(dns_zone_t *zone, isc_stdtime_t when, isc_stdtime_t now) +{ + unsigned int delta; + + zone->key_expiry = when; + if (when <= now) { + dns_zone_log(zone, ISC_LOG_ERROR, + "DNSKEY RRSIG(s) have expired"); + isc_time_settoepoch(&zone->keywarntime); + } else if (when < now + 7 * 24 * 3600) { + dns_zone_log(zone, ISC_LOG_WARNING, + "DNSKEY RRSIG(s) will expire at %u", + when); /* XXXMPA convert to date. */ + delta = when - now; + delta--; /* loop prevention */ + delta /= 24 * 3600; /* to whole days */ + delta *= 24 * 3600; /* to seconds */ + isc_time_set(&zone->keywarntime, when - delta, 0); + } else { + dns_zone_log(zone, ISC_LOG_NOTICE, /* XXMPA ISC_LOG_DEBUG(1) */ + "setting keywarntime to %u - 7 days", + when); /* XXXMPA convert to date. */ + isc_time_set(&zone->keywarntime, when - 7 * 24 * 3600, 0); + } +} + +/* + * Delete expired RRsigs and any RRsigs we are about to re-sign. + * See also update.c:del_keysigs(). + */ +static isc_result_t +del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys, + unsigned int nkeys, isc_stdtime_t now) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i; + dns_rdata_rrsig_t rrsig; + isc_boolean_t found; + isc_stdtime_t warn = 0, maybe = 0; + + dns_rdataset_init(&rdataset); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, type, + (isc_stdtime_t) 0, &rdataset, NULL); + dns_db_detachnode(db, &node); + + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + if (type != dns_rdatatype_dnskey) { + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + continue; + } + + /* + * RRSIG(DNSKEY) requires special processing. + */ + found = ISC_FALSE; + for (i = 0; i < nkeys; i++) { + if (rrsig.keyid == dst_key_id(keys[i])) { + found = ISC_TRUE; + /* + * Mark offline RRSIG(DNSKEY). + * We want the earliest offline expire time + * iff there is a new offline signature. + */ + if (!dst_key_isprivate(keys[i])) { + if (warn != 0 && + warn > rrsig.timeexpire) + warn = rrsig.timeexpire; + if (rdata.flags & DNS_RDATA_OFFLINE) { + if (maybe == 0 || + maybe > rrsig.timeexpire) + maybe = + rrsig.timeexpire; + break; + } + if (warn == 0) + warn = maybe; + if (warn == 0 || + warn > rrsig.timeexpire) + warn = rrsig.timeexpire; + result = offline(db, ver, diff, name, + rdataset.ttl, &rdata); + break; + } + result = update_one_rr(db, ver, diff, + DNS_DIFFOP_DEL, + name, rdataset.ttl, + &rdata); + break; + } + } + /* + * If there is not a matching DNSKEY then + * delete the RRSIG. + */ + if (!found) + result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata); + dns_rdata_reset(&rdata); + if (result != ISC_R_SUCCESS) + break; + } + dns_rdataset_disassociate(&rdataset); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + if (warn != 0) + set_key_expiry_warning(zone, warn, now); +failure: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, + dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys, + unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception, + isc_stdtime_t expire, isc_boolean_t check_ksk) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t sig_rdata = DNS_RDATA_INIT; + unsigned char data[1024]; /* XXX */ + isc_buffer_t buffer; + unsigned int i; + + dns_rdataset_init(&rdataset); + isc_buffer_init(&buffer, data, sizeof(data)); + + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + result = dns_db_findrdataset(db, node, ver, type, 0, + (isc_stdtime_t) 0, &rdataset, NULL); + dns_db_detachnode(db, &node); + if (result == ISC_R_NOTFOUND) + return (ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + goto failure; + + for (i = 0; i < nkeys; i++) { + if (check_ksk && type != dns_rdatatype_dnskey && + (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0) + continue; + if (!dst_key_isprivate(keys[i])) + continue; + /* Calculate the signature, creating a RRSIG RDATA. */ + CHECK(dns_dnssec_sign(name, &rdataset, keys[i], + &inception, &expire, + mctx, &buffer, &sig_rdata)); + /* Update the database and journal with the RRSIG. */ + /* XXX inefficient - will cause dataset merging */ + CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, + name, rdataset.ttl, &sig_rdata)); + dns_rdata_reset(&sig_rdata); + } + + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static void +zone_resigninc(dns_zone_t *zone) { + const char *journalfile; + dns_db_t *db = NULL; + dns_dbversion_t *version = NULL; + dns_diff_t sig_diff; + dns_fixedname_t fixed; + dns_name_t *name; + dns_rdataset_t rdataset; + dns_rdatatype_t covers; + dst_key_t *zone_keys[MAXZONEKEYS]; + isc_boolean_t check_ksk; + isc_result_t result; + isc_stdtime_t now, inception, soaexpire, expire, stop; + isc_uint32_t jitter; + unsigned int i; + unsigned int nkeys = 0; + unsigned int resign; + + dns_rdataset_init(&rdataset); + dns_fixedname_init(&fixed); + dns_diff_init(zone->mctx, &sig_diff); + + /* + * Updates are disabled. Pause for 5 minutes. + */ + if (zone->update_disabled) { + result = ISC_R_FAILURE; + goto failure; + } + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + dns_db_attach(zone->db, &db); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:dns_db_newversion -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = find_zone_keys(zone, db, version, zone->mctx, MAXZONEKEYS, + zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:find_zone_keys -> %s\n", + dns_result_totext(result)); + goto failure; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for clock skew. */ + inception = now - 4; /* XXXMPA */ + soaexpire = now + dns_zone_getsigvalidityinterval(zone); + /* + * Spread out signatures over time if they happen to be + * clumped. We don't do this for each add_sigs() call as + * we still want some clustering to occur. + */ + isc_random_get(&jitter); + expire = soaexpire - jitter % 3600; + expire = soaexpire; /* XXXMPA */ + stop = now + 5; + + check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); + if (check_ksk) + check_ksk = ksk_sanity(db, version); + + name = dns_fixedname_name(&fixed); + result = dns_db_getsigningtime(db, &rdataset, name); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:dns_db_getsigningtime -> %s\n", + dns_result_totext(result)); + } + + i = 0; + while (result == ISC_R_SUCCESS) { + resign = rdataset.resign; + covers = rdataset.covers; + /* + * Stop if we hit the SOA as that means we have walked the + * entire zone. The SOA record should always be the most + * recent signature. + */ + /* XXXMPA increase number of RRsets signed pre call */ + if (covers == dns_rdatatype_soa || i++ > 3 || resign > stop) { + /* + * Ensure that we don't loop resigning the SOA. + */ + if (covers == dns_rdatatype_soa) + dns_db_resigned(db, &rdataset, version); + dns_rdataset_disassociate(&rdataset); + break; + } + + dns_db_resigned(db, &rdataset, version); + dns_rdataset_disassociate(&rdataset); + + result = del_sigs(zone, db, version, name, covers, &sig_diff, + zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:del_sigs -> %s\n", + dns_result_totext(result)); + break; + } + result = add_sigs(db, version, name, covers, &sig_diff, + zone_keys, nkeys, zone->mctx, inception, + expire, check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:add_sigs -> %s\n", + dns_result_totext(result)); + break; + } + result = dns_db_getsigningtime(db, &rdataset, + dns_fixedname_name(&fixed)); + if (nkeys == 0 && result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + break; + } + if (result != ISC_R_SUCCESS) + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:dns_db_getsigningtime -> %s\n", + dns_result_totext(result)); + } + + if (result != ISC_R_NOMORE && result != ISC_R_SUCCESS) + goto failure; + + result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa, + &sig_diff, zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:del_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = increment_soa_serial(db, version, &sig_diff, zone->mctx); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:increment_soa_serial -> %s\n", + dns_result_totext(result)); + goto failure; + } + + /* + * Generate maximum life time signatures so that the above loop + * termination is sensible. + */ + result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa, + &sig_diff, zone_keys, nkeys, zone->mctx, inception, + soaexpire, check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:add_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + + journalfile = dns_zone_getjournal(zone); + if (journalfile != NULL) { + dns_journal_t *journal = NULL; + result = dns_journal_open(zone->mctx, journalfile, + ISC_TRUE, &journal); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:dns_journal_open -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = dns_journal_write_transaction(journal, &sig_diff); + dns_journal_destroy(&journal); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_resigninc:dns_journal_write_transaction -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + + /* + * Everything has succeeded. Commit the changes. + */ + dns_db_closeversion(db, &version, ISC_TRUE); + + failure: + dns_diff_clear(&sig_diff); + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); + if (version != NULL) { + dns_db_closeversion(zone->db, &version, ISC_FALSE); + dns_db_detach(&db); + } else if (db != NULL) + dns_db_detach(&db); + if (result == ISC_R_SUCCESS) { + set_resigntime(zone); + LOCK_ZONE(zone); + zone_needdump(zone, DNS_DUMP_DELAY); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); + UNLOCK_ZONE(zone); + } else { + /* + * Something failed. Retry in 5 minutes. + */ + isc_interval_t ival; + isc_interval_set(&ival, 300, 0); + isc_time_nowplusinterval(&zone->resigntime, &ival); + } +} + +static isc_result_t +next_active(dns_db_t *db, dns_dbversion_t *version, dns_name_t *oldname, + dns_name_t *newname, isc_boolean_t bottom) +{ + isc_result_t result; + dns_dbiterator_t *dbit = NULL; + dns_rdatasetiter_t *rdsit = NULL; + dns_dbnode_t *node = NULL; + + CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit)); + CHECK(dns_dbiterator_seek(dbit, oldname)); + while (1) { + result = dns_dbiterator_next(dbit); + if (result == ISC_R_NOMORE) + CHECK(dns_dbiterator_first(dbit)); + CHECK(dns_dbiterator_current(dbit, &node, newname)); + if (bottom && dns_name_issubdomain(newname, oldname) && + !dns_name_equal(newname, oldname)) { + dns_db_detachnode(db, &node); + continue; + } + /* + * Is this node empty? + */ + CHECK(dns_db_allrdatasets(db, node, version, 0, &rdsit)); + result = dns_rdatasetiter_first(rdsit); + dns_db_detachnode(db, &node); + dns_rdatasetiter_destroy(&rdsit); + if (result != ISC_R_NOMORE) + break; + } + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + if (dbit != NULL) + dns_dbiterator_destroy(&dbit); + return (result); +} + +static void +set_bit(unsigned char *array, unsigned int index) { + unsigned int shift, mask; + + shift = 7 - (index % 8); + mask = 1 << shift; + + array[index / 8] |= mask; +} + +static isc_boolean_t +signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dst_key_t *key) +{ + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t rrsig; + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, version, dns_rdatatype_rrsig, + type, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &rrsig, NULL); + INSIST(result == ISC_R_SUCCESS); + if (rrsig.algorithm == dst_key_alg(key) && + rrsig.keyid == dst_key_id(key)) { + dns_rdataset_disassociate(&rdataset); + return (ISC_TRUE); + } + dns_rdata_reset(&rdata); + } + dns_rdataset_disassociate(&rdataset); + return (ISC_FALSE); +} + +static isc_result_t +sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node, + dns_dbversion_t *version, dst_key_t *key, + isc_stdtime_t inception, isc_stdtime_t expire, + unsigned int minimum, + isc_boolean_t is_ksk, isc_boolean_t *delegation, + dns_diff_t *diff, int *signatures, isc_mem_t *mctx) +{ + isc_result_t result; + dns_rdatasetiter_t *iterator = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_buffer_t buffer; + unsigned char data[1024]; + isc_boolean_t seen_soa, seen_ns, seen_rr, seen_dname, seen_nsec; + unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE]; + isc_boolean_t bottom; + dns_name_t *next; + dns_fixedname_t fixed; + + result = dns_db_allrdatasets(db, node, version, 0, &iterator); + if (result != ISC_R_SUCCESS) { + if (result == ISC_R_NOTFOUND) + result = ISC_R_SUCCESS; + return (result); + } + dns_rdataset_init(&rdataset); + isc_buffer_init(&buffer, data, sizeof(data)); + seen_rr = seen_soa = seen_ns = seen_dname = seen_nsec = ISC_FALSE; + for (result = dns_rdatasetiter_first(iterator); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iterator)) { + dns_rdatasetiter_current(iterator, &rdataset); + if (rdataset.type == dns_rdatatype_soa) + seen_soa = ISC_TRUE; + else if (rdataset.type == dns_rdatatype_ns) + seen_ns = ISC_TRUE; + else if (rdataset.type == dns_rdatatype_dname) + seen_dname = ISC_TRUE; + else if (rdataset.type == dns_rdatatype_nsec) + seen_nsec = ISC_TRUE; + seen_rr = ISC_TRUE; + dns_rdataset_disassociate(&rdataset); + } + if (result != ISC_R_NOMORE) + goto failure; + if (seen_ns && !seen_soa) + *delegation = ISC_TRUE; + if (!seen_nsec && seen_rr) { + /* Build and add NSEC. */ + dns_fixedname_init(&fixed); + next = dns_fixedname_name(&fixed); + bottom = (seen_ns && !seen_soa) || seen_dname; + CHECK(next_active(db, version, name, next, bottom)); + CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer, + &rdata)); + if (dns_name_equal(dns_db_origin(db), name)) { + /* + * Set the OPT bit to indicate that this is a + * partially secure zone. + */ + isc_region_t region; + + dns_rdata_toregion(&rdata, ®ion); + dns_name_fromregion(next, ®ion); + isc_region_consume(®ion, next->length); + INSIST(region.length > (2 + dns_rdatatype_opt / 8) && + region.base[0] == 0 && + region.base[1] > dns_rdatatype_opt / 8); + set_bit(region.base + 2, dns_rdatatype_opt); + } + CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, + minimum, &rdata)); + dns_rdata_reset(&rdata); + /* Count a NSEC generation as a signature generation. */ + (*signatures)--; + } + result = dns_rdatasetiter_first(iterator); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(iterator, &rdataset); + if (rdataset.type == dns_rdatatype_soa || + rdataset.type == dns_rdatatype_rrsig) + goto next_rdataset; + if (is_ksk && rdataset.type != dns_rdatatype_dnskey) + goto next_rdataset; + if (*delegation && !dns_rdatatype_atparent(rdataset.type)) + goto next_rdataset; + if (signed_with_key(db, node, version, rdataset.type, key)) + goto next_rdataset; + /* Calculate the signature, creating a RRSIG RDATA. */ + CHECK(dns_dnssec_sign(name, &rdataset, key, &inception, + &expire, mctx, &buffer, &rdata)); + /* Update the database and journal with the RRSIG. */ + /* XXX inefficient - will cause dataset merging */ + CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADDRESIGN, + name, rdataset.ttl, &rdata)); + dns_rdata_reset(&rdata); + (*signatures)--; + next_rdataset: + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(iterator); + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + if (seen_dname) + *delegation = ISC_TRUE; +failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (iterator != NULL) + dns_rdatasetiter_destroy(&iterator); + return (result); +} + +static isc_result_t +updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + dns_ttl_t minimum, isc_boolean_t *secureupdated, dns_diff_t *diff) +{ + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE]; + dns_rdataset_t rdataset; + dns_rdata_nsec_t nsec; + dns_dbnode_t *node = NULL; + + /* + * Check to see if the OPT bit has already been cleared. + */ + CHECK(dns_db_getoriginnode(db, &node)); + dns_rdataset_init(&rdataset); + CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, + dns_rdatatype_none, 0, &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); + dns_rdataset_current(&rdataset, &rdata); + if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt)) + goto failure; + + /* + * Find the NEXT name for building the new record. + */ + CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL)); + + /* + * Delete the old NSEC record. + */ + CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum, + &rdata)); + dns_rdata_reset(&rdata); + + /* + * Add the new NSEC record. + */ + CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer, + &rdata)); + CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum, + &rdata)); + dns_rdata_reset(&rdata); + + *secureupdated = ISC_TRUE; + + failure: + if (node != NULL) + dns_db_detachnode(db, &node); + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + return (result); +} + +static isc_result_t +updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version, + dns_name_t *name, dns_diff_t *diff) +{ + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned char data[4]; + + dns_rdataset_init(&rdataset); + result = dns_db_getoriginnode(signing->db, &node); + if (result != ISC_R_SUCCESS) + goto failure; + + result = dns_db_findrdataset(signing->db, node, version, 0xffff, + dns_rdatatype_none, 0, &rdataset, NULL); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + goto failure; + } + if (result != ISC_R_SUCCESS) + goto failure; + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) { + dns_rdataset_current(&rdataset, &rdata); + if (rdata.length != 4 || + rdata.data[0] != signing->algorithm || + rdata.data[1] != ((signing->keyid >> 8) & 0xff) || + rdata.data[2] != (signing->keyid & 0xff) || + rdata.data[3] != 0) { + dns_rdata_reset(&rdata); + continue; + } + CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_DEL, + name, rdataset.ttl, &rdata)); + memcpy(data, rdata.data, 4); + data[3] = 1; /* mark as done */ + rdata.data = data; + CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD, + name, rdataset.ttl, &rdata)); + break; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + failure: + if (dns_rdataset_isassociated(&rdataset)) + dns_rdataset_disassociate(&rdataset); + if (node != NULL) + dns_db_detachnode(signing->db, &node); + return (result); +} + +/* + * Incrementally sign the zone using the keys requested. + * Builds the NSEC chain is required. + */ +static void +zone_sign(dns_zone_t *zone) { + const char *journalfile; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_diff_t sig_diff; + dns_fixedname_t fixed; + dns_fixedname_t nextfixed; + dns_name_t *name, *nextname; + dns_rdataset_t rdataset; + dns_signing_t *signing, *nextsigning; + dns_signinglist_t cleanup; + dst_key_t *zone_keys[MAXZONEKEYS]; + int signatures; + isc_boolean_t check_ksk, is_ksk; + isc_boolean_t delegation; + isc_boolean_t finishedakey = ISC_FALSE; + isc_boolean_t secureupdated = ISC_FALSE; + isc_boolean_t first; + isc_result_t result; + isc_stdtime_t now, inception, soaexpire, expire, stop; + isc_uint32_t jitter; + unsigned int i; + unsigned int nkeys = 0; + unsigned int nodes; + + dns_rdataset_init(&rdataset); + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + dns_fixedname_init(&nextfixed); + nextname = dns_fixedname_name(&nextfixed); + dns_diff_init(zone->mctx, &sig_diff); + ISC_LIST_INIT(cleanup); + +fprintf(stderr, "zone_sign\n"); + + /* + * Updates are disabled. Pause for 5 minutes. + */ + if (zone->update_disabled) { + result = ISC_R_FAILURE; + goto failure; + } + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + dns_db_attach(zone->db, &db); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + + result = dns_db_newversion(db, &version); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:dns_db_newversion -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = find_zone_keys(zone, db, version, zone->mctx, + MAXZONEKEYS, zone_keys, &nkeys); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:find_zone_keys -> %s\n", + dns_result_totext(result)); + goto failure; + } + + isc_stdtime_get(&now); + inception = now - 3600; /* Allow for clock skew. */ + inception = now - 4; /* XXXMPA */ + soaexpire = now + dns_zone_getsigvalidityinterval(zone); + /* + * Spread out signatures over time if they happen to be + * clumped. We don't do this for each add_sigs() call as + * we still want some clustering to occur. + */ + isc_random_get(&jitter); + expire = soaexpire - jitter % 3600; + expire = soaexpire; /* XXXMPA */ + stop = now + 5; + + check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK); + if (check_ksk) + check_ksk = ksk_sanity(db, version); + + /* + * We keep pulling nodes off each interator in turn until + * we have no more noded to pull off or we reach the limits + * for this quantum. + */ + nodes = 10; + signatures = 3; + signing = ISC_LIST_HEAD(zone->signing); + first = ISC_TRUE; + while (signing != NULL && nodes-- > 0 && signatures > 0) { + nextsigning = ISC_LIST_NEXT(signing, link); + + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); + if (signing->db != zone->db) { + /* + * The zone has been reloaded. We will have + * created new signings as part of the reload + * process so we can destroy this one. + */ + ISC_LIST_UNLINK(zone->signing, signing, link); + dns_db_detach(&signing->db); + dns_dbiterator_destroy(&signing->dbiterator); + isc_mem_put(zone->mctx, signing, sizeof *signing); + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + goto next_signing; + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); + + if (signing->db != db) + goto next_signing; + + is_ksk = ISC_FALSE; + dns_dbiterator_current(signing->dbiterator, &node, name); + /* + * On the first pass we need to check if the current node + * has not been obscured. + */ + delegation = ISC_FALSE; + if (first) { + dns_fixedname_t ffound; + dns_name_t *found; + dns_fixedname_init(&ffound); + found = dns_fixedname_name(&ffound); + result = dns_db_find(db, name, version, + dns_rdatatype_soa, + DNS_DBFIND_NOWILD, 0, NULL, found, + NULL, NULL); + if ((result == DNS_R_DELEGATION || + result == DNS_R_DNAME) && + !dns_name_equal(name, found)) { + /* + * Remember the obscuring name so that + * we skip all obscured names. + */ + dns_name_copy(found, name, NULL); + delegation = ISC_TRUE; + goto next_node; + } + } + + /* + * Process one node. + */ + for (i = 0; i < nkeys; i++) { + /* + * Find the key we want to sign with. + */ + if (dst_key_alg(zone_keys[i]) != signing->algorithm || + dst_key_id(zone_keys[i]) != signing->keyid || + !dst_key_isprivate(zone_keys[i])) + continue; + /* + * Do we do KSK processing? + */ + if (check_ksk && + (dst_key_flags(zone_keys[i]) & DNS_KEYFLAG_KSK) != 0) + is_ksk = ISC_TRUE; + CHECK(sign_a_node(db, name, node, version, + zone_keys[i], inception, expire, + zone->minimum, is_ksk, &delegation, + &sig_diff, &signatures, zone->mctx)); + break; + } + /* + * Go onto next node. + */ + next_node: + dns_db_detachnode(db, &node); + do { + result = dns_dbiterator_next(signing->dbiterator); + if (result == ISC_R_NOMORE) { + ISC_LIST_UNLINK(zone->signing, signing, link); + ISC_LIST_APPEND(cleanup, signing, link); + finishedakey = ISC_TRUE; + if (!is_ksk && !secureupdated) { + /* + * We have finished regenerating the + * zone with a zone signing key. + * The NSEC chain is now complete and + * there is a full set of signatures + * for the zone. We can now clear the + * OPT bit from the NSEC record. + */ + result = updatesecure(db, version, + &zone->origin, + zone->minimum, + &secureupdated, + &sig_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, + ISC_LOG_ERROR, + "updatesecure -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + result = updatesignwithkey(signing, version, + &zone->origin, + &sig_diff); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "updatesignwithkey -> %s\n", + dns_result_totext(result)); + goto failure; + } + break; + } else if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:dns_dbiterator_next -> %s\n", + dns_result_totext(result)); + goto failure; + } else if (delegation) { + dns_dbiterator_current(signing->dbiterator, + &node, nextname); + dns_db_detachnode(db, &node); + if (!dns_name_issubdomain(nextname, name)) + break; + } else + break; + } while (1); + next_signing: + signing = nextsigning; + if (signing == NULL) { + first = ISC_FALSE; + signing = ISC_LIST_HEAD(zone->signing); + } + } + + if (secureupdated) { + /* + * We have changed the NSEC RRset above so we need to update + * the signatures. + */ + result = del_sigs(zone, db, version, &zone->origin, + dns_rdatatype_nsec, &sig_diff, zone_keys, + nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:del_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + result = add_sigs(db, version, &zone->origin, + dns_rdatatype_nsec, &sig_diff, zone_keys, + nkeys, zone->mctx, inception, soaexpire, + check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:add_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + if (finishedakey) { + /* + * We have changed the RRset above so we need to update + * the signatures. + */ + result = del_sigs(zone, db, version, &zone->origin, + (dns_rdatatype_t)0xffff, &sig_diff, + zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:del_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + result = add_sigs(db, version, &zone->origin, + (dns_rdatatype_t)0xffff, &sig_diff, + zone_keys, nkeys, zone->mctx, inception, + soaexpire, check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:add_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa, + &sig_diff, zone_keys, nkeys, now); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:del_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = increment_soa_serial(db, version, &sig_diff, zone->mctx); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:increment_soa_serial -> %s\n", + dns_result_totext(result)); + goto failure; + } + + /* + * Generate maximum life time signatures so that the above loop + * termination is sensible. + */ + result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa, + &sig_diff, zone_keys, nkeys, zone->mctx, inception, + soaexpire, check_ksk); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:add_sigs -> %s\n", + dns_result_totext(result)); + goto failure; + } + + journalfile = dns_zone_getjournal(zone); + if (journalfile != NULL) { + dns_journal_t *journal = NULL; + result = dns_journal_open(zone->mctx, journalfile, + ISC_TRUE, &journal); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:dns_journal_open -> %s\n", + dns_result_totext(result)); + goto failure; + } + + result = dns_journal_write_transaction(journal, &sig_diff); + dns_journal_destroy(&journal); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_ERROR, + "zone_sign:dns_journal_write_transaction -> %s\n", + dns_result_totext(result)); + goto failure; + } + } + + /* + * Everything has succeeded. Commit the changes. + */ + dns_db_closeversion(db, &version, ISC_TRUE); + + /* + * Everything succeeded so we can clean these up now. + */ + signing = ISC_LIST_HEAD(cleanup); + while (signing != NULL) { + ISC_LIST_UNLINK(cleanup, signing, link); + dns_db_detach(&signing->db); + dns_dbiterator_destroy(&signing->dbiterator); + isc_mem_put(zone->mctx, signing, sizeof *signing); + signing = ISC_LIST_HEAD(cleanup); + } + + set_resigntime(zone); + + failure: + /* + * Rollback the cleanup list. + */ + signing = ISC_LIST_HEAD(cleanup); + while (signing != NULL) { + ISC_LIST_UNLINK(cleanup, signing, link); + ISC_LIST_APPEND(zone->signing, signing, link); + dns_dbiterator_first(signing->dbiterator); + signing = ISC_LIST_HEAD(cleanup); + } + for (signing = ISC_LIST_HEAD(zone->signing); + signing != NULL; + signing = ISC_LIST_NEXT(signing, link)) + dns_dbiterator_pause(signing->dbiterator); + dns_diff_clear(&sig_diff); + for (i = 0; i < nkeys; i++) + dst_key_free(&zone_keys[i]); + if (version != NULL) { + dns_db_closeversion(db, &version, ISC_FALSE); + dns_db_detach(&db); + } else if (db != NULL) + dns_db_detach(&db); + if (ISC_LIST_HEAD(zone->signing) != NULL) { + isc_interval_t i; + if (zone->update_disabled) + isc_interval_set(&i, 60, 0); /* 1 minute */ + else + isc_interval_set(&i, 0, 10000000); /* 10 ms */ + isc_interval_set(&i, 120, 0); /* XXXMPA */ + isc_time_nowplusinterval(&zone->signingtime, &i); + } else { + fprintf(stderr, "sign_zone done\n"); + isc_time_settoepoch(&zone->signingtime); + } +} + static void zone_maintenance(dns_zone_t *zone) { const char me[] = "zone_maintenance"; @@ -3090,15 +4505,31 @@ zone_maintenance(dns_zone_t *zone) { break; } - /* - * Do we need to send out notify messages? - */ switch (zone->type) { case dns_zone_master: case dns_zone_slave: + /* + * Do we need to send out notify messages? + */ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && isc_time_compare(&now, &zone->notifytime) >= 0) zone_notify(zone, &now); + /* + * Do we need to sign/resign some RRsets? + */ + if (!isc_time_isepoch(&zone->signingtime) && + isc_time_compare(&now, &zone->signingtime) >= 0) + zone_sign(zone); + else if (!isc_time_isepoch(&zone->resigntime) && + isc_time_compare(&now, &zone->resigntime) >= 0) + zone_resigninc(zone); + /* + * Do we need to issue a key expiry warning. + */ + if (!isc_time_isepoch(&zone->keywarntime) && + isc_time_compare(&now, &zone->keywarntime) >= 0) + set_key_expiry_warning(zone, zone->key_expiry, + isc_time_seconds(&now)); break; default: break; @@ -3110,6 +4541,7 @@ void dns_zone_markdirty(dns_zone_t *zone) { LOCK_ZONE(zone); + set_resigntime(zone); /* XXXMPA make seperate call back */ zone_needdump(zone, DNS_DUMP_DELAY); UNLOCK_ZONE(zone); } @@ -5481,6 +6913,21 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { isc_time_compare(&zone->dumptime, &next) < 0) next = zone->dumptime; } + if (!isc_time_isepoch(&zone->resigntime)) { + if (isc_time_isepoch(&next) || + isc_time_compare(&zone->resigntime, &next) < 0) + next = zone->resigntime; + } + if (!isc_time_isepoch(&zone->keywarntime)) { + if (isc_time_isepoch(&next) || + isc_time_compare(&zone->keywarntime, &next) < 0) + next = zone->keywarntime; + } + if (!isc_time_isepoch(&zone->signingtime)) { + if (isc_time_isepoch(&next) || + isc_time_compare(&zone->signingtime, &next) < 0) + next = zone->signingtime; + } break; case dns_zone_slave: @@ -6931,6 +8378,7 @@ dns_zone_setsigvalidityinterval(dns_zone_t *zone, isc_uint32_t interval) { REQUIRE(DNS_ZONE_VALID(zone)); zone->sigvalidityinterval = interval; + zone->sigvalidityinterval = 3600; /* XXXMPA */ } isc_uint32_t @@ -8326,6 +9774,68 @@ dns_zone_getnotifydelay(dns_zone_t *zone) { return (zone->notifydelay); } +isc_result_t +dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid) { + isc_result_t result; + REQUIRE(DNS_ZONE_VALID(zone)); + + dns_zone_log(zone, ISC_LOG_NOTICE, + "dns_zone_signwithkey(algorithm=%u, keyid=%u)", + algorithm, keyid); + LOCK_ZONE(zone); + result = zone_signwithkey(zone, algorithm, keyid); + UNLOCK_ZONE(zone); + + return (result); +} + +static isc_result_t +zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, uint16_t keyid) { + dns_signing_t *signing; + isc_result_t result; + isc_time_t now; + + signing = isc_mem_get(zone->mctx, sizeof *signing); + if (signing == NULL) + return (ISC_R_NOMEMORY); + + signing->magic = 0; + signing->db = NULL; + signing->dbiterator = NULL; + signing->algorithm = algorithm; + signing->keyid = keyid; + + TIME_NOW(&now); + + if (zone->db != NULL) { + dns_db_attach(zone->db, &signing->db); + result = dns_db_createiterator(signing->db, ISC_FALSE, + &signing->dbiterator); + + if (result == ISC_R_SUCCESS) + result = dns_dbiterator_first(signing->dbiterator); + if (result == ISC_R_SUCCESS) { + dns_dbiterator_pause(signing->dbiterator); + ISC_LIST_INITANDAPPEND(zone->signing, signing, link); + signing = NULL; + if (isc_time_isepoch(&zone->signingtime)) { + zone->signingtime = now; + if (zone->task != NULL) + zone_settimer(zone, &now); + } + } + } else + result = ISC_R_NOTFOUND; + + if (signing != NULL) { + dns_db_detach(&signing->db); + if (signing->dbiterator != NULL) + dns_dbiterator_destroy(&signing->dbiterator); + isc_mem_put(zone->mctx, signing, sizeof *signing); + } + return (result); +} + #ifdef HAVE_LIBXML2 isc_result_t diff --git a/lib/isc/mem.c b/lib/isc/mem.c index fc40362aa1..bd4c068d74 100644 --- a/lib/isc/mem.c +++ b/lib/isc/mem.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: mem.c,v 1.143 2008/03/31 23:47:11 tbox Exp $ */ +/* $Id: mem.c,v 1.144 2008/04/01 01:37:25 marka Exp $ */ /*! \file */ @@ -112,7 +112,7 @@ typedef ISC_LIST(debuglink_t) debuglist_t; static ISC_LIST(isc_mem_t) contexts; static isc_once_t once = ISC_ONCE_INIT; -static isc_mutex_t lock; +static isc_mutex_t contextlock; /*% * Total size of lost memory due to a bug of external library. @@ -124,7 +124,7 @@ struct isc_mem { unsigned int magic; isc_ondestroy_t ondestroy; unsigned int flags; - isc_mutex_t lock; + isc_mutex_t memlock; isc_memalloc_t memalloc; isc_memfree_t memfree; void * arg; @@ -702,7 +702,7 @@ default_memfree(void *arg, void *ptr) { static void initialize_action(void) { - RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS); + RUNTIME_CHECK(isc_mutex_init(&contextlock) == ISC_R_SUCCESS); ISC_LIST_INIT(contexts); totallost = 0; } @@ -742,7 +742,7 @@ isc_mem_createx2(size_t init_max_size, size_t target_size, return (ISC_R_NOMEMORY); if ((flags & ISC_MEMFLAG_NOLOCK) == 0) { - result = isc_mutex_init(&ctx->lock); + result = isc_mutex_init(&ctx->memlock); if (result != ISC_R_SUCCESS) { (memfree)(arg, ctx); return (result); @@ -827,9 +827,9 @@ isc_mem_createx2(size_t init_max_size, size_t target_size, ctx->memalloc_failures = 0; - LOCK(&lock); + LOCK(&contextlock); ISC_LIST_INITANDAPPEND(contexts, ctx, link); - UNLOCK(&lock); + UNLOCK(&contextlock); *ctxp = ctx; return (ISC_R_SUCCESS); @@ -845,7 +845,7 @@ isc_mem_createx2(size_t init_max_size, size_t target_size, (ctx->memfree)(ctx->arg, ctx->debuglist); #endif /* ISC_MEM_TRACKLINES */ if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0) - DESTROYLOCK(&ctx->lock); + DESTROYLOCK(&ctx->memlock); (memfree)(arg, ctx); } @@ -877,10 +877,10 @@ destroy(isc_mem_t *ctx) { ctx->magic = 0; - LOCK(&lock); + LOCK(&contextlock); ISC_LIST_UNLINK(contexts, ctx, link); totallost += ctx->inuse; - UNLOCK(&lock); + UNLOCK(&contextlock); INSIST(ISC_LIST_EMPTY(ctx->pools)); @@ -932,7 +932,7 @@ destroy(isc_mem_t *ctx) { ondest = ctx->ondestroy; if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0) - DESTROYLOCK(&ctx->lock); + DESTROYLOCK(&ctx->memlock); (ctx->memfree)(ctx->arg, ctx); isc_ondestroy_notify(&ondest, ctx); @@ -943,9 +943,9 @@ isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) { REQUIRE(VALID_CONTEXT(source)); REQUIRE(targetp != NULL && *targetp == NULL); - MCTXLOCK(source, &source->lock); + MCTXLOCK(source, &source->memlock); source->references++; - MCTXUNLOCK(source, &source->lock); + MCTXUNLOCK(source, &source->memlock); *targetp = source; } @@ -959,12 +959,12 @@ isc_mem_detach(isc_mem_t **ctxp) { ctx = *ctxp; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); INSIST(ctx->references > 0); ctx->references--; if (ctx->references == 0) want_destroy = ISC_TRUE; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (want_destroy) destroy(ctx); @@ -1010,11 +1010,11 @@ isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { } isc__mem_free(ctx, ptr FLARG_PASS); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); ctx->references--; if (ctx->references == 0) want_destroy = ISC_TRUE; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (want_destroy) destroy(ctx); @@ -1022,11 +1022,11 @@ isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { } if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putunlocked(ctx, ptr, size); } else { mem_put(ctx, ptr, size); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putstats(ctx, ptr, size); } @@ -1036,7 +1036,7 @@ isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { if (ctx->references == 0) want_destroy = ISC_TRUE; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (want_destroy) destroy(ctx); @@ -1055,14 +1055,14 @@ isc_mem_destroy(isc_mem_t **ctxp) { ctx = *ctxp; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); #if ISC_MEM_TRACKLINES if (ctx->references != 1) print_active(ctx, stderr); #endif REQUIRE(ctx->references == 1); ctx->references--; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); destroy(ctx); @@ -1073,9 +1073,9 @@ isc_result_t isc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) { isc_result_t res; - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); res = isc_ondestroy_register(&ctx->ondestroy, task, event); - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); return (res); } @@ -1092,11 +1092,11 @@ isc__mem_get(isc_mem_t *ctx, size_t size FLARG) { return (isc__mem_allocate(ctx, size FLARG_PASS)); if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); ptr = mem_getunlocked(ctx, size); } else { ptr = mem_get(ctx, size); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); if (ptr != NULL) mem_getstats(ctx, size); } @@ -1113,7 +1113,7 @@ isc__mem_get(isc_mem_t *ctx, size_t size FLARG) { fprintf(stderr, "maxinuse = %lu\n", (unsigned long)ctx->inuse); } - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (call_water) (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); @@ -1144,11 +1144,11 @@ isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG) } if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putunlocked(ctx, ptr, size); } else { mem_put(ctx, ptr, size); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putstats(ctx, ptr, size); } @@ -1164,7 +1164,7 @@ isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG) if (ctx->water != NULL) call_water = ISC_TRUE; } - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (call_water) (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); @@ -1174,12 +1174,12 @@ void isc_mem_waterack(isc_mem_t *ctx, int flag) { REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); if (flag == ISC_MEM_LOWATER) ctx->hi_called = ISC_FALSE; else if (flag == ISC_MEM_HIWATER) ctx->hi_called = ISC_TRUE; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); } #if ISC_MEM_TRACKLINES @@ -1233,7 +1233,7 @@ isc_mem_stats(isc_mem_t *ctx, FILE *out) { const isc_mempool_t *pool; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); for (i = 0; i <= ctx->max_size; i++) { s = &ctx->stats[i]; @@ -1254,7 +1254,7 @@ isc_mem_stats(isc_mem_t *ctx, FILE *out) { * Note that since a pool can be locked now, these stats might be * somewhat off if the pool is in active use at the time the stats * are dumped. The link fields are protected by the isc_mem_t's - * lock, however, so walking this list and extracting integers from + * contextlock, however, so walking this list and extracting integers from * stats fields is always safe. */ pool = ISC_LIST_HEAD(ctx->pools); @@ -1294,7 +1294,7 @@ isc_mem_stats(isc_mem_t *ctx, FILE *out) { print_active(ctx, out); #endif - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); } /* @@ -1333,11 +1333,11 @@ isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) { REQUIRE(VALID_CONTEXT(ctx)); if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); si = isc__mem_allocateunlocked(ctx, size); } else { si = isc__mem_allocateunlocked(ctx, size); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); if (si != NULL) mem_getstats(ctx, si[-1].u.size); } @@ -1357,7 +1357,7 @@ isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) { fprintf(stderr, "maxinuse = %lu\n", (unsigned long)ctx->inuse); } - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (call_water) (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); @@ -1384,11 +1384,11 @@ isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) { } if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putunlocked(ctx, si, size); } else { mem_put(ctx, si, size); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); mem_putstats(ctx, si, size); } @@ -1406,7 +1406,7 @@ isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) { if (ctx->water != NULL) call_water = ISC_TRUE; } - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (call_water) (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); @@ -1438,11 +1438,11 @@ isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { void isc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) { REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); ctx->checkfree = flag; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); } /* @@ -1452,11 +1452,11 @@ isc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) { void isc_mem_setquota(isc_mem_t *ctx, size_t quota) { REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); ctx->quota = quota; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); } size_t @@ -1464,11 +1464,11 @@ isc_mem_getquota(isc_mem_t *ctx) { size_t quota; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); quota = ctx->quota; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); return (quota); } @@ -1478,11 +1478,11 @@ isc_mem_inuse(isc_mem_t *ctx) { size_t inuse; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); inuse = ctx->inuse; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); return (inuse); } @@ -1498,7 +1498,7 @@ isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg, REQUIRE(VALID_CONTEXT(ctx)); REQUIRE(hiwater >= lowater); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); oldwater = ctx->water; oldwater_arg = ctx->water_arg; if (water == NULL) { @@ -1519,7 +1519,7 @@ isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg, ctx->lo_water = lowater; ctx->hi_called = ISC_FALSE; } - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); if (callwater && oldwater != NULL) (oldwater)(oldwater_arg, ISC_MEM_LOWATER); @@ -1529,11 +1529,11 @@ void isc_mem_setname(isc_mem_t *ctx, const char *name, void *tag) { REQUIRE(VALID_CONTEXT(ctx)); - LOCK(&ctx->lock); + LOCK(&ctx->memlock); memset(ctx->name, 0, sizeof(ctx->name)); strncpy(ctx->name, name, sizeof(ctx->name) - 1); ctx->tag = tag; - UNLOCK(&ctx->lock); + UNLOCK(&ctx->memlock); } const char * @@ -1587,10 +1587,10 @@ isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) { *mpctxp = mpctx; - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link); mctx->poolcnt++; - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); return (ISC_R_SUCCESS); } @@ -1643,7 +1643,7 @@ isc_mempool_destroy(isc_mempool_t **mpctxp) { /* * Return any items on the free list */ - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); while (mpctx->items != NULL) { INSIST(mpctx->freecount > 0); mpctx->freecount--; @@ -1657,15 +1657,15 @@ isc_mempool_destroy(isc_mempool_t **mpctxp) { mem_putstats(mctx, item, mpctx->size); } } - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); /* * Remove our linked list entry from the memory context. */ - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); ISC_LIST_UNLINK(mctx->pools, mpctx, link); mctx->poolcnt--; - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); mpctx->magic = 0; @@ -1724,7 +1724,7 @@ isc__mempool_get(isc_mempool_t *mpctx FLARG) { * We need to dip into the well. Lock the memory context here and * fill up our free list. */ - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); for (i = 0; i < mpctx->fillcount; i++) { if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { item = mem_getunlocked(mctx, mpctx->size); @@ -1739,7 +1739,7 @@ isc__mempool_get(isc_mempool_t *mpctx FLARG) { mpctx->items = item; mpctx->freecount++; } - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); /* * If we didn't get any items, return NULL. @@ -1759,9 +1759,9 @@ isc__mempool_get(isc_mempool_t *mpctx FLARG) { #if ISC_MEM_TRACKLINES if (item != NULL) { - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); ADD_TRACE(mctx, item, mpctx->size, file, line); - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); } #endif /* ISC_MEM_TRACKLINES */ @@ -1785,9 +1785,9 @@ isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) { mpctx->allocated--; #if ISC_MEM_TRACKLINES - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); DELETE_TRACE(mctx, mem, mpctx->size, file, line); - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); #endif /* ISC_MEM_TRACKLINES */ /* @@ -1795,14 +1795,14 @@ isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) { */ if (mpctx->freecount >= mpctx->freemax) { if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); mem_putunlocked(mctx, mem, mpctx->size); - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); } else { mem_put(mctx, mem, mpctx->size); - MCTXLOCK(mctx, &mctx->lock); + MCTXLOCK(mctx, &mctx->memlock); mem_putstats(mctx, mem, mpctx->size); - MCTXUNLOCK(mctx, &mctx->lock); + MCTXUNLOCK(mctx, &mctx->memlock); } if (mpctx->lock != NULL) UNLOCK(mpctx->lock); @@ -1975,14 +1975,14 @@ isc_mem_printallactive(FILE *file) { RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); - LOCK(&lock); + LOCK(&contextlock); for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; ctx = ISC_LIST_NEXT(ctx, link)) { fprintf(file, "context: %p\n", ctx); print_active(ctx, file); } - UNLOCK(&lock); + UNLOCK(&contextlock); #endif } @@ -1991,7 +1991,7 @@ isc_mem_checkdestroyed(FILE *file) { RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); - LOCK(&lock); + LOCK(&contextlock); if (!ISC_LIST_EMPTY(contexts)) { #if ISC_MEM_TRACKLINES isc_mem_t *ctx; @@ -2006,7 +2006,7 @@ isc_mem_checkdestroyed(FILE *file) { #endif INSIST(0); } - UNLOCK(&lock); + UNLOCK(&contextlock); } unsigned int @@ -2014,9 +2014,9 @@ isc_mem_references(isc_mem_t *ctx) { unsigned int references; REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); references = ctx->references; - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); return (references); } @@ -2047,7 +2047,7 @@ renderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) { } REQUIRE(VALID_CONTEXT(ctx)); - MCTXLOCK(ctx, &ctx->lock); + MCTXLOCK(ctx, &ctx->memlock); summary->contextsize += sizeof(*ctx) + (ctx->max_size + 1) * sizeof(struct stats) + @@ -2110,7 +2110,7 @@ renderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) { (isc_uint64_t)ctx->lo_water); xmlTextWriterEndElement(writer); /* lowater */ - MCTXUNLOCK(ctx, &ctx->lock); + MCTXUNLOCK(ctx, &ctx->memlock); xmlTextWriterEndElement(writer); /* context */ } @@ -2127,14 +2127,14 @@ isc_mem_renderxml(xmlTextWriterPtr writer) { RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); - LOCK(&lock); + LOCK(&contextlock); lost = totallost; for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; ctx = ISC_LIST_NEXT(ctx, link)) { renderctx(ctx, &summary, writer); } - UNLOCK(&lock); + UNLOCK(&contextlock); xmlTextWriterEndElement(writer); /* contexts */