xfrout.c revision 218384
1135446Strhodes/* 2218384Sdougb * Copyright (C) 2004-2010 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18218384Sdougb/* $Id: xfrout.c,v 1.131.26.6 2010-05-27 23:48:18 tbox Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/formatcheck.h> 23135446Strhodes#include <isc/mem.h> 24135446Strhodes#include <isc/timer.h> 25135446Strhodes#include <isc/print.h> 26193149Sdougb#include <isc/stats.h> 27135446Strhodes#include <isc/util.h> 28135446Strhodes 29135446Strhodes#include <dns/db.h> 30135446Strhodes#include <dns/dbiterator.h> 31170222Sdougb#ifdef DLZ 32170222Sdougb#include <dns/dlz.h> 33170222Sdougb#endif 34135446Strhodes#include <dns/fixedname.h> 35135446Strhodes#include <dns/journal.h> 36135446Strhodes#include <dns/message.h> 37135446Strhodes#include <dns/peer.h> 38135446Strhodes#include <dns/rdataclass.h> 39135446Strhodes#include <dns/rdatalist.h> 40135446Strhodes#include <dns/rdataset.h> 41135446Strhodes#include <dns/rdatasetiter.h> 42135446Strhodes#include <dns/result.h> 43135446Strhodes#include <dns/soa.h> 44193149Sdougb#include <dns/stats.h> 45135446Strhodes#include <dns/timer.h> 46135446Strhodes#include <dns/tsig.h> 47135446Strhodes#include <dns/view.h> 48135446Strhodes#include <dns/zone.h> 49135446Strhodes#include <dns/zt.h> 50135446Strhodes 51135446Strhodes#include <named/client.h> 52135446Strhodes#include <named/log.h> 53135446Strhodes#include <named/server.h> 54135446Strhodes#include <named/xfrout.h> 55135446Strhodes 56193149Sdougb/*! \file 57170222Sdougb * \brief 58135446Strhodes * Outgoing AXFR and IXFR. 59135446Strhodes */ 60135446Strhodes 61135446Strhodes/* 62135446Strhodes * TODO: 63135446Strhodes * - IXFR over UDP 64135446Strhodes */ 65135446Strhodes 66135446Strhodes#define XFROUT_COMMON_LOGARGS \ 67135446Strhodes ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT 68135446Strhodes 69135446Strhodes#define XFROUT_PROTOCOL_LOGARGS \ 70135446Strhodes XFROUT_COMMON_LOGARGS, ISC_LOG_INFO 71135446Strhodes 72135446Strhodes#define XFROUT_DEBUG_LOGARGS(n) \ 73135446Strhodes XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n) 74135446Strhodes 75135446Strhodes#define XFROUT_RR_LOGARGS \ 76135446Strhodes XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL 77135446Strhodes 78135446Strhodes#define XFROUT_RR_LOGLEVEL ISC_LOG_DEBUG(8) 79135446Strhodes 80170222Sdougb/*% 81135446Strhodes * Fail unconditionally and log as a client error. 82135446Strhodes * The test against ISC_R_SUCCESS is there to keep the Solaris compiler 83135446Strhodes * from complaining about "end-of-loop code not reached". 84135446Strhodes */ 85135446Strhodes#define FAILC(code, msg) \ 86135446Strhodes do { \ 87135446Strhodes result = (code); \ 88135446Strhodes ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \ 89135446Strhodes NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \ 90135446Strhodes "bad zone transfer request: %s (%s)", \ 91193149Sdougb msg, isc_result_totext(code)); \ 92135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 93135446Strhodes } while (0) 94135446Strhodes 95135446Strhodes#define FAILQ(code, msg, question, rdclass) \ 96135446Strhodes do { \ 97135446Strhodes char _buf1[DNS_NAME_FORMATSIZE]; \ 98135446Strhodes char _buf2[DNS_RDATACLASS_FORMATSIZE]; \ 99135446Strhodes result = (code); \ 100135446Strhodes dns_name_format(question, _buf1, sizeof(_buf1)); \ 101135446Strhodes dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \ 102135446Strhodes ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \ 103135446Strhodes NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \ 104135446Strhodes "bad zone transfer request: '%s/%s': %s (%s)", \ 105193149Sdougb _buf1, _buf2, msg, isc_result_totext(code)); \ 106135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 107135446Strhodes } while (0) 108135446Strhodes 109135446Strhodes#define CHECK(op) \ 110193149Sdougb do { result = (op); \ 111135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 112135446Strhodes } while (0) 113135446Strhodes 114135446Strhodes/**************************************************************************/ 115170222Sdougb/*% 116135446Strhodes * A db_rr_iterator_t is an iterator that iterates over an entire database, 117135446Strhodes * returning one RR at a time, in some arbitrary order. 118135446Strhodes */ 119135446Strhodes 120135446Strhodestypedef struct db_rr_iterator db_rr_iterator_t; 121135446Strhodes 122170222Sdougb/*% db_rr_iterator structure */ 123135446Strhodesstruct db_rr_iterator { 124135446Strhodes isc_result_t result; 125135446Strhodes dns_db_t *db; 126193149Sdougb dns_dbiterator_t *dbit; 127135446Strhodes dns_dbversion_t *ver; 128135446Strhodes isc_stdtime_t now; 129135446Strhodes dns_dbnode_t *node; 130135446Strhodes dns_fixedname_t fixedname; 131193149Sdougb dns_rdatasetiter_t *rdatasetit; 132135446Strhodes dns_rdataset_t rdataset; 133135446Strhodes dns_rdata_t rdata; 134135446Strhodes}; 135135446Strhodes 136135446Strhodesstatic isc_result_t 137135446Strhodesdb_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver, 138135446Strhodes isc_stdtime_t now); 139135446Strhodes 140135446Strhodesstatic isc_result_t 141135446Strhodesdb_rr_iterator_first(db_rr_iterator_t *it); 142135446Strhodes 143135446Strhodesstatic isc_result_t 144135446Strhodesdb_rr_iterator_next(db_rr_iterator_t *it); 145135446Strhodes 146135446Strhodesstatic void 147135446Strhodesdb_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name, 148135446Strhodes isc_uint32_t *ttl, dns_rdata_t **rdata); 149135446Strhodes 150135446Strhodesstatic void 151135446Strhodesdb_rr_iterator_destroy(db_rr_iterator_t *it); 152135446Strhodes 153193149Sdougbstatic inline void 154193149Sdougbinc_stats(dns_zone_t *zone, isc_statscounter_t counter) { 155193149Sdougb isc_stats_increment(ns_g_server->nsstats, counter); 156193149Sdougb if (zone != NULL) { 157193149Sdougb isc_stats_t *zonestats = dns_zone_getrequeststats(zone); 158193149Sdougb if (zonestats != NULL) 159193149Sdougb isc_stats_increment(zonestats, counter); 160193149Sdougb } 161193149Sdougb} 162193149Sdougb 163135446Strhodesstatic isc_result_t 164135446Strhodesdb_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver, 165135446Strhodes isc_stdtime_t now) 166135446Strhodes{ 167135446Strhodes isc_result_t result; 168135446Strhodes it->db = db; 169135446Strhodes it->dbit = NULL; 170135446Strhodes it->ver = ver; 171135446Strhodes it->now = now; 172135446Strhodes it->node = NULL; 173193149Sdougb result = dns_db_createiterator(it->db, 0, &it->dbit); 174135446Strhodes if (result != ISC_R_SUCCESS) 175135446Strhodes return (result); 176135446Strhodes it->rdatasetit = NULL; 177135446Strhodes dns_rdata_init(&it->rdata); 178135446Strhodes dns_rdataset_init(&it->rdataset); 179135446Strhodes dns_fixedname_init(&it->fixedname); 180135446Strhodes INSIST(! dns_rdataset_isassociated(&it->rdataset)); 181135446Strhodes it->result = ISC_R_SUCCESS; 182135446Strhodes return (it->result); 183135446Strhodes} 184135446Strhodes 185135446Strhodesstatic isc_result_t 186135446Strhodesdb_rr_iterator_first(db_rr_iterator_t *it) { 187135446Strhodes it->result = dns_dbiterator_first(it->dbit); 188135446Strhodes /* 189135446Strhodes * The top node may be empty when out of zone glue exists. 190135446Strhodes * Walk the tree to find the first node with data. 191135446Strhodes */ 192135446Strhodes while (it->result == ISC_R_SUCCESS) { 193135446Strhodes it->result = dns_dbiterator_current(it->dbit, &it->node, 194135446Strhodes dns_fixedname_name(&it->fixedname)); 195135446Strhodes if (it->result != ISC_R_SUCCESS) 196135446Strhodes return (it->result); 197135446Strhodes 198135446Strhodes it->result = dns_db_allrdatasets(it->db, it->node, 199135446Strhodes it->ver, it->now, 200135446Strhodes &it->rdatasetit); 201135446Strhodes if (it->result != ISC_R_SUCCESS) 202135446Strhodes return (it->result); 203135446Strhodes 204135446Strhodes it->result = dns_rdatasetiter_first(it->rdatasetit); 205135446Strhodes if (it->result != ISC_R_SUCCESS) { 206135446Strhodes /* 207135446Strhodes * This node is empty. Try next node. 208135446Strhodes */ 209135446Strhodes dns_rdatasetiter_destroy(&it->rdatasetit); 210135446Strhodes dns_db_detachnode(it->db, &it->node); 211135446Strhodes it->result = dns_dbiterator_next(it->dbit); 212135446Strhodes continue; 213135446Strhodes } 214135446Strhodes dns_rdatasetiter_current(it->rdatasetit, &it->rdataset); 215170222Sdougb it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER; 216135446Strhodes it->result = dns_rdataset_first(&it->rdataset); 217135446Strhodes return (it->result); 218135446Strhodes } 219135446Strhodes return (it->result); 220135446Strhodes} 221135446Strhodes 222135446Strhodes 223135446Strhodesstatic isc_result_t 224135446Strhodesdb_rr_iterator_next(db_rr_iterator_t *it) { 225135446Strhodes if (it->result != ISC_R_SUCCESS) 226135446Strhodes return (it->result); 227135446Strhodes 228135446Strhodes INSIST(it->dbit != NULL); 229135446Strhodes INSIST(it->node != NULL); 230135446Strhodes INSIST(it->rdatasetit != NULL); 231135446Strhodes 232135446Strhodes it->result = dns_rdataset_next(&it->rdataset); 233135446Strhodes if (it->result == ISC_R_NOMORE) { 234135446Strhodes dns_rdataset_disassociate(&it->rdataset); 235135446Strhodes it->result = dns_rdatasetiter_next(it->rdatasetit); 236135446Strhodes /* 237135446Strhodes * The while loop body is executed more than once 238135446Strhodes * only when an empty dbnode needs to be skipped. 239135446Strhodes */ 240135446Strhodes while (it->result == ISC_R_NOMORE) { 241135446Strhodes dns_rdatasetiter_destroy(&it->rdatasetit); 242135446Strhodes dns_db_detachnode(it->db, &it->node); 243135446Strhodes it->result = dns_dbiterator_next(it->dbit); 244135446Strhodes if (it->result == ISC_R_NOMORE) { 245135446Strhodes /* We are at the end of the entire database. */ 246135446Strhodes return (it->result); 247135446Strhodes } 248135446Strhodes if (it->result != ISC_R_SUCCESS) 249135446Strhodes return (it->result); 250135446Strhodes it->result = dns_dbiterator_current(it->dbit, 251135446Strhodes &it->node, 252135446Strhodes dns_fixedname_name(&it->fixedname)); 253135446Strhodes if (it->result != ISC_R_SUCCESS) 254135446Strhodes return (it->result); 255135446Strhodes it->result = dns_db_allrdatasets(it->db, it->node, 256135446Strhodes it->ver, it->now, 257135446Strhodes &it->rdatasetit); 258135446Strhodes if (it->result != ISC_R_SUCCESS) 259135446Strhodes return (it->result); 260135446Strhodes it->result = dns_rdatasetiter_first(it->rdatasetit); 261135446Strhodes } 262135446Strhodes if (it->result != ISC_R_SUCCESS) 263135446Strhodes return (it->result); 264135446Strhodes dns_rdatasetiter_current(it->rdatasetit, &it->rdataset); 265170222Sdougb it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER; 266135446Strhodes it->result = dns_rdataset_first(&it->rdataset); 267135446Strhodes if (it->result != ISC_R_SUCCESS) 268135446Strhodes return (it->result); 269135446Strhodes } 270135446Strhodes return (it->result); 271135446Strhodes} 272135446Strhodes 273135446Strhodesstatic void 274135446Strhodesdb_rr_iterator_pause(db_rr_iterator_t *it) { 275135446Strhodes RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS); 276135446Strhodes} 277135446Strhodes 278135446Strhodesstatic void 279135446Strhodesdb_rr_iterator_destroy(db_rr_iterator_t *it) { 280135446Strhodes if (dns_rdataset_isassociated(&it->rdataset)) 281135446Strhodes dns_rdataset_disassociate(&it->rdataset); 282135446Strhodes if (it->rdatasetit != NULL) 283135446Strhodes dns_rdatasetiter_destroy(&it->rdatasetit); 284135446Strhodes if (it->node != NULL) 285135446Strhodes dns_db_detachnode(it->db, &it->node); 286135446Strhodes dns_dbiterator_destroy(&it->dbit); 287135446Strhodes} 288135446Strhodes 289135446Strhodesstatic void 290135446Strhodesdb_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name, 291135446Strhodes isc_uint32_t *ttl, dns_rdata_t **rdata) 292135446Strhodes{ 293135446Strhodes REQUIRE(name != NULL && *name == NULL); 294135446Strhodes REQUIRE(it->result == ISC_R_SUCCESS); 295135446Strhodes *name = dns_fixedname_name(&it->fixedname); 296135446Strhodes *ttl = it->rdataset.ttl; 297135446Strhodes dns_rdata_reset(&it->rdata); 298135446Strhodes dns_rdataset_current(&it->rdataset, &it->rdata); 299135446Strhodes *rdata = &it->rdata; 300135446Strhodes} 301135446Strhodes 302135446Strhodes/**************************************************************************/ 303135446Strhodes 304170222Sdougb/*% Log an RR (for debugging) */ 305135446Strhodes 306135446Strhodesstatic void 307135446Strhodeslog_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) { 308135446Strhodes isc_result_t result; 309135446Strhodes isc_buffer_t buf; 310135446Strhodes char mem[2000]; 311135446Strhodes dns_rdatalist_t rdl; 312135446Strhodes dns_rdataset_t rds; 313135446Strhodes dns_rdata_t rd = DNS_RDATA_INIT; 314135446Strhodes 315135446Strhodes rdl.type = rdata->type; 316135446Strhodes rdl.rdclass = rdata->rdclass; 317135446Strhodes rdl.ttl = ttl; 318193149Sdougb if (rdata->type == dns_rdatatype_sig || 319193149Sdougb rdata->type == dns_rdatatype_rrsig) 320193149Sdougb rdl.covers = dns_rdata_covers(rdata); 321193149Sdougb else 322193149Sdougb rdl.covers = dns_rdatatype_none; 323135446Strhodes ISC_LIST_INIT(rdl.rdata); 324135446Strhodes ISC_LINK_INIT(&rdl, link); 325135446Strhodes dns_rdataset_init(&rds); 326135446Strhodes dns_rdata_init(&rd); 327135446Strhodes dns_rdata_clone(rdata, &rd); 328135446Strhodes ISC_LIST_APPEND(rdl.rdata, &rd, link); 329135446Strhodes RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS); 330135446Strhodes 331135446Strhodes isc_buffer_init(&buf, mem, sizeof(mem)); 332135446Strhodes result = dns_rdataset_totext(&rds, name, 333135446Strhodes ISC_FALSE, ISC_FALSE, &buf); 334135446Strhodes 335135446Strhodes /* 336135446Strhodes * We could use xfrout_log(), but that would produce 337135446Strhodes * very long lines with a repetitive prefix. 338135446Strhodes */ 339135446Strhodes if (result == ISC_R_SUCCESS) { 340135446Strhodes /* 341135446Strhodes * Get rid of final newline. 342135446Strhodes */ 343135446Strhodes INSIST(buf.used >= 1 && 344135446Strhodes ((char *) buf.base)[buf.used - 1] == '\n'); 345135446Strhodes buf.used--; 346193149Sdougb 347135446Strhodes isc_log_write(XFROUT_RR_LOGARGS, "%.*s", 348135446Strhodes (int)isc_buffer_usedlength(&buf), 349135446Strhodes (char *)isc_buffer_base(&buf)); 350135446Strhodes } else { 351135446Strhodes isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>"); 352135446Strhodes } 353135446Strhodes} 354135446Strhodes 355135446Strhodes/**************************************************************************/ 356135446Strhodes/* 357135446Strhodes * An 'rrstream_t' is a polymorphic iterator that returns 358135446Strhodes * a stream of resource records. There are multiple implementations, 359135446Strhodes * e.g. for generating AXFR and IXFR records streams. 360135446Strhodes */ 361135446Strhodes 362135446Strhodestypedef struct rrstream_methods rrstream_methods_t; 363135446Strhodes 364135446Strhodestypedef struct rrstream { 365135446Strhodes isc_mem_t *mctx; 366135446Strhodes rrstream_methods_t *methods; 367135446Strhodes} rrstream_t; 368135446Strhodes 369135446Strhodesstruct rrstream_methods { 370135446Strhodes isc_result_t (*first)(rrstream_t *); 371135446Strhodes isc_result_t (*next)(rrstream_t *); 372135446Strhodes void (*current)(rrstream_t *, 373135446Strhodes dns_name_t **, 374135446Strhodes isc_uint32_t *, 375135446Strhodes dns_rdata_t **); 376135446Strhodes void (*pause)(rrstream_t *); 377135446Strhodes void (*destroy)(rrstream_t **); 378135446Strhodes}; 379135446Strhodes 380135446Strhodesstatic void 381135446Strhodesrrstream_noop_pause(rrstream_t *rs) { 382135446Strhodes UNUSED(rs); 383135446Strhodes} 384135446Strhodes 385135446Strhodes/**************************************************************************/ 386135446Strhodes/* 387135446Strhodes * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns 388135446Strhodes * an IXFR-like RR stream from a journal file. 389135446Strhodes * 390135446Strhodes * The SOA at the beginning of each sequence of additions 391135446Strhodes * or deletions are included in the stream, but the extra 392135446Strhodes * SOAs at the beginning and end of the entire transfer are 393135446Strhodes * not included. 394135446Strhodes */ 395135446Strhodes 396135446Strhodestypedef struct ixfr_rrstream { 397135446Strhodes rrstream_t common; 398135446Strhodes dns_journal_t *journal; 399135446Strhodes} ixfr_rrstream_t; 400135446Strhodes 401135446Strhodes/* Forward declarations. */ 402135446Strhodesstatic void 403135446Strhodesixfr_rrstream_destroy(rrstream_t **sp); 404135446Strhodes 405135446Strhodesstatic rrstream_methods_t ixfr_rrstream_methods; 406135446Strhodes 407135446Strhodes/* 408135446Strhodes * Returns: anything dns_journal_open() or dns_journal_iter_init() 409135446Strhodes * may return. 410135446Strhodes */ 411135446Strhodes 412135446Strhodesstatic isc_result_t 413135446Strhodesixfr_rrstream_create(isc_mem_t *mctx, 414135446Strhodes const char *journal_filename, 415135446Strhodes isc_uint32_t begin_serial, 416135446Strhodes isc_uint32_t end_serial, 417135446Strhodes rrstream_t **sp) 418135446Strhodes{ 419135446Strhodes ixfr_rrstream_t *s; 420135446Strhodes isc_result_t result; 421135446Strhodes 422135446Strhodes INSIST(sp != NULL && *sp == NULL); 423135446Strhodes 424135446Strhodes s = isc_mem_get(mctx, sizeof(*s)); 425135446Strhodes if (s == NULL) 426135446Strhodes return (ISC_R_NOMEMORY); 427135446Strhodes s->common.mctx = mctx; 428135446Strhodes s->common.methods = &ixfr_rrstream_methods; 429135446Strhodes s->journal = NULL; 430135446Strhodes 431135446Strhodes CHECK(dns_journal_open(mctx, journal_filename, 432135446Strhodes ISC_FALSE, &s->journal)); 433135446Strhodes CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial)); 434135446Strhodes 435135446Strhodes *sp = (rrstream_t *) s; 436135446Strhodes return (ISC_R_SUCCESS); 437135446Strhodes 438135446Strhodes failure: 439135446Strhodes ixfr_rrstream_destroy((rrstream_t **) (void *)&s); 440135446Strhodes return (result); 441135446Strhodes} 442135446Strhodes 443135446Strhodesstatic isc_result_t 444135446Strhodesixfr_rrstream_first(rrstream_t *rs) { 445135446Strhodes ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; 446135446Strhodes return (dns_journal_first_rr(s->journal)); 447135446Strhodes} 448135446Strhodes 449135446Strhodesstatic isc_result_t 450135446Strhodesixfr_rrstream_next(rrstream_t *rs) { 451135446Strhodes ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; 452135446Strhodes return (dns_journal_next_rr(s->journal)); 453135446Strhodes} 454135446Strhodes 455135446Strhodesstatic void 456135446Strhodesixfr_rrstream_current(rrstream_t *rs, 457135446Strhodes dns_name_t **name, isc_uint32_t *ttl, 458135446Strhodes dns_rdata_t **rdata) 459135446Strhodes{ 460135446Strhodes ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs; 461135446Strhodes dns_journal_current_rr(s->journal, name, ttl, rdata); 462135446Strhodes} 463135446Strhodes 464135446Strhodesstatic void 465135446Strhodesixfr_rrstream_destroy(rrstream_t **rsp) { 466135446Strhodes ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp; 467135446Strhodes if (s->journal != 0) 468135446Strhodes dns_journal_destroy(&s->journal); 469135446Strhodes isc_mem_put(s->common.mctx, s, sizeof(*s)); 470135446Strhodes} 471135446Strhodes 472135446Strhodesstatic rrstream_methods_t ixfr_rrstream_methods = { 473135446Strhodes ixfr_rrstream_first, 474135446Strhodes ixfr_rrstream_next, 475135446Strhodes ixfr_rrstream_current, 476135446Strhodes rrstream_noop_pause, 477135446Strhodes ixfr_rrstream_destroy 478135446Strhodes}; 479135446Strhodes 480135446Strhodes/**************************************************************************/ 481135446Strhodes/* 482135446Strhodes * An 'axfr_rrstream_t' is an 'rrstream_t' that returns 483135446Strhodes * an AXFR-like RR stream from a database. 484135446Strhodes * 485135446Strhodes * The SOAs at the beginning and end of the transfer are 486135446Strhodes * not included in the stream. 487135446Strhodes */ 488135446Strhodes 489135446Strhodestypedef struct axfr_rrstream { 490135446Strhodes rrstream_t common; 491135446Strhodes db_rr_iterator_t it; 492135446Strhodes isc_boolean_t it_valid; 493135446Strhodes} axfr_rrstream_t; 494135446Strhodes 495135446Strhodes/* 496135446Strhodes * Forward declarations. 497135446Strhodes */ 498135446Strhodesstatic void 499135446Strhodesaxfr_rrstream_destroy(rrstream_t **rsp); 500135446Strhodes 501135446Strhodesstatic rrstream_methods_t axfr_rrstream_methods; 502135446Strhodes 503135446Strhodesstatic isc_result_t 504135446Strhodesaxfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, 505135446Strhodes rrstream_t **sp) 506135446Strhodes{ 507135446Strhodes axfr_rrstream_t *s; 508135446Strhodes isc_result_t result; 509135446Strhodes 510135446Strhodes INSIST(sp != NULL && *sp == NULL); 511135446Strhodes 512135446Strhodes s = isc_mem_get(mctx, sizeof(*s)); 513135446Strhodes if (s == NULL) 514135446Strhodes return (ISC_R_NOMEMORY); 515135446Strhodes s->common.mctx = mctx; 516135446Strhodes s->common.methods = &axfr_rrstream_methods; 517135446Strhodes s->it_valid = ISC_FALSE; 518135446Strhodes 519135446Strhodes CHECK(db_rr_iterator_init(&s->it, db, ver, 0)); 520135446Strhodes s->it_valid = ISC_TRUE; 521135446Strhodes 522135446Strhodes *sp = (rrstream_t *) s; 523135446Strhodes return (ISC_R_SUCCESS); 524135446Strhodes 525135446Strhodes failure: 526135446Strhodes axfr_rrstream_destroy((rrstream_t **) (void *)&s); 527135446Strhodes return (result); 528135446Strhodes} 529135446Strhodes 530135446Strhodesstatic isc_result_t 531135446Strhodesaxfr_rrstream_first(rrstream_t *rs) { 532135446Strhodes axfr_rrstream_t *s = (axfr_rrstream_t *) rs; 533135446Strhodes isc_result_t result; 534135446Strhodes result = db_rr_iterator_first(&s->it); 535135446Strhodes if (result != ISC_R_SUCCESS) 536135446Strhodes return (result); 537135446Strhodes /* Skip SOA records. */ 538135446Strhodes for (;;) { 539135446Strhodes dns_name_t *name_dummy = NULL; 540135446Strhodes isc_uint32_t ttl_dummy; 541135446Strhodes dns_rdata_t *rdata = NULL; 542135446Strhodes db_rr_iterator_current(&s->it, &name_dummy, 543135446Strhodes &ttl_dummy, &rdata); 544135446Strhodes if (rdata->type != dns_rdatatype_soa) 545135446Strhodes break; 546135446Strhodes result = db_rr_iterator_next(&s->it); 547135446Strhodes if (result != ISC_R_SUCCESS) 548135446Strhodes break; 549135446Strhodes } 550135446Strhodes return (result); 551135446Strhodes} 552135446Strhodes 553135446Strhodesstatic isc_result_t 554135446Strhodesaxfr_rrstream_next(rrstream_t *rs) { 555135446Strhodes axfr_rrstream_t *s = (axfr_rrstream_t *) rs; 556135446Strhodes isc_result_t result; 557135446Strhodes 558135446Strhodes /* Skip SOA records. */ 559135446Strhodes for (;;) { 560135446Strhodes dns_name_t *name_dummy = NULL; 561135446Strhodes isc_uint32_t ttl_dummy; 562135446Strhodes dns_rdata_t *rdata = NULL; 563135446Strhodes result = db_rr_iterator_next(&s->it); 564135446Strhodes if (result != ISC_R_SUCCESS) 565135446Strhodes break; 566135446Strhodes db_rr_iterator_current(&s->it, &name_dummy, 567135446Strhodes &ttl_dummy, &rdata); 568135446Strhodes if (rdata->type != dns_rdatatype_soa) 569135446Strhodes break; 570135446Strhodes } 571135446Strhodes return (result); 572135446Strhodes} 573135446Strhodes 574135446Strhodesstatic void 575135446Strhodesaxfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, 576135446Strhodes dns_rdata_t **rdata) 577135446Strhodes{ 578135446Strhodes axfr_rrstream_t *s = (axfr_rrstream_t *) rs; 579135446Strhodes db_rr_iterator_current(&s->it, name, ttl, rdata); 580135446Strhodes} 581135446Strhodes 582135446Strhodesstatic void 583135446Strhodesaxfr_rrstream_pause(rrstream_t *rs) { 584135446Strhodes axfr_rrstream_t *s = (axfr_rrstream_t *) rs; 585135446Strhodes db_rr_iterator_pause(&s->it); 586135446Strhodes} 587135446Strhodes 588135446Strhodesstatic void 589135446Strhodesaxfr_rrstream_destroy(rrstream_t **rsp) { 590135446Strhodes axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp; 591135446Strhodes if (s->it_valid) 592135446Strhodes db_rr_iterator_destroy(&s->it); 593135446Strhodes isc_mem_put(s->common.mctx, s, sizeof(*s)); 594135446Strhodes} 595135446Strhodes 596135446Strhodesstatic rrstream_methods_t axfr_rrstream_methods = { 597135446Strhodes axfr_rrstream_first, 598135446Strhodes axfr_rrstream_next, 599135446Strhodes axfr_rrstream_current, 600135446Strhodes axfr_rrstream_pause, 601135446Strhodes axfr_rrstream_destroy 602135446Strhodes}; 603135446Strhodes 604135446Strhodes/**************************************************************************/ 605135446Strhodes/* 606135446Strhodes * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns 607135446Strhodes * a single SOA record. 608135446Strhodes */ 609135446Strhodes 610135446Strhodestypedef struct soa_rrstream { 611135446Strhodes rrstream_t common; 612135446Strhodes dns_difftuple_t *soa_tuple; 613135446Strhodes} soa_rrstream_t; 614135446Strhodes 615135446Strhodes/* 616135446Strhodes * Forward declarations. 617135446Strhodes */ 618135446Strhodesstatic void 619135446Strhodessoa_rrstream_destroy(rrstream_t **rsp); 620135446Strhodes 621135446Strhodesstatic rrstream_methods_t soa_rrstream_methods; 622135446Strhodes 623135446Strhodesstatic isc_result_t 624135446Strhodessoa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver, 625135446Strhodes rrstream_t **sp) 626135446Strhodes{ 627135446Strhodes soa_rrstream_t *s; 628135446Strhodes isc_result_t result; 629135446Strhodes 630135446Strhodes INSIST(sp != NULL && *sp == NULL); 631135446Strhodes 632135446Strhodes s = isc_mem_get(mctx, sizeof(*s)); 633135446Strhodes if (s == NULL) 634135446Strhodes return (ISC_R_NOMEMORY); 635135446Strhodes s->common.mctx = mctx; 636135446Strhodes s->common.methods = &soa_rrstream_methods; 637135446Strhodes s->soa_tuple = NULL; 638135446Strhodes 639135446Strhodes CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, 640135446Strhodes &s->soa_tuple)); 641135446Strhodes 642135446Strhodes *sp = (rrstream_t *) s; 643135446Strhodes return (ISC_R_SUCCESS); 644135446Strhodes 645135446Strhodes failure: 646135446Strhodes soa_rrstream_destroy((rrstream_t **) (void *)&s); 647135446Strhodes return (result); 648135446Strhodes} 649135446Strhodes 650135446Strhodesstatic isc_result_t 651135446Strhodessoa_rrstream_first(rrstream_t *rs) { 652135446Strhodes UNUSED(rs); 653135446Strhodes return (ISC_R_SUCCESS); 654135446Strhodes} 655135446Strhodes 656135446Strhodesstatic isc_result_t 657135446Strhodessoa_rrstream_next(rrstream_t *rs) { 658135446Strhodes UNUSED(rs); 659135446Strhodes return (ISC_R_NOMORE); 660135446Strhodes} 661135446Strhodes 662135446Strhodesstatic void 663135446Strhodessoa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, 664135446Strhodes dns_rdata_t **rdata) 665135446Strhodes{ 666135446Strhodes soa_rrstream_t *s = (soa_rrstream_t *) rs; 667135446Strhodes *name = &s->soa_tuple->name; 668135446Strhodes *ttl = s->soa_tuple->ttl; 669135446Strhodes *rdata = &s->soa_tuple->rdata; 670135446Strhodes} 671135446Strhodes 672135446Strhodesstatic void 673135446Strhodessoa_rrstream_destroy(rrstream_t **rsp) { 674135446Strhodes soa_rrstream_t *s = (soa_rrstream_t *) *rsp; 675135446Strhodes if (s->soa_tuple != NULL) 676135446Strhodes dns_difftuple_free(&s->soa_tuple); 677135446Strhodes isc_mem_put(s->common.mctx, s, sizeof(*s)); 678135446Strhodes} 679135446Strhodes 680135446Strhodesstatic rrstream_methods_t soa_rrstream_methods = { 681135446Strhodes soa_rrstream_first, 682135446Strhodes soa_rrstream_next, 683135446Strhodes soa_rrstream_current, 684135446Strhodes rrstream_noop_pause, 685135446Strhodes soa_rrstream_destroy 686135446Strhodes}; 687135446Strhodes 688135446Strhodes/**************************************************************************/ 689135446Strhodes/* 690135446Strhodes * A 'compound_rrstream_t' objects owns a soa_rrstream 691135446Strhodes * and another rrstream, the "data stream". It returns 692135446Strhodes * a concatenated stream consisting of the soa_rrstream, then 693135446Strhodes * the data stream, then the soa_rrstream again. 694135446Strhodes * 695135446Strhodes * The component streams are owned by the compound_rrstream_t 696135446Strhodes * and are destroyed with it. 697135446Strhodes */ 698135446Strhodes 699135446Strhodestypedef struct compound_rrstream { 700135446Strhodes rrstream_t common; 701135446Strhodes rrstream_t *components[3]; 702135446Strhodes int state; 703135446Strhodes isc_result_t result; 704135446Strhodes} compound_rrstream_t; 705135446Strhodes 706135446Strhodes/* 707135446Strhodes * Forward declarations. 708135446Strhodes */ 709135446Strhodesstatic void 710135446Strhodescompound_rrstream_destroy(rrstream_t **rsp); 711135446Strhodes 712135446Strhodesstatic isc_result_t 713135446Strhodescompound_rrstream_next(rrstream_t *rs); 714135446Strhodes 715135446Strhodesstatic rrstream_methods_t compound_rrstream_methods; 716135446Strhodes 717135446Strhodes/* 718135446Strhodes * Requires: 719135446Strhodes * soa_stream != NULL && *soa_stream != NULL 720135446Strhodes * data_stream != NULL && *data_stream != NULL 721135446Strhodes * sp != NULL && *sp == NULL 722135446Strhodes * 723135446Strhodes * Ensures: 724135446Strhodes * *soa_stream == NULL 725135446Strhodes * *data_stream == NULL 726135446Strhodes * *sp points to a valid compound_rrstream_t 727135446Strhodes * The soa and data streams will be destroyed 728135446Strhodes * when the compound_rrstream_t is destroyed. 729135446Strhodes */ 730135446Strhodesstatic isc_result_t 731135446Strhodescompound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream, 732135446Strhodes rrstream_t **data_stream, rrstream_t **sp) 733135446Strhodes{ 734135446Strhodes compound_rrstream_t *s; 735135446Strhodes 736135446Strhodes INSIST(sp != NULL && *sp == NULL); 737135446Strhodes 738135446Strhodes s = isc_mem_get(mctx, sizeof(*s)); 739135446Strhodes if (s == NULL) 740135446Strhodes return (ISC_R_NOMEMORY); 741135446Strhodes s->common.mctx = mctx; 742135446Strhodes s->common.methods = &compound_rrstream_methods; 743135446Strhodes s->components[0] = *soa_stream; 744135446Strhodes s->components[1] = *data_stream; 745135446Strhodes s->components[2] = *soa_stream; 746135446Strhodes s->state = -1; 747135446Strhodes s->result = ISC_R_FAILURE; 748135446Strhodes 749135446Strhodes *soa_stream = NULL; 750135446Strhodes *data_stream = NULL; 751135446Strhodes *sp = (rrstream_t *) s; 752135446Strhodes return (ISC_R_SUCCESS); 753135446Strhodes} 754135446Strhodes 755135446Strhodesstatic isc_result_t 756135446Strhodescompound_rrstream_first(rrstream_t *rs) { 757135446Strhodes compound_rrstream_t *s = (compound_rrstream_t *) rs; 758135446Strhodes s->state = 0; 759135446Strhodes do { 760135446Strhodes rrstream_t *curstream = s->components[s->state]; 761135446Strhodes s->result = curstream->methods->first(curstream); 762135446Strhodes } while (s->result == ISC_R_NOMORE && s->state < 2); 763135446Strhodes return (s->result); 764135446Strhodes} 765135446Strhodes 766135446Strhodesstatic isc_result_t 767135446Strhodescompound_rrstream_next(rrstream_t *rs) { 768135446Strhodes compound_rrstream_t *s = (compound_rrstream_t *) rs; 769135446Strhodes rrstream_t *curstream = s->components[s->state]; 770135446Strhodes s->result = curstream->methods->next(curstream); 771135446Strhodes while (s->result == ISC_R_NOMORE) { 772135446Strhodes /* 773135446Strhodes * Make sure locks held by the current stream 774135446Strhodes * are released before we switch streams. 775135446Strhodes */ 776135446Strhodes curstream->methods->pause(curstream); 777135446Strhodes if (s->state == 2) 778135446Strhodes return (ISC_R_NOMORE); 779135446Strhodes s->state++; 780135446Strhodes curstream = s->components[s->state]; 781135446Strhodes s->result = curstream->methods->first(curstream); 782135446Strhodes } 783135446Strhodes return (s->result); 784135446Strhodes} 785135446Strhodes 786135446Strhodesstatic void 787135446Strhodescompound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl, 788135446Strhodes dns_rdata_t **rdata) 789135446Strhodes{ 790135446Strhodes compound_rrstream_t *s = (compound_rrstream_t *) rs; 791135446Strhodes rrstream_t *curstream; 792135446Strhodes INSIST(0 <= s->state && s->state < 3); 793135446Strhodes INSIST(s->result == ISC_R_SUCCESS); 794135446Strhodes curstream = s->components[s->state]; 795135446Strhodes curstream->methods->current(curstream, name, ttl, rdata); 796135446Strhodes} 797135446Strhodes 798135446Strhodesstatic void 799135446Strhodescompound_rrstream_pause(rrstream_t *rs) 800135446Strhodes{ 801135446Strhodes compound_rrstream_t *s = (compound_rrstream_t *) rs; 802135446Strhodes rrstream_t *curstream; 803135446Strhodes INSIST(0 <= s->state && s->state < 3); 804135446Strhodes curstream = s->components[s->state]; 805135446Strhodes curstream->methods->pause(curstream); 806135446Strhodes} 807135446Strhodes 808135446Strhodesstatic void 809135446Strhodescompound_rrstream_destroy(rrstream_t **rsp) { 810135446Strhodes compound_rrstream_t *s = (compound_rrstream_t *) *rsp; 811135446Strhodes s->components[0]->methods->destroy(&s->components[0]); 812135446Strhodes s->components[1]->methods->destroy(&s->components[1]); 813135446Strhodes s->components[2] = NULL; /* Copy of components[0]. */ 814135446Strhodes isc_mem_put(s->common.mctx, s, sizeof(*s)); 815135446Strhodes} 816135446Strhodes 817135446Strhodesstatic rrstream_methods_t compound_rrstream_methods = { 818135446Strhodes compound_rrstream_first, 819135446Strhodes compound_rrstream_next, 820135446Strhodes compound_rrstream_current, 821135446Strhodes compound_rrstream_pause, 822135446Strhodes compound_rrstream_destroy 823135446Strhodes}; 824135446Strhodes 825135446Strhodes/**************************************************************************/ 826135446Strhodes/* 827135446Strhodes * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR 828135446Strhodes * in progress. 829135446Strhodes */ 830135446Strhodes 831135446Strhodestypedef struct { 832135446Strhodes isc_mem_t *mctx; 833135446Strhodes ns_client_t *client; 834135446Strhodes unsigned int id; /* ID of request */ 835135446Strhodes dns_name_t *qname; /* Question name of request */ 836135446Strhodes dns_rdatatype_t qtype; /* dns_rdatatype_{a,i}xfr */ 837135446Strhodes dns_rdataclass_t qclass; 838193149Sdougb dns_zone_t *zone; /* (necessary for stats) */ 839135446Strhodes dns_db_t *db; 840135446Strhodes dns_dbversion_t *ver; 841135446Strhodes isc_quota_t *quota; 842135446Strhodes rrstream_t *stream; /* The XFR RR stream */ 843135446Strhodes isc_boolean_t end_of_stream; /* EOS has been reached */ 844135446Strhodes isc_buffer_t buf; /* Buffer for message owner 845135446Strhodes names and rdatas */ 846135446Strhodes isc_buffer_t txlenbuf; /* Transmit length buffer */ 847135446Strhodes isc_buffer_t txbuf; /* Transmit message buffer */ 848135446Strhodes void *txmem; 849135446Strhodes unsigned int txmemlen; 850135446Strhodes unsigned int nmsg; /* Number of messages sent */ 851135446Strhodes dns_tsigkey_t *tsigkey; /* Key used to create TSIG */ 852135446Strhodes isc_buffer_t *lasttsig; /* the last TSIG */ 853135446Strhodes isc_boolean_t many_answers; 854135446Strhodes int sends; /* Send in progress */ 855135446Strhodes isc_boolean_t shuttingdown; 856135446Strhodes const char *mnemonic; /* Style of transfer */ 857135446Strhodes} xfrout_ctx_t; 858135446Strhodes 859135446Strhodesstatic isc_result_t 860135446Strhodesxfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, 861135446Strhodes unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype, 862193149Sdougb dns_rdataclass_t qclass, dns_zone_t *zone, 863135446Strhodes dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota, 864135446Strhodes rrstream_t *stream, dns_tsigkey_t *tsigkey, 865135446Strhodes isc_buffer_t *lasttsig, 866135446Strhodes unsigned int maxtime, 867135446Strhodes unsigned int idletime, 868135446Strhodes isc_boolean_t many_answers, 869135446Strhodes xfrout_ctx_t **xfrp); 870135446Strhodes 871135446Strhodesstatic void 872135446Strhodessendstream(xfrout_ctx_t *xfr); 873135446Strhodes 874135446Strhodesstatic void 875135446Strhodesxfrout_senddone(isc_task_t *task, isc_event_t *event); 876135446Strhodes 877135446Strhodesstatic void 878135446Strhodesxfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg); 879135446Strhodes 880135446Strhodesstatic void 881135446Strhodesxfrout_maybe_destroy(xfrout_ctx_t *xfr); 882135446Strhodes 883135446Strhodesstatic void 884135446Strhodesxfrout_ctx_destroy(xfrout_ctx_t **xfrp); 885135446Strhodes 886135446Strhodesstatic void 887135446Strhodesxfrout_client_shutdown(void *arg, isc_result_t result); 888135446Strhodes 889135446Strhodesstatic void 890135446Strhodesxfrout_log1(ns_client_t *client, dns_name_t *zonename, 891135446Strhodes dns_rdataclass_t rdclass, int level, 892135446Strhodes const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6); 893135446Strhodes 894135446Strhodesstatic void 895153816Sdougbxfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) 896135446Strhodes ISC_FORMAT_PRINTF(3, 4); 897135446Strhodes 898135446Strhodes/**************************************************************************/ 899135446Strhodes 900135446Strhodesvoid 901135446Strhodesns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { 902135446Strhodes isc_result_t result; 903135446Strhodes dns_name_t *question_name; 904135446Strhodes dns_rdataset_t *question_rdataset; 905135446Strhodes dns_zone_t *zone = NULL; 906135446Strhodes dns_db_t *db = NULL; 907135446Strhodes dns_dbversion_t *ver = NULL; 908135446Strhodes dns_rdataclass_t question_class; 909135446Strhodes rrstream_t *soa_stream = NULL; 910135446Strhodes rrstream_t *data_stream = NULL; 911135446Strhodes rrstream_t *stream = NULL; 912135446Strhodes dns_difftuple_t *current_soa_tuple = NULL; 913135446Strhodes dns_name_t *soa_name; 914135446Strhodes dns_rdataset_t *soa_rdataset; 915135446Strhodes dns_rdata_t soa_rdata = DNS_RDATA_INIT; 916135446Strhodes isc_boolean_t have_soa = ISC_FALSE; 917135446Strhodes const char *mnemonic = NULL; 918135446Strhodes isc_mem_t *mctx = client->mctx; 919135446Strhodes dns_message_t *request = client->message; 920135446Strhodes xfrout_ctx_t *xfr = NULL; 921135446Strhodes isc_quota_t *quota = NULL; 922135446Strhodes dns_transfer_format_t format = client->view->transfer_format; 923135446Strhodes isc_netaddr_t na; 924135446Strhodes dns_peer_t *peer = NULL; 925135446Strhodes isc_buffer_t *tsigbuf = NULL; 926135446Strhodes char *journalfile; 927135446Strhodes char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; 928135446Strhodes char keyname[DNS_NAME_FORMATSIZE]; 929135446Strhodes isc_boolean_t is_poll = ISC_FALSE; 930170222Sdougb#ifdef DLZ 931170222Sdougb isc_boolean_t is_dlz = ISC_FALSE; 932170222Sdougb#endif 933135446Strhodes 934135446Strhodes switch (reqtype) { 935135446Strhodes case dns_rdatatype_axfr: 936135446Strhodes mnemonic = "AXFR"; 937135446Strhodes break; 938135446Strhodes case dns_rdatatype_ixfr: 939135446Strhodes mnemonic = "IXFR"; 940135446Strhodes break; 941135446Strhodes default: 942135446Strhodes INSIST(0); 943135446Strhodes break; 944135446Strhodes } 945135446Strhodes 946135446Strhodes ns_client_log(client, 947135446Strhodes DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, 948135446Strhodes ISC_LOG_DEBUG(6), "%s request", mnemonic); 949135446Strhodes /* 950135446Strhodes * Apply quota. 951135446Strhodes */ 952135446Strhodes result = isc_quota_attach(&ns_g_server->xfroutquota, "a); 953135446Strhodes if (result != ISC_R_SUCCESS) { 954135446Strhodes isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, 955135446Strhodes "%s request denied: %s", mnemonic, 956135446Strhodes isc_result_totext(result)); 957135446Strhodes goto failure; 958135446Strhodes } 959135446Strhodes 960135446Strhodes /* 961135446Strhodes * Interpret the question section. 962135446Strhodes */ 963135446Strhodes result = dns_message_firstname(request, DNS_SECTION_QUESTION); 964135446Strhodes INSIST(result == ISC_R_SUCCESS); 965135446Strhodes 966135446Strhodes /* 967135446Strhodes * The question section must contain exactly one question, and 968135446Strhodes * it must be for AXFR/IXFR as appropriate. 969135446Strhodes */ 970135446Strhodes question_name = NULL; 971135446Strhodes dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name); 972135446Strhodes question_rdataset = ISC_LIST_HEAD(question_name->list); 973135446Strhodes question_class = question_rdataset->rdclass; 974135446Strhodes INSIST(question_rdataset->type == reqtype); 975135446Strhodes if (ISC_LIST_NEXT(question_rdataset, link) != NULL) 976135446Strhodes FAILC(DNS_R_FORMERR, "multiple questions"); 977135446Strhodes result = dns_message_nextname(request, DNS_SECTION_QUESTION); 978135446Strhodes if (result != ISC_R_NOMORE) 979135446Strhodes FAILC(DNS_R_FORMERR, "multiple questions"); 980135446Strhodes 981135446Strhodes result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, 982135446Strhodes &zone); 983170222Sdougb 984135446Strhodes if (result != ISC_R_SUCCESS) 985170222Sdougb#ifdef DLZ 986170222Sdougb { 987170222Sdougb /* 988218384Sdougb * Normal zone table does not have a match. 989218384Sdougb * Try the DLZ database 990170222Sdougb */ 991193149Sdougb if (client->view->dlzdatabase != NULL) { 992170222Sdougb result = dns_dlzallowzonexfr(client->view, 993218384Sdougb question_name, 994218384Sdougb &client->peeraddr, 995170222Sdougb &db); 996170222Sdougb 997170222Sdougb if (result == ISC_R_NOPERM) { 998170222Sdougb char _buf1[DNS_NAME_FORMATSIZE]; 999170222Sdougb char _buf2[DNS_RDATACLASS_FORMATSIZE]; 1000170222Sdougb 1001170222Sdougb result = DNS_R_REFUSED; 1002170222Sdougb dns_name_format(question_name, _buf1, 1003170222Sdougb sizeof(_buf1)); 1004170222Sdougb dns_rdataclass_format(question_class, 1005170222Sdougb _buf2, sizeof(_buf2)); 1006170222Sdougb ns_client_log(client, DNS_LOGCATEGORY_SECURITY, 1007170222Sdougb NS_LOGMODULE_XFER_OUT, 1008170222Sdougb ISC_LOG_ERROR, 1009170222Sdougb "zone transfer '%s/%s' denied", 1010170222Sdougb _buf1, _buf2); 1011170222Sdougb goto failure; 1012170222Sdougb } 1013170222Sdougb if (result != ISC_R_SUCCESS) 1014170222Sdougb#endif 1015170222Sdougb FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", 1016170222Sdougb question_name, question_class); 1017170222Sdougb#ifdef DLZ 1018170222Sdougb is_dlz = ISC_TRUE; 1019170222Sdougb /* 1020170222Sdougb * DLZ only support full zone transfer, not incremental 1021170222Sdougb */ 1022170222Sdougb if (reqtype != dns_rdatatype_axfr) { 1023170222Sdougb mnemonic = "AXFR-style IXFR"; 1024170222Sdougb reqtype = dns_rdatatype_axfr; 1025170222Sdougb } 1026170222Sdougb 1027170222Sdougb } else { 1028170222Sdougb /* 1029193149Sdougb * not DLZ and not in normal zone table, we are 1030170222Sdougb * not authoritative 1031170222Sdougb */ 1032170222Sdougb FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", 1033170222Sdougb question_name, question_class); 1034170222Sdougb } 1035170222Sdougb } else { 1036170222Sdougb /* zone table has a match */ 1037170222Sdougb#endif 1038170222Sdougb switch(dns_zone_gettype(zone)) { 1039170222Sdougb case dns_zone_master: 1040170222Sdougb case dns_zone_slave: 1041170222Sdougb break; /* Master and slave zones are OK for transfer. */ 1042170222Sdougb default: 1043170222Sdougb FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); 1044170222Sdougb } 1045170222Sdougb CHECK(dns_zone_getdb(zone, &db)); 1046170222Sdougb dns_db_currentversion(db, &ver); 1047170222Sdougb#ifdef DLZ 1048135446Strhodes } 1049170222Sdougb#endif 1050135446Strhodes 1051135446Strhodes xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), 1052135446Strhodes "%s question section OK", mnemonic); 1053135446Strhodes 1054135446Strhodes /* 1055135446Strhodes * Check the authority section. Look for a SOA record with 1056135446Strhodes * the same name and class as the question. 1057135446Strhodes */ 1058135446Strhodes for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY); 1059135446Strhodes result == ISC_R_SUCCESS; 1060135446Strhodes result = dns_message_nextname(request, DNS_SECTION_AUTHORITY)) 1061135446Strhodes { 1062135446Strhodes soa_name = NULL; 1063135446Strhodes dns_message_currentname(request, DNS_SECTION_AUTHORITY, 1064135446Strhodes &soa_name); 1065135446Strhodes 1066135446Strhodes /* 1067135446Strhodes * Ignore data whose owner name is not the zone apex. 1068135446Strhodes */ 1069135446Strhodes if (! dns_name_equal(soa_name, question_name)) 1070135446Strhodes continue; 1071135446Strhodes 1072135446Strhodes for (soa_rdataset = ISC_LIST_HEAD(soa_name->list); 1073135446Strhodes soa_rdataset != NULL; 1074135446Strhodes soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link)) 1075135446Strhodes { 1076135446Strhodes /* 1077135446Strhodes * Ignore non-SOA data. 1078135446Strhodes */ 1079135446Strhodes if (soa_rdataset->type != dns_rdatatype_soa) 1080135446Strhodes continue; 1081135446Strhodes if (soa_rdataset->rdclass != question_class) 1082135446Strhodes continue; 1083135446Strhodes 1084135446Strhodes CHECK(dns_rdataset_first(soa_rdataset)); 1085135446Strhodes dns_rdataset_current(soa_rdataset, &soa_rdata); 1086135446Strhodes result = dns_rdataset_next(soa_rdataset); 1087135446Strhodes if (result == ISC_R_SUCCESS) 1088135446Strhodes FAILC(DNS_R_FORMERR, 1089135446Strhodes "IXFR authority section " 1090135446Strhodes "has multiple SOAs"); 1091135446Strhodes have_soa = ISC_TRUE; 1092135446Strhodes goto got_soa; 1093135446Strhodes } 1094135446Strhodes } 1095135446Strhodes got_soa: 1096135446Strhodes if (result != ISC_R_NOMORE) 1097135446Strhodes CHECK(result); 1098135446Strhodes 1099135446Strhodes xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), 1100135446Strhodes "%s authority section OK", mnemonic); 1101135446Strhodes 1102135446Strhodes /* 1103135446Strhodes * Decide whether to allow this transfer. 1104135446Strhodes */ 1105170222Sdougb#ifdef DLZ 1106170222Sdougb /* 1107170222Sdougb * if not a DLZ zone decide whether to allow this transfer. 1108170222Sdougb */ 1109170222Sdougb if (!is_dlz) { 1110170222Sdougb#endif 1111170222Sdougb ns_client_aclmsg("zone transfer", question_name, reqtype, 1112170222Sdougb client->view->rdclass, msg, sizeof(msg)); 1113193149Sdougb CHECK(ns_client_checkacl(client, NULL, msg, 1114193149Sdougb dns_zone_getxfracl(zone), 1115193149Sdougb ISC_TRUE, ISC_LOG_ERROR)); 1116170222Sdougb#ifdef DLZ 1117170222Sdougb } 1118170222Sdougb#endif 1119135446Strhodes 1120135446Strhodes /* 1121135446Strhodes * AXFR over UDP is not possible. 1122135446Strhodes */ 1123135446Strhodes if (reqtype == dns_rdatatype_axfr && 1124135446Strhodes (client->attributes & NS_CLIENTATTR_TCP) == 0) 1125135446Strhodes FAILC(DNS_R_FORMERR, "attempted AXFR over UDP"); 1126135446Strhodes 1127135446Strhodes /* 1128135446Strhodes * Look up the requesting server in the peer table. 1129135446Strhodes */ 1130135446Strhodes isc_netaddr_fromsockaddr(&na, &client->peeraddr); 1131135446Strhodes (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer); 1132135446Strhodes 1133135446Strhodes /* 1134135446Strhodes * Decide on the transfer format (one-answer or many-answers). 1135135446Strhodes */ 1136135446Strhodes if (peer != NULL) 1137135446Strhodes (void)dns_peer_gettransferformat(peer, &format); 1138135446Strhodes 1139135446Strhodes /* 1140135446Strhodes * Get a dynamically allocated copy of the current SOA. 1141135446Strhodes */ 1142170222Sdougb#ifdef DLZ 1143170222Sdougb if (is_dlz) 1144170222Sdougb dns_db_currentversion(db, &ver); 1145170222Sdougb#endif 1146135446Strhodes CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, 1147135446Strhodes ¤t_soa_tuple)); 1148135446Strhodes 1149135446Strhodes if (reqtype == dns_rdatatype_ixfr) { 1150135446Strhodes isc_uint32_t begin_serial, current_serial; 1151135446Strhodes isc_boolean_t provide_ixfr; 1152135446Strhodes 1153135446Strhodes /* 1154135446Strhodes * Outgoing IXFR may have been disabled for this peer 1155135446Strhodes * or globally. 1156135446Strhodes */ 1157135446Strhodes provide_ixfr = client->view->provideixfr; 1158135446Strhodes if (peer != NULL) 1159135446Strhodes (void) dns_peer_getprovideixfr(peer, &provide_ixfr); 1160135446Strhodes if (provide_ixfr == ISC_FALSE) 1161135446Strhodes goto axfr_fallback; 1162135446Strhodes 1163135446Strhodes if (! have_soa) 1164135446Strhodes FAILC(DNS_R_FORMERR, 1165135446Strhodes "IXFR request missing SOA"); 1166135446Strhodes 1167135446Strhodes begin_serial = dns_soa_getserial(&soa_rdata); 1168135446Strhodes current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); 1169135446Strhodes 1170135446Strhodes /* 1171135446Strhodes * RFC1995 says "If an IXFR query with the same or 1172135446Strhodes * newer version number than that of the server 1173135446Strhodes * is received, it is replied to with a single SOA 1174135446Strhodes * record of the server's current version, just as 1175135446Strhodes * in AXFR". The claim about AXFR is incorrect, 1176135446Strhodes * but other than that, we do as the RFC says. 1177135446Strhodes * 1178135446Strhodes * Sending a single SOA record is also how we refuse 1179135446Strhodes * IXFR over UDP (currently, we always do). 1180135446Strhodes */ 1181135446Strhodes if (DNS_SERIAL_GE(begin_serial, current_serial) || 1182135446Strhodes (client->attributes & NS_CLIENTATTR_TCP) == 0) 1183135446Strhodes { 1184135446Strhodes CHECK(soa_rrstream_create(mctx, db, ver, &stream)); 1185135446Strhodes is_poll = ISC_TRUE; 1186135446Strhodes goto have_stream; 1187135446Strhodes } 1188135446Strhodes journalfile = dns_zone_getjournal(zone); 1189135446Strhodes if (journalfile != NULL) 1190135446Strhodes result = ixfr_rrstream_create(mctx, 1191135446Strhodes journalfile, 1192135446Strhodes begin_serial, 1193135446Strhodes current_serial, 1194135446Strhodes &data_stream); 1195135446Strhodes else 1196135446Strhodes result = ISC_R_NOTFOUND; 1197135446Strhodes if (result == ISC_R_NOTFOUND || 1198135446Strhodes result == ISC_R_RANGE) { 1199135446Strhodes xfrout_log1(client, question_name, question_class, 1200135446Strhodes ISC_LOG_DEBUG(4), 1201135446Strhodes "IXFR version not in journal, " 1202135446Strhodes "falling back to AXFR"); 1203135446Strhodes mnemonic = "AXFR-style IXFR"; 1204135446Strhodes goto axfr_fallback; 1205135446Strhodes } 1206135446Strhodes CHECK(result); 1207135446Strhodes } else { 1208135446Strhodes axfr_fallback: 1209135446Strhodes CHECK(axfr_rrstream_create(mctx, db, ver, 1210135446Strhodes &data_stream)); 1211135446Strhodes } 1212135446Strhodes 1213135446Strhodes /* 1214193149Sdougb * Bracket the data stream with SOAs. 1215135446Strhodes */ 1216135446Strhodes CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream)); 1217135446Strhodes CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream, 1218135446Strhodes &stream)); 1219135446Strhodes soa_stream = NULL; 1220135446Strhodes data_stream = NULL; 1221135446Strhodes 1222135446Strhodes have_stream: 1223135446Strhodes CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf)); 1224135446Strhodes /* 1225135446Strhodes * Create the xfrout context object. This transfers the ownership 1226135446Strhodes * of "stream", "db", "ver", and "quota" to the xfrout context object. 1227135446Strhodes */ 1228170222Sdougb 1229170222Sdougb 1230170222Sdougb 1231170222Sdougb#ifdef DLZ 1232170222Sdougb if (is_dlz) 1233218384Sdougb CHECK(xfrout_ctx_create(mctx, client, request->id, 1234218384Sdougb question_name, reqtype, question_class, 1235218384Sdougb zone, db, ver, quota, stream, 1236193149Sdougb dns_message_gettsigkey(request), 1237193149Sdougb tsigbuf, 1238193149Sdougb 3600, 1239193149Sdougb 3600, 1240193149Sdougb (format == dns_many_answers) ? 1241193149Sdougb ISC_TRUE : ISC_FALSE, 1242193149Sdougb &xfr)); 1243193149Sdougb else 1244170222Sdougb#endif 1245218384Sdougb CHECK(xfrout_ctx_create(mctx, client, request->id, 1246218384Sdougb question_name, reqtype, question_class, 1247218384Sdougb zone, db, ver, quota, stream, 1248193149Sdougb dns_message_gettsigkey(request), 1249193149Sdougb tsigbuf, 1250193149Sdougb dns_zone_getmaxxfrout(zone), 1251193149Sdougb dns_zone_getidleout(zone), 1252193149Sdougb (format == dns_many_answers) ? 1253193149Sdougb ISC_TRUE : ISC_FALSE, 1254193149Sdougb &xfr)); 1255170222Sdougb 1256135446Strhodes xfr->mnemonic = mnemonic; 1257135446Strhodes stream = NULL; 1258135446Strhodes quota = NULL; 1259135446Strhodes 1260135446Strhodes CHECK(xfr->stream->methods->first(xfr->stream)); 1261135446Strhodes 1262135446Strhodes if (xfr->tsigkey != NULL) { 1263135446Strhodes dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname)); 1264135446Strhodes } else 1265135446Strhodes keyname[0] = '\0'; 1266135446Strhodes if (is_poll) 1267135446Strhodes xfrout_log1(client, question_name, question_class, 1268135446Strhodes ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s", 1269135446Strhodes (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); 1270135446Strhodes else 1271135446Strhodes xfrout_log1(client, question_name, question_class, 1272135446Strhodes ISC_LOG_INFO, "%s started%s%s", mnemonic, 1273135446Strhodes (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); 1274135446Strhodes 1275135446Strhodes /* 1276135446Strhodes * Hand the context over to sendstream(). Set xfr to NULL; 1277135446Strhodes * sendstream() is responsible for either passing the 1278135446Strhodes * context on to a later event handler or destroying it. 1279135446Strhodes */ 1280135446Strhodes sendstream(xfr); 1281135446Strhodes xfr = NULL; 1282135446Strhodes 1283135446Strhodes result = ISC_R_SUCCESS; 1284135446Strhodes 1285135446Strhodes failure: 1286193149Sdougb if (result == DNS_R_REFUSED) 1287193149Sdougb inc_stats(zone, dns_nsstatscounter_xfrrej); 1288135446Strhodes if (quota != NULL) 1289135446Strhodes isc_quota_detach("a); 1290135446Strhodes if (current_soa_tuple != NULL) 1291135446Strhodes dns_difftuple_free(¤t_soa_tuple); 1292135446Strhodes if (stream != NULL) 1293135446Strhodes stream->methods->destroy(&stream); 1294135446Strhodes if (soa_stream != NULL) 1295135446Strhodes soa_stream->methods->destroy(&soa_stream); 1296135446Strhodes if (data_stream != NULL) 1297135446Strhodes data_stream->methods->destroy(&data_stream); 1298135446Strhodes if (ver != NULL) 1299135446Strhodes dns_db_closeversion(db, &ver, ISC_FALSE); 1300135446Strhodes if (db != NULL) 1301135446Strhodes dns_db_detach(&db); 1302135446Strhodes if (zone != NULL) 1303135446Strhodes dns_zone_detach(&zone); 1304135446Strhodes /* XXX kludge */ 1305135446Strhodes if (xfr != NULL) { 1306135446Strhodes xfrout_fail(xfr, result, "setting up zone transfer"); 1307135446Strhodes } else if (result != ISC_R_SUCCESS) { 1308135446Strhodes ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, 1309135446Strhodes NS_LOGMODULE_XFER_OUT, 1310135446Strhodes ISC_LOG_DEBUG(3), "zone transfer setup failed"); 1311135446Strhodes ns_client_error(client, result); 1312135446Strhodes } 1313135446Strhodes} 1314135446Strhodes 1315135446Strhodesstatic isc_result_t 1316135446Strhodesxfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id, 1317135446Strhodes dns_name_t *qname, dns_rdatatype_t qtype, 1318193149Sdougb dns_rdataclass_t qclass, dns_zone_t *zone, 1319135446Strhodes dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota, 1320135446Strhodes rrstream_t *stream, dns_tsigkey_t *tsigkey, 1321135446Strhodes isc_buffer_t *lasttsig, unsigned int maxtime, 1322135446Strhodes unsigned int idletime, isc_boolean_t many_answers, 1323135446Strhodes xfrout_ctx_t **xfrp) 1324135446Strhodes{ 1325135446Strhodes xfrout_ctx_t *xfr; 1326135446Strhodes isc_result_t result; 1327135446Strhodes unsigned int len; 1328135446Strhodes void *mem; 1329135446Strhodes 1330135446Strhodes INSIST(xfrp != NULL && *xfrp == NULL); 1331135446Strhodes xfr = isc_mem_get(mctx, sizeof(*xfr)); 1332135446Strhodes if (xfr == NULL) 1333135446Strhodes return (ISC_R_NOMEMORY); 1334135446Strhodes xfr->mctx = mctx; 1335135446Strhodes xfr->client = NULL; 1336135446Strhodes ns_client_attach(client, &xfr->client); 1337135446Strhodes xfr->id = id; 1338135446Strhodes xfr->qname = qname; 1339135446Strhodes xfr->qtype = qtype; 1340135446Strhodes xfr->qclass = qclass; 1341193149Sdougb xfr->zone = NULL; 1342135446Strhodes xfr->db = NULL; 1343135446Strhodes xfr->ver = NULL; 1344193149Sdougb if (zone != NULL) /* zone will be NULL if it's DLZ */ 1345193149Sdougb dns_zone_attach(zone, &xfr->zone); 1346135446Strhodes dns_db_attach(db, &xfr->db); 1347135446Strhodes dns_db_attachversion(db, ver, &xfr->ver); 1348135446Strhodes xfr->end_of_stream = ISC_FALSE; 1349135446Strhodes xfr->tsigkey = tsigkey; 1350135446Strhodes xfr->lasttsig = lasttsig; 1351135446Strhodes xfr->txmem = NULL; 1352135446Strhodes xfr->txmemlen = 0; 1353135446Strhodes xfr->nmsg = 0; 1354135446Strhodes xfr->many_answers = many_answers, 1355135446Strhodes xfr->sends = 0; 1356135446Strhodes xfr->shuttingdown = ISC_FALSE; 1357135446Strhodes xfr->mnemonic = NULL; 1358135446Strhodes xfr->buf.base = NULL; 1359135446Strhodes xfr->buf.length = 0; 1360135446Strhodes xfr->txmem = NULL; 1361135446Strhodes xfr->txmemlen = 0; 1362135446Strhodes xfr->stream = NULL; 1363135446Strhodes xfr->quota = NULL; 1364135446Strhodes 1365135446Strhodes /* 1366135446Strhodes * Allocate a temporary buffer for the uncompressed response 1367135446Strhodes * message data. The size should be no more than 65535 bytes 1368135446Strhodes * so that the compressed data will fit in a TCP message, 1369135446Strhodes * and no less than 65535 bytes so that an almost maximum-sized 1370135446Strhodes * RR will fit. Note that although 65535-byte RRs are allowed 1371135446Strhodes * in principle, they cannot be zone-transferred (at least not 1372135446Strhodes * if uncompressible), because the message and RR headers would 1373135446Strhodes * push the size of the TCP message over the 65536 byte limit. 1374135446Strhodes */ 1375135446Strhodes len = 65535; 1376135446Strhodes mem = isc_mem_get(mctx, len); 1377135446Strhodes if (mem == NULL) { 1378135446Strhodes result = ISC_R_NOMEMORY; 1379135446Strhodes goto failure; 1380135446Strhodes } 1381135446Strhodes isc_buffer_init(&xfr->buf, mem, len); 1382135446Strhodes 1383135446Strhodes /* 1384135446Strhodes * Allocate another temporary buffer for the compressed 1385135446Strhodes * response message and its TCP length prefix. 1386135446Strhodes */ 1387135446Strhodes len = 2 + 65535; 1388135446Strhodes mem = isc_mem_get(mctx, len); 1389135446Strhodes if (mem == NULL) { 1390135446Strhodes result = ISC_R_NOMEMORY; 1391135446Strhodes goto failure; 1392135446Strhodes } 1393135446Strhodes isc_buffer_init(&xfr->txlenbuf, mem, 2); 1394135446Strhodes isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2); 1395135446Strhodes xfr->txmem = mem; 1396135446Strhodes xfr->txmemlen = len; 1397135446Strhodes 1398135446Strhodes CHECK(dns_timer_setidle(xfr->client->timer, 1399135446Strhodes maxtime, idletime, ISC_FALSE)); 1400135446Strhodes 1401135446Strhodes /* 1402135446Strhodes * Register a shutdown callback with the client, so that we 1403135446Strhodes * can stop the transfer immediately when the client task 1404135446Strhodes * gets a shutdown event. 1405135446Strhodes */ 1406135446Strhodes xfr->client->shutdown = xfrout_client_shutdown; 1407135446Strhodes xfr->client->shutdown_arg = xfr; 1408135446Strhodes /* 1409135446Strhodes * These MUST be after the last "goto failure;" / CHECK to 1410135446Strhodes * prevent a double free by the caller. 1411135446Strhodes */ 1412135446Strhodes xfr->quota = quota; 1413135446Strhodes xfr->stream = stream; 1414135446Strhodes 1415135446Strhodes *xfrp = xfr; 1416135446Strhodes return (ISC_R_SUCCESS); 1417135446Strhodes 1418135446Strhodesfailure: 1419135446Strhodes xfrout_ctx_destroy(&xfr); 1420135446Strhodes return (result); 1421135446Strhodes} 1422135446Strhodes 1423135446Strhodes 1424135446Strhodes/* 1425135446Strhodes * Arrange to send as much as we can of "stream" without blocking. 1426135446Strhodes * 1427135446Strhodes * Requires: 1428135446Strhodes * The stream iterator is initialized and points at an RR, 1429193149Sdougb * or possibly at the end of the stream (that is, the 1430135446Strhodes * _first method of the iterator has been called). 1431135446Strhodes */ 1432135446Strhodesstatic void 1433135446Strhodessendstream(xfrout_ctx_t *xfr) { 1434135446Strhodes dns_message_t *tcpmsg = NULL; 1435135446Strhodes dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */ 1436135446Strhodes isc_result_t result; 1437135446Strhodes isc_region_t used; 1438135446Strhodes isc_region_t region; 1439135446Strhodes dns_rdataset_t *qrdataset; 1440135446Strhodes dns_name_t *msgname = NULL; 1441135446Strhodes dns_rdata_t *msgrdata = NULL; 1442135446Strhodes dns_rdatalist_t *msgrdl = NULL; 1443135446Strhodes dns_rdataset_t *msgrds = NULL; 1444135446Strhodes dns_compress_t cctx; 1445135446Strhodes isc_boolean_t cleanup_cctx = ISC_FALSE; 1446135446Strhodes 1447135446Strhodes int n_rrs; 1448135446Strhodes 1449135446Strhodes isc_buffer_clear(&xfr->buf); 1450135446Strhodes isc_buffer_clear(&xfr->txlenbuf); 1451135446Strhodes isc_buffer_clear(&xfr->txbuf); 1452135446Strhodes 1453135446Strhodes if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) { 1454135446Strhodes /* 1455135446Strhodes * In the UDP case, we put the response data directly into 1456135446Strhodes * the client message. 1457135446Strhodes */ 1458135446Strhodes msg = xfr->client->message; 1459135446Strhodes CHECK(dns_message_reply(msg, ISC_TRUE)); 1460135446Strhodes } else { 1461135446Strhodes /* 1462135446Strhodes * TCP. Build a response dns_message_t, temporarily storing 1463135446Strhodes * the raw, uncompressed owner names and RR data contiguously 1464135446Strhodes * in xfr->buf. We know that if the uncompressed data fits 1465135446Strhodes * in xfr->buf, the compressed data will surely fit in a TCP 1466135446Strhodes * message. 1467135446Strhodes */ 1468135446Strhodes 1469135446Strhodes CHECK(dns_message_create(xfr->mctx, 1470135446Strhodes DNS_MESSAGE_INTENTRENDER, &tcpmsg)); 1471135446Strhodes msg = tcpmsg; 1472135446Strhodes 1473135446Strhodes msg->id = xfr->id; 1474135446Strhodes msg->rcode = dns_rcode_noerror; 1475135446Strhodes msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA; 1476135446Strhodes if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0) 1477135446Strhodes msg->flags |= DNS_MESSAGEFLAG_RA; 1478135446Strhodes CHECK(dns_message_settsigkey(msg, xfr->tsigkey)); 1479135446Strhodes CHECK(dns_message_setquerytsig(msg, xfr->lasttsig)); 1480135446Strhodes if (xfr->lasttsig != NULL) 1481135446Strhodes isc_buffer_free(&xfr->lasttsig); 1482135446Strhodes 1483135446Strhodes /* 1484135446Strhodes * Include a question section in the first message only. 1485135446Strhodes * BIND 8.2.1 will not recognize an IXFR if it does not 1486135446Strhodes * have a question section. 1487135446Strhodes */ 1488135446Strhodes if (xfr->nmsg == 0) { 1489135446Strhodes dns_name_t *qname = NULL; 1490135446Strhodes isc_region_t r; 1491135446Strhodes 1492135446Strhodes /* 1493135446Strhodes * Reserve space for the 12-byte message header 1494135446Strhodes * and 4 bytes of question. 1495135446Strhodes */ 1496135446Strhodes isc_buffer_add(&xfr->buf, 12 + 4); 1497135446Strhodes 1498135446Strhodes qrdataset = NULL; 1499135446Strhodes result = dns_message_gettemprdataset(msg, &qrdataset); 1500135446Strhodes if (result != ISC_R_SUCCESS) 1501135446Strhodes goto failure; 1502135446Strhodes dns_rdataset_init(qrdataset); 1503135446Strhodes dns_rdataset_makequestion(qrdataset, 1504135446Strhodes xfr->client->message->rdclass, 1505135446Strhodes xfr->qtype); 1506135446Strhodes 1507135446Strhodes result = dns_message_gettempname(msg, &qname); 1508135446Strhodes if (result != ISC_R_SUCCESS) 1509135446Strhodes goto failure; 1510135446Strhodes dns_name_init(qname, NULL); 1511135446Strhodes isc_buffer_availableregion(&xfr->buf, &r); 1512135446Strhodes INSIST(r.length >= xfr->qname->length); 1513135446Strhodes r.length = xfr->qname->length; 1514135446Strhodes isc_buffer_putmem(&xfr->buf, xfr->qname->ndata, 1515135446Strhodes xfr->qname->length); 1516135446Strhodes dns_name_fromregion(qname, &r); 1517135446Strhodes ISC_LIST_INIT(qname->list); 1518135446Strhodes ISC_LIST_APPEND(qname->list, qrdataset, link); 1519135446Strhodes 1520135446Strhodes dns_message_addname(msg, qname, DNS_SECTION_QUESTION); 1521135446Strhodes } 1522135446Strhodes else 1523135446Strhodes msg->tcp_continuation = 1; 1524135446Strhodes } 1525135446Strhodes 1526135446Strhodes /* 1527135446Strhodes * Try to fit in as many RRs as possible, unless "one-answer" 1528135446Strhodes * format has been requested. 1529135446Strhodes */ 1530135446Strhodes for (n_rrs = 0; ; n_rrs++) { 1531135446Strhodes dns_name_t *name = NULL; 1532135446Strhodes isc_uint32_t ttl; 1533135446Strhodes dns_rdata_t *rdata = NULL; 1534135446Strhodes 1535135446Strhodes unsigned int size; 1536135446Strhodes isc_region_t r; 1537135446Strhodes 1538135446Strhodes msgname = NULL; 1539135446Strhodes msgrdata = NULL; 1540135446Strhodes msgrdl = NULL; 1541135446Strhodes msgrds = NULL; 1542135446Strhodes 1543135446Strhodes xfr->stream->methods->current(xfr->stream, 1544135446Strhodes &name, &ttl, &rdata); 1545135446Strhodes size = name->length + 10 + rdata->length; 1546135446Strhodes isc_buffer_availableregion(&xfr->buf, &r); 1547135446Strhodes if (size >= r.length) { 1548135446Strhodes /* 1549135446Strhodes * RR would not fit. If there are other RRs in the 1550135446Strhodes * buffer, send them now and leave this RR to the 1551135446Strhodes * next message. If this RR overflows the buffer 1552135446Strhodes * all by itself, fail. 1553135446Strhodes * 1554135446Strhodes * In theory some RRs might fit in a TCP message 1555135446Strhodes * when compressed even if they do not fit when 1556135446Strhodes * uncompressed, but surely we don't want 1557135446Strhodes * to send such monstrosities to an unsuspecting 1558135446Strhodes * slave. 1559135446Strhodes */ 1560135446Strhodes if (n_rrs == 0) { 1561135446Strhodes xfrout_log(xfr, ISC_LOG_WARNING, 1562135446Strhodes "RR too large for zone transfer " 1563135446Strhodes "(%d bytes)", size); 1564135446Strhodes /* XXX DNS_R_RRTOOLARGE? */ 1565135446Strhodes result = ISC_R_NOSPACE; 1566135446Strhodes goto failure; 1567135446Strhodes } 1568135446Strhodes break; 1569135446Strhodes } 1570135446Strhodes 1571135446Strhodes if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL)) 1572135446Strhodes log_rr(name, rdata, ttl); /* XXX */ 1573135446Strhodes 1574135446Strhodes result = dns_message_gettempname(msg, &msgname); 1575135446Strhodes if (result != ISC_R_SUCCESS) 1576135446Strhodes goto failure; 1577135446Strhodes dns_name_init(msgname, NULL); 1578135446Strhodes isc_buffer_availableregion(&xfr->buf, &r); 1579135446Strhodes INSIST(r.length >= name->length); 1580135446Strhodes r.length = name->length; 1581135446Strhodes isc_buffer_putmem(&xfr->buf, name->ndata, name->length); 1582135446Strhodes dns_name_fromregion(msgname, &r); 1583135446Strhodes 1584135446Strhodes /* Reserve space for RR header. */ 1585135446Strhodes isc_buffer_add(&xfr->buf, 10); 1586135446Strhodes 1587135446Strhodes result = dns_message_gettemprdata(msg, &msgrdata); 1588135446Strhodes if (result != ISC_R_SUCCESS) 1589135446Strhodes goto failure; 1590135446Strhodes isc_buffer_availableregion(&xfr->buf, &r); 1591135446Strhodes r.length = rdata->length; 1592135446Strhodes isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length); 1593135446Strhodes dns_rdata_init(msgrdata); 1594135446Strhodes dns_rdata_fromregion(msgrdata, 1595135446Strhodes rdata->rdclass, rdata->type, &r); 1596135446Strhodes 1597135446Strhodes result = dns_message_gettemprdatalist(msg, &msgrdl); 1598135446Strhodes if (result != ISC_R_SUCCESS) 1599135446Strhodes goto failure; 1600135446Strhodes msgrdl->type = rdata->type; 1601135446Strhodes msgrdl->rdclass = rdata->rdclass; 1602135446Strhodes msgrdl->ttl = ttl; 1603193149Sdougb if (rdata->type == dns_rdatatype_sig || 1604193149Sdougb rdata->type == dns_rdatatype_rrsig) 1605193149Sdougb msgrdl->covers = dns_rdata_covers(rdata); 1606193149Sdougb else 1607193149Sdougb msgrdl->covers = dns_rdatatype_none; 1608135446Strhodes ISC_LINK_INIT(msgrdl, link); 1609135446Strhodes ISC_LIST_INIT(msgrdl->rdata); 1610135446Strhodes ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link); 1611135446Strhodes 1612135446Strhodes result = dns_message_gettemprdataset(msg, &msgrds); 1613135446Strhodes if (result != ISC_R_SUCCESS) 1614135446Strhodes goto failure; 1615135446Strhodes dns_rdataset_init(msgrds); 1616135446Strhodes result = dns_rdatalist_tordataset(msgrdl, msgrds); 1617135446Strhodes INSIST(result == ISC_R_SUCCESS); 1618135446Strhodes 1619135446Strhodes ISC_LIST_APPEND(msgname->list, msgrds, link); 1620135446Strhodes 1621135446Strhodes dns_message_addname(msg, msgname, DNS_SECTION_ANSWER); 1622135446Strhodes msgname = NULL; 1623135446Strhodes 1624135446Strhodes result = xfr->stream->methods->next(xfr->stream); 1625135446Strhodes if (result == ISC_R_NOMORE) { 1626135446Strhodes xfr->end_of_stream = ISC_TRUE; 1627135446Strhodes break; 1628135446Strhodes } 1629135446Strhodes CHECK(result); 1630135446Strhodes 1631135446Strhodes if (! xfr->many_answers) 1632135446Strhodes break; 1633135446Strhodes } 1634135446Strhodes 1635135446Strhodes if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) { 1636135446Strhodes CHECK(dns_compress_init(&cctx, -1, xfr->mctx)); 1637170222Sdougb dns_compress_setsensitive(&cctx, ISC_TRUE); 1638135446Strhodes cleanup_cctx = ISC_TRUE; 1639135446Strhodes CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf)); 1640135446Strhodes CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0)); 1641135446Strhodes CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0)); 1642135446Strhodes CHECK(dns_message_renderend(msg)); 1643135446Strhodes dns_compress_invalidate(&cctx); 1644135446Strhodes cleanup_cctx = ISC_FALSE; 1645135446Strhodes 1646135446Strhodes isc_buffer_usedregion(&xfr->txbuf, &used); 1647135446Strhodes isc_buffer_putuint16(&xfr->txlenbuf, 1648135446Strhodes (isc_uint16_t)used.length); 1649135446Strhodes region.base = xfr->txlenbuf.base; 1650135446Strhodes region.length = 2 + used.length; 1651135446Strhodes xfrout_log(xfr, ISC_LOG_DEBUG(8), 1652135446Strhodes "sending TCP message of %d bytes", 1653135446Strhodes used.length); 1654135446Strhodes CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */ 1655135446Strhodes ®ion, xfr->client->task, 1656135446Strhodes xfrout_senddone, 1657135446Strhodes xfr)); 1658135446Strhodes xfr->sends++; 1659135446Strhodes } else { 1660135446Strhodes xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response"); 1661135446Strhodes ns_client_send(xfr->client); 1662135446Strhodes xfr->stream->methods->pause(xfr->stream); 1663135446Strhodes xfrout_ctx_destroy(&xfr); 1664135446Strhodes return; 1665135446Strhodes } 1666135446Strhodes 1667135446Strhodes /* Advance lasttsig to be the last TSIG generated */ 1668135446Strhodes CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); 1669135446Strhodes 1670135446Strhodes xfr->nmsg++; 1671135446Strhodes 1672135446Strhodes failure: 1673135446Strhodes if (msgname != NULL) { 1674135446Strhodes if (msgrds != NULL) { 1675135446Strhodes if (dns_rdataset_isassociated(msgrds)) 1676135446Strhodes dns_rdataset_disassociate(msgrds); 1677135446Strhodes dns_message_puttemprdataset(msg, &msgrds); 1678135446Strhodes } 1679135446Strhodes if (msgrdl != NULL) { 1680135446Strhodes ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link); 1681135446Strhodes dns_message_puttemprdatalist(msg, &msgrdl); 1682135446Strhodes } 1683135446Strhodes if (msgrdata != NULL) 1684135446Strhodes dns_message_puttemprdata(msg, &msgrdata); 1685135446Strhodes dns_message_puttempname(msg, &msgname); 1686135446Strhodes } 1687135446Strhodes 1688135446Strhodes if (tcpmsg != NULL) 1689135446Strhodes dns_message_destroy(&tcpmsg); 1690135446Strhodes 1691135446Strhodes if (cleanup_cctx) 1692135446Strhodes dns_compress_invalidate(&cctx); 1693135446Strhodes /* 1694135446Strhodes * Make sure to release any locks held by database 1695135446Strhodes * iterators before returning from the event handler. 1696135446Strhodes */ 1697135446Strhodes xfr->stream->methods->pause(xfr->stream); 1698193149Sdougb 1699135446Strhodes if (result == ISC_R_SUCCESS) 1700135446Strhodes return; 1701135446Strhodes 1702135446Strhodes xfrout_fail(xfr, result, "sending zone data"); 1703135446Strhodes} 1704135446Strhodes 1705135446Strhodesstatic void 1706135446Strhodesxfrout_ctx_destroy(xfrout_ctx_t **xfrp) { 1707135446Strhodes xfrout_ctx_t *xfr = *xfrp; 1708135446Strhodes 1709135446Strhodes INSIST(xfr->sends == 0); 1710135446Strhodes 1711135446Strhodes xfr->client->shutdown = NULL; 1712135446Strhodes xfr->client->shutdown_arg = NULL; 1713135446Strhodes 1714135446Strhodes if (xfr->stream != NULL) 1715135446Strhodes xfr->stream->methods->destroy(&xfr->stream); 1716135446Strhodes if (xfr->buf.base != NULL) 1717135446Strhodes isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length); 1718135446Strhodes if (xfr->txmem != NULL) 1719135446Strhodes isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen); 1720135446Strhodes if (xfr->lasttsig != NULL) 1721135446Strhodes isc_buffer_free(&xfr->lasttsig); 1722135446Strhodes if (xfr->quota != NULL) 1723135446Strhodes isc_quota_detach(&xfr->quota); 1724135446Strhodes if (xfr->ver != NULL) 1725135446Strhodes dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE); 1726193149Sdougb if (xfr->zone != NULL) 1727193149Sdougb dns_zone_detach(&xfr->zone); 1728135446Strhodes if (xfr->db != NULL) 1729135446Strhodes dns_db_detach(&xfr->db); 1730135446Strhodes 1731135446Strhodes ns_client_detach(&xfr->client); 1732135446Strhodes 1733135446Strhodes isc_mem_put(xfr->mctx, xfr, sizeof(*xfr)); 1734135446Strhodes 1735135446Strhodes *xfrp = NULL; 1736135446Strhodes} 1737135446Strhodes 1738135446Strhodesstatic void 1739135446Strhodesxfrout_senddone(isc_task_t *task, isc_event_t *event) { 1740135446Strhodes isc_socketevent_t *sev = (isc_socketevent_t *)event; 1741135446Strhodes xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg; 1742135446Strhodes isc_result_t evresult = sev->result; 1743135446Strhodes 1744135446Strhodes UNUSED(task); 1745135446Strhodes 1746135446Strhodes INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); 1747135446Strhodes 1748135446Strhodes isc_event_free(&event); 1749135446Strhodes xfr->sends--; 1750135446Strhodes INSIST(xfr->sends == 0); 1751135446Strhodes 1752135446Strhodes (void)isc_timer_touch(xfr->client->timer); 1753135446Strhodes if (xfr->shuttingdown == ISC_TRUE) { 1754135446Strhodes xfrout_maybe_destroy(xfr); 1755135446Strhodes } else if (evresult != ISC_R_SUCCESS) { 1756135446Strhodes xfrout_fail(xfr, evresult, "send"); 1757135446Strhodes } else if (xfr->end_of_stream == ISC_FALSE) { 1758135446Strhodes sendstream(xfr); 1759135446Strhodes } else { 1760135446Strhodes /* End of zone transfer stream. */ 1761193149Sdougb inc_stats(xfr->zone, dns_nsstatscounter_xfrdone); 1762135446Strhodes xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic); 1763135446Strhodes ns_client_next(xfr->client, ISC_R_SUCCESS); 1764135446Strhodes xfrout_ctx_destroy(&xfr); 1765135446Strhodes } 1766135446Strhodes} 1767135446Strhodes 1768135446Strhodesstatic void 1769135446Strhodesxfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) { 1770135446Strhodes xfr->shuttingdown = ISC_TRUE; 1771135446Strhodes xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s", 1772135446Strhodes msg, isc_result_totext(result)); 1773135446Strhodes xfrout_maybe_destroy(xfr); 1774135446Strhodes} 1775135446Strhodes 1776135446Strhodesstatic void 1777135446Strhodesxfrout_maybe_destroy(xfrout_ctx_t *xfr) { 1778135446Strhodes INSIST(xfr->shuttingdown == ISC_TRUE); 1779135446Strhodes if (xfr->sends > 0) { 1780135446Strhodes /* 1781135446Strhodes * If we are currently sending, cancel it and wait for 1782135446Strhodes * cancel event before destroying the context. 1783135446Strhodes */ 1784135446Strhodes isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task, 1785135446Strhodes ISC_SOCKCANCEL_SEND); 1786135446Strhodes } else { 1787135446Strhodes ns_client_next(xfr->client, ISC_R_CANCELED); 1788135446Strhodes xfrout_ctx_destroy(&xfr); 1789135446Strhodes } 1790135446Strhodes} 1791135446Strhodes 1792135446Strhodesstatic void 1793135446Strhodesxfrout_client_shutdown(void *arg, isc_result_t result) { 1794135446Strhodes xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg; 1795135446Strhodes xfrout_fail(xfr, result, "aborted"); 1796135446Strhodes} 1797135446Strhodes 1798135446Strhodes/* 1799135446Strhodes * Log outgoing zone transfer messages in a format like 1800135446Strhodes * <client>: transfer of <zone>: <message> 1801135446Strhodes */ 1802135446Strhodes 1803135446Strhodesstatic void 1804135446Strhodesxfrout_logv(ns_client_t *client, dns_name_t *zonename, 1805135446Strhodes dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap) 1806135446Strhodes ISC_FORMAT_PRINTF(5, 0); 1807135446Strhodes 1808135446Strhodesstatic void 1809135446Strhodesxfrout_logv(ns_client_t *client, dns_name_t *zonename, 1810135446Strhodes dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap) 1811135446Strhodes{ 1812135446Strhodes char msgbuf[2048]; 1813135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 1814135446Strhodes char classbuf[DNS_RDATACLASS_FORMATSIZE]; 1815135446Strhodes 1816135446Strhodes dns_name_format(zonename, namebuf, sizeof(namebuf)); 1817135446Strhodes dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf)); 1818135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 1819135446Strhodes ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, 1820135446Strhodes NS_LOGMODULE_XFER_OUT, level, 1821135446Strhodes "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf); 1822135446Strhodes} 1823135446Strhodes 1824135446Strhodes/* 1825135446Strhodes * Logging function for use when a xfrout_ctx_t has not yet been created. 1826135446Strhodes */ 1827135446Strhodesstatic void 1828135446Strhodesxfrout_log1(ns_client_t *client, dns_name_t *zonename, 1829135446Strhodes dns_rdataclass_t rdclass, int level, const char *fmt, ...) { 1830135446Strhodes va_list ap; 1831135446Strhodes va_start(ap, fmt); 1832135446Strhodes xfrout_logv(client, zonename, rdclass, level, fmt, ap); 1833135446Strhodes va_end(ap); 1834135446Strhodes} 1835135446Strhodes 1836135446Strhodes/* 1837135446Strhodes * Logging function for use when there is a xfrout_ctx_t. 1838135446Strhodes */ 1839135446Strhodesstatic void 1840153816Sdougbxfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) { 1841135446Strhodes va_list ap; 1842135446Strhodes va_start(ap, fmt); 1843135446Strhodes xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap); 1844135446Strhodes va_end(ap); 1845135446Strhodes} 1846