📄 update.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: update.c,v 1.88.2.5.2.23 2004/07/23 02:56:52 marka Exp $ */#include <config.h>#include <isc/print.h>#include <isc/string.h>#include <isc/taskpool.h>#include <isc/util.h>#include <dns/db.h>#include <dns/dbiterator.h>#include <dns/diff.h>#include <dns/dnssec.h>#include <dns/events.h>#include <dns/fixedname.h>#include <dns/journal.h>#include <dns/message.h>#include <dns/nsec.h>#include <dns/rdataclass.h>#include <dns/rdataset.h>#include <dns/rdatasetiter.h>#include <dns/rdatatype.h>#include <dns/soa.h>#include <dns/ssu.h>#include <dns/view.h>#include <dns/zone.h>#include <dns/zt.h>#include <named/client.h>#include <named/log.h>#include <named/update.h>/* * This module implements dynamic update as in RFC2136. *//* XXX TODO: - document strict minimality*//**************************************************************************//* * Log level for tracing dynamic update protocol requests. */#define LOGLEVEL_PROTOCOL ISC_LOG_INFO/* * Log level for low-level debug tracing. */#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)/* * Check an operation for failure. These macros all assume that * the function using them has a 'result' variable and a 'failure' * label. */#define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)/* * Fail unconditionally with result 'code', which must not * be ISC_R_SUCCESS. The reason for failure presumably has * been logged already. * * The test against ISC_R_SUCCESS is there to keep the Solaris compiler * from complaining about "end-of-loop code not reached". */#define FAIL(code) \ do { \ result = (code); \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)/* * Fail unconditionally and log as a client error. * The test against ISC_R_SUCCESS is there to keep the Solaris compiler * from complaining about "end-of-loop code not reached". */#define FAILC(code, msg) \ do { \ const char *_what = "failed"; \ result = (code); \ switch (result) { \ case DNS_R_NXDOMAIN: \ case DNS_R_YXDOMAIN: \ case DNS_R_YXRRSET: \ case DNS_R_NXRRSET: \ _what = "unsuccessful"; \ } \ update_log(client, zone, LOGLEVEL_PROTOCOL, \ "update %s: %s (%s)", _what, \ msg, isc_result_totext(result)); \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)#define FAILN(code, name, msg) \ do { \ const char *_what = "failed"; \ result = (code); \ switch (result) { \ case DNS_R_NXDOMAIN: \ case DNS_R_YXDOMAIN: \ case DNS_R_YXRRSET: \ case DNS_R_NXRRSET: \ _what = "unsuccessful"; \ } \ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ char _nbuf[DNS_NAME_FORMATSIZE]; \ dns_name_format(name, _nbuf, sizeof(_nbuf)); \ update_log(client, zone, LOGLEVEL_PROTOCOL, \ "update %s: %s: %s (%s)", _what, _nbuf, \ msg, isc_result_totext(result)); \ } \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)#define FAILNT(code, name, type, msg) \ do { \ const char *_what = "failed"; \ result = (code); \ switch (result) { \ case DNS_R_NXDOMAIN: \ case DNS_R_YXDOMAIN: \ case DNS_R_YXRRSET: \ case DNS_R_NXRRSET: \ _what = "unsuccessful"; \ } \ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \ char _nbuf[DNS_NAME_FORMATSIZE]; \ char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ dns_name_format(name, _nbuf, sizeof(_nbuf)); \ dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ update_log(client, zone, LOGLEVEL_PROTOCOL, \ "update %s: %s/%s: %s (%s)", \ _what, _nbuf, _tbuf, msg, \ isc_result_totext(result)); \ } \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)/* * Fail unconditionally and log as a server error. * The test against ISC_R_SUCCESS is there to keep the Solaris compiler * from complaining about "end-of-loop code not reached". */#define FAILS(code, msg) \ do { \ result = (code); \ update_log(client, zone, LOGLEVEL_PROTOCOL, \ "error: %s: %s", \ msg, isc_result_totext(result)); \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)/**************************************************************************/typedef struct rr rr_t;struct rr { /* dns_name_t name; */ isc_uint32_t ttl; dns_rdata_t rdata;};typedef struct update_event update_event_t;struct update_event { ISC_EVENT_COMMON(update_event_t); dns_zone_t *zone; isc_result_t result; dns_message_t *answer;};/**************************************************************************//* * Forward declarations. */static void update_action(isc_task_t *task, isc_event_t *event);static void updatedone_action(isc_task_t *task, isc_event_t *event);static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);static void forward_done(isc_task_t *task, isc_event_t *event);/**************************************************************************/static voidupdate_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);static voidupdate_log(ns_client_t *client, dns_zone_t *zone, int level, const char *fmt, ...){ va_list ap; char message[4096]; char namebuf[DNS_NAME_FORMATSIZE]; char classbuf[DNS_RDATACLASS_FORMATSIZE]; if (client == NULL || zone == NULL) return; if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE) return; dns_name_format(dns_zone_getorigin(zone), namebuf, sizeof(namebuf)); dns_rdataclass_format(dns_zone_getclass(zone), classbuf, sizeof(classbuf)); va_start(ap, fmt); vsnprintf(message, sizeof(message), fmt, ap); va_end(ap); ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE, level, "updating zone '%s/%s': %s", namebuf, classbuf, message);}static isc_result_tcheckupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message, dns_name_t *zonename, isc_boolean_t slave){ char namebuf[DNS_NAME_FORMATSIZE]; char classbuf[DNS_RDATACLASS_FORMATSIZE]; int level = ISC_LOG_ERROR; const char *msg = "denied"; isc_result_t result; if (slave && acl == NULL) { result = DNS_R_NOTIMP; level = ISC_LOG_DEBUG(3); msg = "disabled"; } else result = ns_client_checkaclsilent(client, acl, ISC_FALSE); if (result == ISC_R_SUCCESS) { level = ISC_LOG_DEBUG(3); msg = "approved"; } dns_name_format(zonename, namebuf, sizeof(namebuf)); dns_rdataclass_format(client->view->rdclass, classbuf, sizeof(classbuf)); ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY, NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s", message, namebuf, classbuf, msg); return (result);}/* * Update a single RR in version 'ver' of 'db' and log the * update in 'diff'. * * Ensures: * '*tuple' == NULL. Either the tuple is freed, or its * ownership has been transferred to the diff. */static isc_result_tdo_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);}/* * Perform the updates in 'updates' in version 'ver' of 'db' and log the * update in 'diff'. * * Ensures: * 'updates' is empty. */static isc_result_tdo_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff){ isc_result_t result; while (! ISC_LIST_EMPTY(updates->tuples)) { dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples); ISC_LIST_UNLINK(updates->tuples, t, link); CHECK(do_one_tuple(&t, db, ver, diff)); } return (ISC_R_SUCCESS); failure: dns_diff_clear(diff); return (result);}static isc_result_tupdate_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));}/**************************************************************************//* * Callback-style iteration over rdatasets and rdatas. * * foreach_rrset() can be used to iterate over the RRsets * of a name and call a callback function with each * one. Similarly, foreach_rr() can be used to iterate * over the individual RRs at name, optionally restricted * to RRs of a given type. * * The callback functions are called "actions" and take * two arguments: a void pointer for passing arbitrary * context information, and a pointer to the current RRset * or RR. By convention, their names end in "_action". *//* * XXXRTH We might want to make this public somewhere in libdns. *//* * Function type for foreach_rrset() iterator actions. */typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);/* * Function type for foreach_rr() iterator actions. */typedef isc_result_t rr_func(void *data, rr_t *rr);/* * Internal context struct for foreach_node_rr(). */typedef struct { rr_func * rr_action; void * rr_action_data;} foreach_node_rr_ctx_t;/* * Internal helper function for foreach_node_rr(). */static isc_result_tforeach_node_rr_action(void *data, dns_rdataset_t *rdataset) { isc_result_t result; foreach_node_rr_ctx_t *ctx = data; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { rr_t rr = { 0, DNS_RDATA_INIT }; dns_rdataset_current(rdataset, &rr.rdata); rr.ttl = rdataset->ttl; result = (*ctx->rr_action)(ctx->rr_action_data, &rr); if (result != ISC_R_SUCCESS) return (result); } if (result != ISC_R_NOMORE) return (result); return (ISC_R_SUCCESS);}/* * For each rdataset of 'name' in 'ver' of 'db', call 'action' * with the rdataset and 'action_data' as arguments. If the name * does not exist, do nothing. * * If 'action' returns an error, abort iteration and return the error. */static isc_result_tforeach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, rrset_func *action, void *action_data){ isc_result_t result; dns_dbnode_t *node; dns_rdatasetiter_t *iter; node = NULL; result = dns_db_findnode(db, name, ISC_FALSE, &node); if (result == ISC_R_NOTFOUND) return (ISC_R_SUCCESS); if (result != ISC_R_SUCCESS) return (result); iter = NULL; result = dns_db_allrdatasets(db, node, ver, (isc_stdtime_t) 0, &iter); if (result != ISC_R_SUCCESS) goto cleanup_node; for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(iter)) { dns_rdataset_t rdataset; dns_rdataset_init(&rdataset); dns_rdatasetiter_current(iter, &rdataset); result = (*action)(action_data, &rdataset); dns_rdataset_disassociate(&rdataset); if (result != ISC_R_SUCCESS) goto cleanup_iterator; } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; cleanup_iterator: dns_rdatasetiter_destroy(&iter); cleanup_node: dns_db_detachnode(db, &node); return (result);}/* * For each RR of 'name' in 'ver' of 'db', call 'action' * with the RR and 'action_data' as arguments. If the name * does not exist, do nothing. * * If 'action' returns an error, abort iteration * and return the error.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -