xfrin.c revision 97a2a26cd93e153dcf8fb1ea6bf2311426496251
f6b909274159da7aaad8463c90f15018136cf6cbTinderbox User/*
bf8267aa453e5d2a735ed732a043b77a0b355b20Mark Andrews * Copyright (C) 2004-2008, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * Copyright (C) 1999-2003 Internet Software Consortium.
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews *
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * Permission to use, copy, modify, and/or distribute this software for any
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley * purpose with or without fee is hereby granted, provided that the above
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley * copyright notice and this permission notice appear in all copies.
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley *
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
c2bc56dc65b4b103a5600565680eb5f33fa4c90bMark Andrews * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
51917258dbb23cfe6069ae1cf2b7fc5aefc1e0c2Bob Halley * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
6d5dcd0dc9bdbd679282b1ffc47987d24c3a1346Bob Halley * PERFORMANCE OF THIS SOFTWARE.
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki */
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
6b66ee9147e940572a0e873ecbd67456ccb85c39Mark Andrews/* $Id$ */
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
0e40083fdd5445703bd30e46e5bfe7d047bced12Brian Wellington/*! \file */
2729aea3c1a720269aaae92ce3a84af1ba0a75ebMark Andrews
2729aea3c1a720269aaae92ce3a84af1ba0a75ebMark Andrews#include <config.h>
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff#include <isc/mem.h>
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff#include <isc/print.h>
64e41159a919b0711321fe688ca5da4f4d1b7d80Bob Halley#include <isc/random.h>
64e41159a919b0711321fe688ca5da4f4d1b7d80Bob Halley#include <isc/string.h> /* Required for HP/UX (and others?) */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉#include <isc/task.h>
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson#include <isc/timer.h>
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff#include <isc/util.h>
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff#include <dns/callbacks.h>
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley#include <dns/db.h>
a00f9e2f50675bd43cc6a9fe2669709162a2ccb4Evan Hunt#include <dns/diff.h>
a00f9e2f50675bd43cc6a9fe2669709162a2ccb4Evan Hunt#include <dns/events.h>
a00f9e2f50675bd43cc6a9fe2669709162a2ccb4Evan Hunt#include <dns/journal.h>
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence#include <dns/log.h>
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt#include <dns/message.h>
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt#include <dns/rdataclass.h>
578e3196076b409c509c6c0a0873e7bfe54f4d61Evan Hunt#include <dns/rdatalist.h>
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#include <dns/rdataset.h>
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence#include <dns/result.h>
800cfc8a5ce9ffacfc076003b34b5f787f82c701Mark Andrews#include <dns/soa.h>
800cfc8a5ce9ffacfc076003b34b5f787f82c701Mark Andrews#include <dns/tcpmsg.h>
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#include <dns/timer.h>
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#include <dns/tsig.h>
42654cd6bbf5bc40c4374d3737e98a3dc05176f2Andreas Gustafsson#include <dns/view.h>
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#include <dns/xfrin.h>
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#include <dns/zone.h>
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉#include <dst/dst.h>
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews/*
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * Incoming AXFR and IXFR.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews/*%
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * It would be non-sensical (or at least obtuse) to use FAIL() with an
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * from complaining about "end-of-loop code not reached".
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define FAIL(code) \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews do { result = (code); \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews if (result != ISC_R_SUCCESS) goto failure; \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews } while (0)
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define CHECK(op) \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews do { result = (op); \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews if (result != ISC_R_SUCCESS) goto failure; \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews } while (0)
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews/*%
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * The states of the *XFR state machine. We handle both IXFR and AXFR
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * with a single integrated state machine because they cannot be distinguished
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * immediately - an AXFR response to an IXFR request can only be detected
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * when the first two (2) response RRs have already been received.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewstypedef enum {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_SOAQUERY,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_GOTSOA,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_INITIALSOA,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_FIRSTDATA,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_IXFR_DELSOA,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews XFRST_IXFR_DEL,
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley XFRST_IXFR_ADDSOA,
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence XFRST_IXFR_ADD,
38122021242747404f7f3e02ff4428a7cb884b77Evan Hunt XFRST_IXFR_END,
e7fb847ed570dd8c1bcdacabb3d69bd81feb79aeMark Andrews XFRST_AXFR,
e7fb847ed570dd8c1bcdacabb3d69bd81feb79aeMark Andrews XFRST_AXFR_END
96d3f5b8ca61f7b59365d68e26aa923d13a0846bBob Halley} xfrin_state_t;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
96d3f5b8ca61f7b59365d68e26aa923d13a0846bBob Halley/*%
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence * Incoming zone transfer context.
5c14b1acbe0e7d244fbebbe5a16cea29233d79a3Michael Graff */
96e79f7ede9fd09c79ac6452ab09e4e48b288e4dMichael Graff
60e0b5df02d27961f735a04ae62b5f09a9c17480Mark Andrewsstruct dns_xfrin_ctx {
f40036cbd6ba047c954dcea8705a0b9392d403d9Mark Andrews unsigned int magic;
3e1178f56e54ec8c955056c8883718d07b833357Michael Graff isc_mem_t *mctx;
6a88ed7c3f786bdf2e2ecfae16d2b5cb530281c6David Lawrence dns_zone_t *zone;
a71f9502bfb791b572c22d3bd39875842de6532aMark Andrews
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff int refcount;
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley
f4ce616539dd81322fa4db9676f42ef2e0a19031Michael Graff isc_task_t *task;
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence isc_timer_t *timer;
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff isc_socketmgr_t *socketmgr;
54d47fb18b5ee4d36d6ef77e379501f6cbe2f23cDavid Lawrence
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence int connects; /*%< Connect in progress */
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence int sends; /*%< Send in progress */
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley int recvs; /*%< Receive in progress */
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews isc_boolean_t shuttingdown;
53c24d27c83084d93f591d2d5eeb40a127b514c6Andreas Gustafsson
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence dns_name_t name; /*%< Name of zone to transfer */
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence dns_rdataclass_t rdclass;
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence isc_boolean_t checkid;
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence dns_messageid_t id;
ca22760047a55f78cf6071d558b6f49f20c5202dBrian Wellington
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley /*%
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley * Requested transfer type (dns_rdatatype_axfr or
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley * dns_rdatatype_ixfr). The actual transfer type
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley * may differ due to IXFR->AXFR fallback.
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley */
a9eeaeb1c22d8a1de51510fa4fe260cf784f50dcAndreas Gustafsson dns_rdatatype_t reqtype;
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley isc_dscp_t dscp;
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley isc_sockaddr_t masteraddr;
79f0eedd657d75889009226aa7ce23eeac28df5cMark Andrews isc_sockaddr_t sourceaddr;
6b66ee9147e940572a0e873ecbd67456ccb85c39Mark Andrews isc_socket_t *socket;
6b66ee9147e940572a0e873ecbd67456ccb85c39Mark Andrews
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 /*% Buffer for IXFR/AXFR request message */
8dd17056ff254ed8ce429843bada5621f786d257Michael Graff isc_buffer_t qbuffer;
8dd17056ff254ed8ce429843bada5621f786d257Michael Graff unsigned char qbuffer_data[512];
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews /*% Incoming reply TCP message */
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_tcpmsg_t tcpmsg;
64828244e04e86dfa40f0a4f0c05f27923da499dMichael Graff isc_boolean_t tcpmsg_valid;
9ecedaea58defeadaf54dfc1211270653e0657ceMark Andrews
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dns_db_t *db;
9ecedaea58defeadaf54dfc1211270653e0657ceMark Andrews dns_dbversion_t *ver;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dns_diff_t diff; /*%< Pending database changes */
9ecedaea58defeadaf54dfc1211270653e0657ceMark Andrews int difflen; /*%< Number of pending tuples */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 xfrin_state_t state;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 isc_uint32_t end_serial;
ee537376ad830bed312d801e16bd3b26387ff1aaTatuya JINMEI 神明達哉 isc_boolean_t is_ixfr;
ee537376ad830bed312d801e16bd3b26387ff1aaTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 unsigned int nmsg; /*%< Number of messages recvd */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 unsigned int nrecs; /*%< Number of records recvd */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 isc_uint64_t nbytes; /*%< Number of bytes received */
9ecedaea58defeadaf54dfc1211270653e0657ceMark Andrews
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 isc_time_t start; /*%< Start time of the transfer */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 isc_time_t end; /*%< End time of the transfer */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dns_tsigkey_t *tsigkey; /*%< Key used to create TSIG */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 isc_buffer_t *lasttsig; /*%< The last TSIG */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dst_context_t *tsigctx; /*%< TSIG verification context */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 unsigned int sincetsig; /*%< recvd since the last TSIG */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dns_xfrindone_t done;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
9ecedaea58defeadaf54dfc1211270653e0657ceMark Andrews /*%
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 * AXFR- and IXFR-specific data. Only one is used at a time
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 * according to the is_ixfr flag, so this could be a union,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt * but keeping them separate makes it a bit simpler to clean
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews * things up when destroying the context.
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt */
20eb22375f52b35105fa36263f83f116b580eba8Bob Halley dns_rdatacallbacks_t axfr;
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews struct {
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_uint32_t request_serial;
2d80f690e0b5f9d7494448cf844e2f178419d6e9Bob Halley isc_uint32_t current_serial;
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_journal_t *journal;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt } ixfr;
99a9539ccde4a3769fd890bdae5bcce3a3492fbaBob Halley};
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews#define XFRIN_MAGIC ISC_MAGIC('X', 'f', 'r', 'I')
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt#define VALID_XFRIN(x) ISC_MAGIC_VALID(x, XFRIN_MAGIC)
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt/**************************************************************************/
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews/*
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt * Forward declarations.
f4ce616539dd81322fa4db9676f42ef2e0a19031Michael Graff */
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic isc_result_t
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntxfrin_create(isc_mem_t *mctx,
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence dns_zone_t *zone,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_db_t *db,
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews isc_task_t *task,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_timermgr_t *timermgr,
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence isc_socketmgr_t *socketmgr,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_name_t *zonename,
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews dns_rdataclass_t rdclass,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_rdatatype_t reqtype,
74da616f07f038138ddd45c10fc8de0920244d12Michael Graff isc_sockaddr_t *masteraddr,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_sockaddr_t *sourceaddr,
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews isc_dscp_t dscp,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_tsigkey_t *tsigkey,
5c14b1acbe0e7d244fbebbe5a16cea29233d79a3Michael Graff dns_xfrin_ctx_t **xfrp);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic isc_result_t axfr_init(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp);
96e79f7ede9fd09c79ac6452ab09e4e48b288e4dMichael Graffstatic isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_name_t *name, dns_ttl_t ttl,
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews dns_rdata_t *rdata);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halleystatic isc_result_t axfr_commit(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t axfr_finalize(dns_xfrin_ctx_t *xfr);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t ixfr_init(dns_xfrin_ctx_t *xfr);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halleystatic isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
36ff1620e474585845d05ea9b013071d5fc345aaMark Andrews dns_name_t *name, dns_ttl_t ttl,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_rdata_t *rdata);
c9f9dd2dd2344e45cf7b95d064338d97f08f1fb8Bob Halleystatic isc_result_t ixfr_commit(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic isc_result_t xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_uint32_t ttl, dns_rdata_t *rdata);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic void xfrin_connect_done(isc_task_t *task, isc_event_t *event);
9a2eda6f8e435ecf0d84502c84b1e75e2f5e9220Bob Halleystatic isc_result_t xfrin_send_request(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic void xfrin_send_done(isc_task_t *task, isc_event_t *event);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic void xfrin_sendlen_done(isc_task_t *task, isc_event_t *event);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic void xfrin_recv_done(isc_task_t *task, isc_event_t *event);
53c24d27c83084d93f591d2d5eeb40a127b514c6Andreas Gustafssonstatic void xfrin_timeout(isc_task_t *task, isc_event_t *event);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic void maybe_free(dns_xfrin_ctx_t *xfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halleystatic void
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntxfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic isc_result_t
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntrender(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf);
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic void
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsxfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt const char *fmt, va_list ap)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews ISC_FORMAT_PRINTF(4, 0);
2a49f6bbfeb3f801ada23d525342c3b6b9a8e655Evan Hunt
2a49f6bbfeb3f801ada23d525342c3b6b9a8e655Evan Huntstatic void
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntxfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence const char *fmt, ...)
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt ISC_FORMAT_PRINTF(4, 5);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic void
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrencexfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt ISC_FORMAT_PRINTF(3, 4);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt/**************************************************************************/
3ff55a3111fe09f517218905248974b8319b2c59Mark Andrews/*
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt * AXFR handling
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews */
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
1ef8965366d91e02a4672c35a187d30aa4a4c72cMark Andrewsstatic isc_result_t
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntaxfr_init(dns_xfrin_ctx_t *xfr) {
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews isc_result_t result;
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
60650dd537ca5e2eda953914bf5715d5e8f8b872Mark Andrews xfr->is_ixfr = ISC_FALSE;
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews if (xfr->db != NULL)
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_db_detach(&xfr->db);
658c72a46ee3b1193cc38e4f94121e344e0751e9Mark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt CHECK(axfr_makedb(xfr, &xfr->db));
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews dns_rdatacallbacks_init(&xfr->axfr);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt CHECK(dns_db_beginload(xfr->db, &xfr->axfr));
6a88ed7c3f786bdf2e2ecfae16d2b5cb530281c6David Lawrence result = ISC_R_SUCCESS;
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt failure:
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (result);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt}
a71f9502bfb791b572c22d3bd39875842de6532aMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Huntstatic isc_result_t
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsaxfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) {
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_result_t result;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt result = dns_db_create(xfr->mctx, /* XXX */
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews "rbt", /* XXX guess */
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt &xfr->name,
e25765f84a5646fc7d54bdc73e8b353b5a78ae86James Brister dns_dbtype_zone,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt xfr->rdclass,
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews 0, NULL, /* XXX guess */
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dbp);
dbb75545040bd612809c475d9f84affed56d417aBrian Wellington if (result == ISC_R_SUCCESS)
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews dns_zone_rpz_enable_db(xfr->zone, *dbp);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (result);
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt}
f40036cbd6ba047c954dcea8705a0b9392d403d9Mark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic isc_result_t
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsaxfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
60e0b5df02d27961f735a04ae62b5f09a9c17480Mark Andrews{
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt isc_result_t result;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt dns_difftuple_t *tuple = NULL;
ca22760047a55f78cf6071d558b6f49f20c5202dBrian Wellington
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt CHECK(dns_zone_checknames(xfr->zone, name, rdata));
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews CHECK(dns_difftuple_create(xfr->diff.mctx, op,
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt name, ttl, rdata, &tuple));
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson dns_diff_append(&xfr->diff, &tuple);
578e3196076b409c509c6c0a0873e7bfe54f4d61Evan Hunt if (++xfr->difflen > 100)
578e3196076b409c509c6c0a0873e7bfe54f4d61Evan Hunt CHECK(axfr_apply(xfr));
dbb012765c735ee0d82dedb116cdc7cf18957814Evan Hunt result = ISC_R_SUCCESS;
578e3196076b409c509c6c0a0873e7bfe54f4d61Evan Hunt failure:
d3397876060db0baa38432d90283eda6097f608dBob Halley return (result);
d3397876060db0baa38432d90283eda6097f608dBob Halley}
d3397876060db0baa38432d90283eda6097f608dBob Halley
3d7e707dd6059735e48d54af7dfc96927e22ca9aBob Halley/*
fd088dc8c8e63f495c4d0257f21849542a582c1eDavid Lawrence * Store a set of AXFR RRs in the database.
44aae046c38e796e581110b7ecdf4478167d684dBob Halley */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉static isc_result_t
e2ab74e3bf3c7604112d064c849e621e3f519eccJames Bristeraxfr_apply(dns_xfrin_ctx_t *xfr) {
66870de5231491d8132b98159734a62b1fa12f1fAndreas Gustafsson isc_result_t result;
66870de5231491d8132b98159734a62b1fa12f1fAndreas Gustafsson
37aab7fc211828df0358628f119ef91140c6f2c9William King CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add, xfr->axfr.add_private));
e582897ab49fe449d606fa14e0e4bbb8124099ffWilliam King xfr->difflen = 0;
37aab7fc211828df0358628f119ef91140c6f2c9William King dns_diff_clear(&xfr->diff);
7bdfc3914aaeeeea66918c735619f7592b8a2c39William King result = ISC_R_SUCCESS;
37aab7fc211828df0358628f119ef91140c6f2c9William King failure:
return (result);
}
static isc_result_t
axfr_commit(dns_xfrin_ctx_t *xfr) {
isc_result_t result;
CHECK(axfr_apply(xfr));
CHECK(dns_db_endload(xfr->db, &xfr->axfr));
result = ISC_R_SUCCESS;
failure:
return (result);
}
static isc_result_t
axfr_finalize(dns_xfrin_ctx_t *xfr) {
isc_result_t result;
CHECK(dns_zone_replacedb(xfr->zone, xfr->db, ISC_TRUE));
result = ISC_R_SUCCESS;
failure:
return (result);
}
/**************************************************************************/
/*
* IXFR handling
*/
static isc_result_t
ixfr_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,
DNS_JOURNAL_CREATE, &xfr->ixfr.journal));
result = ISC_R_SUCCESS;
failure:
return (result);
}
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)
{
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_t
ixfr_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_t
ixfr_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_t
xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl,
dns_rdata_t *rdata)
{
isc_result_t result;
xfr->nrecs++;
if (rdata->type == dns_rdatatype_none ||
dns_rdatatype_ismeta(rdata->type))
FAIL(DNS_R_FORMERR);
redo:
switch (xfr->state) {
case XFRST_SOAQUERY:
if (rdata->type != dns_rdatatype_soa) {
xfrin_log(xfr, ISC_LOG_ERROR,
"non-SOA response to SOA query");
FAIL(DNS_R_FORMERR);
}
xfr->end_serial = dns_soa_getserial(rdata);
if (!DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial) &&
!dns_zone_isforced(xfr->zone)) {
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);
}
xfr->state = XFRST_GOTSOA;
break;
case XFRST_GOTSOA:
/*
* Skip other records in the answer section.
*/
break;
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 initial 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));
xfr->state = XFRST_IXFR_DELSOA;
} else {
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"got nonincremental response");
CHECK(axfr_init(xfr));
xfr->state = XFRST_AXFR;
}
goto redo;
case XFRST_IXFR_DELSOA:
INSIST(rdata->type == dns_rdatatype_soa);
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
xfr->state = XFRST_IXFR_DEL;
break;
case XFRST_IXFR_DEL:
if (rdata->type == dns_rdatatype_soa) {
isc_uint32_t soa_serial = dns_soa_getserial(rdata);
xfr->state = XFRST_IXFR_ADDSOA;
xfr->ixfr.current_serial = soa_serial;
goto redo;
}
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
break;
case XFRST_IXFR_ADDSOA:
INSIST(rdata->type == dns_rdatatype_soa);
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
xfr->state = XFRST_IXFR_ADD;
break;
case XFRST_IXFR_ADD:
if (rdata->type == dns_rdatatype_soa) {
isc_uint32_t soa_serial = dns_soa_getserial(rdata);
if (soa_serial == xfr->end_serial) {
CHECK(ixfr_commit(xfr));
xfr->state = XFRST_IXFR_END;
break;
} else if (soa_serial != xfr->ixfr.current_serial) {
xfrin_log(xfr, ISC_LOG_ERROR,
"IXFR out of sync: "
"expected serial %u, got %u",
xfr->ixfr.current_serial, soa_serial);
FAIL(DNS_R_FORMERR);
} else {
CHECK(ixfr_commit(xfr));
xfr->state = XFRST_IXFR_DELSOA;
goto redo;
}
}
if (rdata->type == dns_rdatatype_ns &&
dns_name_iswildcard(name))
FAIL(DNS_R_INVALIDNS);
CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
break;
case XFRST_AXFR:
/*
* Old BINDs sent cross class A records for non IN classes.
*/
if (rdata->type == dns_rdatatype_a &&
rdata->rdclass != xfr->rdclass &&
xfr->rdclass != dns_rdataclass_in)
break;
CHECK(axfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
if (rdata->type == dns_rdatatype_soa) {
CHECK(axfr_commit(xfr));
xfr->state = XFRST_AXFR_END;
break;
}
break;
case XFRST_AXFR_END:
case XFRST_IXFR_END:
FAIL(DNS_R_EXTRADATA);
/* NOTREACHED */
default:
INSIST(0);
break;
}
result = ISC_R_SUCCESS;
failure:
return (result);
}
isc_result_t
dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
isc_mem_t *mctx, isc_timermgr_t *timermgr,
isc_socketmgr_t *socketmgr, isc_task_t *task,
dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp)
{
isc_sockaddr_t sourceaddr;
isc_dscp_t dscp;
switch (isc_sockaddr_pf(masteraddr)) {
case PF_INET:
sourceaddr = *dns_zone_getxfrsource4(zone);
dscp = dns_zone_getxfrsource4dscp(zone);
break;
case PF_INET6:
sourceaddr = *dns_zone_getxfrsource6(zone);
dscp = dns_zone_getxfrsource6dscp(zone);
break;
default:
INSIST(0);
}
return(dns_xfrin_create3(zone, xfrtype, masteraddr, &sourceaddr,
dscp, tsigkey, mctx, timermgr, socketmgr,
task, done, xfrp));
}
isc_result_t
dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype,
isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
dns_tsigkey_t *tsigkey, isc_mem_t *mctx,
isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
isc_task_t *task, dns_xfrindone_t done,
dns_xfrin_ctx_t **xfrp)
{
return (dns_xfrin_create3(zone, xfrtype, masteraddr, sourceaddr, -1,
tsigkey, mctx, timermgr, socketmgr, task,
done, xfrp));
}
isc_result_t
dns_xfrin_create3(dns_zone_t *zone, dns_rdatatype_t xfrtype,
isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
isc_dscp_t dscp, dns_tsigkey_t *tsigkey, isc_mem_t *mctx,
isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
isc_task_t *task, dns_xfrindone_t done,
dns_xfrin_ctx_t **xfrp)
{
dns_name_t *zonename = dns_zone_getorigin(zone);
dns_xfrin_ctx_t *xfr = NULL;
isc_result_t result;
dns_db_t *db = NULL;
REQUIRE(xfrp != NULL && *xfrp == NULL);
(void)dns_zone_getdb(zone, &db);
if (xfrtype == dns_rdatatype_soa || xfrtype == dns_rdatatype_ixfr)
REQUIRE(db != NULL);
CHECK(xfrin_create(mctx, zone, db, task, timermgr, socketmgr, zonename,
dns_zone_getclass(zone), xfrtype, masteraddr,
sourceaddr, dscp, tsigkey, &xfr));
CHECK(xfrin_start(xfr));
xfr->done = done;
xfr->refcount++;
*xfrp = xfr;
failure:
if (db != NULL)
dns_db_detach(&db);
if (result != ISC_R_SUCCESS) {
char zonetext[DNS_NAME_MAXTEXT+32];
dns_zone_name(zone, zonetext, sizeof(zonetext));
xfrin_log1(ISC_LOG_ERROR, zonetext, masteraddr,
"zone transfer setup failed");
}
return (result);
}
void
dns_xfrin_shutdown(dns_xfrin_ctx_t *xfr) {
if (! xfr->shuttingdown)
xfrin_fail(xfr, ISC_R_CANCELED, "shut down");
}
void
dns_xfrin_attach(dns_xfrin_ctx_t *source, dns_xfrin_ctx_t **target) {
REQUIRE(target != NULL && *target == NULL);
source->refcount++;
*target = source;
}
void
dns_xfrin_detach(dns_xfrin_ctx_t **xfrp) {
dns_xfrin_ctx_t *xfr = *xfrp;
INSIST(xfr->refcount > 0);
xfr->refcount--;
maybe_free(xfr);
*xfrp = NULL;
}
static void
xfrin_cancelio(dns_xfrin_ctx_t *xfr) {
if (xfr->connects > 0) {
isc_socket_cancel(xfr->socket, xfr->task,
ISC_SOCKCANCEL_CONNECT);
} else if (xfr->recvs > 0) {
dns_tcpmsg_cancelread(&xfr->tcpmsg);
} else if (xfr->sends > 0) {
isc_socket_cancel(xfr->socket, xfr->task,
ISC_SOCKCANCEL_SEND);
}
}
static void
xfrin_reset(dns_xfrin_ctx_t *xfr) {
REQUIRE(VALID_XFRIN(xfr));
xfrin_log(xfr, ISC_LOG_INFO, "resetting");
xfrin_cancelio(xfr);
if (xfr->socket != NULL)
isc_socket_detach(&xfr->socket);
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
dns_diff_clear(&xfr->diff);
xfr->difflen = 0;
if (xfr->ixfr.journal != NULL)
dns_journal_destroy(&xfr->ixfr.journal);
if (xfr->axfr.add_private != NULL)
(void)dns_db_endload(xfr->db, &xfr->axfr);
if (xfr->tcpmsg_valid) {
dns_tcpmsg_invalidate(&xfr->tcpmsg);
xfr->tcpmsg_valid = ISC_FALSE;
}
if (xfr->ver != NULL)
dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
}
static void
xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
if (result != DNS_R_UPTODATE) {
xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s",
msg, isc_result_totext(result));
if (xfr->is_ixfr)
/* Pass special result code to force AXFR retry */
result = DNS_R_BADIXFR;
}
xfrin_cancelio(xfr);
/*
* Close the journal.
*/
if (xfr->ixfr.journal != NULL)
dns_journal_destroy(&xfr->ixfr.journal);
if (xfr->done != NULL) {
(xfr->done)(xfr->zone, result);
xfr->done = NULL;
}
xfr->shuttingdown = ISC_TRUE;
maybe_free(xfr);
}
static isc_result_t
xfrin_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,
isc_dscp_t dscp,
dns_tsigkey_t *tsigkey,
dns_xfrin_ctx_t **xfrp)
{
dns_xfrin_ctx_t *xfr = NULL;
isc_result_t result;
isc_uint32_t tmp;
xfr = isc_mem_get(mctx, sizeof(*xfr));
if (xfr == NULL)
return (ISC_R_NOMEMORY);
xfr->mctx = NULL;
isc_mem_attach(mctx, &xfr->mctx);
xfr->refcount = 0;
xfr->zone = NULL;
dns_zone_iattach(zone, &xfr->zone);
xfr->task = NULL;
isc_task_attach(task, &xfr->task);
xfr->timer = NULL;
xfr->socketmgr = socketmgr;
xfr->done = NULL;
xfr->connects = 0;
xfr->sends = 0;
xfr->recvs = 0;
xfr->shuttingdown = ISC_FALSE;
dns_name_init(&xfr->name, NULL);
xfr->rdclass = rdclass;
isc_random_get(&tmp);
xfr->checkid = ISC_TRUE;
xfr->id = (isc_uint16_t)(tmp & 0xffff);
xfr->reqtype = reqtype;
xfr->dscp = dscp;
/* sockaddr */
xfr->socket = NULL;
/* qbuffer */
/* qbuffer_data */
/* tcpmsg */
xfr->tcpmsg_valid = ISC_FALSE;
xfr->db = NULL;
if (db != NULL)
dns_db_attach(db, &xfr->db);
xfr->ver = NULL;
dns_diff_init(xfr->mctx, &xfr->diff);
xfr->difflen = 0;
if (reqtype == dns_rdatatype_soa)
xfr->state = XFRST_SOAQUERY;
else
xfr->state = XFRST_INITIALSOA;
/* end_serial */
xfr->nmsg = 0;
xfr->nrecs = 0;
xfr->nbytes = 0;
isc_time_now(&xfr->start);
xfr->tsigkey = NULL;
if (tsigkey != NULL)
dns_tsigkey_attach(tsigkey, &xfr->tsigkey);
xfr->lasttsig = NULL;
xfr->tsigctx = NULL;
xfr->sincetsig = 0;
xfr->is_ixfr = ISC_FALSE;
/* ixfr.request_serial */
/* ixfr.current_serial */
xfr->ixfr.journal = NULL;
xfr->axfr.add = NULL;
xfr->axfr.add_private = NULL;
CHECK(dns_name_dup(zonename, mctx, &xfr->name));
CHECK(isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
task, xfrin_timeout, xfr, &xfr->timer));
CHECK(dns_timer_setidle(xfr->timer,
dns_zone_getmaxxfrin(xfr->zone),
dns_zone_getidlein(xfr->zone),
ISC_FALSE));
xfr->masteraddr = *masteraddr;
INSIST(isc_sockaddr_pf(masteraddr) == isc_sockaddr_pf(sourceaddr));
xfr->sourceaddr = *sourceaddr;
isc_sockaddr_setport(&xfr->sourceaddr, 0);
isc_buffer_init(&xfr->qbuffer, xfr->qbuffer_data,
sizeof(xfr->qbuffer_data));
xfr->magic = XFRIN_MAGIC;
*xfrp = xfr;
return (ISC_R_SUCCESS);
failure:
if (xfr->timer != NULL)
isc_timer_detach(&xfr->timer);
if (dns_name_dynamic(&xfr->name))
dns_name_free(&xfr->name, xfr->mctx);
if (xfr->tsigkey != NULL)
dns_tsigkey_detach(&xfr->tsigkey);
if (xfr->db != NULL)
dns_db_detach(&xfr->db);
isc_task_detach(&xfr->task);
dns_zone_idetach(&xfr->zone);
isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
return (result);
}
static isc_result_t
xfrin_start(dns_xfrin_ctx_t *xfr) {
isc_result_t result;
CHECK(isc_socket_create(xfr->socketmgr,
isc_sockaddr_pf(&xfr->sourceaddr),
isc_sockettype_tcp,
&xfr->socket));
isc_socket_setname(xfr->socket, "xfrin", NULL);
#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
CHECK(isc_socket_bind(xfr->socket, &xfr->sourceaddr,
ISC_SOCKET_REUSEADDRESS));
#endif
isc_socket_dscp(xfr->socket, xfr->dscp);
CHECK(isc_socket_connect(xfr->socket, &xfr->masteraddr, xfr->task,
xfrin_connect_done, xfr));
xfr->connects++;
return (ISC_R_SUCCESS);
failure:
xfrin_fail(xfr, result, "failed setting up socket");
return (result);
}
/* XXX the resolver could use this, too */
static isc_result_t
render(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf) {
dns_compress_t cctx;
isc_boolean_t cleanup_cctx = ISC_FALSE;
isc_result_t result;
CHECK(dns_compress_init(&cctx, -1, mctx));
cleanup_cctx = ISC_TRUE;
CHECK(dns_message_renderbegin(msg, &cctx, buf));
CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
CHECK(dns_message_rendersection(msg, DNS_SECTION_AUTHORITY, 0));
CHECK(dns_message_rendersection(msg, DNS_SECTION_ADDITIONAL, 0));
CHECK(dns_message_renderend(msg));
result = ISC_R_SUCCESS;
failure:
if (cleanup_cctx)
dns_compress_invalidate(&cctx);
return (result);
}
/*
* A connection has been established.
*/
static void
xfrin_connect_done(isc_task_t *task, isc_event_t *event) {
isc_socket_connev_t *cev = (isc_socket_connev_t *) event;
dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
isc_result_t result = cev->result;
char sourcetext[ISC_SOCKADDR_FORMATSIZE];
isc_sockaddr_t sockaddr;
dns_zonemgr_t * zmgr;
isc_time_t now;
REQUIRE(VALID_XFRIN(xfr));
UNUSED(task);
INSIST(event->ev_type == ISC_SOCKEVENT_CONNECT);
isc_event_free(&event);
xfr->connects--;
if (xfr->shuttingdown) {
maybe_free(xfr);
return;
}
zmgr = dns_zone_getmgr(xfr->zone);
if (zmgr != NULL) {
if (result != ISC_R_SUCCESS) {
TIME_NOW(&now);
dns_zonemgr_unreachableadd(zmgr, &xfr->masteraddr,
&xfr->sourceaddr, &now);
goto failure;
} else
dns_zonemgr_unreachabledel(zmgr, &xfr->masteraddr,
&xfr->sourceaddr);
}
result = isc_socket_getsockname(xfr->socket, &sockaddr);
if (result == ISC_R_SUCCESS) {
isc_sockaddr_format(&sockaddr, sourcetext, sizeof(sourcetext));
} else
strcpy(sourcetext, "<UNKNOWN>");
xfrin_log(xfr, ISC_LOG_INFO, "connected using %s", sourcetext);
dns_tcpmsg_init(xfr->mctx, xfr->socket, &xfr->tcpmsg);
xfr->tcpmsg_valid = ISC_TRUE;
CHECK(xfrin_send_request(xfr));
failure:
if (result != ISC_R_SUCCESS)
xfrin_fail(xfr, result, "failed to connect");
}
/*
* Convert a tuple into a dns_name_t suitable for inserting
* into the given dns_message_t.
*/
static isc_result_t
tuple2msgname(dns_difftuple_t *tuple, dns_message_t *msg, dns_name_t **target)
{
isc_result_t result;
dns_rdata_t *rdata = NULL;
dns_rdatalist_t *rdl = NULL;
dns_rdataset_t *rds = NULL;
dns_name_t *name = NULL;
REQUIRE(target != NULL && *target == NULL);
CHECK(dns_message_gettemprdata(msg, &rdata));
dns_rdata_init(rdata);
dns_rdata_clone(&tuple->rdata, rdata);
CHECK(dns_message_gettemprdatalist(msg, &rdl));
dns_rdatalist_init(rdl);
rdl->type = tuple->rdata.type;
rdl->rdclass = tuple->rdata.rdclass;
rdl->ttl = tuple->ttl;
ISC_LIST_APPEND(rdl->rdata, rdata, link);
CHECK(dns_message_gettemprdataset(msg, &rds));
dns_rdataset_init(rds);
CHECK(dns_rdatalist_tordataset(rdl, rds));
CHECK(dns_message_gettempname(msg, &name));
dns_name_init(name, NULL);
dns_name_clone(&tuple->name, name);
ISC_LIST_APPEND(name->list, rds, link);
*target = name;
return (ISC_R_SUCCESS);
failure:
if (rds != NULL) {
dns_rdataset_disassociate(rds);
dns_message_puttemprdataset(msg, &rds);
}
if (rdl != NULL) {
ISC_LIST_UNLINK(rdl->rdata, rdata, link);
dns_message_puttemprdatalist(msg, &rdl);
}
if (rdata != NULL)
dns_message_puttemprdata(msg, &rdata);
return (result);
}
/*
* Build an *XFR request and send its length prefix.
*/
static isc_result_t
xfrin_send_request(dns_xfrin_ctx_t *xfr) {
isc_result_t result;
isc_region_t region;
isc_region_t lregion;
dns_rdataset_t *qrdataset = NULL;
dns_message_t *msg = NULL;
unsigned char length[2];
dns_difftuple_t *soatuple = NULL;
dns_name_t *qname = NULL;
dns_dbversion_t *ver = NULL;
dns_name_t *msgsoaname = NULL;
/* Create the request message */
CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &msg));
CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
/* Create a name for the question section. */
CHECK(dns_message_gettempname(msg, &qname));
dns_name_init(qname, NULL);
dns_name_clone(&xfr->name, qname);
/* Formulate the question and attach it to the question name. */
CHECK(dns_message_gettemprdataset(msg, &qrdataset));
dns_rdataset_init(qrdataset);
dns_rdataset_makequestion(qrdataset, xfr->rdclass, xfr->reqtype);
ISC_LIST_APPEND(qname->list, qrdataset, link);
qrdataset = NULL;
dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
qname = NULL;
if (xfr->reqtype == dns_rdatatype_ixfr) {
/* Get the SOA and add it to the authority section. */
/* XXX is using the current version the right thing? */
dns_db_currentversion(xfr->db, &ver);
CHECK(dns_db_createsoatuple(xfr->db, ver, xfr->mctx,
DNS_DIFFOP_EXISTS, &soatuple));
xfr->ixfr.request_serial = dns_soa_getserial(&soatuple->rdata);
xfr->ixfr.current_serial = xfr->ixfr.request_serial;
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"requesting IXFR for serial %u",
xfr->ixfr.request_serial);
CHECK(tuple2msgname(soatuple, msg, &msgsoaname));
dns_message_addname(msg, msgsoaname, DNS_SECTION_AUTHORITY);
} else if (xfr->reqtype == dns_rdatatype_soa)
CHECK(dns_db_getsoaserial(xfr->db, NULL,
&xfr->ixfr.request_serial));
xfr->checkid = ISC_TRUE;
xfr->id++;
xfr->nmsg = 0;
xfr->nrecs = 0;
xfr->nbytes = 0;
isc_time_now(&xfr->start);
msg->id = xfr->id;
if (xfr->tsigctx != NULL)
dst_context_destroy(&xfr->tsigctx);
CHECK(render(msg, xfr->mctx, &xfr->qbuffer));
/*
* Free the last tsig, if there is one.
*/
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
/*
* Save the query TSIG and don't let message_destroy free it.
*/
CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
isc_buffer_usedregion(&xfr->qbuffer, &region);
INSIST(region.length <= 65535);
length[0] = region.length >> 8;
length[1] = region.length & 0xFF;
lregion.base = length;
lregion.length = 2;
CHECK(isc_socket_send(xfr->socket, &lregion, xfr->task,
xfrin_sendlen_done, xfr));
xfr->sends++;
failure:
if (qname != NULL)
dns_message_puttempname(msg, &qname);
if (qrdataset != NULL)
dns_message_puttemprdataset(msg, &qrdataset);
if (msg != NULL)
dns_message_destroy(&msg);
if (soatuple != NULL)
dns_difftuple_free(&soatuple);
if (ver != NULL)
dns_db_closeversion(xfr->db, &ver, ISC_FALSE);
return (result);
}
/* XXX there should be library support for sending DNS TCP messages */
static void
xfrin_sendlen_done(isc_task_t *task, isc_event_t *event) {
isc_socketevent_t *sev = (isc_socketevent_t *) event;
dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
isc_result_t evresult = sev->result;
isc_result_t result;
isc_region_t region;
REQUIRE(VALID_XFRIN(xfr));
UNUSED(task);
INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
isc_event_free(&event);
xfr->sends--;
if (xfr->shuttingdown) {
maybe_free(xfr);
return;
}
xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request length prefix");
CHECK(evresult);
isc_buffer_usedregion(&xfr->qbuffer, &region);
CHECK(isc_socket_send(xfr->socket, &region, xfr->task,
xfrin_send_done, xfr));
xfr->sends++;
failure:
if (result != ISC_R_SUCCESS)
xfrin_fail(xfr, result, "failed sending request length prefix");
}
static void
xfrin_send_done(isc_task_t *task, isc_event_t *event) {
isc_socketevent_t *sev = (isc_socketevent_t *) event;
dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
isc_result_t result;
REQUIRE(VALID_XFRIN(xfr));
UNUSED(task);
INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
xfr->sends--;
xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request data");
CHECK(sev->result);
CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
xfrin_recv_done, xfr));
xfr->recvs++;
failure:
isc_event_free(&event);
if (result != ISC_R_SUCCESS)
xfrin_fail(xfr, result, "failed sending request data");
}
static void
xfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) ev->ev_arg;
isc_result_t result;
dns_message_t *msg = NULL;
dns_name_t *name;
dns_tcpmsg_t *tcpmsg;
dns_name_t *tsigowner = NULL;
REQUIRE(VALID_XFRIN(xfr));
UNUSED(task);
INSIST(ev->ev_type == DNS_EVENT_TCPMSG);
tcpmsg = ev->ev_sender;
isc_event_free(&ev);
xfr->recvs--;
if (xfr->shuttingdown) {
maybe_free(xfr);
return;
}
CHECK(tcpmsg->result);
xfrin_log(xfr, ISC_LOG_DEBUG(7), "received %u bytes",
tcpmsg->buffer.used);
CHECK(isc_timer_touch(xfr->timer));
CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTPARSE, &msg));
CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
msg->tsigctx = xfr->tsigctx;
xfr->tsigctx = NULL;
if (xfr->nmsg > 0)
msg->tcp_continuation = 1;
result = dns_message_parse(msg, &tcpmsg->buffer,
DNS_MESSAGEPARSE_PRESERVEORDER);
if (result == ISC_R_SUCCESS)
dns_message_logpacket(msg, "received message:\n",
DNS_LOGCATEGORY_XFER_IN,
DNS_LOGMODULE_XFER_IN,
ISC_LOG_DEBUG(10), xfr->mctx);
else
xfrin_log(xfr, ISC_LOG_DEBUG(10), "dns_message_parse: %s",
dns_result_totext(result));
if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror ||
(xfr->checkid && msg->id != xfr->id)) {
if (result == ISC_R_SUCCESS)
result = ISC_RESULTCLASS_DNSRCODE + msg->rcode; /*XXX*/
if (result == ISC_R_SUCCESS || result == DNS_R_NOERROR)
result = DNS_R_UNEXPECTEDID;
if (xfr->reqtype == dns_rdatatype_axfr ||
xfr->reqtype == dns_rdatatype_soa)
goto failure;
xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR",
isc_result_totext(result));
try_axfr:
dns_message_destroy(&msg);
xfrin_reset(xfr);
xfr->reqtype = dns_rdatatype_soa;
xfr->state = XFRST_SOAQUERY;
(void)xfrin_start(xfr);
return;
}
/*
* Does the server know about IXFR? If it doesn't we will get
* a message with a empty answer section or a potentially a CNAME /
* DNAME, the later is handled by xfr_rr() which will return FORMERR
* if the first RR in the answer section is not a SOA record.
*/
if (xfr->reqtype == dns_rdatatype_ixfr &&
xfr->state == XFRST_INITIALSOA &&
msg->counts[DNS_SECTION_ANSWER] == 0) {
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"empty answer section, retrying with AXFR");
goto try_axfr;
}
if (xfr->reqtype == dns_rdatatype_soa &&
(msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
FAIL(DNS_R_NOTAUTHORITATIVE);
}
result = dns_message_checksig(msg, dns_zone_getview(xfr->zone));
if (result != ISC_R_SUCCESS) {
xfrin_log(xfr, ISC_LOG_DEBUG(3), "TSIG check failed: %s",
isc_result_totext(result));
goto failure;
}
for (result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
result == ISC_R_SUCCESS;
result = dns_message_nextname(msg, DNS_SECTION_ANSWER))
{
dns_rdataset_t *rds;
name = NULL;
dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
for (rds = ISC_LIST_HEAD(name->list);
rds != NULL;
rds = ISC_LIST_NEXT(rds, link))
{
for (result = dns_rdataset_first(rds);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(rds))
{
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_current(rds, &rdata);
CHECK(xfr_rr(xfr, name, rds->ttl, &rdata));
}
}
}
if (result != ISC_R_NOMORE)
goto failure;
if (dns_message_gettsig(msg, &tsigowner) != NULL) {
/*
* Reset the counter.
*/
xfr->sincetsig = 0;
/*
* Free the last tsig, if there is one.
*/
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
/*
* Update the last tsig pointer.
*/
CHECK(dns_message_getquerytsig(msg, xfr->mctx,
&xfr->lasttsig));
} else if (dns_message_gettsigkey(msg) != NULL) {
xfr->sincetsig++;
if (xfr->sincetsig > 100 || xfr->nmsg == 0 ||
xfr->state == XFRST_AXFR_END ||
xfr->state == XFRST_IXFR_END)
{
result = DNS_R_EXPECTEDTSIG;
goto failure;
}
}
/*
* Update the number of messages received.
*/
xfr->nmsg++;
/*
* Update the number of bytes received.
*/
xfr->nbytes += tcpmsg->buffer.used;
/*
* Take the context back.
*/
INSIST(xfr->tsigctx == NULL);
xfr->tsigctx = msg->tsigctx;
msg->tsigctx = NULL;
dns_message_destroy(&msg);
switch (xfr->state) {
case XFRST_GOTSOA:
xfr->reqtype = dns_rdatatype_axfr;
xfr->state = XFRST_INITIALSOA;
CHECK(xfrin_send_request(xfr));
break;
case XFRST_AXFR_END:
CHECK(axfr_finalize(xfr));
/* FALLTHROUGH */
case XFRST_IXFR_END:
/*
* Close the journal.
*/
if (xfr->ixfr.journal != NULL)
dns_journal_destroy(&xfr->ixfr.journal);
/*
* Inform the caller we succeeded.
*/
if (xfr->done != NULL) {
(xfr->done)(xfr->zone, ISC_R_SUCCESS);
xfr->done = NULL;
}
/*
* We should have no outstanding events at this
* point, thus maybe_free() should succeed.
*/
xfr->shuttingdown = ISC_TRUE;
maybe_free(xfr);
break;
default:
/*
* Read the next message.
*/
CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
xfrin_recv_done, xfr));
xfr->recvs++;
}
return;
failure:
if (msg != NULL)
dns_message_destroy(&msg);
if (result != ISC_R_SUCCESS)
xfrin_fail(xfr, result, "failed while receiving responses");
}
static void
xfrin_timeout(isc_task_t *task, isc_event_t *event) {
dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
REQUIRE(VALID_XFRIN(xfr));
UNUSED(task);
isc_event_free(&event);
/*
* This will log "giving up: timeout".
*/
xfrin_fail(xfr, ISC_R_TIMEDOUT, "giving up");
}
static void
maybe_free(dns_xfrin_ctx_t *xfr) {
isc_uint64_t msecs;
isc_uint64_t persec;
REQUIRE(VALID_XFRIN(xfr));
if (! xfr->shuttingdown || xfr->refcount != 0 ||
xfr->connects != 0 || xfr->sends != 0 ||
xfr->recvs != 0)
return;
/*
* Calculate the length of time the transfer took,
* and print a log message with the bytes and rate.
*/
isc_time_now(&xfr->end);
msecs = isc_time_microdiff(&xfr->end, &xfr->start) / 1000;
if (msecs == 0)
msecs = 1;
persec = (xfr->nbytes * 1000) / msecs;
xfrin_log(xfr, ISC_LOG_INFO,
"Transfer completed: %d messages, %d records, "
"%" ISC_PRINT_QUADFORMAT "u bytes, "
"%u.%03u secs (%u bytes/sec)",
xfr->nmsg, xfr->nrecs, xfr->nbytes,
(unsigned int) (msecs / 1000), (unsigned int) (msecs % 1000),
(unsigned int) persec);
if (xfr->socket != NULL)
isc_socket_detach(&xfr->socket);
if (xfr->timer != NULL)
isc_timer_detach(&xfr->timer);
if (xfr->task != NULL)
isc_task_detach(&xfr->task);
if (xfr->tsigkey != NULL)
dns_tsigkey_detach(&xfr->tsigkey);
if (xfr->lasttsig != NULL)
isc_buffer_free(&xfr->lasttsig);
dns_diff_clear(&xfr->diff);
if (xfr->ixfr.journal != NULL)
dns_journal_destroy(&xfr->ixfr.journal);
if (xfr->axfr.add_private != NULL)
(void)dns_db_endload(xfr->db, &xfr->axfr);
if (xfr->tcpmsg_valid)
dns_tcpmsg_invalidate(&xfr->tcpmsg);
if (xfr->tsigctx != NULL)
dst_context_destroy(&xfr->tsigctx);
if ((xfr->name.attributes & DNS_NAMEATTR_DYNAMIC) != 0)
dns_name_free(&xfr->name, xfr->mctx);
if (xfr->ver != NULL)
dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
if (xfr->db != NULL)
dns_db_detach(&xfr->db);
if (xfr->zone != NULL)
dns_zone_idetach(&xfr->zone);
isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
}
/*
* Log incoming zone transfer messages in a format like
* transfer of <zone> from <address>: <message>
*/
static void
xfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
const char *fmt, va_list ap)
{
char mastertext[ISC_SOCKADDR_FORMATSIZE];
char msgtext[2048];
isc_sockaddr_format(masteraddr, mastertext, sizeof(mastertext));
vsnprintf(msgtext, sizeof(msgtext), fmt, ap);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_XFER_IN,
DNS_LOGMODULE_XFER_IN, level,
"transfer of '%s' from %s: %s",
zonetext, mastertext, msgtext);
}
/*
* Logging function for use when a xfrin_ctx_t has not yet been created.
*/
static void
xfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
const char *fmt, ...)
{
va_list ap;
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
va_start(ap, fmt);
xfrin_logv(level, zonetext, masteraddr, fmt, ap);
va_end(ap);
}
/*
* Logging function for use when there is a xfrin_ctx_t.
*/
static void
xfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
{
va_list ap;
char zonetext[DNS_NAME_MAXTEXT+32];
if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
return;
dns_zone_name(xfr->zone, zonetext, sizeof(zonetext));
va_start(ap, fmt);
xfrin_logv(level, zonetext, &xfr->masteraddr, fmt, ap);
va_end(ap);
}