📄 xfrin.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: xfrin.c,v 1.124.2.4.2.7 2004/03/08 09:04:33 marka Exp $ */#include <config.h>#include <isc/mem.h>#include <isc/print.h>#include <isc/random.h>#include <isc/string.h> /* Required for HP/UX (and others?) */#include <isc/task.h>#include <isc/timer.h>#include <isc/util.h>#include <dns/db.h>#include <dns/diff.h>#include <dns/events.h>#include <dns/journal.h>#include <dns/log.h>#include <dns/message.h>#include <dns/rdataclass.h>#include <dns/rdatalist.h>#include <dns/rdataset.h>#include <dns/result.h>#include <dns/soa.h>#include <dns/tcpmsg.h>#include <dns/timer.h>#include <dns/tsig.h>#include <dns/view.h>#include <dns/xfrin.h>#include <dns/zone.h>#include <dst/dst.h>/* * Incoming AXFR and IXFR. *//* * It would be non-sensical (or at least obtuse) to use FAIL() with an * ISC_R_SUCCESS code, but the test 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)#define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto failure; \ } while (0)/* * The states of the *XFR state machine. We handle both IXFR and AXFR * with a single integrated state machine because they cannot be distinguished * immediately - an AXFR response to an IXFR request can only be detected * when the first two (2) response RRs have already been received. */typedef enum { XFRST_INITIALSOA, XFRST_FIRSTDATA, XFRST_IXFR_DELSOA, XFRST_IXFR_DEL, XFRST_IXFR_ADDSOA, XFRST_IXFR_ADD, XFRST_AXFR, XFRST_END} xfrin_state_t;/* * Incoming zone transfer context. */struct dns_xfrin_ctx { unsigned int magic; isc_mem_t *mctx; dns_zone_t *zone; int refcount; isc_task_t *task; isc_timer_t *timer; isc_socketmgr_t *socketmgr; int connects; /* Connect in progress */ int sends; /* Send in progress */ int recvs; /* Receive in progress */ isc_boolean_t shuttingdown; dns_name_t name; /* Name of zone to transfer */ dns_rdataclass_t rdclass; isc_boolean_t checkid; dns_messageid_t id; /* * Requested transfer type (dns_rdatatype_axfr or * dns_rdatatype_ixfr). The actual transfer type * may differ due to IXFR->AXFR fallback. */ dns_rdatatype_t reqtype; isc_sockaddr_t masteraddr; isc_sockaddr_t sourceaddr; isc_socket_t *socket; /* Buffer for IXFR/AXFR request message */ isc_buffer_t qbuffer; unsigned char qbuffer_data[512]; /* Incoming reply TCP message */ dns_tcpmsg_t tcpmsg; isc_boolean_t tcpmsg_valid; dns_db_t *db; dns_dbversion_t *ver; dns_diff_t diff; /* Pending database changes */ int difflen; /* Number of pending tuples */ xfrin_state_t state; isc_uint32_t end_serial; isc_boolean_t is_ixfr; unsigned int nmsg; /* Number of messages recvd */ dns_tsigkey_t *tsigkey; /* Key used to create TSIG */ isc_buffer_t *lasttsig; /* The last TSIG */ dst_context_t *tsigctx; /* TSIG verification context */ unsigned int sincetsig; /* recvd since the last TSIG */ dns_xfrindone_t done; /* * AXFR- and IXFR-specific data. Only one is used at a time * according to the is_ixfr flag, so this could be a union, * but keeping them separate makes it a bit simpler to clean * things up when destroying the context. */ struct { dns_addrdatasetfunc_t add_func; dns_dbload_t *add_private; } axfr; struct { isc_uint32_t request_serial; isc_uint32_t current_serial; dns_journal_t *journal; } ixfr;};#define XFRIN_MAGIC ISC_MAGIC('X', 'f', 'r', 'I')#define VALID_XFRIN(x) ISC_MAGIC_VALID(x, XFRIN_MAGIC)/**************************************************************************//* * Forward declarations. */static isc_result_txfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_task_t *task, isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr, dns_name_t *zonename, dns_rdataclass_t rdclass, dns_rdatatype_t reqtype, isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, dns_xfrin_ctx_t **xfrp);static isc_result_t axfr_init(dns_xfrin_ctx_t *xfr);static isc_result_t axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp);static isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata);static isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr);static isc_result_t axfr_commit(dns_xfrin_ctx_t *xfr);static isc_result_t ixfr_init(dns_xfrin_ctx_t *xfr);static isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr);static isc_result_t ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata);static isc_result_t ixfr_commit(dns_xfrin_ctx_t *xfr);static isc_result_t xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl, dns_rdata_t *rdata);static isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr);static void xfrin_connect_done(isc_task_t *task, isc_event_t *event);static isc_result_t xfrin_send_request(dns_xfrin_ctx_t *xfr);static void xfrin_send_done(isc_task_t *task, isc_event_t *event);static void xfrin_sendlen_done(isc_task_t *task, isc_event_t *event);static void xfrin_recv_done(isc_task_t *task, isc_event_t *event);static void xfrin_timeout(isc_task_t *task, isc_event_t *event);static void maybe_free(dns_xfrin_ctx_t *xfr);static voidxfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg);static isc_result_trender(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf);static voidxfrin_logv(int level, dns_name_t *zonename, dns_rdataclass_t rdclass, isc_sockaddr_t *masteraddr, const char *fmt, va_list ap) ISC_FORMAT_PRINTF(5, 0);static voidxfrin_log1(int level, dns_name_t *zonename, dns_rdataclass_t rdclass, isc_sockaddr_t *masteraddr, const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);static voidxfrin_log(dns_xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);/**************************************************************************//* * AXFR handling */static isc_result_taxfr_init(dns_xfrin_ctx_t *xfr) { isc_result_t result; xfr->is_ixfr = ISC_FALSE; if (xfr->db != NULL) dns_db_detach(&xfr->db); CHECK(axfr_makedb(xfr, &xfr->db)); CHECK(dns_db_beginload(xfr->db, &xfr->axfr.add_func, &xfr->axfr.add_private)); result = ISC_R_SUCCESS; failure: return (result);}static isc_result_taxfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) { return (dns_db_create(xfr->mctx, /* XXX */ "rbt", /* XXX guess */ &xfr->name, dns_dbtype_zone, xfr->rdclass, 0, NULL, /* XXX guess */ dbp));}static isc_result_taxfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata){ isc_result_t result; dns_difftuple_t *tuple = NULL; CHECK(dns_zone_checknames(xfr->zone, name, rdata)); CHECK(dns_difftuple_create(xfr->diff.mctx, op, name, ttl, rdata, &tuple)); dns_diff_append(&xfr->diff, &tuple); if (++xfr->difflen > 100) CHECK(axfr_apply(xfr)); result = ISC_R_SUCCESS; failure: return (result);}/* * Store a set of AXFR RRs in the database. */static isc_result_taxfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add_func, xfr->axfr.add_private)); xfr->difflen = 0; dns_diff_clear(&xfr->diff); result = ISC_R_SUCCESS; failure: return (result);}static isc_result_taxfr_commit(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(axfr_apply(xfr)); CHECK(dns_db_endload(xfr->db, &xfr->axfr.add_private)); CHECK(dns_zone_replacedb(xfr->zone, xfr->db, ISC_TRUE)); result = ISC_R_SUCCESS; failure: return (result);}/**************************************************************************//* * IXFR handling */static isc_result_tixfr_init(dns_xfrin_ctx_t *xfr) { isc_result_t result; char *journalfile; if (xfr->reqtype != dns_rdatatype_ixfr) { xfrin_log(xfr, ISC_LOG_ERROR, "got incremental response to AXFR request"); return (DNS_R_FORMERR); } xfr->is_ixfr = ISC_TRUE; INSIST(xfr->db != NULL); xfr->difflen = 0; journalfile = dns_zone_getjournal(xfr->zone); if (journalfile != NULL) CHECK(dns_journal_open(xfr->mctx, journalfile, ISC_TRUE, &xfr->ixfr.journal)); result = ISC_R_SUCCESS; failure: return (result);}static isc_result_tixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata){ isc_result_t result; dns_difftuple_t *tuple = NULL; if (op == DNS_DIFFOP_ADD) CHECK(dns_zone_checknames(xfr->zone, name, rdata)); CHECK(dns_difftuple_create(xfr->diff.mctx, op, name, ttl, rdata, &tuple)); dns_diff_append(&xfr->diff, &tuple); if (++xfr->difflen > 100) CHECK(ixfr_apply(xfr)); result = ISC_R_SUCCESS; failure: return (result);}/* * Apply a set of IXFR changes to the database. */static isc_result_tixfr_apply(dns_xfrin_ctx_t *xfr) { isc_result_t result; if (xfr->ver == NULL) { CHECK(dns_db_newversion(xfr->db, &xfr->ver)); if (xfr->ixfr.journal != NULL) CHECK(dns_journal_begin_transaction(xfr->ixfr.journal)); } CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver)); if (xfr->ixfr.journal != NULL) { result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff); if (result != ISC_R_SUCCESS) goto failure; } dns_diff_clear(&xfr->diff); xfr->difflen = 0; result = ISC_R_SUCCESS; failure: return (result);}static isc_result_tixfr_commit(dns_xfrin_ctx_t *xfr) { isc_result_t result; CHECK(ixfr_apply(xfr)); if (xfr->ver != NULL) { /* XXX enter ready-to-commit state here */ if (xfr->ixfr.journal != NULL) CHECK(dns_journal_commit(xfr->ixfr.journal)); dns_db_closeversion(xfr->db, &xfr->ver, ISC_TRUE); dns_zone_markdirty(xfr->zone); } result = ISC_R_SUCCESS; failure: return (result);}/**************************************************************************//* * Common AXFR/IXFR protocol code *//* * Handle a single incoming resource record according to the current * state. */static isc_result_txfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl, dns_rdata_t *rdata){ isc_result_t result; redo: switch (xfr->state) { case XFRST_INITIALSOA: if (rdata->type != dns_rdatatype_soa) { xfrin_log(xfr, ISC_LOG_ERROR, "first RR in zone transfer must be SOA"); FAIL(DNS_R_FORMERR); } /* * Remember the serial number in the intial SOA. * We need it to recognize the end of an IXFR. */ xfr->end_serial = dns_soa_getserial(rdata); if (xfr->reqtype == dns_rdatatype_ixfr && ! DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial) && !dns_zone_isforced(xfr->zone)) { /* * This must be the single SOA record that is * sent when the current version on the master * is not newer than the version in the request. */ xfrin_log(xfr, ISC_LOG_DEBUG(3), "requested serial %u, " "master has %u, not updating", xfr->ixfr.request_serial, xfr->end_serial); FAIL(DNS_R_UPTODATE); } if (xfr->reqtype == dns_rdatatype_axfr) xfr->checkid = ISC_FALSE; xfr->state = XFRST_FIRSTDATA; break; case XFRST_FIRSTDATA: /* * If the transfer begins with one SOA record, it is an AXFR, * if it begins with two SOAs, it is an IXFR. */ if (xfr->reqtype == dns_rdatatype_ixfr && rdata->type == dns_rdatatype_soa && xfr->ixfr.request_serial == dns_soa_getserial(rdata)) { xfrin_log(xfr, ISC_LOG_DEBUG(3), "got incremental response"); CHECK(ixfr_init(xfr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -