diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/authzone.c | 653 | ||||
-rw-r--r-- | services/authzone.h | 50 | ||||
-rw-r--r-- | services/cache/dns.c | 48 | ||||
-rw-r--r-- | services/cache/rrset.c | 2 | ||||
-rw-r--r-- | services/listen_dnsport.c | 48 | ||||
-rw-r--r-- | services/mesh.c | 17 | ||||
-rw-r--r-- | services/outside_network.c | 39 | ||||
-rw-r--r-- | services/outside_network.h | 9 |
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, |