165b390aaSDag-Erling Smørgrav /* 265b390aaSDag-Erling Smørgrav * respip/respip.c - filtering response IP module 365b390aaSDag-Erling Smørgrav */ 465b390aaSDag-Erling Smørgrav 565b390aaSDag-Erling Smørgrav /** 665b390aaSDag-Erling Smørgrav * \file 765b390aaSDag-Erling Smørgrav * 865b390aaSDag-Erling Smørgrav * This file contains a module that inspects a result of recursive resolution 965b390aaSDag-Erling Smørgrav * to see if any IP address record should trigger a special action. 1065b390aaSDag-Erling Smørgrav * If applicable these actions can modify the original response. 1165b390aaSDag-Erling Smørgrav */ 1265b390aaSDag-Erling Smørgrav #include "config.h" 1365b390aaSDag-Erling Smørgrav 1465b390aaSDag-Erling Smørgrav #include "services/localzone.h" 1565b390aaSDag-Erling Smørgrav #include "services/cache/dns.h" 1665b390aaSDag-Erling Smørgrav #include "sldns/str2wire.h" 1765b390aaSDag-Erling Smørgrav #include "util/config_file.h" 1865b390aaSDag-Erling Smørgrav #include "util/fptr_wlist.h" 1965b390aaSDag-Erling Smørgrav #include "util/module.h" 2065b390aaSDag-Erling Smørgrav #include "util/net_help.h" 2165b390aaSDag-Erling Smørgrav #include "util/regional.h" 2265b390aaSDag-Erling Smørgrav #include "util/data/msgreply.h" 2365b390aaSDag-Erling Smørgrav #include "util/storage/dnstree.h" 2465b390aaSDag-Erling Smørgrav #include "respip/respip.h" 2565b390aaSDag-Erling Smørgrav #include "services/view.h" 2665b390aaSDag-Erling Smørgrav #include "sldns/rrdef.h" 2765b390aaSDag-Erling Smørgrav 2865b390aaSDag-Erling Smørgrav /** 2965b390aaSDag-Erling Smørgrav * Conceptual set of IP addresses for response AAAA or A records that should 3065b390aaSDag-Erling Smørgrav * trigger special actions. 3165b390aaSDag-Erling Smørgrav */ 3265b390aaSDag-Erling Smørgrav struct respip_set { 3365b390aaSDag-Erling Smørgrav struct regional* region; 3465b390aaSDag-Erling Smørgrav struct rbtree_type ip_tree; 3565b390aaSDag-Erling Smørgrav char* const* tagname; /* shallow copy of tag names, for logging */ 3665b390aaSDag-Erling Smørgrav int num_tags; /* number of tagname entries */ 3765b390aaSDag-Erling Smørgrav }; 3865b390aaSDag-Erling Smørgrav 3965b390aaSDag-Erling Smørgrav /** An address span with response control information */ 4065b390aaSDag-Erling Smørgrav struct resp_addr { 4165b390aaSDag-Erling Smørgrav /** node in address tree */ 4265b390aaSDag-Erling Smørgrav struct addr_tree_node node; 4365b390aaSDag-Erling Smørgrav /** tag bitlist */ 4465b390aaSDag-Erling Smørgrav uint8_t* taglist; 4565b390aaSDag-Erling Smørgrav /** length of the taglist (in bytes) */ 4665b390aaSDag-Erling Smørgrav size_t taglen; 4765b390aaSDag-Erling Smørgrav /** action for this address span */ 4865b390aaSDag-Erling Smørgrav enum respip_action action; 4965b390aaSDag-Erling Smørgrav /** "local data" for this node */ 5065b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* data; 5165b390aaSDag-Erling Smørgrav }; 5265b390aaSDag-Erling Smørgrav 5365b390aaSDag-Erling Smørgrav /** Subset of resp_addr.node, used for inform-variant logging */ 5465b390aaSDag-Erling Smørgrav struct respip_addr_info { 5565b390aaSDag-Erling Smørgrav struct sockaddr_storage addr; 5665b390aaSDag-Erling Smørgrav socklen_t addrlen; 5765b390aaSDag-Erling Smørgrav int net; 5865b390aaSDag-Erling Smørgrav }; 5965b390aaSDag-Erling Smørgrav 6065b390aaSDag-Erling Smørgrav /** Query state regarding the response-ip module. */ 6165b390aaSDag-Erling Smørgrav enum respip_state { 6265b390aaSDag-Erling Smørgrav /** 6365b390aaSDag-Erling Smørgrav * The general state. Unless CNAME chasing takes place, all processing 6465b390aaSDag-Erling Smørgrav * is completed in this state without any other asynchronous event. 6565b390aaSDag-Erling Smørgrav */ 6665b390aaSDag-Erling Smørgrav RESPIP_INIT = 0, 6765b390aaSDag-Erling Smørgrav 6865b390aaSDag-Erling Smørgrav /** 6965b390aaSDag-Erling Smørgrav * A subquery for CNAME chasing is completed. 7065b390aaSDag-Erling Smørgrav */ 7165b390aaSDag-Erling Smørgrav RESPIP_SUBQUERY_FINISHED 7265b390aaSDag-Erling Smørgrav }; 7365b390aaSDag-Erling Smørgrav 7465b390aaSDag-Erling Smørgrav /** Per query state for the response-ip module. */ 7565b390aaSDag-Erling Smørgrav struct respip_qstate { 7665b390aaSDag-Erling Smørgrav enum respip_state state; 7765b390aaSDag-Erling Smørgrav }; 7865b390aaSDag-Erling Smørgrav 7965b390aaSDag-Erling Smørgrav struct respip_set* 8065b390aaSDag-Erling Smørgrav respip_set_create(void) 8165b390aaSDag-Erling Smørgrav { 8265b390aaSDag-Erling Smørgrav struct respip_set* set = calloc(1, sizeof(*set)); 8365b390aaSDag-Erling Smørgrav if(!set) 8465b390aaSDag-Erling Smørgrav return NULL; 8565b390aaSDag-Erling Smørgrav set->region = regional_create(); 8665b390aaSDag-Erling Smørgrav if(!set->region) { 8765b390aaSDag-Erling Smørgrav free(set); 8865b390aaSDag-Erling Smørgrav return NULL; 8965b390aaSDag-Erling Smørgrav } 9065b390aaSDag-Erling Smørgrav addr_tree_init(&set->ip_tree); 9165b390aaSDag-Erling Smørgrav return set; 9265b390aaSDag-Erling Smørgrav } 9365b390aaSDag-Erling Smørgrav 9465b390aaSDag-Erling Smørgrav void 9565b390aaSDag-Erling Smørgrav respip_set_delete(struct respip_set* set) 9665b390aaSDag-Erling Smørgrav { 9765b390aaSDag-Erling Smørgrav if(!set) 9865b390aaSDag-Erling Smørgrav return; 9965b390aaSDag-Erling Smørgrav regional_destroy(set->region); 10065b390aaSDag-Erling Smørgrav free(set); 10165b390aaSDag-Erling Smørgrav } 10265b390aaSDag-Erling Smørgrav 10365b390aaSDag-Erling Smørgrav struct rbtree_type* 10465b390aaSDag-Erling Smørgrav respip_set_get_tree(struct respip_set* set) 10565b390aaSDag-Erling Smørgrav { 10665b390aaSDag-Erling Smørgrav if(!set) 10765b390aaSDag-Erling Smørgrav return NULL; 10865b390aaSDag-Erling Smørgrav return &set->ip_tree; 10965b390aaSDag-Erling Smørgrav } 11065b390aaSDag-Erling Smørgrav 11165b390aaSDag-Erling Smørgrav /** returns the node in the address tree for the specified netblock string; 11265b390aaSDag-Erling Smørgrav * non-existent node will be created if 'create' is true */ 11365b390aaSDag-Erling Smørgrav static struct resp_addr* 11465b390aaSDag-Erling Smørgrav respip_find_or_create(struct respip_set* set, const char* ipstr, int create) 11565b390aaSDag-Erling Smørgrav { 11665b390aaSDag-Erling Smørgrav struct resp_addr* node; 11765b390aaSDag-Erling Smørgrav struct sockaddr_storage addr; 11865b390aaSDag-Erling Smørgrav int net; 11965b390aaSDag-Erling Smørgrav socklen_t addrlen; 12065b390aaSDag-Erling Smørgrav 12165b390aaSDag-Erling Smørgrav if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) { 12265b390aaSDag-Erling Smørgrav log_err("cannot parse netblock: '%s'", ipstr); 12365b390aaSDag-Erling Smørgrav return NULL; 12465b390aaSDag-Erling Smørgrav } 12565b390aaSDag-Erling Smørgrav node = (struct resp_addr*)addr_tree_find(&set->ip_tree, &addr, addrlen, net); 12665b390aaSDag-Erling Smørgrav if(!node && create) { 12765b390aaSDag-Erling Smørgrav node = regional_alloc_zero(set->region, sizeof(*node)); 12865b390aaSDag-Erling Smørgrav if(!node) { 12965b390aaSDag-Erling Smørgrav log_err("out of memory"); 13065b390aaSDag-Erling Smørgrav return NULL; 13165b390aaSDag-Erling Smørgrav } 13265b390aaSDag-Erling Smørgrav node->action = respip_none; 13365b390aaSDag-Erling Smørgrav if(!addr_tree_insert(&set->ip_tree, &node->node, &addr, 13465b390aaSDag-Erling Smørgrav addrlen, net)) { 13565b390aaSDag-Erling Smørgrav /* We know we didn't find it, so this should be 13665b390aaSDag-Erling Smørgrav * impossible. */ 13765b390aaSDag-Erling Smørgrav log_warn("unexpected: duplicate address: %s", ipstr); 13865b390aaSDag-Erling Smørgrav } 13965b390aaSDag-Erling Smørgrav } 14065b390aaSDag-Erling Smørgrav return node; 14165b390aaSDag-Erling Smørgrav } 14265b390aaSDag-Erling Smørgrav 14365b390aaSDag-Erling Smørgrav static int 14465b390aaSDag-Erling Smørgrav respip_tag_cfg(struct respip_set* set, const char* ipstr, 14565b390aaSDag-Erling Smørgrav const uint8_t* taglist, size_t taglen) 14665b390aaSDag-Erling Smørgrav { 14765b390aaSDag-Erling Smørgrav struct resp_addr* node; 14865b390aaSDag-Erling Smørgrav 14965b390aaSDag-Erling Smørgrav if(!(node=respip_find_or_create(set, ipstr, 1))) 15065b390aaSDag-Erling Smørgrav return 0; 15165b390aaSDag-Erling Smørgrav if(node->taglist) { 15265b390aaSDag-Erling Smørgrav log_warn("duplicate response-address-tag for '%s', overridden.", 15365b390aaSDag-Erling Smørgrav ipstr); 15465b390aaSDag-Erling Smørgrav } 15565b390aaSDag-Erling Smørgrav node->taglist = regional_alloc_init(set->region, taglist, taglen); 15665b390aaSDag-Erling Smørgrav if(!node->taglist) { 15765b390aaSDag-Erling Smørgrav log_err("out of memory"); 15865b390aaSDag-Erling Smørgrav return 0; 15965b390aaSDag-Erling Smørgrav } 16065b390aaSDag-Erling Smørgrav node->taglen = taglen; 16165b390aaSDag-Erling Smørgrav return 1; 16265b390aaSDag-Erling Smørgrav } 16365b390aaSDag-Erling Smørgrav 16465b390aaSDag-Erling Smørgrav /** set action for the node specified by the netblock string */ 16565b390aaSDag-Erling Smørgrav static int 16665b390aaSDag-Erling Smørgrav respip_action_cfg(struct respip_set* set, const char* ipstr, 16765b390aaSDag-Erling Smørgrav const char* actnstr) 16865b390aaSDag-Erling Smørgrav { 16965b390aaSDag-Erling Smørgrav struct resp_addr* node; 17065b390aaSDag-Erling Smørgrav enum respip_action action; 17165b390aaSDag-Erling Smørgrav 17265b390aaSDag-Erling Smørgrav if(!(node=respip_find_or_create(set, ipstr, 1))) 17365b390aaSDag-Erling Smørgrav return 0; 17465b390aaSDag-Erling Smørgrav if(node->action != respip_none) { 175a755b6f6SDag-Erling Smørgrav verbose(VERB_QUERY, "duplicate response-ip action for '%s', overridden.", 17665b390aaSDag-Erling Smørgrav ipstr); 17765b390aaSDag-Erling Smørgrav } 17865b390aaSDag-Erling Smørgrav if(strcmp(actnstr, "deny") == 0) 17965b390aaSDag-Erling Smørgrav action = respip_deny; 18065b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "redirect") == 0) 18165b390aaSDag-Erling Smørgrav action = respip_redirect; 18265b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "inform") == 0) 18365b390aaSDag-Erling Smørgrav action = respip_inform; 18465b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "inform_deny") == 0) 18565b390aaSDag-Erling Smørgrav action = respip_inform_deny; 186*e86b9096SDag-Erling Smørgrav else if(strcmp(actnstr, "inform_redirect") == 0) 187*e86b9096SDag-Erling Smørgrav action = respip_inform_redirect; 18865b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "always_transparent") == 0) 18965b390aaSDag-Erling Smørgrav action = respip_always_transparent; 19065b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "always_refuse") == 0) 19165b390aaSDag-Erling Smørgrav action = respip_always_refuse; 19265b390aaSDag-Erling Smørgrav else if(strcmp(actnstr, "always_nxdomain") == 0) 19365b390aaSDag-Erling Smørgrav action = respip_always_nxdomain; 19465b390aaSDag-Erling Smørgrav else { 19565b390aaSDag-Erling Smørgrav log_err("unknown response-ip action %s", actnstr); 19665b390aaSDag-Erling Smørgrav return 0; 19765b390aaSDag-Erling Smørgrav } 19865b390aaSDag-Erling Smørgrav node->action = action; 19965b390aaSDag-Erling Smørgrav return 1; 20065b390aaSDag-Erling Smørgrav } 20165b390aaSDag-Erling Smørgrav 20265b390aaSDag-Erling Smørgrav /** allocate and initialize an rrset structure; this function is based 20365b390aaSDag-Erling Smørgrav * on new_local_rrset() from the localzone.c module */ 20465b390aaSDag-Erling Smørgrav static struct ub_packed_rrset_key* 20565b390aaSDag-Erling Smørgrav new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass) 20665b390aaSDag-Erling Smørgrav { 20765b390aaSDag-Erling Smørgrav struct packed_rrset_data* pd; 20865b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* rrset = regional_alloc_zero( 20965b390aaSDag-Erling Smørgrav region, sizeof(*rrset)); 21065b390aaSDag-Erling Smørgrav if(!rrset) { 21165b390aaSDag-Erling Smørgrav log_err("out of memory"); 21265b390aaSDag-Erling Smørgrav return NULL; 21365b390aaSDag-Erling Smørgrav } 21465b390aaSDag-Erling Smørgrav rrset->entry.key = rrset; 21565b390aaSDag-Erling Smørgrav pd = regional_alloc_zero(region, sizeof(*pd)); 21665b390aaSDag-Erling Smørgrav if(!pd) { 21765b390aaSDag-Erling Smørgrav log_err("out of memory"); 21865b390aaSDag-Erling Smørgrav return NULL; 21965b390aaSDag-Erling Smørgrav } 22065b390aaSDag-Erling Smørgrav pd->trust = rrset_trust_prim_noglue; 22165b390aaSDag-Erling Smørgrav pd->security = sec_status_insecure; 22265b390aaSDag-Erling Smørgrav rrset->entry.data = pd; 22365b390aaSDag-Erling Smørgrav rrset->rk.dname = regional_alloc_zero(region, 1); 22465b390aaSDag-Erling Smørgrav if(!rrset->rk.dname) { 22565b390aaSDag-Erling Smørgrav log_err("out of memory"); 22665b390aaSDag-Erling Smørgrav return NULL; 22765b390aaSDag-Erling Smørgrav } 22865b390aaSDag-Erling Smørgrav rrset->rk.dname_len = 1; 22965b390aaSDag-Erling Smørgrav rrset->rk.type = htons(rrtype); 23065b390aaSDag-Erling Smørgrav rrset->rk.rrset_class = htons(rrclass); 23165b390aaSDag-Erling Smørgrav return rrset; 23265b390aaSDag-Erling Smørgrav } 23365b390aaSDag-Erling Smørgrav 23465b390aaSDag-Erling Smørgrav /** enter local data as resource records into a response-ip node */ 23565b390aaSDag-Erling Smørgrav static int 23665b390aaSDag-Erling Smørgrav respip_enter_rr(struct regional* region, struct resp_addr* raddr, 23765b390aaSDag-Erling Smørgrav const char* rrstr, const char* netblock) 23865b390aaSDag-Erling Smørgrav { 23965b390aaSDag-Erling Smørgrav uint8_t* nm; 24065b390aaSDag-Erling Smørgrav uint16_t rrtype = 0, rrclass = 0; 24165b390aaSDag-Erling Smørgrav time_t ttl = 0; 24265b390aaSDag-Erling Smørgrav uint8_t rr[LDNS_RR_BUF_SIZE]; 24365b390aaSDag-Erling Smørgrav uint8_t* rdata = NULL; 24465b390aaSDag-Erling Smørgrav size_t rdata_len = 0; 24565b390aaSDag-Erling Smørgrav char buf[65536]; 24665b390aaSDag-Erling Smørgrav char bufshort[64]; 24765b390aaSDag-Erling Smørgrav struct packed_rrset_data* pd; 24865b390aaSDag-Erling Smørgrav struct sockaddr* sa; 24965b390aaSDag-Erling Smørgrav int ret; 250*e86b9096SDag-Erling Smørgrav if(raddr->action != respip_redirect 251*e86b9096SDag-Erling Smørgrav && raddr->action != respip_inform_redirect) { 25265b390aaSDag-Erling Smørgrav log_err("cannot parse response-ip-data %s: response-ip " 25365b390aaSDag-Erling Smørgrav "action for %s is not redirect", rrstr, netblock); 25465b390aaSDag-Erling Smørgrav return 0; 25565b390aaSDag-Erling Smørgrav } 25665b390aaSDag-Erling Smørgrav ret = snprintf(buf, sizeof(buf), ". %s", rrstr); 25765b390aaSDag-Erling Smørgrav if(ret < 0 || ret >= (int)sizeof(buf)) { 25865b390aaSDag-Erling Smørgrav strlcpy(bufshort, rrstr, sizeof(bufshort)); 25965b390aaSDag-Erling Smørgrav log_err("bad response-ip-data: %s...", bufshort); 26065b390aaSDag-Erling Smørgrav return 0; 26165b390aaSDag-Erling Smørgrav } 26265b390aaSDag-Erling Smørgrav if(!rrstr_get_rr_content(buf, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), 26365b390aaSDag-Erling Smørgrav &rdata, &rdata_len)) { 26465b390aaSDag-Erling Smørgrav log_err("bad response-ip-data: %s", rrstr); 26565b390aaSDag-Erling Smørgrav return 0; 26665b390aaSDag-Erling Smørgrav } 267c7f4d7adSDag-Erling Smørgrav free(nm); 26865b390aaSDag-Erling Smørgrav sa = (struct sockaddr*)&raddr->node.addr; 26965b390aaSDag-Erling Smørgrav if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) { 27065b390aaSDag-Erling Smørgrav log_err("CNAME response-ip data (%s) can not co-exist with other " 27165b390aaSDag-Erling Smørgrav "response-ip data for netblock %s", rrstr, netblock); 27265b390aaSDag-Erling Smørgrav return 0; 27365b390aaSDag-Erling Smørgrav } else if (raddr->data && 27465b390aaSDag-Erling Smørgrav raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) { 27565b390aaSDag-Erling Smørgrav log_err("response-ip data (%s) can not be added; CNAME response-ip " 27665b390aaSDag-Erling Smørgrav "data already in place for netblock %s", rrstr, netblock); 27765b390aaSDag-Erling Smørgrav return 0; 27865b390aaSDag-Erling Smørgrav } else if((rrtype != LDNS_RR_TYPE_CNAME) && 27965b390aaSDag-Erling Smørgrav ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) || 28065b390aaSDag-Erling Smørgrav (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) { 28165b390aaSDag-Erling Smørgrav log_err("response-ip data %s record type does not correspond " 28265b390aaSDag-Erling Smørgrav "to netblock %s address family", rrstr, netblock); 28365b390aaSDag-Erling Smørgrav return 0; 28465b390aaSDag-Erling Smørgrav } 28565b390aaSDag-Erling Smørgrav 28665b390aaSDag-Erling Smørgrav if(!raddr->data) { 28765b390aaSDag-Erling Smørgrav raddr->data = new_rrset(region, rrtype, rrclass); 28865b390aaSDag-Erling Smørgrav if(!raddr->data) 28965b390aaSDag-Erling Smørgrav return 0; 29065b390aaSDag-Erling Smørgrav } 29165b390aaSDag-Erling Smørgrav pd = raddr->data->entry.data; 29265b390aaSDag-Erling Smørgrav return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr); 29365b390aaSDag-Erling Smørgrav } 29465b390aaSDag-Erling Smørgrav 29565b390aaSDag-Erling Smørgrav static int 29665b390aaSDag-Erling Smørgrav respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr) 29765b390aaSDag-Erling Smørgrav { 29865b390aaSDag-Erling Smørgrav struct resp_addr* node; 29965b390aaSDag-Erling Smørgrav 30065b390aaSDag-Erling Smørgrav node=respip_find_or_create(set, ipstr, 0); 30165b390aaSDag-Erling Smørgrav if(!node || node->action == respip_none) { 30265b390aaSDag-Erling Smørgrav log_err("cannot parse response-ip-data %s: " 30365b390aaSDag-Erling Smørgrav "response-ip node for %s not found", rrstr, ipstr); 30465b390aaSDag-Erling Smørgrav return 0; 30565b390aaSDag-Erling Smørgrav } 30665b390aaSDag-Erling Smørgrav return respip_enter_rr(set->region, node, rrstr, ipstr); 30765b390aaSDag-Erling Smørgrav } 30865b390aaSDag-Erling Smørgrav 30965b390aaSDag-Erling Smørgrav static int 31065b390aaSDag-Erling Smørgrav respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags, 31165b390aaSDag-Erling Smørgrav struct config_strbytelist* respip_tags, 31265b390aaSDag-Erling Smørgrav struct config_str2list* respip_actions, 31365b390aaSDag-Erling Smørgrav struct config_str2list* respip_data) 31465b390aaSDag-Erling Smørgrav { 31565b390aaSDag-Erling Smørgrav struct config_strbytelist* p; 31665b390aaSDag-Erling Smørgrav struct config_str2list* pa; 31765b390aaSDag-Erling Smørgrav struct config_str2list* pd; 31865b390aaSDag-Erling Smørgrav 31965b390aaSDag-Erling Smørgrav set->tagname = tagname; 32065b390aaSDag-Erling Smørgrav set->num_tags = num_tags; 32165b390aaSDag-Erling Smørgrav 32265b390aaSDag-Erling Smørgrav p = respip_tags; 32365b390aaSDag-Erling Smørgrav while(p) { 32465b390aaSDag-Erling Smørgrav struct config_strbytelist* np = p->next; 32565b390aaSDag-Erling Smørgrav 32665b390aaSDag-Erling Smørgrav log_assert(p->str && p->str2); 32765b390aaSDag-Erling Smørgrav if(!respip_tag_cfg(set, p->str, p->str2, p->str2len)) { 32865b390aaSDag-Erling Smørgrav config_del_strbytelist(p); 32965b390aaSDag-Erling Smørgrav return 0; 33065b390aaSDag-Erling Smørgrav } 33165b390aaSDag-Erling Smørgrav free(p->str); 33265b390aaSDag-Erling Smørgrav free(p->str2); 33365b390aaSDag-Erling Smørgrav free(p); 33465b390aaSDag-Erling Smørgrav p = np; 33565b390aaSDag-Erling Smørgrav } 33665b390aaSDag-Erling Smørgrav 33765b390aaSDag-Erling Smørgrav pa = respip_actions; 33865b390aaSDag-Erling Smørgrav while(pa) { 33965b390aaSDag-Erling Smørgrav struct config_str2list* np = pa->next; 34065b390aaSDag-Erling Smørgrav log_assert(pa->str && pa->str2); 34165b390aaSDag-Erling Smørgrav if(!respip_action_cfg(set, pa->str, pa->str2)) { 34265b390aaSDag-Erling Smørgrav config_deldblstrlist(pa); 34365b390aaSDag-Erling Smørgrav return 0; 34465b390aaSDag-Erling Smørgrav } 34565b390aaSDag-Erling Smørgrav free(pa->str); 34665b390aaSDag-Erling Smørgrav free(pa->str2); 34765b390aaSDag-Erling Smørgrav free(pa); 34865b390aaSDag-Erling Smørgrav pa = np; 34965b390aaSDag-Erling Smørgrav } 35065b390aaSDag-Erling Smørgrav 35165b390aaSDag-Erling Smørgrav pd = respip_data; 35265b390aaSDag-Erling Smørgrav while(pd) { 35365b390aaSDag-Erling Smørgrav struct config_str2list* np = pd->next; 35465b390aaSDag-Erling Smørgrav log_assert(pd->str && pd->str2); 35565b390aaSDag-Erling Smørgrav if(!respip_data_cfg(set, pd->str, pd->str2)) { 35665b390aaSDag-Erling Smørgrav config_deldblstrlist(pd); 35765b390aaSDag-Erling Smørgrav return 0; 35865b390aaSDag-Erling Smørgrav } 35965b390aaSDag-Erling Smørgrav free(pd->str); 36065b390aaSDag-Erling Smørgrav free(pd->str2); 36165b390aaSDag-Erling Smørgrav free(pd); 36265b390aaSDag-Erling Smørgrav pd = np; 36365b390aaSDag-Erling Smørgrav } 36465b390aaSDag-Erling Smørgrav 36565b390aaSDag-Erling Smørgrav return 1; 36665b390aaSDag-Erling Smørgrav } 36765b390aaSDag-Erling Smørgrav 36865b390aaSDag-Erling Smørgrav int 36965b390aaSDag-Erling Smørgrav respip_global_apply_cfg(struct respip_set* set, struct config_file* cfg) 37065b390aaSDag-Erling Smørgrav { 37165b390aaSDag-Erling Smørgrav int ret = respip_set_apply_cfg(set, cfg->tagname, cfg->num_tags, 37265b390aaSDag-Erling Smørgrav cfg->respip_tags, cfg->respip_actions, cfg->respip_data); 37365b390aaSDag-Erling Smørgrav cfg->respip_data = NULL; 37465b390aaSDag-Erling Smørgrav cfg->respip_actions = NULL; 37565b390aaSDag-Erling Smørgrav cfg->respip_tags = NULL; 37665b390aaSDag-Erling Smørgrav return ret; 37765b390aaSDag-Erling Smørgrav } 37865b390aaSDag-Erling Smørgrav 37965b390aaSDag-Erling Smørgrav /** Iterate through raw view data and apply the view-specific respip 38065b390aaSDag-Erling Smørgrav * configuration; at this point we should have already seen all the views, 38165b390aaSDag-Erling Smørgrav * so if any of the views that respip data refer to does not exist, that's 38265b390aaSDag-Erling Smørgrav * an error. This additional iteration through view configuration data 38365b390aaSDag-Erling Smørgrav * is expected to not have significant performance impact (or rather, its 38465b390aaSDag-Erling Smørgrav * performance impact is not expected to be prohibitive in the configuration 38565b390aaSDag-Erling Smørgrav * processing phase). 38665b390aaSDag-Erling Smørgrav */ 38765b390aaSDag-Erling Smørgrav int 38865b390aaSDag-Erling Smørgrav respip_views_apply_cfg(struct views* vs, struct config_file* cfg, 38965b390aaSDag-Erling Smørgrav int* have_view_respip_cfg) 39065b390aaSDag-Erling Smørgrav { 39165b390aaSDag-Erling Smørgrav struct config_view* cv; 39265b390aaSDag-Erling Smørgrav struct view* v; 39365b390aaSDag-Erling Smørgrav int ret; 39465b390aaSDag-Erling Smørgrav 39565b390aaSDag-Erling Smørgrav for(cv = cfg->views; cv; cv = cv->next) { 39665b390aaSDag-Erling Smørgrav 39765b390aaSDag-Erling Smørgrav /** if no respip config for this view then there's 39865b390aaSDag-Erling Smørgrav * nothing to do; note that even though respip data must go 39965b390aaSDag-Erling Smørgrav * with respip action, we're checking for both here because 40065b390aaSDag-Erling Smørgrav * we want to catch the case where the respip action is missing 40165b390aaSDag-Erling Smørgrav * while the data is present */ 40265b390aaSDag-Erling Smørgrav if(!cv->respip_actions && !cv->respip_data) 40365b390aaSDag-Erling Smørgrav continue; 40465b390aaSDag-Erling Smørgrav 40565b390aaSDag-Erling Smørgrav if(!(v = views_find_view(vs, cv->name, 1))) { 40665b390aaSDag-Erling Smørgrav log_err("view '%s' unexpectedly missing", cv->name); 40765b390aaSDag-Erling Smørgrav return 0; 40865b390aaSDag-Erling Smørgrav } 40965b390aaSDag-Erling Smørgrav if(!v->respip_set) { 41065b390aaSDag-Erling Smørgrav v->respip_set = respip_set_create(); 41165b390aaSDag-Erling Smørgrav if(!v->respip_set) { 41265b390aaSDag-Erling Smørgrav log_err("out of memory"); 41365b390aaSDag-Erling Smørgrav lock_rw_unlock(&v->lock); 41465b390aaSDag-Erling Smørgrav return 0; 41565b390aaSDag-Erling Smørgrav } 41665b390aaSDag-Erling Smørgrav } 41765b390aaSDag-Erling Smørgrav ret = respip_set_apply_cfg(v->respip_set, NULL, 0, NULL, 41865b390aaSDag-Erling Smørgrav cv->respip_actions, cv->respip_data); 41965b390aaSDag-Erling Smørgrav lock_rw_unlock(&v->lock); 42065b390aaSDag-Erling Smørgrav if(!ret) { 42165b390aaSDag-Erling Smørgrav log_err("Error while applying respip configuration " 42265b390aaSDag-Erling Smørgrav "for view '%s'", cv->name); 42365b390aaSDag-Erling Smørgrav return 0; 42465b390aaSDag-Erling Smørgrav } 42565b390aaSDag-Erling Smørgrav *have_view_respip_cfg = (*have_view_respip_cfg || 42665b390aaSDag-Erling Smørgrav v->respip_set->ip_tree.count); 42765b390aaSDag-Erling Smørgrav cv->respip_actions = NULL; 42865b390aaSDag-Erling Smørgrav cv->respip_data = NULL; 42965b390aaSDag-Erling Smørgrav } 43065b390aaSDag-Erling Smørgrav return 1; 43165b390aaSDag-Erling Smørgrav } 43265b390aaSDag-Erling Smørgrav 43365b390aaSDag-Erling Smørgrav /** 43465b390aaSDag-Erling Smørgrav * make a deep copy of 'key' in 'region'. 43565b390aaSDag-Erling Smørgrav * This is largely derived from packed_rrset_copy_region() and 43665b390aaSDag-Erling Smørgrav * packed_rrset_ptr_fixup(), but differs in the following points: 43765b390aaSDag-Erling Smørgrav * 43865b390aaSDag-Erling Smørgrav * - It doesn't assume all data in 'key' are in a contiguous memory region. 43965b390aaSDag-Erling Smørgrav * Although that would be the case in most cases, 'key' can be passed from 44065b390aaSDag-Erling Smørgrav * a lower-level module and it might not build the rrset to meet the 44165b390aaSDag-Erling Smørgrav * assumption. In fact, an rrset specified as response-ip-data or generated 44265b390aaSDag-Erling Smørgrav * in local_data_find_tag_datas() breaks the assumption. So it would be 44365b390aaSDag-Erling Smørgrav * safer not to naively rely on the assumption. On the other hand, this 44465b390aaSDag-Erling Smørgrav * function ensures the copied rrset data are in a contiguous region so 44565b390aaSDag-Erling Smørgrav * that it won't cause a disruption even if an upper layer module naively 44665b390aaSDag-Erling Smørgrav * assumes the memory layout. 44765b390aaSDag-Erling Smørgrav * - It doesn't copy RRSIGs (if any) in 'key'. The rrset will be used in 44865b390aaSDag-Erling Smørgrav * a reply that was already faked, so it doesn't make much sense to provide 44965b390aaSDag-Erling Smørgrav * partial sigs even if they are valid themselves. 45065b390aaSDag-Erling Smørgrav * - It doesn't adjust TTLs as it basically has to be a verbatim copy of 'key' 45165b390aaSDag-Erling Smørgrav * just allocated in 'region' (the assumption is necessary TTL adjustment 45265b390aaSDag-Erling Smørgrav * has been already done in 'key'). 45365b390aaSDag-Erling Smørgrav * 45465b390aaSDag-Erling Smørgrav * This function returns the copied rrset key on success, and NULL on memory 45565b390aaSDag-Erling Smørgrav * allocation failure. 45665b390aaSDag-Erling Smørgrav */ 45765b390aaSDag-Erling Smørgrav static struct ub_packed_rrset_key* 45865b390aaSDag-Erling Smørgrav copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) 45965b390aaSDag-Erling Smørgrav { 46065b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* ck = regional_alloc(region, 46165b390aaSDag-Erling Smørgrav sizeof(struct ub_packed_rrset_key)); 46265b390aaSDag-Erling Smørgrav struct packed_rrset_data* d; 46365b390aaSDag-Erling Smørgrav struct packed_rrset_data* data = key->entry.data; 46465b390aaSDag-Erling Smørgrav size_t dsize, i; 46565b390aaSDag-Erling Smørgrav uint8_t* nextrdata; 46665b390aaSDag-Erling Smørgrav 46765b390aaSDag-Erling Smørgrav /* derived from packed_rrset_copy_region(), but don't use 46865b390aaSDag-Erling Smørgrav * packed_rrset_sizeof() and do exclude RRSIGs */ 46965b390aaSDag-Erling Smørgrav if(!ck) 47065b390aaSDag-Erling Smørgrav return NULL; 47165b390aaSDag-Erling Smørgrav ck->id = key->id; 47265b390aaSDag-Erling Smørgrav memset(&ck->entry, 0, sizeof(ck->entry)); 47365b390aaSDag-Erling Smørgrav ck->entry.hash = key->entry.hash; 47465b390aaSDag-Erling Smørgrav ck->entry.key = ck; 47565b390aaSDag-Erling Smørgrav ck->rk = key->rk; 47665b390aaSDag-Erling Smørgrav ck->rk.dname = regional_alloc_init(region, key->rk.dname, 47765b390aaSDag-Erling Smørgrav key->rk.dname_len); 47865b390aaSDag-Erling Smørgrav if(!ck->rk.dname) 47965b390aaSDag-Erling Smørgrav return NULL; 48065b390aaSDag-Erling Smørgrav 48165b390aaSDag-Erling Smørgrav dsize = sizeof(struct packed_rrset_data) + data->count * 48265b390aaSDag-Erling Smørgrav (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)); 48365b390aaSDag-Erling Smørgrav for(i=0; i<data->count; i++) 48465b390aaSDag-Erling Smørgrav dsize += data->rr_len[i]; 48565b390aaSDag-Erling Smørgrav d = regional_alloc(region, dsize); 48665b390aaSDag-Erling Smørgrav if(!d) 48765b390aaSDag-Erling Smørgrav return NULL; 48865b390aaSDag-Erling Smørgrav *d = *data; 48965b390aaSDag-Erling Smørgrav d->rrsig_count = 0; 49065b390aaSDag-Erling Smørgrav ck->entry.data = d; 49165b390aaSDag-Erling Smørgrav 49265b390aaSDag-Erling Smørgrav /* derived from packed_rrset_ptr_fixup() with copying the data */ 49365b390aaSDag-Erling Smørgrav d->rr_len = (size_t*)((uint8_t*)d + sizeof(struct packed_rrset_data)); 49465b390aaSDag-Erling Smørgrav d->rr_data = (uint8_t**)&(d->rr_len[d->count]); 49565b390aaSDag-Erling Smørgrav d->rr_ttl = (time_t*)&(d->rr_data[d->count]); 49665b390aaSDag-Erling Smørgrav nextrdata = (uint8_t*)&(d->rr_ttl[d->count]); 49765b390aaSDag-Erling Smørgrav for(i=0; i<d->count; i++) { 49865b390aaSDag-Erling Smørgrav d->rr_len[i] = data->rr_len[i]; 49965b390aaSDag-Erling Smørgrav d->rr_ttl[i] = data->rr_ttl[i]; 50065b390aaSDag-Erling Smørgrav d->rr_data[i] = nextrdata; 50165b390aaSDag-Erling Smørgrav memcpy(d->rr_data[i], data->rr_data[i], data->rr_len[i]); 50265b390aaSDag-Erling Smørgrav nextrdata += d->rr_len[i]; 50365b390aaSDag-Erling Smørgrav } 50465b390aaSDag-Erling Smørgrav 50565b390aaSDag-Erling Smørgrav return ck; 50665b390aaSDag-Erling Smørgrav } 50765b390aaSDag-Erling Smørgrav 50865b390aaSDag-Erling Smørgrav int 50965b390aaSDag-Erling Smørgrav respip_init(struct module_env* env, int id) 51065b390aaSDag-Erling Smørgrav { 51165b390aaSDag-Erling Smørgrav (void)env; 51265b390aaSDag-Erling Smørgrav (void)id; 51365b390aaSDag-Erling Smørgrav return 1; 51465b390aaSDag-Erling Smørgrav } 51565b390aaSDag-Erling Smørgrav 51665b390aaSDag-Erling Smørgrav void 51765b390aaSDag-Erling Smørgrav respip_deinit(struct module_env* env, int id) 51865b390aaSDag-Erling Smørgrav { 51965b390aaSDag-Erling Smørgrav (void)env; 52065b390aaSDag-Erling Smørgrav (void)id; 52165b390aaSDag-Erling Smørgrav } 52265b390aaSDag-Erling Smørgrav 52365b390aaSDag-Erling Smørgrav /** Convert a packed AAAA or A RRset to sockaddr. */ 52465b390aaSDag-Erling Smørgrav static int 52565b390aaSDag-Erling Smørgrav rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i, 52665b390aaSDag-Erling Smørgrav struct sockaddr_storage* ss, socklen_t* addrlenp) 52765b390aaSDag-Erling Smørgrav { 52865b390aaSDag-Erling Smørgrav /* unbound can accept and cache odd-length AAAA/A records, so we have 52965b390aaSDag-Erling Smørgrav * to validate the length. */ 53065b390aaSDag-Erling Smørgrav if(rtype == LDNS_RR_TYPE_A && rd->rr_len[i] == 6) { 53165b390aaSDag-Erling Smørgrav struct sockaddr_in* sa4 = (struct sockaddr_in*)ss; 53265b390aaSDag-Erling Smørgrav 53365b390aaSDag-Erling Smørgrav memset(sa4, 0, sizeof(*sa4)); 53465b390aaSDag-Erling Smørgrav sa4->sin_family = AF_INET; 53565b390aaSDag-Erling Smørgrav memcpy(&sa4->sin_addr, rd->rr_data[i] + 2, 53665b390aaSDag-Erling Smørgrav sizeof(sa4->sin_addr)); 53765b390aaSDag-Erling Smørgrav *addrlenp = sizeof(*sa4); 53865b390aaSDag-Erling Smørgrav return 1; 53965b390aaSDag-Erling Smørgrav } else if(rtype == LDNS_RR_TYPE_AAAA && rd->rr_len[i] == 18) { 54065b390aaSDag-Erling Smørgrav struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ss; 54165b390aaSDag-Erling Smørgrav 54265b390aaSDag-Erling Smørgrav memset(sa6, 0, sizeof(*sa6)); 54365b390aaSDag-Erling Smørgrav sa6->sin6_family = AF_INET6; 54465b390aaSDag-Erling Smørgrav memcpy(&sa6->sin6_addr, rd->rr_data[i] + 2, 54565b390aaSDag-Erling Smørgrav sizeof(sa6->sin6_addr)); 54665b390aaSDag-Erling Smørgrav *addrlenp = sizeof(*sa6); 54765b390aaSDag-Erling Smørgrav return 1; 54865b390aaSDag-Erling Smørgrav } 54965b390aaSDag-Erling Smørgrav return 0; 55065b390aaSDag-Erling Smørgrav } 55165b390aaSDag-Erling Smørgrav 55265b390aaSDag-Erling Smørgrav /** 55365b390aaSDag-Erling Smørgrav * Search the given 'iptree' for response address information that matches 55465b390aaSDag-Erling Smørgrav * any of the IP addresses in an AAAA or A in the answer section of the 55565b390aaSDag-Erling Smørgrav * response (stored in 'rep'). If found, a pointer to the matched resp_addr 55665b390aaSDag-Erling Smørgrav * structure will be returned, and '*rrset_id' is set to the index in 55765b390aaSDag-Erling Smørgrav * rep->rrsets for the RRset that contains the matching IP address record 55865b390aaSDag-Erling Smørgrav * (the index is normally 0, but can be larger than that if this is a CNAME 55965b390aaSDag-Erling Smørgrav * chain or type-ANY response). 56065b390aaSDag-Erling Smørgrav */ 56165b390aaSDag-Erling Smørgrav static const struct resp_addr* 56265b390aaSDag-Erling Smørgrav respip_addr_lookup(const struct reply_info *rep, struct rbtree_type* iptree, 56365b390aaSDag-Erling Smørgrav size_t* rrset_id) 56465b390aaSDag-Erling Smørgrav { 56565b390aaSDag-Erling Smørgrav size_t i; 56665b390aaSDag-Erling Smørgrav struct resp_addr* ra; 56765b390aaSDag-Erling Smørgrav struct sockaddr_storage ss; 56865b390aaSDag-Erling Smørgrav socklen_t addrlen; 56965b390aaSDag-Erling Smørgrav 57065b390aaSDag-Erling Smørgrav for(i=0; i<rep->an_numrrsets; i++) { 57165b390aaSDag-Erling Smørgrav size_t j; 57265b390aaSDag-Erling Smørgrav const struct packed_rrset_data* rd; 57365b390aaSDag-Erling Smørgrav uint16_t rtype = ntohs(rep->rrsets[i]->rk.type); 57465b390aaSDag-Erling Smørgrav 57565b390aaSDag-Erling Smørgrav if(rtype != LDNS_RR_TYPE_A && rtype != LDNS_RR_TYPE_AAAA) 57665b390aaSDag-Erling Smørgrav continue; 57765b390aaSDag-Erling Smørgrav rd = rep->rrsets[i]->entry.data; 57865b390aaSDag-Erling Smørgrav for(j = 0; j < rd->count; j++) { 57965b390aaSDag-Erling Smørgrav if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen)) 58065b390aaSDag-Erling Smørgrav continue; 58165b390aaSDag-Erling Smørgrav ra = (struct resp_addr*)addr_tree_lookup(iptree, &ss, 58265b390aaSDag-Erling Smørgrav addrlen); 58365b390aaSDag-Erling Smørgrav if(ra) { 58465b390aaSDag-Erling Smørgrav *rrset_id = i; 58565b390aaSDag-Erling Smørgrav return ra; 58665b390aaSDag-Erling Smørgrav } 58765b390aaSDag-Erling Smørgrav } 58865b390aaSDag-Erling Smørgrav } 58965b390aaSDag-Erling Smørgrav 59065b390aaSDag-Erling Smørgrav return NULL; 59165b390aaSDag-Erling Smørgrav } 59265b390aaSDag-Erling Smørgrav 59365b390aaSDag-Erling Smørgrav /* 59465b390aaSDag-Erling Smørgrav * Create a new reply_info based on 'rep'. The new info is based on 59565b390aaSDag-Erling Smørgrav * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets' 59665b390aaSDag-Erling Smørgrav * RRsets in the answer section. These answer rrsets are copied to the 59765b390aaSDag-Erling Smørgrav * new info, up to 'copy_rrsets' rrsets (which must not be larger than 59865b390aaSDag-Erling Smørgrav * 'an_numrrsets'). If an_numrrsets > copy_rrsets, the remaining rrsets array 59965b390aaSDag-Erling Smørgrav * entries will be kept empty so the caller can fill them later. When rrsets 60065b390aaSDag-Erling Smørgrav * are copied, they are shallow copied. The caller must ensure that the 60165b390aaSDag-Erling Smørgrav * copied rrsets are valid throughout its lifetime and must provide appropriate 60265b390aaSDag-Erling Smørgrav * mutex if it can be shared by multiple threads. 60365b390aaSDag-Erling Smørgrav */ 60465b390aaSDag-Erling Smørgrav static struct reply_info * 60565b390aaSDag-Erling Smørgrav make_new_reply_info(const struct reply_info* rep, struct regional* region, 60665b390aaSDag-Erling Smørgrav size_t an_numrrsets, size_t copy_rrsets) 60765b390aaSDag-Erling Smørgrav { 60865b390aaSDag-Erling Smørgrav struct reply_info* new_rep; 60965b390aaSDag-Erling Smørgrav size_t i; 61065b390aaSDag-Erling Smørgrav 61165b390aaSDag-Erling Smørgrav /* create a base struct. we specify 'insecure' security status as 61265b390aaSDag-Erling Smørgrav * the modified response won't be DNSSEC-valid. In our faked response 61365b390aaSDag-Erling Smørgrav * the authority and additional sections will be empty (except possible 61465b390aaSDag-Erling Smørgrav * EDNS0 OPT RR in the additional section appended on sending it out), 61565b390aaSDag-Erling Smørgrav * so the total number of RRsets is an_numrrsets. */ 61665b390aaSDag-Erling Smørgrav new_rep = construct_reply_info_base(region, rep->flags, 6174c75e3aaSDag-Erling Smørgrav rep->qdcount, rep->ttl, rep->prefetch_ttl, 6184c75e3aaSDag-Erling Smørgrav rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, 6194c75e3aaSDag-Erling Smørgrav sec_status_insecure); 62065b390aaSDag-Erling Smørgrav if(!new_rep) 62165b390aaSDag-Erling Smørgrav return NULL; 62265b390aaSDag-Erling Smørgrav if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) 62365b390aaSDag-Erling Smørgrav return NULL; 62465b390aaSDag-Erling Smørgrav for(i=0; i<copy_rrsets; i++) 62565b390aaSDag-Erling Smørgrav new_rep->rrsets[i] = rep->rrsets[i]; 62665b390aaSDag-Erling Smørgrav 62765b390aaSDag-Erling Smørgrav return new_rep; 62865b390aaSDag-Erling Smørgrav } 62965b390aaSDag-Erling Smørgrav 63065b390aaSDag-Erling Smørgrav /** 63165b390aaSDag-Erling Smørgrav * See if response-ip or tag data should override the original answer rrset 63265b390aaSDag-Erling Smørgrav * (which is rep->rrsets[rrset_id]) and if so override it. 63365b390aaSDag-Erling Smørgrav * This is (mostly) equivalent to localzone.c:local_data_answer() but for 63465b390aaSDag-Erling Smørgrav * response-ip actions. 63565b390aaSDag-Erling Smørgrav * Note that this function distinguishes error conditions from "success but 63665b390aaSDag-Erling Smørgrav * not overridden". This is because we want to avoid accidentally applying 63765b390aaSDag-Erling Smørgrav * the "no data" action in case of error. 63865b390aaSDag-Erling Smørgrav * @param raddr: address span that requires an action 63965b390aaSDag-Erling Smørgrav * @param action: action to apply 64065b390aaSDag-Erling Smørgrav * @param qtype: original query type 64165b390aaSDag-Erling Smørgrav * @param rep: original reply message 64265b390aaSDag-Erling Smørgrav * @param rrset_id: the rrset ID in 'rep' to which the action should apply 64365b390aaSDag-Erling Smørgrav * @param new_repp: see respip_rewrite_reply 64465b390aaSDag-Erling Smørgrav * @param tag: if >= 0 the tag ID used to determine the action and data 64565b390aaSDag-Erling Smørgrav * @param tag_datas: data corresponding to 'tag'. 64665b390aaSDag-Erling Smørgrav * @param tag_datas_size: size of 'tag_datas' 64765b390aaSDag-Erling Smørgrav * @param tagname: array of tag names, used for logging 64865b390aaSDag-Erling Smørgrav * @param num_tags: size of 'tagname', used for logging 64965b390aaSDag-Erling Smørgrav * @param redirect_rrsetp: ptr to redirect record 65065b390aaSDag-Erling Smørgrav * @param region: region for building new reply 65165b390aaSDag-Erling Smørgrav * @return 1 if overridden, 0 if not overridden, -1 on error. 65265b390aaSDag-Erling Smørgrav */ 65365b390aaSDag-Erling Smørgrav static int 65465b390aaSDag-Erling Smørgrav respip_data_answer(const struct resp_addr* raddr, enum respip_action action, 65565b390aaSDag-Erling Smørgrav uint16_t qtype, const struct reply_info* rep, 65665b390aaSDag-Erling Smørgrav size_t rrset_id, struct reply_info** new_repp, int tag, 65765b390aaSDag-Erling Smørgrav struct config_strlist** tag_datas, size_t tag_datas_size, 65865b390aaSDag-Erling Smørgrav char* const* tagname, int num_tags, 65965b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region) 66065b390aaSDag-Erling Smørgrav { 66165b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* rp = raddr->data; 66265b390aaSDag-Erling Smørgrav struct reply_info* new_rep; 66365b390aaSDag-Erling Smørgrav *redirect_rrsetp = NULL; 66465b390aaSDag-Erling Smørgrav 66565b390aaSDag-Erling Smørgrav if(action == respip_redirect && tag != -1 && 66665b390aaSDag-Erling Smørgrav (size_t)tag<tag_datas_size && tag_datas[tag]) { 66765b390aaSDag-Erling Smørgrav struct query_info dataqinfo; 66865b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key r; 66965b390aaSDag-Erling Smørgrav 67065b390aaSDag-Erling Smørgrav /* Extract parameters of the original answer rrset that can be 67165b390aaSDag-Erling Smørgrav * rewritten below, in the form of query_info. Note that these 67265b390aaSDag-Erling Smørgrav * can be different from the info of the original query if the 67365b390aaSDag-Erling Smørgrav * rrset is a CNAME target.*/ 67465b390aaSDag-Erling Smørgrav memset(&dataqinfo, 0, sizeof(dataqinfo)); 67565b390aaSDag-Erling Smørgrav dataqinfo.qname = rep->rrsets[rrset_id]->rk.dname; 67665b390aaSDag-Erling Smørgrav dataqinfo.qname_len = rep->rrsets[rrset_id]->rk.dname_len; 67765b390aaSDag-Erling Smørgrav dataqinfo.qtype = ntohs(rep->rrsets[rrset_id]->rk.type); 67865b390aaSDag-Erling Smørgrav dataqinfo.qclass = ntohs(rep->rrsets[rrset_id]->rk.rrset_class); 67965b390aaSDag-Erling Smørgrav 68065b390aaSDag-Erling Smørgrav memset(&r, 0, sizeof(r)); 68165b390aaSDag-Erling Smørgrav if(local_data_find_tag_datas(&dataqinfo, tag_datas[tag], &r, 68265b390aaSDag-Erling Smørgrav region)) { 68365b390aaSDag-Erling Smørgrav verbose(VERB_ALGO, 68465b390aaSDag-Erling Smørgrav "response-ip redirect with tag data [%d] %s", 68565b390aaSDag-Erling Smørgrav tag, (tag<num_tags?tagname[tag]:"null")); 68665b390aaSDag-Erling Smørgrav /* use copy_rrset() to 'normalize' memory layout */ 68765b390aaSDag-Erling Smørgrav rp = copy_rrset(&r, region); 68865b390aaSDag-Erling Smørgrav if(!rp) 68965b390aaSDag-Erling Smørgrav return -1; 69065b390aaSDag-Erling Smørgrav } 69165b390aaSDag-Erling Smørgrav } 69265b390aaSDag-Erling Smørgrav if(!rp) 69365b390aaSDag-Erling Smørgrav return 0; 69465b390aaSDag-Erling Smørgrav 69565b390aaSDag-Erling Smørgrav /* If we are using response-ip-data, we need to make a copy of rrset 69665b390aaSDag-Erling Smørgrav * to replace the rrset's dname. Note that, unlike local data, we 69765b390aaSDag-Erling Smørgrav * rename the dname for other actions than redirect. This is because 69865b390aaSDag-Erling Smørgrav * response-ip-data isn't associated to any specific name. */ 69965b390aaSDag-Erling Smørgrav if(rp == raddr->data) { 70065b390aaSDag-Erling Smørgrav rp = copy_rrset(rp, region); 70165b390aaSDag-Erling Smørgrav if(!rp) 70265b390aaSDag-Erling Smørgrav return -1; 70365b390aaSDag-Erling Smørgrav rp->rk.dname = rep->rrsets[rrset_id]->rk.dname; 70465b390aaSDag-Erling Smørgrav rp->rk.dname_len = rep->rrsets[rrset_id]->rk.dname_len; 70565b390aaSDag-Erling Smørgrav } 70665b390aaSDag-Erling Smørgrav 70765b390aaSDag-Erling Smørgrav /* Build a new reply with redirect rrset. We keep any preceding CNAMEs 70865b390aaSDag-Erling Smørgrav * and replace the address rrset that triggers the action. If it's 70965b390aaSDag-Erling Smørgrav * type ANY query, however, no other answer records should be kept 71065b390aaSDag-Erling Smørgrav * (note that it can't be a CNAME chain in this case due to 71165b390aaSDag-Erling Smørgrav * sanitizing). */ 71265b390aaSDag-Erling Smørgrav if(qtype == LDNS_RR_TYPE_ANY) 71365b390aaSDag-Erling Smørgrav rrset_id = 0; 71465b390aaSDag-Erling Smørgrav new_rep = make_new_reply_info(rep, region, rrset_id + 1, rrset_id); 71565b390aaSDag-Erling Smørgrav if(!new_rep) 71665b390aaSDag-Erling Smørgrav return -1; 71765b390aaSDag-Erling Smørgrav rp->rk.flags |= PACKED_RRSET_FIXEDTTL; /* avoid adjusting TTL */ 71865b390aaSDag-Erling Smørgrav new_rep->rrsets[rrset_id] = rp; 71965b390aaSDag-Erling Smørgrav 72065b390aaSDag-Erling Smørgrav *redirect_rrsetp = rp; 72165b390aaSDag-Erling Smørgrav *new_repp = new_rep; 72265b390aaSDag-Erling Smørgrav return 1; 72365b390aaSDag-Erling Smørgrav } 72465b390aaSDag-Erling Smørgrav 72565b390aaSDag-Erling Smørgrav /** 72665b390aaSDag-Erling Smørgrav * apply response ip action in case where no action data is provided. 72765b390aaSDag-Erling Smørgrav * this is similar to localzone.c:lz_zone_answer() but simplified due to 72865b390aaSDag-Erling Smørgrav * the characteristics of response ip: 72965b390aaSDag-Erling Smørgrav * - 'deny' variants will be handled at the caller side 73065b390aaSDag-Erling Smørgrav * - no specific processing for 'transparent' variants: unlike local zones, 73165b390aaSDag-Erling Smørgrav * there is no such a case of 'no data but name existing'. so all variants 73265b390aaSDag-Erling Smørgrav * just mean 'transparent if no data'. 73365b390aaSDag-Erling Smørgrav * @param qtype: query type 73465b390aaSDag-Erling Smørgrav * @param action: found action 73565b390aaSDag-Erling Smørgrav * @param rep: 73665b390aaSDag-Erling Smørgrav * @param new_repp 73765b390aaSDag-Erling Smørgrav * @param rrset_id 73865b390aaSDag-Erling Smørgrav * @param region: region for building new reply 73965b390aaSDag-Erling Smørgrav * @return 1 on success, 0 on error. 74065b390aaSDag-Erling Smørgrav */ 74165b390aaSDag-Erling Smørgrav static int 74265b390aaSDag-Erling Smørgrav respip_nodata_answer(uint16_t qtype, enum respip_action action, 74365b390aaSDag-Erling Smørgrav const struct reply_info *rep, size_t rrset_id, 74465b390aaSDag-Erling Smørgrav struct reply_info** new_repp, struct regional* region) 74565b390aaSDag-Erling Smørgrav { 74665b390aaSDag-Erling Smørgrav struct reply_info* new_rep; 74765b390aaSDag-Erling Smørgrav 74865b390aaSDag-Erling Smørgrav if(action == respip_refuse || action == respip_always_refuse) { 74965b390aaSDag-Erling Smørgrav new_rep = make_new_reply_info(rep, region, 0, 0); 75065b390aaSDag-Erling Smørgrav if(!new_rep) 75165b390aaSDag-Erling Smørgrav return 0; 75265b390aaSDag-Erling Smørgrav FLAGS_SET_RCODE(new_rep->flags, LDNS_RCODE_REFUSED); 75365b390aaSDag-Erling Smørgrav *new_repp = new_rep; 75465b390aaSDag-Erling Smørgrav return 1; 75565b390aaSDag-Erling Smørgrav } else if(action == respip_static || action == respip_redirect || 756*e86b9096SDag-Erling Smørgrav action == respip_always_nxdomain || 757*e86b9096SDag-Erling Smørgrav action == respip_inform_redirect) { 75865b390aaSDag-Erling Smørgrav /* Since we don't know about other types of the owner name, 75965b390aaSDag-Erling Smørgrav * we generally return NOERROR/NODATA unless an NXDOMAIN action 76065b390aaSDag-Erling Smørgrav * is explicitly specified. */ 76165b390aaSDag-Erling Smørgrav int rcode = (action == respip_always_nxdomain)? 76265b390aaSDag-Erling Smørgrav LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; 76365b390aaSDag-Erling Smørgrav 76465b390aaSDag-Erling Smørgrav /* We should empty the answer section except for any preceding 76565b390aaSDag-Erling Smørgrav * CNAMEs (in that case rrset_id > 0). Type-ANY case is 76665b390aaSDag-Erling Smørgrav * special as noted in respip_data_answer(). */ 76765b390aaSDag-Erling Smørgrav if(qtype == LDNS_RR_TYPE_ANY) 76865b390aaSDag-Erling Smørgrav rrset_id = 0; 76965b390aaSDag-Erling Smørgrav new_rep = make_new_reply_info(rep, region, rrset_id, rrset_id); 77065b390aaSDag-Erling Smørgrav if(!new_rep) 77165b390aaSDag-Erling Smørgrav return 0; 77265b390aaSDag-Erling Smørgrav FLAGS_SET_RCODE(new_rep->flags, rcode); 77365b390aaSDag-Erling Smørgrav *new_repp = new_rep; 77465b390aaSDag-Erling Smørgrav return 1; 77565b390aaSDag-Erling Smørgrav } 77665b390aaSDag-Erling Smørgrav 77765b390aaSDag-Erling Smørgrav return 1; 77865b390aaSDag-Erling Smørgrav } 77965b390aaSDag-Erling Smørgrav 78065b390aaSDag-Erling Smørgrav /** Populate action info structure with the results of response-ip action 78165b390aaSDag-Erling Smørgrav * processing, iff as the result of response-ip processing we are actually 78265b390aaSDag-Erling Smørgrav * taking some action. Only action is set if action_only is true. 78365b390aaSDag-Erling Smørgrav * Returns true on success, false on failure. 78465b390aaSDag-Erling Smørgrav */ 78565b390aaSDag-Erling Smørgrav static int 78665b390aaSDag-Erling Smørgrav populate_action_info(struct respip_action_info* actinfo, 78765b390aaSDag-Erling Smørgrav enum respip_action action, const struct resp_addr* raddr, 78865b390aaSDag-Erling Smørgrav const struct ub_packed_rrset_key* ATTR_UNUSED(rrset), 78965b390aaSDag-Erling Smørgrav int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset), 79065b390aaSDag-Erling Smørgrav int ATTR_UNUSED(action_only), struct regional* region) 79165b390aaSDag-Erling Smørgrav { 79265b390aaSDag-Erling Smørgrav if(action == respip_none || !raddr) 79365b390aaSDag-Erling Smørgrav return 1; 79465b390aaSDag-Erling Smørgrav actinfo->action = action; 79565b390aaSDag-Erling Smørgrav 79665b390aaSDag-Erling Smørgrav /* for inform variants, make a copy of the matched address block for 79765b390aaSDag-Erling Smørgrav * later logging. We make a copy to proactively avoid disruption if 79865b390aaSDag-Erling Smørgrav * and when we allow a dynamic update to the respip tree. */ 79965b390aaSDag-Erling Smørgrav if(action == respip_inform || action == respip_inform_deny) { 80065b390aaSDag-Erling Smørgrav struct respip_addr_info* a = 80165b390aaSDag-Erling Smørgrav regional_alloc_zero(region, sizeof(*a)); 80265b390aaSDag-Erling Smørgrav if(!a) { 80365b390aaSDag-Erling Smørgrav log_err("out of memory"); 80465b390aaSDag-Erling Smørgrav return 0; 80565b390aaSDag-Erling Smørgrav } 80665b390aaSDag-Erling Smørgrav a->addr = raddr->node.addr; 80765b390aaSDag-Erling Smørgrav a->addrlen = raddr->node.addrlen; 80865b390aaSDag-Erling Smørgrav a->net = raddr->node.net; 80965b390aaSDag-Erling Smørgrav actinfo->addrinfo = a; 81065b390aaSDag-Erling Smørgrav } 81165b390aaSDag-Erling Smørgrav 81265b390aaSDag-Erling Smørgrav return 1; 81365b390aaSDag-Erling Smørgrav } 81465b390aaSDag-Erling Smørgrav 81565b390aaSDag-Erling Smørgrav int 81665b390aaSDag-Erling Smørgrav respip_rewrite_reply(const struct query_info* qinfo, 81765b390aaSDag-Erling Smørgrav const struct respip_client_info* cinfo, const struct reply_info* rep, 81865b390aaSDag-Erling Smørgrav struct reply_info** new_repp, struct respip_action_info* actinfo, 81965b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key** alias_rrset, int search_only, 82065b390aaSDag-Erling Smørgrav struct regional* region) 82165b390aaSDag-Erling Smørgrav { 82265b390aaSDag-Erling Smørgrav const uint8_t* ctaglist; 82365b390aaSDag-Erling Smørgrav size_t ctaglen; 82465b390aaSDag-Erling Smørgrav const uint8_t* tag_actions; 82565b390aaSDag-Erling Smørgrav size_t tag_actions_size; 82665b390aaSDag-Erling Smørgrav struct config_strlist** tag_datas; 82765b390aaSDag-Erling Smørgrav size_t tag_datas_size; 82865b390aaSDag-Erling Smørgrav struct view* view = NULL; 82965b390aaSDag-Erling Smørgrav struct respip_set* ipset = NULL; 83065b390aaSDag-Erling Smørgrav size_t rrset_id = 0; 83165b390aaSDag-Erling Smørgrav enum respip_action action = respip_none; 83265b390aaSDag-Erling Smørgrav int tag = -1; 83365b390aaSDag-Erling Smørgrav const struct resp_addr* raddr = NULL; 83465b390aaSDag-Erling Smørgrav int ret = 1; 83565b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* redirect_rrset = NULL; 83665b390aaSDag-Erling Smørgrav 83765b390aaSDag-Erling Smørgrav if(!cinfo) 83865b390aaSDag-Erling Smørgrav goto done; 83965b390aaSDag-Erling Smørgrav ctaglist = cinfo->taglist; 84065b390aaSDag-Erling Smørgrav ctaglen = cinfo->taglen; 84165b390aaSDag-Erling Smørgrav tag_actions = cinfo->tag_actions; 84265b390aaSDag-Erling Smørgrav tag_actions_size = cinfo->tag_actions_size; 84365b390aaSDag-Erling Smørgrav tag_datas = cinfo->tag_datas; 84465b390aaSDag-Erling Smørgrav tag_datas_size = cinfo->tag_datas_size; 84565b390aaSDag-Erling Smørgrav view = cinfo->view; 84665b390aaSDag-Erling Smørgrav ipset = cinfo->respip_set; 84765b390aaSDag-Erling Smørgrav 84865b390aaSDag-Erling Smørgrav /** Try to use response-ip config from the view first; use 84965b390aaSDag-Erling Smørgrav * global response-ip config if we don't have the view or we don't 85065b390aaSDag-Erling Smørgrav * have the matching per-view config (and the view allows the use 85165b390aaSDag-Erling Smørgrav * of global data in this case). 85265b390aaSDag-Erling Smørgrav * Note that we lock the view even if we only use view members that 85365b390aaSDag-Erling Smørgrav * currently don't change after creation. This is for safety for 85465b390aaSDag-Erling Smørgrav * future possible changes as the view documentation seems to expect 85565b390aaSDag-Erling Smørgrav * any of its member can change in the view's lifetime. 85665b390aaSDag-Erling Smørgrav * Note also that we assume 'view' is valid in this function, which 85765b390aaSDag-Erling Smørgrav * should be safe (see unbound bug #1191) */ 85865b390aaSDag-Erling Smørgrav if(view) { 85965b390aaSDag-Erling Smørgrav lock_rw_rdlock(&view->lock); 86065b390aaSDag-Erling Smørgrav if(view->respip_set) { 86165b390aaSDag-Erling Smørgrav if((raddr = respip_addr_lookup(rep, 86265b390aaSDag-Erling Smørgrav &view->respip_set->ip_tree, &rrset_id))) { 86365b390aaSDag-Erling Smørgrav /** for per-view respip directives the action 86465b390aaSDag-Erling Smørgrav * can only be direct (i.e. not tag-based) */ 86565b390aaSDag-Erling Smørgrav action = raddr->action; 86665b390aaSDag-Erling Smørgrav } 86765b390aaSDag-Erling Smørgrav } 86865b390aaSDag-Erling Smørgrav if(!raddr && !view->isfirst) 86965b390aaSDag-Erling Smørgrav goto done; 87065b390aaSDag-Erling Smørgrav } 87165b390aaSDag-Erling Smørgrav if(!raddr && ipset && (raddr = respip_addr_lookup(rep, &ipset->ip_tree, 87265b390aaSDag-Erling Smørgrav &rrset_id))) { 87365b390aaSDag-Erling Smørgrav action = (enum respip_action)local_data_find_tag_action( 87465b390aaSDag-Erling Smørgrav raddr->taglist, raddr->taglen, ctaglist, ctaglen, 87565b390aaSDag-Erling Smørgrav tag_actions, tag_actions_size, 87665b390aaSDag-Erling Smørgrav (enum localzone_type)raddr->action, &tag, 87765b390aaSDag-Erling Smørgrav ipset->tagname, ipset->num_tags); 87865b390aaSDag-Erling Smørgrav } 87965b390aaSDag-Erling Smørgrav if(raddr && !search_only) { 88065b390aaSDag-Erling Smørgrav int result = 0; 88165b390aaSDag-Erling Smørgrav 88265b390aaSDag-Erling Smørgrav /* first, see if we have response-ip or tag action for the 88365b390aaSDag-Erling Smørgrav * action except for 'always' variants. */ 88465b390aaSDag-Erling Smørgrav if(action != respip_always_refuse 88565b390aaSDag-Erling Smørgrav && action != respip_always_transparent 88665b390aaSDag-Erling Smørgrav && action != respip_always_nxdomain 88765b390aaSDag-Erling Smørgrav && (result = respip_data_answer(raddr, action, 88865b390aaSDag-Erling Smørgrav qinfo->qtype, rep, rrset_id, new_repp, tag, tag_datas, 88965b390aaSDag-Erling Smørgrav tag_datas_size, ipset->tagname, ipset->num_tags, 89065b390aaSDag-Erling Smørgrav &redirect_rrset, region)) < 0) { 89165b390aaSDag-Erling Smørgrav ret = 0; 89265b390aaSDag-Erling Smørgrav goto done; 89365b390aaSDag-Erling Smørgrav } 89465b390aaSDag-Erling Smørgrav 89565b390aaSDag-Erling Smørgrav /* if no action data applied, take action specific to the 89665b390aaSDag-Erling Smørgrav * action without data. */ 89765b390aaSDag-Erling Smørgrav if(!result && !respip_nodata_answer(qinfo->qtype, action, rep, 89865b390aaSDag-Erling Smørgrav rrset_id, new_repp, region)) { 89965b390aaSDag-Erling Smørgrav ret = 0; 90065b390aaSDag-Erling Smørgrav goto done; 90165b390aaSDag-Erling Smørgrav } 90265b390aaSDag-Erling Smørgrav } 90365b390aaSDag-Erling Smørgrav done: 90465b390aaSDag-Erling Smørgrav if(view) { 90565b390aaSDag-Erling Smørgrav lock_rw_unlock(&view->lock); 90665b390aaSDag-Erling Smørgrav } 90765b390aaSDag-Erling Smørgrav if(ret) { 90865b390aaSDag-Erling Smørgrav /* If we're redirecting the original answer to a 90965b390aaSDag-Erling Smørgrav * CNAME, record the CNAME rrset so the caller can take 91065b390aaSDag-Erling Smørgrav * the appropriate action. Note that we don't check the 91165b390aaSDag-Erling Smørgrav * action type; it should normally be 'redirect', but it 91265b390aaSDag-Erling Smørgrav * can be of other type when a data-dependent tag action 91365b390aaSDag-Erling Smørgrav * uses redirect response-ip data. 91465b390aaSDag-Erling Smørgrav */ 91565b390aaSDag-Erling Smørgrav if(redirect_rrset && 91665b390aaSDag-Erling Smørgrav redirect_rrset->rk.type == ntohs(LDNS_RR_TYPE_CNAME) && 91765b390aaSDag-Erling Smørgrav qinfo->qtype != LDNS_RR_TYPE_ANY) 91865b390aaSDag-Erling Smørgrav *alias_rrset = redirect_rrset; 91965b390aaSDag-Erling Smørgrav /* on success, populate respip result structure */ 92065b390aaSDag-Erling Smørgrav ret = populate_action_info(actinfo, action, raddr, 92165b390aaSDag-Erling Smørgrav redirect_rrset, tag, ipset, search_only, region); 92265b390aaSDag-Erling Smørgrav } 92365b390aaSDag-Erling Smørgrav return ret; 92465b390aaSDag-Erling Smørgrav } 92565b390aaSDag-Erling Smørgrav 92665b390aaSDag-Erling Smørgrav static int 92765b390aaSDag-Erling Smørgrav generate_cname_request(struct module_qstate* qstate, 92865b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* alias_rrset) 92965b390aaSDag-Erling Smørgrav { 93065b390aaSDag-Erling Smørgrav struct module_qstate* subq = NULL; 93165b390aaSDag-Erling Smørgrav struct query_info subqi; 93265b390aaSDag-Erling Smørgrav 93365b390aaSDag-Erling Smørgrav memset(&subqi, 0, sizeof(subqi)); 93465b390aaSDag-Erling Smørgrav get_cname_target(alias_rrset, &subqi.qname, &subqi.qname_len); 93565b390aaSDag-Erling Smørgrav if(!subqi.qname) 93665b390aaSDag-Erling Smørgrav return 0; /* unexpected: not a valid CNAME RDATA */ 93765b390aaSDag-Erling Smørgrav subqi.qtype = qstate->qinfo.qtype; 93865b390aaSDag-Erling Smørgrav subqi.qclass = qstate->qinfo.qclass; 93965b390aaSDag-Erling Smørgrav fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); 94065b390aaSDag-Erling Smørgrav return (*qstate->env->attach_sub)(qstate, &subqi, BIT_RD, 0, 0, &subq); 94165b390aaSDag-Erling Smørgrav } 94265b390aaSDag-Erling Smørgrav 94365b390aaSDag-Erling Smørgrav void 94465b390aaSDag-Erling Smørgrav respip_operate(struct module_qstate* qstate, enum module_ev event, int id, 94565b390aaSDag-Erling Smørgrav struct outbound_entry* outbound) 94665b390aaSDag-Erling Smørgrav { 94765b390aaSDag-Erling Smørgrav struct respip_qstate* rq = (struct respip_qstate*)qstate->minfo[id]; 94865b390aaSDag-Erling Smørgrav 94965b390aaSDag-Erling Smørgrav log_query_info(VERB_QUERY, "respip operate: query", &qstate->qinfo); 95065b390aaSDag-Erling Smørgrav (void)outbound; 95165b390aaSDag-Erling Smørgrav 95265b390aaSDag-Erling Smørgrav if(event == module_event_new || event == module_event_pass) { 95365b390aaSDag-Erling Smørgrav if(!rq) { 95465b390aaSDag-Erling Smørgrav rq = regional_alloc_zero(qstate->region, sizeof(*rq)); 95565b390aaSDag-Erling Smørgrav if(!rq) 95665b390aaSDag-Erling Smørgrav goto servfail; 95765b390aaSDag-Erling Smørgrav rq->state = RESPIP_INIT; 95865b390aaSDag-Erling Smørgrav qstate->minfo[id] = rq; 95965b390aaSDag-Erling Smørgrav } 96065b390aaSDag-Erling Smørgrav if(rq->state == RESPIP_SUBQUERY_FINISHED) { 96165b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 96265b390aaSDag-Erling Smørgrav return; 96365b390aaSDag-Erling Smørgrav } 96465b390aaSDag-Erling Smørgrav verbose(VERB_ALGO, "respip: pass to next module"); 96565b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_wait_module; 96665b390aaSDag-Erling Smørgrav } else if(event == module_event_moddone) { 96765b390aaSDag-Erling Smørgrav /* If the reply may be subject to response-ip rewriting 96865b390aaSDag-Erling Smørgrav * according to the query type, check the actions. If a 96965b390aaSDag-Erling Smørgrav * rewrite is necessary, we'll replace the reply in qstate 97065b390aaSDag-Erling Smørgrav * with the new one. */ 97165b390aaSDag-Erling Smørgrav enum module_ext_state next_state = module_finished; 97265b390aaSDag-Erling Smørgrav 97365b390aaSDag-Erling Smørgrav if((qstate->qinfo.qtype == LDNS_RR_TYPE_A || 97465b390aaSDag-Erling Smørgrav qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA || 97565b390aaSDag-Erling Smørgrav qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) && 97665b390aaSDag-Erling Smørgrav qstate->return_msg && qstate->return_msg->rep) { 97765b390aaSDag-Erling Smørgrav struct respip_action_info actinfo = {respip_none, NULL}; 97865b390aaSDag-Erling Smørgrav struct reply_info* new_rep = qstate->return_msg->rep; 97965b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* alias_rrset = NULL; 98065b390aaSDag-Erling Smørgrav 98165b390aaSDag-Erling Smørgrav if(!respip_rewrite_reply(&qstate->qinfo, 98265b390aaSDag-Erling Smørgrav qstate->client_info, qstate->return_msg->rep, 98365b390aaSDag-Erling Smørgrav &new_rep, &actinfo, &alias_rrset, 0, 98465b390aaSDag-Erling Smørgrav qstate->region)) { 98565b390aaSDag-Erling Smørgrav goto servfail; 98665b390aaSDag-Erling Smørgrav } 98765b390aaSDag-Erling Smørgrav if(actinfo.action != respip_none) { 98865b390aaSDag-Erling Smørgrav /* save action info for logging on a 98965b390aaSDag-Erling Smørgrav * per-front-end-query basis */ 99065b390aaSDag-Erling Smørgrav if(!(qstate->respip_action_info = 99165b390aaSDag-Erling Smørgrav regional_alloc_init(qstate->region, 99265b390aaSDag-Erling Smørgrav &actinfo, sizeof(actinfo)))) 99365b390aaSDag-Erling Smørgrav { 99465b390aaSDag-Erling Smørgrav log_err("out of memory"); 99565b390aaSDag-Erling Smørgrav goto servfail; 99665b390aaSDag-Erling Smørgrav } 99765b390aaSDag-Erling Smørgrav } else { 99865b390aaSDag-Erling Smørgrav qstate->respip_action_info = NULL; 99965b390aaSDag-Erling Smørgrav } 100065b390aaSDag-Erling Smørgrav if (new_rep == qstate->return_msg->rep && 100165b390aaSDag-Erling Smørgrav (actinfo.action == respip_deny || 100265b390aaSDag-Erling Smørgrav actinfo.action == respip_inform_deny)) { 100365b390aaSDag-Erling Smørgrav /* for deny-variant actions (unless response-ip 100465b390aaSDag-Erling Smørgrav * data is applied), mark the query state so 100565b390aaSDag-Erling Smørgrav * the response will be dropped for all 100665b390aaSDag-Erling Smørgrav * clients. */ 100765b390aaSDag-Erling Smørgrav qstate->is_drop = 1; 100865b390aaSDag-Erling Smørgrav } else if(alias_rrset) { 100965b390aaSDag-Erling Smørgrav if(!generate_cname_request(qstate, alias_rrset)) 101065b390aaSDag-Erling Smørgrav goto servfail; 101165b390aaSDag-Erling Smørgrav next_state = module_wait_subquery; 101265b390aaSDag-Erling Smørgrav } 101365b390aaSDag-Erling Smørgrav qstate->return_msg->rep = new_rep; 101465b390aaSDag-Erling Smørgrav } 101565b390aaSDag-Erling Smørgrav qstate->ext_state[id] = next_state; 101665b390aaSDag-Erling Smørgrav } else 101765b390aaSDag-Erling Smørgrav qstate->ext_state[id] = module_finished; 101865b390aaSDag-Erling Smørgrav 101965b390aaSDag-Erling Smørgrav return; 102065b390aaSDag-Erling Smørgrav 102165b390aaSDag-Erling Smørgrav servfail: 102265b390aaSDag-Erling Smørgrav qstate->return_rcode = LDNS_RCODE_SERVFAIL; 102365b390aaSDag-Erling Smørgrav qstate->return_msg = NULL; 102465b390aaSDag-Erling Smørgrav } 102565b390aaSDag-Erling Smørgrav 102665b390aaSDag-Erling Smørgrav int 102765b390aaSDag-Erling Smørgrav respip_merge_cname(struct reply_info* base_rep, 102865b390aaSDag-Erling Smørgrav const struct query_info* qinfo, const struct reply_info* tgt_rep, 102965b390aaSDag-Erling Smørgrav const struct respip_client_info* cinfo, int must_validate, 103065b390aaSDag-Erling Smørgrav struct reply_info** new_repp, struct regional* region) 103165b390aaSDag-Erling Smørgrav { 103265b390aaSDag-Erling Smørgrav struct reply_info* new_rep; 103365b390aaSDag-Erling Smørgrav struct reply_info* tmp_rep = NULL; /* just a placeholder */ 103465b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */ 103565b390aaSDag-Erling Smørgrav uint16_t tgt_rcode; 103665b390aaSDag-Erling Smørgrav size_t i, j; 103765b390aaSDag-Erling Smørgrav struct respip_action_info actinfo = {respip_none, NULL}; 103865b390aaSDag-Erling Smørgrav 103965b390aaSDag-Erling Smørgrav /* If the query for the CNAME target would result in an unusual rcode, 104065b390aaSDag-Erling Smørgrav * we generally translate it as a failure for the base query 104165b390aaSDag-Erling Smørgrav * (which would then be translated into SERVFAIL). The only exception 104265b390aaSDag-Erling Smørgrav * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s). 104365b390aaSDag-Erling Smørgrav * The YXDOMAIN case would be rare but still possible (when 104465b390aaSDag-Erling Smørgrav * DNSSEC-validated DNAME has been cached but synthesizing CNAME 104565b390aaSDag-Erling Smørgrav * can't be generated due to length limitation) */ 104665b390aaSDag-Erling Smørgrav tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags); 104765b390aaSDag-Erling Smørgrav if((tgt_rcode != LDNS_RCODE_NOERROR && 104865b390aaSDag-Erling Smørgrav tgt_rcode != LDNS_RCODE_NXDOMAIN && 104965b390aaSDag-Erling Smørgrav tgt_rcode != LDNS_RCODE_YXDOMAIN) || 105065b390aaSDag-Erling Smørgrav (must_validate && tgt_rep->security <= sec_status_bogus)) { 105165b390aaSDag-Erling Smørgrav return 0; 105265b390aaSDag-Erling Smørgrav } 105365b390aaSDag-Erling Smørgrav 105465b390aaSDag-Erling Smørgrav /* see if the target reply would be subject to a response-ip action. */ 105565b390aaSDag-Erling Smørgrav if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, 105665b390aaSDag-Erling Smørgrav &alias_rrset, 1, region)) 105765b390aaSDag-Erling Smørgrav return 0; 105865b390aaSDag-Erling Smørgrav if(actinfo.action != respip_none) { 105965b390aaSDag-Erling Smørgrav log_info("CNAME target of redirect response-ip action would " 106065b390aaSDag-Erling Smørgrav "be subject to response-ip action, too; stripped"); 106165b390aaSDag-Erling Smørgrav *new_repp = base_rep; 106265b390aaSDag-Erling Smørgrav return 1; 106365b390aaSDag-Erling Smørgrav } 106465b390aaSDag-Erling Smørgrav 106565b390aaSDag-Erling Smørgrav /* Append target reply to the base. Since we cannot assume 106665b390aaSDag-Erling Smørgrav * tgt_rep->rrsets is valid throughout the lifetime of new_rep 106765b390aaSDag-Erling Smørgrav * or it can be safely shared by multiple threads, we need to make a 106865b390aaSDag-Erling Smørgrav * deep copy. */ 106965b390aaSDag-Erling Smørgrav new_rep = make_new_reply_info(base_rep, region, 107065b390aaSDag-Erling Smørgrav base_rep->an_numrrsets + tgt_rep->an_numrrsets, 107165b390aaSDag-Erling Smørgrav base_rep->an_numrrsets); 107265b390aaSDag-Erling Smørgrav if(!new_rep) 107365b390aaSDag-Erling Smørgrav return 0; 107465b390aaSDag-Erling Smørgrav for(i=0,j=base_rep->an_numrrsets; i<tgt_rep->an_numrrsets; i++,j++) { 107565b390aaSDag-Erling Smørgrav new_rep->rrsets[j] = copy_rrset(tgt_rep->rrsets[i], region); 107665b390aaSDag-Erling Smørgrav if(!new_rep->rrsets[j]) 107765b390aaSDag-Erling Smørgrav return 0; 107865b390aaSDag-Erling Smørgrav } 107965b390aaSDag-Erling Smørgrav 108065b390aaSDag-Erling Smørgrav FLAGS_SET_RCODE(new_rep->flags, tgt_rcode); 108165b390aaSDag-Erling Smørgrav *new_repp = new_rep; 108265b390aaSDag-Erling Smørgrav return 1; 108365b390aaSDag-Erling Smørgrav } 108465b390aaSDag-Erling Smørgrav 108565b390aaSDag-Erling Smørgrav void 108665b390aaSDag-Erling Smørgrav respip_inform_super(struct module_qstate* qstate, int id, 108765b390aaSDag-Erling Smørgrav struct module_qstate* super) 108865b390aaSDag-Erling Smørgrav { 108965b390aaSDag-Erling Smørgrav struct respip_qstate* rq = (struct respip_qstate*)super->minfo[id]; 109065b390aaSDag-Erling Smørgrav struct reply_info* new_rep = NULL; 109165b390aaSDag-Erling Smørgrav 109265b390aaSDag-Erling Smørgrav rq->state = RESPIP_SUBQUERY_FINISHED; 109365b390aaSDag-Erling Smørgrav 109465b390aaSDag-Erling Smørgrav /* respip subquery should have always been created with a valid reply 109565b390aaSDag-Erling Smørgrav * in super. */ 109665b390aaSDag-Erling Smørgrav log_assert(super->return_msg && super->return_msg->rep); 109765b390aaSDag-Erling Smørgrav 109865b390aaSDag-Erling Smørgrav /* return_msg can be NULL when, e.g., the sub query resulted in 109965b390aaSDag-Erling Smørgrav * SERVFAIL, in which case we regard it as a failure of the original 110065b390aaSDag-Erling Smørgrav * query. Other checks are probably redundant, but we check them 110165b390aaSDag-Erling Smørgrav * for safety. */ 110265b390aaSDag-Erling Smørgrav if(!qstate->return_msg || !qstate->return_msg->rep || 110365b390aaSDag-Erling Smørgrav qstate->return_rcode != LDNS_RCODE_NOERROR) 110465b390aaSDag-Erling Smørgrav goto fail; 110565b390aaSDag-Erling Smørgrav 110665b390aaSDag-Erling Smørgrav if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo, 110765b390aaSDag-Erling Smørgrav qstate->return_msg->rep, super->client_info, 110865b390aaSDag-Erling Smørgrav super->env->need_to_validate, &new_rep, super->region)) 110965b390aaSDag-Erling Smørgrav goto fail; 111065b390aaSDag-Erling Smørgrav super->return_msg->rep = new_rep; 111165b390aaSDag-Erling Smørgrav return; 111265b390aaSDag-Erling Smørgrav 111365b390aaSDag-Erling Smørgrav fail: 111465b390aaSDag-Erling Smørgrav super->return_rcode = LDNS_RCODE_SERVFAIL; 111565b390aaSDag-Erling Smørgrav super->return_msg = NULL; 111665b390aaSDag-Erling Smørgrav return; 111765b390aaSDag-Erling Smørgrav } 111865b390aaSDag-Erling Smørgrav 111965b390aaSDag-Erling Smørgrav void 112065b390aaSDag-Erling Smørgrav respip_clear(struct module_qstate* qstate, int id) 112165b390aaSDag-Erling Smørgrav { 112265b390aaSDag-Erling Smørgrav qstate->minfo[id] = NULL; 112365b390aaSDag-Erling Smørgrav } 112465b390aaSDag-Erling Smørgrav 112565b390aaSDag-Erling Smørgrav size_t 112665b390aaSDag-Erling Smørgrav respip_get_mem(struct module_env* env, int id) 112765b390aaSDag-Erling Smørgrav { 112865b390aaSDag-Erling Smørgrav (void)env; 112965b390aaSDag-Erling Smørgrav (void)id; 113065b390aaSDag-Erling Smørgrav return 0; 113165b390aaSDag-Erling Smørgrav } 113265b390aaSDag-Erling Smørgrav 113365b390aaSDag-Erling Smørgrav /** 113465b390aaSDag-Erling Smørgrav * The response-ip function block 113565b390aaSDag-Erling Smørgrav */ 113665b390aaSDag-Erling Smørgrav static struct module_func_block respip_block = { 113765b390aaSDag-Erling Smørgrav "respip", 113865b390aaSDag-Erling Smørgrav &respip_init, &respip_deinit, &respip_operate, &respip_inform_super, 113965b390aaSDag-Erling Smørgrav &respip_clear, &respip_get_mem 114065b390aaSDag-Erling Smørgrav }; 114165b390aaSDag-Erling Smørgrav 114265b390aaSDag-Erling Smørgrav struct module_func_block* 114365b390aaSDag-Erling Smørgrav respip_get_funcblock(void) 114465b390aaSDag-Erling Smørgrav { 114565b390aaSDag-Erling Smørgrav return &respip_block; 114665b390aaSDag-Erling Smørgrav } 114765b390aaSDag-Erling Smørgrav 114865b390aaSDag-Erling Smørgrav enum respip_action 114965b390aaSDag-Erling Smørgrav resp_addr_get_action(const struct resp_addr* addr) 115065b390aaSDag-Erling Smørgrav { 115165b390aaSDag-Erling Smørgrav return addr ? addr->action : respip_none; 115265b390aaSDag-Erling Smørgrav } 115365b390aaSDag-Erling Smørgrav 115465b390aaSDag-Erling Smørgrav struct ub_packed_rrset_key* 115565b390aaSDag-Erling Smørgrav resp_addr_get_rrset(struct resp_addr* addr) 115665b390aaSDag-Erling Smørgrav { 115765b390aaSDag-Erling Smørgrav return addr ? addr->data : NULL; 115865b390aaSDag-Erling Smørgrav } 115965b390aaSDag-Erling Smørgrav 116065b390aaSDag-Erling Smørgrav int 116165b390aaSDag-Erling Smørgrav respip_set_is_empty(const struct respip_set* set) 116265b390aaSDag-Erling Smørgrav { 116365b390aaSDag-Erling Smørgrav return set ? set->ip_tree.count == 0 : 1; 116465b390aaSDag-Erling Smørgrav } 116565b390aaSDag-Erling Smørgrav 116665b390aaSDag-Erling Smørgrav void 116765b390aaSDag-Erling Smørgrav respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname, 116865b390aaSDag-Erling Smørgrav uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias, 116965b390aaSDag-Erling Smørgrav struct comm_reply* repinfo) 117065b390aaSDag-Erling Smørgrav { 117165b390aaSDag-Erling Smørgrav char srcip[128], respip[128], txt[512]; 117265b390aaSDag-Erling Smørgrav unsigned port; 117365b390aaSDag-Erling Smørgrav 117465b390aaSDag-Erling Smørgrav if(local_alias) 117565b390aaSDag-Erling Smørgrav qname = local_alias->rrset->rk.dname; 117665b390aaSDag-Erling Smørgrav port = (unsigned)((repinfo->addr.ss_family == AF_INET) ? 117765b390aaSDag-Erling Smørgrav ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port) : 117865b390aaSDag-Erling Smørgrav ntohs(((struct sockaddr_in6*)&repinfo->addr)->sin6_port)); 117965b390aaSDag-Erling Smørgrav addr_to_str(&repinfo->addr, repinfo->addrlen, srcip, sizeof(srcip)); 118065b390aaSDag-Erling Smørgrav addr_to_str(&respip_addr->addr, respip_addr->addrlen, 118165b390aaSDag-Erling Smørgrav respip, sizeof(respip)); 118265b390aaSDag-Erling Smørgrav snprintf(txt, sizeof(txt), "%s/%d inform %s@%u", respip, 118365b390aaSDag-Erling Smørgrav respip_addr->net, srcip, port); 118465b390aaSDag-Erling Smørgrav log_nametypeclass(0, txt, qname, qtype, qclass); 118565b390aaSDag-Erling Smørgrav } 1186