From 16e425ef771eadd3682f48c4e404c87d9d570ba1 Mon Sep 17 00:00:00 2001 From: Yuri Schaeffer Date: Mon, 18 Feb 2013 16:23:43 +0000 Subject: [PATCH] Make sure we set unused addr bytes to zero Improve structure git-svn-id: file:///svn/unbound/branches/edns-subnet@2846 be551aaa-1e26-0410-a405-d3ace91eadb9 --- edns-subnet/edns-subnet.c | 19 +++++ edns-subnet/edns-subnet.h | 5 ++ edns-subnet/subnetmod.c | 151 +++++++++++++++++-------------------- edns-subnet/subnetmod.h | 10 --- services/mesh.c | 15 +++- services/outside_network.c | 15 +--- services/outside_network.h | 40 ++-------- util/data/msgencode.c | 1 + util/data/msgparse.c | 47 ++++++++---- 9 files changed, 149 insertions(+), 154 deletions(-) diff --git a/edns-subnet/edns-subnet.c b/edns-subnet/edns-subnet.c index fdfecc71c..cc3d63115 100644 --- a/edns-subnet/edns-subnet.c +++ b/edns-subnet/edns-subnet.c @@ -55,6 +55,25 @@ uint16_t EDNSSUBNET_OPCODE = 0x50fa; uint8_t EDNSSUBNET_MAX_SUBNET_IP4 = 24; uint8_t EDNSSUBNET_MAX_SUBNET_IP6 = 64; +int +copy_clear(uint8_t* dst, int dstlen, uint8_t* src, int srclen, int n) +{ + int intpart = n / 8; /* bytes */ + int fracpart = n % 8; /* bits */ + int written = intpart; + if (intpart > dstlen || intpart > srclen) + return 1; + if (fracpart && (intpart+1 > dstlen || intpart+1 > srclen)) + return 1; + memcpy(dst, src, intpart); + if (fracpart) { + dst[intpart] = src[intpart] & ~(0xFF >> fracpart); + written++; + } + memset(dst + written, 0, dstlen - written); + return 0; +} + struct ednssubnet_upstream* upstream_create(void) { diff --git a/edns-subnet/edns-subnet.h b/edns-subnet/edns-subnet.h index 969e6e805..b052fdea7 100644 --- a/edns-subnet/edns-subnet.h +++ b/edns-subnet/edns-subnet.h @@ -57,6 +57,11 @@ extern uint16_t EDNSSUBNET_OPCODE; extern uint8_t EDNSSUBNET_MAX_SUBNET_IP4; extern uint8_t EDNSSUBNET_MAX_SUBNET_IP6; +/** copy the first n BITS from src to dst iff both src and dst + * are large enough, return 0 on succes */ +int +copy_clear(uint8_t* dst, int dstlen, uint8_t* src, int srclen, int n); + /** * ednssubnet_upstream structure */ diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index acaf115fc..d2571eb12 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -48,27 +48,6 @@ #include "util/module.h" #include "util/regional.h" -/** fill in message structure */ -static struct subnet_qstate* -sub_new_getmsg(struct module_qstate* qstate, struct subnet_qstate* snq) -{ - return snq; -} - -/** allocate new subnet query state */ -static struct subnet_qstate* -sub_new(struct module_qstate* qstate, int id) -{ - struct subnet_qstate* snq = (struct subnet_qstate*)regional_alloc( - qstate->region, sizeof(*snq)); - log_assert(!qstate->minfo[id]); - if(!snq) - return NULL; - memset(snq, 0, sizeof(*snq)); - qstate->minfo[id] = snq; - return sub_new_getmsg(qstate, snq); -} - int subnetmod_init(struct module_env* env, int id) { return 1; @@ -78,7 +57,8 @@ void subnetmod_deinit(struct module_env* env, int id) { } -void subnetmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super) +void subnetmod_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super) { } @@ -91,6 +71,69 @@ void cp_edns_bad_response(struct edns_data* target, struct edns_data* source) target->subnet_validdata = 1; } +enum module_ext_state eval_response(struct module_qstate* qstate) +{ + if (!qstate->edns_out.subnet_sent) { + if (qstate->edns_in.subnet_validdata) + verbose(VERB_QUERY, "subnet: received spurious data"); + if (qstate->edns_out.subnet_downstream) { + /* Copy question back to client */ + qstate->edns_to_client = &qstate->edns_in; + cp_edns_bad_response(qstate->edns_to_client, + qstate->edns_from_client); + } + return module_finished; + } + + /* subnet sent but nothing came back */ + if (!qstate->edns_in.subnet_validdata) { + /** The authority indicated no support for vandergaast. As a + * consequence the answer ended up in the regular cache. It + * is still usefull to put it in the vandergaast cache for + * when a client explicitly asks for subnet specific answer. */ + verbose(VERB_QUERY, "subnet: Authority indicates no support"); + // TODO PUT IT IN OUR SPECIAL CACHE + if (qstate->edns_out.subnet_downstream) { + qstate->edns_to_client = &qstate->edns_in; + cp_edns_bad_response(qstate->edns_to_client, + qstate->edns_from_client); + } + return module_finished; + } + + /** Being here means We asked for and got a subnet specific answer. + * Also the answer from the authority is not yet cached anywhere. */ + + /* can we accept response? */ + size_t sn_octs, remainder; + sn_octs = qstate->edns_out.subnet_source_mask / 8; + assert(sn_octs <= INET6_SIZE); /* Enforced by msgparse */ + remainder = 8 - (size_t)(qstate->edns_out.subnet_source_mask % 8); + if(qstate->edns_out.subnet_addr_fam != qstate->edns_in.subnet_addr_fam || + qstate->edns_out.subnet_source_mask != qstate->edns_in.subnet_source_mask || + memcmp(qstate->edns_out.subnet_addr, qstate->edns_in.subnet_addr, sn_octs) != 0 || + (qstate->edns_out.subnet_addr[sn_octs]^qstate->edns_in.subnet_addr[sn_octs])>>remainder) { + /* we can not, restart query without option */ + verbose(VERB_QUERY, "subnet: forged data"); + qstate->edns_out.subnet_validdata = 0; + qstate->edns_out.subnet_sent = 0; + return module_wait_module; + } + + /* TODO PUT IT IN OUR SPECIAL CACHE */ + + if (qstate->edns_out.subnet_downstream) { + /* Client wants to see the answer, echo option back + * and adjust the scope. */ + qstate->edns_to_client = qstate->edns_from_client; + verbose(VERB_QUERY, "subnet: attach"); + qstate->edns_to_client->subnet_scope_mask = + qstate->edns_in.subnet_scope_mask; + } + verbose(VERB_QUERY, "subnet: done"); + return module_finished; +} + void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* ATTR_UNUSED(outbound)) { @@ -98,19 +141,16 @@ void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, void* cachehit; int max_mask; #endif - struct edns_data* edns_from_client; //from client + struct edns_data* edns_from_client; struct subnet_env* sne = (struct subnet_env*)qstate->env->modinfo[id]; struct subnet_qstate* snq = (struct subnet_qstate*)qstate->minfo[id]; verbose(VERB_QUERY, "subnet[module %d] operate: extstate:%s " "event:%s", id, strextstate(qstate->ext_state[id]), strmodulevent(event)); - log_query_info(VERB_QUERY, "subnet operate: query", - &qstate->qinfo); - /* This query is new for us */ - if(event == module_event_new || - (event == module_event_pass && snq == NULL)) { - snq = sub_new(qstate, id); + log_query_info(VERB_QUERY, "subnet operate: query", &qstate->qinfo); + + if(event == module_event_new) { edns_from_client = qstate->edns_from_client; if(!edns_from_client || !edns_from_client->subnet_validdata) { @@ -184,58 +224,7 @@ void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, if(event == module_event_moddone) { verbose(VERB_QUERY, "subnet: done"); qstate->edns_to_client = NULL; - if (!qstate->edns_out.subnet_sent) { - verbose(VERB_QUERY, "subnet: did not sent"); - if (qstate->edns_in.subnet_validdata) { - verbose(VERB_QUERY, "subnet: received spurious data"); - } - if (qstate->edns_out.subnet_downstream) { - verbose(VERB_QUERY, "subnet: client shows interest"); - qstate->edns_to_client = &qstate->edns_in; - cp_edns_bad_response(qstate->edns_to_client, - qstate->edns_from_client); - } - } else { - verbose(VERB_QUERY, "subnet: did sent"); - /* subnet sent but nothing came back */ - if (!qstate->edns_in.subnet_validdata) { - verbose(VERB_QUERY, "subnet: missing data"); - // TODO PUT IT IN OUR SPECIAL CACHE - if (qstate->edns_out.subnet_downstream) { - qstate->edns_to_client = &qstate->edns_in; - cp_edns_bad_response(qstate->edns_to_client, - qstate->edns_from_client); - } - } else { - verbose(VERB_QUERY, "subnet: is not cached"); - /* can we accept response? */ - size_t sn_octs, remainder; - sn_octs = qstate->edns_out.subnet_source_mask / 8; - /* should be enforced by msgparse */ - assert(sn_octs <= INET6_SIZE); - remainder = 8 - (size_t)(qstate->edns_out.subnet_source_mask % 8); - if(qstate->edns_out.subnet_addr_fam != qstate->edns_in.subnet_addr_fam || - qstate->edns_out.subnet_source_mask != qstate->edns_in.subnet_source_mask || - memcmp(qstate->edns_out.subnet_addr, qstate->edns_in.subnet_addr, sn_octs) != 0 || - (qstate->edns_out.subnet_addr[sn_octs]^qstate->edns_in.subnet_addr[sn_octs])>>remainder) { - /* we can not, restart query without option */ - verbose(VERB_QUERY, "subnet: forged data"); - qstate->edns_out.subnet_validdata = 0; - qstate->edns_out.subnet_sent = 0; - qstate->ext_state[id] = module_wait_module; - return; - } - verbose(VERB_QUERY, "subnet: now cache it"); - /* TODO PUT IT IN OUR SPECIAL CACHE */ - if (qstate->edns_out.subnet_downstream) { - qstate->edns_to_client = qstate->edns_from_client; - verbose(VERB_QUERY, "subnet: attach"); - qstate->edns_to_client->subnet_scope_mask = - qstate->edns_in.subnet_scope_mask; - } - } - } - qstate->ext_state[id] = module_finished; + qstate->ext_state[id] = eval_response(qstate); return; } /* We are being revisited */ diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index 229959327..be9179f4a 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -51,16 +51,6 @@ struct subnet_env { }; -/** - * Per query state for the subnet module. - */ -struct subnet_qstate { - /** - * State of the subnet module. - */ - struct edns_data edns; -}; - /** * Get the module function block. * @return: function block with function pointers to module methods. diff --git a/services/mesh.c b/services/mesh.c index d85b04eb0..c71c1a1f9 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -394,6 +394,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, } else { struct sockaddr_storage *ss; void* sinaddr; + edns->subnet_validdata = 0; edns->subnet_downstream = 0; /* Construct subnet option from original query */ ss = &s->reply_list->query_reply.addr; @@ -401,16 +402,22 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, edns->subnet_source_mask = EDNSSUBNET_MAX_SUBNET_IP4; edns->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4; sinaddr = &((struct sockaddr_in*)ss)->sin_addr; - memcpy(edns->subnet_addr, (uint8_t *)sinaddr, INET_SIZE); - edns->subnet_validdata = 1; + if (!copy_clear( + edns->subnet_addr, INET6_SIZE, (uint8_t *)sinaddr, + INET_SIZE, EDNSSUBNET_MAX_SUBNET_IP4)) { + edns->subnet_validdata = 1; + } } #ifdef INET6 else { edns->subnet_source_mask = EDNSSUBNET_MAX_SUBNET_IP6; edns->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6; sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr; - memcpy(edns->subnet_addr, (uint8_t *)sinaddr, INET6_SIZE); - edns->subnet_validdata = 1; + if (!copy_clear( + edns->subnet_addr, INET6_SIZE, (uint8_t *)sinaddr, + INET6_SIZE, EDNSSUBNET_MAX_SUBNET_IP4)) { + edns->subnet_validdata = 1; + } } #else else { diff --git a/services/outside_network.c b/services/outside_network.c index 4ba9efed4..3fa9f3d7d 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -557,7 +557,6 @@ static int setup_if(struct port_if* pif, const char* addrstr, return 1; } -#ifdef CLIENT_SUBNET struct outside_network* outside_network_create(struct comm_base *base, size_t bufsize, size_t num_ports, char** ifs, int num_ifs, int do_ip4, @@ -565,17 +564,11 @@ outside_network_create(struct comm_base *base, size_t bufsize, struct ub_randstate* rnd, int use_caps_for_id, int* availports, int numavailports, size_t unwanted_threshold, void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx, struct ednssubnet_upstream* edns_subnet_upstreams) -#else -struct outside_network* -outside_network_create(struct comm_base *base, size_t bufsize, - size_t num_ports, char** ifs, int num_ifs, int do_ip4, - int do_ip6, size_t num_tcp, struct infra_cache* infra, - struct ub_randstate* rnd, int use_caps_for_id, int* availports, - int numavailports, size_t unwanted_threshold, - void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx) + void* sslctx +#ifdef CLIENT_SUBNET + , struct ednssubnet_upstream* edns_subnet_upstreams #endif + ) { struct outside_network* outnet = (struct outside_network*) calloc(1, sizeof(struct outside_network)); diff --git a/services/outside_network.h b/services/outside_network.h index 9001c89ba..bbe6feff2 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -362,7 +362,7 @@ struct serviced_query { #endif }; -#ifdef CLIENT_SUBNET + /** * Create outside_network structure with N udp ports. * @param base: the communication base to use for event handling. @@ -393,40 +393,12 @@ struct outside_network* outside_network_create(struct comm_base* base, struct ub_randstate* rnd, int use_caps_for_id, int* availports, int numavailports, size_t unwanted_threshold, void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx, struct ednssubnet_upstream* edns_subnet_upstreams); -#else -/** - * Create outside_network structure with N udp ports. - * @param base: the communication base to use for event handling. - * @param bufsize: size for network buffers. - * @param num_ports: number of udp ports to open per interface. - * @param ifs: interface names (or NULL for default interface). - * These interfaces must be able to access all authoritative servers. - * @param num_ifs: number of names in array ifs. - * @param do_ip4: service IP4. - * @param do_ip6: service IP6. - * @param num_tcp: number of outgoing tcp buffers to preallocate. - * @param infra: pointer to infra cached used for serviced queries. - * @param rnd: stored to create random numbers for serviced queries. - * @param use_caps_for_id: enable to use 0x20 bits to encode id randomness. - * @param availports: array of available ports. - * @param numavailports: number of available ports in array. - * @param unwanted_threshold: when to take defensive action. - * @param unwanted_action: the action to take. - * @param unwanted_param: user parameter to action. - * @param do_udp: if udp is done. - * @param sslctx: context to create outgoing connections with (if enabled). - * @return: the new structure (with no pending answers) or NULL on error. - */ - -struct outside_network* outside_network_create(struct comm_base* base, - size_t bufsize, size_t num_ports, char** ifs, int num_ifs, - int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra, - struct ub_randstate* rnd, int use_caps_for_id, int* availports, - int numavailports, size_t unwanted_threshold, - void (*unwanted_action)(void*), void* unwanted_param, int do_udp, - void* sslctx); + void* sslctx +#ifdef CLIENT_SUBNET + , struct ednssubnet_upstream* edns_subnet_upstreams #endif +); + /** * Delete outside_network structure. * @param outnet: object to delete. diff --git a/util/data/msgencode.c b/util/data/msgencode.c index d45f2f1a3..45e6db17a 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -763,6 +763,7 @@ attach_edns_record(ldns_buffer* pkt, struct edns_data* edns) sn_octs = edns->subnet_source_mask / 8; sn_octs_remainder = (size_t)((edns->subnet_source_mask % 8)>0?1:0); + assert(sn_octs + sn_octs_remainder <= INET6_SIZE); assert(ldns_buffer_available(pkt, sn_octs + sn_octs_remainder + 4 + 6)); ldns_buffer_write_u16(pkt, sn_octs + sn_octs_remainder + 4 + 4); /* rdatalen */ diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 3d34b25ad..7477e588d 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -934,6 +934,34 @@ parse_packet(ldns_buffer* pkt, struct msg_parse* msg, struct regional* region) } #ifdef CLIENT_SUBNET + +void parse_subnet_option(uint8_t* data, struct edns_data* edns, int opt_len) +{ + if(opt_len < 4) return; /* try next */ + + edns->subnet_addr_fam = ldns_read_uint16(data); + edns->subnet_source_mask = data[2]; + edns->subnet_scope_mask = data[3]; + /* remaing bytes indicate address */ + + /* validate input*/ + if(opt_len - 4 > INET6_SIZE || opt_len == 0) return; + if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { + if (edns->subnet_source_mask > 32 || edns->subnet_scope_mask > 32) + return; + } else if (edns->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6) { + if (edns->subnet_source_mask > 128 || edns->subnet_scope_mask > 128) + return; + } else return; + + + if (copy_clear(edns->subnet_addr, INET6_SIZE, data + 6, + opt_len - 4, edns->subnet_source_mask)) + return; + + edns->subnet_validdata = 1; +} + void parse_ednsdata(uint8_t* data, struct edns_data* edns) { @@ -943,29 +971,20 @@ parse_ednsdata(uint8_t* data, struct edns_data* edns) edns_datalen = ldns_read_uint16(data); data += 2; if(edns_datalen < 4) return; - /* iterate trough all options */ opt_start = 0; while(opt_start + 4 <= edns_datalen) { /* opcode + len must fit */ opt_opc = ldns_read_uint16(&data[opt_start]); opt_len = ldns_read_uint16(&data[2 + opt_start]); /* Option does not fit in remaining data */ - if(opt_start + 4 + opt_len > edns_datalen) return; - opt_start += 4; + if(opt_start + 4 + opt_len > edns_datalen) break; + if(opt_opc == EDNSSUBNET_OPCODE) { - if(opt_len < 4) break; - edns->subnet_addr_fam = ldns_read_uint16(data + opt_start); - edns->subnet_source_mask = data[2 + opt_start]; - edns->subnet_scope_mask = data[3 + opt_start]; - /* remaing bytes indicate address */ - if(opt_len - 4 > INET6_SIZE || opt_len == 0) break; - memset(edns->subnet_addr, 0, INET6_SIZE); - memcpy(edns->subnet_addr, data + 6 + opt_start, opt_len - 4); - edns->subnet_validdata = 1; - break; + parse_subnet_option(data + opt_start + 4, edns, opt_len); } else { /* Unknown opcode */ verbose(VERB_QUERY, "Unknow EDNS option %x", opt_opc); } - opt_start += opt_len; + + opt_start += opt_len + 4; } } #endif -- 2.47.2