225 lines
6.0 KiB
C
225 lines
6.0 KiB
C
/*
|
|
* axfr.c -- generating AXFR responses.
|
|
*
|
|
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
|
|
*
|
|
* See LICENSE for the license.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "axfr.h"
|
|
#include "dns.h"
|
|
#include "packet.h"
|
|
#include "options.h"
|
|
|
|
/* draft-ietf-dnsop-rfc2845bis-06, section 5.3.1 says to sign every packet */
|
|
#define AXFR_TSIG_SIGN_EVERY_NTH 0 /* tsig sign every N packets. */
|
|
|
|
query_state_type
|
|
query_axfr(struct nsd *nsd, struct query *query)
|
|
{
|
|
domain_type *closest_match;
|
|
domain_type *closest_encloser;
|
|
int exact;
|
|
int added;
|
|
uint16_t total_added = 0;
|
|
|
|
if (query->axfr_is_done)
|
|
return QUERY_PROCESSED;
|
|
|
|
if (query->maxlen > AXFR_MAX_MESSAGE_LEN)
|
|
query->maxlen = AXFR_MAX_MESSAGE_LEN;
|
|
|
|
assert(!query_overflow(query));
|
|
/* only keep running values for most packets */
|
|
query->tsig_prepare_it = 0;
|
|
query->tsig_update_it = 1;
|
|
if(query->tsig_sign_it) {
|
|
/* prepare for next updates */
|
|
query->tsig_prepare_it = 1;
|
|
query->tsig_sign_it = 0;
|
|
}
|
|
|
|
if (query->axfr_zone == NULL) {
|
|
domain_type* qdomain;
|
|
/* Start AXFR. */
|
|
STATUP(nsd, raxfr);
|
|
exact = namedb_lookup(nsd->db,
|
|
query->qname,
|
|
&closest_match,
|
|
&closest_encloser);
|
|
|
|
qdomain = closest_encloser;
|
|
query->axfr_zone = domain_find_zone(nsd->db, closest_encloser);
|
|
|
|
if (!exact
|
|
|| query->axfr_zone == NULL
|
|
|| query->axfr_zone->apex != qdomain
|
|
|| query->axfr_zone->soa_rrset == NULL)
|
|
{
|
|
/* No SOA no transfer */
|
|
RCODE_SET(query->packet, RCODE_NOTAUTH);
|
|
return QUERY_PROCESSED;
|
|
}
|
|
ZTATUP(nsd, query->axfr_zone, raxfr);
|
|
|
|
query->axfr_current_domain = qdomain;
|
|
query->axfr_current_rrset = NULL;
|
|
query->axfr_current_rr = 0;
|
|
if(query->tsig.status == TSIG_OK) {
|
|
query->tsig_sign_it = 1; /* sign first packet in stream */
|
|
}
|
|
|
|
query_add_compression_domain(query, qdomain, QHEADERSZ);
|
|
|
|
assert(query->axfr_zone->soa_rrset->rr_count == 1);
|
|
added = packet_encode_rr(query,
|
|
query->axfr_zone->apex,
|
|
&query->axfr_zone->soa_rrset->rrs[0],
|
|
query->axfr_zone->soa_rrset->rrs[0].ttl);
|
|
if (!added) {
|
|
/* XXX: This should never happen... generate error code? */
|
|
abort();
|
|
}
|
|
++total_added;
|
|
} else {
|
|
/*
|
|
* Query name and EDNS need not be repeated after the
|
|
* first response packet.
|
|
*/
|
|
query->edns.status = EDNS_NOT_PRESENT;
|
|
buffer_set_limit(query->packet, QHEADERSZ);
|
|
QDCOUNT_SET(query->packet, 0);
|
|
query_prepare_response(query);
|
|
}
|
|
|
|
/* Add zone RRs until answer is full. */
|
|
while (query->axfr_current_domain != NULL &&
|
|
domain_is_subdomain(query->axfr_current_domain,
|
|
query->axfr_zone->apex))
|
|
{
|
|
if (!query->axfr_current_rrset) {
|
|
query->axfr_current_rrset = domain_find_any_rrset(
|
|
query->axfr_current_domain,
|
|
query->axfr_zone);
|
|
query->axfr_current_rr = 0;
|
|
}
|
|
while (query->axfr_current_rrset) {
|
|
if (query->axfr_current_rrset != query->axfr_zone->soa_rrset
|
|
&& query->axfr_current_rrset->zone == query->axfr_zone)
|
|
{
|
|
while (query->axfr_current_rr < query->axfr_current_rrset->rr_count) {
|
|
added = packet_encode_rr(
|
|
query,
|
|
query->axfr_current_domain,
|
|
&query->axfr_current_rrset->rrs[query->axfr_current_rr],
|
|
query->axfr_current_rrset->rrs[query->axfr_current_rr].ttl);
|
|
if (!added)
|
|
goto return_answer;
|
|
++total_added;
|
|
++query->axfr_current_rr;
|
|
}
|
|
}
|
|
|
|
query->axfr_current_rrset = query->axfr_current_rrset->next;
|
|
query->axfr_current_rr = 0;
|
|
}
|
|
assert(query->axfr_current_domain);
|
|
query->axfr_current_domain
|
|
= domain_next(query->axfr_current_domain);
|
|
}
|
|
|
|
/* Add terminating SOA RR. */
|
|
assert(query->axfr_zone->soa_rrset->rr_count == 1);
|
|
added = packet_encode_rr(query,
|
|
query->axfr_zone->apex,
|
|
&query->axfr_zone->soa_rrset->rrs[0],
|
|
query->axfr_zone->soa_rrset->rrs[0].ttl);
|
|
if (added) {
|
|
++total_added;
|
|
query->tsig_sign_it = 1; /* sign last packet */
|
|
query->axfr_is_done = 1;
|
|
}
|
|
|
|
return_answer:
|
|
AA_SET(query->packet);
|
|
ANCOUNT_SET(query->packet, total_added);
|
|
NSCOUNT_SET(query->packet, 0);
|
|
ARCOUNT_SET(query->packet, 0);
|
|
|
|
/* check if it needs tsig signatures */
|
|
if(query->tsig.status == TSIG_OK) {
|
|
#if AXFR_TSIG_SIGN_EVERY_NTH > 0
|
|
if(query->tsig.updates_since_last_prepare >= AXFR_TSIG_SIGN_EVERY_NTH) {
|
|
#endif
|
|
query->tsig_sign_it = 1;
|
|
#if AXFR_TSIG_SIGN_EVERY_NTH > 0
|
|
}
|
|
#endif
|
|
}
|
|
query_clear_compression_tables(query);
|
|
return QUERY_IN_AXFR;
|
|
}
|
|
|
|
/*
|
|
* Answer if this is an AXFR or IXFR query.
|
|
*/
|
|
query_state_type
|
|
answer_axfr_ixfr(struct nsd *nsd, struct query *q)
|
|
{
|
|
struct acl_options *acl = NULL;
|
|
/* Is it AXFR? */
|
|
switch (q->qtype) {
|
|
case TYPE_AXFR:
|
|
if (q->tcp) {
|
|
struct zone_options* zone_opt;
|
|
zone_opt = zone_options_find(nsd->options, q->qname);
|
|
if(!zone_opt ||
|
|
acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1)
|
|
{
|
|
if (verbosity >= 2) {
|
|
char a[128];
|
|
addr2str(&q->addr, a, sizeof(a));
|
|
VERBOSITY(2, (LOG_INFO, "axfr for %s from %s refused, %s",
|
|
dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches"));
|
|
}
|
|
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s",
|
|
acl?"blocked":"no acl matches"));
|
|
if (!zone_opt) {
|
|
RCODE_SET(q->packet, RCODE_NOTAUTH);
|
|
} else {
|
|
RCODE_SET(q->packet, RCODE_REFUSE);
|
|
}
|
|
return QUERY_PROCESSED;
|
|
}
|
|
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s",
|
|
acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
|
|
if (verbosity >= 1) {
|
|
char a[128];
|
|
addr2str(&q->addr, a, sizeof(a));
|
|
VERBOSITY(1, (LOG_INFO, "%s for %s from %s",
|
|
(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
|
|
dname_to_string(q->qname, NULL), a));
|
|
}
|
|
return query_axfr(nsd, q);
|
|
}
|
|
/* AXFR over UDP queries are discarded. */
|
|
RCODE_SET(q->packet, RCODE_IMPL);
|
|
return QUERY_PROCESSED;
|
|
case TYPE_IXFR:
|
|
/* get rid of authority section, if present */
|
|
NSCOUNT_SET(q->packet, 0);
|
|
if(QDCOUNT(q->packet) > 0 && (size_t)QHEADERSZ+4+
|
|
q->qname->name_size <= buffer_limit(q->packet)) {
|
|
buffer_set_position(q->packet, QHEADERSZ+4+
|
|
q->qname->name_size);
|
|
}
|
|
RCODE_SET(q->packet, RCODE_IMPL);
|
|
return QUERY_PROCESSED;
|
|
default:
|
|
return QUERY_DISCARDED;
|
|
}
|
|
}
|