diff options
Diffstat (limited to '')
-rw-r--r-- | external/unbound/testcode/testpkts.c | 308 |
1 files changed, 289 insertions, 19 deletions
diff --git a/external/unbound/testcode/testpkts.c b/external/unbound/testcode/testpkts.c index d1960a410..e1a7768ab 100644 --- a/external/unbound/testcode/testpkts.c +++ b/external/unbound/testcode/testpkts.c @@ -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); @@ -118,6 +119,12 @@ static void matchline(char* line, struct entry* e) e->match_qtype = 1; } else if(str_keyword(&parse, "qname")) { e->match_qname = 1; + } else if(str_keyword(&parse, "rcode")) { + e->match_rcode = 1; + } else if(str_keyword(&parse, "question")) { + e->match_question = 1; + } else if(str_keyword(&parse, "answer")) { + e->match_answer = 1; } else if(str_keyword(&parse, "subdomain")) { e->match_subdomain = 1; } else if(str_keyword(&parse, "all")) { @@ -128,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")) { @@ -224,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)) @@ -239,7 +250,7 @@ static void adjustline(char* line, struct entry* e, } /** create new entry */ -static struct entry* new_entry() +static struct entry* new_entry(void) { struct entry* e = (struct entry*)malloc(sizeof(struct entry)); if(!e) error("out of memory"); @@ -247,6 +258,9 @@ static struct entry* new_entry() e->match_opcode = 0; e->match_qtype = 0; e->match_qname = 0; + e->match_rcode = 0; + e->match_question = 0; + e->match_answer = 0; e->match_subdomain = 0; e->match_all = 0; e->match_ttl = 0; @@ -258,6 +272,7 @@ static struct entry* new_entry() 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; @@ -475,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. */ @@ -507,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 */ @@ -574,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, @@ -596,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); @@ -691,6 +739,14 @@ static int get_opcode(uint8_t* pkt, size_t pktlen) return (int)LDNS_OPCODE_WIRE(pkt); } +/** returns rcode from packet */ +static int get_rcode(uint8_t* pkt, size_t pktlen) +{ + if(pktlen < LDNS_HEADER_SIZE) + return 0; + return (int)LDNS_RCODE_WIRE(pkt); +} + /** get authority section SOA serial value */ static uint32_t get_serial(uint8_t* p, size_t plen) { @@ -761,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; @@ -802,8 +858,8 @@ get_do_flag(uint8_t* pkt, size_t len) uint16_t edns_bits; uint8_t* walk = pkt; size_t walk_len = len; - if(pkt_find_edns_opt(&walk, &walk_len)) { - return 1; + if(!pkt_find_edns_opt(&walk, &walk_len)) { + return 0; } if(walk_len < 6) return 0; /* malformed */ @@ -1086,6 +1142,138 @@ static void lowercase_pkt(uint8_t* pkt, size_t pktlen) } } +/** match question section of packet */ +static int +match_question(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl) +{ + char* qstr, *pstr, *s, *qcmpstr, *pcmpstr; + uint8_t* qb = q, *pb = p; + int r; + /* zero TTLs */ + qb = memdup(q, qlen); + pb = memdup(p, plen); + if(!qb || !pb) error("out of memory"); + if(!mttl) { + zerottls(qb, qlen); + zerottls(pb, plen); + } + lowercase_pkt(qb, qlen); + lowercase_pkt(pb, plen); + qstr = sldns_wire2str_pkt(qb, qlen); + pstr = sldns_wire2str_pkt(pb, plen); + if(!qstr || !pstr) error("cannot pkt2string"); + + /* remove before ;; QUESTION */ + s = strstr(qstr, ";; QUESTION SECTION"); + qcmpstr = s; + s = strstr(pstr, ";; QUESTION SECTION"); + pcmpstr = s; + if(!qcmpstr && !pcmpstr) { + free(qstr); + free(pstr); + free(qb); + free(pb); + return 1; + } + if(!qcmpstr || !pcmpstr) { + free(qstr); + free(pstr); + free(qb); + free(pb); + return 0; + } + + /* remove after answer section, (;; AUTH, ;; ADD, ;; MSG size ..) */ + s = strstr(qcmpstr, ";; ANSWER SECTION"); + if(!s) s = strstr(qcmpstr, ";; AUTHORITY SECTION"); + if(!s) s = strstr(qcmpstr, ";; ADDITIONAL SECTION"); + if(!s) s = strstr(qcmpstr, ";; MSG SIZE"); + if(s) *s = 0; + s = strstr(pcmpstr, ";; ANSWER SECTION"); + if(!s) s = strstr(pcmpstr, ";; AUTHORITY SECTION"); + if(!s) s = strstr(pcmpstr, ";; ADDITIONAL SECTION"); + if(!s) s = strstr(pcmpstr, ";; MSG SIZE"); + if(s) *s = 0; + + r = (strcmp(qcmpstr, pcmpstr) == 0); + + if(!r) { + verbose(3, "mismatch question section '%s' and '%s'", + qcmpstr, pcmpstr); + } + + free(qstr); + free(pstr); + free(qb); + free(pb); + return r; +} + +/** match answer section of packet */ +static int +match_answer(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl) +{ + char* qstr, *pstr, *s, *qcmpstr, *pcmpstr; + uint8_t* qb = q, *pb = p; + int r; + /* zero TTLs */ + qb = memdup(q, qlen); + pb = memdup(p, plen); + if(!qb || !pb) error("out of memory"); + if(!mttl) { + zerottls(qb, qlen); + zerottls(pb, plen); + } + lowercase_pkt(qb, qlen); + lowercase_pkt(pb, plen); + qstr = sldns_wire2str_pkt(qb, qlen); + pstr = sldns_wire2str_pkt(pb, plen); + if(!qstr || !pstr) error("cannot pkt2string"); + + /* remove before ;; ANSWER */ + s = strstr(qstr, ";; ANSWER SECTION"); + qcmpstr = s; + s = strstr(pstr, ";; ANSWER SECTION"); + pcmpstr = s; + if(!qcmpstr && !pcmpstr) { + free(qstr); + free(pstr); + free(qb); + free(pb); + return 1; + } + if(!qcmpstr || !pcmpstr) { + free(qstr); + free(pstr); + free(qb); + free(pb); + return 0; + } + + /* remove after answer section, (;; AUTH, ;; ADD, ;; MSG size ..) */ + s = strstr(qcmpstr, ";; AUTHORITY SECTION"); + if(!s) s = strstr(qcmpstr, ";; ADDITIONAL SECTION"); + if(!s) s = strstr(qcmpstr, ";; MSG SIZE"); + if(s) *s = 0; + s = strstr(pcmpstr, ";; AUTHORITY SECTION"); + if(!s) s = strstr(pcmpstr, ";; ADDITIONAL SECTION"); + if(!s) s = strstr(pcmpstr, ";; MSG SIZE"); + if(s) *s = 0; + + r = (strcmp(qcmpstr, pcmpstr) == 0); + + if(!r) { + verbose(3, "mismatch answer section '%s' and '%s'", + qcmpstr, pcmpstr); + } + + free(qstr); + free(pstr); + free(qb); + free(pb); + return r; +} + /** match all of the packet */ int match_all(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl, @@ -1128,6 +1316,9 @@ match_all(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl, /* check for reordered sections */ r = match_noloc(qstr, pstr, q, qlen, p, plen); } + if(!r) { + verbose(3, "mismatch pkt '%s' and '%s'", qstr, pstr); + } free(qstr); free(pstr); free(qb); @@ -1186,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, @@ -1214,6 +1430,31 @@ find_match(struct entry* entries, uint8_t* query_pkt, size_t len, continue; } } + if(p->match_rcode) { + if(get_rcode(query_pkt, len) != get_rcode(reply, rlen)) { + char *r1 = sldns_wire2str_rcode(get_rcode(query_pkt, len)); + char *r2 = sldns_wire2str_rcode(get_rcode(reply, rlen)); + verbose(3, "bad rcode %s instead of %s\n", + r1, r2); + free(r1); + free(r2); + continue; + } + } + if(p->match_question) { + if(!match_question(query_pkt, len, reply, rlen, + (int)p->match_ttl)) { + verbose(3, "bad question section\n"); + continue; + } + } + if(p->match_answer) { + if(!match_answer(query_pkt, len, reply, rlen, + (int)p->match_ttl)) { + verbose(3, "bad answer section\n"); + continue; + } + } if(p->match_subdomain) { if(!subdomain_dname(query_pkt, len, reply, rlen)) { verbose(3, "bad subdomain\n"); @@ -1232,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; @@ -1317,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 @@ -1410,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; } |