📄 xfrout.c
字号:
* These MUST be after the last "goto failure;" / CHECK to * prevent a double free by the caller. */ xfr->quota = quota; xfr->stream = stream; *xfrp = xfr; return (ISC_R_SUCCESS);failure: xfrout_ctx_destroy(&xfr); return (result);}/* * Arrange to send as much as we can of "stream" without blocking. * * Requires: * The stream iterator is initialized and points at an RR, * or possiby at the end of the stream (that is, the * _first method of the iterator has been called). */static voidsendstream(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; int n_rrs; isc_buffer_clear(&xfr->buf); isc_buffer_clear(&xfr->txlenbuf); isc_buffer_clear(&xfr->txbuf); if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) { /* * 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); /* * 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_init(qrdataset); 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 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; ISC_LINK_INIT(msgrdl, link); ISC_LIST_INIT(msgrdl->rdata); ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link); result = dns_message_gettemprdataset(msg, &msgrds); if (result != ISC_R_SUCCESS) goto failure; dns_rdataset_init(msgrds); 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; } if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) { CHECK(dns_compress_init(&cctx, -1, xfr->mctx)); 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 voidxfrout_ctx_destroy(xfrout_ctx_t **xfrp) { xfrout_ctx_t *xfr = *xfrp; INSIST(xfr->sends == 0); xfr->client->shutdown = NULL; xfr->client->shutdown_arg = NULL; if (xfr->stream != NULL) xfr->stream->methods->destroy(&xfr->stream); if (xfr->buf.base != NULL) isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length); if (xfr->txmem != NULL) isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen); if (xfr->lasttsig != NULL) isc_buffer_free(&xfr->lasttsig); if (xfr->quota != NULL) isc_quota_detach(&xfr->quota); if (xfr->ver != NULL) dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE); if (xfr->db != NULL) dns_db_detach(&xfr->db); ns_client_detach(&xfr->client); isc_mem_put(xfr->mctx, xfr, sizeof(*xfr)); *xfrp = NULL;}static voidxfrout_senddone(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *)event; xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg; isc_result_t evresult = sev->result; UNUSED(task); INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); isc_event_free(&event); xfr->sends--; INSIST(xfr->sends == 0); (void)isc_timer_touch(xfr->client->timer); if (xfr->shuttingdown == ISC_TRUE) { xfrout_maybe_destroy(xfr); } else if (evresult != ISC_R_SUCCESS) { xfrout_fail(xfr, evresult, "send"); } else if (xfr->end_of_stream == ISC_FALSE) { sendstream(xfr); } else { /* End of zone transfer stream. */ xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic); ns_client_next(xfr->client, ISC_R_SUCCESS); xfrout_ctx_destroy(&xfr); }}static voidxfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) { xfr->shuttingdown = ISC_TRUE; xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s", msg, isc_result_totext(result)); xfrout_maybe_destroy(xfr);}static voidxfrout_maybe_destroy(xfrout_ctx_t *xfr) { INSIST(xfr->shuttingdown == ISC_TRUE); if (xfr->sends > 0) { /* * If we are currently sending, cancel it and wait for * cancel event before destroying the context. */ isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task, ISC_SOCKCANCEL_SEND); } else { ns_client_next(xfr->client, ISC_R_CANCELED); xfrout_ctx_destroy(&xfr); }}static voidxfrout_client_shutdown(void *arg, isc_result_t result) { xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg; xfrout_fail(xfr, result, "aborted");}/* * Log outgoing zone transfer messages in a format like * <client>: transfer of <zone>: <message> */static voidxfrout_logv(ns_client_t *client, dns_name_t *zonename, dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap) ISC_FORMAT_PRINTF(5, 0);static voidxfrout_logv(ns_client_t *client, dns_name_t *zonename, dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap){ char msgbuf[2048]; char namebuf[DNS_NAME_FORMATSIZE]; char classbuf[DNS_RDATACLASS_FORMATSIZE]; dns_name_format(zonename, namebuf, sizeof(namebuf)); dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf)); vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, level, "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf);}/* * Logging function for use when a xfrout_ctx_t has not yet been created. */static voidxfrout_log1(ns_client_t *client, dns_name_t *zonename, dns_rdataclass_t rdclass, int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); xfrout_logv(client, zonename, rdclass, level, fmt, ap); va_end(ap);}/* * Logging function for use when there is a xfrout_ctx_t. */static voidxfrout_log(xfrout_ctx_t *xfr, unsigned int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap); va_end(ap);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -