aboutsummaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2018-05-12 11:56:52 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2018-05-12 11:56:52 +0000
commit4289761a7b61df4b64c11ada446a187df61e6a1e (patch)
treeed7ceb7a1652fb9f865fafd21fbe18d1a3b5f79d /services
parent197f1a0fe3e81cde0cd25a3a1f37ebedf9a99488 (diff)
downloadsrc-4289761a7b61df4b64c11ada446a187df61e6a1e.tar.gz
src-4289761a7b61df4b64c11ada446a187df61e6a1e.zip
Vendor import of Unbound 1.7.1.vendor/unbound/1.7.1
Notes
Notes: svn path=/vendor/unbound/dist/; revision=333549 svn path=/vendor/unbound/1.7.1/; revision=333550; tag=vendor/unbound/1.7.1
Diffstat (limited to 'services')
-rw-r--r--services/authzone.c653
-rw-r--r--services/authzone.h50
-rw-r--r--services/cache/dns.c48
-rw-r--r--services/cache/rrset.c2
-rw-r--r--services/listen_dnsport.c48
-rw-r--r--services/mesh.c17
-rw-r--r--services/outside_network.c39
-rw-r--r--services/outside_network.h9
8 files changed, 795 insertions, 71 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;
}
diff --git a/services/authzone.h b/services/authzone.h
index d54ef4b96e49..4e06c0654d99 100644
--- a/services/authzone.h
+++ b/services/authzone.h
@@ -77,6 +77,10 @@ struct auth_zones {
rbtree_type xtree;
/** do we have downstream enabled */
int have_downstream;
+ /** number of queries upstream */
+ size_t num_query_up;
+ /** number of queries downstream */
+ size_t num_query_down;
};
/**
@@ -122,6 +126,10 @@ struct auth_zone {
/** for upstream: this zone answers queries that unbound intends to
* send upstream. */
int for_upstream;
+ /** zone has been deleted */
+ int zone_deleted;
+ /** deletelist pointer, unused normally except during delete */
+ struct auth_zone* delete_next;
};
/**
@@ -214,8 +222,14 @@ struct auth_xfer {
* Hold the lock to access this member (and the serial).
*/
int notify_received;
+ /** true if the notify_received has a serial number */
+ int notify_has_serial;
/** serial number of the notify */
uint32_t notify_serial;
+ /** the list of masters for checking notifies. This list is
+ * empty on start, and a copy of the list from the probe_task when
+ * it is done looking them up. */
+ struct auth_master* allow_notify_list;
/* protected by the lock on the structure, information about
* the loaded authority zone. */
@@ -292,6 +306,9 @@ struct auth_probe {
struct auth_master* lookup_target;
/** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */
int lookup_aaaa;
+ /** we only want to do lookups for making config work (for notify),
+ * don't proceed with UDP SOA probe queries */
+ int only_lookup;
/** once notified, or the timeout has been reached. a scan starts. */
/** the scan specific target (notify source), or NULL if none */
@@ -400,6 +417,9 @@ struct auth_master {
int http;
/** use IXFR for this master */
int ixfr;
+ /** this is an allow notify member, the master can send notifies
+ * to us, but we don't send SOA probes, or zone transfer from it */
+ int allow_notify;
/** use ssl for channel */
int ssl;
/** the port number (for urls) */
@@ -541,9 +561,39 @@ int auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr);
int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass);
+/** process notify for auth zones.
+ * first checks the access list. Then processes the notify. This starts
+ * the probe sequence or it notes the serial number (if any)
+ * @param az: auth zones structure.
+ * @param env: module env of the worker that is handling the notify. it will
+ * pick up the task probe (or transfer), unless already in progress by
+ * another worker.
+ * @param nm: name of the zone. Uncompressed. from query.
+ * @param nmlen: length of name.
+ * @param dclass: class of zone.
+ * @param addr: source address of notify
+ * @param addrlen: length of addr.
+ * @param has_serial: if true, the notify has a serial attached.
+ * @param serial: the serial number, if has_serial is true.
+ * @param refused: is set to true on failure to note refused access.
+ * @return fail on failures (refused is false) and when access is
+ * denied (refused is true). True when processed.
+ */
+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);
+
+/** process notify packet and read serial number from SOA.
+ * returns 0 if no soa record in the notify */
+int auth_zone_parse_notify_serial(struct sldns_buffer* pkt, uint32_t *serial);
+
/** read auth zone from zonefile. caller must lock zone. false on failure */
int auth_zone_read_zonefile(struct auth_zone* z);
+/** find serial number of zone or false if none (no SOA record) */
+int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial);
+
/** compare auth_zones for sorted rbtree */
int auth_zone_cmp(const void* z1, const void* z2);
diff --git a/services/cache/dns.c b/services/cache/dns.c
index 411793c6c270..35adc35b57ee 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -108,6 +108,48 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
}
}
+/** delete message from message cache */
+static void
+msg_cache_remove(struct module_env* env, uint8_t* qname, size_t qnamelen,
+ uint16_t qtype, uint16_t qclass, uint16_t flags)
+{
+ struct query_info k;
+ hashvalue_type h;
+
+ k.qname = qname;
+ k.qname_len = qnamelen;
+ k.qtype = qtype;
+ k.qclass = qclass;
+ k.local_alias = NULL;
+ h = query_info_hash(&k, flags);
+ slabhash_remove(env->msg_cache, h, &k);
+}
+
+/** remove servfail msg cache entry */
+static void
+msg_del_servfail(struct module_env* env, struct query_info* qinfo,
+ uint32_t flags)
+{
+ struct msgreply_entry* e;
+ /* see if the entry is servfail, and then remove it, so that
+ * lookups move from the cacheresponse stage to the recursionresponse
+ * stage */
+ e = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len,
+ qinfo->qtype, qinfo->qclass, flags, 0, 0);
+ if(!e) return;
+ /* we don't check for the ttl here, also expired servfail entries
+ * are removed. If the user uses serve-expired, they would still be
+ * used to answer from cache */
+ if(FLAGS_GET_RCODE(((struct reply_info*)e->entry.data)->flags)
+ != LDNS_RCODE_SERVFAIL) {
+ lock_rw_unlock(&e->entry.lock);
+ return;
+ }
+ lock_rw_unlock(&e->entry.lock);
+ msg_cache_remove(env, qinfo->qname, qinfo->qname_len, qinfo->qtype,
+ qinfo->qclass, flags);
+}
+
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
@@ -132,6 +174,12 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
* which could be useful for delegation information */
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
free(rep);
+ /* if the message is SERVFAIL in cache, remove that SERVFAIL,
+ * so that the TTL 0 response can be returned for future
+ * responses (i.e. don't get answered by the servfail from
+ * cache, but instead go to recursion to get this TTL0
+ * response). */
+ msg_del_servfail(env, qinfo, flags);
return;
}
diff --git a/services/cache/rrset.c b/services/cache/rrset.c
index 0b41fcd7dc3f..26c1aeb91c62 100644
--- a/services/cache/rrset.c
+++ b/services/cache/rrset.c
@@ -255,9 +255,11 @@ void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
wc_dname[1] = (uint8_t)'*';
memmove(wc_dname+2, ce, ce_len);
+ free(rrset->rk.dname);
rrset->rk.dname_len = ce_len + 2;
rrset->rk.dname = (uint8_t*)memdup(wc_dname, rrset->rk.dname_len);
if(!rrset->rk.dname) {
+ alloc_special_release(alloc, rrset);
log_err("memdup failure in rrset_cache_update_wildcard");
return;
}
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index d099ca9449b7..6639fd3fc5cf 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -1056,6 +1056,26 @@ set_recvpktinfo(int s, int family)
return 1;
}
+/** see if interface is ssl, its port number == the ssl port number */
+static int
+if_is_ssl(const char* ifname, const char* port, int ssl_port,
+ struct config_strlist* additional_tls_port)
+{
+ struct config_strlist* s;
+ char* p = strchr(ifname, '@');
+ if(!p && atoi(port) == ssl_port)
+ return 1;
+ if(p && atoi(p+1) == ssl_port)
+ return 1;
+ for(s = additional_tls_port; s; s = s->next) {
+ if(p && atoi(p+1) == atoi(s->str))
+ return 1;
+ if(!p && atoi(port) == atoi(s->str))
+ return 1;
+ }
+ return 0;
+}
+
/**
* Helper for ports_open. Creates one interface (or NULL for default).
* @param ifname: The interface ip address.
@@ -1069,6 +1089,7 @@ set_recvpktinfo(int s, int family)
* @param rcv: receive buffer size for UDP
* @param snd: send buffer size for UDP
* @param ssl_port: ssl service port number
+ * @param additional_tls_port: list of additional ssl service port numbers.
* @param reuseport: try to set SO_REUSEPORT if nonNULL and true.
* set to false on exit if reuseport failed due to no kernel support.
* @param transparent: set IP_TRANSPARENT socket option.
@@ -1081,8 +1102,10 @@ set_recvpktinfo(int s, int family)
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
- size_t rcv, size_t snd, int ssl_port, int* reuseport, int transparent,
- int tcp_mss, int freebind, int use_systemd, int dnscrypt_port)
+ size_t rcv, size_t snd, int ssl_port,
+ struct config_strlist* additional_tls_port, int* reuseport,
+ int transparent, int tcp_mss, int freebind, int use_systemd,
+ int dnscrypt_port)
{
int s, noip6=0;
#ifdef USE_DNSCRYPT
@@ -1146,9 +1169,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
}
if(do_tcp) {
- int is_ssl = ((strchr(ifname, '@') &&
- atoi(strchr(ifname, '@')+1) == ssl_port) ||
- (!strchr(ifname, '@') && atoi(port) == ssl_port));
+ int is_ssl = if_is_ssl(ifname, port, ssl_port,
+ additional_tls_port);
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
&noip6, 0, 0, reuseport, transparent, tcp_mss,
freebind, use_systemd)) == -1) {
@@ -1334,8 +1356,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, reuseport,
- cfg->ip_transparent,
+ cfg->ssl_port, cfg->additional_tls_port,
+ reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
@@ -1348,8 +1370,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, reuseport,
- cfg->ip_transparent,
+ cfg->ssl_port, cfg->additional_tls_port,
+ reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
@@ -1364,8 +1386,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, reuseport,
- cfg->ip_transparent,
+ cfg->ssl_port, cfg->additional_tls_port,
+ reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
@@ -1378,8 +1400,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport)
if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, reuseport,
- cfg->ip_transparent,
+ cfg->ssl_port, cfg->additional_tls_port,
+ reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd,
cfg->dnscrypt_port)) {
listening_ports_free(list);
diff --git a/services/mesh.c b/services/mesh.c
index 3395dc62e674..3027cef00900 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -736,7 +736,8 @@ mesh_state_cleanup(struct mesh_state* mstate)
comm_point_drop_reply(&rep->query_reply);
mesh->num_reply_addrs--;
}
- for(cb=mstate->cb_list; cb; cb=cb->next) {
+ while((cb = mstate->cb_list)!=NULL) {
+ mstate->cb_list = cb->next;
fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
sec_status_unchecked, NULL);
@@ -974,7 +975,8 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
- if(!rcode && rep->security == sec_status_bogus) {
+ if(!rcode && (rep->security == sec_status_bogus ||
+ rep->security == sec_status_secure_sentinel_fail)) {
if(!(reason = errinf_to_str(&m->s)))
rcode = LDNS_RCODE_SERVFAIL;
}
@@ -1040,7 +1042,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
/* examine security status */
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
m->s.env->cfg->ignore_cd) && rep &&
- rep->security <= sec_status_bogus) {
+ (rep->security <= sec_status_bogus ||
+ rep->security == sec_status_secure_sentinel_fail)) {
rcode = LDNS_RCODE_SERVFAIL;
if(m->s.env->cfg->stat_extended)
m->s.env->mesh->ans_bogus++;
@@ -1167,7 +1170,13 @@ void mesh_query_done(struct mesh_state* mstate)
}
}
mstate->replies_sent = 1;
- for(c = mstate->cb_list; c; c = c->next) {
+ while((c = mstate->cb_list) != NULL) {
+ /* take this cb off the list; so that the list can be
+ * changed, eg. by adds from the callback routine */
+ mstate->cb_list = c->next;
+ if(!mstate->reply_list && !mstate->cb_list &&
+ mstate->super_set.count == 0)
+ mstate->s.env->mesh->num_detached_states++;
mesh_do_callback(mstate, mstate->s.return_rcode, rep, c);
}
}
diff --git a/services/outside_network.c b/services/outside_network.c
index 92212be02f0d..63dfe4961183 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -364,6 +364,20 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl);
#endif
pend->c->ssl_shake_state = comm_ssl_shake_write;
+#ifdef HAVE_SSL_SET1_HOST
+ if(w->tls_auth_name) {
+ SSL_set_verify(pend->c->ssl, SSL_VERIFY_PEER, NULL);
+ /* setting the hostname makes openssl verify the
+ * host name in the x509 certificate in the
+ * SSL connection*/
+ if(!SSL_set1_host(pend->c->ssl, w->tls_auth_name)) {
+ log_err("SSL_set1_host failed");
+ pend->c->fd = s;
+ comm_point_close(pend->c);
+ return 0;
+ }
+ }
+#endif /* HAVE_SSL_SET1_HOST */
}
w->pkt = NULL;
w->next_waiting = (void*)pend;
@@ -851,6 +865,7 @@ serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg))
struct service_callback* p = sq->cblist, *np;
free(sq->qbuf);
free(sq->zone);
+ free(sq->tls_auth_name);
edns_opt_list_free(sq->opt_list);
while(p) {
np = p->next;
@@ -1284,6 +1299,7 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
w->cb = callback;
w->cb_arg = callback_arg;
w->ssl_upstream = sq->ssl_upstream;
+ w->tls_auth_name = sq->tls_auth_name;
#ifndef S_SPLINT_S
tv.tv_sec = timeout;
tv.tv_usec = 0;
@@ -1357,8 +1373,8 @@ lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
static struct serviced_query*
serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream,
- struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
- size_t zonelen, int qtype, struct edns_option* opt_list)
+ char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
+ uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
#ifdef UNBOUND_DEBUG
@@ -1386,12 +1402,24 @@ serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
sq->nocaps = nocaps;
sq->tcp_upstream = tcp_upstream;
sq->ssl_upstream = ssl_upstream;
+ if(tls_auth_name) {
+ sq->tls_auth_name = strdup(tls_auth_name);
+ if(!sq->tls_auth_name) {
+ free(sq->zone);
+ free(sq->qbuf);
+ free(sq);
+ return NULL;
+ }
+ } else {
+ sq->tls_auth_name = NULL;
+ }
memcpy(&sq->addr, addr, addrlen);
sq->addrlen = addrlen;
sq->opt_list = NULL;
if(opt_list) {
sq->opt_list = edns_opt_copy_alloc(opt_list);
if(!sq->opt_list) {
+ free(sq->tls_auth_name);
free(sq->zone);
free(sq->qbuf);
free(sq);
@@ -2055,7 +2083,7 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct serviced_query*
outnet_serviced_query(struct outside_network* outnet,
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, int tcp_upstream, int ssl_upstream,
+ int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
size_t zonelen, struct module_qstate* qstate,
comm_point_callback_type* callback, void* callback_arg, sldns_buffer* buff,
@@ -2078,8 +2106,9 @@ outnet_serviced_query(struct outside_network* outnet,
if(!sq) {
/* make new serviced query entry */
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
- tcp_upstream, ssl_upstream, addr, addrlen, zone,
- zonelen, (int)qinfo->qtype, qstate->edns_opts_back_out);
+ tcp_upstream, ssl_upstream, tls_auth_name, addr,
+ addrlen, zone, zonelen, (int)qinfo->qtype,
+ qstate->edns_opts_back_out);
if(!sq) {
free(cb);
return NULL;
diff --git a/services/outside_network.h b/services/outside_network.h
index 09b2e6cedff6..105f7651363f 100644
--- a/services/outside_network.h
+++ b/services/outside_network.h
@@ -290,6 +290,8 @@ struct waiting_tcp {
void* cb_arg;
/** if it uses ssl upstream */
int ssl_upstream;
+ /** ref to the tls_auth_name from the serviced_query */
+ char* tls_auth_name;
};
/**
@@ -332,6 +334,9 @@ struct serviced_query {
int nocaps;
/** tcp upstream used, use tcp, or ssl_upstream for SSL */
int tcp_upstream, ssl_upstream;
+ /** the name of the tls authentication name, eg. 'ns.example.com'
+ * or NULL */
+ char* tls_auth_name;
/** where to send it */
struct sockaddr_storage addr;
/** length of addr field in use. */
@@ -484,6 +489,8 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
* @param nocaps: ignore use_caps_for_id and use unperturbed qname.
* @param tcp_upstream: use TCP for upstream queries.
* @param ssl_upstream: use SSL for upstream queries.
+ * @param tls_auth_name: when ssl_upstream is true, use this name to check
+ * the server's peer certificate.
* @param addr: to which server to send the query.
* @param addrlen: length of addr.
* @param zone: name of the zone of the delegation point. wireformat dname.
@@ -501,7 +508,7 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
*/
struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
- int nocaps, int tcp_upstream, int ssl_upstream,
+ int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
size_t zonelen, struct module_qstate* qstate,
comm_point_callback_type* callback, void* callback_arg,