📄 xfrin.c
字号:
/* * Build an *XFR request and send its length prefix. */static isc_result_txfrin_send_request(dns_xfrin_ctx_t *xfr) { isc_result_t result; isc_region_t region; isc_region_t lregion; dns_rdataset_t *qrdataset = NULL; dns_message_t *msg = NULL; unsigned char length[2]; dns_difftuple_t *soatuple = NULL; dns_name_t *qname = NULL; dns_dbversion_t *ver = NULL; dns_name_t *msgsoaname = NULL; /* Create the request message */ CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &msg)); CHECK(dns_message_settsigkey(msg, xfr->tsigkey)); /* Create a name for the question section. */ CHECK(dns_message_gettempname(msg, &qname)); dns_name_init(qname, NULL); dns_name_clone(&xfr->name, qname); /* Formulate the question and attach it to the question name. */ CHECK(dns_message_gettemprdataset(msg, &qrdataset)); dns_rdataset_init(qrdataset); dns_rdataset_makequestion(qrdataset, xfr->rdclass, xfr->reqtype); ISC_LIST_APPEND(qname->list, qrdataset, link); qrdataset = NULL; dns_message_addname(msg, qname, DNS_SECTION_QUESTION); qname = NULL; if (xfr->reqtype == dns_rdatatype_ixfr) { /* Get the SOA and add it to the authority section. */ /* XXX is using the current version the right thing? */ dns_db_currentversion(xfr->db, &ver); CHECK(dns_db_createsoatuple(xfr->db, ver, xfr->mctx, DNS_DIFFOP_EXISTS, &soatuple)); xfr->ixfr.request_serial = dns_soa_getserial(&soatuple->rdata); xfr->ixfr.current_serial = xfr->ixfr.request_serial; xfrin_log(xfr, ISC_LOG_DEBUG(3), "requesting IXFR for serial %u", xfr->ixfr.request_serial); CHECK(tuple2msgname(soatuple, msg, &msgsoaname)); dns_message_addname(msg, msgsoaname, DNS_SECTION_AUTHORITY); } xfr->checkid = ISC_TRUE; xfr->id++; msg->id = xfr->id; CHECK(render(msg, xfr->mctx, &xfr->qbuffer)); /* * Free the last tsig, if there is one. */ if (xfr->lasttsig != NULL) isc_buffer_free(&xfr->lasttsig); /* * Save the query TSIG and don't let message_destroy free it. */ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); isc_buffer_usedregion(&xfr->qbuffer, ®ion); INSIST(region.length <= 65535); length[0] = region.length >> 8; length[1] = region.length & 0xFF; lregion.base = length; lregion.length = 2; CHECK(isc_socket_send(xfr->socket, &lregion, xfr->task, xfrin_sendlen_done, xfr)); xfr->sends++; failure: if (qname != NULL) dns_message_puttempname(msg, &qname); if (qrdataset != NULL) dns_message_puttemprdataset(msg, &qrdataset); if (msg != NULL) dns_message_destroy(&msg); if (soatuple != NULL) dns_difftuple_free(&soatuple); if (ver != NULL) dns_db_closeversion(xfr->db, &ver, ISC_FALSE); return (result);}/* XXX there should be library support for sending DNS TCP messages */static voidxfrin_sendlen_done(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *) event; dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg; isc_result_t evresult = sev->result; isc_result_t result; isc_region_t region; REQUIRE(VALID_XFRIN(xfr)); UNUSED(task); INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); isc_event_free(&event); xfr->sends--; if (xfr->shuttingdown) { maybe_free(xfr); return; } xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request length prefix"); CHECK(evresult); isc_buffer_usedregion(&xfr->qbuffer, ®ion); CHECK(isc_socket_send(xfr->socket, ®ion, xfr->task, xfrin_send_done, xfr)); xfr->sends++; failure: if (result != ISC_R_SUCCESS) xfrin_fail(xfr, result, "failed sending request length prefix");}static voidxfrin_send_done(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sev = (isc_socketevent_t *) event; dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg; isc_result_t result; REQUIRE(VALID_XFRIN(xfr)); UNUSED(task); INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE); xfr->sends--; xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request data"); CHECK(sev->result); CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task, xfrin_recv_done, xfr)); xfr->recvs++; failure: isc_event_free(&event); if (result != ISC_R_SUCCESS) xfrin_fail(xfr, result, "failed sending request data");}static voidxfrin_recv_done(isc_task_t *task, isc_event_t *ev) { dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) ev->ev_arg; isc_result_t result; dns_message_t *msg = NULL; dns_name_t *name; dns_tcpmsg_t *tcpmsg; dns_name_t *tsigowner = NULL; REQUIRE(VALID_XFRIN(xfr)); UNUSED(task); INSIST(ev->ev_type == DNS_EVENT_TCPMSG); tcpmsg = ev->ev_sender; isc_event_free(&ev); xfr->recvs--; if (xfr->shuttingdown) { maybe_free(xfr); return; } CHECK(tcpmsg->result); xfrin_log(xfr, ISC_LOG_DEBUG(7), "received %u bytes", tcpmsg->buffer.used); CHECK(isc_timer_touch(xfr->timer)); CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTPARSE, &msg)); CHECK(dns_message_settsigkey(msg, xfr->tsigkey)); CHECK(dns_message_setquerytsig(msg, xfr->lasttsig)); msg->tsigctx = xfr->tsigctx; if (xfr->nmsg > 0) msg->tcp_continuation = 1; result = dns_message_parse(msg, &tcpmsg->buffer, DNS_MESSAGEPARSE_PRESERVEORDER); if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror || (xfr->checkid && msg->id != xfr->id)) { if (result == ISC_R_SUCCESS) result = ISC_RESULTCLASS_DNSRCODE + msg->rcode; /*XXX*/ if (result == ISC_R_SUCCESS || result == DNS_R_NOERROR) result = DNS_R_UNEXPECTEDID; if (xfr->reqtype == dns_rdatatype_axfr || xfr->reqtype == dns_rdatatype_soa) FAIL(result); xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR", isc_result_totext(result)); try_axfr: dns_message_destroy(&msg); xfrin_reset(xfr); xfr->reqtype = dns_rdatatype_axfr; xfr->state = XFRST_INITIALSOA; (void)xfrin_start(xfr); return; } /* * Does the server know about IXFR? If it doesn't we will get * a message with a empty answer section or a potentially a CNAME / * DNAME, the later is handled by xfr_rr() which will return FORMERR * if the first RR in the answer section is not a SOA record. */ if (xfr->reqtype == dns_rdatatype_ixfr && xfr->state == XFRST_INITIALSOA && msg->counts[DNS_SECTION_ANSWER] == 0) { xfrin_log(xfr, ISC_LOG_DEBUG(3), "empty answer section, retrying with AXFR"); goto try_axfr; } if (xfr->reqtype == dns_rdatatype_soa && (msg->flags & DNS_MESSAGEFLAG_AA) == 0) { FAIL(DNS_R_NOTAUTHORITATIVE); } result = dns_message_checksig(msg, dns_zone_getview(xfr->zone)); if (result != ISC_R_SUCCESS) { xfrin_log(xfr, ISC_LOG_DEBUG(3), "TSIG check failed: %s", isc_result_totext(result)); FAIL(result); } for (result = dns_message_firstname(msg, DNS_SECTION_ANSWER); result == ISC_R_SUCCESS; result = dns_message_nextname(msg, DNS_SECTION_ANSWER)) { dns_rdataset_t *rds; name = NULL; dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); for (rds = ISC_LIST_HEAD(name->list); rds != NULL; rds = ISC_LIST_NEXT(rds, link)) { for (result = dns_rdataset_first(rds); result == ISC_R_SUCCESS; result = dns_rdataset_next(rds)) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(rds, &rdata); CHECK(xfr_rr(xfr, name, rds->ttl, &rdata)); } } } if (result != ISC_R_NOMORE) goto failure; if (dns_message_gettsig(msg, &tsigowner) != NULL) { /* * Reset the counter. */ xfr->sincetsig = 0; /* * Free the last tsig, if there is one. */ if (xfr->lasttsig != NULL) isc_buffer_free(&xfr->lasttsig); /* * Update the last tsig pointer. */ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); } else if (dns_message_gettsigkey(msg) != NULL) { xfr->sincetsig++; if (xfr->sincetsig > 100 || xfr->nmsg == 0 || xfr->state == XFRST_END) { result = DNS_R_EXPECTEDTSIG; goto failure; } } /* * Update the number of messages received. */ xfr->nmsg++; /* * Copy the context back. */ xfr->tsigctx = msg->tsigctx; dns_message_destroy(&msg); if (xfr->state == XFRST_END) { /* * Inform the caller we succeeded. */ if (xfr->done != NULL) { (xfr->done)(xfr->zone, ISC_R_SUCCESS); xfr->done = NULL; } /* * We should have no outstanding events at this * point, thus maybe_free() should succeed. */ xfr->shuttingdown = ISC_TRUE; maybe_free(xfr); } else { /* * Read the next message. */ CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task, xfrin_recv_done, xfr)); xfr->recvs++; } return; failure: if (msg != NULL) dns_message_destroy(&msg); if (result != ISC_R_SUCCESS) xfrin_fail(xfr, result, "failed while receiving responses");}static voidxfrin_timeout(isc_task_t *task, isc_event_t *event) { dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg; REQUIRE(VALID_XFRIN(xfr)); UNUSED(task); isc_event_free(&event); /* * This will log "giving up: timeout". */ xfrin_fail(xfr, ISC_R_TIMEDOUT, "giving up");}static voidmaybe_free(dns_xfrin_ctx_t *xfr) { REQUIRE(VALID_XFRIN(xfr)); if (! xfr->shuttingdown || xfr->refcount != 0 || xfr->connects != 0 || xfr->sends != 0 || xfr->recvs != 0) return; xfrin_log(xfr, ISC_LOG_INFO, "end of transfer"); if (xfr->socket != NULL) isc_socket_detach(&xfr->socket); if (xfr->timer != NULL) isc_timer_detach(&xfr->timer); if (xfr->task != NULL) isc_task_detach(&xfr->task); if (xfr->tsigkey != NULL) dns_tsigkey_detach(&xfr->tsigkey); if (xfr->lasttsig != NULL) isc_buffer_free(&xfr->lasttsig); dns_diff_clear(&xfr->diff); if (xfr->ixfr.journal != NULL) dns_journal_destroy(&xfr->ixfr.journal); if (xfr->axfr.add_private != NULL) (void)dns_db_endload(xfr->db, &xfr->axfr.add_private); if (xfr->tcpmsg_valid) dns_tcpmsg_invalidate(&xfr->tcpmsg); if ((xfr->name.attributes & DNS_NAMEATTR_DYNAMIC) != 0) dns_name_free(&xfr->name, xfr->mctx); if (xfr->ver != NULL) dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE); if (xfr->db != NULL) dns_db_detach(&xfr->db); if (xfr->zone != NULL) dns_zone_idetach(&xfr->zone); isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));}/* * Log incoming zone transfer messages in a format like * transfer of <zone> from <address>: <message> */static voidxfrin_logv(int level, dns_name_t *zonename, dns_rdataclass_t rdclass, isc_sockaddr_t *masteraddr, const char *fmt, va_list ap){ char zntext[DNS_NAME_FORMATSIZE]; char mastertext[ISC_SOCKADDR_FORMATSIZE]; char classtext[DNS_RDATACLASS_FORMATSIZE]; char msgtext[2048]; dns_name_format(zonename, zntext, sizeof(zntext)); dns_rdataclass_format(rdclass, classtext, sizeof(classtext)); isc_sockaddr_format(masteraddr, mastertext, sizeof(mastertext)); vsnprintf(msgtext, sizeof(msgtext), fmt, ap); isc_log_write(dns_lctx, DNS_LOGCATEGORY_XFER_IN, DNS_LOGMODULE_XFER_IN, level, "transfer of '%s/%s' from %s: %s", zntext, classtext, mastertext, msgtext);}/* * Logging function for use when a xfrin_ctx_t has not yet been created. */static voidxfrin_log1(int level, dns_name_t *zonename, dns_rdataclass_t rdclass, isc_sockaddr_t *masteraddr, const char *fmt, ...){ va_list ap; if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) return; va_start(ap, fmt); xfrin_logv(level, zonename, rdclass, masteraddr, fmt, ap); va_end(ap);}/* * Logging function for use when there is a xfrin_ctx_t. */static voidxfrin_log(dns_xfrin_ctx_t *xfr, unsigned int level, const char *fmt, ...){ va_list ap; if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) return; va_start(ap, fmt); xfrin_logv(level, &xfr->name, xfr->rdclass, &xfr->masteraddr, fmt, ap); va_end(ap);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -