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)
{
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
*/
#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;
{
}
-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)
{
}
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))
{
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) {
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 */
};
-/**
- * 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.
} 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;
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 {
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,
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));
#endif
};
-#ifdef CLIENT_SUBNET
+
/**
* Create outside_network structure with N udp ports.
* @param base: the communication base to use for event handling.
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.
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 */
}
#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)
{
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