diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/authzone.c | 10 | ||||
-rw-r--r-- | services/cache/dns.c | 52 | ||||
-rw-r--r-- | services/cache/infra.c | 170 | ||||
-rw-r--r-- | services/cache/infra.h | 28 | ||||
-rw-r--r-- | services/cache/rrset.c | 87 | ||||
-rw-r--r-- | services/cache/rrset.h | 31 | ||||
-rw-r--r-- | services/listen_dnsport.c | 51 | ||||
-rw-r--r-- | services/listen_dnsport.h | 8 | ||||
-rw-r--r-- | services/localzone.c | 6 | ||||
-rw-r--r-- | services/mesh.c | 106 | ||||
-rw-r--r-- | services/mesh.h | 6 | ||||
-rw-r--r-- | services/rpz.c | 342 | ||||
-rw-r--r-- | services/rpz.h | 8 | ||||
-rw-r--r-- | services/view.h | 3 |
14 files changed, 734 insertions, 174 deletions
diff --git a/services/authzone.c b/services/authzone.c index 93fef8ef1c29..f01a6d9e0e0e 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -2152,6 +2152,16 @@ auth_zones_cfg(struct auth_zones* az, struct config_auth* c) if(az->rpz_first) az->rpz_first->rpz_az_prev = z; az->rpz_first = z; + } else if(c->isrpz && z->rpz) { + if(!rpz_config(z->rpz, c)) { + log_err("Could not change rpz config"); + if(x) { + lock_basic_unlock(&x->lock); + } + lock_rw_unlock(&z->lock); + lock_rw_unlock(&az->rpz_lock); + return 0; + } } if(c->isrpz) { lock_rw_unlock(&az->rpz_lock); diff --git a/services/cache/dns.c b/services/cache/dns.c index 6a980548d72f..632ed79ace49 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -193,46 +193,6 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc); } -/** see if an rrset is expired above the qname, return upper qname. */ -static int -rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen, - uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop, - size_t expiretoplen) -{ - struct ub_packed_rrset_key *rrset; - uint8_t lablen; - - while(*qnamelen > 0) { - /* look one label higher */ - lablen = **qname; - *qname += lablen + 1; - *qnamelen -= lablen + 1; - if(*qnamelen <= 0) - break; - - /* looks up with a time of 0, to see expired entries */ - if((rrset = rrset_cache_lookup(env->rrset_cache, *qname, - *qnamelen, searchtype, qclass, 0, 0, 0))) { - struct packed_rrset_data* data = - (struct packed_rrset_data*)rrset->entry.data; - if(now > data->ttl) { - /* it is expired, this is not wanted */ - lock_rw_unlock(&rrset->entry.lock); - log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass); - return 1; - } - /* it is not expired, continue looking */ - lock_rw_unlock(&rrset->entry.lock); - } - - /* do not look above the expiretop. */ - if(expiretop && *qnamelen == expiretoplen && - query_dname_compare(*qname, expiretop)==0) - break; - } - return 0; -} - /** find closest NS or DNAME and returns the rrset (locked) */ static struct ub_packed_rrset_key* find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, @@ -266,12 +226,12 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, /* check for expiry, but we have to let go of the rrset * for the lock ordering */ lock_rw_unlock(&rrset->entry.lock); - /* the expired_above function always takes off one - * label (if qnamelen>0) and returns the final qname - * where it searched, so we can continue from there - * turning the O N*N search into O N. */ - if(!rrset_expired_above(env, &qname, &qnamelen, - searchtype, qclass, now, expiretop, + /* the rrset_cache_expired_above function always takes + * off one label (if qnamelen>0) and returns the final + * qname where it searched, so we can continue from + * there turning the O N*N search into O N. */ + if(!rrset_cache_expired_above(env->rrset_cache, &qname, + &qnamelen, searchtype, qclass, now, expiretop, expiretoplen)) { /* we want to return rrset, but it may be * gone from cache, if so, just loop like diff --git a/services/cache/infra.c b/services/cache/infra.c index 31462d13ae0a..457685ab5985 100644 --- a/services/cache/infra.c +++ b/services/cache/infra.c @@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* infra, struct config_file* cfg) return 1; } +/** find or create element in wait limit netblock tree */ +static struct wait_limit_netblock_info* +wait_limit_netblock_findcreate(struct infra_cache* infra, char* str, + int cookie) +{ + rbtree_type* tree; + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + struct wait_limit_netblock_info* d; + + if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) { + log_err("cannot parse wait limit netblock '%s'", str); + return 0; + } + + /* can we find it? */ + if(cookie) + tree = &infra->wait_limits_cookie_netblock; + else + tree = &infra->wait_limits_netblock; + d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr, + addrlen, net); + if(d) + return d; + + /* create it */ + d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d)); + if(!d) + return NULL; + d->limit = -1; + if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) { + log_err("duplicate element in domainlimit tree"); + free(d); + return NULL; + } + return d; +} + + +/** insert wait limit information into lookup tree */ +static int +infra_wait_limit_netblock_insert(struct infra_cache* infra, + struct config_file* cfg) +{ + struct config_str2list* p; + struct wait_limit_netblock_info* d; + for(p = cfg->wait_limit_netblock; p; p = p->next) { + d = wait_limit_netblock_findcreate(infra, p->str, 0); + if(!d) + return 0; + d->limit = atoi(p->str2); + } + for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) { + d = wait_limit_netblock_findcreate(infra, p->str, 1); + if(!d) + return 0; + d->limit = atoi(p->str2); + } + return 1; +} + +/** setup wait limits tree (0 on failure) */ +static int +setup_wait_limits(struct infra_cache* infra, struct config_file* cfg) +{ + addr_tree_init(&infra->wait_limits_netblock); + addr_tree_init(&infra->wait_limits_cookie_netblock); + if(!infra_wait_limit_netblock_insert(infra, cfg)) + return 0; + addr_tree_init_parents(&infra->wait_limits_netblock); + addr_tree_init_parents(&infra->wait_limits_cookie_netblock); + return 1; +} + struct infra_cache* infra_create(struct config_file* cfg) { @@ -267,6 +342,10 @@ infra_create(struct config_file* cfg) infra_delete(infra); return NULL; } + if(!setup_wait_limits(infra, cfg)) { + infra_delete(infra); + return NULL; + } infra_ip_ratelimit = cfg->ip_ratelimit; infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, @@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) } } +/** delete wait_limit_netblock_info entries */ +static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + free(n); +} + void infra_delete(struct infra_cache* infra) { @@ -296,6 +381,10 @@ infra_delete(struct infra_cache* infra) slabhash_delete(infra->domain_rates); traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); slabhash_delete(infra->client_ip_rates); + traverse_postorder(&infra->wait_limits_netblock, + wait_limit_netblock_del, NULL); + traverse_postorder(&infra->wait_limits_cookie_netblock, + wait_limit_netblock_del, NULL); free(infra); } @@ -880,7 +969,8 @@ static void infra_create_ratedata(struct infra_cache* infra, /** create rate data item for ip address */ static void infra_ip_create_ratedata(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow) + struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow, + int mesh_wait) { hashvalue_type h = hash_addr(addr, addrlen, 0); struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); @@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(struct infra_cache* infra, k->entry.data = d; d->qps[0] = 1; d->timestamp[0] = timenow; + d->mesh_wait = mesh_wait; slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); } @@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra, } /* create */ - infra_ip_create_ratedata(infra, addr, addrlen, timenow); + infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0); return 1; } + +int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, + int cookie_valid, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return 1; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 0); + if(entry) { + rbtree_type* tree; + struct wait_limit_netblock_info* w; + struct rate_data* d = (struct rate_data*)entry->data; + int mesh_wait = d->mesh_wait; + lock_rw_unlock(&entry->lock); + + /* have the wait amount, check how much is allowed */ + if(cookie_valid) + tree = &infra->wait_limits_cookie_netblock; + else tree = &infra->wait_limits_netblock; + w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree, + &rep->client_addr, rep->client_addrlen); + if(w) { + if(w->limit != -1 && mesh_wait > w->limit) + return 0; + } else { + /* if there is no IP netblock specific information, + * use the configured value. */ + if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie: + cfg->wait_limit)) + return 0; + } + } + return 1; +} + +void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, + time_t timenow, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + /* Find it */ + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + d->mesh_wait++; + lock_rw_unlock(&entry->lock); + return; + } + + /* Create it */ + infra_ip_create_ratedata(infra, &rep->client_addr, + rep->client_addrlen, timenow, 1); +} + +void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, + struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + if(d->mesh_wait > 0) + d->mesh_wait--; + lock_rw_unlock(&entry->lock); + } +} diff --git a/services/cache/infra.h b/services/cache/infra.h index 525073bf35bb..ee6f384de345 100644 --- a/services/cache/infra.h +++ b/services/cache/infra.h @@ -122,6 +122,10 @@ struct infra_cache { rbtree_type domain_limits; /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ struct slabhash* client_ip_rates; + /** tree of addr_tree_node, with wait_limit_netblock_info information */ + rbtree_type wait_limits_netblock; + /** tree of addr_tree_node, with wait_limit_netblock_info information */ + rbtree_type wait_limits_cookie_netblock; }; /** ratelimit, unless overridden by domain_limits, 0 is off */ @@ -184,10 +188,22 @@ struct rate_data { /** what the timestamp is of the qps array members, counter is * valid for that timestamp. Usually now and now-1. */ time_t timestamp[RATE_WINDOW]; + /** the number of queries waiting in the mesh */ + int mesh_wait; }; #define ip_rate_data rate_data +/** + * Data to store the configuration per netblock for the wait limit + */ +struct wait_limit_netblock_info { + /** The addr tree node, this must be first. */ + struct addr_tree_node node; + /** the limit on the amount */ + int limit; +}; + /** infra host cache default hash lookup size */ #define INFRA_HOST_STARTSIZE 32 /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ @@ -474,4 +490,16 @@ void ip_rate_delkeyfunc(void* d, void* arg); /* delete data */ #define ip_rate_deldatafunc rate_deldatafunc +/** See if the IP address can have another reply in the wait limit */ +int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, + int cookie_valid, struct config_file* cfg); + +/** Increment number of waiting replies for IP */ +void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, + time_t timenow, struct config_file* cfg); + +/** Decrement number of waiting replies for IP */ +void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, + struct config_file* cfg); + #endif /* SERVICES_CACHE_INFRA_H */ diff --git a/services/cache/rrset.c b/services/cache/rrset.c index 4e3d08bdaaf5..2c03214c8fe2 100644 --- a/services/cache/rrset.c +++ b/services/cache/rrset.c @@ -46,6 +46,7 @@ #include "util/data/packed_rrset.h" #include "util/data/msgreply.h" #include "util/data/msgparse.h" +#include "util/data/dname.h" #include "util/regional.h" #include "util/alloc.h" #include "util/net_help.h" @@ -127,6 +128,9 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns) { struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; + /* o if new data is expired, current data is better */ + if( newd->ttl < timenow && cached->ttl >= timenow) + return 0; /* o store if rrset has been validated * everything better than bogus data * secure is preferred */ @@ -440,6 +444,89 @@ rrset_check_sec_status(struct rrset_cache* r, lock_rw_unlock(&e->lock); } +void +rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, size_t* + qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* + qnametop, size_t qnametoplen) +{ + struct ub_packed_rrset_key *rrset; + uint8_t lablen; + + while(*qnamelen > 0) { + /* look one label higher */ + lablen = **qname; + *qname += lablen + 1; + *qnamelen -= lablen + 1; + if(*qnamelen <= 0) + return; + + /* stop at qnametop */ + if(qnametop && *qnamelen == qnametoplen && + query_dname_compare(*qname, qnametop)==0) + return; + + if(verbosity >= VERB_ALGO) { + /* looks up with a time of 0, to see expired entries */ + if((rrset = rrset_cache_lookup(r, *qname, + *qnamelen, searchtype, qclass, 0, 0, 0))) { + struct packed_rrset_data* data = + (struct packed_rrset_data*)rrset->entry.data; + int expired = (now > data->ttl); + lock_rw_unlock(&rrset->entry.lock); + if(expired) + log_nametypeclass(verbosity, "this " + "(grand)parent rrset will be " + "removed (expired)", + *qname, searchtype, qclass); + else log_nametypeclass(verbosity, "this " + "(grand)parent rrset will be " + "removed", + *qname, searchtype, qclass); + } + } + rrset_cache_remove(r, *qname, *qnamelen, searchtype, qclass, 0); + } +} + +int +rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t* + qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* + qnametop, size_t qnametoplen) +{ + struct ub_packed_rrset_key *rrset; + uint8_t lablen; + + while(*qnamelen > 0) { + /* look one label higher */ + lablen = **qname; + *qname += lablen + 1; + *qnamelen -= lablen + 1; + if(*qnamelen <= 0) + break; + + /* looks up with a time of 0, to see expired entries */ + if((rrset = rrset_cache_lookup(r, *qname, + *qnamelen, searchtype, qclass, 0, 0, 0))) { + struct packed_rrset_data* data = + (struct packed_rrset_data*)rrset->entry.data; + if(now > data->ttl) { + /* it is expired, this is not wanted */ + lock_rw_unlock(&rrset->entry.lock); + log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass); + return 1; + } + /* it is not expired, continue looking */ + lock_rw_unlock(&rrset->entry.lock); + } + + /* do not look above the qnametop. */ + if(qnametop && *qnamelen == qnametoplen && + query_dname_compare(*qname, qnametop)==0) + break; + } + return 0; +} + void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, uint16_t type, uint16_t dclass, uint32_t flags) { diff --git a/services/cache/rrset.h b/services/cache/rrset.h index 7c36d4032ebc..6db79d90f532 100644 --- a/services/cache/rrset.h +++ b/services/cache/rrset.h @@ -232,6 +232,37 @@ void rrset_check_sec_status(struct rrset_cache* r, struct ub_packed_rrset_key* rrset, time_t now); /** + * Removes rrsets above the qname, returns upper qname. + * @param r: the rrset cache. + * @param qname: the start qname, also used as the output. + * @param qnamelen: length of qname, updated when it returns. + * @param searchtype: qtype to search for. + * @param qclass: qclass to search for. + * @param now: current time. + * @param qnametop: the top qname to stop removal (it is not removed). + * @param qnametoplen: length of qnametop. + */ +void rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, + size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, + uint8_t* qnametop, size_t qnametoplen); + +/** + * Sees if an rrset is expired above the qname, returns upper qname. + * @param r: the rrset cache. + * @param qname: the start qname, also used as the output. + * @param qnamelen: length of qname, updated when it returns. + * @param searchtype: qtype to search for. + * @param qclass: qclass to search for. + * @param now: current time. + * @param qnametop: the top qname, don't look farther than that. + * @param qnametoplen: length of qnametop. + * @return true if there is an expired rrset above, false otherwise. + */ +int rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, + size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, + uint8_t* qnametop, size_t qnametoplen); + +/** * Remove an rrset from the cache, by name and type and flags * @param r: rrset cache * @param nm: name of rrset diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 753550978a07..7eb59a1618a1 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -140,9 +140,11 @@ void verbose_print_unbound_socket(struct unbound_socket* ub_sock) { if(verbosity >= VERB_ALGO) { + char buf[256]; log_info("listing of unbound_socket structure:"); - verbose_print_addr(ub_sock->addr); - log_info("s is: %d, fam is: %s, acl: %s", ub_sock->s, + addr_to_str((void*)ub_sock->addr, ub_sock->addrlen, buf, + sizeof(buf)); + log_info("%s s is: %d, fam is: %s, acl: %s", buf, ub_sock->s, ub_sock->fam == AF_INET?"AF_INET":"AF_INET6", ub_sock->acl?"yes":"no"); } @@ -610,7 +612,9 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr, # elif defined(IP_DONTFRAG) && !defined(__APPLE__) /* the IP_DONTFRAG option if defined in the 11.0 OSX headers, * but does not work on that version, so we exclude it */ - int off = 0; + /* a nonzero value disables fragmentation, according to + * docs.oracle.com for ip(4). */ + int off = 1; if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG, &off, (socklen_t)sizeof(off)) < 0) { log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s", @@ -1047,7 +1051,22 @@ make_sock(int stype, const char* ifname, const char* port, } } - ub_sock->addr = res; + if(!res->ai_addr) { + log_err("getaddrinfo returned no address"); + freeaddrinfo(res); + sock_close(s); + return -1; + } + ub_sock->addr = memdup(res->ai_addr, res->ai_addrlen); + ub_sock->addrlen = res->ai_addrlen; + if(!ub_sock->addr) { + log_err("out of memory: allocate listening address"); + freeaddrinfo(res); + sock_close(s); + return -1; + } + freeaddrinfo(res); + ub_sock->s = s; ub_sock->fam = hints->ai_family; ub_sock->acl = NULL; @@ -1277,8 +1296,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { log_warn("IPv6 protocol not available"); @@ -1289,8 +1307,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, /* getting source addr packet info is highly non-portable */ if(!set_recvpktinfo(s, hints->ai_family)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1301,8 +1318,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, ?listen_type_udpancil_dnscrypt:listen_type_udpancil, is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1314,8 +1330,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { log_warn("IPv6 protocol not available"); @@ -1332,8 +1347,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, listen_type_udpancil:listen_type_udp), is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1356,8 +1370,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, &noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { /*log_warn("IPv6 protocol not available");*/ @@ -1369,8 +1382,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, verbose(VERB_ALGO, "setup TCP for SSL service"); if(!port_insert(list, s, port_type, is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1952,8 +1964,7 @@ void listening_ports_free(struct listen_port* list) } /* rc_ports don't have ub_socket */ if(list->socket) { - if(list->socket->addr) - freeaddrinfo(list->socket->addr); + free(list->socket->addr); free(list->socket); } free(list); diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h index 816d79aea61b..84ac4b068b1b 100644 --- a/services/listen_dnsport.h +++ b/services/listen_dnsport.h @@ -107,11 +107,13 @@ enum listen_type { * socket properties (just like NSD nsd_socket structure definition) */ struct unbound_socket { - /** socket-address structure */ - struct addrinfo* addr; + /** the address of the socket */ + struct sockaddr* addr; + /** length of the address */ + socklen_t addrlen; /** socket descriptor returned by socket() syscall */ int s; - /** address family (AF_INET/IF_INET6) */ + /** address family (AF_INET/AF_INET6) */ int fam; /** ACL on the socket (listening interface) */ struct acl_addr* acl; diff --git a/services/localzone.c b/services/localzone.c index 9c8e3c7acb16..51056c8ffef4 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -330,14 +330,16 @@ get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass, static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { - struct local_rrset* p; + struct local_rrset* p, *cname = NULL; type = htons(type); for(p = data->rrsets; p; p = p->next) { if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; + cname = p; } + if(alias_ok) + return cname; return NULL; } diff --git a/services/mesh.c b/services/mesh.c index 47cfb04249b5..e886c4b92c84 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -47,6 +47,7 @@ #include "services/outbound_list.h" #include "services/cache/dns.h" #include "services/cache/rrset.h" +#include "services/cache/infra.h" #include "util/log.h" #include "util/net_help.h" #include "util/module.h" @@ -385,7 +386,7 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout) &mesh_serve_expired_lookup; /* In case this timer already popped, start it again */ - if(!mstate->s.serve_expired_data->timer) { + if(!mstate->s.serve_expired_data->timer && timeout != -1) { mstate->s.serve_expired_data->timer = comm_timer_create( mstate->s.env->worker_base, mesh_serve_expired_callback, mstate); if(!mstate->s.serve_expired_data->timer) @@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, if(rep->c->tcp_req_info) { r_buffer = rep->c->tcp_req_info->spool_buffer; } + if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, + edns->cookie_valid, mesh->env->cfg)) { + verbose(VERB_ALGO, "Too many queries waiting from the IP. " + "dropping incoming query."); + comm_point_drop_reply(rep); + mesh->stats_dropped++; + return; + } if(!unique) s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); /* does this create a new reply state? */ @@ -511,6 +520,19 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, log_err("mesh_new_client: out of memory initializing serve expired"); goto servfail_mem; } +#ifdef USE_CACHEDB + if(!timeout && mesh->env->cfg->serve_expired && + !mesh->env->cfg->serve_expired_client_timeout && + (mesh->env->cachedb_enabled && + mesh->env->cfg->cachedb_check_when_serve_expired)) { + if(!mesh_serve_expired_init(s, -1)) { + log_err("mesh_new_client: out of memory initializing serve expired"); + goto servfail_mem; + } + } +#endif + infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now, + mesh->env->cfg); /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); @@ -616,6 +638,18 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, mesh_state_delete(&s->s); return 0; } +#ifdef USE_CACHEDB + if(!timeout && mesh->env->cfg->serve_expired && + !mesh->env->cfg->serve_expired_client_timeout && + (mesh->env->cachedb_enabled && + mesh->env->cfg->cachedb_check_when_serve_expired)) { + if(!mesh_serve_expired_init(s, -1)) { + if(added) + mesh_state_delete(&s->s); + return 0; + } + } +#endif /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); @@ -930,6 +964,8 @@ mesh_state_cleanup(struct mesh_state* mstate) * takes no time and also it does not do the mesh accounting */ mstate->reply_list = NULL; for(; rep; rep=rep->next) { + infra_wait_limit_dec(mesh->env->infra_cache, + &rep->query_reply, mesh->env->cfg); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; @@ -1179,7 +1215,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, rcode = LDNS_RCODE_SERVFAIL; if(!rcode && rep && (rep->security == sec_status_bogus || rep->security == sec_status_secure_sentinel_fail)) { - if(!(reason = errinf_to_str_bogus(&m->s))) + if(!(reason = errinf_to_str_bogus(&m->s, NULL))) rcode = LDNS_RCODE_SERVFAIL; } /* send the reply */ @@ -1413,6 +1449,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, comm_point_send_reply(&r->query_reply); m->reply_list = rlist; } + infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, + m->s.env->cfg); /* account */ log_assert(m->s.env->mesh->num_reply_addrs > 0); m->s.env->mesh->num_reply_addrs--; @@ -1436,7 +1474,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, log_reply_info(NO_VERBOSE, &m->s.qinfo, &r->query_reply.client_addr, r->query_reply.client_addrlen, duration, 0, r_buffer, - (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr->ai_addr:NULL), + (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL), r->query_reply.c->type); } } @@ -1464,12 +1502,32 @@ void mesh_query_done(struct mesh_state* mstate) && mstate->s.env->cfg->log_servfail && !mstate->s.env->cfg->val_log_squelch) { char* err = errinf_to_str_servfail(&mstate->s); - if(err) - log_err("%s", err); - free(err); + if(err) { log_err("%s", err); } } } for(r = mstate->reply_list; r; r = r->next) { + struct timeval old; + timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); + if(mstate->s.env->cfg->discard_timeout != 0 && + ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > + mstate->s.env->cfg->discard_timeout) { + /* Drop the reply, it is too old */ + /* briefly set the reply_list to NULL, so that the + * tcp req info cleanup routine that calls the mesh + * to deregister the meshstate for it is not done + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; + verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; + mstate->s.env->mesh->stats_dropped++; + continue; + } + i++; tv = r->start_time; @@ -1493,6 +1551,8 @@ void mesh_query_done(struct mesh_state* mstate) * because the list is NULL and also accounting is not * done there, but instead we do that here. */ struct mesh_reply* reply_list = mstate->reply_list; + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; @@ -2025,6 +2085,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, /* delete it, but allocated in m region */ log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; + infra_wait_limit_dec(mesh->env->infra_cache, + &n->query_reply, mesh->env->cfg); /* prev = prev; */ n = n->next; @@ -2165,6 +2227,28 @@ mesh_serve_expired_callback(void* arg) log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); for(r = mstate->reply_list; r; r = r->next) { + struct timeval old; + timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); + if(mstate->s.env->cfg->discard_timeout != 0 && + ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > + mstate->s.env->cfg->discard_timeout) { + /* Drop the reply, it is too old */ + /* briefly set the reply_list to NULL, so that the + * tcp req info cleanup routine that calls the mesh + * to deregister the meshstate for it is not done + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; + verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; + mstate->s.env->mesh->stats_dropped++; + continue; + } + i++; tv = r->start_time; @@ -2192,6 +2276,8 @@ mesh_serve_expired_callback(void* arg) r, r_buffer, prev, prev_buffer); if(r->query_reply.c->tcp_req_info) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); prev = r; prev_buffer = r_buffer; } @@ -2238,6 +2324,14 @@ mesh_serve_expired_callback(void* arg) } } +void +mesh_respond_serve_expired(struct mesh_state* mstate) +{ + if(!mstate->s.serve_expired_data) + mesh_serve_expired_init(mstate, -1); + mesh_serve_expired_callback(mstate); +} + int mesh_jostle_exceeded(struct mesh_area* mesh) { if(mesh->all.count < mesh->max_reply_states) diff --git a/services/mesh.h b/services/mesh.h index d926cfc9dec3..5bd53e065e8f 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -690,4 +690,10 @@ mesh_serve_expired_lookup(struct module_qstate* qstate, */ int mesh_jostle_exceeded(struct mesh_area* mesh); +/** + * Give the serve expired responses. + * @param mstate: mesh state for query that has serve_expired_data. + */ +void mesh_respond_serve_expired(struct mesh_state* mstate); + #endif /* SERVICES_MESH_H */ diff --git a/services/rpz.c b/services/rpz.c index 18d76c07bff3..f036cc5fd649 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -478,43 +478,30 @@ new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen) return rrset; } -struct rpz* -rpz_create(struct config_auth* p) +/** delete the cname override */ +static void +delete_cname_override(struct rpz* r) { - struct rpz* r = calloc(1, sizeof(*r)); - if(!r) - goto err; - - r->region = regional_create_custom(sizeof(struct regional)); - if(!r->region) { - goto err; - } - - if(!(r->local_zones = local_zones_create())){ - goto err; - } - - r->nsdname_zones = local_zones_create(); - if(r->local_zones == NULL){ - goto err; - } - - if(!(r->respip_set = respip_set_create())) { - goto err; - } - - r->client_set = rpz_clientip_synthesized_set_create(); - if(r->client_set == NULL) { - goto err; + if(r->cname_override) { + /* The cname override is what is allocated in the region. */ + regional_free_all(r->region); + r->cname_override = NULL; } +} - r->ns_set = rpz_clientip_synthesized_set_create(); - if(r->ns_set == NULL) { - goto err; +/** Apply rpz config elements to the rpz structure, false on failure. */ +static int +rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p) +{ + if(p->rpz_taglist && p->rpz_taglistlen) { + r->taglistlen = p->rpz_taglistlen; + r->taglist = memdup(p->rpz_taglist, r->taglistlen); + if(!r->taglist) { + log_err("malloc failure on RPZ taglist alloc"); + return 0; + } } - r->taglistlen = p->rpz_taglistlen; - r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { r->action_override = rpz_config_to_action(p->rpz_action_override); } @@ -528,17 +515,17 @@ rpz_create(struct config_auth* p) if(!p->rpz_cname) { log_err("rpz: override with cname action found, but no " "rpz-cname-override configured"); - goto err; + return 0; } if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) { log_err("rpz: cannot parse cname override: %s", p->rpz_cname); - goto err; + return 0; } r->cname_override = new_cname_override(r->region, nm, nmlen); if(!r->cname_override) { - goto err; + return 0; } } r->log = p->rpz_log; @@ -546,9 +533,49 @@ rpz_create(struct config_auth* p) if(p->rpz_log_name) { if(!(r->log_name = strdup(p->rpz_log_name))) { log_err("malloc failure on RPZ log_name strdup"); - goto err; + return 0; } } + return 1; +} + +struct rpz* +rpz_create(struct config_auth* p) +{ + struct rpz* r = calloc(1, sizeof(*r)); + if(!r) + goto err; + + r->region = regional_create_custom(sizeof(struct regional)); + if(!r->region) { + goto err; + } + + if(!(r->local_zones = local_zones_create())){ + goto err; + } + + r->nsdname_zones = local_zones_create(); + if(r->local_zones == NULL){ + goto err; + } + + if(!(r->respip_set = respip_set_create())) { + goto err; + } + + r->client_set = rpz_clientip_synthesized_set_create(); + if(r->client_set == NULL) { + goto err; + } + + r->ns_set = rpz_clientip_synthesized_set_create(); + if(r->ns_set == NULL) { + goto err; + } + + if(!rpz_apply_cfg_elements(r, p)) + goto err; return r; err: if(r) { @@ -571,6 +598,32 @@ err: return NULL; } +int +rpz_config(struct rpz* r, struct config_auth* p) +{ + /* If the zonefile changes, it is read later, after which + * rpz_clear and rpz_finish_config is called. */ + + /* free taglist, if any */ + if(r->taglist) { + free(r->taglist); + r->taglist = NULL; + r->taglistlen = 0; + } + + /* free logname, if any */ + if(r->log_name) { + free(r->log_name); + r->log_name = NULL; + } + + delete_cname_override(r); + + if(!rpz_apply_cfg_elements(r, p)) + return 0; + return 1; +} + /** * Remove RPZ zone name from dname * Copy dname to newdname, without the originlen number of trailing bytes @@ -1191,16 +1244,20 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1 /** Find entry for RR type in the list of rrsets for the clientip. */ static struct local_rrset* rpz_find_synthesized_rrset(uint16_t qtype, - struct clientip_synthesized_rr* data) + struct clientip_synthesized_rr* data, int alias_ok) { - struct local_rrset* cursor = data->data; + struct local_rrset* cursor = data->data, *cname = NULL; while( cursor != NULL) { struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; if(htons(qtype) == packed_rrset->type) { return cursor; } + if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok) + cname = cursor; cursor = cursor->next; } + if(alias_ok) + return cname; return NULL; } @@ -1386,7 +1443,7 @@ static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node, struct local_rrset* rrset; struct packed_rrset_data* d; size_t index; - rrset = rpz_find_synthesized_rrset(rr_type, node); + rrset = rpz_find_synthesized_rrset(rr_type, node, 0); if(rrset == NULL) return 0; /* type not found, ignore */ d = (struct packed_rrset_data*)rrset->rrset->entry.data; @@ -1789,7 +1846,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, } /* check query type / rr type */ - rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); + rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; @@ -1823,6 +1880,28 @@ nodata: rrset_count, rcode, rsoa); } +/** Apply the cname override action, during worker request callback. + * false on failure. */ +static int +rpz_apply_cname_override_action(struct rpz* r, + struct query_info* qinfo, struct regional* temp) +{ + if(!r) + return 0; + qinfo->local_alias = regional_alloc_zero(temp, + sizeof(struct local_rrset)); + if(qinfo->local_alias == NULL) + return 0; /* out of memory */ + qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp); + if(qinfo->local_alias->rrset == NULL) { + qinfo->local_alias = NULL; + return 0; /* out of memory */ + } + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + return 1; +} + /** add additional section SOA record to the reply. * Since this gets fed into the normal iterator answer creation, it * gets minimal-responses applied to it, that can remove the additional SOA @@ -1933,6 +2012,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } + msg->qinfo = *qi; new_reply_info = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, 1, /* qd */ @@ -1975,40 +2055,42 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs static inline struct dns_msg* rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data, struct auth_zone* az) + struct query_info* qi, struct clientip_synthesized_rr* data, + struct auth_zone* az) { - struct query_info* qi = &ms->qinfo; struct local_rrset* rrset; - rrset = rpz_find_synthesized_rrset(qi->qtype, data); + rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* copy'n'paste from localzone.c */ static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { - struct local_rrset* p; + struct local_rrset* p, *cname = NULL; type = htons(type); for(p = data->rrsets; p; p = p->next) { if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; + cname = p; } + if(alias_ok) + return cname; return NULL; } /* based on localzone.c:local_data_answer() */ static inline struct dns_msg* rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) + struct query_info* qi, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -2029,13 +2111,13 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - rrset = local_data_find_type(ld, ms->qinfo.qtype, 1); + rrset = local_data_find_type(ld, qi->qtype, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* like local_data_answer for qname triggers after a cname */ @@ -2052,17 +2134,70 @@ rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, key.namelabs = dname_count_labels(qinfo->qname); ld = (struct local_data*)rbtree_search(&z->data, &key.node); if(ld == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + verbose(VERB_ALGO, "rpz: qname: name not found"); return NULL; } rrset = local_data_find_type(ld, qinfo->qtype, 1); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + verbose(VERB_ALGO, "rpz: qname: type not found"); return NULL; } return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); } +/** Synthesize a CNAME message for RPZ action override */ +static struct dns_msg* +rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo) +{ + struct dns_msg* msg = NULL; + struct reply_info* new_reply_info; + struct ub_packed_rrset_key* rp; + + msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return NULL; } + + msg->qinfo = *qinfo; + new_reply_info = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 1, /* an */ + 0, /* ns */ + 0, /* ar */ + 1, /* total */ + sec_status_insecure, + LDNS_EDE_NONE); + if(new_reply_info == NULL) { + log_err("out of memory"); + return NULL; + } + new_reply_info->authoritative = 1; + + rp = respip_copy_rrset(r->cname_override, ms->region); + if(rp == NULL) { + log_err("out of memory"); + return NULL; + } + rp->rk.dname = qinfo->qname; + rp->rk.dname_len = qinfo->qname_len; + /* this rrset is from the rpz data, or synthesized. + * It is not actually from the network, so we flag it with this + * flags as a fake RRset. If later the cache is used to look up + * rrsets, then the fake ones are not returned (if you look without + * the flag). For like CNAME lookups from the iterator or A, AAAA + * lookups for nameserver targets, it would use the without flag + * actual data. So that the actual network data and fake data + * are kept track of separately. */ + rp->rk.flags |= PACKED_RRSET_RPZ; + new_reply_info->rrsets[0] = rp; + + msg->rep = new_reply_info; + return msg; +} + static int rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, @@ -2072,17 +2207,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_data* ld = NULL; int ret = 0; if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { - qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(qinfo->local_alias == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override, - sizeof(*r->cname_override)); - if(qinfo->local_alias->rrset == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset->rk.dname = qinfo->qname; - qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + if(!rpz_apply_cname_override_action(r, qinfo, temp)) + return 0; if(r->log) { log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, qinfo, repinfo, NULL, r->log_name); @@ -2134,8 +2260,9 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* } static struct dns_msg* -rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, - struct clientip_synthesized_rr* raddr, struct auth_zone* az) +rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct clientip_synthesized_rr* raddr, + struct auth_zone* az) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -2148,16 +2275,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2166,17 +2293,20 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); @@ -2194,9 +2324,9 @@ done: } static struct dns_msg* -rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -2209,10 +2339,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2221,19 +2351,22 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: - verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); ret = NULL; } @@ -2324,7 +2457,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* /* the nsdname has precedence over the nsip triggers */ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass, &match); + is->qchase.qclass, &match); if(z != NULL) { lock_rw_unlock(&a->lock); break; @@ -2347,9 +2480,9 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* if(z) { lock_rw_unlock(&z->lock); } - return rpz_apply_nsip_trigger(ms, r, raddr, a); + return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); } - return rpz_apply_nsdname_trigger(ms, r, z, &match, a); + return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, @@ -2412,10 +2545,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, dname_str(is->qchase.qname, nm); dname_str(z->name, zn); if(strcmp(zn, nm) != 0) - verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s", zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); else - verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s", nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); } switch(localzone_type_to_rpz_action(lzt)) { @@ -2444,7 +2577,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, ms->rpz_passthru = 1; break; default: - verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } @@ -2472,8 +2605,21 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); + if(node != NULL && *r_out && + (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) { + client_action = (*r_out)->action_override; + } if(client_action == RPZ_PASSTHRU_ACTION) { + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); *passthru = 1; + ret = 0; + goto done; } if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { @@ -2488,14 +2634,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, edns, repinfo, buf, temp, *a_out); + ret = 1; + } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) { + if(!rpz_apply_cname_override_action(*r_out, qinfo, + temp)) { + ret = 0; + goto done; + } + ret = 0; } else { - if(*r_out && (*r_out)->log) - log_rpz_apply( - (node?"clientip":"qname"), - ((*z_out)?(*z_out)->name:NULL), - (node?&node->node:NULL), - client_action, qinfo, repinfo, NULL, - (*r_out)->log_name); local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); @@ -2503,8 +2650,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) == LDNS_RCODE_NXDOMAIN) LDNS_RA_CLR(sldns_buffer_begin(buf)); + ret = 1; } - ret = 1; + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); goto done; } ret = -1; diff --git a/services/rpz.h b/services/rpz.h index e6d8bf566e16..7f409087f772 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -226,6 +226,14 @@ int rpz_clear(struct rpz* r); struct rpz* rpz_create(struct config_auth* p); /** + * Change config on rpz, after reload. + * @param r: the rpz structure. + * @param p: the config that was read. + * @return false on failure. + */ +int rpz_config(struct rpz* r, struct config_auth* p); + +/** * String for RPZ action enum * @param a: RPZ action to get string for * @return: string for RPZ action diff --git a/services/view.h b/services/view.h index 17778100474b..12f7a64e7171 100644 --- a/services/view.h +++ b/services/view.h @@ -126,7 +126,8 @@ void view_delete(struct view* v); */ void views_print(struct views* v); -/* Find a view by name. +/** + * Find a view by name. * @param vs: views * @param name: name of the view we are looking for * @param write: 1 for obtaining write lock on found view, 0 for read lock |