diff options
author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2018-05-12 11:53:39 +0000 |
---|---|---|
committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2018-05-12 11:53:39 +0000 |
commit | 6cacf549d3c2d5bddb0dcadd620e1db2897c7f26 (patch) | |
tree | e187e7d708a063f1628697fe779e2bb101d451b8 /services | |
parent | fbdb9ac866a647da0919b224f05cca039afc02fa (diff) | |
download | src-6cacf549d3c2d5bddb0dcadd620e1db2897c7f26.tar.gz src-6cacf549d3c2d5bddb0dcadd620e1db2897c7f26.zip |
Vendor import of Unbound 1.6.2.vendor/unbound/1.6.2
Notes
Notes:
svn path=/vendor/unbound/dist/; revision=333532
svn path=/vendor/unbound/1.6.2/; revision=333533; tag=vendor/unbound/1.6.2
Diffstat (limited to 'services')
-rw-r--r-- | services/cache/dns.c | 8 | ||||
-rw-r--r-- | services/cache/dns.h | 14 | ||||
-rw-r--r-- | services/listen_dnsport.c | 73 | ||||
-rw-r--r-- | services/listen_dnsport.h | 13 | ||||
-rw-r--r-- | services/localzone.c | 56 | ||||
-rw-r--r-- | services/localzone.h | 108 | ||||
-rw-r--r-- | services/mesh.c | 144 | ||||
-rw-r--r-- | services/mesh.h | 22 | ||||
-rw-r--r-- | services/modstack.c | 14 | ||||
-rw-r--r-- | services/view.c | 5 | ||||
-rw-r--r-- | services/view.h | 3 |
11 files changed, 394 insertions, 66 deletions
diff --git a/services/cache/dns.c b/services/cache/dns.c index 7beb76164986..a8fde9f2890e 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -479,8 +479,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num) return msg; } -/** generate dns_msg from cached message */ -static struct dns_msg* +struct dns_msg* tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, struct regional* region, time_t now, struct regional* scratch) { @@ -525,8 +524,11 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, return NULL; } } - rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, + if(env) + rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, r->rrset_count); + else + rrset_array_unlock(r->ref, r->rrset_count); return msg; } diff --git a/services/cache/dns.h b/services/cache/dns.h index 15a4a236b028..0dfb68874403 100644 --- a/services/cache/dns.h +++ b/services/cache/dns.h @@ -126,6 +126,20 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct regional* region, struct dns_msg** msg, time_t timenow); +/** + * generate dns_msg from cached message + * @param env: module environment with the DNS cache. NULL if the LRU from cache + * does not need to be touched. + * @param q: query info, contains qname that will make up the dns message. + * @param r: reply info that, together with qname, will make up the dns message. + * @param region: where to allocate dns message. + * @param now: the time now, for check if TTL on cache entry is ok. + * @param scratch: where to allocate temporary data. + * */ +struct dns_msg* tomsg(struct module_env* env, struct query_info* q, + struct reply_info* r, struct regional* region, time_t now, + struct regional* scratch); + /** * Find cached message * @param env: module environment with the DNS cache. diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 0132ce45f781..37ee9a6b9b46 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1056,15 +1056,25 @@ set_recvpktinfo(int s, int family) * @param tcp_mss: maximum segment size of tcp socket. default if zero. * @param freebind: set IP_FREEBIND socket option. * @param use_systemd: if true, fetch sockets from systemd. + * @param dnscrypt_port: dnscrypt service port number * @return: returns false on error. */ 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 tcp_mss, int freebind, int use_systemd, int dnscrypt_port) { int s, noip6=0; +#ifdef USE_DNSCRYPT + int is_dnscrypt = ((strchr(ifname, '@') && + atoi(strchr(ifname, '@')+1) == dnscrypt_port) || + (!strchr(ifname, '@') && atoi(port) == dnscrypt_port)); +#else + int is_dnscrypt = 0; + (void)dnscrypt_port; +#endif + if(!do_udp && !do_tcp) return 0; if(do_auto) { @@ -1086,7 +1096,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, #endif return 0; } - if(!port_insert(list, s, listen_type_udpancil)) { + if(!port_insert(list, s, + is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) { #ifndef USE_WINSOCK close(s); #else @@ -1105,7 +1116,8 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, } return 0; } - if(!port_insert(list, s, listen_type_udp)) { + if(!port_insert(list, s, + is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) { #ifndef USE_WINSOCK close(s); #else @@ -1130,7 +1142,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, if(is_ssl) verbose(VERB_ALGO, "setup TCP for SSL service"); if(!port_insert(list, s, is_ssl?listen_type_ssl: - listen_type_tcp)) { + (is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) { #ifndef USE_WINSOCK close(s); #else @@ -1172,6 +1184,9 @@ listen_create(struct comm_base* base, struct listen_port* ports, return NULL; front->cps = NULL; front->udp_buff = sldns_buffer_new(bufsize); +#ifdef USE_DNSCRYPT + front->dnscrypt_udp_buff = NULL; +#endif if(!front->udp_buff) { free(front); return NULL; @@ -1180,17 +1195,20 @@ listen_create(struct comm_base* base, struct listen_port* ports, /* create comm points as needed */ while(ports) { struct comm_point* cp = NULL; - if(ports->ftype == listen_type_udp) + if(ports->ftype == listen_type_udp || + ports->ftype == listen_type_udp_dnscrypt) cp = comm_point_create_udp(base, ports->fd, front->udp_buff, cb, cb_arg); - else if(ports->ftype == listen_type_tcp) + else if(ports->ftype == listen_type_tcp || + ports->ftype == listen_type_tcp_dnscrypt) cp = comm_point_create_tcp(base, ports->fd, tcp_accept_count, bufsize, cb, cb_arg); else if(ports->ftype == listen_type_ssl) { cp = comm_point_create_tcp(base, ports->fd, tcp_accept_count, bufsize, cb, cb_arg); cp->ssl = sslctx; - } else if(ports->ftype == listen_type_udpancil) + } else if(ports->ftype == listen_type_udpancil || + ports->ftype == listen_type_udpancil_dnscrypt) cp = comm_point_create_udp_ancil(base, ports->fd, front->udp_buff, cb, cb_arg); if(!cp) { @@ -1200,6 +1218,21 @@ listen_create(struct comm_base* base, struct listen_port* ports, } cp->dtenv = dtenv; cp->do_not_close = 1; +#ifdef USE_DNSCRYPT + if (ports->ftype == listen_type_udp_dnscrypt || + ports->ftype == listen_type_tcp_dnscrypt || + ports->ftype == listen_type_udpancil_dnscrypt) { + cp->dnscrypt = 1; + cp->dnscrypt_buffer = sldns_buffer_new(bufsize); + if(!cp->dnscrypt_buffer) { + log_err("can't alloc dnscrypt_buffer"); + comm_point_delete(cp); + listen_delete(front); + return NULL; + } + front->dnscrypt_udp_buff = cp->dnscrypt_buffer; + } +#endif if(!listen_cp_insert(cp, front)) { log_err("malloc failed"); comm_point_delete(cp); @@ -1235,6 +1268,12 @@ listen_delete(struct listen_dnsport* front) if(!front) return; listen_list_delete(front->cps); +#ifdef USE_DNSCRYPT + if(front->dnscrypt_udp_buff && + front->udp_buff != front->dnscrypt_udp_buff) { + sldns_buffer_free(front->dnscrypt_udp_buff); + } +#endif sldns_buffer_free(front->udp_buff); free(front); } @@ -1278,7 +1317,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport) cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port, reuseport, cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) { + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1291,7 +1331,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport) cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port, reuseport, cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) { + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1306,7 +1347,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport) cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port, reuseport, cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) { + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1319,7 +1361,8 @@ listening_ports_open(struct config_file* cfg, int* reuseport) cfg->so_rcvbuf, cfg->so_sndbuf, cfg->ssl_port, reuseport, cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd)) { + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1347,10 +1390,16 @@ void listening_ports_free(struct listen_port* list) size_t listen_get_mem(struct listen_dnsport* listen) { + struct listen_list* p; size_t s = sizeof(*listen) + sizeof(*listen->base) + sizeof(*listen->udp_buff) + sldns_buffer_capacity(listen->udp_buff); - struct listen_list* p; +#ifdef USE_DNSCRYPT + s += sizeof(*listen->dnscrypt_udp_buff); + if(listen->udp_buff != listen->dnscrypt_udp_buff){ + s += sldns_buffer_capacity(listen->dnscrypt_udp_buff); + } +#endif for(p = listen->cps; p; p = p->next) { s += sizeof(*p); s += comm_point_get_mem(p->com); diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h index 93d2ef7148e2..fac0f7970924 100644 --- a/services/listen_dnsport.h +++ b/services/listen_dnsport.h @@ -59,7 +59,9 @@ struct listen_dnsport { /** buffer shared by UDP connections, since there is only one datagram at any time. */ struct sldns_buffer* udp_buff; - +#ifdef USE_DNSCRYPT + struct sldns_buffer* dnscrypt_udp_buff; +#endif /** list of comm points used to get incoming events */ struct listen_list* cps; }; @@ -85,7 +87,14 @@ enum listen_type { /** udp ipv6 (v4mapped) for use with ancillary data */ listen_type_udpancil, /** ssl over tcp type */ - listen_type_ssl + listen_type_ssl, + /** udp type + dnscrypt*/ + listen_type_udp_dnscrypt, + /** tcp type + dnscrypt */ + listen_type_tcp_dnscrypt, + /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/ + listen_type_udpancil_dnscrypt + }; /** diff --git a/services/localzone.c b/services/localzone.c index d813ab586172..dcce46e863e4 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -229,9 +229,8 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type, return z; } -/** return name and class and rdata of rr; parses string */ -static int -get_rr_content(const char* str, uint8_t** nm, uint16_t* type, +int +rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, uint8_t** rdata, size_t* rdata_len) { @@ -353,8 +352,8 @@ new_local_rrset(struct regional* region, struct local_data* node, } /** insert RR into RRset data structure; Wastes a couple of bytes */ -static int -insert_rr(struct regional* region, struct packed_rrset_data* pd, +int +rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr) { size_t* oldlen = pd->rr_len; @@ -456,8 +455,8 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) uint8_t rr[LDNS_RR_BUF_SIZE]; uint8_t* rdata; size_t rdata_len; - if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), - &rdata, &rdata_len)) { + if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, + sizeof(rr), &rdata, &rdata_len)) { log_err("bad local-data: %s", rrstr); return 0; } @@ -513,7 +512,7 @@ lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); return 1; } - return insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr); + return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr); } /** enter a data RR into auth data; a zone for it must exist */ @@ -1233,9 +1232,10 @@ local_error_encode(struct query_info* qinfo, struct module_env* env, } /** find local data tag string match for the given type in the list */ -static int -find_tag_datas(struct query_info* qinfo, struct config_strlist* list, - struct ub_packed_rrset_key* r, struct regional* temp) +int +local_data_find_tag_datas(const struct query_info* qinfo, + struct config_strlist* list, struct ub_packed_rrset_key* r, + struct regional* temp) { struct config_strlist* p; char buf[65536]; @@ -1312,13 +1312,24 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list, sldns_wirerr_get_rdatawl(rr, len, 1), d->rr_len[d->count]); if(!d->rr_data[d->count]) - if(!d) return 0; /* out of memory */ + return 0; /* out of memory */ d->count++; } + if(r->rk.dname) + return 1; + return 0; +} + +static int +find_tag_datas(struct query_info* qinfo, struct config_strlist* list, + struct ub_packed_rrset_key* r, struct regional* temp) +{ + int result = local_data_find_tag_datas(qinfo, list, r, temp); + /* If we've found a non-exact alias type of local data, make a shallow * copy of the RRset and remember it in qinfo to complete the alias * chain later. */ - if(r->rk.dname && qinfo->qtype != LDNS_RR_TYPE_CNAME && + if(result && qinfo->qtype != LDNS_RR_TYPE_CNAME && r->rk.type == htons(LDNS_RR_TYPE_CNAME)) { qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset)); @@ -1329,9 +1340,7 @@ find_tag_datas(struct query_info* qinfo, struct config_strlist* list, if(!qinfo->local_alias->rrset) return 0; /* out of memory */ } - if(r->rk.dname) - return 1; - return 0; + return result; } /** answer local data match */ @@ -1497,8 +1506,6 @@ lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2, struct comm_reply* repinfo, struct rbtree_type* override_tree, int* tag, char** tagname, int num_tags) { - size_t i, j; - uint8_t tagmatch; struct local_zone_override* lzo; if(repinfo && override_tree) { lzo = (struct local_zone_override*)addr_tree_lookup( @@ -1511,6 +1518,19 @@ lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2, } if(!taglist || !taglist2) return lzt; + return local_data_find_tag_action(taglist, taglen, taglist2, taglen2, + tagactions, tagactionssize, lzt, tag, tagname, num_tags); +} + +enum localzone_type +local_data_find_tag_action(const uint8_t* taglist, size_t taglen, + const uint8_t* taglist2, size_t taglen2, const uint8_t* tagactions, + size_t tagactionssize, enum localzone_type lzt, int* tag, + char* const* tagname, int num_tags) +{ + size_t i, j; + uint8_t tagmatch; + for(i=0; i<taglen && i<taglen2; i++) { tagmatch = (taglist[i] & taglist2[i]); for(j=0; j<8 && tagmatch>0; j++) { diff --git a/services/localzone.h b/services/localzone.h index bf9c9bf489cb..658f28024ef4 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -46,6 +46,7 @@ #include "util/storage/dnstree.h" #include "util/module.h" #include "services/view.h" +struct packed_rrset_data; struct ub_packed_rrset_key; struct regional; struct config_file; @@ -389,4 +390,111 @@ void local_zones_del_data(struct local_zones* zones, */ int parse_dname(const char* str, uint8_t** res, size_t* len, int* labs); +/** + * Find local data tag string match for the given type (in qinfo) in the list. + * If found, 'r' will be filled with corresponding rrset information. + * @param qinfo: contains name, type, and class for the data + * @param list: stores local tag data to be searched + * @param r: rrset key to be filled for matched data + * @param temp: region to allocate rrset in 'r' + * @return 1 if a match is found and rrset is built; otherwise 0 including + * errors. + */ +int local_data_find_tag_datas(const struct query_info* qinfo, + struct config_strlist* list, struct ub_packed_rrset_key* r, + struct regional* temp); + +/** + * See if two sets of tag lists (in the form of bitmap) have the same tag that + * has an action. If so, '*tag' will be set to the found tag index, and the + * corresponding action will be returned in the form of local zone type. + * Otherwise the passed type (lzt) will be returned as the default action. + * Pointers except tagactions must not be NULL. + * @param taglist: 1st list of tags + * @param taglen: size of taglist in bytes + * @param taglist2: 2nd list of tags + * @param taglen2: size of taglist2 in bytes + * @param tagactions: local data actions for tags. May be NULL. + * @param tagactionssize: length of the tagactions. + * @param lzt: default action (local zone type) if no tag action is found. + * @param tag: see above. + * @param tagname: array of tag name strings (for debug output). + * @param num_tags: number of items in tagname array. + * @return found tag action or the default action. + */ +enum localzone_type local_data_find_tag_action(const uint8_t* taglist, + size_t taglen, const uint8_t* taglist2, size_t taglen2, + const uint8_t* tagactions, size_t tagactionssize, + enum localzone_type lzt, int* tag, char* const* tagname, int num_tags); + +/** + * Parses resource record string into wire format, also returning its field values. + * @param str: input resource record + * @param nm: domain name field + * @param type: record type field + * @param dclass: record class field + * @param ttl: ttl field + * @param rr: buffer for the parsed rr in wire format + * @param len: buffer length + * @param rdata: rdata field + * @param rdata_len: rdata field length + * @return 1 on success; 0 otherwise. + */ +int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, + uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, + uint8_t** rdata, size_t* rdata_len); + +/** + * Insert specified rdata into the specified resource record. + * @param region: allocator + * @param pd: data portion of the destination resource record + * @param rdata: source rdata + * @param rdata_len: source rdata length + * @param ttl: time to live + * @param rrstr: resource record in text form (for logging) + * @return 1 on success; 0 otherwise. + */ +int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, + uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr); + +/** + * Valid response ip actions for the IP-response-driven-action feature; + * defined here instead of in the respip module to enable sharing of enum + * values with the localzone_type enum. + * Note that these values except 'none' are the same as localzone types of + * the 'same semantics'. It's intentional as we use these values via + * access-control-tags, which can be shared for both response ip actions and + * local zones. + */ +enum respip_action { + /** no respip action */ + respip_none = local_zone_unset, + /** don't answer */ + respip_deny = local_zone_deny, + /** redirect as per provided data */ + respip_redirect = local_zone_redirect, + /** log query source and answer query */ + respip_inform = local_zone_inform, + /** log query source and don't answer query */ + respip_inform_deny = local_zone_inform_deny, + /** resolve normally, even when there is response-ip data */ + respip_always_transparent = local_zone_always_transparent, + /** answer with 'refused' response */ + respip_always_refuse = local_zone_always_refuse, + /** answer with 'no such domain' response */ + respip_always_nxdomain = local_zone_always_nxdomain, + + /* The rest of the values are only possible as + * access-control-tag-action */ + + /** serves response data (if any), else, drops queries. */ + respip_refuse = local_zone_refuse, + /** serves response data, else, nodata answer. */ + respip_static = local_zone_static, + /** gives response data (if any), else nodata answer. */ + respip_transparent = local_zone_transparent, + /** gives response data (if any), else nodata answer. */ + respip_typetransparent = local_zone_typetransparent, +}; + #endif /* SERVICES_LOCALZONE_H */ diff --git a/services/mesh.c b/services/mesh.c index f5a193ac2d48..0cb134ade85f 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -59,6 +59,7 @@ #include "sldns/wire2str.h" #include "services/localzone.h" #include "util/data/dname.h" +#include "respip/respip.h" /** subtract timers and the values do not overflow or become negative */ static void @@ -124,11 +125,64 @@ timeval_smaller(const struct timeval* x, const struct timeval* y) #endif } +/* + * Compare two response-ip client info entries for the purpose of mesh state + * compare. It returns 0 if ci_a and ci_b are considered equal; otherwise + * 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but + * in practice it should be only used to mean they are different). + * We cannot share the mesh state for two queries if different response-ip + * actions can apply in the end, even if those queries are otherwise identical. + * For this purpose we compare tag lists and tag action lists; they should be + * identical to share the same state. + * For tag data, we don't look into the data content, as it can be + * expensive; unless tag data are not defined for both or they point to the + * exact same data in memory (i.e., they come from the same ACL entry), we + * consider these data different. + * Likewise, if the client info is associated with views, we don't look into + * the views. They are considered different unless they are exactly the same + * even if the views only differ in the names. + */ +static int +client_info_compare(const struct respip_client_info* ci_a, + const struct respip_client_info* ci_b) +{ + int cmp; + + if(!ci_a && !ci_b) + return 0; + if(ci_a && !ci_b) + return -1; + if(!ci_a && ci_b) + return 1; + if(ci_a->taglen != ci_b->taglen) + return (ci_a->taglen < ci_b->taglen) ? -1 : 1; + cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen); + if(cmp != 0) + return cmp; + if(ci_a->tag_actions_size != ci_b->tag_actions_size) + return (ci_a->tag_actions_size < ci_b->tag_actions_size) ? + -1 : 1; + cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions, + ci_a->tag_actions_size); + if(cmp != 0) + return cmp; + if(ci_a->tag_datas != ci_b->tag_datas) + return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1; + if(ci_a->view != ci_b->view) + return ci_a->view < ci_b->view ? -1 : 1; + /* For the unbound daemon these should be non-NULL and identical, + * but we check that just in case. */ + if(ci_a->respip_set != ci_b->respip_set) + return ci_a->respip_set < ci_b->respip_set ? -1 : 1; + return 0; +} + int mesh_state_compare(const void* ap, const void* bp) { struct mesh_state* a = (struct mesh_state*)ap; struct mesh_state* b = (struct mesh_state*)bp; + int cmp; if(a->unique < b->unique) return -1; @@ -155,7 +209,10 @@ mesh_state_compare(const void* ap, const void* bp) if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD)) return 1; - return query_info_compare(&a->s.qinfo, &b->s.qinfo); + cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo); + if(cmp != 0) + return cmp; + return client_info_compare(a->s.client_info, b->s.client_info); } int @@ -287,16 +344,16 @@ int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf) } void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, - uint16_t qid) + struct respip_client_info* cinfo, uint16_t qflags, + struct edns_data* edns, struct comm_reply* rep, uint16_t qid) { struct mesh_state* s = NULL; - int unique = edns_unique_mesh_state(edns->opt_list, mesh->env); + int unique = unique_mesh_state(edns->opt_list, mesh->env); int was_detached = 0; int was_noreply = 0; int added = 0; if(!unique) - s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); /* does this create a new reply state? */ if(!s || s->list_select == mesh_no_list) { if(!mesh_make_new_space(mesh, rep->c->buffer)) { @@ -323,7 +380,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, #ifdef UNBOUND_DEBUG struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, cinfo, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { log_err("mesh_state_create: out of memory; SERVFAIL"); if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, @@ -412,12 +470,13 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qid, mesh_cb_func_type cb, void* cb_arg) { struct mesh_state* s = NULL; - int unique = edns_unique_mesh_state(edns->opt_list, mesh->env); + int unique = unique_mesh_state(edns->opt_list, mesh->env); int was_detached = 0; int was_noreply = 0; int added = 0; if(!unique) - s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + /* there are no limits on the number of callbacks */ /* see if it already exists, if not, create one */ @@ -425,7 +484,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, #ifdef UNBOUND_DEBUG struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { return 0; } @@ -476,8 +536,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, time_t leeway) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), - 0, 0); + struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, + qflags&(BIT_RD|BIT_CD), 0, 0); #ifdef UNBOUND_DEBUG struct rbnode_type* n; #endif @@ -497,7 +557,8 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, return; } - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { log_err("prefetch mesh_state_create: out of memory"); return; @@ -546,7 +607,8 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, struct mesh_state* mesh_state_create(struct module_env* env, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec) + struct respip_client_info* cinfo, uint16_t qflags, int prime, + int valrec) { struct regional* region = alloc_reg_obtain(env->alloc); struct mesh_state* mstate; @@ -582,6 +644,14 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, alloc_reg_release(env->alloc, region); return NULL; } + if(cinfo) { + mstate->s.client_info = regional_alloc_init(region, cinfo, + sizeof(*cinfo)); + if(!mstate->s.client_info) { + alloc_reg_release(env->alloc, region); + return NULL; + } + } /* remove all weird bits from qflags */ mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD)); mstate->s.is_priming = prime; @@ -756,7 +826,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, { /* find it, if not, create it */ struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime, valrec); + struct mesh_state* sub = mesh_area_find(mesh, NULL, qinfo, qflags, + prime, valrec); int was_detached; if(mesh_detect_cycle_found(qstate, sub)) { verbose(VERB_ALGO, "attach failed, cycle detected"); @@ -767,7 +838,8 @@ int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, struct rbnode_type* n; #endif /* create a new one */ - sub = mesh_state_create(qstate->env, qinfo, qflags, prime, valrec); + sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, + valrec); if(!sub) { log_err("mesh_attach_sub: out of memory"); return 0; @@ -1035,8 +1107,25 @@ void mesh_query_done(struct mesh_state* mstate) struct reply_info* rep = (mstate->s.return_msg? mstate->s.return_msg->rep:NULL); for(r = mstate->reply_list; r; r = r->next) { - mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev); - prev = r; + /* if a response-ip address block has been stored the + * information should be logged for each client. */ + if(mstate->s.respip_action_info && + mstate->s.respip_action_info->addrinfo) { + respip_inform_print(mstate->s.respip_action_info->addrinfo, + r->qname, mstate->s.qinfo.qtype, + mstate->s.qinfo.qclass, r->local_alias, + &r->query_reply); + } + + /* if this query is determined to be dropped during the + * mesh processing, this is the point to take that action. */ + if(mstate->s.is_drop) + comm_point_drop_reply(&r->query_reply); + else { + mesh_send_reply(mstate, mstate->s.return_rcode, rep, + r, prev); + prev = r; + } } mstate->replies_sent = 1; for(c = mstate->cb_list; c; c = c->next) { @@ -1060,7 +1149,8 @@ void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate) } struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec) + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec) { struct mesh_state key; struct mesh_state* result; @@ -1074,6 +1164,7 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, * aggregate the state. Thus unique is set to NULL. (default when we * desire aggregation).*/ key.unique = NULL; + key.s.client_info = cinfo; result = (struct mesh_state*)rbtree_search(&mesh->all, &key); return result; @@ -1224,11 +1315,16 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, return mesh_continue(mesh, mstate, module_error, ev); } if(s == module_restart_next) { - fptr_ok(fptr_whitelist_mod_clear( - mesh->mods.mod[mstate->s.curmod]->clear)); - (*mesh->mods.mod[mstate->s.curmod]->clear) - (&mstate->s, mstate->s.curmod); - mstate->s.minfo[mstate->s.curmod] = NULL; + int curmod = mstate->s.curmod; + for(; mstate->s.curmod < mesh->mods.num; + mstate->s.curmod++) { + fptr_ok(fptr_whitelist_mod_clear( + mesh->mods.mod[mstate->s.curmod]->clear)); + (*mesh->mods.mod[mstate->s.curmod]->clear) + (&mstate->s, mstate->s.curmod); + mstate->s.minfo[mstate->s.curmod] = NULL; + } + mstate->s.curmod = curmod; } *ev = module_event_pass; return 1; @@ -1378,7 +1474,7 @@ mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo, struct mesh_area* mesh = qstate->env->mesh; struct mesh_state* dep_m = NULL; if(!mesh_state_is_unique(qstate->mesh_info)) - dep_m = mesh_area_find(mesh, qinfo, flags, prime, valrec); + dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); return mesh_detect_cycle_found(qstate, dep_m); } diff --git a/services/mesh.h b/services/mesh.h index 435f89c689d5..1c77945320e3 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -59,6 +59,7 @@ struct query_info; struct reply_info; struct outbound_entry; struct timehist; +struct respip_client_info; /** * Maximum number of mesh state activations. Any more is likely an @@ -274,14 +275,18 @@ void mesh_delete(struct mesh_area* mesh); * * @param mesh: the mesh. * @param qinfo: query from client. + * @param cinfo: additional information associated with the query client. + * 'cinfo' itself is ephemeral but data pointed to by its members + * can be assumed to be valid and unchanged until the query processing is + * completed. * @param qflags: flags from client query. * @param edns: edns data from client query. * @param rep: where to reply to. * @param qid: query id to reply with. */ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, - uint16_t qid); + struct respip_client_info* cinfo, uint16_t qflags, + struct edns_data* edns, struct comm_reply* rep, uint16_t qid); /** * New query with callback. Create new query state if needed, and @@ -409,14 +414,16 @@ void mesh_state_delete(struct module_qstate* qstate); * Does not put the mesh state into rbtrees and so on. * @param env: module environment to set. * @param qinfo: query info that the mesh is for. + * @param cinfo: control info for the query client (can be NULL). * @param qflags: flags for query (RD / CD flag). * @param prime: if true, it is a priming query, set is_priming on mesh state. * @param valrec: if true, it is a validation recursion query, and sets * is_valrec on the mesh state. * @return: new mesh state or NULL on allocation error. */ -struct mesh_state* mesh_state_create(struct module_env* env, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec); +struct mesh_state* mesh_state_create(struct module_env* env, + struct query_info* qinfo, struct respip_client_info* cinfo, + uint16_t qflags, int prime, int valrec); /** * Check if the mesh state is unique. @@ -451,14 +458,17 @@ void mesh_delete_all(struct mesh_area* mesh); * Find a mesh state in the mesh area. Pass relevant flags. * * @param mesh: the mesh area to look in. + * @param cinfo: if non-NULL client specific info that may affect IP-based + * actions that apply to the query result. * @param qinfo: what query * @param qflags: if RD / CD bit is set or not. * @param prime: if it is a priming query. * @param valrec: if it is a validation-recursion query. * @return: mesh state or NULL if not found. */ -struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec); +struct mesh_state* mesh_area_find(struct mesh_area* mesh, + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec); /** * Setup attachment super/sub relation between super and sub mesh state. diff --git a/services/modstack.c b/services/modstack.c index 70e066670d5d..9bebd3a5634c 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -46,6 +46,7 @@ #include "dns64/dns64.h" #include "iterator/iterator.h" #include "validator/validator.h" +#include "respip/respip.h" #ifdef WITH_PYTHONMODULE #include "pythonmod/pythonmod.h" @@ -53,6 +54,9 @@ #ifdef USE_CACHEDB #include "cachedb/cachedb.h" #endif +#ifdef CLIENT_SUBNET +#include "edns-subnet/subnetmod.h" +#endif /** count number of modules (words) in the string */ static int @@ -127,6 +131,10 @@ module_list_avail(void) #ifdef USE_CACHEDB "cachedb", #endif +#ifdef CLIENT_SUBNET + "subnetcache", +#endif + "respip", "validator", "iterator", NULL}; @@ -148,6 +156,10 @@ module_funcs_avail(void) #ifdef USE_CACHEDB &cachedb_get_funcblock, #endif +#ifdef CLIENT_SUBNET + &subnetmod_get_funcblock, +#endif + &respip_get_funcblock, &val_get_funcblock, &iter_get_funcblock, NULL}; @@ -216,7 +228,7 @@ int modstack_find(struct module_stack* stack, const char* name) { int i; - for(i=0; i<stack->num; i++) { + for(i=0; i<stack->num; i++) { if(strcmp(stack->mod[i]->name, name) == 0) return i; } diff --git a/services/view.c b/services/view.c index c9dfc3c87383..33f4f4986ba7 100644 --- a/services/view.c +++ b/services/view.c @@ -66,6 +66,10 @@ views_create(void) return v; } +/** This prototype is defined in in respip.h, but we want to avoid + * unnecessary dependencies */ +void respip_set_delete(struct respip_set *set); + void view_delete(struct view* v) { @@ -73,6 +77,7 @@ view_delete(struct view* v) return; lock_rw_destroy(&v->lock); local_zones_delete(v->local_zones); + respip_set_delete(v->respip_set); free(v->name); free(v); } diff --git a/services/view.h b/services/view.h index ce4b69d6c510..e0b346419e9b 100644 --- a/services/view.h +++ b/services/view.h @@ -47,6 +47,7 @@ struct regional; struct config_file; struct config_view; +struct respip_set; /** @@ -71,6 +72,8 @@ struct view { char* name; /** view specific local authority zones */ struct local_zones* local_zones; + /** response-ip configuration data for this view */ + struct respip_set* respip_set; /** Fallback to global local_zones when there is no match in the view * specific tree. 1 for yes, 0 for no */ int isfirst; |