diff options
Diffstat (limited to 'external/unbound/testcode/testpkts.c')
-rw-r--r-- | external/unbound/testcode/testpkts.c | 1697 |
1 files changed, 0 insertions, 1697 deletions
diff --git a/external/unbound/testcode/testpkts.c b/external/unbound/testcode/testpkts.c deleted file mode 100644 index e1a7768ab..000000000 --- a/external/unbound/testcode/testpkts.c +++ /dev/null @@ -1,1697 +0,0 @@ -/* - * testpkts. Data file parse for test packets, and query matching. - * - * Data storage for specially crafted replies for testing purposes. - * - * (c) NLnet Labs, 2005, 2006, 2007, 2008 - * See the file LICENSE for the license - */ - -/** - * \file - * This is a debugging aid. It is not efficient, especially - * with a long config file, but it can give any reply to any query. - * This can help the developer pre-script replies for queries. - * - * You can specify a packet RR by RR with header flags to return. - * - * Missing features: - * - matching content different from reply content. - * - find way to adjust mangled packets? - */ - -#include "config.h" -struct sockaddr_storage; -#include <errno.h> -#include <stdarg.h> -#include <ctype.h> -#include "testcode/testpkts.h" -#include "util/net_help.h" -#include "sldns/sbuffer.h" -#include "sldns/rrdef.h" -#include "sldns/pkthdr.h" -#include "sldns/str2wire.h" -#include "sldns/wire2str.h" - -/** max size of a packet */ -#define MAX_PACKETLEN 65536 -/** max line length */ -#define MAX_LINE 10240 -/** string to show in warnings and errors */ -static const char* prog_name = "testpkts"; - -#ifndef UTIL_LOG_H -/** verbosity definition for compat */ -enum verbosity_value { NO_VERBOSE=0 }; -#endif -/** logging routine, provided by caller */ -void verbose(enum verbosity_value lvl, const char* msg, ...) ATTR_FORMAT(printf, 2, 3); - -/** print error and exit */ -static void error(const char* msg, ...) -{ - va_list args; - va_start(args, msg); - fprintf(stderr, "%s error: ", prog_name); - vfprintf(stderr, msg, args); - fprintf(stderr, "\n"); - fflush(stderr); - va_end(args); - exit(EXIT_FAILURE); -} - -/** return if string is empty or comment */ -static int isendline(char c) -{ - if(c == ';' || c == '#' - || c == '\n' || c == 0) - return 1; - return 0; -} - -/** true if the string starts with the keyword given. Moves the str ahead. - * @param str: before keyword, afterwards after keyword and spaces. - * @param keyword: the keyword to match - * @return: true if keyword present. False otherwise, and str unchanged. -*/ -static int str_keyword(char** str, const char* keyword) -{ - size_t len = strlen(keyword); - assert(str && keyword); - if(strncmp(*str, keyword, len) != 0) - return 0; - *str += len; - while(isspace((unsigned char)**str)) - (*str)++; - return 1; -} - -/** Add reply packet to entry */ -static struct reply_packet* -entry_add_reply(struct entry* entry) -{ - struct reply_packet* pkt = (struct reply_packet*)malloc( - sizeof(struct reply_packet)); - struct reply_packet ** p = &entry->reply_list; - if(!pkt) error("out of memory"); - pkt->next = NULL; - 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); - *p = pkt; - return pkt; -} - -/** parse MATCH line */ -static void matchline(char* line, struct entry* e) -{ - char* parse = line; - while(*parse) { - if(isendline(*parse)) - return; - if(str_keyword(&parse, "opcode")) { - e->match_opcode = 1; - } else if(str_keyword(&parse, "qtype")) { - 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")) { - e->match_all = 1; - } else if(str_keyword(&parse, "ttl")) { - e->match_ttl = 1; - } else if(str_keyword(&parse, "DO")) { - 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")) { - e->match_transport = transport_tcp; - } else if(str_keyword(&parse, "serial")) { - e->match_serial = 1; - if(*parse != '=' && *parse != ':') - error("expected = or : in MATCH: %s", line); - parse++; - e->ixfr_soa_serial = (uint32_t)strtol(parse, (char**)&parse, 10); - while(isspace((unsigned char)*parse)) - parse++; - } else { - error("could not parse MATCH: '%s'", parse); - } - } -} - -/** parse REPLY line */ -static void replyline(char* line, uint8_t* reply, size_t reply_len, - int* do_flag) -{ - char* parse = line; - if(reply_len < LDNS_HEADER_SIZE) error("packet too short for header"); - while(*parse) { - if(isendline(*parse)) - return; - /* opcodes */ - if(str_keyword(&parse, "QUERY")) { - LDNS_OPCODE_SET(reply, LDNS_PACKET_QUERY); - } else if(str_keyword(&parse, "IQUERY")) { - LDNS_OPCODE_SET(reply, LDNS_PACKET_IQUERY); - } else if(str_keyword(&parse, "STATUS")) { - LDNS_OPCODE_SET(reply, LDNS_PACKET_STATUS); - } else if(str_keyword(&parse, "NOTIFY")) { - LDNS_OPCODE_SET(reply, LDNS_PACKET_NOTIFY); - } else if(str_keyword(&parse, "UPDATE")) { - LDNS_OPCODE_SET(reply, LDNS_PACKET_UPDATE); - /* rcodes */ - } else if(str_keyword(&parse, "NOERROR")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NOERROR); - } else if(str_keyword(&parse, "FORMERR")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_FORMERR); - } else if(str_keyword(&parse, "SERVFAIL")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_SERVFAIL); - } else if(str_keyword(&parse, "NXDOMAIN")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NXDOMAIN); - } else if(str_keyword(&parse, "NOTIMPL")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NOTIMPL); - } else if(str_keyword(&parse, "REFUSED")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_REFUSED); - } else if(str_keyword(&parse, "YXDOMAIN")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_YXDOMAIN); - } else if(str_keyword(&parse, "YXRRSET")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_YXRRSET); - } else if(str_keyword(&parse, "NXRRSET")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NXRRSET); - } else if(str_keyword(&parse, "NOTAUTH")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NOTAUTH); - } else if(str_keyword(&parse, "NOTZONE")) { - LDNS_RCODE_SET(reply, LDNS_RCODE_NOTZONE); - /* flags */ - } else if(str_keyword(&parse, "QR")) { - LDNS_QR_SET(reply); - } else if(str_keyword(&parse, "AA")) { - LDNS_AA_SET(reply); - } else if(str_keyword(&parse, "TC")) { - LDNS_TC_SET(reply); - } else if(str_keyword(&parse, "RD")) { - LDNS_RD_SET(reply); - } else if(str_keyword(&parse, "CD")) { - LDNS_CD_SET(reply); - } else if(str_keyword(&parse, "RA")) { - LDNS_RA_SET(reply); - } else if(str_keyword(&parse, "AD")) { - LDNS_AD_SET(reply); - } else if(str_keyword(&parse, "DO")) { - *do_flag = 1; - } else { - error("could not parse REPLY: '%s'", parse); - } - } -} - -/** parse ADJUST line */ -static void adjustline(char* line, struct entry* e, - struct reply_packet* pkt) -{ - char* parse = line; - while(*parse) { - if(isendline(*parse)) - return; - if(str_keyword(&parse, "copy_id")) { - 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)) - parse++; - } else if(str_keyword(&parse, "packet_sleep=")) { - pkt->packet_sleep = (unsigned int) strtol(parse, (char**)&parse, 10); - while(isspace((unsigned char)*parse)) - parse++; - } else { - error("could not parse ADJUST: '%s'", parse); - } - } -} - -/** create new entry */ -static struct entry* new_entry(void) -{ - struct entry* e = (struct entry*)malloc(sizeof(struct entry)); - if(!e) error("out of memory"); - memset(e, 0, sizeof(*e)); - 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; - e->match_do = 0; - e->match_noedns = 0; - e->match_serial = 0; - e->ixfr_soa_serial = 0; - e->match_transport = transport_any; - 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; -} - -/** - * Converts a hex string to binary data - * @param hexstr: string of hex. - * @param len: is the length of the string - * @param buf: is the buffer to store the result in - * @param offset: is the starting position in the result buffer - * @param buf_len: is the length of buf. - * @return This function returns the length of the result - */ -static size_t -hexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len) -{ - char c; - int i; - uint8_t int8 = 0; - int sec = 0; - size_t bufpos = 0; - - if (len % 2 != 0) { - return 0; - } - - for (i=0; i<len; i++) { - c = hexstr[i]; - - /* case insensitive, skip spaces */ - if (c != ' ') { - if (c >= '0' && c <= '9') { - int8 += c & 0x0f; - } else if (c >= 'a' && c <= 'z') { - int8 += (c & 0x0f) + 9; - } else if (c >= 'A' && c <= 'Z') { - int8 += (c & 0x0f) + 9; - } else { - return 0; - } - - if (sec == 0) { - int8 = int8 << 4; - sec = 1; - } else { - if (bufpos + offset + 1 <= buf_len) { - buf[bufpos+offset] = int8; - int8 = 0; - sec = 0; - bufpos++; - } else { - fprintf(stderr, "Buffer too small in hexstr2bin"); - } - } - } - } - return bufpos; -} - -/** convert hex buffer to binary buffer */ -static sldns_buffer * -hex_buffer2wire(sldns_buffer *data_buffer) -{ - sldns_buffer *wire_buffer = NULL; - int c; - - /* stat hack - * 0 = normal - * 1 = comment (skip to end of line) - * 2 = unprintable character found, read binary data directly - */ - size_t data_buf_pos = 0; - int state = 0; - uint8_t *hexbuf; - int hexbufpos = 0; - size_t wirelen; - uint8_t *data_wire = (uint8_t *) sldns_buffer_begin(data_buffer); - uint8_t *wire = (uint8_t*)malloc(MAX_PACKETLEN); - if(!wire) error("out of memory"); - - hexbuf = (uint8_t*)malloc(MAX_PACKETLEN); - if(!hexbuf) error("out of memory"); - for (data_buf_pos = 0; data_buf_pos < sldns_buffer_position(data_buffer); data_buf_pos++) { - c = (int) data_wire[data_buf_pos]; - - if (state < 2 && !isascii((unsigned char)c)) { - /*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/ - state = 2; - } - switch (state) { - case 0: - if ( (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F') ) - { - if (hexbufpos >= MAX_PACKETLEN) { - error("buffer overflow"); - free(hexbuf); - return 0; - - } - hexbuf[hexbufpos] = (uint8_t) c; - hexbufpos++; - } else if (c == ';') { - state = 1; - } else if (c == ' ' || c == '\t' || c == '\n') { - /* skip whitespace */ - } - break; - case 1: - if (c == '\n' || c == EOF) { - state = 0; - } - break; - case 2: - if (hexbufpos >= MAX_PACKETLEN) { - error("buffer overflow"); - free(hexbuf); - return 0; - } - hexbuf[hexbufpos] = (uint8_t) c; - hexbufpos++; - break; - } - } - - if (hexbufpos >= MAX_PACKETLEN) { - /*verbose("packet size reached\n");*/ - } - - /* lenient mode: length must be multiple of 2 */ - if (hexbufpos % 2 != 0) { - if (hexbufpos >= MAX_PACKETLEN) { - error("buffer overflow"); - free(hexbuf); - return 0; - } - hexbuf[hexbufpos] = (uint8_t) '0'; - hexbufpos++; - } - - if (state < 2) { - wirelen = hexstr2bin((char *) hexbuf, hexbufpos, wire, 0, MAX_PACKETLEN); - wire_buffer = sldns_buffer_new(wirelen); - sldns_buffer_new_frm_data(wire_buffer, wire, wirelen); - } else { - error("Incomplete hex data, not at byte boundary\n"); - } - free(wire); - free(hexbuf); - return wire_buffer; -} - -/** parse ORIGIN */ -static void -get_origin(const char* name, struct sldns_file_parse_state* pstate, char* parse) -{ - /* snip off rest of the text so as to make the parse work in ldns */ - char* end; - char store; - int status; - - end=parse; - while(!isspace((unsigned char)*end) && !isendline(*end)) - end++; - store = *end; - *end = 0; - verbose(3, "parsing '%s'\n", parse); - status = sldns_str2wire_dname_buf(parse, pstate->origin, - &pstate->origin_len); - *end = store; - if(status != 0) - error("%s line %d:\n\t%s: %s", name, pstate->lineno, - sldns_get_errorstr_parse(status), parse); -} - -/** add RR to packet */ -static void add_rr(char* rrstr, uint8_t* pktbuf, size_t pktsize, - size_t* pktlen, struct sldns_file_parse_state* pstate, - sldns_pkt_section add_section, const char* fname) -{ - /* it must be a RR, parse and add to packet. */ - size_t rr_len = pktsize - *pktlen; - size_t dname_len = 0; - int status; - uint8_t* origin = pstate->origin_len?pstate->origin:0; - uint8_t* prev = pstate->prev_rr_len?pstate->prev_rr:0; - if(*pktlen > pktsize || *pktlen < LDNS_HEADER_SIZE) - error("packet overflow"); - - /* parse RR */ - if(add_section == LDNS_SECTION_QUESTION) - status = sldns_str2wire_rr_question_buf(rrstr, pktbuf+*pktlen, - &rr_len, &dname_len, origin, pstate->origin_len, - prev, pstate->prev_rr_len); - else status = sldns_str2wire_rr_buf(rrstr, pktbuf+*pktlen, &rr_len, - &dname_len, pstate->default_ttl, origin, - pstate->origin_len, prev, pstate->prev_rr_len); - if(status != 0) - error("%s line %d:%d %s\n\t%s", fname, pstate->lineno, - LDNS_WIREPARSE_OFFSET(status), - sldns_get_errorstr_parse(status), rrstr); - *pktlen += rr_len; - - /* increase RR count */ - if(add_section == LDNS_SECTION_QUESTION) - sldns_write_uint16(pktbuf+4, LDNS_QDCOUNT(pktbuf)+1); - else if(add_section == LDNS_SECTION_ANSWER) - sldns_write_uint16(pktbuf+6, LDNS_ANCOUNT(pktbuf)+1); - else if(add_section == LDNS_SECTION_AUTHORITY) - sldns_write_uint16(pktbuf+8, LDNS_NSCOUNT(pktbuf)+1); - else if(add_section == LDNS_SECTION_ADDITIONAL) - sldns_write_uint16(pktbuf+10, LDNS_ARCOUNT(pktbuf)+1); - else error("internal error bad section %d", (int)add_section); -} - -/* add EDNS 4096 opt record */ -static void -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 */ - (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) + 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) + ednslen); -} - -/* Reads one entry from file. Returns entry or NULL on error. */ -struct entry* -read_entry(FILE* in, const char* name, struct sldns_file_parse_state* pstate, - int skip_whitespace) -{ - struct entry* current = NULL; - char line[MAX_LINE]; - char* parse; - 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 */ - memset(pktbuf, 0, pktlen); /* ID = 0, FLAGS="", and rr counts 0 */ - - while(fgets(line, (int)sizeof(line), in) != NULL) { - line[MAX_LINE-1] = 0; - parse = line; - pstate->lineno++; - - while(isspace((unsigned char)*parse)) - parse++; - /* test for keywords */ - if(isendline(*parse)) - continue; /* skip comment and empty lines */ - if(str_keyword(&parse, "ENTRY_BEGIN")) { - if(current) { - error("%s line %d: previous entry does not ENTRY_END", - name, pstate->lineno); - } - current = new_entry(); - current->lineno = pstate->lineno; - cur_reply = entry_add_reply(current); - continue; - } else if(str_keyword(&parse, "$ORIGIN")) { - get_origin(name, pstate, parse); - continue; - } else if(str_keyword(&parse, "$TTL")) { - pstate->default_ttl = (uint32_t)atoi(parse); - continue; - } - - /* working inside an entry */ - if(!current) { - error("%s line %d: expected ENTRY_BEGIN but got %s", - name, pstate->lineno, line); - } - if(str_keyword(&parse, "MATCH")) { - matchline(parse, current); - } else if(str_keyword(&parse, "REPLY")) { - replyline(parse, pktbuf, pktlen, &do_flag); - } else if(str_keyword(&parse, "ADJUST")) { - adjustline(parse, current, cur_reply); - } else if(str_keyword(&parse, "EXTRA_PACKET")) { - cur_reply = entry_add_reply(current); - } else if(str_keyword(&parse, "SECTION")) { - if(str_keyword(&parse, "QUESTION")) - add_section = LDNS_SECTION_QUESTION; - else if(str_keyword(&parse, "ANSWER")) - add_section = LDNS_SECTION_ANSWER; - else if(str_keyword(&parse, "AUTHORITY")) - add_section = LDNS_SECTION_AUTHORITY; - else if(str_keyword(&parse, "ADDITIONAL")) - add_section = LDNS_SECTION_ADDITIONAL; - else error("%s line %d: bad section %s", name, pstate->lineno, parse); - } else if(str_keyword(&parse, "HEX_ANSWER_BEGIN")) { - hex_data_buffer = sldns_buffer_new(MAX_PACKETLEN); - reading_hex = 1; - } else if(str_keyword(&parse, "HEX_ANSWER_END")) { - if(!reading_hex) { - error("%s line %d: HEX_ANSWER_END read but no HEX_ANSWER_BEGIN keyword seen", name, pstate->lineno); - } - reading_hex = 0; - 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 || 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 { - add_rr(skip_whitespace?parse:line, pktbuf, - sizeof(pktbuf), &pktlen, pstate, add_section, - name); - } - - } - 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); - } - return 0; -} - -/* reads the canned reply file and returns a list of structs */ -struct entry* -read_datafile(const char* name, int skip_whitespace) -{ - struct entry* list = NULL; - struct entry* last = NULL; - struct entry* current = NULL; - FILE *in; - struct sldns_file_parse_state pstate; - int entry_num = 0; - memset(&pstate, 0, sizeof(pstate)); - - if((in=fopen(name, "r")) == NULL) { - error("could not open file %s: %s", name, strerror(errno)); - } - - while((current = read_entry(in, name, &pstate, skip_whitespace))) - { - if(last) - last->next = current; - else list = current; - last = current; - entry_num ++; - } - verbose(1, "%s: Read %d entries\n", prog_name, entry_num); - - fclose(in); - return list; -} - -/** get qtype from packet */ -static sldns_rr_type get_qtype(uint8_t* pkt, size_t pktlen) -{ - uint8_t* d; - size_t dl, sl=0; - char* snull = NULL; - if(pktlen < LDNS_HEADER_SIZE) - return 0; - if(LDNS_QDCOUNT(pkt) == 0) - return 0; - /* skip over dname with dname-scan routine */ - d = pkt+LDNS_HEADER_SIZE; - dl = pktlen-LDNS_HEADER_SIZE; - (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen); - if(dl < 2) - return 0; - return sldns_read_uint16(d); -} - -/** get qtype from packet */ -static size_t get_qname_len(uint8_t* pkt, size_t pktlen) -{ - uint8_t* d; - size_t dl, sl=0; - char* snull = NULL; - if(pktlen < LDNS_HEADER_SIZE) - return 0; - if(LDNS_QDCOUNT(pkt) == 0) - return 0; - /* skip over dname with dname-scan routine */ - d = pkt+LDNS_HEADER_SIZE; - dl = pktlen-LDNS_HEADER_SIZE; - (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen); - return pktlen-dl-LDNS_HEADER_SIZE; -} - -/** returns owner from packet */ -static uint8_t* get_qname(uint8_t* pkt, size_t pktlen) -{ - if(pktlen < LDNS_HEADER_SIZE) - return NULL; - if(LDNS_QDCOUNT(pkt) == 0) - return NULL; - return pkt+LDNS_HEADER_SIZE; -} - -/** returns opcode from packet */ -static int get_opcode(uint8_t* pkt, size_t pktlen) -{ - if(pktlen < LDNS_HEADER_SIZE) - return 0; - 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) -{ - uint8_t* walk = p; - size_t walk_len = plen, sl=0; - char* snull = NULL; - uint16_t i; - - if(walk_len < LDNS_HEADER_SIZE) - return 0; - walk += LDNS_HEADER_SIZE; - walk_len -= LDNS_HEADER_SIZE; - - /* skip other records with wire2str_scan */ - for(i=0; i < LDNS_QDCOUNT(p); i++) - (void)sldns_wire2str_rrquestion_scan(&walk, &walk_len, - &snull, &sl, p, plen); - for(i=0; i < LDNS_ANCOUNT(p); i++) - (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl, - p, plen); - - /* walk through authority section */ - for(i=0; i < LDNS_NSCOUNT(p); i++) { - /* if this is SOA then get serial, skip compressed dname */ - uint8_t* dstart = walk; - size_t dlen = walk_len; - (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl, - p, plen); - if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_SOA) { - /* skip type, class, TTL, rdatalen */ - if(dlen < 10) - return 0; - if(dlen < 10 + (size_t)sldns_read_uint16(dstart+8)) - return 0; - dstart += 10; - dlen -= 10; - /* check third rdf */ - (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, - &sl, p, plen); - (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, - &sl, p, plen); - if(dlen < 4) - return 0; - verbose(3, "found serial %u in msg. ", - (int)sldns_read_uint32(dstart)); - return sldns_read_uint32(dstart); - } - /* move to next RR */ - (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl, - p, plen); - } - return 0; -} - -/** get ptr to EDNS OPT record (and remaining length); behind the type u16 */ -static int -pkt_find_edns_opt(uint8_t** p, size_t* plen) -{ - /* walk over the packet with scan routines */ - uint8_t* w = *p; - size_t wlen = *plen, sl=0; - char* snull = NULL; - uint16_t i; - - if(wlen < LDNS_HEADER_SIZE) - return 0; - w += LDNS_HEADER_SIZE; - wlen -= LDNS_HEADER_SIZE; - - /* skip other records with wire2str_scan */ - 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++) - (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen); - 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++) { - /* if this is OPT then done */ - uint8_t* dstart = w; - size_t dlen = wlen; - (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl, - *p, *plen); - if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_OPT) { - *p = dstart+2; - *plen = dlen-2; - return 1; - } - /* move to next RR */ - (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen); - } - return 0; -} - -/** return true if the packet has EDNS OPT record */ -static int -get_has_edns(uint8_t* pkt, size_t len) -{ - /* use arguments as temporary variables */ - return pkt_find_edns_opt(&pkt, &len); -} - -/** return true if the DO flag is set */ -static int -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 0; - } - if(walk_len < 6) - return 0; /* malformed */ - edns_bits = sldns_read_uint16(walk+4); - return (int)(edns_bits&LDNS_EDNS_MASK_DO_BIT); -} - -/** zero TTLs in packet */ -static void -zerottls(uint8_t* pkt, size_t pktlen) -{ - uint8_t* walk = pkt; - size_t walk_len = pktlen, sl=0; - char* snull = NULL; - uint16_t i; - uint16_t num = LDNS_ANCOUNT(pkt)+LDNS_NSCOUNT(pkt)+LDNS_ARCOUNT(pkt); - if(walk_len < LDNS_HEADER_SIZE) - return; - walk += LDNS_HEADER_SIZE; - walk_len -= LDNS_HEADER_SIZE; - for(i=0; i < LDNS_QDCOUNT(pkt); i++) - (void)sldns_wire2str_rrquestion_scan(&walk, &walk_len, - &snull, &sl, pkt, pktlen); - for(i=0; i < num; i++) { - /* wipe TTL */ - uint8_t* dstart = walk; - size_t dlen = walk_len; - (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl, - pkt, pktlen); - if(dlen < 8) - return; - sldns_write_uint32(dstart+4, 0); - /* go to next RR */ - (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl, - pkt, pktlen); - } -} - -/** get one line (\n) from a string, move next to after the \n, zero \n */ -static int -get_line(char** s, char** n) -{ - /* at end of string? end */ - if(*n == NULL || **n == 0) - return 0; - /* result starts at next string */ - *s = *n; - /* find \n after that */ - *n = strchr(*s, '\n'); - if(*n && **n != 0) { - /* terminate line */ - (*n)[0] = 0; - (*n)++; - } - return 1; -} - -/** match two RR sections without ordering */ -static int -match_noloc_section(char** q, char** nq, char** p, char** np, uint16_t num) -{ - /* for max number of RRs in packet */ - const uint16_t numarray = 3000; - char* qlines[numarray], *plines[numarray]; - uint16_t i, j, numq=0, nump=0; - if(num > numarray) fatal_exit("too many RRs"); - /* gather lines */ - for(i=0; i<num; i++) { - get_line(q, nq); - get_line(p, np); - qlines[numq++] = *q; - plines[nump++] = *p; - } - /* see if they are all present in the other */ - for(i=0; i<num; i++) { - int found = 0; - for(j=0; j<num; j++) { - if(strcmp(qlines[i], plines[j]) == 0) { - found = 1; - break; - } - } - if(!found) { - verbose(3, "comparenoloc: failed for %s", qlines[i]); - return 0; - } - } - return 1; -} - -/** match two strings for unordered equality of RRs and everything else */ -static int -match_noloc(char* q, char* p, uint8_t* q_pkt, size_t q_pkt_len, - uint8_t* p_pkt, size_t p_pkt_len) -{ - char* nq = q, *np = p; - /* if no header, compare bytes */ - if(p_pkt_len < LDNS_HEADER_SIZE || q_pkt_len < LDNS_HEADER_SIZE) { - if(p_pkt_len != q_pkt_len) return 0; - return memcmp(p, q, p_pkt_len); - } - /* compare RR counts */ - if(LDNS_QDCOUNT(p_pkt) != LDNS_QDCOUNT(q_pkt)) - return 0; - if(LDNS_ANCOUNT(p_pkt) != LDNS_ANCOUNT(q_pkt)) - return 0; - if(LDNS_NSCOUNT(p_pkt) != LDNS_NSCOUNT(q_pkt)) - return 0; - if(LDNS_ARCOUNT(p_pkt) != LDNS_ARCOUNT(q_pkt)) - return 0; - /* get a line from both; compare; at sections do section */ - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) { - /* header line opcode, rcode, id */ - return 0; - } - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) { - /* header flags, rr counts */ - return 0; - } - /* ;; QUESTION SECTION */ - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - if(!match_noloc_section(&q, &nq, &p, &np, LDNS_QDCOUNT(p_pkt))) - return 0; - - /* empty line and ;; ANSWER SECTION */ - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - if(!match_noloc_section(&q, &nq, &p, &np, LDNS_ANCOUNT(p_pkt))) - return 0; - - /* empty line and ;; AUTHORITY SECTION */ - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - if(!match_noloc_section(&q, &nq, &p, &np, LDNS_NSCOUNT(p_pkt))) - return 0; - - /* empty line and ;; ADDITIONAL SECTION */ - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - get_line(&q, &nq); - get_line(&p, &np); - if(strcmp(q, p) != 0) return 0; - if(!match_noloc_section(&q, &nq, &p, &np, LDNS_ARCOUNT(p_pkt))) - return 0; - - return 1; -} - -/** lowercase domain name - does not follow compression pointers */ -static void lowercase_dname(uint8_t** p, size_t* remain) -{ - unsigned i, llen; - if(*remain == 0) return; - while(**p != 0) { - /* compressed? */ - if((**p & 0xc0) == 0xc0) { - *p += 2; - *remain -= 2; - return; - } - llen = (unsigned int)**p; - *p += 1; - *remain -= 1; - if(*remain < llen) - llen = (unsigned int)*remain; - for(i=0; i<llen; i++) { - (*p)[i] = (uint8_t)tolower((int)(*p)[i]); - } - *p += llen; - *remain -= llen; - if(*remain == 0) return; - } - /* skip root label */ - *p += 1; - *remain -= 1; -} - -/** lowercase rdata of type */ -static void lowercase_rdata(uint8_t** p, size_t* remain, - uint16_t rdatalen, uint16_t t) -{ - const sldns_rr_descriptor *desc = sldns_rr_descript(t); - uint8_t dname_count = 0; - size_t i = 0; - size_t rdataremain = rdatalen; - if(!desc) { - /* unknown type */ - *p += rdatalen; - *remain -= rdatalen; - return; - } - while(dname_count < desc->_dname_count) { - sldns_rdf_type f = sldns_rr_descriptor_field_type(desc, i++); - if(f == LDNS_RDF_TYPE_DNAME) { - lowercase_dname(p, &rdataremain); - dname_count++; - } else if(f == LDNS_RDF_TYPE_STR) { - uint8_t len; - if(rdataremain == 0) return; - len = **p; - *p += len+1; - rdataremain -= len+1; - } else { - int len = 0; - switch(f) { - case LDNS_RDF_TYPE_CLASS: - case LDNS_RDF_TYPE_ALG: - case LDNS_RDF_TYPE_INT8: - len = 1; - break; - case LDNS_RDF_TYPE_INT16: - case LDNS_RDF_TYPE_TYPE: - case LDNS_RDF_TYPE_CERT_ALG: - len = 2; - break; - case LDNS_RDF_TYPE_INT32: - case LDNS_RDF_TYPE_TIME: - case LDNS_RDF_TYPE_A: - case LDNS_RDF_TYPE_PERIOD: - len = 4; - break; - case LDNS_RDF_TYPE_TSIGTIME: - len = 6; - break; - case LDNS_RDF_TYPE_AAAA: - len = 16; - break; - default: error("bad rdf type in lowercase %d", (int)f); - } - *p += len; - rdataremain -= len; - } - } - /* skip remainder of rdata */ - *p += rdataremain; - *remain -= rdatalen; -} - -/** lowercase all names in the message */ -static void lowercase_pkt(uint8_t* pkt, size_t pktlen) -{ - uint16_t i; - uint8_t* p = pkt; - size_t remain = pktlen; - uint16_t t, rdatalen; - if(pktlen < LDNS_HEADER_SIZE) - return; - p += LDNS_HEADER_SIZE; - remain -= LDNS_HEADER_SIZE; - for(i=0; i<LDNS_QDCOUNT(pkt); i++) { - lowercase_dname(&p, &remain); - if(remain < 4) return; - p += 4; - remain -= 4; - } - for(i=0; i<LDNS_ANCOUNT(pkt)+LDNS_NSCOUNT(pkt)+LDNS_ARCOUNT(pkt); i++) { - lowercase_dname(&p, &remain); - if(remain < 10) return; - t = sldns_read_uint16(p); - rdatalen = sldns_read_uint16(p+8); - p += 10; - remain -= 10; - if(remain < rdatalen) return; - lowercase_rdata(&p, &remain, rdatalen, t); - } -} - -/** 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, - int noloc) -{ - char* qstr, *pstr; - 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"); - r = (strcmp(qstr, pstr) == 0); - if(!r) { - /* remove ;; MSG SIZE (at end of string) */ - char* s = strstr(qstr, ";; MSG SIZE"); - if(s) *s=0; - s = strstr(pstr, ";; MSG SIZE"); - if(s) *s=0; - r = (strcmp(qstr, pstr) == 0); - if(!r && !noloc) { - /* we are going to fail see if it is because of EDNS */ - char* a = strstr(qstr, "; EDNS"); - char* b = strstr(pstr, "; EDNS"); - if( (a&&!b) || (b&&!a) ) { - verbose(3, "mismatch in EDNS\n"); - } - } - } - if(!r && noloc) { - /* 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); - free(pb); - return r; -} - -/** see if domain names are equal */ -static int equal_dname(uint8_t* q, size_t qlen, uint8_t* p, size_t plen) -{ - uint8_t* qn = get_qname(q, qlen); - uint8_t* pn = get_qname(p, plen); - char qs[512], ps[512]; - size_t qslen = sizeof(qs), pslen = sizeof(ps); - char* qss = qs, *pss = ps; - if(!qn || !pn) - return 0; - (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen); - (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen); - return (strcmp(qs, ps) == 0); -} - -/** see if domain names are subdomain q of p */ -static int subdomain_dname(uint8_t* q, size_t qlen, uint8_t* p, size_t plen) -{ - /* we use the tostring routines so as to test unbound's routines - * with something else */ - uint8_t* qn = get_qname(q, qlen); - uint8_t* pn = get_qname(p, plen); - char qs[5120], ps[5120]; - size_t qslen = sizeof(qs), pslen = sizeof(ps); - char* qss = qs, *pss = ps; - if(!qn || !pn) - return 0; - /* decompresses domain names */ - (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen); - (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen); - /* same: false, (strict subdomain check)??? */ - if(strcmp(qs, ps) == 0) - return 1; - /* qs must end in ps, at a dot, without \ in front */ - qslen = strlen(qs); - pslen = strlen(ps); - if(qslen > pslen && strcmp(qs + (qslen-pslen), ps) == 0 && - qslen + 2 >= pslen && /* space for label and dot */ - qs[qslen-pslen-1] == '.') { - unsigned int slashcount = 0; - size_t i = qslen-pslen-2; - while(i>0 && qs[i]=='\\') { - i++; - slashcount++; - } - if(slashcount%1 == 1) return 0; /* . preceded by \ */ - return 1; - } - 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, - enum transport_type transport) -{ - struct entry* p = entries; - uint8_t* reply; - size_t rlen; - for(p=entries; p; p=p->next) { - verbose(3, "comparepkt: "); - reply = p->reply_list->reply_pkt; - rlen = p->reply_list->reply_len; - if(p->match_opcode && get_opcode(query_pkt, len) != - get_opcode(reply, rlen)) { - verbose(3, "bad opcode\n"); - continue; - } - if(p->match_qtype && get_qtype(query_pkt, len) != - get_qtype(reply, rlen)) { - verbose(3, "bad qtype %d %d\n", get_qtype(query_pkt, len), get_qtype(reply, rlen)); - continue; - } - if(p->match_qname) { - if(!equal_dname(query_pkt, len, reply, rlen)) { - verbose(3, "bad qname\n"); - 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"); - continue; - } - } - if(p->match_serial && get_serial(query_pkt, len) != p->ixfr_soa_serial) { - verbose(3, "bad serial\n"); - continue; - } - if(p->match_do && !get_do_flag(query_pkt, len)) { - verbose(3, "no DO bit set\n"); - continue; - } - if(p->match_noedns && get_has_edns(query_pkt, 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; - } - if(p->match_all && !match_all(query_pkt, len, reply, rlen, - (int)p->match_ttl, 0)) { - verbose(3, "bad allmatch\n"); - continue; - } - verbose(3, "match!\n"); - return p; - } - return NULL; -} - -void -adjust_packet(struct entry* match, uint8_t** answer_pkt, size_t *answer_len, - uint8_t* query_pkt, size_t query_len) -{ - uint8_t* orig = *answer_pkt; - size_t origlen = *answer_len; - uint8_t* res; - size_t reslen; - - /* perform the copy; if possible; must be uncompressed */ - if(match->copy_query && origlen >= LDNS_HEADER_SIZE && - query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0 - && LDNS_QDCOUNT(orig)==0) { - /* no qname in output packet, insert it */ - size_t dlen = get_qname_len(query_pkt, query_len); - reslen = origlen + dlen + 4; - res = (uint8_t*)malloc(reslen); - if(!res) { - verbose(1, "out of memory; send without adjust\n"); - return; - } - /* copy the header, query, remainder */ - memcpy(res, orig, LDNS_HEADER_SIZE); - memmove(res+LDNS_HEADER_SIZE, query_pkt+LDNS_HEADER_SIZE, - dlen+4); - memmove(res+LDNS_HEADER_SIZE+dlen+4, orig+LDNS_HEADER_SIZE, - reslen-(LDNS_HEADER_SIZE+dlen+4)); - /* set QDCOUNT */ - sldns_write_uint16(res+4, 1); - } else if(match->copy_query && origlen >= LDNS_HEADER_SIZE && - query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0 - && get_qname_len(orig, origlen) == 0) { - /* QDCOUNT(orig)!=0 but qlen == 0, therefore, an error */ - verbose(1, "error: malformed qname; send without adjust\n"); - res = memdup(orig, origlen); - reslen = origlen; - } else if(match->copy_query && origlen >= LDNS_HEADER_SIZE && - query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0 - && LDNS_QDCOUNT(orig)!=0) { - /* in this case olen != 0 and QDCOUNT(orig)!=0 */ - /* copy query section */ - size_t dlen = get_qname_len(query_pkt, query_len); - size_t olen = get_qname_len(orig, origlen); - reslen = origlen + dlen - olen; - res = (uint8_t*)malloc(reslen); - if(!res) { - verbose(1, "out of memory; send without adjust\n"); - return; - } - /* copy the header, query, remainder */ - memcpy(res, orig, LDNS_HEADER_SIZE); - memmove(res+LDNS_HEADER_SIZE, query_pkt+LDNS_HEADER_SIZE, - dlen+4); - memmove(res+LDNS_HEADER_SIZE+dlen+4, - orig+LDNS_HEADER_SIZE+olen+4, - reslen-(LDNS_HEADER_SIZE+dlen+4)); - } else { - res = memdup(orig, origlen); - reslen = origlen; - } - if(!res) { - verbose(1, "out of memory; send without adjust\n"); - return; - } - /* copy the ID */ - if(match->copy_id && reslen >= 2) - res[1] = orig[1]; - 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 - sleep(match->sleeptime); -#else - Sleep(match->sleeptime * 1000); -#endif - } - *answer_pkt = res; - *answer_len = reslen; -} - -/* - * Parses data buffer to a query, finds the correct answer - * and calls the given function for every packet to send. - */ -void -handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count, - enum transport_type transport, void (*sendfunc)(uint8_t*, size_t, void*), - void* userdata, FILE* verbose_out) -{ - struct reply_packet *p; - uint8_t *outbuf = NULL; - size_t outlen = 0; - struct entry* entry = NULL; - - verbose(1, "query %d: id %d: %s %d bytes: ", ++(*count), - (int)(inlen>=2?LDNS_ID_WIRE(inbuf):0), - (transport==transport_tcp)?"TCP":"UDP", (int)inlen); - if(verbose_out) { - char* out = sldns_wire2str_pkt(inbuf, (size_t)inlen); - printf("%s\n", out); - free(out); - } - - /* fill up answer packet */ - entry = find_match(entries, inbuf, (size_t)inlen, transport); - if(!entry || !entry->reply_list) { - verbose(1, "no answer packet for this query, no reply.\n"); - return; - } - for(p = entry->reply_list; p; p = p->next) - { - verbose(3, "Answer pkt:\n"); - if (p->reply_from_hex) { - /* try to adjust the hex packet, if it can be - * parsed, we can use adjust rules. if not, - * send packet literally */ - /* still try to adjust ID if others fail */ - outlen = sldns_buffer_limit(p->reply_from_hex); - outbuf = sldns_buffer_begin(p->reply_from_hex); - } else { - outbuf = p->reply_pkt; - outlen = p->reply_len; - } - if(!outbuf) { - verbose(1, "out of memory\n"); - return; - } - /* copies outbuf in memory allocation */ - adjust_packet(entry, &outbuf, &outlen, inbuf, (size_t)inlen); - verbose(1, "Answer packet size: %u bytes.\n", (unsigned int)outlen); - if(verbose_out) { - char* out = sldns_wire2str_pkt(outbuf, outlen); - printf("%s\n", out); - free(out); - } - if(p->packet_sleep) { - verbose(3, "sleeping for next packet %d secs\n", - p->packet_sleep); -#ifdef HAVE_SLEEP - sleep(p->packet_sleep); -#else - Sleep(p->packet_sleep * 1000); -#endif - verbose(3, "wakeup for next packet " - "(slept %d secs)\n", p->packet_sleep); - } - sendfunc(outbuf, outlen, userdata); - free(outbuf); - outbuf = NULL; - outlen = 0; - } -} - -/** delete the list of reply packets */ -void delete_replylist(struct reply_packet* replist) -{ - struct reply_packet *p=replist, *np; - while(p) { - np = p->next; - free(p->reply_pkt); - sldns_buffer_free(p->reply_from_hex); - sldns_buffer_free(p->raw_ednsdata); - free(p); - p=np; - } -} - -void delete_entry(struct entry* list) -{ - struct entry *p=list, *np; - while(p) { - np = p->next; - delete_replylist(p->reply_list); - free(p); - p = np; - } -} |