if(!e->qsent) {
return NULL;
}
+ if(e->qstate->mesh_info->reply_list)
+ e->qsent->client = &e->qstate->mesh_info->reply_list->query_reply;
return e;
}
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "services/cache/infra.h"
+#include "services/outbound_list.h"
#include "testcode/replay.h"
#include "testcode/ldns-testpkts.h"
#include "util/log.h"
#include "util/fptr_wlist.h"
+#include "edns-subnet/edns-subnet.h"
#include <signal.h>
struct worker;
struct daemon_remote;
return NULL;
runtime->infra = infra;
outnet->base = base;
+ outnet->edns_subnet_upstreams = edns_subnet_upstreams;
outnet->udp_buff = ldns_buffer_new(bufsize);
if(!outnet->udp_buff)
return NULL;
size_t zonelen, comm_point_callback_t* callback, void* callback_arg,
ldns_buffer* ATTR_UNUSED(buff))
{
+ struct sockaddr_in target_addr;
+ struct mesh_reply* reply_list;
+ struct sockaddr_storage *ss;
+ void* sinaddr;
struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
struct fake_pending* pend = (struct fake_pending*)calloc(1,
sizeof(struct fake_pending));
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
edns.bits = 0;
- edns.subnet_option_add = 0;
+ /* begin EDNS subnet option
+ * 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)
+ pend->client = &reply_list->query_reply;
+
+ /* The testcode does not do networking and thus has no target.
+ * But the subnet code depends on it. Lets pretend 5.0.15.10 is
+ * our target. */
+ inet_pton(AF_INET, "5.0.15.10", &(target_addr.sin_addr));
+ memcpy(&pend->addr, (struct sockaddr_storage*)&target_addr,
+ sizeof(struct sockaddr_storage));
+ pend->addrlen = 16;
+ edns.subnet_option_add = pend->client && upstream_lookup(
+ outnet->edns_subnet_upstreams, &pend->addr, pend->addrlen);
+ if(edns.subnet_option_add) {
+ ss = &pend->client->addr;
+ if(((struct sockaddr_in*)ss)->sin_family == AF_INET) {
+ edns.subnet_addr_fam = IANA_ADDRFAM_IP4;
+ sinaddr = &((struct sockaddr_in*)ss)->sin_addr;
+ memcpy(edns.subnet_addr, (uint8_t *)sinaddr, INET_SIZE);
+ /* YBS TODO: source mask must come from original query if
+ * any. Some default otherwise. But not more than
+ * configured maximum */
+ edns.subnet_source_mask = MAX_CLIENT_SUBNET_IP4;
+ }
+#ifdef INET6
+ else {
+ edns.subnet_addr_fam = IANA_ADDRFAM_IP6;
+ sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr;
+ memcpy(edns.subnet_addr, (uint8_t *)sinaddr, INET6_SIZE);
+ edns.subnet_source_mask = MAX_CLIENT_SUBNET_IP6;
+ }
+#endif
+ edns.subnet_scope_mask = 0;
+ }
+ /* end EDNS subnet option */
if(dnssec)
edns.bits = EDNS_DO;
attach_edns_record(pend->buffer, &edns);
e->match_do = true;
} else if(str_keyword(&parse, "noedns")) {
e->match_noedns = true;
+ } else if(str_keyword(&parse, "ednsdata")) {
+ e->match_ednsdata_raw = true;
} else if(str_keyword(&parse, "UDP")) {
e->match_transport = transport_udp;
} else if(str_keyword(&parse, "TCP")) {
ldns_pkt_section add_section = LDNS_SECTION_QUESTION;
struct reply_packet *cur_reply = NULL;
bool reading_hex = false;
+ bool reading_hex_ednsdata = false;
ldns_buffer* hex_data_buffer = NULL;
+ ldns_buffer* hex_ednsdata_buffer = NULL;
while(fgets(line, (int)sizeof(line), in) != NULL) {
line[MAX_LINE-1] = 0;
reading_hex = false;
cur_reply->reply_from_hex = data_buffer2wire(hex_data_buffer);
ldns_buffer_free(hex_data_buffer);
- } else if(str_keyword(&parse, "ENTRY_END")) {
- return current;
} else if(reading_hex) {
ldns_buffer_printf(hex_data_buffer, line);
+ } else if(str_keyword(&parse, "HEX_EDNSDATA_BEGIN")) {
+ hex_ednsdata_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
+ reading_hex_ednsdata = true;
+ } else if(str_keyword(&parse, "HEX_EDNSDATA_END")) {
+ if (!reading_hex_ednsdata) {
+ error("%s line %d: HEX_EDNSDATA_END read but no"
+ "HEX_EDNSDATA_BEGIN keyword seen", name, *lineno);
+ }
+ reading_hex_ednsdata = false;
+ cur_reply->raw_ednsdata = data_buffer2wire(hex_ednsdata_buffer);
+ ldns_buffer_free(hex_ednsdata_buffer);
+ } else if(reading_hex_ednsdata) {
+ ldns_buffer_printf(hex_ednsdata_buffer, line);
+ } else if(str_keyword(&parse, "ENTRY_END")) {
+ return current;
} else {
/* it must be a RR, parse and add to packet. */
ldns_rr* n = NULL;
return 1;
}
+/** Match q edns data to p raw edns data */
+static int
+match_ednsdata(ldns_pkt* q, struct reply_packet* p)
+{
+ size_t qdlen;
+ ldns_rdf *edns_data;
+ if(!ldns_pkt_edns(q) || !ldns_pkt_edns_data(q)) {
+ verbose(3, "No EDNS data\n");
+ return 0;
+ }
+ edns_data = ldns_pkt_edns_data(q);
+ qdlen = ldns_rdf_size(edns_data);
+ if( qdlen != ldns_buffer_limit(p->raw_ednsdata) ||
+ 0 != memcmp(ldns_rdf_data(edns_data),
+ ldns_buffer_begin(p->raw_ednsdata),
+ qdlen) ) {
+ verbose(3, "EDNS data does not match.\n");
+ return 0;
+ }
+ return 1;
+}
+
/* finds entry in list, or returns NULL */
struct entry*
find_match(struct entry* entries, ldns_pkt* query_pkt,
verbose(3, "bad; EDNS OPT present\n");
continue;
}
+ if(p->match_ednsdata_raw &&
+ !match_ednsdata(query_pkt, p->reply_list)) {
+ verbose(3, "bad EDNS data match.\n");
+ continue;
+ }
if(p->match_transport != transport_any && p->match_transport != transport) {
verbose(3, "bad transport\n");
continue;
; 'ttl' used with all, rrs in packet must also have matching TTLs.
; 'DO' will match only queries with DO bit set.
; 'noedns' matches queries without EDNS OPT records.
+ ; 'ednsdata' matches queries to HEX_EDNS section.
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
MATCH [UDP|TCP] DO
MATCH ...
; be parsed, ADJUST rules for the answer packet
; are ignored. Only copy_id is done.
HEX_ANSWER_END
+ HEX_EDNS_BEGIN ; follow with hex data.
+ ; Raw EDNS data to match against. It must be an
+ ; exact match (all options are matched) and will be
+ ; evaluated only when 'MATCH ednsdata' given.
+ HEX_EDNS_END
ENTRY_END
struct reply_packet* next;
/** the reply pkt */
ldns_pkt* reply;
+ /** Additional EDNS data for matching queries. */
+ ldns_buffer* raw_ednsdata;
/** or reply pkt in hex if not parsable */
ldns_buffer* reply_from_hex;
/** seconds to sleep before giving packet */
bool match_do;
/** match absence of EDNS OPT record in query */
bool match_noedns;
+ /** match edns data field given in hex */
+ bool match_ednsdata_raw;
/** match query serial with this value. */
uint32_t ixfr_soa_serial;
/** match on UDP/TCP */
int serviced;
/** the runtime structure this is part of */
struct replay_runtime* runtime;
+ /** Clients initiating lookup. Not owned by serviced_query */
+ struct comm_reply *client;
};
/**
--- /dev/null
+server:
+ send-client-subnet: 5.0.15.10
+ client-subnet-opc: 20730
+ max-client-subnet-ipv4: 21
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test formatting of edns subnet option
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+a.gtld-servers.net. IN AAAA
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+a.gtld-servers.net. IN AAAA
+SECTION ANSWER
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 5.0.15.10
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 5.0.15.10
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 5.0.15.10
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION ANSWER
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 4.3.2.1
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; query sent to .com server
+STEP 5 CHECK_OUT_QUERY
+ENTRY_BEGIN
+MATCH qname qtype opcode ednsdata
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 50 fa ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 15 00 ; source mask, scopemask
+ 7f 00 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 4.3.2.1
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END
es.udp_size = EDNS_ADVERTISED_SIZE;
es.ext_rcode = 0;
es.bits &= EDNS_DO;
+ es.subnet_option_add = 0;
if(ldns_buffer_limit(buf) + calc_edns_field_size(&es) >
edns->udp_size)
return;