void ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { isc_result_t result; dns_name_t *question_name; dns_rdataset_t *question_rdataset; dns_zone_t *zone = NULL, *raw = NULL, *mayberaw; dns_db_t *db = NULL; dns_dbversion_t *ver = NULL; dns_rdataclass_t question_class; rrstream_t *soa_stream = NULL; rrstream_t *data_stream = NULL; rrstream_t *stream = NULL; dns_difftuple_t *current_soa_tuple = NULL; dns_name_t *soa_name; dns_rdataset_t *soa_rdataset; dns_rdata_t soa_rdata = DNS_RDATA_INIT; isc_boolean_t have_soa = ISC_FALSE; const char *mnemonic = NULL; isc_mem_t *mctx = client->mctx; dns_message_t *request = client->message; xfrout_ctx_t *xfr = NULL; isc_quota_t *quota = NULL; dns_transfer_format_t format = client->view->transfer_format; isc_netaddr_t na; dns_peer_t *peer = NULL; isc_buffer_t *tsigbuf = NULL; char *journalfile; char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; char keyname[DNS_NAME_FORMATSIZE]; isc_boolean_t is_poll = ISC_FALSE; isc_boolean_t is_dlz = ISC_FALSE; isc_boolean_t is_ixfr = ISC_FALSE; isc_uint32_t begin_serial = 0, current_serial; switch (reqtype) { case dns_rdatatype_axfr: mnemonic = "AXFR"; break; case dns_rdatatype_ixfr: mnemonic = "IXFR"; break; default: INSIST(0); break; } ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(6), "%s request", mnemonic); /* * Apply quota. */ result = isc_quota_attach(&ns_g_server->xfroutquota, "a); if (result != ISC_R_SUCCESS) { isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, "%s request denied: %s", mnemonic, isc_result_totext(result)); goto failure; } /* * Interpret the question section. */ result = dns_message_firstname(request, DNS_SECTION_QUESTION); INSIST(result == ISC_R_SUCCESS); /* * The question section must contain exactly one question, and * it must be for AXFR/IXFR as appropriate. */ question_name = NULL; dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name); question_rdataset = ISC_LIST_HEAD(question_name->list); question_class = question_rdataset->rdclass; INSIST(question_rdataset->type == reqtype); if (ISC_LIST_NEXT(question_rdataset, link) != NULL) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_message_nextname(request, DNS_SECTION_QUESTION); if (result != ISC_R_NOMORE) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, &zone); if (result != ISC_R_SUCCESS) { /* * Normal zone table does not have a match. * Try the DLZ database */ // Temporary: only searching the first DLZ database if (! ISC_LIST_EMPTY(client->view->dlz_searched)) { result = dns_dlzallowzonexfr(client->view, question_name, &client->peeraddr, &db); pfilter_notify(result, client, "zonexfr"); if (result == ISC_R_NOPERM) { char _buf1[DNS_NAME_FORMATSIZE]; char _buf2[DNS_RDATACLASS_FORMATSIZE]; result = DNS_R_REFUSED; dns_name_format(question_name, _buf1, sizeof(_buf1)); dns_rdataclass_format(question_class, _buf2, sizeof(_buf2)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_XFER_OUT, ISC_LOG_ERROR, "zone transfer '%s/%s' denied", _buf1, _buf2); goto failure; } if (result != ISC_R_SUCCESS) FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); is_dlz = ISC_TRUE; } else { /* * not DLZ and not in normal zone table, we are * not authoritative */ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } } else { /* zone table has a match */ switch(dns_zone_gettype(zone)) { /* Master and slave zones are OK for transfer. */ case dns_zone_master: case dns_zone_slave: case dns_zone_dlz: break; default: FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } CHECK(dns_zone_getdb(zone, &db)); dns_db_currentversion(db, &ver); } xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s question section OK", mnemonic); /* * Check the authority section. Look for a SOA record with * the same name and class as the question. */ for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_AUTHORITY)) { soa_name = NULL; dns_message_currentname(request, DNS_SECTION_AUTHORITY, &soa_name); /* * Ignore data whose owner name is not the zone apex. */ if (! dns_name_equal(soa_name, question_name)) continue; for (soa_rdataset = ISC_LIST_HEAD(soa_name->list); soa_rdataset != NULL; soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link)) { /* * Ignore non-SOA data. */ if (soa_rdataset->type != dns_rdatatype_soa) continue; if (soa_rdataset->rdclass != question_class) continue; CHECK(dns_rdataset_first(soa_rdataset)); dns_rdataset_current(soa_rdataset, &soa_rdata); result = dns_rdataset_next(soa_rdataset); if (result == ISC_R_SUCCESS) FAILC(DNS_R_FORMERR, "IXFR authority section " "has multiple SOAs"); have_soa = ISC_TRUE; goto got_soa; } } got_soa: if (result != ISC_R_NOMORE) CHECK(result); xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s authority section OK", mnemonic); /* * If not a DLZ zone, decide whether to allow this transfer. */ if (!is_dlz) { ns_client_aclmsg("zone transfer", question_name, reqtype, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, NULL, msg, dns_zone_getxfracl(zone), ISC_TRUE, ISC_LOG_ERROR)); } /* * AXFR over UDP is not possible. */ if (reqtype == dns_rdatatype_axfr && (client->attributes & NS_CLIENTATTR_TCP) == 0) FAILC(DNS_R_FORMERR, "attempted AXFR over UDP"); /* * Look up the requesting server in the peer table. */ isc_netaddr_fromsockaddr(&na, &client->peeraddr); (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer); /* * Decide on the transfer format (one-answer or many-answers). */ if (peer != NULL) (void)dns_peer_gettransferformat(peer, &format); /* * Get a dynamically allocated copy of the current SOA. */ if (is_dlz) dns_db_currentversion(db, &ver); CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, ¤t_soa_tuple)); current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); if (reqtype == dns_rdatatype_ixfr) { isc_boolean_t provide_ixfr; /* * Outgoing IXFR may have been disabled for this peer * or globally. */ provide_ixfr = client->view->provideixfr; if (peer != NULL) (void) dns_peer_getprovideixfr(peer, &provide_ixfr); if (provide_ixfr == ISC_FALSE) goto axfr_fallback; if (! have_soa) FAILC(DNS_R_FORMERR, "IXFR request missing SOA"); begin_serial = dns_soa_getserial(&soa_rdata); /* * RFC1995 says "If an IXFR query with the same or * newer version number than that of the server * is received, it is replied to with a single SOA * record of the server's current version, just as * in AXFR". The claim about AXFR is incorrect, * but other than that, we do as the RFC says. * * Sending a single SOA record is also how we refuse * IXFR over UDP (currently, we always do). */ if (DNS_SERIAL_GE(begin_serial, current_serial) || (client->attributes & NS_CLIENTATTR_TCP) == 0) { CHECK(soa_rrstream_create(mctx, db, ver, &stream)); is_poll = ISC_TRUE; goto have_stream; } journalfile = is_dlz ? NULL : dns_zone_getjournal(zone); if (journalfile != NULL) result = ixfr_rrstream_create(mctx, journalfile, begin_serial, current_serial, &data_stream); else result = ISC_R_NOTFOUND; if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(4), "IXFR version not in journal, " "falling back to AXFR"); mnemonic = "AXFR-style IXFR"; goto axfr_fallback; } CHECK(result); is_ixfr = ISC_TRUE; } else { axfr_fallback: CHECK(axfr_rrstream_create(mctx, db, ver, &data_stream)); } /* * Bracket the data stream with SOAs. */ CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream)); CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream, &stream)); soa_stream = NULL; data_stream = NULL; have_stream: CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf)); /* * Create the xfrout context object. This transfers the ownership * of "stream", "db", "ver", and "quota" to the xfrout context object. */ if (is_dlz) CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, 3600, 3600, (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); else CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, dns_zone_getmaxxfrout(zone), dns_zone_getidleout(zone), (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; CHECK(xfr->stream->methods->first(xfr->stream)); if (xfr->tsigkey != NULL) dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname)); else keyname[0] = '\0'; if (is_poll) xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s", (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); else if (is_ixfr) xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u -> %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, begin_serial, current_serial); else xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, current_serial); if (zone != NULL) { dns_zone_getraw(zone, &raw); mayberaw = (raw != NULL) ? raw : zone; if ((client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 && dns_zone_gettype(mayberaw) == dns_zone_slave) { isc_time_t expiretime; isc_uint32_t secs; dns_zone_getexpiretime(zone, &expiretime); secs = isc_time_seconds(&expiretime); if (secs >= client->now && result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_HAVEEXPIRE; client->expire = secs - client->now; } } if (raw != NULL) dns_zone_detach(&raw); } /* * Hand the context over to sendstream(). Set xfr to NULL; * sendstream() is responsible for either passing the * context on to a later event handler or destroying it. */ sendstream(xfr); xfr = NULL; result = ISC_R_SUCCESS; failure: if (result == DNS_R_REFUSED) inc_stats(zone, dns_nsstatscounter_xfrrej); if (quota != NULL) isc_quota_detach("a); if (current_soa_tuple != NULL) dns_difftuple_free(¤t_soa_tuple); if (stream != NULL) stream->methods->destroy(&stream); if (soa_stream != NULL) soa_stream->methods->destroy(&soa_stream); if (data_stream != NULL) data_stream->methods->destroy(&data_stream); if (ver != NULL) dns_db_closeversion(db, &ver, ISC_FALSE); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); /* XXX kludge */ if (xfr != NULL) { xfrout_fail(xfr, result, "setting up zone transfer"); } else if (result != ISC_R_SUCCESS) { ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(3), "zone transfer setup failed"); ns_client_error(client, result); } }
ATF_TC_BODY(tsig_tcp, tc) { const dns_name_t *tsigowner = NULL; dns_fixedname_t fkeyname; dns_message_t *msg = NULL; dns_name_t *keyname; dns_tsig_keyring_t *ring = NULL; dns_tsigkey_t *key = NULL; isc_buffer_t *buf = NULL; isc_buffer_t *querytsig = NULL; isc_buffer_t *tsigin = NULL; isc_buffer_t *tsigout = NULL; isc_result_t result; unsigned char secret[16] = { 0 }; dst_context_t *tsigctx = NULL; dst_context_t *outctx = NULL; UNUSED(tc); result = dns_test_begin(stderr, ISC_TRUE); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* isc_log_setdebuglevel(lctx, 99); */ dns_fixedname_init(&fkeyname); keyname = dns_fixedname_name(&fkeyname); result = dns_name_fromstring(keyname, "test", 0, NULL); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); result = dns_tsigkeyring_create(mctx, &ring); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); result = dns_tsigkey_create(keyname, dns_tsig_hmacsha256_name, secret, sizeof(secret), ISC_FALSE, NULL, 0, 0, mctx, ring, &key); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* * Create request. */ result = isc_buffer_allocate(mctx, &buf, 65535); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); render(buf, 0, key, &tsigout, &querytsig, NULL); isc_buffer_free(&buf); /* * Create response message 1. */ result = isc_buffer_allocate(mctx, &buf, 65535); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL); /* * Process response message 1. */ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s", dns_result_totext(result)); result = dns_message_settsigkey(msg, key); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s", dns_result_totext(result)); result = dns_message_parse(msg, buf, 0); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s", dns_result_totext(result)); printmessage(msg); result = dns_message_setquerytsig(msg, querytsig); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_setquerytsig: %s", dns_result_totext(result)); result = dns_tsig_verify(buf, msg, NULL, NULL); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_tsig_verify: %s", dns_result_totext(result)); ATF_CHECK_EQ(msg->verified_sig, 1); ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror); /* * Check that we have a TSIG in the first message. */ ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL); result = dns_message_getquerytsig(msg, mctx, &tsigin); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_getquerytsig: %s", dns_result_totext(result)); tsigctx = msg->tsigctx; msg->tsigctx = NULL; isc_buffer_free(&buf); dns_message_destroy(&msg); result = dst_context_create3(key->key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &outctx); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* * Start digesting. */ result = add_mac(outctx, tsigout); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* * Create response message 2. */ result = isc_buffer_allocate(mctx, &buf, 65535); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx); /* * Process response message 2. */ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s", dns_result_totext(result)); msg->tcp_continuation = 1; msg->tsigctx = tsigctx; tsigctx = NULL; result = dns_message_settsigkey(msg, key); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s", dns_result_totext(result)); result = dns_message_parse(msg, buf, 0); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s", dns_result_totext(result)); printmessage(msg); result = dns_message_setquerytsig(msg, tsigin); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_setquerytsig: %s", dns_result_totext(result)); result = dns_tsig_verify(buf, msg, NULL, NULL); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_tsig_verify: %s", dns_result_totext(result)); ATF_CHECK_EQ(msg->verified_sig, 1); ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror); /* * Check that we don't have a TSIG in the second message. */ tsigowner = NULL; ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) == NULL); tsigctx = msg->tsigctx; msg->tsigctx = NULL; isc_buffer_free(&buf); dns_message_destroy(&msg); /* * Create response message 3. */ result = isc_buffer_allocate(mctx, &buf, 65535); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx); result = add_tsig(outctx, key, buf); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "add_tsig: %s", dns_result_totext(result)); /* * Process response message 3. */ result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s", dns_result_totext(result)); msg->tcp_continuation = 1; msg->tsigctx = tsigctx; tsigctx = NULL; result = dns_message_settsigkey(msg, key); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s", dns_result_totext(result)); result = dns_message_parse(msg, buf, 0); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_parse: %s", dns_result_totext(result)); printmessage(msg); /* * Check that we had a TSIG in the third message. */ ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL); result = dns_message_setquerytsig(msg, tsigin); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_setquerytsig: %s", dns_result_totext(result)); result = dns_tsig_verify(buf, msg, NULL, NULL); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_tsig_verify: %s", dns_result_totext(result)); ATF_CHECK_EQ(msg->verified_sig, 1); ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror); if (tsigin != NULL) isc_buffer_free(&tsigin); result = dns_message_getquerytsig(msg, mctx, &tsigin); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_getquerytsig: %s", dns_result_totext(result)); isc_buffer_free(&buf); dns_message_destroy(&msg); if (outctx != NULL) dst_context_destroy(&outctx); if (querytsig != NULL) isc_buffer_free(&querytsig); if (tsigin != NULL) isc_buffer_free(&tsigin); if (tsigout != NULL) isc_buffer_free(&tsigout); if (buf != NULL) isc_buffer_free(&buf); if (msg != NULL) dns_message_destroy(&msg); if (key != NULL) dns_tsigkey_detach(&key); if (ring != NULL) dns_tsigkeyring_detach(&ring); dns_test_end(); }
/* * Arrange to send as much as we can of "stream" without blocking. * * Requires: * The stream iterator is initialized and points at an RR, * or possibly at the end of the stream (that is, the * _first method of the iterator has been called). */ static void sendstream(xfrout_ctx_t *xfr) { dns_message_t *tcpmsg = NULL; dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */ isc_result_t result; isc_region_t used; isc_region_t region; dns_rdataset_t *qrdataset; dns_name_t *msgname = NULL; dns_rdata_t *msgrdata = NULL; dns_rdatalist_t *msgrdl = NULL; dns_rdataset_t *msgrds = NULL; dns_compress_t cctx; isc_boolean_t cleanup_cctx = ISC_FALSE; isc_boolean_t is_tcp; int n_rrs; isc_buffer_clear(&xfr->buf); isc_buffer_clear(&xfr->txlenbuf); isc_buffer_clear(&xfr->txbuf); is_tcp = ISC_TF((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0); if (!is_tcp) { /* * In the UDP case, we put the response data directly into * the client message. */ msg = xfr->client->message; CHECK(dns_message_reply(msg, ISC_TRUE)); } else { /* * TCP. Build a response dns_message_t, temporarily storing * the raw, uncompressed owner names and RR data contiguously * in xfr->buf. We know that if the uncompressed data fits * in xfr->buf, the compressed data will surely fit in a TCP * message. */ CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &tcpmsg)); msg = tcpmsg; msg->id = xfr->id; msg->rcode = dns_rcode_noerror; msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA; if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0) msg->flags |= DNS_MESSAGEFLAG_RA; CHECK(dns_message_settsigkey(msg, xfr->tsigkey)); CHECK(dns_message_setquerytsig(msg, xfr->lasttsig)); if (xfr->lasttsig != NULL) isc_buffer_free(&xfr->lasttsig); /* * Add a EDNS option to the message? */ if ((xfr->client->attributes & NS_CLIENTATTR_WANTOPT) != 0) { dns_rdataset_t *opt = NULL; CHECK(ns_client_addopt(xfr->client, msg, &opt)); CHECK(dns_message_setopt(msg, opt)); /* * Add to first message only. */ xfr->client->attributes &= ~NS_CLIENTATTR_WANTNSID; xfr->client->attributes &= ~NS_CLIENTATTR_HAVEEXPIRE; } /* * Account for reserved space. */ if (xfr->tsigkey != NULL) INSIST(msg->reserved != 0U); isc_buffer_add(&xfr->buf, msg->reserved); /* * Include a question section in the first message only. * BIND 8.2.1 will not recognize an IXFR if it does not * have a question section. */ if (xfr->nmsg == 0) { dns_name_t *qname = NULL; isc_region_t r; /* * Reserve space for the 12-byte message header * and 4 bytes of question. */ isc_buffer_add(&xfr->buf, 12 + 4); qrdataset = NULL; result = dns_message_gettemprdataset(msg, &qrdataset); if (result != ISC_R_SUCCESS) goto failure; dns_rdataset_makequestion(qrdataset, xfr->client->message->rdclass, xfr->qtype); result = dns_message_gettempname(msg, &qname); if (result != ISC_R_SUCCESS) goto failure; dns_name_init(qname, NULL); isc_buffer_availableregion(&xfr->buf, &r); INSIST(r.length >= xfr->qname->length); r.length = xfr->qname->length; isc_buffer_putmem(&xfr->buf, xfr->qname->ndata, xfr->qname->length); dns_name_fromregion(qname, &r); ISC_LIST_INIT(qname->list); ISC_LIST_APPEND(qname->list, qrdataset, link); dns_message_addname(msg, qname, DNS_SECTION_QUESTION); } else { /* * Reserve space for the 12-byte message header */ isc_buffer_add(&xfr->buf, 12); msg->tcp_continuation = 1; } } /* * Try to fit in as many RRs as possible, unless "one-answer" * format has been requested. */ for (n_rrs = 0; ; n_rrs++) { dns_name_t *name = NULL; isc_uint32_t ttl; dns_rdata_t *rdata = NULL; unsigned int size; isc_region_t r; msgname = NULL; msgrdata = NULL; msgrdl = NULL; msgrds = NULL; xfr->stream->methods->current(xfr->stream, &name, &ttl, &rdata); size = name->length + 10 + rdata->length; isc_buffer_availableregion(&xfr->buf, &r); if (size >= r.length) { /* * RR would not fit. If there are other RRs in the * buffer, send them now and leave this RR to the * next message. If this RR overflows the buffer * all by itself, fail. * * In theory some RRs might fit in a TCP message * when compressed even if they do not fit when * uncompressed, but surely we don't want * to send such monstrosities to an unsuspecting * slave. */ if (n_rrs == 0) { xfrout_log(xfr, ISC_LOG_WARNING, "RR too large for zone transfer " "(%d bytes)", size); /* XXX DNS_R_RRTOOLARGE? */ result = ISC_R_NOSPACE; goto failure; } break; } if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL)) log_rr(name, rdata, ttl); /* XXX */ result = dns_message_gettempname(msg, &msgname); if (result != ISC_R_SUCCESS) goto failure; dns_name_init(msgname, NULL); isc_buffer_availableregion(&xfr->buf, &r); INSIST(r.length >= name->length); r.length = name->length; isc_buffer_putmem(&xfr->buf, name->ndata, name->length); dns_name_fromregion(msgname, &r); /* Reserve space for RR header. */ isc_buffer_add(&xfr->buf, 10); result = dns_message_gettemprdata(msg, &msgrdata); if (result != ISC_R_SUCCESS) goto failure; isc_buffer_availableregion(&xfr->buf, &r); r.length = rdata->length; isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length); dns_rdata_init(msgrdata); dns_rdata_fromregion(msgrdata, rdata->rdclass, rdata->type, &r); result = dns_message_gettemprdatalist(msg, &msgrdl); if (result != ISC_R_SUCCESS) goto failure; msgrdl->type = rdata->type; msgrdl->rdclass = rdata->rdclass; msgrdl->ttl = ttl; if (rdata->type == dns_rdatatype_sig || rdata->type == dns_rdatatype_rrsig) msgrdl->covers = dns_rdata_covers(rdata); else msgrdl->covers = dns_rdatatype_none; ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link); result = dns_message_gettemprdataset(msg, &msgrds); if (result != ISC_R_SUCCESS) goto failure; result = dns_rdatalist_tordataset(msgrdl, msgrds); INSIST(result == ISC_R_SUCCESS); ISC_LIST_APPEND(msgname->list, msgrds, link); dns_message_addname(msg, msgname, DNS_SECTION_ANSWER); msgname = NULL; result = xfr->stream->methods->next(xfr->stream); if (result == ISC_R_NOMORE) { xfr->end_of_stream = ISC_TRUE; break; } CHECK(result); if (! xfr->many_answers) break; /* * At this stage, at least 1 RR has been rendered into * the message. Check if we want to clamp this message * here (TCP only). 20480 was set as an upper limit to * improve message compression. */ if ((isc_buffer_usedlength(&xfr->buf) >= 20480) && is_tcp) break; } if (is_tcp) { CHECK(dns_compress_init(&cctx, -1, xfr->mctx)); dns_compress_setsensitive(&cctx, ISC_TRUE); cleanup_cctx = ISC_TRUE; CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf)); CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0)); CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0)); CHECK(dns_message_renderend(msg)); dns_compress_invalidate(&cctx); cleanup_cctx = ISC_FALSE; isc_buffer_usedregion(&xfr->txbuf, &used); isc_buffer_putuint16(&xfr->txlenbuf, (isc_uint16_t)used.length); region.base = xfr->txlenbuf.base; region.length = 2 + used.length; xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending TCP message of %d bytes", used.length); CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */ ®ion, xfr->client->task, xfrout_senddone, xfr)); xfr->sends++; } else { xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response"); ns_client_send(xfr->client); xfr->stream->methods->pause(xfr->stream); xfrout_ctx_destroy(&xfr); return; } /* Advance lasttsig to be the last TSIG generated */ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); xfr->nmsg++; failure: if (msgname != NULL) { if (msgrds != NULL) { if (dns_rdataset_isassociated(msgrds)) dns_rdataset_disassociate(msgrds); dns_message_puttemprdataset(msg, &msgrds); } if (msgrdl != NULL) { ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link); dns_message_puttemprdatalist(msg, &msgrdl); } if (msgrdata != NULL) dns_message_puttemprdata(msg, &msgrdata); dns_message_puttempname(msg, &msgname); } if (tcpmsg != NULL) dns_message_destroy(&tcpmsg); if (cleanup_cctx) dns_compress_invalidate(&cctx); /* * Make sure to release any locks held by database * iterators before returning from the event handler. */ xfr->stream->methods->pause(xfr->stream); if (result == ISC_R_SUCCESS) return; xfrout_fail(xfr, result, "sending zone data"); }
static void render(isc_buffer_t *buf, unsigned flags, dns_tsigkey_t *key, isc_buffer_t **tsigin, isc_buffer_t **tsigout, dst_context_t *tsigctx) { dns_message_t *msg = NULL; dns_compress_t cctx; isc_result_t result; result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &msg); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_create: %s", dns_result_totext(result)); msg->id = 50; msg->rcode = dns_rcode_noerror; msg->flags = flags; if (tsigin == tsigout) msg->tcp_continuation = 1; if (tsigctx == NULL) { result = dns_message_settsigkey(msg, key); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_settsigkey: %s", dns_result_totext(result)); result = dns_message_setquerytsig(msg, *tsigin); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_setquerytsig: %s", dns_result_totext(result)); } result = dns_compress_init(&cctx, -1, mctx); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_compress_init: %s", dns_result_totext(result)); result = dns_message_renderbegin(msg, &cctx, buf); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_renderbegin: %s", dns_result_totext(result)); result = dns_message_renderend(msg); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_renderend: %s", dns_result_totext(result)); if (tsigctx != NULL) { isc_region_t r; isc_buffer_usedregion(buf, &r); result = dst_context_adddata(tsigctx, &r); } else { if (tsigin == tsigout && *tsigin != NULL) isc_buffer_free(tsigin); result = dns_message_getquerytsig(msg, mctx, tsigout); ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "dns_message_getquerytsig: %s", dns_result_totext(result)); } dns_compress_invalidate(&cctx); dns_message_destroy(&msg); }