diff options
Diffstat (limited to 'external/unbound/util/data/msgreply.c')
-rw-r--r-- | external/unbound/util/data/msgreply.c | 367 |
1 files changed, 357 insertions, 10 deletions
diff --git a/external/unbound/util/data/msgreply.c b/external/unbound/util/data/msgreply.c index 06593ffe1..2ce898d7f 100644 --- a/external/unbound/util/data/msgreply.c +++ b/external/unbound/util/data/msgreply.c @@ -52,6 +52,8 @@ #include "util/data/msgencode.h" #include "sldns/sbuffer.h" #include "sldns/wire2str.h" +#include "util/module.h" +#include "util/fptr_wlist.h" /** MAX TTL default for messages and rrsets */ time_t MAX_TTL = 3600 * 24 * 10; /* ten days */ @@ -76,6 +78,7 @@ parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg, qinf->qname_len = msg->qname_len; qinf->qtype = msg->qtype; qinf->qclass = msg->qclass; + qinf->local_alias = NULL; return 1; } @@ -130,9 +133,8 @@ parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep, return 1; } -/** allocate (special) rrset keys, return 0 on error */ -static int -repinfo_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, +int +reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region) { size_t i; @@ -435,7 +437,7 @@ parse_create_msg(sldns_buffer* pkt, struct msg_parse* msg, return 0; if(!parse_create_repinfo(msg, rep, region)) return 0; - if(!repinfo_alloc_rrset_keys(*rep, alloc, region)) + if(!reply_info_alloc_rrset_keys(*rep, alloc, region)) return 0; if(!parse_copy_decompress(pkt, msg, *rep, region)) return 0; @@ -451,6 +453,7 @@ int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, int ret; qinf->qname = NULL; + qinf->local_alias = NULL; *rep = NULL; if(!(msg = regional_alloc(region, sizeof(*msg)))) { return LDNS_RCODE_SERVFAIL; @@ -461,7 +464,7 @@ int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, if((ret = parse_packet(pkt, msg, region)) != 0) { return ret; } - if((ret = parse_extract_edns(msg, edns)) != 0) + if((ret = parse_extract_edns(msg, edns, region)) != 0) return ret; /* parse OK, allocate return structures */ @@ -542,6 +545,7 @@ query_info_parse(struct query_info* m, sldns_buffer* query) return 0; /* need qtype, qclass */ m->qtype = sldns_buffer_read_u16(query); m->qclass = sldns_buffer_read_u16(query); + m->local_alias = NULL; return 1; } @@ -603,10 +607,10 @@ reply_info_delete(void* d, void* ATTR_UNUSED(arg)) free(r); } -hashvalue_t +hashvalue_type query_info_hash(struct query_info *q, uint16_t flags) { - hashvalue_t h = 0xab; + hashvalue_type h = 0xab; h = hashlittle(&q->qtype, sizeof(q->qtype), h); if(q->qtype == LDNS_RR_TYPE_AAAA && (flags&BIT_CD)) h++; @@ -617,7 +621,7 @@ query_info_hash(struct query_info *q, uint16_t flags) struct msgreply_entry* query_info_entrysetup(struct query_info* q, struct reply_info* r, - hashvalue_t h) + hashvalue_type h) { struct msgreply_entry* e = (struct msgreply_entry*)malloc( sizeof(struct msgreply_entry)); @@ -683,7 +687,7 @@ reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, if(!cp) return NULL; /* allocate ub_key structures special or not */ - if(!repinfo_alloc_rrset_keys(cp, alloc, region)) { + if(!reply_info_alloc_rrset_keys(cp, alloc, region)) { if(!region) reply_info_parsedelete(cp, alloc); return NULL; @@ -814,7 +818,39 @@ log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep) regional_destroy(region); } -void +void +log_reply_info(enum verbosity_value v, struct query_info *qinf, + struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur, + int cached, struct sldns_buffer *rmsg) +{ + char qname_buf[LDNS_MAX_DOMAINLEN+1]; + char clientip_buf[128]; + char rcode_buf[16]; + char type_buf[16]; + char class_buf[16]; + size_t pktlen; + uint16_t rcode = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(rmsg, 2)); + + if(verbosity < v) + return; + + sldns_wire2str_rcode_buf((int)rcode, rcode_buf, sizeof(rcode_buf)); + addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf)); + if(rcode == LDNS_RCODE_FORMERR) + { + log_info("%s - - - %s - - - ", clientip_buf, rcode_buf); + } else { + dname_str(qinf->qname, qname_buf); + pktlen = sldns_buffer_limit(rmsg); + sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf)); + sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf)); + log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d", + clientip_buf, qname_buf, type_buf, class_buf, + rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen); + } +} + +void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf) { @@ -857,3 +893,314 @@ reply_all_rrsets_secure(struct reply_info* rep) } return 1; } + +int edns_opt_append(struct edns_data* edns, struct regional* region, + uint16_t code, size_t len, uint8_t* data) +{ + struct edns_option** prevp; + struct edns_option* opt; + + /* allocate new element */ + opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); + if(!opt) + return 0; + opt->next = NULL; + opt->opt_code = code; + opt->opt_len = len; + opt->opt_data = NULL; + if(len > 0) { + opt->opt_data = regional_alloc_init(region, data, len); + if(!opt->opt_data) + return 0; + } + + /* append at end of list */ + prevp = &edns->opt_list; + while(*prevp != NULL) + prevp = &((*prevp)->next); + *prevp = opt; + return 1; +} + +int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, + uint8_t* data, struct regional* region) +{ + struct edns_option** prevp; + struct edns_option* opt; + + /* allocate new element */ + opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); + if(!opt) + return 0; + opt->next = NULL; + opt->opt_code = code; + opt->opt_len = len; + opt->opt_data = NULL; + if(len > 0) { + opt->opt_data = regional_alloc_init(region, data, len); + if(!opt->opt_data) + return 0; + } + + /* append at end of list */ + prevp = list; + while(*prevp != NULL) { + prevp = &((*prevp)->next); + } + *prevp = opt; + return 1; +} + +int edns_opt_list_remove(struct edns_option** list, uint16_t code) +{ + /* The list should already be allocated in a region. Freeing the + * allocated space in a region is not possible. We just unlink the + * required elements and they will be freed together with the region. */ + + struct edns_option* prev; + struct edns_option* curr; + if(!list || !(*list)) return 0; + + /* Unlink and repoint if the element(s) are first in list */ + while(list && *list && (*list)->opt_code == code) { + *list = (*list)->next; + } + + if(!list || !(*list)) return 1; + /* Unlink elements and reattach the list */ + prev = *list; + curr = (*list)->next; + while(curr != NULL) { + if(curr->opt_code == code) { + prev->next = curr->next; + curr = curr->next; + } else { + prev = curr; + curr = curr->next; + } + } + return 1; +} + +static int inplace_cb_reply_call_generic( + struct inplace_cb* callback_list, enum inplace_cb_list_type type, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct regional* region) +{ + struct inplace_cb* cb; + struct edns_option* opt_list_out = NULL; + if(qstate) + opt_list_out = qstate->edns_opts_front_out; + for(cb=callback_list; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_reply_generic( + (inplace_cb_reply_func_type*)cb->cb, type)); + (void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep, + rcode, edns, &opt_list_out, region, cb->id, cb->cb_arg); + } + edns->opt_list = opt_list_out; + return 1; +} + +int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo, + qstate, rep, rcode, edns, region); +} + +int inplace_cb_reply_cache_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_cache], inplace_cb_reply_cache, + qinfo, qstate, rep, rcode, edns, region); +} + +int inplace_cb_reply_local_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_local], inplace_cb_reply_local, + qinfo, qstate, rep, rcode, edns, region); +} + +int inplace_cb_reply_servfail_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct regional* region) +{ + /* We are going to servfail. Remove any potential edns options. */ + if(qstate) + qstate->edns_opts_front_out = NULL; + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_servfail], + inplace_cb_reply_servfail, qinfo, qstate, rep, rcode, edns, region); +} + +int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo, + uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, struct module_qstate* qstate, + struct regional* region) +{ + struct inplace_cb* cb = env->inplace_cb_lists[inplace_cb_query]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_query( + (inplace_cb_query_func_type*)cb->cb)); + (void)(*(inplace_cb_query_func_type*)cb->cb)(qinfo, flags, + qstate, addr, addrlen, zone, zonelen, region, + cb->id, cb->cb_arg); + } + return 1; +} + +int inplace_cb_edns_back_parsed_call(struct module_env* env, + struct module_qstate* qstate) +{ + struct inplace_cb* cb = + env->inplace_cb_lists[inplace_cb_edns_back_parsed]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_edns_back_parsed( + (inplace_cb_edns_back_parsed_func_type*)cb->cb)); + (void)(*(inplace_cb_edns_back_parsed_func_type*)cb->cb)(qstate, + cb->id, cb->cb_arg); + } + return 1; +} + +int inplace_cb_query_response_call(struct module_env* env, + struct module_qstate* qstate, struct dns_msg* response) { + struct inplace_cb* cb = + env->inplace_cb_lists[inplace_cb_query_response]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_query_response( + (inplace_cb_query_response_func_type*)cb->cb)); + (void)(*(inplace_cb_query_response_func_type*)cb->cb)(qstate, + response, cb->id, cb->cb_arg); + } + return 1; +} + +struct edns_option* edns_opt_copy_region(struct edns_option* list, + struct regional* region) +{ + struct edns_option* result = NULL, *cur = NULL, *s; + while(list) { + /* copy edns option structure */ + s = regional_alloc_init(region, list, sizeof(*list)); + if(!s) return NULL; + s->next = NULL; + + /* copy option data */ + if(s->opt_data) { + s->opt_data = regional_alloc_init(region, s->opt_data, + s->opt_len); + if(!s->opt_data) + return NULL; + } + + /* link into list */ + if(cur) + cur->next = s; + else result = s; + cur = s; + + /* examine next element */ + list = list->next; + } + return result; +} + +int edns_opt_compare(struct edns_option* p, struct edns_option* q) +{ + if(!p && !q) return 0; + if(!p) return -1; + if(!q) return 1; + log_assert(p && q); + if(p->opt_code != q->opt_code) + return (int)q->opt_code - (int)p->opt_code; + if(p->opt_len != q->opt_len) + return (int)q->opt_len - (int)p->opt_len; + if(p->opt_len != 0) + return memcmp(p->opt_data, q->opt_data, p->opt_len); + return 0; +} + +int edns_opt_list_compare(struct edns_option* p, struct edns_option* q) +{ + int r; + while(p && q) { + r = edns_opt_compare(p, q); + if(r != 0) + return r; + p = p->next; + q = q->next; + } + if(p || q) { + /* uneven length lists */ + if(p) return 1; + if(q) return -1; + } + return 0; +} + +void edns_opt_list_free(struct edns_option* list) +{ + struct edns_option* n; + while(list) { + free(list->opt_data); + n = list->next; + free(list); + list = n; + } +} + +struct edns_option* edns_opt_copy_alloc(struct edns_option* list) +{ + struct edns_option* result = NULL, *cur = NULL, *s; + while(list) { + /* copy edns option structure */ + s = memdup(list, sizeof(*list)); + if(!s) { + edns_opt_list_free(result); + return NULL; + } + s->next = NULL; + + /* copy option data */ + if(s->opt_data) { + s->opt_data = memdup(s->opt_data, s->opt_len); + if(!s->opt_data) { + free(s); + edns_opt_list_free(result); + return NULL; + } + } + + /* link into list */ + if(cur) + cur->next = s; + else result = s; + cur = s; + + /* examine next element */ + list = list->next; + } + return result; +} + +struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code) +{ + struct edns_option* p; + for(p=list; p; p=p->next) { + if(p->opt_code == code) + return p; + } + return NULL; +} |