From: Yuri Schaeffer Date: Mon, 17 Sep 2012 13:31:39 +0000 (+0000) Subject: Parse EDNS data on incoming queries and incoming replies. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e013523aeecedeb006a83ea444f9da6b80b6974c;p=thirdparty%2Funbound.git Parse EDNS data on incoming queries and incoming replies. git-svn-id: file:///svn/unbound/branches/edns-subnet@2761 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 82dfb93fe..67ffebc3f 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -475,7 +475,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); regional_free_all(worker->scratchpad); @@ -504,7 +504,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; msg->rep->flags |= BIT_QR|BIT_RA; if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, repinfo->c->buffer, 0, 1, worker->scratchpad, @@ -563,7 +563,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); rrset_array_unlock_touch(worker->env.rrset_cache, @@ -595,7 +595,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; if(!reply_info_answer_encode(qinfo, rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { @@ -669,7 +669,7 @@ chaos_replystr(ldns_buffer* pkt, const char* str, struct edns_data* edns) edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; attach_edns_record(pkt, edns); } @@ -821,7 +821,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; edns.bits &= EDNS_DO; - edns.subnet_option_add = 0; + edns.subnet_option = 0; verbose(VERB_ALGO, "query with bad edns version."); log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo, diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 5d8c40bcf..b50af944e 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -531,7 +531,7 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q, edns->ext_rcode = 0; edns->edns_version = 0; edns->bits = EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; if(ldns_buffer_capacity(w->back->udp_buff) < 65535) edns->udp_size = (uint16_t)ldns_buffer_capacity( w->back->udp_buff); @@ -725,8 +725,7 @@ struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, if(!e->qsent) { return NULL; } - if(e->qstate->mesh_info->reply_list) - e->qsent->client = &e->qstate->mesh_info->reply_list->query_reply; + e->qsent->mesh_info = e->qstate->mesh_info; return e; } diff --git a/services/localzone.c b/services/localzone.c index 6a166e257..b8ef26aca 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1010,7 +1010,7 @@ local_encode(struct query_info* qinfo, struct edns_data* edns, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - edns->subnet_option_add = 0; + edns->subnet_option = 0; if(!reply_info_answer_encode(qinfo, &rep, *(uint16_t*)ldns_buffer_begin(buf), ldns_buffer_read_u16_at(buf, 2), diff --git a/services/mesh.c b/services/mesh.c index fdbdc6e5d..c041b2584 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -791,7 +791,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; - r->edns.subnet_option_add = 0; + r->edns.subnet_option = 0; if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->buf, 0, 1, m->s.env->scratch, udp_size, &r->edns, @@ -863,7 +863,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; - r->edns.subnet_option_add = 0; + r->edns.subnet_option = 0; m->s.qinfo.qname = r->qname; if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->query_reply.c->buffer, 0, 1, diff --git a/services/mesh.h b/services/mesh.h index 5f109779a..8291fc4bb 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -182,6 +182,12 @@ struct mesh_state { /** true if replies have been sent out (at end for alignment) */ uint8_t replies_sent; + + /* YBS */ + int subnet_option_expect; + uint16_t subnet_addr_fam; + uint8_t subnet_source_mask; + uint8_t subnet_addr[16]; }; /** diff --git a/services/outside_network.c b/services/outside_network.c index 10c25a897..3da40d9dc 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -1209,7 +1209,7 @@ serviced_create(struct outside_network* outnet, ldns_buffer* buff, int dnssec, sq->status = serviced_initial; sq->retry = 0; sq->to_be_deleted = 0; - sq->client = NULL; + sq->mesh_info = NULL; #ifdef UNBOUND_DEBUG ins = #endif @@ -1333,10 +1333,11 @@ serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns) edns.edns_version = EDNS_ADVERTISED_VERSION; /* If this query has an interested client and the upstream * target is in the whitelist, add the edns subnet option. */ - edns.subnet_option_add = sq->client && upstream_lookup( - sq->outnet->edns_subnet_upstreams, &sq->addr, sq->addrlen); - if(edns.subnet_option_add) { - ss = &sq->client->addr; + edns.subnet_option = sq->mesh_info->reply_list && + upstream_lookup(sq->outnet->edns_subnet_upstreams, + &sq->addr, sq->addrlen); + if(edns.subnet_option) { + ss = &sq->mesh_info->reply_list->query_reply.addr; if(((struct sockaddr_in*)ss)->sin_family == AF_INET) { edns.subnet_addr_fam = IANA_ADDRFAM_IP4; sinaddr = &((struct sockaddr_in*)ss)->sin_addr; @@ -1355,6 +1356,11 @@ serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns) } #endif edns.subnet_scope_mask = 0; + //YBS add addr,fam,mask to mesh. + sq->mesh_info->subnet_option_expect = 1; + sq->mesh_info->subnet_addr_fam = edns.subnet_addr_fam; + sq->mesh_info->subnet_source_mask = edns.subnet_source_mask; + memcpy(sq->mesh_info->subnet_addr, (uint8_t *)sinaddr, INET6_SIZE); } if(sq->status == serviced_query_UDP_EDNS_FRAG) { if(addr_is_ip6(&sq->addr, sq->addrlen)) { @@ -1825,7 +1831,6 @@ outnet_serviced_query(struct outside_network* outnet, { struct serviced_query* sq; struct service_callback* cb; - struct mesh_reply* reply_list; serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags); sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen); /* duplicate entries are inclded in the callback list, because @@ -1844,10 +1849,8 @@ outnet_serviced_query(struct outside_network* outnet, } /* Is this a client initiated query? Make clients available * to serviced query. */ - reply_list = ((struct outbound_entry*)callback_arg) - ->qstate->mesh_info->reply_list; - if(reply_list) - sq->client = &reply_list->query_reply; + sq->mesh_info = ((struct outbound_entry*)callback_arg) + ->qstate->mesh_info; /* perform first network action */ if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) { diff --git a/services/outside_network.h b/services/outside_network.h index 74208441b..b781015d5 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -356,7 +356,7 @@ struct serviced_query { /** the UDP or TCP query that is pending, see status which */ void* pending; /** Clients initiating lookup. Not owned by serviced_query */ - struct comm_reply *client; + struct mesh_state *mesh_info; }; /** diff --git a/testcode/fake_event.c b/testcode/fake_event.c index baa9bb662..f0ca60251 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1100,9 +1100,9 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, memcpy(&pend->addr, (struct sockaddr_storage*)&target_addr, sizeof(struct sockaddr_storage)); pend->addrlen = 16; - edns.subnet_option_add = pend->client && upstream_lookup( + edns.subnet_option = pend->client && upstream_lookup( outnet->edns_subnet_upstreams, &pend->addr, pend->addrlen); - if(edns.subnet_option_add) { + if(edns.subnet_option) { ss = &pend->client->addr; if(((struct sockaddr_in*)ss)->sin_family == AF_INET) { edns.subnet_addr_fam = IANA_ADDRFAM_IP4; diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index de123ed4d..cb7a5d1be 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -141,7 +141,7 @@ write_q(int fd, int udp, SSL* ssl, ldns_buffer* buf, uint16_t id, edns.edns_present = 1; edns.bits = EDNS_DO; edns.udp_size = 4096; - edns.subnet_option_add = 0; + edns.subnet_option = 0; attach_edns_record(buf, &edns); } diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 97fe83f75..5b386a1e1 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -746,7 +746,7 @@ attach_edns_record(ldns_buffer* pkt, struct edns_data* edns) ldns_buffer_write_u8(pkt, edns->edns_version); ldns_buffer_write_u16(pkt, edns->bits); /* Add edns-subnet option to record */ - if(edns->subnet_option_add) { + if(edns->subnet_option) { assert(edns->subnet_addr_fam == IANA_ADDRFAM_IP4 || edns->subnet_addr_fam == IANA_ADDRFAM_IP6); assert(edns->subnet_addr_fam != IANA_ADDRFAM_IP4 || @@ -863,7 +863,7 @@ error_encode(ldns_buffer* buf, int r, struct query_info* qinfo, es.udp_size = EDNS_ADVERTISED_SIZE; es.ext_rcode = 0; es.bits &= EDNS_DO; - es.subnet_option_add = 0; + es.subnet_option = 0; if(ldns_buffer_limit(buf) + calc_edns_field_size(&es) > edns->udp_size) return; diff --git a/util/data/msgparse.c b/util/data/msgparse.c index e5f00551b..7be61f886 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -43,6 +43,7 @@ #include "util/data/packed_rrset.h" #include "util/storage/lookup3.h" #include "util/regional.h" +#include "util/net_help.h" /** smart comparison of (compressed, valid) dnames from packet */ static int @@ -930,6 +931,40 @@ parse_packet(ldns_buffer* pkt, struct msg_parse* msg, struct regional* region) return 0; } +void +parse_ednsdata(uint8_t* data, struct edns_data* edns) +{ + int edns_datalen, opt_opc, opt_len, opt_start; + edns->subnet_option = 0; + /* Parse EDNS data field */ + edns_datalen = ldns_read_uint16(data); + 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[2 + opt_start]); + opt_len = ldns_read_uint16(&data[4 + opt_start]); + /* Option does not fit in remaining data */ + if(opt_start + 4 + opt_len > edns_datalen) return; + opt_start += 4; + if(opt_opc == EDNS_SUBNET_OPC) { + if(opt_len < 4) break; + edns->subnet_addr_fam = ldns_read_uint16(data + 2 + opt_start); + edns->subnet_source_mask = data[4 + opt_start]; + edns->subnet_scope_mask = data[5 + 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_option = 1; + break; + } else { /* Unknown opcode */ + verbose(VERB_QUERY, "Unknow EDNS option %x", opt_opc); + } + opt_start += opt_len; + } +} + int parse_extract_edns(struct msg_parse* msg, struct edns_data* edns) { @@ -979,12 +1014,11 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns) /* take the data ! */ edns->edns_present = 1; + edns->udp_size = ntohs(found->rrset_class); edns->ext_rcode = found->rr_last->ttl_data[0]; edns->edns_version = found->rr_last->ttl_data[1]; edns->bits = ldns_read_uint16(&found->rr_last->ttl_data[2]); - edns->udp_size = ntohs(found->rrset_class); - edns->subnet_option_add = 0; //YBS do some actual parsing here - /* ignore rdata and rrsigs */ + parse_ednsdata(found->rr_last->ttl_data + 4, edns); return 0; } @@ -1015,6 +1049,6 @@ parse_edns_from_pkt(ldns_buffer* pkt, struct edns_data* edns) edns->ext_rcode = ldns_buffer_read_u8(pkt); /* ttl used for bits */ edns->edns_version = ldns_buffer_read_u8(pkt); edns->bits = ldns_buffer_read_u16(pkt); - /* ignore rdata and rrsigs */ + parse_ednsdata(ldns_buffer_current(pkt), edns); return 0; } diff --git a/util/data/msgparse.h b/util/data/msgparse.h index b0c665e2a..606880dbe 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -211,7 +211,7 @@ struct edns_data { uint16_t bits; /** UDP reassembly size. */ uint16_t udp_size; - int subnet_option_add; + int subnet_option; /*YBS*/ uint16_t subnet_addr_fam; uint8_t subnet_source_mask; uint8_t subnet_scope_mask; diff --git a/validator/autotrust.c b/validator/autotrust.c index 149f3f889..0e1988e56 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -2133,7 +2133,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp) edns.ext_rcode = 0; edns.edns_version = 0; edns.bits = EDNS_DO; - edns.subnet_option_add = 0; + edns.subnet_option = 0; if(ldns_buffer_capacity(buf) < 65535) edns.udp_size = (uint16_t)ldns_buffer_capacity(buf); else edns.udp_size = 65535;