NetBSD/external/bsd/nsd/dist/axfr.c

284 lines
7.7 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"
#include "ixfr.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, int wstats)
{
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. */
if(wstats) {
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;
}
if(wstats) {
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) {
size_t oldmaxlen = query->maxlen;
if(total_added == 0)
/* RR > 16K can be first RR */
query->maxlen = (query->tcp?TCP_MAX_MESSAGE_LEN:UDP_MAX_MESSAGE_LEN);
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(total_added == 0) {
query->maxlen = oldmaxlen;
if(query_overflow(query)) {
if(added) {
++total_added;
++query->axfr_current_rr;
goto return_answer;
}
}
}
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;
}
/* See if the query can be admitted. */
static int axfr_ixfr_can_admit_query(struct nsd* nsd, struct query* q)
{
struct acl_options *acl = NULL;
struct zone_options* zone_opt;
zone_opt = zone_options_find(nsd->options, q->qname);
if(zone_opt && q->is_proxied && acl_check_incoming_block_proxy(
zone_opt->pattern->provide_xfr, q, &acl) == -1) {
/* the proxy address is blocked */
if (verbosity >= 2) {
char address[128], proxy[128];
addr2str(&q->client_addr, address, sizeof(address));
addr2str(&q->remote_addr, proxy, sizeof(proxy));
VERBOSITY(2, (LOG_INFO, "%s for %s from %s via proxy %s refused because of proxy, %s %s",
(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
dname_to_string(q->qname, NULL),
address, proxy,
(acl?acl->ip_address_spec:"."),
(acl ? ( acl->nokey ? "NOKEY"
: acl->blocked ? "BLOCKED"
: acl->key_name )
: "no acl matches")));
}
RCODE_SET(q->packet, RCODE_REFUSE);
/* RFC8914 - Extended DNS Errors
* 4.19. Extended DNS Error Code 18 - Prohibited */
q->edns.ede = EDE_PROHIBITED;
return 0;
}
if(!zone_opt ||
acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1)
{
if (verbosity >= 2) {
char a[128];
addr2str(&q->client_addr, a, sizeof(a));
VERBOSITY(2, (LOG_INFO, "%s for %s from %s refused, %s",
(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches"));
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s refused, %s",
(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
acl?"blocked":"no acl matches"));
if (!zone_opt) {
RCODE_SET(q->packet, RCODE_NOTAUTH);
} else {
RCODE_SET(q->packet, RCODE_REFUSE);
/* RFC8914 - Extended DNS Errors
* 4.19. Extended DNS Error Code 18 - Prohibited */
q->edns.ede = EDE_PROHIBITED;
}
return 0;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s admitted acl %s %s",
(q->qtype==TYPE_AXFR?"axfr":"ixfr"),
acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY"));
if (verbosity >= 1) {
char a[128];
addr2str(&q->client_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 1;
}
/*
* Answer if this is an AXFR or IXFR query.
*/
query_state_type
answer_axfr_ixfr(struct nsd *nsd, struct query *q)
{
/* Is it AXFR? */
switch (q->qtype) {
case TYPE_AXFR:
if (q->tcp) {
if(!axfr_ixfr_can_admit_query(nsd, q))
return QUERY_PROCESSED;
return query_axfr(nsd, q, 1);
}
/* AXFR over UDP queries are discarded. */
RCODE_SET(q->packet, RCODE_IMPL);
return QUERY_PROCESSED;
case TYPE_IXFR:
if(!axfr_ixfr_can_admit_query(nsd, q)) {
/* get rid of authority section, if present */
NSCOUNT_SET(q->packet, 0);
ARCOUNT_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);
}
return QUERY_PROCESSED;
}
return query_ixfr(nsd, q);
default:
return QUERY_DISCARDED;
}
}