]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix edns subnet so that scope 0 answers only match sourcemask 0
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 2 Aug 2022 12:13:55 +0000 (14:13 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 2 Aug 2022 12:13:55 +0000 (14:13 +0200)
  queries for answers from cache if from a query with sourcemask 0.

doc/Changelog
edns-subnet/addrtree.c
edns-subnet/addrtree.h
edns-subnet/subnetmod.c
testdata/subnet_scopezero.crpl [new file with mode: 0644]

index f457dfc4ba40bb089de417dab31b2cc0727060ca..ecb07508d855d151536b0281eea915cdba4874f0 100644 (file)
@@ -1,3 +1,7 @@
+2 August 2022: Wouter
+       - Fix edns subnet so that scope 0 answers only match sourcemask 0
+         queries for answers from cache if from a query with sourcemask 0.
+
 1 August 2022: Wouter
        - Fix the novel ghost domain issues CVE-2022-30698 and CVE-2022-30699.
        - Tests for ghost domain fixes.
index 180a0227917c50c01cf957689650210a8ba78db9..ebe71b9706e497510987b944f8b63db0512ee248 100644 (file)
@@ -97,6 +97,7 @@ node_create(struct addrtree *tree, void *elem, addrlen_t scope,
        tree->node_count++;
        node->scope = scope;
        node->ttl = ttl;
+       node->only_match_scope_zero = 0;
        node->edge[0] = NULL;
        node->edge[1] = NULL;
        node->parent_edge = NULL;
@@ -155,6 +156,7 @@ clean_node(struct addrtree *tree, struct addrnode *node)
        if (!node->elem) return;
        tree->size_bytes -= tree->sizefunc(node->elem);
        tree->delfunc(tree->env, node->elem);
+       node->only_match_scope_zero = 0;
        node->elem = NULL;
 }
 
@@ -358,7 +360,7 @@ issub(const addrkey_t *s1, addrlen_t l1,
 void
 addrtree_insert(struct addrtree *tree, const addrkey_t *addr, 
        addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, 
-       time_t now)
+       time_t now, int only_match_scope_zero)
 {
        struct addrnode *newnode, *node;
        struct addredge *edge;
@@ -381,6 +383,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        /* update this node's scope and data */
                        clean_node(tree, node);
                        node->ttl = ttl;
+                       node->only_match_scope_zero = only_match_scope_zero;
                        node->elem = elem;
                        node->scope = scope;
                        tree->size_bytes += tree->sizefunc(elem);
@@ -447,6 +450,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        newnode->elem = elem;
                        newnode->scope = scope;
                        newnode->ttl = ttl;
+                       newnode->only_match_scope_zero = only_match_scope_zero;
                } 
                
                tree->size_bytes += node_size(tree, newnode);
@@ -483,7 +487,8 @@ addrtree_find(struct addrtree *tree, const addrkey_t *addr,
                /* Current node more specific then question. */
                log_assert(depth <= sourcemask);
                /* does this node have data? if yes, see if we have a match */
-               if (node->elem && node->ttl >= now) {
+               if (node->elem && node->ttl >= now &&
+                       !(sourcemask != 0 && node->only_match_scope_zero)) {
                        /* saved at wrong depth */;
                        log_assert(node->scope >= depth);
                        if (depth == node->scope ||
index 1aea54e01f79544b3583f5d286e5112eab91f7fe..0bc1837cdb8072126a54f8ae8bce3f0abb76c559 100644 (file)
@@ -95,6 +95,10 @@ struct addrnode {
        time_t ttl;
        /** Number of significant bits in address. */
        addrlen_t scope;
+       /** Only use the element for queries for subnet/0. Set if the query
+        * for /0 was answered with scope 0. For query /x answer scope 0,
+        * they can match anything and this is false. */
+       int only_match_scope_zero;
        /** A node can have 0-2 edges, set to NULL for unused */
        struct addredge *edge[2];
        /** edge between this node and parent */
@@ -157,11 +161,12 @@ void addrtree_delete(struct addrtree *tree);
  * @param scope: Number of significant bits in addr.
  * @param elem: data to store in the tree.
  * @param ttl: elem is valid up to this time, seconds.
+ * @param only_match_scope_zero: set for when query /0 has scope /0 answer.
  * @param now: Current time in seconds.
  */
 void addrtree_insert(struct addrtree *tree, const addrkey_t *addr, 
        addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, 
-       time_t now);
+       time_t now, int only_match_scope_zero);
 
 /**
  * Find a node containing an element in the tree.
index 75446113b742a46a12650f53f1d7da9aa66926f5..d4f61bdd6e7f938e3c0a2eb3be1304efc9191448 100644 (file)
@@ -55,6 +55,7 @@
 #include "util/config_file.h"
 #include "util/data/msgreply.h"
 #include "sldns/sbuffer.h"
+#include "sldns/wire2str.h"
 #include "iterator/iter_utils.h"
 
 /** externally called */
@@ -331,6 +332,7 @@ update_cache(struct module_qstate *qstate, int id)
        struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
        struct ecs_data *edns = &sq->ecs_client_in;
        size_t i;
+       int only_match_scope_zero;
 
        /* We already calculated hash upon lookup (lookup_and_reply) if we were
         * allowed to look in the ECS cache */
@@ -392,9 +394,12 @@ update_cache(struct module_qstate *qstate, int id)
        reply_info_set_ttls(rep, *qstate->env->now);
        rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
        rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache   */
+       if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
+               only_match_scope_zero = 1;
+       else only_match_scope_zero = 0;
        addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, 
                edns->subnet_source_mask, sq->max_scope, rep,
-               rep->ttl, *qstate->env->now);
+               rep->ttl, *qstate->env->now, only_match_scope_zero);
 
        lock_rw_unlock(&lru_entry->lock);
        if (need_to_insert) {
@@ -674,6 +679,24 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
        return 1;
 }
 
+/** verbose print edns subnet option in pretty print */
+static void
+subnet_log_print(const char* s, struct edns_option* ecs_opt)
+{
+       if(verbosity >= VERB_ALGO) {
+               char buf[256];
+               char* str = buf;
+               size_t str_len = sizeof(buf);
+               if(!ecs_opt) {
+                       verbose(VERB_ALGO, "%s (null)", s);
+                       return;
+               }
+               (void)sldns_wire2str_edns_subnet_print(&str, &str_len,
+                       ecs_opt->opt_data, ecs_opt->opt_len);
+               verbose(VERB_ALGO, "%s %s", s, buf);
+       }
+}
+
 int
 ecs_edns_back_parsed(struct module_qstate* qstate, int id,
        void* ATTR_UNUSED(cbargs))
@@ -688,6 +711,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
                qstate->env->cfg->client_subnet_opcode)) &&
                parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
                sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
+                       subnet_log_print("answer has edns subnet", ecs_opt);
                        /* Only skip global cache store if we sent an ECS option
                         * and received one back. Answers from non-whitelisted
                         * servers will end up in global cache. Answers for
@@ -736,6 +760,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
                                qstate->ext_state[id] = module_finished;
                                return;
                        }
+                       subnet_log_print("query has edns subnet", ecs_opt);
                        sq->subnet_downstream = 1;
                }
                else if(qstate->mesh_info->reply_list) {
@@ -775,6 +800,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
                                subnet_ecs_opt_list_append(&sq->ecs_client_out,
                                        &qstate->edns_opts_front_out, qstate,
                                        qstate->region);
+                               if(verbosity >= VERB_ALGO) {
+                                       subnet_log_print("reply has edns subnet",
+                                               edns_opt_list_find(
+                                               qstate->edns_opts_front_out,
+                                               qstate->env->cfg->
+                                               client_subnet_opcode));
+                               }
                                return;
                        }
                        lock_rw_unlock(&sne->biglock);
@@ -823,6 +855,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
                        subnet_ecs_opt_list_append(&sq->ecs_client_out,
                                &qstate->edns_opts_front_out, qstate,
                                qstate->region);
+                       if(verbosity >= VERB_ALGO) {
+                               subnet_log_print("reply has edns subnet",
+                                       edns_opt_list_find(
+                                       qstate->edns_opts_front_out,
+                                       qstate->env->cfg->
+                                       client_subnet_opcode));
+                       }
                }
                qstate->no_cache_store = sq->started_no_cache_store;
                qstate->no_cache_lookup = sq->started_no_cache_lookup;
diff --git a/testdata/subnet_scopezero.crpl b/testdata/subnet_scopezero.crpl
new file mode 100644 (file)
index 0000000..e006514
--- /dev/null
@@ -0,0 +1,439 @@
+; scope of 0, if the query also had scope of 0, do not answer this
+; to everyone, but only for scope 0 queries. Otherwise can answer cached.
+
+server:
+       target-fetch-policy: "0 0 0 0 0"
+       send-client-subnet: 1.2.3.4
+       module-config: "subnetcache validator iterator"
+       verbosity: 4
+       qname-minimisation: no
+
+stub-zone:
+       name: "."
+       stub-addr: 193.0.14.129
+
+stub-zone:
+       name: "example.com"
+       stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test subnet cache with scope zero queries and responses.
+
+; the upstream server.
+RANGE_BEGIN 0 100
+       ADDRESS 193.0.14.129
+
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+       ;; we expect to receive empty
+HEX_EDNSDATA_END
+K.ROOT-SERVERS.NET.     IN      A       193.0.14.129
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 0 11
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.0.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 00 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 20 31
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.0.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 01 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 40 51
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+       00 08           ; OPC
+       00 04           ; option length
+       00 01           ; Family
+       00 00           ; source mask, scopemask
+                       ; address 0.0.0.0/0 scope 0
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 120 131
+       ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 00           ; source mask, scopemask
+       7f 02 00        ; address 127.2.0.0/24 scope 0
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+; query for 127.0.0.0/24
+STEP 1 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 00 00                        ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.0.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 00 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.1.0.0/24
+STEP 20 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 01 00                        ;127.1.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.1.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 01 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 0.0.0.0/0
+STEP 40 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 08
+
+       00 08 00 04                     ; OPC, optlen
+       00 01 00 00                     ; ip4, scope 0, source 0
+                                       ;0.0.0.0/0
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+       00 08           ; OPC
+       00 04           ; option length
+       00 01           ; Family
+       00 00           ; source mask, scopemask
+                       ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.0.0.0/24, again, it should be in cache.
+; and not from the scope 0 answer.
+STEP 60 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 00 00                        ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.0.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 00 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.1.0.0/24, again, it should be in cache.
+STEP 80 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 01 00                        ;127.1.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17
+STEP 90 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.1.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 11           ; source mask, scopemask
+       7f 01 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 0.0.0.0/0, again.
+STEP 100 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 08
+
+       00 08 00 04                     ; OPC, optlen
+       00 01 00 00                     ; ip4, scope 0, source 0
+                                       ;0.0.0.0/0
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0
+STEP 110 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+       00 08           ; OPC
+       00 04           ; option length
+       00 01           ; Family
+       00 00           ; source mask, scopemask
+                       ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; now a query for a /24 that gets an answer for a /0.
+STEP 120 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 02 00                        ;127.2.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0
+STEP 130 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.2.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 00           ; source mask, scopemask
+       7f 02 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; the scope 0 answer is now used to answer queries from
+; query for 127.0.0.0/24
+STEP 140 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+       00 00 01 00 00 01 00 00         ;ID 0
+       00 00 00 01 03 77 77 77         ; www.example.com A? (DO)
+       07 65 78 61 6d 70 6c 65
+       03 63 6f 6d 00 00 01 00
+       01 00 00 29 10 00 00 00
+       80 00 00 0b
+
+       00 08 00 07                     ; OPC, optlen
+       00 01 18 00                     ; ip4, scope 24, source 0
+       7f 00 00                        ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+STEP 150 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A   10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+                       ; client is 127.0.0.1
+       00 08           ; OPC
+       00 07           ; option length
+       00 01           ; Family
+       18 00           ; source mask, scopemask
+       7f 00 00        ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+SCENARIO_END