📄 update.c
字号:
"update zone section empty"); /* * The zone section must contain exactly one "question", and * it must be of type SOA. */ zonename = NULL; dns_message_currentname(request, DNS_SECTION_ZONE, &zonename); zone_rdataset = ISC_LIST_HEAD(zonename->list); if (zone_rdataset->type != dns_rdatatype_soa) FAILC(DNS_R_FORMERR, "update zone section contains non-SOA"); if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) FAILC(DNS_R_FORMERR, "update zone section contains multiple RRs"); /* The zone section must have exactly one name. */ result = dns_message_nextname(request, DNS_SECTION_ZONE); if (result != ISC_R_NOMORE) FAILC(DNS_R_FORMERR, "update zone section contains multiple RRs"); result = dns_zt_find(client->view->zonetable, zonename, 0, NULL, &zone); if (result != ISC_R_SUCCESS) FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); switch(dns_zone_gettype(zone)) { case dns_zone_master: /* * We can now fail due to a bad signature as we now know * that we are the master. */ if (sigresult != ISC_R_SUCCESS) FAIL(sigresult); CHECK(send_update_event(client, zone)); break; case dns_zone_slave: CHECK(ns_client_checkacl(client, "update forwarding", dns_zone_getforwardacl(zone), ISC_FALSE, ISC_LOG_ERROR)); CHECK(send_forward_event(client, zone)); break; default: FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); } return; failure: /* * We failed without having sent an update event to the zone. * We are still in the client task context, so we can * simply give an error response without switching tasks. */ respond(client, result); if (zone != NULL) dns_zone_detach(&zone);}static voidupdate_action(isc_task_t *task, isc_event_t *event) { update_event_t *uev = (update_event_t *) event; dns_zone_t *zone = uev->zone; ns_client_t *client = (ns_client_t *)event->ev_arg; isc_result_t result; dns_db_t *db = NULL; dns_dbversion_t *oldver = NULL; dns_dbversion_t *ver = NULL; dns_diff_t diff; /* Pending updates. */ dns_diff_t temp; /* Pending RR existence assertions. */ isc_boolean_t soa_serial_changed = ISC_FALSE; isc_mem_t *mctx = client->mctx; dns_rdatatype_t covers; dns_message_t *request = client->message; dns_rdataclass_t zoneclass; dns_name_t *zonename; dns_ssutable_t *ssutable = NULL; INSIST(event->ev_type == DNS_EVENT_UPDATE); dns_diff_init(mctx, &diff); dns_diff_init(mctx, &temp); CHECK(dns_zone_getdb(zone, &db)); zonename = dns_db_origin(db); zoneclass = dns_db_class(db); dns_zone_getssutable(zone, &ssutable); dns_db_currentversion(db, &oldver); CHECK(dns_db_newversion(db, &ver)); /* * Check prerequisites. */ for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE)) { dns_name_t *name = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; dns_ttl_t ttl; dns_rdataclass_t update_class; isc_boolean_t flag; get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass, &name, &rdata, &covers, &ttl, &update_class); if (ttl != 0) FAILC(DNS_R_FORMERR, "prerequisite TTL is not zero"); if (! dns_name_issubdomain(name, zonename)) FAILC(DNS_R_NOTZONE, "prerequisite name is out of zone"); if (update_class == dns_rdataclass_any) { if (rdata.length != 0) FAILC(DNS_R_FORMERR, "class ANY prerequisite " "RDATA is not empty"); if (rdata.type == dns_rdatatype_any) { CHECK(name_exists(db, ver, name, &flag)); if (! flag) { FAILC(DNS_R_NXDOMAIN, "'name in use' prerequisite " "not satisfied"); } } else { CHECK(rrset_exists(db, ver, name, rdata.type, covers, &flag)); if (! flag) { /* RRset does not exist. */ FAILC(DNS_R_NXRRSET, "'rrset exists (value independent)' " "prerequisite not satisfied"); } } } else if (update_class == dns_rdataclass_none) { if (rdata.length != 0) FAILC(DNS_R_FORMERR, "class NONE prerequisite " "RDATA is not empty"); if (rdata.type == dns_rdatatype_any) { CHECK(name_exists(db, ver, name, &flag)); if (flag) { FAILC(DNS_R_YXDOMAIN, "'name not in use' prerequisite " "not satisfied"); } } else { CHECK(rrset_exists(db, ver, name, rdata.type, covers, &flag)); if (flag) { /* RRset exists. */ FAILC(DNS_R_YXRRSET, "'rrset does not exist' " "prerequisite not satisfied"); } } } else if (update_class == zoneclass) { /* "temp<rr.name, rr.type> += rr;" */ result = temp_append(&temp, name, &rdata); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "temp entry creation failed: %s", dns_result_totext(result)); FAIL(ISC_R_UNEXPECTED); } } else { FAILC(DNS_R_FORMERR, "malformed prerequisite"); } } if (result != ISC_R_NOMORE) FAIL(result); /* * Perform the final check of the "rrset exists (value dependent)" * prerequisites. */ result = temp_check(mctx, &temp, db, ver); if (result != ISC_R_SUCCESS) FAILC(result, "'RRset exists (value dependent)' " "prerequisite not satisfied"); update_log(client, zone, LOGLEVEL_DEBUG, "prerequisites are OK"); /* * Check Requestor's Permissions. It seems a bit silly to do this * only after prerequisite testing, but that is what RFC2136 says. */ if (ssutable == NULL) { char msg[DNS_RDATACLASS_FORMATSIZE + DNS_NAME_FORMATSIZE + sizeof("update '/'")]; ns_client_aclmsg("update", zonename, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, msg, dns_zone_getupdateacl(zone), ISC_FALSE, ISC_LOG_ERROR)); } else if (client->signer == NULL) { /* This gets us a free log message. */ char msg[DNS_RDATACLASS_FORMATSIZE + DNS_NAME_FORMATSIZE + sizeof("update '/'")]; ns_client_aclmsg("update", zonename, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, msg, NULL, ISC_FALSE, ISC_LOG_ERROR)); } /* * Perform the Update Section Prescan. */ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_UPDATE)) { dns_name_t *name = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; dns_ttl_t ttl; dns_rdataclass_t update_class; get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name, &rdata, &covers, &ttl, &update_class); if (! dns_name_issubdomain(name, zonename)) FAILC(DNS_R_NOTZONE, "update RR is outside zone"); if (update_class == zoneclass) { /* * Check for meta-RRs. The RFC2136 pseudocode says * check for ANY|AXFR|MAILA|MAILB, but the text adds * "or any other QUERY metatype" */ if (dns_rdatatype_ismeta(rdata.type)) { FAILC(DNS_R_FORMERR, "meta-RR in update"); } } else if (update_class == dns_rdataclass_any) { if (ttl != 0 || rdata.length != 0 || (dns_rdatatype_ismeta(rdata.type) && rdata.type != dns_rdatatype_any)) FAILC(DNS_R_FORMERR, "meta-RR in update"); } else if (update_class == dns_rdataclass_none) { if (ttl != 0 || dns_rdatatype_ismeta(rdata.type)) FAILC(DNS_R_FORMERR, "meta-RR in update"); } else { update_log(client, zone, ISC_LOG_WARNING, "update RR has incorrect class %d", update_class); FAIL(DNS_R_FORMERR); } /* * draft-ietf-dnsind-simple-secure-update-01 says * "Unlike traditional dynamic update, the client * is forbidden from updating NXT records." */ if (dns_db_issecure(db)) { if (rdata.type == dns_rdatatype_nxt) { FAILC(DNS_R_REFUSED, "explicit NXT updates are not allowed " "in secure zones"); } else if (rdata.type == dns_rdatatype_sig) { FAILC(DNS_R_REFUSED, "explicit SIG updates are currently not " "supported in secure zones"); } } if (ssutable != NULL && client->signer != NULL) { if (rdata.type != dns_rdatatype_any) { if (!dns_ssutable_checkrules(ssutable, client->signer, name, rdata.type)) FAILC(DNS_R_REFUSED, "rejected by secure update"); } else { if (!ssu_checkall(db, ver, name, ssutable, client->signer)) FAILC(DNS_R_REFUSED, "rejected by secure update"); } } } if (result != ISC_R_NOMORE) FAIL(result); update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK"); /* * Process the Update Section. */ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_UPDATE)) { dns_name_t *name = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; dns_ttl_t ttl; dns_rdataclass_t update_class; isc_boolean_t flag; get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name, &rdata, &covers, &ttl, &update_class); if (update_class == zoneclass) { if (rdata.type == dns_rdatatype_cname) { CHECK(cname_incompatible_rrset_exists(db, ver, name, &flag)); if (flag) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to add CNAME " "alongside non-CNAME " "ignored"); continue; } } else { CHECK(rrset_exists(db, ver, name, dns_rdatatype_cname, 0, &flag)); if (flag && ! dns_rdatatype_isdnssec(rdata.type)) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to add non-CNAME " "alongside CNAME ignored"); continue; } } if (rdata.type == dns_rdatatype_soa) { isc_boolean_t ok; CHECK(rrset_exists(db, ver, name, dns_rdatatype_soa, 0, &flag)); if (! flag) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to create 2nd " "SOA ignored"); continue; } CHECK(check_soa_increment(db, ver, &rdata, &ok)); if (! ok) { update_log(client, zone, LOGLEVEL_PROTOCOL, "SOA update failed to " "increment serial, " "ignoring it"); continue; } soa_serial_changed = ISC_TRUE; } update_log(client, zone, LOGLEVEL_PROTOCOL, "adding an RR"); /* Prepare the affected RRset for the addition. */ { add_rr_prepare_ctx_t ctx; ctx.db = db; ctx.ver = ver; ctx.diff = &diff; ctx.name = name; ctx.update_rr = &rdata; ctx.update_rr_ttl = ttl; ctx.ignore_add = ISC_FALSE; dns_diff_init(mctx, &ctx.del_diff); dns_diff_init(mctx, &ctx.add_diff); CHECK(foreach_rr(db, ver, name, rdata.type, covers, add_rr_prepare_action, &ctx)); if (ctx.ignore_add) { dns_diff_clear(&ctx.del_diff); dns_diff_clear(&ctx.add_diff); } else { CHECK(do_diff(&ctx.del_diff, db, ver, &diff)); CHECK(do_diff(&ctx.add_diff, db, ver, &diff)); CHECK(update_one_rr(db, ver, &diff, DNS_DIFFOP_ADD, name, ttl, &rdata)); } } } else if (update_class == dns_rdataclass_any) { if (rdata.type == dns_rdatatype_any) { update_log(client, zone, LOGLEVEL_PROTOCOL, "delete all rrsets from a name"); if (dns_name_equal(name, zonename)) { CHECK(delete_if(type_not_soa_nor_ns_p, db, ver, name, dns_rdatatype_any, 0, &rdata, &diff)); } else { CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_any, 0, &rdata, &diff)); } } else if (dns_name_equal(name, zonename) && (rdata.type == dns_rdatatype_soa || rdata.type == dns_rdatatype_ns)) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to delete all SOA " "or NS records ignored"); continue; } else { update_log(client, zone, LOGLEVEL_PROTOCOL, "deleting an rrset"); CHECK(delete_if(true_p, db, ver, name, rdata.type, covers, &rdata, &diff)); } } else if (update_class == dns_rdataclass_none) { /* * The (name == zonename) condition appears in * RFC2136 3.4.2.4 but is missing from the pseudocode. */ if (dns_name_equal(name, zonename)) { if (rdata.type == dns_rdatatype_soa) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to delete SOA " "ignored"); continue; } if (rdata.type == dns_rdatatype_ns) { int count; CHECK(rr_count(db, ver, name, dns_rdatatype_ns, 0, &count)); if (count == 1) { update_log(client, zone, LOGLEVEL_PROTOCOL, "attempt to " "delete last " "NS ignored"); continue; } } } update_log(client, zone, LOGLEVEL_PROTOCOL, "deleting an RR"); CHECK(delete_if(rr_equal_p, db, ver, name, rdata.type, covers, &rdata, &diff)); } } if (result != ISC_R_NOMORE) FAIL(result); /* * If any changes were made, increment the SOA serial number, * update SIGs and NXTs (if zone is secure), and write the update * to the journal. */ if (! ISC_LIST_EMPTY(diff.tuples)) { char *journalfile; dns_journal_t *journal; /* * Increment the SOA serial, but only if it was not * changed as a result of an update operation. */ if (! soa_serial_changed) { CHECK(increment_soa_serial(db, ver, &diff, mctx)); } if (dns_db_issecure(db)) { result = update_signatures(mctx, db, o
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -