aboutsummaryrefslogtreecommitdiff
path: root/services/authzone.c
diff options
context:
space:
mode:
Diffstat (limited to 'services/authzone.c')
-rw-r--r--services/authzone.c146
1 files changed, 105 insertions, 41 deletions
diff --git a/services/authzone.c b/services/authzone.c
index 4d0dd1b5ae9c..a87c2274fb9e 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -88,6 +88,9 @@
#define AUTH_HTTPS_PORT 443
/* max depth for nested $INCLUDEs */
#define MAX_INCLUDE_DEPTH 10
+/** number of timeouts before we fallback from IXFR to AXFR,
+ * because some versions of servers (eg. dnsmasq) drop IXFR packets. */
+#define NUM_TIMEOUTS_FALLBACK_IXFR 3
/** pick up nextprobe task to start waiting to perform transfer actions */
static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
@@ -1450,11 +1453,13 @@ az_remove_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen,
* The lineno is set at 1 and then increased by the function.
* @param fname: file name.
* @param depth: recursion depth for includes
+ * @param cfg: config for chroot.
* returns false on failure, has printed an error message
*/
static int
az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
- struct sldns_file_parse_state* state, char* fname, int depth)
+ struct sldns_file_parse_state* state, char* fname, int depth,
+ struct config_file* cfg)
{
size_t rr_len, dname_len;
int status;
@@ -1480,6 +1485,11 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
/* skip spaces */
while(*incfile == ' ' || *incfile == '\t')
incfile++;
+ /* adjust for chroot on include file */
+ if(cfg->chrootdir && cfg->chrootdir[0] &&
+ strncmp(incfile, cfg->chrootdir,
+ strlen(cfg->chrootdir)) == 0)
+ incfile += strlen(cfg->chrootdir);
incfile = strdup(incfile);
if(!incfile) {
log_err("malloc failure");
@@ -1490,7 +1500,7 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
inc = fopen(incfile, "r");
if(!inc) {
log_err("%s:%d cannot open include "
- "file %s: %s", z->zonefile,
+ "file %s: %s", fname,
lineno_orig, incfile,
strerror(errno));
free(incfile);
@@ -1498,7 +1508,7 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
}
/* recurse read that file now */
if(!az_parse_file(z, inc, rr, rrbuflen,
- state, incfile, depth+1)) {
+ state, incfile, depth+1, cfg)) {
log_err("%s:%d cannot parse include "
"file %s", fname,
lineno_orig, incfile);
@@ -1538,30 +1548,36 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
}
int
-auth_zone_read_zonefile(struct auth_zone* z)
+auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
struct sldns_file_parse_state state;
+ char* zfilename;
FILE* in;
if(!z || !z->zonefile || z->zonefile[0]==0)
return 1; /* no file, or "", nothing to read */
+
+ zfilename = z->zonefile;
+ if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
+ cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+ zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
dname_str(z->name, nm);
- verbose(VERB_ALGO, "read zonefile %s for %s", z->zonefile, nm);
+ verbose(VERB_ALGO, "read zonefile %s for %s", zfilename, nm);
}
- in = fopen(z->zonefile, "r");
+ in = fopen(zfilename, "r");
if(!in) {
char* n = sldns_wire2str_dname(z->name, z->namelen);
if(z->zone_is_slave && errno == ENOENT) {
/* we fetch the zone contents later, no file yet */
verbose(VERB_ALGO, "no zonefile %s for %s",
- z->zonefile, n?n:"error");
+ zfilename, n?n:"error");
free(n);
return 1;
}
log_err("cannot open zonefile %s for %s: %s",
- z->zonefile, n?n:"error", strerror(errno));
+ zfilename, n?n:"error", strerror(errno));
free(n);
return 0;
}
@@ -1579,10 +1595,10 @@ auth_zone_read_zonefile(struct auth_zone* z)
state.origin_len = z->namelen;
}
/* parse the (toplevel) file */
- if(!az_parse_file(z, in, rr, sizeof(rr), &state, z->zonefile, 0)) {
+ if(!az_parse_file(z, in, rr, sizeof(rr), &state, zfilename, 0, cfg)) {
char* n = sldns_wire2str_dname(z->name, z->namelen);
log_err("error parsing zonefile %s for %s",
- z->zonefile, n?n:"error");
+ zfilename, n?n:"error");
free(n);
fclose(in);
return 0;
@@ -1710,13 +1726,13 @@ int auth_zone_write_file(struct auth_zone* z, const char* fname)
/** read all auth zones from file (if they have) */
static int
-auth_zones_read_zones(struct auth_zones* az)
+auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg)
{
struct auth_zone* z;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
- if(!auth_zone_read_zonefile(z)) {
+ if(!auth_zone_read_zonefile(z, cfg)) {
lock_rw_unlock(&z->lock);
lock_rw_unlock(&az->lock);
return 0;
@@ -1953,7 +1969,7 @@ int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
}
}
az_delete_deleted_zones(az);
- if(!auth_zones_read_zones(az))
+ if(!auth_zones_read_zones(az, cfg))
return 0;
if(setup) {
if(!auth_zones_setup_zones(az))
@@ -1966,7 +1982,7 @@ int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
* @param at: transfer structure with chunks list. The chunks and their
* data are freed.
*/
-void
+static void
auth_chunks_delete(struct auth_transfer* at)
{
if(at->chunks_first) {
@@ -2605,7 +2621,7 @@ az_nsec3_hashname(struct auth_zone* z, uint8_t* hashname, size_t* hashnmlen,
}
/** Find the datanode that covers the nsec3hash-name */
-struct auth_data*
+static struct auth_data*
az_nsec3_findnode(struct auth_zone* z, uint8_t* hashnm, size_t hashnmlen)
{
struct query_info qinfo;
@@ -2730,13 +2746,14 @@ az_nsec3_insert(struct auth_zone* z, struct regional* region,
* that is an exact match that should exist for it.
* If that does not exist, a higher exact match + nxproof is enabled
* (for some sort of opt-out empty nonterminal cases).
+ * ceproof: include ce proof NSEC3 (omitted for wildcard replies).
* nxproof: include denial of the qname.
* wcproof: include denial of wildcard (wildcard.ce).
*/
static int
az_add_nsec3_proof(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, uint8_t* cenm, size_t cenmlen, uint8_t* qname,
- size_t qname_len, int nxproof, int wcproof)
+ size_t qname_len, int ceproof, int nxproof, int wcproof)
{
int algo;
size_t iter, saltlen;
@@ -2748,11 +2765,13 @@ az_add_nsec3_proof(struct auth_zone* z, struct regional* region,
if(!az_nsec3_param(z, &algo, &iter, &salt, &saltlen))
return 1; /* no nsec3 */
/* find ce that has an NSEC3 */
- node = az_nsec3_find_ce(z, &cenm, &cenmlen, &no_exact_ce,
- algo, iter, salt, saltlen);
- if(no_exact_ce) nxproof = 1;
- if(!az_nsec3_insert(z, region, msg, node))
- return 0;
+ if(ceproof) {
+ node = az_nsec3_find_ce(z, &cenm, &cenmlen, &no_exact_ce,
+ algo, iter, salt, saltlen);
+ if(no_exact_ce) nxproof = 1;
+ if(!az_nsec3_insert(z, region, msg, node))
+ return 0;
+ }
if(nxproof) {
uint8_t* nx;
@@ -2828,7 +2847,7 @@ az_generate_any_answer(struct auth_zone* z, struct regional* region,
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
added++;
}
- if(added == 0 && node->rrsets) {
+ if(added == 0 && node && node->rrsets) {
if(!msg_add_rrset_an(z, region, msg, node,
node->rrsets)) return 0;
}
@@ -2897,7 +2916,7 @@ az_generate_notype_answer(struct auth_zone* z, struct regional* region,
/* DNSSEC denial NSEC3 */
if(!az_add_nsec3_proof(z, region, msg, node->name,
node->namelen, msg->qinfo.qname,
- msg->qinfo.qname_len, 0, 0))
+ msg->qinfo.qname_len, 1, 0, 0))
return 0;
}
return 1;
@@ -2924,7 +2943,7 @@ az_generate_referral_answer(struct auth_zone* z, struct regional* region,
} else {
if(!az_add_nsec3_proof(z, region, msg, ce->name,
ce->namelen, msg->qinfo.qname,
- msg->qinfo.qname_len, 0, 0))
+ msg->qinfo.qname_len, 1, 0, 0))
return 0;
}
}
@@ -2995,9 +3014,12 @@ az_generate_wildcard_answer(struct auth_zone* z, struct query_info* qinfo,
if((nsec=az_find_nsec_cover(z, &node)) != NULL) {
if(!msg_add_rrset_ns(z, region, msg, node, nsec)) return 0;
} else if(ce) {
- if(!az_add_nsec3_proof(z, region, msg, ce->name,
- ce->namelen, msg->qinfo.qname,
- msg->qinfo.qname_len, 1, 0))
+ uint8_t* wildup = wildcard->name;
+ size_t wilduplen= wildcard->namelen;
+ dname_remove_label(&wildup, &wilduplen);
+ if(!az_add_nsec3_proof(z, region, msg, wildup,
+ wilduplen, msg->qinfo.qname,
+ msg->qinfo.qname_len, 0, 1, 0))
return 0;
}
@@ -3023,7 +3045,7 @@ az_generate_nxdomain_answer(struct auth_zone* z, struct regional* region,
} else if(ce) {
if(!az_add_nsec3_proof(z, region, msg, ce->name,
ce->namelen, msg->qinfo.qname,
- msg->qinfo.qname_len, 1, 1))
+ msg->qinfo.qname_len, 1, 1, 1))
return 0;
}
return 1;
@@ -3163,6 +3185,11 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
*fallback = 1;
return 0;
}
+ if(z->zone_expired) {
+ *fallback = z->fallback_enabled;
+ lock_rw_unlock(&z->lock);
+ return 0;
+ }
/* see what answer that zone would generate */
r = auth_zone_generate_answer(z, qinfo, region, msg, fallback);
lock_rw_unlock(&z->lock);
@@ -3250,6 +3277,19 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
lock_rw_unlock(&z->lock);
return 0;
}
+ if(z->zone_expired) {
+ if(z->fallback_enabled) {
+ lock_rw_unlock(&z->lock);
+ return 0;
+ }
+ lock_rw_unlock(&z->lock);
+ lock_rw_wrlock(&az->lock);
+ az->num_query_down++;
+ lock_rw_unlock(&az->lock);
+ auth_error_encode(qinfo, env, edns, repinfo, buf, temp,
+ LDNS_RCODE_SERVFAIL);
+ return 1;
+ }
/* answer it from zone z */
r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback);
@@ -4773,8 +4813,10 @@ auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
static void
xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
{
+ struct config_file* cfg = env->cfg;
struct auth_zone* z;
char tmpfile[1024];
+ char* zfilename;
lock_basic_unlock(&xfr->lock);
/* get lock again, so it is a readlock and concurrently queries
@@ -4792,20 +4834,24 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&env->auth_zones->lock);
- if(z->zonefile == NULL) {
+ if(z->zonefile == NULL || z->zonefile[0] == 0) {
lock_rw_unlock(&z->lock);
/* no write needed, no zonefile set */
return;
}
+ zfilename = z->zonefile;
+ if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
+ cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
+ zfilename += strlen(cfg->chrootdir);
/* write to tempfile first */
- if((size_t)strlen(z->zonefile) + 16 > sizeof(tmpfile)) {
+ if((size_t)strlen(zfilename) + 16 > sizeof(tmpfile)) {
verbose(VERB_ALGO, "tmpfilename too long, cannot update "
- " zonefile %s", z->zonefile);
+ " zonefile %s", zfilename);
lock_rw_unlock(&z->lock);
return;
}
- snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", z->zonefile,
+ snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", zfilename,
(unsigned)getpid());
if(xfr->task_transfer->master->http) {
/* use the stored chunk list to write them */
@@ -4818,8 +4864,8 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
lock_rw_unlock(&z->lock);
return;
}
- if(rename(tmpfile, z->zonefile) < 0) {
- log_err("could not rename(%s, %s): %s", tmpfile, z->zonefile,
+ if(rename(tmpfile, zfilename) < 0) {
+ log_err("could not rename(%s, %s): %s", tmpfile, zfilename,
strerror(errno));
unlink(tmpfile);
lock_rw_unlock(&z->lock);
@@ -4951,12 +4997,12 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.qtype = LDNS_RR_TYPE_AAAA;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
- char buf[512];
+ char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(xfr->name, buf2);
- snprintf(buf, sizeof(buf), "auth zone %s: master lookup"
+ snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_transfer", buf2);
- log_query_info(VERB_ALGO, buf, &qinfo);
+ log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;
@@ -5593,15 +5639,33 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err,
* and continue task_transfer*/
verbose(VERB_ALGO, "xfr stopped, connection lost to %s",
xfr->task_transfer->master->host);
+
+ /* see if IXFR caused the failure, if so, try AXFR */
+ if(xfr->task_transfer->on_ixfr) {
+ xfr->task_transfer->ixfr_possible_timeout_count++;
+ if(xfr->task_transfer->ixfr_possible_timeout_count >=
+ NUM_TIMEOUTS_FALLBACK_IXFR) {
+ verbose(VERB_ALGO, "xfr to %s, fallback "
+ "from IXFR to AXFR (because of timeouts)",
+ xfr->task_transfer->master->host);
+ xfr->task_transfer->ixfr_fail = 1;
+ gonextonfail = 0;
+ }
+ }
+
failed:
/* delete transferred data from list */
auth_chunks_delete(xfr->task_transfer);
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
- xfr_transfer_nextmaster(xfr);
+ if(gonextonfail)
+ xfr_transfer_nextmaster(xfr);
xfr_transfer_nexttarget_or_end(xfr, env);
return 0;
}
+ /* note that IXFR worked without timeout */
+ if(xfr->task_transfer->on_ixfr)
+ xfr->task_transfer->ixfr_possible_timeout_count = 0;
/* handle returned packet */
/* if it fails, cleanup and end this transfer */
@@ -5973,12 +6037,12 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.qtype = LDNS_RR_TYPE_AAAA;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
- char buf[512];
+ char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(xfr->name, buf2);
- snprintf(buf, sizeof(buf), "auth zone %s: master lookup"
+ snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_probe", buf2);
- log_query_info(VERB_ALGO, buf, &qinfo);
+ log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;