diff options
Diffstat (limited to 'services/authzone.c')
| -rw-r--r-- | services/authzone.c | 653 | 
1 files changed, 605 insertions, 48 deletions
diff --git a/services/authzone.c b/services/authzone.c index 1da924d841ea..1f56ac8cfc87 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -86,13 +86,21 @@  #define AUTH_HTTP_PORT 80  /* auth https port number */  #define AUTH_HTTPS_PORT 443 +/* max depth for nested $INCLUDEs */ +#define MAX_INCLUDE_DEPTH 10  /** pick up nextprobe task to start waiting to perform transfer actions */  static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, -	int failure); +	int failure, int lookup_only);  /** move to sending the probe packets, next if fails. task_probe */  static void xfr_probe_send_or_end(struct auth_xfer* xfr,  	struct module_env* env); +/** pick up probe task with specified(or NULL) destination first, + * or transfer task if nothing to probe, or false if already in progress */ +static int xfr_start_probe(struct auth_xfer* xfr, struct module_env* env, +	struct auth_master* spec); +/** delete xfer structure (not its tree entry) */ +void auth_xfer_delete(struct auth_xfer* xfr);  /** create new dns_msg */  static struct dns_msg* @@ -1437,11 +1445,13 @@ az_remove_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen,   * @param state: parse state with $ORIGIN, $TTL and 'prev-dname' and so on,   *	that is kept between includes.   *	The lineno is set at 1 and then increased by the function. + * @param fname: file name. + * @param depth: recursion depth for includes   * 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) +	struct sldns_file_parse_state* state, char* fname, int depth)  {  	size_t rr_len, dname_len;  	int status; @@ -1459,6 +1469,11 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,  				FILE* inc;  				int lineno_orig = state->lineno;  				char* incfile = (char*)rr + 8; +				if(depth > MAX_INCLUDE_DEPTH) { +					log_err("%s:%d max include depth" +					  "exceeded", fname, state->lineno); +					return 0; +				}  				/* skip spaces */  				while(*incfile == ' ' || *incfile == '\t')  					incfile++; @@ -1480,11 +1495,12 @@ 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)) { +					state, incfile, depth+1)) {  					log_err("%s:%d cannot parse include " -						"file %s", z->zonefile, +						"file %s", fname,  						lineno_orig, incfile);  					fclose(inc); +					free(incfile);  					return 0;  				}  				fclose(inc); @@ -1496,7 +1512,7 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,  			continue;  		}  		if(status != 0) { -			log_err("parse error %s %d:%d: %s", z->zonefile, +			log_err("parse error %s %d:%d: %s", fname,  				state->lineno, LDNS_WIREPARSE_OFFSET(status),  				sldns_get_errorstr_parse(status));  			return 0; @@ -1511,7 +1527,7 @@ az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,  			sldns_wire2str_type_buf(sldns_wirerr_get_type(rr,  				rr_len, dname_len), buf, sizeof(buf));  			log_err("%s:%d cannot insert RR of type %s", -				z->zonefile, state->lineno, buf); +				fname, state->lineno, buf);  			return 0;  		}  	} @@ -1546,6 +1562,11 @@ auth_zone_read_zonefile(struct auth_zone* z)  		free(n);  		return 0;  	} + +	/* clear the data tree */ +	traverse_postorder(&z->data, auth_data_del, NULL); +	rbtree_init(&z->data, &auth_data_cmp); +  	memset(&state, 0, sizeof(state));  	/* default TTL to 3600 */  	state.default_ttl = 3600; @@ -1555,7 +1576,7 @@ 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)) { +	if(!az_parse_file(z, in, rr, sizeof(rr), &state, z->zonefile, 0)) {  		char* n = sldns_wire2str_dname(z->name, z->namelen);  		log_err("error parsing zonefile %s for %s",  			z->zonefile, n?n:"error"); @@ -1569,9 +1590,9 @@ auth_zone_read_zonefile(struct auth_zone* z)  /** write buffer to file and check return codes */  static int -write_out(FILE* out, const char* str) +write_out(FILE* out, const char* str, size_t len)  { -	size_t r, len = strlen(str); +	size_t r;  	if(len == 0)  		return 1;  	r = fwrite(str, 1, len, out); @@ -1634,7 +1655,7 @@ auth_zone_write_rrset(struct auth_zone* z, struct auth_data* node,  			verbose(VERB_ALGO, "failed to rr2str rr %d", (int)i);  			continue;  		} -		if(!write_out(out, buf)) +		if(!write_out(out, buf, strlen(buf)))  			return 0;  	}  	return 1; @@ -1703,6 +1724,24 @@ auth_zones_read_zones(struct auth_zones* az)  	return 1;  } +/** find serial number of zone or false if none */ +int +auth_zone_get_serial(struct auth_zone* z, uint32_t* serial) +{ +	struct auth_data* apex; +	struct auth_rrset* soa; +	struct packed_rrset_data* d; +	apex = az_find_name(z, z->name, z->namelen); +	if(!apex) return 0; +	soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); +	if(!soa || soa->data->count==0) +		return 0; /* no RRset or no RRs in rrset */ +	if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */ +	d = soa->data; +	*serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20)); +	return 1; +} +  /** Find auth_zone SOA and populate the values in xfr(soa values). */  static int  xfr_find_soa(struct auth_zone* z, struct auth_xfer* xfr) @@ -1808,6 +1847,7 @@ auth_zones_cfg(struct auth_zones* az, struct config_auth* c)  	lock_rw_unlock(&az->lock);  	/* set options */ +	z->zone_deleted = 0;  	if(!auth_zone_set_zonefile(z, c->zonefile)) {  		if(x) {  			lock_basic_unlock(&x->lock); @@ -1840,10 +1880,65 @@ auth_zones_cfg(struct auth_zones* az, struct config_auth* c)  	return 1;  } +/** set all auth zones deleted, then in auth_zones_cfg, it marks them + * as nondeleted (if they are still in the config), and then later + * we can find deleted zones */ +static void +az_setall_deleted(struct auth_zones* az) +{ +	struct auth_zone* z; +	lock_rw_wrlock(&az->lock); +	RBTREE_FOR(z, struct auth_zone*, &az->ztree) { +		lock_rw_wrlock(&z->lock); +		z->zone_deleted = 1; +		lock_rw_unlock(&z->lock); +	} +	lock_rw_unlock(&az->lock); +} + +/** find zones that are marked deleted and delete them. + * This is called from apply_cfg, and there are no threads and no + * workers, so the xfr can just be deleted. */ +static void +az_delete_deleted_zones(struct auth_zones* az) +{ +	struct auth_zone* z; +	struct auth_zone* delete_list = NULL, *next; +	struct auth_xfer* xfr; +	lock_rw_wrlock(&az->lock); +	RBTREE_FOR(z, struct auth_zone*, &az->ztree) { +		lock_rw_wrlock(&z->lock); +		if(z->zone_deleted) { +			/* we cannot alter the rbtree right now, but +			 * we can put it on a linked list and then +			 * delete it */ +			z->delete_next = delete_list; +			delete_list = z; +		} +		lock_rw_unlock(&z->lock); +	} +	/* now we are out of the tree loop and we can loop and delete +	 * the zones */ +	z = delete_list; +	while(z) { +		next = z->delete_next; +		xfr = auth_xfer_find(az, z->name, z->namelen, z->dclass); +		if(xfr) { +			(void)rbtree_delete(&az->xtree, &xfr->node); +			auth_xfer_delete(xfr); +		} +		(void)rbtree_delete(&az->ztree, &z->node); +		auth_zone_delete(z); +		z = next; +	} +	lock_rw_unlock(&az->lock); +} +  int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,  	int setup)  {  	struct config_auth* p; +	az_setall_deleted(az);  	for(p = cfg->auths; p; p = p->next) {  		if(!p->name || p->name[0] == 0) {  			log_warn("auth-zone without a name, skipped"); @@ -1854,6 +1949,7 @@ int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,  			return 0;  		}  	} +	az_delete_deleted_zones(az);  	if(!auth_zones_read_zones(az))  		return 0;  	if(setup) { @@ -1937,6 +2033,7 @@ auth_xfer_delete(struct auth_xfer* xfr)  		}  		free(xfr->task_transfer);  	} +	auth_free_masters(xfr->allow_notify_list);  	free(xfr);  } @@ -3153,10 +3250,13 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,  	/* answer it from zone z */  	r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback);  	lock_rw_unlock(&z->lock); -	if(fallback) { +	if(!r && fallback) {  		/* fallback to regular answering (recursive) */  		return 0;  	} +	lock_rw_wrlock(&az->lock); +	az->num_query_down++; +	lock_rw_unlock(&az->lock);  	/* encode answer */  	if(!r) @@ -3186,6 +3286,187 @@ int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,  	return r;  } +int +auth_zone_parse_notify_serial(sldns_buffer* pkt, uint32_t *serial) +{ +	struct query_info q; +	uint16_t rdlen; +	memset(&q, 0, sizeof(q)); +	sldns_buffer_set_position(pkt, 0); +	if(!query_info_parse(&q, pkt)) return 0; +	if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0) return 0; +	/* skip name of RR in answer section */ +	if(sldns_buffer_remaining(pkt) < 1) return 0; +	if(pkt_dname_len(pkt) == 0) return 0; +	/* check type */ +	if(sldns_buffer_remaining(pkt) < 10 /* type,class,ttl,rdatalen*/) +		return 0; +	if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_SOA) return 0; +	sldns_buffer_skip(pkt, 2); /* class */ +	sldns_buffer_skip(pkt, 4); /* ttl */ +	rdlen = sldns_buffer_read_u16(pkt); /* rdatalen */ +	if(sldns_buffer_remaining(pkt) < rdlen) return 0; +	if(rdlen < 22) return 0; /* bad soa length */ +	sldns_buffer_skip(pkt, (ssize_t)(rdlen-20)); +	*serial = sldns_buffer_read_u32(pkt); +	/* return true when has serial in answer section */ +	return 1; +} + +/** see if addr appears in the list */ +static int +addr_in_list(struct auth_addr* list, struct sockaddr_storage* addr, +	socklen_t addrlen) +{ +	struct auth_addr* p; +	for(p=list; p; p=p->next) { +		if(sockaddr_cmp_addr(addr, addrlen, &p->addr, p->addrlen)==0) +			return 1; +	} +	return 0; +} + +/** check if an address matches a master specification (or one of its + * addresses in the addr list) */ +static int +addr_matches_master(struct auth_master* master, struct sockaddr_storage* addr, +	socklen_t addrlen, struct auth_master** fromhost) +{ +	struct sockaddr_storage a; +	socklen_t alen = 0; +	int net = 0; +	if(addr_in_list(master->list, addr, addrlen)) { +		*fromhost = master; +		return 1;	 +	} +	/* compare address (but not port number, that is the destination +	 * port of the master, the port number of the received notify is +	 * allowed to by any port on that master) */ +	if(extstrtoaddr(master->host, &a, &alen) && +		sockaddr_cmp_addr(addr, addrlen, &a, alen)==0) { +		*fromhost = master; +		return 1; +	} +	/* prefixes, addr/len, like 10.0.0.0/8 */ +	/* not http and has a / and there is one / */ +	if(master->allow_notify && !master->http && +		strchr(master->host, '/') != NULL && +		strchr(master->host, '/') == strrchr(master->host, '/') && +		netblockstrtoaddr(master->host, UNBOUND_DNS_PORT, &a, &alen, +		&net) && alen == addrlen) { +		if(addr_in_common(addr, (addr_is_ip6(addr, addrlen)?128:32), +			&a, net, alen) >= net) { +			*fromhost = NULL; /* prefix does not have destination +				to send the probe or transfer with */ +			return 1; /* matches the netblock */ +		} +	} +	return 0; +} + +/** check access list for notifies */ +static int +az_xfr_allowed_notify(struct auth_xfer* xfr, struct sockaddr_storage* addr, +	socklen_t addrlen, struct auth_master** fromhost) +{ +	struct auth_master* p; +	for(p=xfr->allow_notify_list; p; p=p->next) { +		if(addr_matches_master(p, addr, addrlen, fromhost)) { +			return 1; +		} +	} +	return 0; +} + +/** see if the serial means the zone has to be updated, i.e. the serial + * is newer than the zone serial, or we have no zone */ +static int +xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial) +{ +	if(!xfr->have_zone) +		return 1; /* no zone, anything is better */ +	if(xfr->zone_expired) +		return 1; /* expired, the sent serial is better than expired +			data */ +	if(compare_serial(xfr->serial, serial) < 0) +		return 1; /* our serial is smaller than the sent serial, +			the data is newer, fetch it */ +	return 0; +} + +/** note notify serial, updates the notify information in the xfr struct */ +static void +xfr_note_notify_serial(struct auth_xfer* xfr, int has_serial, uint32_t serial) +{ +	if(xfr->notify_received && xfr->notify_has_serial && has_serial) { +		/* see if this serial is newer */ +		if(compare_serial(xfr->notify_serial, serial) < 0) +			xfr->notify_serial = serial; +	} else if(xfr->notify_received && xfr->notify_has_serial && +		!has_serial) { +		/* remove serial, we have notify without serial */ +		xfr->notify_has_serial = 0; +		xfr->notify_serial = 0; +	} else if(xfr->notify_received && !xfr->notify_has_serial) { +		/* we already have notify without serial, keep it +		 * that way; no serial check when current operation +		 * is done */ +	} else { +		xfr->notify_received = 1; +		xfr->notify_has_serial = has_serial; +		xfr->notify_serial = serial; +	} +} + +/** process a notify serial, start new probe or note serial. xfr is locked */ +static void +xfr_process_notify(struct auth_xfer* xfr, struct module_env* env, +	int has_serial, uint32_t serial, struct auth_master* fromhost) +{ +	/* if the serial of notify is older than we have, don't fetch +	 * a zone, we already have it */ +	if(has_serial && !xfr_serial_means_update(xfr, serial)) +		return; +	/* start new probe with this addr src, or note serial */ +	if(!xfr_start_probe(xfr, env, fromhost)) { +		/* not started because already in progress, note the serial */ +		xfr_note_notify_serial(xfr, has_serial, serial); +		lock_basic_unlock(&xfr->lock); +	} +} + +int auth_zones_notify(struct auth_zones* az, struct module_env* env, +	uint8_t* nm, size_t nmlen, uint16_t dclass, +	struct sockaddr_storage* addr, socklen_t addrlen, int has_serial, +	uint32_t serial, int* refused) +{ +	struct auth_xfer* xfr; +	struct auth_master* fromhost = NULL; +	/* see which zone this is */ +	lock_rw_rdlock(&az->lock); +	xfr = auth_xfer_find(az, nm, nmlen, dclass); +	if(!xfr) { +		lock_rw_unlock(&az->lock); +		/* no such zone, refuse the notify */ +		*refused = 1; +		return 0; +	} +	lock_basic_lock(&xfr->lock); +	lock_rw_unlock(&az->lock); +	 +	/* check access list for notifies */ +	if(!az_xfr_allowed_notify(xfr, addr, addrlen, &fromhost)) { +		lock_basic_unlock(&xfr->lock); +		/* notify not allowed, refuse the notify */ +		*refused = 1; +		return 0; +	} + +	/* process the notify */ +	xfr_process_notify(xfr, env, has_serial, serial, fromhost); +	return 1; +} +  /** set a zone expired */  static void  auth_xfer_set_expired(struct auth_xfer* xfr, struct module_env* env, @@ -3239,6 +3520,93 @@ xfr_masterlist_free_addrs(struct auth_master* list)  	}  } +/** copy a list of auth_addrs */ +static struct auth_addr* +auth_addr_list_copy(struct auth_addr* source) +{ +	struct auth_addr* list = NULL, *last = NULL; +	struct auth_addr* p; +	for(p=source; p; p=p->next) { +		struct auth_addr* a = (struct auth_addr*)memdup(p, sizeof(*p)); +		if(!a) { +			log_err("malloc failure"); +			auth_free_master_addrs(list); +			return NULL; +		} +		a->next = NULL; +		if(last) last->next = a; +		if(!list) list = a; +		last = a; +	} +	return list; +} + +/** copy a master to a new structure, NULL on alloc failure */ +static struct auth_master* +auth_master_copy(struct auth_master* o) +{ +	struct auth_master* m; +	if(!o) return NULL; +	m = (struct auth_master*)memdup(o, sizeof(*o)); +	if(!m) { +		log_err("malloc failure"); +		return NULL; +	} +	m->next = NULL; +	if(m->host) { +		m->host = strdup(m->host); +		if(!m->host) { +			free(m); +			log_err("malloc failure"); +			return NULL; +		} +	} +	if(m->file) { +		m->file = strdup(m->file); +		if(!m->file) { +			free(m->host); +			free(m); +			log_err("malloc failure"); +			return NULL; +		} +	} +	if(m->list) { +		m->list = auth_addr_list_copy(m->list); +		if(!m->list) { +			free(m->file); +			free(m->host); +			free(m); +			return NULL; +		} +	} +	return m; +} + +/** copy the master addresses from the task_probe lookups to the allow_notify + * list of masters */ +static void +probe_copy_masters_for_allow_notify(struct auth_xfer* xfr) +{ +	struct auth_master* list = NULL, *last = NULL; +	struct auth_master* p; +	/* build up new list with copies */ +	for(p = xfr->task_probe->masters; p; p=p->next) { +		struct auth_master* m = auth_master_copy(p); +		if(!m) { +			auth_free_masters(list); +			/* failed because of malloc failure, use old list */ +			return; +		} +		m->next = NULL; +		if(last) last->next = m; +		if(!list) list = m; +		last = m; +	} +	/* success, replace list */ +	auth_free_masters(xfr->allow_notify_list); +	xfr->allow_notify_list = list; +} +  /** start the lookups for task_transfer */  static void  xfr_transfer_start_lookups(struct auth_xfer* xfr) @@ -3400,11 +3768,19 @@ xfr_transfer_nextmaster(struct auth_xfer* xfr)  	if(xfr->task_transfer->scan_specific) {  		xfr->task_transfer->scan_specific = NULL;  		xfr->task_transfer->scan_target = xfr->task_transfer->masters; +		if(xfr->task_transfer->scan_target && xfr->task_transfer-> +			scan_target->list) +			xfr->task_transfer->scan_addr = +				xfr->task_transfer->scan_target->list;  		return;  	}  	if(!xfr->task_transfer->scan_target)  		return;  	xfr->task_transfer->scan_target = xfr->task_transfer->scan_target->next; +	if(xfr->task_transfer->scan_target && xfr->task_transfer-> +		scan_target->list) +		xfr->task_transfer->scan_addr = +			xfr->task_transfer->scan_target->list;  	return;  } @@ -3422,11 +3798,19 @@ xfr_probe_nextmaster(struct auth_xfer* xfr)  	if(xfr->task_probe->scan_specific) {  		xfr->task_probe->scan_specific = NULL;  		xfr->task_probe->scan_target = xfr->task_probe->masters; +		if(xfr->task_probe->scan_target && xfr->task_probe-> +			scan_target->list) +			xfr->task_probe->scan_addr = +				xfr->task_probe->scan_target->list;  		return;  	}  	if(!xfr->task_probe->scan_target)  		return;  	xfr->task_probe->scan_target = xfr->task_probe->scan_target->next; +	if(xfr->task_probe->scan_target && xfr->task_probe-> +		scan_target->list) +		xfr->task_probe->scan_addr = +			xfr->task_probe->scan_target->list;  	return;  } @@ -3581,22 +3965,6 @@ check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr,  	return 1;  } -/** see if the serial means the zone has to be updated, i.e. the serial - * is newer than the zone serial, or we have no zone */ -static int -xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial) -{ -	if(!xfr->have_zone) -		return 1; /* no zone, anything is better */ -	if(xfr->zone_expired) -		return 1; /* expired, the sent serial is better than expired -			data */ -	if(compare_serial(xfr->serial, serial) < 0) -		return 1; /* our serial is smaller than the sent serial, -			the data is newer, fetch it */ -	return 0; -} -  /** read one line from chunks into buffer at current position */  static int  chunkline_get_line(struct auth_chunk** chunk, size_t* chunk_pos, @@ -3870,6 +4238,11 @@ chunkline_newline_removal(sldns_buffer* buf)  	size_t i, end=sldns_buffer_limit(buf);  	for(i=0; i<end; i++) {  		char c = (char)sldns_buffer_read_u8_at(buf, i); +		if(c == '\n' && i==end-1) { +			sldns_buffer_write_u8_at(buf, i, 0); +			sldns_buffer_set_limit(buf, end-1); +			return; +		}  		if(c == '\n')  			sldns_buffer_write_u8_at(buf, i, (uint8_t)' ');  	} @@ -4032,6 +4405,56 @@ log_rrlist_position(const char* label, struct auth_chunk* rr_chunk,  		str, typestr);  } +/** check that start serial is OK for ixfr. we are at rr_counter == 0, + * and we are going to check rr_counter == 1 (has to be type SOA) serial */ +static int +ixfr_start_serial(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos, +	uint8_t* rr_dname, uint16_t rr_type, uint16_t rr_class, +	uint32_t rr_ttl, uint16_t rr_rdlen, uint8_t* rr_rdata, +	size_t rr_nextpos, uint32_t transfer_serial, uint32_t xfr_serial) +{ +	uint32_t startserial; +	/* move forward on RR */ +	chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos); +	if(chunk_rrlist_end(rr_chunk, rr_num)) { +		/* no second SOA */ +		verbose(VERB_OPS, "IXFR has no second SOA record"); +		return 0; +	} +	if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos, +		&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen, +		&rr_rdata, &rr_nextpos)) { +		verbose(VERB_OPS, "IXFR cannot parse second SOA record"); +		/* failed to parse RR */ +		return 0; +	} +	if(rr_type != LDNS_RR_TYPE_SOA) { +		verbose(VERB_OPS, "IXFR second record is not type SOA"); +		return 0; +	} +	if(rr_rdlen < 22) { +		verbose(VERB_OPS, "IXFR, second SOA has short rdlength"); +		return 0; /* bad SOA rdlen */ +	} +	startserial = sldns_read_uint32(rr_rdata+rr_rdlen-20); +	if(startserial == transfer_serial) { +		/* empty AXFR, not an IXFR */ +		verbose(VERB_OPS, "IXFR second serial same as first"); +		return 0; +	} +	if(startserial != xfr_serial) { +		/* wrong start serial, it does not match the serial in +		 * memory */ +		verbose(VERB_OPS, "IXFR is from serial %u to %u but %u " +			"in memory, rejecting the zone transfer", +			(unsigned)startserial, (unsigned)transfer_serial, +			(unsigned)xfr_serial); +		return 0; +	} +	/* everything OK in second SOA serial */ +	return 1; +} +  /** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */  static int  apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z, @@ -4078,6 +4501,13 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,  				have_transfer_serial = 1;  				transfer_serial = serial;  				delmode = 1; /* gets negated below */ +				/* check second RR before going any further */ +				if(!ixfr_start_serial(rr_chunk, rr_num, rr_pos, +					rr_dname, rr_type, rr_class, rr_ttl, +					rr_rdlen, rr_rdata, rr_nextpos, +					transfer_serial, xfr->serial)) { +					return 0; +				}  			} else if(transfer_serial == serial) {  				have_transfer_serial++;  				if(rr_counter == 1) { @@ -4308,6 +4738,28 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,  	return 1;  } +/** write http chunks to zonefile to create downloaded file */ +static int +auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname) +{ +	FILE* out; +	struct auth_chunk* p; +	out = fopen(fname, "w"); +	if(!out) { +		log_err("could not open %s: %s", fname, strerror(errno)); +		return 0; +	} +	for(p = xfr->task_transfer->chunks_first; p ; p = p->next) { +		if(!write_out(out, (char*)p->data, p->len)) { +			log_err("could not write http download to %s", fname); +			fclose(out); +			return 0; +		} +	} +	fclose(out); +	return 1; +} +  /** write to zonefile after zone has been updated */  static void  xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env) @@ -4346,7 +4798,13 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)  	}  	snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", z->zonefile,  		(unsigned)getpid()); -	if(!auth_zone_write_file(z, tmpfile)) { +	if(xfr->task_transfer->master->http) { +		/* use the stored chunk list to write them */ +		if(!auth_zone_write_chunks(xfr, tmpfile)) { +			unlink(tmpfile); +			lock_rw_unlock(&z->lock); +		} +	} else if(!auth_zone_write_file(z, tmpfile)) {  		unlink(tmpfile);  		lock_rw_unlock(&z->lock);  		return; @@ -4465,6 +4923,9 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)  		/* not needed, host is in IP addr format */  		return 0;  	} +	if(master->allow_notify) +		return 0; /* allow-notifies are not transferred from, no +		lookup is needed */  	/* use mesh_new_callback to probe for non-addr hosts,  	 * and then wait for them to be looked up (in cache, or query) */ @@ -4519,6 +4980,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)  	socklen_t addrlen = 0;  	struct auth_master* master = xfr->task_transfer->master;  	if(!master) return 0; +	if(master->allow_notify) return 0; /* only for notify */  	/* get master addr */  	if(xfr->task_transfer->scan_addr) { @@ -4623,7 +5085,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)  	xfr_transfer_disown(xfr);  	/* pick up the nextprobe task and wait */ -	xfr_set_timeout(xfr, env, 1); +	xfr_set_timeout(xfr, env, 1, 0);  	lock_basic_unlock(&xfr->lock);  } @@ -4635,6 +5097,8 @@ xfr_master_add_addrs(struct auth_master* m, struct ub_packed_rrset_key* rrset,  	size_t i;  	struct packed_rrset_data* data;  	if(!m || !rrset) return; +	if(rrtype != LDNS_RR_TYPE_A && rrtype != LDNS_RR_TYPE_AAAA) +		return;  	data = (struct packed_rrset_data*)rrset->entry.data;  	for(i=0; i<data->count; i++) {  		struct auth_addr* a; @@ -5056,8 +5520,30 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)  		/* we fetched the zone, move to wait task */  		xfr_transfer_disown(xfr); -		/* pick up the nextprobe task and wait (normail wait time) */ -		xfr_set_timeout(xfr, env, 0); +		if(xfr->notify_received && (!xfr->notify_has_serial || +			(xfr->notify_has_serial &&  +			xfr_serial_means_update(xfr, xfr->notify_serial)))) { +			uint32_t sr = xfr->notify_serial; +			int has_sr = xfr->notify_has_serial; +			/* we received a notify while probe/transfer was +			 * in progress.  start a new probe and transfer */ +			xfr->notify_received = 0; +			xfr->notify_has_serial = 0; +			xfr->notify_serial = 0; +			if(!xfr_start_probe(xfr, env, NULL)) { +				/* if we couldn't start it, already in +				 * progress; restore notify serial, +				 * while xfr still locked */ +				xfr->notify_received = 1; +				xfr->notify_has_serial = has_sr; +				xfr->notify_serial = sr; +				lock_basic_unlock(&xfr->lock); +			} +			return; +		} else { +			/* pick up the nextprobe task and wait (normail wait time) */ +			xfr_set_timeout(xfr, env, 0, 0); +		}  		lock_basic_unlock(&xfr->lock);  		return;  	} @@ -5248,6 +5734,9 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,  	/* pick master */  	struct auth_master* master = xfr_probe_current_master(xfr);  	if(!master) return 0; +	if(master->allow_notify) return 0; /* only for notify */ +	if(master->http) return 0; /* only masters get SOA UDP probe, +		not urls, if those are in this list */  	/* get master addr */  	if(xfr->task_probe->scan_addr) { @@ -5400,7 +5889,7 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,  				if(xfr->have_zone)  					xfr->lease_time = *env->now;  				if(xfr->task_nextprobe->worker == NULL) -					xfr_set_timeout(xfr, env, 0); +					xfr_set_timeout(xfr, env, 0, 0);  			}  			/* other tasks are running, we don't do this anymore */  			xfr_probe_disown(xfr); @@ -5445,6 +5934,11 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)  		/* not needed, host is in IP addr format */  		return 0;  	} +	if(master->allow_notify && !master->http && +		strchr(master->host, '/') != NULL && +		strchr(master->host, '/') == strrchr(master->host, '/')) { +		return 0; /* is IP/prefix format, not something to look up */ +	}  	/* use mesh_new_callback to probe for non-addr hosts,  	 * and then wait for them to be looked up (in cache, or query) */ @@ -5507,6 +6001,17 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)  		}  		xfr_probe_move_to_next_lookup(xfr, env);  	} +	/* probe of list has ended.  Create or refresh the list of of +	 * allow_notify addrs */ +	probe_copy_masters_for_allow_notify(xfr); +	if(xfr->task_probe->only_lookup) { +		/* only wanted lookups for copy, stop probe and start wait */ +		xfr->task_probe->only_lookup = 0; +		xfr_probe_disown(xfr); +		xfr_set_timeout(xfr, env, 0, 0); +		lock_basic_unlock(&xfr->lock); +		return; +	}  	/* send probe packets */  	while(!xfr_probe_end_of_list(xfr)) { @@ -5524,7 +6029,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)  	xfr_probe_disown(xfr);  	/* pick up the nextprobe task and wait */ -	xfr_set_timeout(xfr, env, 1); +	xfr_set_timeout(xfr, env, 1, 0);  	lock_basic_unlock(&xfr->lock);  } @@ -5611,19 +6116,45 @@ auth_xfer_timer(void* arg)  	xfr_nextprobe_disown(xfr); +	if(!xfr_start_probe(xfr, env, NULL)) { +		/* not started because already in progress */ +		lock_basic_unlock(&xfr->lock); +	} +} + +/** return true if there are probe (SOA UDP query) targets in the master list*/ +static int +have_probe_targets(struct auth_master* list) +{ +	struct auth_master* p; +	for(p=list; p; p = p->next) { +		if(!p->allow_notify && p->host) +			return 1; +	} +	return 0; +} + +/** start task_probe if possible, if no masters for probe start task_transfer + * returns true if task has been started, and false if the task is already + * in progress. */ +static int +xfr_start_probe(struct auth_xfer* xfr, struct module_env* env, +	struct auth_master* spec) +{  	/* see if we need to start a probe (or maybe it is already in  	 * progress (due to notify)) */  	if(xfr->task_probe->worker == NULL) { -		if(xfr->task_probe->masters == NULL) { +		if(!have_probe_targets(xfr->task_probe->masters) && +			!(xfr->task_probe->only_lookup && +			xfr->task_probe->masters != NULL)) {  			/* useless to pick up task_probe, no masters to  			 * probe. Instead attempt to pick up task transfer */  			if(xfr->task_transfer->worker == NULL) { -				xfr_start_transfer(xfr, env, NULL); -			} else { -				/* task transfer already in progress */ -				lock_basic_unlock(&xfr->lock); +				xfr_start_transfer(xfr, env, spec); +				return 1;  			} -			return; +			/* task transfer already in progress */ +			return 0;  		}  		/* pick up the probe task ourselves */ @@ -5632,15 +6163,17 @@ auth_xfer_timer(void* arg)  		xfr->task_probe->cp = NULL;  		/* start the task */ -		/* this was a timeout, so no specific first master to scan */ -		xfr_probe_start_list(xfr, NULL); +		/* if this was a timeout, no specific first master to scan */ +		/* otherwise, spec is nonNULL the notified master, scan +		 * first and also transfer first from it */ +		xfr_probe_start_list(xfr, spec);  		/* setup to start the lookup of hostnames of masters afresh */  		xfr_probe_start_lookups(xfr);  		/* send the probe packet or next send, or end task */  		xfr_probe_send_or_end(xfr, env); -	} else { -		lock_basic_unlock(&xfr->lock); +		return 1;  	} +	return 0;  }  /** for task_nextprobe. @@ -5648,10 +6181,11 @@ auth_xfer_timer(void* arg)   * @param xfr: task structure   * @param env: module environment, with worker and time.   * @param failure: set true if timer should be set for failure retry. + * @param lookup_only: only perform lookups when timer done, 0 sec timeout   */  static void  xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, -	int failure) +	int failure, int lookup_only)  {  	struct timeval tv;  	log_assert(xfr->task_nextprobe != NULL); @@ -5661,7 +6195,7 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,  	 * but if expiry is sooner, use that one.  	 * after a failure, use the retry timer instead. */  	xfr->task_nextprobe->next_probe = *env->now; -	if(xfr->lease_time) +	if(xfr->lease_time && !failure)  		xfr->task_nextprobe->next_probe = xfr->lease_time;  	if(!failure) { @@ -5684,6 +6218,12 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,  		if(failure)  			xfr->task_nextprobe->next_probe +=  				xfr->task_nextprobe->backoff; +		/* put the timer exactly on expiry, if possible */ +		if(xfr->lease_time && xfr->lease_time+xfr->expiry < +			xfr->task_nextprobe->next_probe && +			xfr->lease_time+xfr->expiry > *env->now) +			xfr->task_nextprobe->next_probe = +				xfr->lease_time+xfr->expiry;  	} else {  		xfr->task_nextprobe->next_probe +=  			xfr->task_nextprobe->backoff; @@ -5708,6 +6248,13 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,  		tv.tv_sec = xfr->task_nextprobe->next_probe -   			*(xfr->task_nextprobe->env->now);  	else	tv.tv_sec = 0; +	if(tv.tv_sec != 0 && lookup_only && xfr->task_probe->masters) { +		/* don't lookup_only, if lookup timeout is 0 anyway, +		 * or if we don't have masters to lookup */ +		tv.tv_sec = 0; +		if(xfr->task_probe && xfr->task_probe->worker == NULL) +			xfr->task_probe->only_lookup = 1; +	}  	if(verbosity >= VERB_ALGO) {  		char zname[255+1];  		dname_str(xfr->name, zname); @@ -5731,8 +6278,9 @@ auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)  		 * notes the start time when the data was acquired */  		if(x->have_zone)  			x->lease_time = *env->now; -		if(x->task_nextprobe && x->task_nextprobe->worker == NULL) -			xfr_set_timeout(x, env, 0); +		if(x->task_nextprobe && x->task_nextprobe->worker == NULL) { +			xfr_set_timeout(x, env, 0, 1); +		}  		lock_basic_unlock(&x->lock);  	}  	lock_rw_unlock(&az->lock); @@ -5889,7 +6437,7 @@ static char*  dup_all(char* str)  {  	char* result = strdup(str); -	if(!str) { +	if(!result) {  		log_err("malloc failure");  		return NULL;  	} @@ -6006,6 +6554,15 @@ xfer_set_masters(struct auth_master** list, struct config_auth* c,  			return 0;  		}  	} +	for(p = c->allow_notify; p; p = p->next) { +		m = auth_master_new(&list); +		m->allow_notify = 1; +		m->host = strdup(p->str); +		if(!m->host) { +			log_err("malloc failure"); +			return 0; +		} +	}  	return 1;  }  | 
