mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-09-10 03:47:42 +02:00
- Merge EDNS Client subnet implementation from feature branch into main branch,
using new EDNS processing framework. git-svn-id: file:///svn/unbound/trunk@4074 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
@@ -1070,8 +1070,13 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
|
||||
sldns_buffer_write_u16(pend->buffer, qinfo->qclass);
|
||||
sldns_buffer_flip(pend->buffer);
|
||||
if(1) {
|
||||
/* add edns */
|
||||
struct edns_data edns;
|
||||
if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen,
|
||||
zone, zonelen, qstate, qstate->region)) {
|
||||
free(pend);
|
||||
return NULL;
|
||||
}
|
||||
/* add edns */
|
||||
edns.edns_present = 1;
|
||||
edns.ext_rcode = 0;
|
||||
edns.edns_version = EDNS_ADVERTISED_VERSION;
|
||||
|
||||
@@ -77,6 +77,7 @@ testbound_usage(void)
|
||||
printf("-2 detect SHA256 support (exit code 0 or 1)\n");
|
||||
printf("-g detect GOST support (exit code 0 or 1)\n");
|
||||
printf("-e detect ECDSA support (exit code 0 or 1)\n");
|
||||
printf("-c detect CLIENT_SUBNET support (exit code 0 or 1)\n");
|
||||
printf("-s testbound self-test - unit test of testbound parts.\n");
|
||||
printf("-o str unbound commandline options separated by spaces.\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
@@ -325,6 +326,15 @@ main(int argc, char* argv[])
|
||||
#else
|
||||
printf("GOST not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
case 'c':
|
||||
#ifdef CLIENT_SUBNET
|
||||
printf("CLIENT_SUBNET supported\n");
|
||||
exit(0);
|
||||
#else
|
||||
printf("CLIENT_SUBNET not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
case 'p':
|
||||
|
||||
+109
-16
@@ -98,6 +98,7 @@ entry_add_reply(struct entry* entry)
|
||||
pkt->packet_sleep = 0;
|
||||
pkt->reply_pkt = NULL;
|
||||
pkt->reply_from_hex = NULL;
|
||||
pkt->raw_ednsdata = NULL;
|
||||
/* link at end */
|
||||
while(*p)
|
||||
p = &((*p)->next);
|
||||
@@ -134,6 +135,8 @@ static void matchline(char* line, struct entry* e)
|
||||
e->match_do = 1;
|
||||
} else if(str_keyword(&parse, "noedns")) {
|
||||
e->match_noedns = 1;
|
||||
} else if(str_keyword(&parse, "ednsdata")) {
|
||||
e->match_ednsdata_raw = 1;
|
||||
} else if(str_keyword(&parse, "UDP")) {
|
||||
e->match_transport = transport_udp;
|
||||
} else if(str_keyword(&parse, "TCP")) {
|
||||
@@ -230,6 +233,8 @@ static void adjustline(char* line, struct entry* e,
|
||||
e->copy_id = 1;
|
||||
} else if(str_keyword(&parse, "copy_query")) {
|
||||
e->copy_query = 1;
|
||||
} else if(str_keyword(&parse, "copy_ednsdata_assume_clientsubnet")) {
|
||||
e->copy_ednsdata_assume_clientsubnet = 1;
|
||||
} else if(str_keyword(&parse, "sleep=")) {
|
||||
e->sleeptime = (unsigned int) strtol(parse, (char**)&parse, 10);
|
||||
while(isspace((unsigned char)*parse))
|
||||
@@ -267,6 +272,7 @@ static struct entry* new_entry(void)
|
||||
e->reply_list = NULL;
|
||||
e->copy_id = 0;
|
||||
e->copy_query = 0;
|
||||
e->copy_ednsdata_assume_clientsubnet = 0;
|
||||
e->sleeptime = 0;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
@@ -484,25 +490,28 @@ static void add_rr(char* rrstr, uint8_t* pktbuf, size_t pktsize,
|
||||
else error("internal error bad section %d", (int)add_section);
|
||||
}
|
||||
|
||||
/* add EDNS 4096 DO opt record */
|
||||
/* add EDNS 4096 opt record */
|
||||
static void
|
||||
add_do_flag(uint8_t* pktbuf, size_t pktsize, size_t* pktlen)
|
||||
add_edns(uint8_t* pktbuf, size_t pktsize, int do_flag, uint8_t *ednsdata,
|
||||
uint16_t ednslen, size_t* pktlen)
|
||||
{
|
||||
uint8_t edns[] = {0x00, /* root label */
|
||||
0x00, LDNS_RR_TYPE_OPT, /* type */
|
||||
0x10, 0x00, /* class is UDPSIZE 4096 */
|
||||
0x00, /* TTL[0] is ext rcode */
|
||||
0x00, /* TTL[1] is edns version */
|
||||
0x80, 0x00, /* TTL[2-3] is edns flags, DO */
|
||||
0x00, 0x00 /* rdatalength (0 options) */
|
||||
(uint8_t)(do_flag?0x80:0x00), 0x00, /* TTL[2-3] is edns flags, DO */
|
||||
(uint8_t)((ednslen >> 8) & 0xff),
|
||||
(uint8_t)(ednslen & 0xff), /* rdatalength */
|
||||
};
|
||||
if(*pktlen < LDNS_HEADER_SIZE)
|
||||
return;
|
||||
if(*pktlen + sizeof(edns) > pktsize)
|
||||
if(*pktlen + sizeof(edns) + ednslen > pktsize)
|
||||
error("not enough space for EDNS OPT record");
|
||||
memmove(pktbuf+*pktlen, edns, sizeof(edns));
|
||||
memmove(pktbuf+*pktlen+sizeof(edns), ednsdata, ednslen);
|
||||
sldns_write_uint16(pktbuf+10, LDNS_ARCOUNT(pktbuf)+1);
|
||||
*pktlen += sizeof(edns);
|
||||
*pktlen += (sizeof(edns) + ednslen);
|
||||
}
|
||||
|
||||
/* Reads one entry from file. Returns entry or NULL on error. */
|
||||
@@ -516,7 +525,9 @@ read_entry(FILE* in, const char* name, struct sldns_file_parse_state* pstate,
|
||||
sldns_pkt_section add_section = LDNS_SECTION_QUESTION;
|
||||
struct reply_packet *cur_reply = NULL;
|
||||
int reading_hex = 0;
|
||||
int reading_hex_ednsdata = 0;
|
||||
sldns_buffer* hex_data_buffer = NULL;
|
||||
sldns_buffer* hex_ednsdata_buffer = NULL;
|
||||
uint8_t pktbuf[MAX_PACKETLEN];
|
||||
size_t pktlen = LDNS_HEADER_SIZE;
|
||||
int do_flag = 0; /* DO flag in EDNS */
|
||||
@@ -583,21 +594,45 @@ read_entry(FILE* in, const char* name, struct sldns_file_parse_state* pstate,
|
||||
cur_reply->reply_from_hex = hex_buffer2wire(hex_data_buffer);
|
||||
sldns_buffer_free(hex_data_buffer);
|
||||
hex_data_buffer = NULL;
|
||||
} else if(reading_hex) {
|
||||
sldns_buffer_printf(hex_data_buffer, "%s", line);
|
||||
} else if(str_keyword(&parse, "HEX_EDNSDATA_BEGIN")) {
|
||||
hex_ednsdata_buffer = sldns_buffer_new(MAX_PACKETLEN);
|
||||
reading_hex_ednsdata = 1;
|
||||
} 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, pstate->lineno);
|
||||
}
|
||||
reading_hex_ednsdata = 0;
|
||||
cur_reply->raw_ednsdata = hex_buffer2wire(hex_ednsdata_buffer);
|
||||
sldns_buffer_free(hex_ednsdata_buffer);
|
||||
hex_ednsdata_buffer = NULL;
|
||||
} else if(reading_hex_ednsdata) {
|
||||
sldns_buffer_printf(hex_ednsdata_buffer, "%s", line);
|
||||
} else if(str_keyword(&parse, "ENTRY_END")) {
|
||||
if(hex_data_buffer)
|
||||
sldns_buffer_free(hex_data_buffer);
|
||||
if(hex_ednsdata_buffer)
|
||||
sldns_buffer_free(hex_ednsdata_buffer);
|
||||
if(pktlen != 0) {
|
||||
if(do_flag)
|
||||
add_do_flag(pktbuf, sizeof(pktbuf),
|
||||
&pktlen);
|
||||
if(do_flag || cur_reply->raw_ednsdata) {
|
||||
if(cur_reply->raw_ednsdata &&
|
||||
sldns_buffer_limit(cur_reply->raw_ednsdata))
|
||||
add_edns(pktbuf, sizeof(pktbuf), do_flag,
|
||||
sldns_buffer_begin(cur_reply->raw_ednsdata),
|
||||
(uint16_t)sldns_buffer_limit(cur_reply->raw_ednsdata),
|
||||
&pktlen);
|
||||
else
|
||||
add_edns(pktbuf, sizeof(pktbuf), do_flag,
|
||||
NULL, 0, &pktlen);
|
||||
}
|
||||
cur_reply->reply_pkt = memdup(pktbuf, pktlen);
|
||||
cur_reply->reply_len = pktlen;
|
||||
if(!cur_reply->reply_pkt)
|
||||
error("out of memory");
|
||||
}
|
||||
return current;
|
||||
} else if(reading_hex) {
|
||||
sldns_buffer_printf(hex_data_buffer, "%s", line);
|
||||
} else {
|
||||
add_rr(skip_whitespace?parse:line, pktbuf,
|
||||
sizeof(pktbuf), &pktlen, pstate, add_section,
|
||||
@@ -605,10 +640,14 @@ read_entry(FILE* in, const char* name, struct sldns_file_parse_state* pstate,
|
||||
}
|
||||
|
||||
}
|
||||
if (reading_hex) {
|
||||
if(reading_hex) {
|
||||
error("%s: End of file reached while still reading hex, "
|
||||
"missing HEX_ANSWER_END\n", name);
|
||||
}
|
||||
if(reading_hex_ednsdata) {
|
||||
error("%s: End of file reached while still reading edns data, "
|
||||
"missing HEX_EDNSDATA_END\n", name);
|
||||
}
|
||||
if(current) {
|
||||
error("%s: End of file reached while reading entry. "
|
||||
"missing ENTRY_END\n", name);
|
||||
@@ -778,16 +817,16 @@ pkt_find_edns_opt(uint8_t** p, size_t* plen)
|
||||
wlen -= LDNS_HEADER_SIZE;
|
||||
|
||||
/* skip other records with wire2str_scan */
|
||||
for(i=0; i < LDNS_QDCOUNT(p); i++)
|
||||
for(i=0; i < LDNS_QDCOUNT(*p); i++)
|
||||
(void)sldns_wire2str_rrquestion_scan(&w, &wlen, &snull, &sl,
|
||||
*p, *plen);
|
||||
for(i=0; i < LDNS_ANCOUNT(p); i++)
|
||||
for(i=0; i < LDNS_ANCOUNT(*p); i++)
|
||||
(void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen);
|
||||
for(i=0; i < LDNS_NSCOUNT(p); i++)
|
||||
for(i=0; i < LDNS_NSCOUNT(*p); i++)
|
||||
(void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen);
|
||||
|
||||
/* walk through additional section */
|
||||
for(i=0; i < LDNS_ARCOUNT(p); i++) {
|
||||
for(i=0; i < LDNS_ARCOUNT(*p); i++) {
|
||||
/* if this is OPT then done */
|
||||
uint8_t* dstart = w;
|
||||
size_t dlen = wlen;
|
||||
@@ -1338,6 +1377,31 @@ static int subdomain_dname(uint8_t* q, size_t qlen, uint8_t* p, size_t plen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Match OPT RDATA (not the EDNS payload size or flags) */
|
||||
static int
|
||||
match_ednsdata(uint8_t* q, size_t qlen, uint8_t* p, size_t plen)
|
||||
{
|
||||
uint8_t* walk_q = q;
|
||||
size_t walk_qlen = qlen;
|
||||
uint8_t* walk_p = p;
|
||||
size_t walk_plen = plen;
|
||||
|
||||
if(!pkt_find_edns_opt(&walk_q, &walk_qlen))
|
||||
walk_qlen = 0;
|
||||
if(!pkt_find_edns_opt(&walk_p, &walk_plen))
|
||||
walk_plen = 0;
|
||||
|
||||
/* class + ttl + rdlen = 8 */
|
||||
if(walk_qlen <= 8 && walk_plen <= 8) {
|
||||
verbose(3, "NO edns opt, move on");
|
||||
return 1;
|
||||
}
|
||||
if(walk_qlen != walk_plen)
|
||||
return 0;
|
||||
|
||||
return (memcmp(walk_p+8, walk_q+8, walk_qlen-8) == 0);
|
||||
}
|
||||
|
||||
/* finds entry in list, or returns NULL */
|
||||
struct entry*
|
||||
find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
|
||||
@@ -1409,6 +1473,11 @@ find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
|
||||
verbose(3, "bad; EDNS OPT present\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_ednsdata_raw &&
|
||||
!match_ednsdata(query_pkt, len, reply, rlen)) {
|
||||
verbose(3, "bad EDNS data match.\n");
|
||||
continue;
|
||||
}
|
||||
if(p->match_transport != transport_any && p->match_transport != transport) {
|
||||
verbose(3, "bad transport\n");
|
||||
continue;
|
||||
@@ -1494,6 +1563,29 @@ adjust_packet(struct entry* match, uint8_t** answer_pkt, size_t *answer_len,
|
||||
if(match->copy_id && reslen >= 1)
|
||||
res[0] = orig[0];
|
||||
|
||||
if(match->copy_ednsdata_assume_clientsubnet) {
|
||||
/** Assume there is only one EDNS option, which is ECS.
|
||||
* Copy source mask from query to scope mask in reply. Assume
|
||||
* rest of ECS data in response (eg address) matches the query.
|
||||
*/
|
||||
uint8_t* walk_q = orig;
|
||||
size_t walk_qlen = origlen;
|
||||
uint8_t* walk_p = res;
|
||||
size_t walk_plen = reslen;
|
||||
|
||||
if(!pkt_find_edns_opt(&walk_q, &walk_qlen)) {
|
||||
walk_qlen = 0;
|
||||
}
|
||||
if(!pkt_find_edns_opt(&walk_p, &walk_plen)) {
|
||||
walk_plen = 0;
|
||||
}
|
||||
/* class + ttl + rdlen + optcode + optlen + ecs fam + ecs source
|
||||
* + ecs scope = index 15 */
|
||||
if(walk_qlen >= 15 && walk_plen >= 15) {
|
||||
walk_p[15] = walk_q[14];
|
||||
}
|
||||
}
|
||||
|
||||
if(match->sleeptime > 0) {
|
||||
verbose(3, "sleeping for %d seconds\n", match->sleeptime);
|
||||
#ifdef HAVE_SLEEP
|
||||
@@ -1587,6 +1679,7 @@ void delete_replylist(struct reply_packet* replist)
|
||||
np = p->next;
|
||||
free(p->reply_pkt);
|
||||
sldns_buffer_free(p->reply_from_hex);
|
||||
sldns_buffer_free(p->raw_ednsdata);
|
||||
free(p);
|
||||
p=np;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ struct sldns_file_parse_state;
|
||||
; 'rcode' makes the query match the rcode from the reply
|
||||
; 'question' makes the query match the question section
|
||||
; 'answer' makes the query match the answer section
|
||||
; 'ednsdata' matches queries to HEX_EDNS section.
|
||||
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
|
||||
MATCH [UDP|TCP] DO
|
||||
MATCH ...
|
||||
@@ -87,6 +88,11 @@ struct sldns_file_parse_state;
|
||||
; 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
|
||||
|
||||
|
||||
@@ -147,6 +153,8 @@ struct reply_packet {
|
||||
uint8_t* reply_pkt;
|
||||
/** length of reply pkt */
|
||||
size_t reply_len;
|
||||
/** Additional EDNS data for matching queries. */
|
||||
struct sldns_buffer* raw_ednsdata;
|
||||
/** or reply pkt in hex if not parsable */
|
||||
struct sldns_buffer* reply_from_hex;
|
||||
/** seconds to sleep before giving packet */
|
||||
@@ -182,6 +190,8 @@ struct entry {
|
||||
uint8_t match_do;
|
||||
/** match absence of EDNS OPT record in query */
|
||||
uint8_t match_noedns;
|
||||
/** match edns data field given in hex */
|
||||
uint8_t match_ednsdata_raw;
|
||||
/** match query serial with this value. */
|
||||
uint32_t ixfr_soa_serial;
|
||||
/** match on UDP/TCP */
|
||||
@@ -195,6 +205,9 @@ struct entry {
|
||||
uint8_t copy_id;
|
||||
/** copy the query nametypeclass from query into the answer */
|
||||
uint8_t copy_query;
|
||||
/** copy ednsdata to reply, assume it is clientsubnet and
|
||||
* adjust scopemask to match sourcemask */
|
||||
uint8_t copy_ednsdata_assume_clientsubnet;
|
||||
/** in seconds */
|
||||
unsigned int sleeptime;
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* testcode/unitecs.c - unit test for ecs routines.
|
||||
*
|
||||
* Copyright (c) 2013, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Calls ecs related unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef CLIENT_SUBNET
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/module.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "edns-subnet/addrtree.h"
|
||||
#include "edns-subnet/subnetmod.h"
|
||||
|
||||
/*
|
||||
void printkey(addrkey_t *k, addrlen_t bits)
|
||||
{
|
||||
int byte;
|
||||
int bytes = bits/8 + ((bits%8)>0);
|
||||
char msk = 0xFF;
|
||||
for (byte = 0; byte < bytes; byte++) {
|
||||
//~ if (byte+1 == bytes)
|
||||
//~ msk = 0xFF<<(8-bits%8);
|
||||
printf("%02x ", k[byte]&msk);
|
||||
}
|
||||
}
|
||||
|
||||
void print_tree(struct addrnode* node, int indent, int maxdepth)
|
||||
{
|
||||
struct addredge* edge;
|
||||
int i, s, byte;
|
||||
if (indent == 0) printf("-----Tree-----\n");
|
||||
if (indent > maxdepth) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
printf("[node elem:%d] (%d)\n", node->elem != NULL, node);
|
||||
for (i = 0; i<2; i++) {
|
||||
if (node->edge[i]) {
|
||||
for (s = 0; s < indent; s++) printf(" ");
|
||||
printkey(node->edge[i]->str, node->edge[i]->len);
|
||||
printf("(len %d bits, %d bytes) ", node->edge[i]->len,
|
||||
node->edge[i]->len/8 + ((node->edge[i]->len%8)>0));
|
||||
print_tree(node->edge[i]->node, indent+1, maxdepth);
|
||||
}
|
||||
}
|
||||
if (indent == 0) printf("-----Tree-----");
|
||||
}
|
||||
*/
|
||||
|
||||
/* what should we check?
|
||||
* X - is it balanced? (a node with 1 child shoudl not have
|
||||
* a node with 1 child MUST have elem
|
||||
* child must be sub of parent
|
||||
* edge must be longer than parent edge
|
||||
* */
|
||||
static int addrtree_inconsistent_subtree(struct addrtree* tree,
|
||||
struct addredge* parent_edge, addrlen_t depth)
|
||||
{
|
||||
struct addredge* edge;
|
||||
struct addrnode* node = parent_edge->node;
|
||||
int childcount, i, r;
|
||||
if (depth > tree->max_depth) return 15;
|
||||
childcount = (node->edge[0] != NULL) + (node->edge[1] != NULL);
|
||||
/* Only nodes with 2 children should possibly have no element. */
|
||||
if (childcount < 2 && !node->elem) return 10;
|
||||
for (i = 0; i<2; i++) {
|
||||
edge = node->edge[i];
|
||||
if (!edge) continue;
|
||||
if (!edge->node) return 11;
|
||||
if (!edge->str) return 12;
|
||||
if (edge->len <= parent_edge->len) return 13;
|
||||
if (!unittest_wrapper_addrtree_issub(parent_edge->str,
|
||||
parent_edge->len, edge->str, edge->len, 0))
|
||||
return 14;
|
||||
if ((r = addrtree_inconsistent_subtree(tree, edge, depth+1)) != 0)
|
||||
return 100+r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int addrtree_inconsistent(struct addrtree* tree)
|
||||
{
|
||||
struct addredge* edge;
|
||||
int i, r;
|
||||
|
||||
if (!tree) return 0;
|
||||
if (!tree->root) return 1;
|
||||
|
||||
for (i = 0; i<2; i++) {
|
||||
edge = tree->root->edge[i];
|
||||
if (!edge) continue;
|
||||
if (!edge->node) return 3;
|
||||
if (!edge->str) return 4;
|
||||
if ((r = addrtree_inconsistent_subtree(tree, edge, 1)) != 0)
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static addrlen_t randomkey(addrkey_t **k, int maxlen)
|
||||
{
|
||||
int byte;
|
||||
int bits = rand() % maxlen;
|
||||
int bytes = bits/8 + (bits%8>0); /*ceil*/
|
||||
*k = (addrkey_t *) malloc(bytes * sizeof(addrkey_t));
|
||||
for (byte = 0; byte < bytes; byte++) {
|
||||
(*k)[byte] = (addrkey_t)(rand() & 0xFF);
|
||||
}
|
||||
return (addrlen_t)bits;
|
||||
}
|
||||
|
||||
static void elemfree(void *envptr, void *elemptr)
|
||||
{
|
||||
struct reply_info *elem = (struct reply_info *)elemptr;
|
||||
(void)envptr;
|
||||
free(elem);
|
||||
}
|
||||
|
||||
static void consistency_test(void)
|
||||
{
|
||||
addrlen_t l;
|
||||
time_t i;
|
||||
unsigned int count;
|
||||
addrkey_t *k;
|
||||
struct addrtree* t;
|
||||
struct module_env env;
|
||||
struct reply_info *elem;
|
||||
time_t timenow = 0;
|
||||
unit_show_func("edns-subnet/addrtree.h", "Tree consistency check");
|
||||
srand(9195); /* just some value for reproducibility */
|
||||
|
||||
t = addrtree_create(100, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 0);
|
||||
count = t->node_count;
|
||||
unit_assert(count == 0);
|
||||
for (i = 0; i < 1000; i++) {
|
||||
l = randomkey(&k, 128);
|
||||
elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
|
||||
addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow);
|
||||
/* This should always hold because no items ever expire. They
|
||||
* could be overwritten, though. */
|
||||
unit_assert( count <= t->node_count );
|
||||
count = t->node_count;
|
||||
free(k);
|
||||
unit_assert( !addrtree_inconsistent(t) );
|
||||
}
|
||||
addrtree_delete(t);
|
||||
|
||||
unit_show_func("edns-subnet/addrtree.h", "Tree consistency with purge");
|
||||
t = addrtree_create(8, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 0);
|
||||
unit_assert(t->node_count == 0);
|
||||
for (i = 0; i < 1000; i++) {
|
||||
l = randomkey(&k, 128);
|
||||
elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
|
||||
addrtree_insert(t, k, l, 64, elem, i + 10, i);
|
||||
free(k);
|
||||
unit_assert( !addrtree_inconsistent(t) );
|
||||
}
|
||||
addrtree_delete(t);
|
||||
|
||||
unit_show_func("edns-subnet/addrtree.h", "Tree consistency with limit");
|
||||
t = addrtree_create(8, &elemfree, &unittest_wrapper_subnetmod_sizefunc, &env, 27);
|
||||
unit_assert(t->node_count == 0);
|
||||
for (i = 0; i < 1000; i++) {
|
||||
l = randomkey(&k, 128);
|
||||
elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
|
||||
addrtree_insert(t, k, l, 64, elem, i + 10, i);
|
||||
unit_assert( t->node_count <= 27);
|
||||
free(k);
|
||||
unit_assert( !addrtree_inconsistent(t) );
|
||||
}
|
||||
addrtree_delete(t);
|
||||
}
|
||||
|
||||
static void issub_test(void)
|
||||
{
|
||||
addrkey_t k1[] = {0x55, 0x55, 0x5A};
|
||||
addrkey_t k2[] = {0x55, 0x5D, 0x5A};
|
||||
unit_show_func("edns-subnet/addrtree.h", "issub");
|
||||
unit_assert( !unittest_wrapper_addrtree_issub(k1, 24, k2, 24, 0) );
|
||||
unit_assert( unittest_wrapper_addrtree_issub(k1, 8, k2, 16, 0) );
|
||||
unit_assert( unittest_wrapper_addrtree_issub(k2, 12, k1, 13, 0) );
|
||||
unit_assert( !unittest_wrapper_addrtree_issub(k1, 16, k2, 12, 0) );
|
||||
unit_assert( unittest_wrapper_addrtree_issub(k1, 12, k2, 12, 0) );
|
||||
unit_assert( !unittest_wrapper_addrtree_issub(k1, 13, k2, 13, 0) );
|
||||
unit_assert( unittest_wrapper_addrtree_issub(k1, 24, k2, 24, 13) );
|
||||
unit_assert( !unittest_wrapper_addrtree_issub(k1, 24, k2, 20, 13) );
|
||||
unit_assert( unittest_wrapper_addrtree_issub(k1, 20, k2, 24, 13) );
|
||||
}
|
||||
|
||||
static void getbit_test(void)
|
||||
{
|
||||
addrkey_t k1[] = {0x55, 0x55, 0x5A};
|
||||
int i;
|
||||
unit_show_func("edns-subnet/addrtree.h", "getbit");
|
||||
for(i = 0; i<20; i++) {
|
||||
unit_assert( unittest_wrapper_addrtree_getbit(k1, 20, (addrlen_t)i) == (i&1) );
|
||||
}
|
||||
}
|
||||
|
||||
static void bits_common_test(void)
|
||||
{
|
||||
addrkey_t k1[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
|
||||
addrkey_t k2[] = {0,0,0,0,0,0,0,0};
|
||||
addrlen_t i;
|
||||
|
||||
unit_show_func("edns-subnet/addrtree.h", "bits_common");
|
||||
for(i = 0; i<64; i++) {
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k1, 64, i) == 64 );
|
||||
}
|
||||
for(i = 0; i<8; i++) {
|
||||
k2[i] = k1[i]^(1<<i);
|
||||
}
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 0) == 0*8+7 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 8) == 1*8+6 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 16) == 2*8+5 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 24) == 3*8+4 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 32) == 4*8+3 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 40) == 5*8+2 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 48) == 6*8+1 );
|
||||
unit_assert( unittest_wrapper_addrtree_bits_common(k1, 64, k2, 64, 56) == 7*8+0 );
|
||||
}
|
||||
|
||||
static void cmpbit_test(void)
|
||||
{
|
||||
addrkey_t k1[] = {0xA5, 0x0F};
|
||||
addrkey_t k2[] = {0x5A, 0xF0};
|
||||
addrlen_t i;
|
||||
|
||||
unit_show_func("edns-subnet/addrtree.h", "cmpbit");
|
||||
for(i = 0; i<16; i++) {
|
||||
unit_assert( !unittest_wrapper_addrtree_cmpbit(k1,k1,i) );
|
||||
unit_assert( unittest_wrapper_addrtree_cmpbit(k1,k2,i) );
|
||||
}
|
||||
}
|
||||
|
||||
void ecs_test(void)
|
||||
{
|
||||
unit_show_feature("ecs");
|
||||
cmpbit_test();
|
||||
bits_common_test();
|
||||
getbit_test();
|
||||
issub_test();
|
||||
consistency_test();
|
||||
}
|
||||
#endif /* CLIENT_SUBNET */
|
||||
|
||||
@@ -880,6 +880,9 @@ main(int argc, char* argv[])
|
||||
infra_test();
|
||||
ldns_test();
|
||||
msgparse_test();
|
||||
#ifdef CLIENT_SUBNET
|
||||
ecs_test();
|
||||
#endif /* CLIENT_SUBNET */
|
||||
checklock_stop();
|
||||
printf("%d checks ok.\n", testcount);
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
@@ -72,6 +72,10 @@ void verify_test(void);
|
||||
void neg_test(void);
|
||||
/** unit test for regional allocator functions */
|
||||
void regional_test(void);
|
||||
#ifdef CLIENT_SUBNET
|
||||
/** Unit test for ECS functions */
|
||||
void ecs_test(void);
|
||||
#endif /* CLIENT_SUBNET */
|
||||
/** unit test for ldns functions */
|
||||
void ldns_test(void);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user