1712b2f30Ssthen /*
2712b2f30Ssthen * testpkts. Data file parse for test packets, and query matching.
3712b2f30Ssthen *
4712b2f30Ssthen * Data storage for specially crafted replies for testing purposes.
5712b2f30Ssthen *
6712b2f30Ssthen * (c) NLnet Labs, 2005, 2006, 2007, 2008
7712b2f30Ssthen * See the file LICENSE for the license
8712b2f30Ssthen */
9712b2f30Ssthen
10712b2f30Ssthen /**
11712b2f30Ssthen * \file
12712b2f30Ssthen * This is a debugging aid. It is not efficient, especially
13712b2f30Ssthen * with a long config file, but it can give any reply to any query.
14712b2f30Ssthen * This can help the developer pre-script replies for queries.
15712b2f30Ssthen *
16712b2f30Ssthen * You can specify a packet RR by RR with header flags to return.
17712b2f30Ssthen *
18712b2f30Ssthen * Missing features:
19712b2f30Ssthen * - matching content different from reply content.
20712b2f30Ssthen * - find way to adjust mangled packets?
21712b2f30Ssthen */
22712b2f30Ssthen
23712b2f30Ssthen #include "config.h"
24712b2f30Ssthen #include <errno.h>
25712b2f30Ssthen #include <stdarg.h>
26712b2f30Ssthen #include <ctype.h>
27712b2f30Ssthen #include "testcode/testpkts.h"
28712b2f30Ssthen #include "util/net_help.h"
29712b2f30Ssthen #include "sldns/sbuffer.h"
30712b2f30Ssthen #include "sldns/rrdef.h"
31712b2f30Ssthen #include "sldns/pkthdr.h"
32712b2f30Ssthen #include "sldns/str2wire.h"
33712b2f30Ssthen #include "sldns/wire2str.h"
34712b2f30Ssthen
35712b2f30Ssthen /** max size of a packet */
36712b2f30Ssthen #define MAX_PACKETLEN 65536
37712b2f30Ssthen /** max line length */
38712b2f30Ssthen #define MAX_LINE 10240
39712b2f30Ssthen /** string to show in warnings and errors */
40712b2f30Ssthen static const char* prog_name = "testpkts";
41712b2f30Ssthen
42712b2f30Ssthen #ifndef UTIL_LOG_H
43712b2f30Ssthen /** verbosity definition for compat */
44712b2f30Ssthen enum verbosity_value { NO_VERBOSE=0 };
45712b2f30Ssthen #endif
46712b2f30Ssthen /** logging routine, provided by caller */
47712b2f30Ssthen void verbose(enum verbosity_value lvl, const char* msg, ...) ATTR_FORMAT(printf, 2, 3);
488771e50fSsthen static void error(const char* msg, ...) ATTR_NORETURN;
49712b2f30Ssthen
50712b2f30Ssthen /** print error and exit */
error(const char * msg,...)51712b2f30Ssthen static void error(const char* msg, ...)
52712b2f30Ssthen {
53712b2f30Ssthen va_list args;
54712b2f30Ssthen va_start(args, msg);
55712b2f30Ssthen fprintf(stderr, "%s error: ", prog_name);
56712b2f30Ssthen vfprintf(stderr, msg, args);
57712b2f30Ssthen fprintf(stderr, "\n");
58712b2f30Ssthen fflush(stderr);
59712b2f30Ssthen va_end(args);
60712b2f30Ssthen exit(EXIT_FAILURE);
61712b2f30Ssthen }
62712b2f30Ssthen
63712b2f30Ssthen /** return if string is empty or comment */
isendline(char c)64712b2f30Ssthen static int isendline(char c)
65712b2f30Ssthen {
66712b2f30Ssthen if(c == ';' || c == '#'
67712b2f30Ssthen || c == '\n' || c == 0)
68712b2f30Ssthen return 1;
69712b2f30Ssthen return 0;
70712b2f30Ssthen }
71712b2f30Ssthen
72712b2f30Ssthen /** true if the string starts with the keyword given. Moves the str ahead.
73712b2f30Ssthen * @param str: before keyword, afterwards after keyword and spaces.
74712b2f30Ssthen * @param keyword: the keyword to match
75712b2f30Ssthen * @return: true if keyword present. False otherwise, and str unchanged.
76712b2f30Ssthen */
str_keyword(char ** str,const char * keyword)77712b2f30Ssthen static int str_keyword(char** str, const char* keyword)
78712b2f30Ssthen {
79712b2f30Ssthen size_t len = strlen(keyword);
80712b2f30Ssthen assert(str && keyword);
81712b2f30Ssthen if(strncmp(*str, keyword, len) != 0)
82712b2f30Ssthen return 0;
83712b2f30Ssthen *str += len;
84712b2f30Ssthen while(isspace((unsigned char)**str))
85712b2f30Ssthen (*str)++;
86712b2f30Ssthen return 1;
87712b2f30Ssthen }
88712b2f30Ssthen
89712b2f30Ssthen /** Add reply packet to entry */
90712b2f30Ssthen static struct reply_packet*
entry_add_reply(struct entry * entry)91712b2f30Ssthen entry_add_reply(struct entry* entry)
92712b2f30Ssthen {
93712b2f30Ssthen struct reply_packet* pkt = (struct reply_packet*)malloc(
94712b2f30Ssthen sizeof(struct reply_packet));
95712b2f30Ssthen struct reply_packet ** p = &entry->reply_list;
96712b2f30Ssthen if(!pkt) error("out of memory");
97712b2f30Ssthen pkt->next = NULL;
98712b2f30Ssthen pkt->packet_sleep = 0;
99712b2f30Ssthen pkt->reply_pkt = NULL;
100712b2f30Ssthen pkt->reply_from_hex = NULL;
101712b2f30Ssthen pkt->raw_ednsdata = NULL;
102712b2f30Ssthen /* link at end */
103712b2f30Ssthen while(*p)
104712b2f30Ssthen p = &((*p)->next);
105712b2f30Ssthen *p = pkt;
106712b2f30Ssthen return pkt;
107712b2f30Ssthen }
108712b2f30Ssthen
109712b2f30Ssthen /** parse MATCH line */
matchline(char * line,struct entry * e)110712b2f30Ssthen static void matchline(char* line, struct entry* e)
111712b2f30Ssthen {
112712b2f30Ssthen char* parse = line;
113712b2f30Ssthen while(*parse) {
114712b2f30Ssthen if(isendline(*parse))
115712b2f30Ssthen return;
116712b2f30Ssthen if(str_keyword(&parse, "opcode")) {
117712b2f30Ssthen e->match_opcode = 1;
118712b2f30Ssthen } else if(str_keyword(&parse, "qtype")) {
119712b2f30Ssthen e->match_qtype = 1;
120712b2f30Ssthen } else if(str_keyword(&parse, "qname")) {
121712b2f30Ssthen e->match_qname = 1;
122712b2f30Ssthen } else if(str_keyword(&parse, "rcode")) {
123712b2f30Ssthen e->match_rcode = 1;
124712b2f30Ssthen } else if(str_keyword(&parse, "question")) {
125712b2f30Ssthen e->match_question = 1;
126712b2f30Ssthen } else if(str_keyword(&parse, "answer")) {
127712b2f30Ssthen e->match_answer = 1;
128712b2f30Ssthen } else if(str_keyword(&parse, "subdomain")) {
129712b2f30Ssthen e->match_subdomain = 1;
130c2862f41Ssthen } else if(str_keyword(&parse, "all_noedns")) {
131c2862f41Ssthen e->match_all_noedns = 1;
132712b2f30Ssthen } else if(str_keyword(&parse, "all")) {
133712b2f30Ssthen e->match_all = 1;
134712b2f30Ssthen } else if(str_keyword(&parse, "ttl")) {
135712b2f30Ssthen e->match_ttl = 1;
136712b2f30Ssthen } else if(str_keyword(&parse, "DO")) {
137712b2f30Ssthen e->match_do = 1;
138712b2f30Ssthen } else if(str_keyword(&parse, "noedns")) {
139712b2f30Ssthen e->match_noedns = 1;
140712b2f30Ssthen } else if(str_keyword(&parse, "ednsdata")) {
141712b2f30Ssthen e->match_ednsdata_raw = 1;
142437d2860Ssthen } else if(str_keyword(&parse, "client_cookie")) {
143437d2860Ssthen e->match_client_cookie = 1;
144437d2860Ssthen } else if(str_keyword(&parse, "server_cookie")) {
145437d2860Ssthen e->match_server_cookie = 1;
146712b2f30Ssthen } else if(str_keyword(&parse, "UDP")) {
147712b2f30Ssthen e->match_transport = transport_udp;
148712b2f30Ssthen } else if(str_keyword(&parse, "TCP")) {
149712b2f30Ssthen e->match_transport = transport_tcp;
150712b2f30Ssthen } else if(str_keyword(&parse, "serial")) {
151712b2f30Ssthen e->match_serial = 1;
152712b2f30Ssthen if(*parse != '=' && *parse != ':')
153712b2f30Ssthen error("expected = or : in MATCH: %s", line);
154712b2f30Ssthen parse++;
155712b2f30Ssthen e->ixfr_soa_serial = (uint32_t)strtol(parse, (char**)&parse, 10);
156712b2f30Ssthen while(isspace((unsigned char)*parse))
157712b2f30Ssthen parse++;
158c2862f41Ssthen } else if(str_keyword(&parse, "ede")) {
159c2862f41Ssthen e->match_ede = 1;
160c2862f41Ssthen if(*parse != '=' && *parse != ':')
161c2862f41Ssthen error("expected = or : in MATCH: %s", line);
162c2862f41Ssthen parse++;
163c2862f41Ssthen while(isspace((unsigned char)*parse))
164c2862f41Ssthen parse++;
165c2862f41Ssthen if(str_keyword(&parse, "any")) {
166c2862f41Ssthen e->match_ede_any = 1;
167c2862f41Ssthen } else {
168c2862f41Ssthen e->ede_info_code = (uint16_t)strtol(parse,
169c2862f41Ssthen (char**)&parse, 10);
170c2862f41Ssthen }
171c2862f41Ssthen while(isspace((unsigned char)*parse))
172c2862f41Ssthen parse++;
173712b2f30Ssthen } else {
174712b2f30Ssthen error("could not parse MATCH: '%s'", parse);
175712b2f30Ssthen }
176712b2f30Ssthen }
177712b2f30Ssthen }
178712b2f30Ssthen
179712b2f30Ssthen /** parse REPLY line */
replyline(char * line,uint8_t * reply,size_t reply_len,int * do_flag)180712b2f30Ssthen static void replyline(char* line, uint8_t* reply, size_t reply_len,
181712b2f30Ssthen int* do_flag)
182712b2f30Ssthen {
183712b2f30Ssthen char* parse = line;
184712b2f30Ssthen if(reply_len < LDNS_HEADER_SIZE) error("packet too short for header");
185712b2f30Ssthen while(*parse) {
186712b2f30Ssthen if(isendline(*parse))
187712b2f30Ssthen return;
188712b2f30Ssthen /* opcodes */
189712b2f30Ssthen if(str_keyword(&parse, "QUERY")) {
190712b2f30Ssthen LDNS_OPCODE_SET(reply, LDNS_PACKET_QUERY);
191712b2f30Ssthen } else if(str_keyword(&parse, "IQUERY")) {
192712b2f30Ssthen LDNS_OPCODE_SET(reply, LDNS_PACKET_IQUERY);
193712b2f30Ssthen } else if(str_keyword(&parse, "STATUS")) {
194712b2f30Ssthen LDNS_OPCODE_SET(reply, LDNS_PACKET_STATUS);
195712b2f30Ssthen } else if(str_keyword(&parse, "NOTIFY")) {
196712b2f30Ssthen LDNS_OPCODE_SET(reply, LDNS_PACKET_NOTIFY);
197712b2f30Ssthen } else if(str_keyword(&parse, "UPDATE")) {
198712b2f30Ssthen LDNS_OPCODE_SET(reply, LDNS_PACKET_UPDATE);
199712b2f30Ssthen /* rcodes */
200712b2f30Ssthen } else if(str_keyword(&parse, "NOERROR")) {
201712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NOERROR);
202712b2f30Ssthen } else if(str_keyword(&parse, "FORMERR")) {
203712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_FORMERR);
204712b2f30Ssthen } else if(str_keyword(&parse, "SERVFAIL")) {
205712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_SERVFAIL);
206712b2f30Ssthen } else if(str_keyword(&parse, "NXDOMAIN")) {
207712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NXDOMAIN);
208712b2f30Ssthen } else if(str_keyword(&parse, "NOTIMPL")) {
209712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NOTIMPL);
210712b2f30Ssthen } else if(str_keyword(&parse, "REFUSED")) {
211712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_REFUSED);
212712b2f30Ssthen } else if(str_keyword(&parse, "YXDOMAIN")) {
213712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_YXDOMAIN);
214712b2f30Ssthen } else if(str_keyword(&parse, "YXRRSET")) {
215712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_YXRRSET);
216712b2f30Ssthen } else if(str_keyword(&parse, "NXRRSET")) {
217712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NXRRSET);
218712b2f30Ssthen } else if(str_keyword(&parse, "NOTAUTH")) {
219712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NOTAUTH);
220712b2f30Ssthen } else if(str_keyword(&parse, "NOTZONE")) {
221712b2f30Ssthen LDNS_RCODE_SET(reply, LDNS_RCODE_NOTZONE);
222712b2f30Ssthen /* flags */
223712b2f30Ssthen } else if(str_keyword(&parse, "QR")) {
224712b2f30Ssthen LDNS_QR_SET(reply);
225712b2f30Ssthen } else if(str_keyword(&parse, "AA")) {
226712b2f30Ssthen LDNS_AA_SET(reply);
227712b2f30Ssthen } else if(str_keyword(&parse, "TC")) {
228712b2f30Ssthen LDNS_TC_SET(reply);
229712b2f30Ssthen } else if(str_keyword(&parse, "RD")) {
230712b2f30Ssthen LDNS_RD_SET(reply);
231712b2f30Ssthen } else if(str_keyword(&parse, "CD")) {
232712b2f30Ssthen LDNS_CD_SET(reply);
233712b2f30Ssthen } else if(str_keyword(&parse, "RA")) {
234712b2f30Ssthen LDNS_RA_SET(reply);
235712b2f30Ssthen } else if(str_keyword(&parse, "AD")) {
236712b2f30Ssthen LDNS_AD_SET(reply);
237712b2f30Ssthen } else if(str_keyword(&parse, "DO")) {
238712b2f30Ssthen *do_flag = 1;
239712b2f30Ssthen } else {
240712b2f30Ssthen error("could not parse REPLY: '%s'", parse);
241712b2f30Ssthen }
242712b2f30Ssthen }
243712b2f30Ssthen }
244712b2f30Ssthen
245712b2f30Ssthen /** parse ADJUST line */
adjustline(char * line,struct entry * e,struct reply_packet * pkt)246712b2f30Ssthen static void adjustline(char* line, struct entry* e,
247712b2f30Ssthen struct reply_packet* pkt)
248712b2f30Ssthen {
249712b2f30Ssthen char* parse = line;
250712b2f30Ssthen while(*parse) {
251712b2f30Ssthen if(isendline(*parse))
252712b2f30Ssthen return;
253712b2f30Ssthen if(str_keyword(&parse, "copy_id")) {
254712b2f30Ssthen e->copy_id = 1;
255712b2f30Ssthen } else if(str_keyword(&parse, "copy_query")) {
256712b2f30Ssthen e->copy_query = 1;
257712b2f30Ssthen } else if(str_keyword(&parse, "copy_ednsdata_assume_clientsubnet")) {
258712b2f30Ssthen e->copy_ednsdata_assume_clientsubnet = 1;
259dc90232dSsthen } else if(str_keyword(&parse, "increment_ecs_scope")) {
260dc90232dSsthen e->increment_ecs_scope = 1;
261712b2f30Ssthen } else if(str_keyword(&parse, "sleep=")) {
262712b2f30Ssthen e->sleeptime = (unsigned int) strtol(parse, (char**)&parse, 10);
263712b2f30Ssthen while(isspace((unsigned char)*parse))
264712b2f30Ssthen parse++;
265712b2f30Ssthen } else if(str_keyword(&parse, "packet_sleep=")) {
266712b2f30Ssthen pkt->packet_sleep = (unsigned int) strtol(parse, (char**)&parse, 10);
267712b2f30Ssthen while(isspace((unsigned char)*parse))
268712b2f30Ssthen parse++;
269712b2f30Ssthen } else {
270712b2f30Ssthen error("could not parse ADJUST: '%s'", parse);
271712b2f30Ssthen }
272712b2f30Ssthen }
273712b2f30Ssthen }
274712b2f30Ssthen
275712b2f30Ssthen /** create new entry */
new_entry(void)276712b2f30Ssthen static struct entry* new_entry(void)
277712b2f30Ssthen {
278712b2f30Ssthen struct entry* e = (struct entry*)malloc(sizeof(struct entry));
279712b2f30Ssthen if(!e) error("out of memory");
280712b2f30Ssthen memset(e, 0, sizeof(*e));
281712b2f30Ssthen e->match_opcode = 0;
282712b2f30Ssthen e->match_qtype = 0;
283712b2f30Ssthen e->match_qname = 0;
284712b2f30Ssthen e->match_rcode = 0;
285712b2f30Ssthen e->match_question = 0;
286712b2f30Ssthen e->match_answer = 0;
287712b2f30Ssthen e->match_subdomain = 0;
288712b2f30Ssthen e->match_all = 0;
289c2862f41Ssthen e->match_all_noedns = 0;
290712b2f30Ssthen e->match_ttl = 0;
291712b2f30Ssthen e->match_do = 0;
292712b2f30Ssthen e->match_noedns = 0;
293712b2f30Ssthen e->match_serial = 0;
294712b2f30Ssthen e->ixfr_soa_serial = 0;
295c2862f41Ssthen e->match_ede = 0;
296c2862f41Ssthen e->match_ede_any = 0;
297c2862f41Ssthen e->ede_info_code = -1;
298712b2f30Ssthen e->match_transport = transport_any;
299712b2f30Ssthen e->reply_list = NULL;
300712b2f30Ssthen e->copy_id = 0;
301712b2f30Ssthen e->copy_query = 0;
302712b2f30Ssthen e->copy_ednsdata_assume_clientsubnet = 0;
303dc90232dSsthen e->increment_ecs_scope = 0;
304712b2f30Ssthen e->sleeptime = 0;
305712b2f30Ssthen e->next = NULL;
306712b2f30Ssthen return e;
307712b2f30Ssthen }
308712b2f30Ssthen
309712b2f30Ssthen /**
310712b2f30Ssthen * Converts a hex string to binary data
311712b2f30Ssthen * @param hexstr: string of hex.
312712b2f30Ssthen * @param len: is the length of the string
313712b2f30Ssthen * @param buf: is the buffer to store the result in
314712b2f30Ssthen * @param offset: is the starting position in the result buffer
315712b2f30Ssthen * @param buf_len: is the length of buf.
316712b2f30Ssthen * @return This function returns the length of the result
317712b2f30Ssthen */
318712b2f30Ssthen static size_t
hexstr2bin(char * hexstr,int len,uint8_t * buf,size_t offset,size_t buf_len)319712b2f30Ssthen hexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len)
320712b2f30Ssthen {
321712b2f30Ssthen char c;
322712b2f30Ssthen int i;
323712b2f30Ssthen uint8_t int8 = 0;
324712b2f30Ssthen int sec = 0;
325712b2f30Ssthen size_t bufpos = 0;
326712b2f30Ssthen
327712b2f30Ssthen if (len % 2 != 0) {
328712b2f30Ssthen return 0;
329712b2f30Ssthen }
330712b2f30Ssthen
331712b2f30Ssthen for (i=0; i<len; i++) {
332712b2f30Ssthen c = hexstr[i];
333712b2f30Ssthen
334712b2f30Ssthen /* case insensitive, skip spaces */
335712b2f30Ssthen if (c != ' ') {
336712b2f30Ssthen if (c >= '0' && c <= '9') {
337712b2f30Ssthen int8 += c & 0x0f;
338712b2f30Ssthen } else if (c >= 'a' && c <= 'z') {
339712b2f30Ssthen int8 += (c & 0x0f) + 9;
340712b2f30Ssthen } else if (c >= 'A' && c <= 'Z') {
341712b2f30Ssthen int8 += (c & 0x0f) + 9;
342712b2f30Ssthen } else {
343712b2f30Ssthen return 0;
344712b2f30Ssthen }
345712b2f30Ssthen
346712b2f30Ssthen if (sec == 0) {
347712b2f30Ssthen int8 = int8 << 4;
348712b2f30Ssthen sec = 1;
349712b2f30Ssthen } else {
350712b2f30Ssthen if (bufpos + offset + 1 <= buf_len) {
351712b2f30Ssthen buf[bufpos+offset] = int8;
352712b2f30Ssthen int8 = 0;
353712b2f30Ssthen sec = 0;
354712b2f30Ssthen bufpos++;
355712b2f30Ssthen } else {
356712b2f30Ssthen fprintf(stderr, "Buffer too small in hexstr2bin");
357712b2f30Ssthen }
358712b2f30Ssthen }
359712b2f30Ssthen }
360712b2f30Ssthen }
361712b2f30Ssthen return bufpos;
362712b2f30Ssthen }
363712b2f30Ssthen
364712b2f30Ssthen /** convert hex buffer to binary buffer */
365712b2f30Ssthen static sldns_buffer *
hex_buffer2wire(sldns_buffer * data_buffer)366712b2f30Ssthen hex_buffer2wire(sldns_buffer *data_buffer)
367712b2f30Ssthen {
368712b2f30Ssthen sldns_buffer *wire_buffer = NULL;
369712b2f30Ssthen int c;
370712b2f30Ssthen
371712b2f30Ssthen /* stat hack
372712b2f30Ssthen * 0 = normal
373712b2f30Ssthen * 1 = comment (skip to end of line)
374712b2f30Ssthen * 2 = unprintable character found, read binary data directly
375712b2f30Ssthen */
376712b2f30Ssthen size_t data_buf_pos = 0;
377712b2f30Ssthen int state = 0;
378712b2f30Ssthen uint8_t *hexbuf;
379712b2f30Ssthen int hexbufpos = 0;
380712b2f30Ssthen size_t wirelen;
381712b2f30Ssthen uint8_t *data_wire = (uint8_t *) sldns_buffer_begin(data_buffer);
382712b2f30Ssthen uint8_t *wire = (uint8_t*)malloc(MAX_PACKETLEN);
383712b2f30Ssthen if(!wire) error("out of memory");
384712b2f30Ssthen
385712b2f30Ssthen hexbuf = (uint8_t*)malloc(MAX_PACKETLEN);
386712b2f30Ssthen if(!hexbuf) error("out of memory");
387712b2f30Ssthen for (data_buf_pos = 0; data_buf_pos < sldns_buffer_position(data_buffer); data_buf_pos++) {
388712b2f30Ssthen c = (int) data_wire[data_buf_pos];
389712b2f30Ssthen
390712b2f30Ssthen if (state < 2 && !isascii((unsigned char)c)) {
391712b2f30Ssthen /*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/
392712b2f30Ssthen state = 2;
393712b2f30Ssthen }
394712b2f30Ssthen switch (state) {
395712b2f30Ssthen case 0:
396712b2f30Ssthen if ( (c >= '0' && c <= '9') ||
397712b2f30Ssthen (c >= 'a' && c <= 'f') ||
398712b2f30Ssthen (c >= 'A' && c <= 'F') )
399712b2f30Ssthen {
400712b2f30Ssthen if (hexbufpos >= MAX_PACKETLEN) {
401712b2f30Ssthen error("buffer overflow");
402712b2f30Ssthen free(hexbuf);
403712b2f30Ssthen return 0;
404712b2f30Ssthen
405712b2f30Ssthen }
406712b2f30Ssthen hexbuf[hexbufpos] = (uint8_t) c;
407712b2f30Ssthen hexbufpos++;
408712b2f30Ssthen } else if (c == ';') {
409712b2f30Ssthen state = 1;
410712b2f30Ssthen } else if (c == ' ' || c == '\t' || c == '\n') {
411712b2f30Ssthen /* skip whitespace */
412712b2f30Ssthen }
413712b2f30Ssthen break;
414712b2f30Ssthen case 1:
415712b2f30Ssthen if (c == '\n' || c == EOF) {
416712b2f30Ssthen state = 0;
417712b2f30Ssthen }
418712b2f30Ssthen break;
419712b2f30Ssthen case 2:
420712b2f30Ssthen if (hexbufpos >= MAX_PACKETLEN) {
421712b2f30Ssthen error("buffer overflow");
422712b2f30Ssthen free(hexbuf);
423712b2f30Ssthen return 0;
424712b2f30Ssthen }
425712b2f30Ssthen hexbuf[hexbufpos] = (uint8_t) c;
426712b2f30Ssthen hexbufpos++;
427712b2f30Ssthen break;
428712b2f30Ssthen }
429712b2f30Ssthen }
430712b2f30Ssthen
431712b2f30Ssthen if (hexbufpos >= MAX_PACKETLEN) {
432712b2f30Ssthen /*verbose("packet size reached\n");*/
433712b2f30Ssthen }
434712b2f30Ssthen
435712b2f30Ssthen /* lenient mode: length must be multiple of 2 */
436712b2f30Ssthen if (hexbufpos % 2 != 0) {
437712b2f30Ssthen if (hexbufpos >= MAX_PACKETLEN) {
438712b2f30Ssthen error("buffer overflow");
439712b2f30Ssthen free(hexbuf);
440712b2f30Ssthen return 0;
441712b2f30Ssthen }
442712b2f30Ssthen hexbuf[hexbufpos] = (uint8_t) '0';
443712b2f30Ssthen hexbufpos++;
444712b2f30Ssthen }
445712b2f30Ssthen
446712b2f30Ssthen if (state < 2) {
447712b2f30Ssthen wirelen = hexstr2bin((char *) hexbuf, hexbufpos, wire, 0, MAX_PACKETLEN);
448712b2f30Ssthen wire_buffer = sldns_buffer_new(wirelen);
449712b2f30Ssthen sldns_buffer_new_frm_data(wire_buffer, wire, wirelen);
450712b2f30Ssthen } else {
451712b2f30Ssthen error("Incomplete hex data, not at byte boundary\n");
452712b2f30Ssthen }
453712b2f30Ssthen free(wire);
454712b2f30Ssthen free(hexbuf);
455712b2f30Ssthen return wire_buffer;
456712b2f30Ssthen }
457712b2f30Ssthen
458712b2f30Ssthen /** parse ORIGIN */
459712b2f30Ssthen static void
get_origin(const char * name,struct sldns_file_parse_state * pstate,char * parse)460712b2f30Ssthen get_origin(const char* name, struct sldns_file_parse_state* pstate, char* parse)
461712b2f30Ssthen {
462712b2f30Ssthen /* snip off rest of the text so as to make the parse work in ldns */
463712b2f30Ssthen char* end;
464712b2f30Ssthen char store;
465712b2f30Ssthen int status;
466712b2f30Ssthen
467712b2f30Ssthen end=parse;
468712b2f30Ssthen while(!isspace((unsigned char)*end) && !isendline(*end))
469712b2f30Ssthen end++;
470712b2f30Ssthen store = *end;
471712b2f30Ssthen *end = 0;
472712b2f30Ssthen verbose(3, "parsing '%s'\n", parse);
473*3f9cc7b6Ssthen pstate->origin_len = sizeof(pstate->origin);
474712b2f30Ssthen status = sldns_str2wire_dname_buf(parse, pstate->origin,
475712b2f30Ssthen &pstate->origin_len);
476712b2f30Ssthen *end = store;
477712b2f30Ssthen if(status != 0)
478712b2f30Ssthen error("%s line %d:\n\t%s: %s", name, pstate->lineno,
479712b2f30Ssthen sldns_get_errorstr_parse(status), parse);
480712b2f30Ssthen }
481712b2f30Ssthen
482712b2f30Ssthen /** add RR to packet */
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)483712b2f30Ssthen static void add_rr(char* rrstr, uint8_t* pktbuf, size_t pktsize,
484712b2f30Ssthen size_t* pktlen, struct sldns_file_parse_state* pstate,
485712b2f30Ssthen sldns_pkt_section add_section, const char* fname)
486712b2f30Ssthen {
487712b2f30Ssthen /* it must be a RR, parse and add to packet. */
488712b2f30Ssthen size_t rr_len = pktsize - *pktlen;
489712b2f30Ssthen size_t dname_len = 0;
490712b2f30Ssthen int status;
491712b2f30Ssthen uint8_t* origin = pstate->origin_len?pstate->origin:0;
492712b2f30Ssthen uint8_t* prev = pstate->prev_rr_len?pstate->prev_rr:0;
493712b2f30Ssthen if(*pktlen > pktsize || *pktlen < LDNS_HEADER_SIZE)
494712b2f30Ssthen error("packet overflow");
495712b2f30Ssthen
496712b2f30Ssthen /* parse RR */
497712b2f30Ssthen if(add_section == LDNS_SECTION_QUESTION)
498712b2f30Ssthen status = sldns_str2wire_rr_question_buf(rrstr, pktbuf+*pktlen,
499712b2f30Ssthen &rr_len, &dname_len, origin, pstate->origin_len,
500712b2f30Ssthen prev, pstate->prev_rr_len);
501712b2f30Ssthen else status = sldns_str2wire_rr_buf(rrstr, pktbuf+*pktlen, &rr_len,
502712b2f30Ssthen &dname_len, pstate->default_ttl, origin,
503712b2f30Ssthen pstate->origin_len, prev, pstate->prev_rr_len);
504712b2f30Ssthen if(status != 0)
505712b2f30Ssthen error("%s line %d:%d %s\n\t%s", fname, pstate->lineno,
506712b2f30Ssthen LDNS_WIREPARSE_OFFSET(status),
507712b2f30Ssthen sldns_get_errorstr_parse(status), rrstr);
508712b2f30Ssthen *pktlen += rr_len;
509712b2f30Ssthen
510712b2f30Ssthen /* increase RR count */
511712b2f30Ssthen if(add_section == LDNS_SECTION_QUESTION)
512712b2f30Ssthen sldns_write_uint16(pktbuf+4, LDNS_QDCOUNT(pktbuf)+1);
513712b2f30Ssthen else if(add_section == LDNS_SECTION_ANSWER)
514712b2f30Ssthen sldns_write_uint16(pktbuf+6, LDNS_ANCOUNT(pktbuf)+1);
515712b2f30Ssthen else if(add_section == LDNS_SECTION_AUTHORITY)
516712b2f30Ssthen sldns_write_uint16(pktbuf+8, LDNS_NSCOUNT(pktbuf)+1);
517712b2f30Ssthen else if(add_section == LDNS_SECTION_ADDITIONAL)
518712b2f30Ssthen sldns_write_uint16(pktbuf+10, LDNS_ARCOUNT(pktbuf)+1);
519712b2f30Ssthen else error("internal error bad section %d", (int)add_section);
520712b2f30Ssthen }
521712b2f30Ssthen
522712b2f30Ssthen /* add EDNS 4096 opt record */
523712b2f30Ssthen static void
add_edns(uint8_t * pktbuf,size_t pktsize,int do_flag,uint8_t * ednsdata,uint16_t ednslen,size_t * pktlen)524712b2f30Ssthen add_edns(uint8_t* pktbuf, size_t pktsize, int do_flag, uint8_t *ednsdata,
525712b2f30Ssthen uint16_t ednslen, size_t* pktlen)
526712b2f30Ssthen {
527712b2f30Ssthen uint8_t edns[] = {0x00, /* root label */
528712b2f30Ssthen 0x00, LDNS_RR_TYPE_OPT, /* type */
529e2a0f313Ssthen 0x04, 0xD0, /* class is UDPSIZE 1232 */
530712b2f30Ssthen 0x00, /* TTL[0] is ext rcode */
531712b2f30Ssthen 0x00, /* TTL[1] is edns version */
532712b2f30Ssthen (uint8_t)(do_flag?0x80:0x00), 0x00, /* TTL[2-3] is edns flags, DO */
533712b2f30Ssthen (uint8_t)((ednslen >> 8) & 0xff),
534712b2f30Ssthen (uint8_t)(ednslen & 0xff), /* rdatalength */
535712b2f30Ssthen };
536712b2f30Ssthen if(*pktlen < LDNS_HEADER_SIZE)
537712b2f30Ssthen return;
538712b2f30Ssthen if(*pktlen + sizeof(edns) + ednslen > pktsize)
539712b2f30Ssthen error("not enough space for EDNS OPT record");
540712b2f30Ssthen memmove(pktbuf+*pktlen, edns, sizeof(edns));
54166a34dc2Ssthen if(ednsdata && ednslen)
542712b2f30Ssthen memmove(pktbuf+*pktlen+sizeof(edns), ednsdata, ednslen);
543712b2f30Ssthen sldns_write_uint16(pktbuf+10, LDNS_ARCOUNT(pktbuf)+1);
544712b2f30Ssthen *pktlen += (sizeof(edns) + ednslen);
545712b2f30Ssthen }
546712b2f30Ssthen
547712b2f30Ssthen /* Reads one entry from file. Returns entry or NULL on error. */
548712b2f30Ssthen struct entry*
read_entry(FILE * in,const char * name,struct sldns_file_parse_state * pstate,int skip_whitespace)549712b2f30Ssthen read_entry(FILE* in, const char* name, struct sldns_file_parse_state* pstate,
550712b2f30Ssthen int skip_whitespace)
551712b2f30Ssthen {
552712b2f30Ssthen struct entry* current = NULL;
553712b2f30Ssthen char line[MAX_LINE];
554712b2f30Ssthen char* parse;
555712b2f30Ssthen sldns_pkt_section add_section = LDNS_SECTION_QUESTION;
556712b2f30Ssthen struct reply_packet *cur_reply = NULL;
557712b2f30Ssthen int reading_hex = 0;
558712b2f30Ssthen int reading_hex_ednsdata = 0;
559712b2f30Ssthen sldns_buffer* hex_data_buffer = NULL;
560712b2f30Ssthen sldns_buffer* hex_ednsdata_buffer = NULL;
561712b2f30Ssthen uint8_t pktbuf[MAX_PACKETLEN];
562712b2f30Ssthen size_t pktlen = LDNS_HEADER_SIZE;
563712b2f30Ssthen int do_flag = 0; /* DO flag in EDNS */
564712b2f30Ssthen memset(pktbuf, 0, pktlen); /* ID = 0, FLAGS="", and rr counts 0 */
565712b2f30Ssthen
566712b2f30Ssthen while(fgets(line, (int)sizeof(line), in) != NULL) {
567712b2f30Ssthen line[MAX_LINE-1] = 0;
568712b2f30Ssthen parse = line;
569712b2f30Ssthen pstate->lineno++;
570712b2f30Ssthen
571712b2f30Ssthen while(isspace((unsigned char)*parse))
572712b2f30Ssthen parse++;
573712b2f30Ssthen /* test for keywords */
574712b2f30Ssthen if(isendline(*parse))
575712b2f30Ssthen continue; /* skip comment and empty lines */
576712b2f30Ssthen if(str_keyword(&parse, "ENTRY_BEGIN")) {
577712b2f30Ssthen if(current) {
578712b2f30Ssthen error("%s line %d: previous entry does not ENTRY_END",
579712b2f30Ssthen name, pstate->lineno);
580712b2f30Ssthen }
581712b2f30Ssthen current = new_entry();
582712b2f30Ssthen current->lineno = pstate->lineno;
583712b2f30Ssthen cur_reply = entry_add_reply(current);
584712b2f30Ssthen continue;
585712b2f30Ssthen } else if(str_keyword(&parse, "$ORIGIN")) {
586712b2f30Ssthen get_origin(name, pstate, parse);
587712b2f30Ssthen continue;
588712b2f30Ssthen } else if(str_keyword(&parse, "$TTL")) {
589712b2f30Ssthen pstate->default_ttl = (uint32_t)atoi(parse);
590712b2f30Ssthen continue;
591712b2f30Ssthen }
592712b2f30Ssthen
593712b2f30Ssthen /* working inside an entry */
594712b2f30Ssthen if(!current) {
595712b2f30Ssthen error("%s line %d: expected ENTRY_BEGIN but got %s",
596712b2f30Ssthen name, pstate->lineno, line);
597712b2f30Ssthen }
598712b2f30Ssthen if(str_keyword(&parse, "MATCH")) {
599712b2f30Ssthen matchline(parse, current);
600712b2f30Ssthen } else if(str_keyword(&parse, "REPLY")) {
601712b2f30Ssthen replyline(parse, pktbuf, pktlen, &do_flag);
602712b2f30Ssthen } else if(str_keyword(&parse, "ADJUST")) {
603712b2f30Ssthen adjustline(parse, current, cur_reply);
604712b2f30Ssthen } else if(str_keyword(&parse, "EXTRA_PACKET")) {
605712b2f30Ssthen /* copy current packet into buffer */
606712b2f30Ssthen cur_reply->reply_pkt = memdup(pktbuf, pktlen);
607712b2f30Ssthen cur_reply->reply_len = pktlen;
608712b2f30Ssthen if(!cur_reply->reply_pkt)
609712b2f30Ssthen error("out of memory");
610712b2f30Ssthen cur_reply = entry_add_reply(current);
611712b2f30Ssthen /* clear for next packet */
612712b2f30Ssthen pktlen = LDNS_HEADER_SIZE;
613712b2f30Ssthen memset(pktbuf, 0, pktlen); /* ID = 0, FLAGS="", and rr counts 0 */
614712b2f30Ssthen } else if(str_keyword(&parse, "SECTION")) {
615712b2f30Ssthen if(str_keyword(&parse, "QUESTION"))
616712b2f30Ssthen add_section = LDNS_SECTION_QUESTION;
617712b2f30Ssthen else if(str_keyword(&parse, "ANSWER"))
618712b2f30Ssthen add_section = LDNS_SECTION_ANSWER;
619712b2f30Ssthen else if(str_keyword(&parse, "AUTHORITY"))
620712b2f30Ssthen add_section = LDNS_SECTION_AUTHORITY;
621712b2f30Ssthen else if(str_keyword(&parse, "ADDITIONAL"))
622712b2f30Ssthen add_section = LDNS_SECTION_ADDITIONAL;
623712b2f30Ssthen else error("%s line %d: bad section %s", name, pstate->lineno, parse);
624712b2f30Ssthen } else if(str_keyword(&parse, "HEX_ANSWER_BEGIN")) {
625712b2f30Ssthen hex_data_buffer = sldns_buffer_new(MAX_PACKETLEN);
626712b2f30Ssthen reading_hex = 1;
627712b2f30Ssthen } else if(str_keyword(&parse, "HEX_ANSWER_END")) {
628712b2f30Ssthen if(!reading_hex) {
629712b2f30Ssthen error("%s line %d: HEX_ANSWER_END read but no HEX_ANSWER_BEGIN keyword seen", name, pstate->lineno);
630712b2f30Ssthen }
631712b2f30Ssthen reading_hex = 0;
632712b2f30Ssthen cur_reply->reply_from_hex = hex_buffer2wire(hex_data_buffer);
633712b2f30Ssthen sldns_buffer_free(hex_data_buffer);
634712b2f30Ssthen hex_data_buffer = NULL;
635712b2f30Ssthen } else if(reading_hex) {
636712b2f30Ssthen sldns_buffer_printf(hex_data_buffer, "%s", line);
637712b2f30Ssthen } else if(str_keyword(&parse, "HEX_EDNSDATA_BEGIN")) {
638712b2f30Ssthen hex_ednsdata_buffer = sldns_buffer_new(MAX_PACKETLEN);
639712b2f30Ssthen reading_hex_ednsdata = 1;
640712b2f30Ssthen } else if(str_keyword(&parse, "HEX_EDNSDATA_END")) {
641712b2f30Ssthen if (!reading_hex_ednsdata) {
642712b2f30Ssthen error("%s line %d: HEX_EDNSDATA_END read but no"
643712b2f30Ssthen "HEX_EDNSDATA_BEGIN keyword seen", name, pstate->lineno);
644712b2f30Ssthen }
645712b2f30Ssthen reading_hex_ednsdata = 0;
646712b2f30Ssthen cur_reply->raw_ednsdata = hex_buffer2wire(hex_ednsdata_buffer);
647712b2f30Ssthen sldns_buffer_free(hex_ednsdata_buffer);
648712b2f30Ssthen hex_ednsdata_buffer = NULL;
649712b2f30Ssthen } else if(reading_hex_ednsdata) {
650712b2f30Ssthen sldns_buffer_printf(hex_ednsdata_buffer, "%s", line);
651712b2f30Ssthen } else if(str_keyword(&parse, "ENTRY_END")) {
652712b2f30Ssthen if(hex_data_buffer)
653712b2f30Ssthen sldns_buffer_free(hex_data_buffer);
654712b2f30Ssthen if(hex_ednsdata_buffer)
655712b2f30Ssthen sldns_buffer_free(hex_ednsdata_buffer);
656712b2f30Ssthen if(pktlen != 0) {
657712b2f30Ssthen if(do_flag || cur_reply->raw_ednsdata) {
658712b2f30Ssthen if(cur_reply->raw_ednsdata &&
659712b2f30Ssthen sldns_buffer_limit(cur_reply->raw_ednsdata))
660712b2f30Ssthen add_edns(pktbuf, sizeof(pktbuf), do_flag,
661712b2f30Ssthen sldns_buffer_begin(cur_reply->raw_ednsdata),
662712b2f30Ssthen (uint16_t)sldns_buffer_limit(cur_reply->raw_ednsdata),
663712b2f30Ssthen &pktlen);
664712b2f30Ssthen else
665712b2f30Ssthen add_edns(pktbuf, sizeof(pktbuf), do_flag,
666712b2f30Ssthen NULL, 0, &pktlen);
667712b2f30Ssthen }
668712b2f30Ssthen cur_reply->reply_pkt = memdup(pktbuf, pktlen);
669712b2f30Ssthen cur_reply->reply_len = pktlen;
670712b2f30Ssthen if(!cur_reply->reply_pkt)
671712b2f30Ssthen error("out of memory");
672712b2f30Ssthen }
673712b2f30Ssthen return current;
674712b2f30Ssthen } else {
675712b2f30Ssthen add_rr(skip_whitespace?parse:line, pktbuf,
676712b2f30Ssthen sizeof(pktbuf), &pktlen, pstate, add_section,
677712b2f30Ssthen name);
678712b2f30Ssthen }
679712b2f30Ssthen
680712b2f30Ssthen }
681712b2f30Ssthen if(reading_hex) {
682712b2f30Ssthen error("%s: End of file reached while still reading hex, "
683712b2f30Ssthen "missing HEX_ANSWER_END\n", name);
684712b2f30Ssthen }
685712b2f30Ssthen if(reading_hex_ednsdata) {
686712b2f30Ssthen error("%s: End of file reached while still reading edns data, "
687712b2f30Ssthen "missing HEX_EDNSDATA_END\n", name);
688712b2f30Ssthen }
689712b2f30Ssthen if(current) {
690712b2f30Ssthen error("%s: End of file reached while reading entry. "
691712b2f30Ssthen "missing ENTRY_END\n", name);
692712b2f30Ssthen }
693712b2f30Ssthen return 0;
694712b2f30Ssthen }
695712b2f30Ssthen
696712b2f30Ssthen /* reads the canned reply file and returns a list of structs */
697712b2f30Ssthen struct entry*
read_datafile(const char * name,int skip_whitespace)698712b2f30Ssthen read_datafile(const char* name, int skip_whitespace)
699712b2f30Ssthen {
700712b2f30Ssthen struct entry* list = NULL;
701712b2f30Ssthen struct entry* last = NULL;
702712b2f30Ssthen struct entry* current = NULL;
703712b2f30Ssthen FILE *in;
704712b2f30Ssthen struct sldns_file_parse_state pstate;
705712b2f30Ssthen int entry_num = 0;
706712b2f30Ssthen memset(&pstate, 0, sizeof(pstate));
707712b2f30Ssthen
708712b2f30Ssthen if((in=fopen(name, "r")) == NULL) {
709712b2f30Ssthen error("could not open file %s: %s", name, strerror(errno));
710712b2f30Ssthen }
711712b2f30Ssthen
712712b2f30Ssthen while((current = read_entry(in, name, &pstate, skip_whitespace)))
713712b2f30Ssthen {
714712b2f30Ssthen if(last)
715712b2f30Ssthen last->next = current;
716712b2f30Ssthen else list = current;
717712b2f30Ssthen last = current;
718712b2f30Ssthen entry_num ++;
719712b2f30Ssthen }
720712b2f30Ssthen verbose(1, "%s: Read %d entries\n", prog_name, entry_num);
721712b2f30Ssthen
722712b2f30Ssthen fclose(in);
723712b2f30Ssthen return list;
724712b2f30Ssthen }
725712b2f30Ssthen
726712b2f30Ssthen /** get qtype from packet */
get_qtype(uint8_t * pkt,size_t pktlen)727712b2f30Ssthen static sldns_rr_type get_qtype(uint8_t* pkt, size_t pktlen)
728712b2f30Ssthen {
729712b2f30Ssthen uint8_t* d;
730712b2f30Ssthen size_t dl, sl=0;
731712b2f30Ssthen char* snull = NULL;
732b0dfc31bSsthen int comprloop = 0;
733712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
734712b2f30Ssthen return 0;
735712b2f30Ssthen if(LDNS_QDCOUNT(pkt) == 0)
736712b2f30Ssthen return 0;
737712b2f30Ssthen /* skip over dname with dname-scan routine */
738712b2f30Ssthen d = pkt+LDNS_HEADER_SIZE;
739712b2f30Ssthen dl = pktlen-LDNS_HEADER_SIZE;
740b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen, &comprloop);
741712b2f30Ssthen if(dl < 2)
742712b2f30Ssthen return 0;
743712b2f30Ssthen return sldns_read_uint16(d);
744712b2f30Ssthen }
745712b2f30Ssthen
746712b2f30Ssthen /** get qtype from packet */
get_qname_len(uint8_t * pkt,size_t pktlen)747712b2f30Ssthen static size_t get_qname_len(uint8_t* pkt, size_t pktlen)
748712b2f30Ssthen {
749712b2f30Ssthen uint8_t* d;
750712b2f30Ssthen size_t dl, sl=0;
751712b2f30Ssthen char* snull = NULL;
752b0dfc31bSsthen int comprloop = 0;
753712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
754712b2f30Ssthen return 0;
755712b2f30Ssthen if(LDNS_QDCOUNT(pkt) == 0)
756712b2f30Ssthen return 0;
757712b2f30Ssthen /* skip over dname with dname-scan routine */
758712b2f30Ssthen d = pkt+LDNS_HEADER_SIZE;
759712b2f30Ssthen dl = pktlen-LDNS_HEADER_SIZE;
760b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&d, &dl, &snull, &sl, pkt, pktlen, &comprloop);
761712b2f30Ssthen return pktlen-dl-LDNS_HEADER_SIZE;
762712b2f30Ssthen }
763712b2f30Ssthen
764712b2f30Ssthen /** returns owner from packet */
get_qname(uint8_t * pkt,size_t pktlen)765712b2f30Ssthen static uint8_t* get_qname(uint8_t* pkt, size_t pktlen)
766712b2f30Ssthen {
767712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
768712b2f30Ssthen return NULL;
769712b2f30Ssthen if(LDNS_QDCOUNT(pkt) == 0)
770712b2f30Ssthen return NULL;
771712b2f30Ssthen return pkt+LDNS_HEADER_SIZE;
772712b2f30Ssthen }
773712b2f30Ssthen
774712b2f30Ssthen /** returns opcode from packet */
get_opcode(uint8_t * pkt,size_t pktlen)775712b2f30Ssthen static int get_opcode(uint8_t* pkt, size_t pktlen)
776712b2f30Ssthen {
777712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
778712b2f30Ssthen return 0;
779712b2f30Ssthen return (int)LDNS_OPCODE_WIRE(pkt);
780712b2f30Ssthen }
781712b2f30Ssthen
782712b2f30Ssthen /** returns rcode from packet */
get_rcode(uint8_t * pkt,size_t pktlen)783712b2f30Ssthen static int get_rcode(uint8_t* pkt, size_t pktlen)
784712b2f30Ssthen {
785712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
786712b2f30Ssthen return 0;
787712b2f30Ssthen return (int)LDNS_RCODE_WIRE(pkt);
788712b2f30Ssthen }
789712b2f30Ssthen
790712b2f30Ssthen /** get authority section SOA serial value */
get_serial(uint8_t * p,size_t plen)791712b2f30Ssthen static uint32_t get_serial(uint8_t* p, size_t plen)
792712b2f30Ssthen {
793712b2f30Ssthen uint8_t* walk = p;
794712b2f30Ssthen size_t walk_len = plen, sl=0;
795712b2f30Ssthen char* snull = NULL;
796712b2f30Ssthen uint16_t i;
797b0dfc31bSsthen int comprloop = 0;
798712b2f30Ssthen
799712b2f30Ssthen if(walk_len < LDNS_HEADER_SIZE)
800712b2f30Ssthen return 0;
801712b2f30Ssthen walk += LDNS_HEADER_SIZE;
802712b2f30Ssthen walk_len -= LDNS_HEADER_SIZE;
803712b2f30Ssthen
804712b2f30Ssthen /* skip other records with wire2str_scan */
805712b2f30Ssthen for(i=0; i < LDNS_QDCOUNT(p); i++)
806712b2f30Ssthen (void)sldns_wire2str_rrquestion_scan(&walk, &walk_len,
807b0dfc31bSsthen &snull, &sl, p, plen, &comprloop);
808712b2f30Ssthen for(i=0; i < LDNS_ANCOUNT(p); i++)
809712b2f30Ssthen (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
810b0dfc31bSsthen p, plen, &comprloop);
811712b2f30Ssthen
812712b2f30Ssthen /* walk through authority section */
813712b2f30Ssthen for(i=0; i < LDNS_NSCOUNT(p); i++) {
814712b2f30Ssthen /* if this is SOA then get serial, skip compressed dname */
815712b2f30Ssthen uint8_t* dstart = walk;
816712b2f30Ssthen size_t dlen = walk_len;
817712b2f30Ssthen (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
818b0dfc31bSsthen p, plen, &comprloop);
819712b2f30Ssthen if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_SOA) {
820712b2f30Ssthen /* skip type, class, TTL, rdatalen */
821712b2f30Ssthen if(dlen < 10)
822712b2f30Ssthen return 0;
823712b2f30Ssthen if(dlen < 10 + (size_t)sldns_read_uint16(dstart+8))
824712b2f30Ssthen return 0;
825712b2f30Ssthen dstart += 10;
826712b2f30Ssthen dlen -= 10;
827712b2f30Ssthen /* check third rdf */
828712b2f30Ssthen (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull,
829b0dfc31bSsthen &sl, p, plen, &comprloop);
830712b2f30Ssthen (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull,
831b0dfc31bSsthen &sl, p, plen, &comprloop);
832712b2f30Ssthen if(dlen < 4)
833712b2f30Ssthen return 0;
834712b2f30Ssthen verbose(3, "found serial %u in msg. ",
835712b2f30Ssthen (int)sldns_read_uint32(dstart));
836712b2f30Ssthen return sldns_read_uint32(dstart);
837712b2f30Ssthen }
838712b2f30Ssthen /* move to next RR */
839712b2f30Ssthen (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
840b0dfc31bSsthen p, plen, &comprloop);
841712b2f30Ssthen }
842712b2f30Ssthen return 0;
843712b2f30Ssthen }
844712b2f30Ssthen
845c2862f41Ssthen /** get ptr to EDNS OPT record (and remaining length); after the type u16 */
846712b2f30Ssthen static int
pkt_find_edns_opt(uint8_t ** p,size_t * plen)847712b2f30Ssthen pkt_find_edns_opt(uint8_t** p, size_t* plen)
848712b2f30Ssthen {
849712b2f30Ssthen /* walk over the packet with scan routines */
850712b2f30Ssthen uint8_t* w = *p;
851712b2f30Ssthen size_t wlen = *plen, sl=0;
852712b2f30Ssthen char* snull = NULL;
853712b2f30Ssthen uint16_t i;
854b0dfc31bSsthen int comprloop = 0;
855712b2f30Ssthen
856712b2f30Ssthen if(wlen < LDNS_HEADER_SIZE)
857712b2f30Ssthen return 0;
858712b2f30Ssthen w += LDNS_HEADER_SIZE;
859712b2f30Ssthen wlen -= LDNS_HEADER_SIZE;
860712b2f30Ssthen
861712b2f30Ssthen /* skip other records with wire2str_scan */
862712b2f30Ssthen for(i=0; i < LDNS_QDCOUNT(*p); i++)
863712b2f30Ssthen (void)sldns_wire2str_rrquestion_scan(&w, &wlen, &snull, &sl,
864b0dfc31bSsthen *p, *plen, &comprloop);
865712b2f30Ssthen for(i=0; i < LDNS_ANCOUNT(*p); i++)
866b0dfc31bSsthen (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
867712b2f30Ssthen for(i=0; i < LDNS_NSCOUNT(*p); i++)
868b0dfc31bSsthen (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
869712b2f30Ssthen
870712b2f30Ssthen /* walk through additional section */
871712b2f30Ssthen for(i=0; i < LDNS_ARCOUNT(*p); i++) {
872712b2f30Ssthen /* if this is OPT then done */
873712b2f30Ssthen uint8_t* dstart = w;
874712b2f30Ssthen size_t dlen = wlen;
875712b2f30Ssthen (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
876b0dfc31bSsthen *p, *plen, &comprloop);
877712b2f30Ssthen if(dlen >= 2 && sldns_read_uint16(dstart) == LDNS_RR_TYPE_OPT) {
878712b2f30Ssthen *p = dstart+2;
879712b2f30Ssthen *plen = dlen-2;
880712b2f30Ssthen return 1;
881712b2f30Ssthen }
882712b2f30Ssthen /* move to next RR */
883b0dfc31bSsthen (void)sldns_wire2str_rr_scan(&w, &wlen, &snull, &sl, *p, *plen, &comprloop);
884712b2f30Ssthen }
885712b2f30Ssthen return 0;
886712b2f30Ssthen }
887712b2f30Ssthen
888712b2f30Ssthen /** return true if the packet has EDNS OPT record */
889712b2f30Ssthen static int
get_has_edns(uint8_t * pkt,size_t len)890712b2f30Ssthen get_has_edns(uint8_t* pkt, size_t len)
891712b2f30Ssthen {
892712b2f30Ssthen /* use arguments as temporary variables */
893712b2f30Ssthen return pkt_find_edns_opt(&pkt, &len);
894712b2f30Ssthen }
895712b2f30Ssthen
896712b2f30Ssthen /** return true if the DO flag is set */
897712b2f30Ssthen static int
get_do_flag(uint8_t * pkt,size_t len)898712b2f30Ssthen get_do_flag(uint8_t* pkt, size_t len)
899712b2f30Ssthen {
900712b2f30Ssthen uint16_t edns_bits;
901712b2f30Ssthen uint8_t* walk = pkt;
902712b2f30Ssthen size_t walk_len = len;
903712b2f30Ssthen if(!pkt_find_edns_opt(&walk, &walk_len)) {
904712b2f30Ssthen return 0;
905712b2f30Ssthen }
906712b2f30Ssthen if(walk_len < 6)
907712b2f30Ssthen return 0; /* malformed */
908712b2f30Ssthen edns_bits = sldns_read_uint16(walk+4);
909712b2f30Ssthen return (int)(edns_bits&LDNS_EDNS_MASK_DO_BIT);
910712b2f30Ssthen }
911712b2f30Ssthen
912437d2860Ssthen /** Snips the specified EDNS option out of the OPT record and puts it in the
913437d2860Ssthen * provided buffer. The buffer should be able to hold any opt data ie 65535.
914437d2860Ssthen * Returns the length of the option written,
915437d2860Ssthen * or 0 if not found, else -1 on error. */
916c2862f41Ssthen static int
pkt_snip_edns_option(uint8_t * pkt,size_t len,sldns_edns_option code,uint8_t * buf)917437d2860Ssthen pkt_snip_edns_option(uint8_t* pkt, size_t len, sldns_edns_option code,
918437d2860Ssthen uint8_t* buf)
919c2862f41Ssthen {
920c2862f41Ssthen uint8_t *rdata, *opt_position = pkt;
921c2862f41Ssthen uint16_t rdlen, optlen;
922c2862f41Ssthen size_t remaining = len;
923437d2860Ssthen if(!pkt_find_edns_opt(&opt_position, &remaining)) return 0;
924c2862f41Ssthen if(remaining < 8) return -1; /* malformed */
925c2862f41Ssthen rdlen = sldns_read_uint16(opt_position+6);
926c2862f41Ssthen rdata = opt_position + 8;
927c2862f41Ssthen while(rdlen > 0) {
928c2862f41Ssthen if(rdlen < 4) return -1; /* malformed */
929c2862f41Ssthen optlen = sldns_read_uint16(rdata+2);
930437d2860Ssthen if(sldns_read_uint16(rdata) == code) {
931437d2860Ssthen /* save data to buf for caller inspection */
932437d2860Ssthen memmove(buf, rdata+4, optlen);
933c2862f41Ssthen /* snip option from packet; assumes len is correct */
934c2862f41Ssthen memmove(rdata, rdata+4+optlen,
935c2862f41Ssthen (pkt+len)-(rdata+4+optlen));
936c2862f41Ssthen /* update OPT size */
937c2862f41Ssthen sldns_write_uint16(opt_position+6,
938c2862f41Ssthen sldns_read_uint16(opt_position+6)-(4+optlen));
939437d2860Ssthen return optlen;
940c2862f41Ssthen }
941c2862f41Ssthen rdlen -= 4 + optlen;
942c2862f41Ssthen rdata += 4 + optlen;
943c2862f41Ssthen }
944437d2860Ssthen return 0;
945437d2860Ssthen }
946437d2860Ssthen
947437d2860Ssthen /** Snips the EDE option out of the OPT record and returns the EDNS EDE
948437d2860Ssthen * INFO-CODE if found, else -1 */
949437d2860Ssthen static int
extract_ede(uint8_t * pkt,size_t len)950437d2860Ssthen extract_ede(uint8_t* pkt, size_t len)
951437d2860Ssthen {
952437d2860Ssthen uint8_t buf[65535];
953437d2860Ssthen int buflen = pkt_snip_edns_option(pkt, len, LDNS_EDNS_EDE, buf);
954437d2860Ssthen if(buflen < 2 /*ede without text at minimum*/) return -1;
955437d2860Ssthen return sldns_read_uint16(buf);
956437d2860Ssthen }
957437d2860Ssthen
958437d2860Ssthen /** Snips the DNS Cookie option out of the OPT record and puts it in the
959437d2860Ssthen * provided cookie buffer (should be at least 24 octets).
960437d2860Ssthen * Returns the length of the cookie if found, else -1. */
961437d2860Ssthen static int
extract_cookie(uint8_t * pkt,size_t len,uint8_t * cookie)962437d2860Ssthen extract_cookie(uint8_t* pkt, size_t len, uint8_t* cookie)
963437d2860Ssthen {
964437d2860Ssthen uint8_t buf[65535];
965437d2860Ssthen int buflen = pkt_snip_edns_option(pkt, len, LDNS_EDNS_COOKIE, buf);
966437d2860Ssthen if(buflen != 8 /*client cookie*/ &&
967437d2860Ssthen buflen != 8 + 16 /*server cookie*/) return -1;
968437d2860Ssthen memcpy(cookie, buf, buflen);
969437d2860Ssthen return buflen;
970c2862f41Ssthen }
971c2862f41Ssthen
972712b2f30Ssthen /** zero TTLs in packet */
973712b2f30Ssthen static void
zerottls(uint8_t * pkt,size_t pktlen)974712b2f30Ssthen zerottls(uint8_t* pkt, size_t pktlen)
975712b2f30Ssthen {
976712b2f30Ssthen uint8_t* walk = pkt;
977712b2f30Ssthen size_t walk_len = pktlen, sl=0;
978712b2f30Ssthen char* snull = NULL;
979712b2f30Ssthen uint16_t i;
980712b2f30Ssthen uint16_t num = LDNS_ANCOUNT(pkt)+LDNS_NSCOUNT(pkt)+LDNS_ARCOUNT(pkt);
981b0dfc31bSsthen int comprloop = 0;
982712b2f30Ssthen if(walk_len < LDNS_HEADER_SIZE)
983712b2f30Ssthen return;
984712b2f30Ssthen walk += LDNS_HEADER_SIZE;
985712b2f30Ssthen walk_len -= LDNS_HEADER_SIZE;
986712b2f30Ssthen for(i=0; i < LDNS_QDCOUNT(pkt); i++)
987712b2f30Ssthen (void)sldns_wire2str_rrquestion_scan(&walk, &walk_len,
988b0dfc31bSsthen &snull, &sl, pkt, pktlen, &comprloop);
989712b2f30Ssthen for(i=0; i < num; i++) {
990712b2f30Ssthen /* wipe TTL */
991712b2f30Ssthen uint8_t* dstart = walk;
992712b2f30Ssthen size_t dlen = walk_len;
993712b2f30Ssthen (void)sldns_wire2str_dname_scan(&dstart, &dlen, &snull, &sl,
994b0dfc31bSsthen pkt, pktlen, &comprloop);
995712b2f30Ssthen if(dlen < 8)
996712b2f30Ssthen return;
997712b2f30Ssthen sldns_write_uint32(dstart+4, 0);
998712b2f30Ssthen /* go to next RR */
999712b2f30Ssthen (void)sldns_wire2str_rr_scan(&walk, &walk_len, &snull, &sl,
1000b0dfc31bSsthen pkt, pktlen, &comprloop);
1001712b2f30Ssthen }
1002712b2f30Ssthen }
1003712b2f30Ssthen
1004712b2f30Ssthen /** get one line (\n) from a string, move next to after the \n, zero \n */
1005712b2f30Ssthen static int
get_line(char ** s,char ** n)1006712b2f30Ssthen get_line(char** s, char** n)
1007712b2f30Ssthen {
1008712b2f30Ssthen /* at end of string? end */
1009712b2f30Ssthen if(*n == NULL || **n == 0)
1010712b2f30Ssthen return 0;
1011712b2f30Ssthen /* result starts at next string */
1012712b2f30Ssthen *s = *n;
1013712b2f30Ssthen /* find \n after that */
1014712b2f30Ssthen *n = strchr(*s, '\n');
1015712b2f30Ssthen if(*n && **n != 0) {
1016712b2f30Ssthen /* terminate line */
1017712b2f30Ssthen (*n)[0] = 0;
1018712b2f30Ssthen (*n)++;
1019712b2f30Ssthen }
1020712b2f30Ssthen return 1;
1021712b2f30Ssthen }
1022712b2f30Ssthen
1023712b2f30Ssthen /** match two RR sections without ordering */
1024712b2f30Ssthen static int
match_noloc_section(char ** q,char ** nq,char ** p,char ** np,uint16_t num)1025712b2f30Ssthen match_noloc_section(char** q, char** nq, char** p, char** np, uint16_t num)
1026712b2f30Ssthen {
1027712b2f30Ssthen /* for max number of RRs in packet */
1028712b2f30Ssthen const uint16_t numarray = 3000;
1029712b2f30Ssthen char* qlines[numarray], *plines[numarray];
1030712b2f30Ssthen uint16_t i, j, numq=0, nump=0;
1031712b2f30Ssthen if(num > numarray) fatal_exit("too many RRs");
1032712b2f30Ssthen /* gather lines */
1033712b2f30Ssthen for(i=0; i<num; i++) {
1034712b2f30Ssthen get_line(q, nq);
1035712b2f30Ssthen get_line(p, np);
1036712b2f30Ssthen qlines[numq++] = *q;
1037712b2f30Ssthen plines[nump++] = *p;
1038712b2f30Ssthen }
1039712b2f30Ssthen /* see if they are all present in the other */
1040712b2f30Ssthen for(i=0; i<num; i++) {
1041712b2f30Ssthen int found = 0;
1042712b2f30Ssthen for(j=0; j<num; j++) {
1043712b2f30Ssthen if(strcmp(qlines[i], plines[j]) == 0) {
1044712b2f30Ssthen found = 1;
1045712b2f30Ssthen break;
1046712b2f30Ssthen }
1047712b2f30Ssthen }
1048712b2f30Ssthen if(!found) {
1049712b2f30Ssthen verbose(3, "comparenoloc: failed for %s", qlines[i]);
1050712b2f30Ssthen return 0;
1051712b2f30Ssthen }
1052712b2f30Ssthen }
1053712b2f30Ssthen return 1;
1054712b2f30Ssthen }
1055712b2f30Ssthen
1056712b2f30Ssthen /** match two strings for unordered equality of RRs and everything else */
1057712b2f30Ssthen 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)1058712b2f30Ssthen match_noloc(char* q, char* p, uint8_t* q_pkt, size_t q_pkt_len,
1059712b2f30Ssthen uint8_t* p_pkt, size_t p_pkt_len)
1060712b2f30Ssthen {
1061712b2f30Ssthen char* nq = q, *np = p;
1062712b2f30Ssthen /* if no header, compare bytes */
1063712b2f30Ssthen if(p_pkt_len < LDNS_HEADER_SIZE || q_pkt_len < LDNS_HEADER_SIZE) {
1064712b2f30Ssthen if(p_pkt_len != q_pkt_len) return 0;
1065712b2f30Ssthen return memcmp(p, q, p_pkt_len);
1066712b2f30Ssthen }
1067712b2f30Ssthen /* compare RR counts */
1068712b2f30Ssthen if(LDNS_QDCOUNT(p_pkt) != LDNS_QDCOUNT(q_pkt))
1069712b2f30Ssthen return 0;
1070712b2f30Ssthen if(LDNS_ANCOUNT(p_pkt) != LDNS_ANCOUNT(q_pkt))
1071712b2f30Ssthen return 0;
1072712b2f30Ssthen if(LDNS_NSCOUNT(p_pkt) != LDNS_NSCOUNT(q_pkt))
1073712b2f30Ssthen return 0;
1074712b2f30Ssthen if(LDNS_ARCOUNT(p_pkt) != LDNS_ARCOUNT(q_pkt))
1075712b2f30Ssthen return 0;
1076712b2f30Ssthen /* get a line from both; compare; at sections do section */
1077712b2f30Ssthen get_line(&q, &nq);
1078712b2f30Ssthen get_line(&p, &np);
1079712b2f30Ssthen if(strcmp(q, p) != 0) {
1080712b2f30Ssthen /* header line opcode, rcode, id */
1081712b2f30Ssthen return 0;
1082712b2f30Ssthen }
1083712b2f30Ssthen get_line(&q, &nq);
1084712b2f30Ssthen get_line(&p, &np);
1085712b2f30Ssthen if(strcmp(q, p) != 0) {
1086712b2f30Ssthen /* header flags, rr counts */
1087712b2f30Ssthen return 0;
1088712b2f30Ssthen }
1089712b2f30Ssthen /* ;; QUESTION SECTION */
1090712b2f30Ssthen get_line(&q, &nq);
1091712b2f30Ssthen get_line(&p, &np);
1092712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1093712b2f30Ssthen if(!match_noloc_section(&q, &nq, &p, &np, LDNS_QDCOUNT(p_pkt)))
1094712b2f30Ssthen return 0;
1095712b2f30Ssthen
1096712b2f30Ssthen /* empty line and ;; ANSWER SECTION */
1097712b2f30Ssthen get_line(&q, &nq);
1098712b2f30Ssthen get_line(&p, &np);
1099712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1100712b2f30Ssthen get_line(&q, &nq);
1101712b2f30Ssthen get_line(&p, &np);
1102712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1103712b2f30Ssthen if(!match_noloc_section(&q, &nq, &p, &np, LDNS_ANCOUNT(p_pkt)))
1104712b2f30Ssthen return 0;
1105712b2f30Ssthen
1106712b2f30Ssthen /* empty line and ;; AUTHORITY SECTION */
1107712b2f30Ssthen get_line(&q, &nq);
1108712b2f30Ssthen get_line(&p, &np);
1109712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1110712b2f30Ssthen get_line(&q, &nq);
1111712b2f30Ssthen get_line(&p, &np);
1112712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1113712b2f30Ssthen if(!match_noloc_section(&q, &nq, &p, &np, LDNS_NSCOUNT(p_pkt)))
1114712b2f30Ssthen return 0;
1115712b2f30Ssthen
1116712b2f30Ssthen /* empty line and ;; ADDITIONAL SECTION */
1117712b2f30Ssthen get_line(&q, &nq);
1118712b2f30Ssthen get_line(&p, &np);
1119712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1120712b2f30Ssthen get_line(&q, &nq);
1121712b2f30Ssthen get_line(&p, &np);
1122712b2f30Ssthen if(strcmp(q, p) != 0) return 0;
1123712b2f30Ssthen if(!match_noloc_section(&q, &nq, &p, &np, LDNS_ARCOUNT(p_pkt)))
1124712b2f30Ssthen return 0;
1125712b2f30Ssthen
1126712b2f30Ssthen return 1;
1127712b2f30Ssthen }
1128712b2f30Ssthen
1129712b2f30Ssthen /** lowercase domain name - does not follow compression pointers */
lowercase_dname(uint8_t ** p,size_t * remain)1130712b2f30Ssthen static void lowercase_dname(uint8_t** p, size_t* remain)
1131712b2f30Ssthen {
1132712b2f30Ssthen unsigned i, llen;
1133712b2f30Ssthen if(*remain == 0) return;
1134712b2f30Ssthen while(**p != 0) {
1135712b2f30Ssthen /* compressed? */
1136712b2f30Ssthen if((**p & 0xc0) == 0xc0) {
1137712b2f30Ssthen *p += 2;
1138712b2f30Ssthen *remain -= 2;
1139712b2f30Ssthen return;
1140712b2f30Ssthen }
1141712b2f30Ssthen llen = (unsigned int)**p;
1142712b2f30Ssthen *p += 1;
1143712b2f30Ssthen *remain -= 1;
1144712b2f30Ssthen if(*remain < llen)
1145712b2f30Ssthen llen = (unsigned int)*remain;
1146712b2f30Ssthen for(i=0; i<llen; i++) {
1147712b2f30Ssthen (*p)[i] = (uint8_t)tolower((int)(*p)[i]);
1148712b2f30Ssthen }
1149712b2f30Ssthen *p += llen;
1150712b2f30Ssthen *remain -= llen;
1151712b2f30Ssthen if(*remain == 0) return;
1152712b2f30Ssthen }
1153712b2f30Ssthen /* skip root label */
1154712b2f30Ssthen *p += 1;
1155712b2f30Ssthen *remain -= 1;
1156712b2f30Ssthen }
1157712b2f30Ssthen
1158712b2f30Ssthen /** lowercase rdata of type */
lowercase_rdata(uint8_t ** p,size_t * remain,uint16_t rdatalen,uint16_t t)1159712b2f30Ssthen static void lowercase_rdata(uint8_t** p, size_t* remain,
1160712b2f30Ssthen uint16_t rdatalen, uint16_t t)
1161712b2f30Ssthen {
1162712b2f30Ssthen const sldns_rr_descriptor *desc = sldns_rr_descript(t);
1163712b2f30Ssthen uint8_t dname_count = 0;
1164712b2f30Ssthen size_t i = 0;
1165712b2f30Ssthen size_t rdataremain = rdatalen;
1166712b2f30Ssthen if(!desc) {
1167712b2f30Ssthen /* unknown type */
1168712b2f30Ssthen *p += rdatalen;
1169712b2f30Ssthen *remain -= rdatalen;
1170712b2f30Ssthen return;
1171712b2f30Ssthen }
1172712b2f30Ssthen while(dname_count < desc->_dname_count) {
1173712b2f30Ssthen sldns_rdf_type f = sldns_rr_descriptor_field_type(desc, i++);
1174712b2f30Ssthen if(f == LDNS_RDF_TYPE_DNAME) {
1175712b2f30Ssthen lowercase_dname(p, &rdataremain);
1176712b2f30Ssthen dname_count++;
1177712b2f30Ssthen } else if(f == LDNS_RDF_TYPE_STR) {
1178712b2f30Ssthen uint8_t len;
1179712b2f30Ssthen if(rdataremain == 0) return;
1180712b2f30Ssthen len = **p;
1181712b2f30Ssthen *p += len+1;
1182712b2f30Ssthen rdataremain -= len+1;
1183712b2f30Ssthen } else {
1184712b2f30Ssthen int len = 0;
1185712b2f30Ssthen switch(f) {
1186712b2f30Ssthen case LDNS_RDF_TYPE_CLASS:
1187712b2f30Ssthen case LDNS_RDF_TYPE_ALG:
1188712b2f30Ssthen case LDNS_RDF_TYPE_INT8:
1189712b2f30Ssthen len = 1;
1190712b2f30Ssthen break;
1191712b2f30Ssthen case LDNS_RDF_TYPE_INT16:
1192712b2f30Ssthen case LDNS_RDF_TYPE_TYPE:
1193712b2f30Ssthen case LDNS_RDF_TYPE_CERT_ALG:
1194712b2f30Ssthen len = 2;
1195712b2f30Ssthen break;
1196712b2f30Ssthen case LDNS_RDF_TYPE_INT32:
1197712b2f30Ssthen case LDNS_RDF_TYPE_TIME:
1198712b2f30Ssthen case LDNS_RDF_TYPE_A:
1199712b2f30Ssthen case LDNS_RDF_TYPE_PERIOD:
1200712b2f30Ssthen len = 4;
1201712b2f30Ssthen break;
1202712b2f30Ssthen case LDNS_RDF_TYPE_TSIGTIME:
1203712b2f30Ssthen len = 6;
1204712b2f30Ssthen break;
1205712b2f30Ssthen case LDNS_RDF_TYPE_AAAA:
1206712b2f30Ssthen len = 16;
1207712b2f30Ssthen break;
1208712b2f30Ssthen default: error("bad rdf type in lowercase %d", (int)f);
1209712b2f30Ssthen }
1210712b2f30Ssthen *p += len;
1211712b2f30Ssthen rdataremain -= len;
1212712b2f30Ssthen }
1213712b2f30Ssthen }
1214712b2f30Ssthen /* skip remainder of rdata */
1215712b2f30Ssthen *p += rdataremain;
1216712b2f30Ssthen *remain -= rdatalen;
1217712b2f30Ssthen }
1218712b2f30Ssthen
1219712b2f30Ssthen /** lowercase all names in the message */
lowercase_pkt(uint8_t * pkt,size_t pktlen)1220712b2f30Ssthen static void lowercase_pkt(uint8_t* pkt, size_t pktlen)
1221712b2f30Ssthen {
1222712b2f30Ssthen uint16_t i;
1223712b2f30Ssthen uint8_t* p = pkt;
1224712b2f30Ssthen size_t remain = pktlen;
1225712b2f30Ssthen uint16_t t, rdatalen;
1226712b2f30Ssthen if(pktlen < LDNS_HEADER_SIZE)
1227712b2f30Ssthen return;
1228712b2f30Ssthen p += LDNS_HEADER_SIZE;
1229712b2f30Ssthen remain -= LDNS_HEADER_SIZE;
1230712b2f30Ssthen for(i=0; i<LDNS_QDCOUNT(pkt); i++) {
1231712b2f30Ssthen lowercase_dname(&p, &remain);
1232712b2f30Ssthen if(remain < 4) return;
1233712b2f30Ssthen p += 4;
1234712b2f30Ssthen remain -= 4;
1235712b2f30Ssthen }
1236712b2f30Ssthen for(i=0; i<LDNS_ANCOUNT(pkt)+LDNS_NSCOUNT(pkt)+LDNS_ARCOUNT(pkt); i++) {
1237712b2f30Ssthen lowercase_dname(&p, &remain);
1238712b2f30Ssthen if(remain < 10) return;
1239712b2f30Ssthen t = sldns_read_uint16(p);
1240712b2f30Ssthen rdatalen = sldns_read_uint16(p+8);
1241712b2f30Ssthen p += 10;
1242712b2f30Ssthen remain -= 10;
1243712b2f30Ssthen if(remain < rdatalen) return;
1244712b2f30Ssthen lowercase_rdata(&p, &remain, rdatalen, t);
1245712b2f30Ssthen }
1246712b2f30Ssthen }
1247712b2f30Ssthen
1248712b2f30Ssthen /** match question section of packet */
1249712b2f30Ssthen static int
match_question(uint8_t * q,size_t qlen,uint8_t * p,size_t plen,int mttl)1250712b2f30Ssthen match_question(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl)
1251712b2f30Ssthen {
1252712b2f30Ssthen char* qstr, *pstr, *s, *qcmpstr, *pcmpstr;
1253712b2f30Ssthen uint8_t* qb = q, *pb = p;
1254712b2f30Ssthen int r;
1255712b2f30Ssthen /* zero TTLs */
1256712b2f30Ssthen qb = memdup(q, qlen);
1257712b2f30Ssthen pb = memdup(p, plen);
1258712b2f30Ssthen if(!qb || !pb) error("out of memory");
1259712b2f30Ssthen if(!mttl) {
1260712b2f30Ssthen zerottls(qb, qlen);
1261712b2f30Ssthen zerottls(pb, plen);
1262712b2f30Ssthen }
1263712b2f30Ssthen lowercase_pkt(qb, qlen);
1264712b2f30Ssthen lowercase_pkt(pb, plen);
1265712b2f30Ssthen qstr = sldns_wire2str_pkt(qb, qlen);
1266712b2f30Ssthen pstr = sldns_wire2str_pkt(pb, plen);
1267712b2f30Ssthen if(!qstr || !pstr) error("cannot pkt2string");
1268712b2f30Ssthen
1269712b2f30Ssthen /* remove before ;; QUESTION */
1270712b2f30Ssthen s = strstr(qstr, ";; QUESTION SECTION");
1271712b2f30Ssthen qcmpstr = s;
1272712b2f30Ssthen s = strstr(pstr, ";; QUESTION SECTION");
1273712b2f30Ssthen pcmpstr = s;
1274712b2f30Ssthen if(!qcmpstr && !pcmpstr) {
1275712b2f30Ssthen free(qstr);
1276712b2f30Ssthen free(pstr);
1277712b2f30Ssthen free(qb);
1278712b2f30Ssthen free(pb);
1279712b2f30Ssthen return 1;
1280712b2f30Ssthen }
1281712b2f30Ssthen if(!qcmpstr || !pcmpstr) {
1282712b2f30Ssthen free(qstr);
1283712b2f30Ssthen free(pstr);
1284712b2f30Ssthen free(qb);
1285712b2f30Ssthen free(pb);
1286712b2f30Ssthen return 0;
1287712b2f30Ssthen }
1288712b2f30Ssthen
1289c2862f41Ssthen /* remove after answer section, (;; ANS, ;; AUTH, ;; ADD ..) */
1290712b2f30Ssthen s = strstr(qcmpstr, ";; ANSWER SECTION");
1291712b2f30Ssthen if(!s) s = strstr(qcmpstr, ";; AUTHORITY SECTION");
1292712b2f30Ssthen if(!s) s = strstr(qcmpstr, ";; ADDITIONAL SECTION");
1293712b2f30Ssthen if(!s) s = strstr(qcmpstr, ";; MSG SIZE");
1294712b2f30Ssthen if(s) *s = 0;
1295712b2f30Ssthen s = strstr(pcmpstr, ";; ANSWER SECTION");
1296712b2f30Ssthen if(!s) s = strstr(pcmpstr, ";; AUTHORITY SECTION");
1297712b2f30Ssthen if(!s) s = strstr(pcmpstr, ";; ADDITIONAL SECTION");
1298712b2f30Ssthen if(!s) s = strstr(pcmpstr, ";; MSG SIZE");
1299712b2f30Ssthen if(s) *s = 0;
1300712b2f30Ssthen
1301712b2f30Ssthen r = (strcmp(qcmpstr, pcmpstr) == 0);
1302712b2f30Ssthen
1303712b2f30Ssthen if(!r) {
1304712b2f30Ssthen verbose(3, "mismatch question section '%s' and '%s'",
1305712b2f30Ssthen qcmpstr, pcmpstr);
1306712b2f30Ssthen }
1307712b2f30Ssthen
1308712b2f30Ssthen free(qstr);
1309712b2f30Ssthen free(pstr);
1310712b2f30Ssthen free(qb);
1311712b2f30Ssthen free(pb);
1312712b2f30Ssthen return r;
1313712b2f30Ssthen }
1314712b2f30Ssthen
1315712b2f30Ssthen /** match answer section of packet */
1316712b2f30Ssthen static int
match_answer(uint8_t * q,size_t qlen,uint8_t * p,size_t plen,int mttl)1317712b2f30Ssthen match_answer(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl)
1318712b2f30Ssthen {
1319712b2f30Ssthen char* qstr, *pstr, *s, *qcmpstr, *pcmpstr;
1320712b2f30Ssthen uint8_t* qb = q, *pb = p;
1321712b2f30Ssthen int r;
1322712b2f30Ssthen /* zero TTLs */
1323712b2f30Ssthen qb = memdup(q, qlen);
1324712b2f30Ssthen pb = memdup(p, plen);
1325712b2f30Ssthen if(!qb || !pb) error("out of memory");
1326712b2f30Ssthen if(!mttl) {
1327712b2f30Ssthen zerottls(qb, qlen);
1328712b2f30Ssthen zerottls(pb, plen);
1329712b2f30Ssthen }
1330712b2f30Ssthen lowercase_pkt(qb, qlen);
1331712b2f30Ssthen lowercase_pkt(pb, plen);
1332712b2f30Ssthen qstr = sldns_wire2str_pkt(qb, qlen);
1333712b2f30Ssthen pstr = sldns_wire2str_pkt(pb, plen);
1334712b2f30Ssthen if(!qstr || !pstr) error("cannot pkt2string");
1335712b2f30Ssthen
1336712b2f30Ssthen /* remove before ;; ANSWER */
1337712b2f30Ssthen s = strstr(qstr, ";; ANSWER SECTION");
1338712b2f30Ssthen qcmpstr = s;
1339712b2f30Ssthen s = strstr(pstr, ";; ANSWER SECTION");
1340712b2f30Ssthen pcmpstr = s;
1341712b2f30Ssthen if(!qcmpstr && !pcmpstr) {
1342712b2f30Ssthen free(qstr);
1343712b2f30Ssthen free(pstr);
1344712b2f30Ssthen free(qb);
1345712b2f30Ssthen free(pb);
1346712b2f30Ssthen return 1;
1347712b2f30Ssthen }
1348712b2f30Ssthen if(!qcmpstr || !pcmpstr) {
1349712b2f30Ssthen free(qstr);
1350712b2f30Ssthen free(pstr);
1351712b2f30Ssthen free(qb);
1352712b2f30Ssthen free(pb);
1353712b2f30Ssthen return 0;
1354712b2f30Ssthen }
1355712b2f30Ssthen
1356712b2f30Ssthen /* remove after answer section, (;; AUTH, ;; ADD, ;; MSG size ..) */
1357712b2f30Ssthen s = strstr(qcmpstr, ";; AUTHORITY SECTION");
1358712b2f30Ssthen if(!s) s = strstr(qcmpstr, ";; ADDITIONAL SECTION");
1359712b2f30Ssthen if(!s) s = strstr(qcmpstr, ";; MSG SIZE");
1360712b2f30Ssthen if(s) *s = 0;
1361712b2f30Ssthen s = strstr(pcmpstr, ";; AUTHORITY SECTION");
1362712b2f30Ssthen if(!s) s = strstr(pcmpstr, ";; ADDITIONAL SECTION");
1363712b2f30Ssthen if(!s) s = strstr(pcmpstr, ";; MSG SIZE");
1364712b2f30Ssthen if(s) *s = 0;
1365712b2f30Ssthen
1366712b2f30Ssthen r = (strcmp(qcmpstr, pcmpstr) == 0);
1367712b2f30Ssthen
1368712b2f30Ssthen if(!r) {
1369712b2f30Ssthen verbose(3, "mismatch answer section '%s' and '%s'",
1370712b2f30Ssthen qcmpstr, pcmpstr);
1371712b2f30Ssthen }
1372712b2f30Ssthen
1373712b2f30Ssthen free(qstr);
1374712b2f30Ssthen free(pstr);
1375712b2f30Ssthen free(qb);
1376712b2f30Ssthen free(pb);
1377712b2f30Ssthen return r;
1378712b2f30Ssthen }
1379712b2f30Ssthen
1380c2862f41Ssthen /** ignore EDNS lines in the string by overwriting them with what's left or
1381c2862f41Ssthen * zero out if at end of the string */
1382c2862f41Ssthen static int
ignore_edns_lines(char * str)1383c2862f41Ssthen ignore_edns_lines(char* str) {
1384c2862f41Ssthen char* edns = str, *n;
1385c2862f41Ssthen size_t str_len = strlen(str);
1386c2862f41Ssthen while((edns = strstr(edns, "; EDNS"))) {
1387c2862f41Ssthen n = strchr(edns, '\n');
1388c2862f41Ssthen if(!n) {
1389c2862f41Ssthen /* EDNS at end of string; zero */
1390c2862f41Ssthen *edns = 0;
1391c2862f41Ssthen break;
1392c2862f41Ssthen }
1393c2862f41Ssthen memmove(edns, n+1, str_len-(n-str));
1394c2862f41Ssthen }
1395c2862f41Ssthen return 1;
1396c2862f41Ssthen }
1397c2862f41Ssthen
1398712b2f30Ssthen /** match all of the packet */
1399712b2f30Ssthen int
match_all(uint8_t * q,size_t qlen,uint8_t * p,size_t plen,int mttl,int noloc,int noedns)1400712b2f30Ssthen match_all(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl,
1401c2862f41Ssthen int noloc, int noedns)
1402712b2f30Ssthen {
1403712b2f30Ssthen char* qstr, *pstr;
1404712b2f30Ssthen uint8_t* qb = q, *pb = p;
1405712b2f30Ssthen int r;
1406712b2f30Ssthen qb = memdup(q, qlen);
1407712b2f30Ssthen pb = memdup(p, plen);
1408712b2f30Ssthen if(!qb || !pb) error("out of memory");
1409c2862f41Ssthen /* zero TTLs */
1410712b2f30Ssthen if(!mttl) {
1411712b2f30Ssthen zerottls(qb, qlen);
1412712b2f30Ssthen zerottls(pb, plen);
1413712b2f30Ssthen }
1414712b2f30Ssthen lowercase_pkt(qb, qlen);
1415712b2f30Ssthen lowercase_pkt(pb, plen);
1416712b2f30Ssthen qstr = sldns_wire2str_pkt(qb, qlen);
1417712b2f30Ssthen pstr = sldns_wire2str_pkt(pb, plen);
1418712b2f30Ssthen if(!qstr || !pstr) error("cannot pkt2string");
1419c2862f41Ssthen /* should we ignore EDNS lines? */
1420c2862f41Ssthen if(noedns) {
1421c2862f41Ssthen ignore_edns_lines(qstr);
1422c2862f41Ssthen ignore_edns_lines(pstr);
1423c2862f41Ssthen }
1424712b2f30Ssthen r = (strcmp(qstr, pstr) == 0);
1425712b2f30Ssthen if(!r) {
1426712b2f30Ssthen /* remove ;; MSG SIZE (at end of string) */
1427712b2f30Ssthen char* s = strstr(qstr, ";; MSG SIZE");
1428712b2f30Ssthen if(s) *s=0;
1429712b2f30Ssthen s = strstr(pstr, ";; MSG SIZE");
1430712b2f30Ssthen if(s) *s=0;
1431712b2f30Ssthen r = (strcmp(qstr, pstr) == 0);
1432c2862f41Ssthen if(!r && !noloc && !noedns) {
1433c2862f41Ssthen /* we are going to fail, see if the cause is EDNS */
1434712b2f30Ssthen char* a = strstr(qstr, "; EDNS");
1435712b2f30Ssthen char* b = strstr(pstr, "; EDNS");
1436712b2f30Ssthen if( (a&&!b) || (b&&!a) ) {
1437712b2f30Ssthen verbose(3, "mismatch in EDNS\n");
1438712b2f30Ssthen }
1439712b2f30Ssthen }
1440712b2f30Ssthen }
1441712b2f30Ssthen if(!r && noloc) {
1442712b2f30Ssthen /* check for reordered sections */
1443712b2f30Ssthen r = match_noloc(qstr, pstr, q, qlen, p, plen);
1444712b2f30Ssthen }
1445712b2f30Ssthen if(!r) {
1446712b2f30Ssthen verbose(3, "mismatch pkt '%s' and '%s'", qstr, pstr);
1447712b2f30Ssthen }
1448712b2f30Ssthen free(qstr);
1449712b2f30Ssthen free(pstr);
1450712b2f30Ssthen free(qb);
1451712b2f30Ssthen free(pb);
1452712b2f30Ssthen return r;
1453712b2f30Ssthen }
1454712b2f30Ssthen
1455712b2f30Ssthen /** see if domain names are equal */
equal_dname(uint8_t * q,size_t qlen,uint8_t * p,size_t plen)1456712b2f30Ssthen static int equal_dname(uint8_t* q, size_t qlen, uint8_t* p, size_t plen)
1457712b2f30Ssthen {
1458712b2f30Ssthen uint8_t* qn = get_qname(q, qlen);
1459712b2f30Ssthen uint8_t* pn = get_qname(p, plen);
1460712b2f30Ssthen char qs[512], ps[512];
1461712b2f30Ssthen size_t qslen = sizeof(qs), pslen = sizeof(ps);
1462712b2f30Ssthen char* qss = qs, *pss = ps;
1463b0dfc31bSsthen int comprloop = 0;
1464712b2f30Ssthen if(!qn || !pn)
1465712b2f30Ssthen return 0;
1466b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen, &comprloop);
1467b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen, &comprloop);
1468712b2f30Ssthen return (strcmp(qs, ps) == 0);
1469712b2f30Ssthen }
1470712b2f30Ssthen
1471712b2f30Ssthen /** see if domain names are subdomain q of p */
subdomain_dname(uint8_t * q,size_t qlen,uint8_t * p,size_t plen)1472712b2f30Ssthen static int subdomain_dname(uint8_t* q, size_t qlen, uint8_t* p, size_t plen)
1473712b2f30Ssthen {
1474712b2f30Ssthen /* we use the tostring routines so as to test unbound's routines
1475712b2f30Ssthen * with something else */
1476712b2f30Ssthen uint8_t* qn = get_qname(q, qlen);
1477712b2f30Ssthen uint8_t* pn = get_qname(p, plen);
1478712b2f30Ssthen char qs[5120], ps[5120];
1479712b2f30Ssthen size_t qslen = sizeof(qs), pslen = sizeof(ps);
1480712b2f30Ssthen char* qss = qs, *pss = ps;
1481b0dfc31bSsthen int comprloop = 0;
1482712b2f30Ssthen if(!qn || !pn)
1483712b2f30Ssthen return 0;
1484712b2f30Ssthen /* decompresses domain names */
1485b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&qn, &qlen, &qss, &qslen, q, qlen, &comprloop);
1486b0dfc31bSsthen (void)sldns_wire2str_dname_scan(&pn, &plen, &pss, &pslen, p, plen, &comprloop);
1487712b2f30Ssthen /* same: false, (strict subdomain check)??? */
1488712b2f30Ssthen if(strcmp(qs, ps) == 0)
1489712b2f30Ssthen return 1;
1490712b2f30Ssthen /* qs must end in ps, at a dot, without \ in front */
1491712b2f30Ssthen qslen = strlen(qs);
1492712b2f30Ssthen pslen = strlen(ps);
1493712b2f30Ssthen if(qslen > pslen && strcmp(qs + (qslen-pslen), ps) == 0 &&
1494712b2f30Ssthen qslen + 2 >= pslen && /* space for label and dot */
1495712b2f30Ssthen qs[qslen-pslen-1] == '.') {
1496712b2f30Ssthen unsigned int slashcount = 0;
1497712b2f30Ssthen size_t i = qslen-pslen-2;
1498712b2f30Ssthen while(i>0 && qs[i]=='\\') {
1499712b2f30Ssthen i++;
1500712b2f30Ssthen slashcount++;
1501712b2f30Ssthen }
1502712b2f30Ssthen if(slashcount%1 == 1) return 0; /* . preceded by \ */
1503712b2f30Ssthen return 1;
1504712b2f30Ssthen }
1505712b2f30Ssthen return 0;
1506712b2f30Ssthen }
1507712b2f30Ssthen
1508712b2f30Ssthen /** Match OPT RDATA (not the EDNS payload size or flags) */
1509712b2f30Ssthen static int
match_ednsdata(uint8_t * q,size_t qlen,uint8_t * p,size_t plen)1510712b2f30Ssthen match_ednsdata(uint8_t* q, size_t qlen, uint8_t* p, size_t plen)
1511712b2f30Ssthen {
1512712b2f30Ssthen uint8_t* walk_q = q;
1513712b2f30Ssthen size_t walk_qlen = qlen;
1514712b2f30Ssthen uint8_t* walk_p = p;
1515712b2f30Ssthen size_t walk_plen = plen;
1516712b2f30Ssthen
1517712b2f30Ssthen if(!pkt_find_edns_opt(&walk_q, &walk_qlen))
1518712b2f30Ssthen walk_qlen = 0;
1519712b2f30Ssthen if(!pkt_find_edns_opt(&walk_p, &walk_plen))
1520712b2f30Ssthen walk_plen = 0;
1521712b2f30Ssthen
1522712b2f30Ssthen /* class + ttl + rdlen = 8 */
1523712b2f30Ssthen if(walk_qlen <= 8 && walk_plen <= 8) {
1524712b2f30Ssthen verbose(3, "NO edns opt, move on");
1525712b2f30Ssthen return 1;
1526712b2f30Ssthen }
1527712b2f30Ssthen if(walk_qlen != walk_plen)
1528712b2f30Ssthen return 0;
1529712b2f30Ssthen
1530712b2f30Ssthen return (memcmp(walk_p+8, walk_q+8, walk_qlen-8) == 0);
1531712b2f30Ssthen }
1532712b2f30Ssthen
1533712b2f30Ssthen /* finds entry in list, or returns NULL */
1534712b2f30Ssthen struct entry*
find_match(struct entry * entries,uint8_t * query_pkt,size_t len,enum transport_type transport)1535712b2f30Ssthen find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
1536712b2f30Ssthen enum transport_type transport)
1537712b2f30Ssthen {
1538712b2f30Ssthen struct entry* p = entries;
1539c2862f41Ssthen uint8_t* reply, *query_pkt_orig;
1540c2862f41Ssthen size_t rlen, query_pkt_orig_len;
1541c2862f41Ssthen /* Keep the original packet; it may be modified */
1542c2862f41Ssthen query_pkt_orig = memdup(query_pkt, len);
1543c2862f41Ssthen query_pkt_orig_len = len;
1544712b2f30Ssthen for(p=entries; p; p=p->next) {
1545712b2f30Ssthen verbose(3, "comparepkt: ");
1546712b2f30Ssthen reply = p->reply_list->reply_pkt;
1547712b2f30Ssthen rlen = p->reply_list->reply_len;
1548c2862f41Ssthen /* Restore the original packet for each entry */
1549c2862f41Ssthen memcpy(query_pkt, query_pkt_orig, query_pkt_orig_len);
1550c2862f41Ssthen /* EDE should be first since it may modify the query_pkt */
1551c2862f41Ssthen if(p->match_ede) {
1552c2862f41Ssthen int info_code = extract_ede(query_pkt, len);
1553c2862f41Ssthen if(info_code == -1) {
1554c2862f41Ssthen verbose(3, "bad EDE. Expected but not found\n");
1555c2862f41Ssthen continue;
1556c2862f41Ssthen } else if(!p->match_ede_any &&
1557c2862f41Ssthen (uint16_t)info_code != p->ede_info_code) {
1558c2862f41Ssthen verbose(3, "bad EDE INFO-CODE. Expected: %d, "
1559c2862f41Ssthen "and got: %d\n", (int)p->ede_info_code,
1560c2862f41Ssthen info_code);
1561c2862f41Ssthen continue;
1562c2862f41Ssthen }
1563c2862f41Ssthen }
1564437d2860Ssthen /* Cookies could also modify the query_pkt; keep them early */
1565437d2860Ssthen if(p->match_client_cookie || p->match_server_cookie) {
1566437d2860Ssthen uint8_t cookie[24];
1567437d2860Ssthen int cookie_len = extract_cookie(query_pkt, len,
1568437d2860Ssthen cookie);
1569437d2860Ssthen if(cookie_len == -1) {
1570437d2860Ssthen verbose(3, "bad DNS Cookie. "
1571437d2860Ssthen "Expected but not found\n");
1572437d2860Ssthen continue;
1573437d2860Ssthen } else if(p->match_client_cookie &&
1574437d2860Ssthen cookie_len != 8) {
1575437d2860Ssthen verbose(3, "bad DNS Cookie. Expected client "
1576437d2860Ssthen "cookie of length 8.");
1577437d2860Ssthen continue;
1578437d2860Ssthen } else if((p->match_server_cookie) &&
1579437d2860Ssthen cookie_len != 24) {
1580437d2860Ssthen verbose(3, "bad DNS Cookie. Expected server "
1581437d2860Ssthen "cookie of length 24.");
1582437d2860Ssthen continue;
1583437d2860Ssthen }
1584437d2860Ssthen }
1585712b2f30Ssthen if(p->match_opcode && get_opcode(query_pkt, len) !=
1586712b2f30Ssthen get_opcode(reply, rlen)) {
1587712b2f30Ssthen verbose(3, "bad opcode\n");
1588712b2f30Ssthen continue;
1589712b2f30Ssthen }
1590712b2f30Ssthen if(p->match_qtype && get_qtype(query_pkt, len) !=
1591712b2f30Ssthen get_qtype(reply, rlen)) {
1592712b2f30Ssthen verbose(3, "bad qtype %d %d\n", get_qtype(query_pkt, len), get_qtype(reply, rlen));
1593712b2f30Ssthen continue;
1594712b2f30Ssthen }
1595712b2f30Ssthen if(p->match_qname) {
1596712b2f30Ssthen if(!equal_dname(query_pkt, len, reply, rlen)) {
1597712b2f30Ssthen verbose(3, "bad qname\n");
1598712b2f30Ssthen continue;
1599712b2f30Ssthen }
1600712b2f30Ssthen }
1601712b2f30Ssthen if(p->match_rcode) {
1602712b2f30Ssthen if(get_rcode(query_pkt, len) != get_rcode(reply, rlen)) {
1603712b2f30Ssthen char *r1 = sldns_wire2str_rcode(get_rcode(query_pkt, len));
1604712b2f30Ssthen char *r2 = sldns_wire2str_rcode(get_rcode(reply, rlen));
1605712b2f30Ssthen verbose(3, "bad rcode %s instead of %s\n",
1606712b2f30Ssthen r1, r2);
1607712b2f30Ssthen free(r1);
1608712b2f30Ssthen free(r2);
1609712b2f30Ssthen continue;
1610712b2f30Ssthen }
1611712b2f30Ssthen }
1612712b2f30Ssthen if(p->match_question) {
1613712b2f30Ssthen if(!match_question(query_pkt, len, reply, rlen,
1614712b2f30Ssthen (int)p->match_ttl)) {
1615712b2f30Ssthen verbose(3, "bad question section\n");
1616712b2f30Ssthen continue;
1617712b2f30Ssthen }
1618712b2f30Ssthen }
1619712b2f30Ssthen if(p->match_answer) {
1620712b2f30Ssthen if(!match_answer(query_pkt, len, reply, rlen,
1621712b2f30Ssthen (int)p->match_ttl)) {
1622712b2f30Ssthen verbose(3, "bad answer section\n");
1623712b2f30Ssthen continue;
1624712b2f30Ssthen }
1625712b2f30Ssthen }
1626712b2f30Ssthen if(p->match_subdomain) {
1627712b2f30Ssthen if(!subdomain_dname(query_pkt, len, reply, rlen)) {
1628712b2f30Ssthen verbose(3, "bad subdomain\n");
1629712b2f30Ssthen continue;
1630712b2f30Ssthen }
1631712b2f30Ssthen }
1632712b2f30Ssthen if(p->match_serial && get_serial(query_pkt, len) != p->ixfr_soa_serial) {
1633712b2f30Ssthen verbose(3, "bad serial\n");
1634712b2f30Ssthen continue;
1635712b2f30Ssthen }
1636712b2f30Ssthen if(p->match_do && !get_do_flag(query_pkt, len)) {
1637712b2f30Ssthen verbose(3, "no DO bit set\n");
1638712b2f30Ssthen continue;
1639712b2f30Ssthen }
1640712b2f30Ssthen if(p->match_noedns && get_has_edns(query_pkt, len)) {
1641712b2f30Ssthen verbose(3, "bad; EDNS OPT present\n");
1642712b2f30Ssthen continue;
1643712b2f30Ssthen }
1644712b2f30Ssthen if(p->match_ednsdata_raw &&
1645712b2f30Ssthen !match_ednsdata(query_pkt, len, reply, rlen)) {
1646712b2f30Ssthen verbose(3, "bad EDNS data match.\n");
1647712b2f30Ssthen continue;
1648712b2f30Ssthen }
1649712b2f30Ssthen if(p->match_transport != transport_any && p->match_transport != transport) {
1650712b2f30Ssthen verbose(3, "bad transport\n");
1651712b2f30Ssthen continue;
1652712b2f30Ssthen }
1653c2862f41Ssthen if(p->match_all_noedns && !match_all(query_pkt, len, reply,
1654c2862f41Ssthen rlen, (int)p->match_ttl, 0, 1)) {
1655c2862f41Ssthen verbose(3, "bad all_noedns match\n");
1656c2862f41Ssthen continue;
1657c2862f41Ssthen }
1658712b2f30Ssthen if(p->match_all && !match_all(query_pkt, len, reply, rlen,
1659c2862f41Ssthen (int)p->match_ttl, 0, 0)) {
1660712b2f30Ssthen verbose(3, "bad allmatch\n");
1661712b2f30Ssthen continue;
1662712b2f30Ssthen }
1663712b2f30Ssthen verbose(3, "match!\n");
1664c2862f41Ssthen /* Restore the original packet */
1665c2862f41Ssthen memcpy(query_pkt, query_pkt_orig, query_pkt_orig_len);
1666c2862f41Ssthen free(query_pkt_orig);
1667712b2f30Ssthen return p;
1668712b2f30Ssthen }
1669c2862f41Ssthen /* Restore the original packet */
1670c2862f41Ssthen memcpy(query_pkt, query_pkt_orig, query_pkt_orig_len);
1671c2862f41Ssthen free(query_pkt_orig);
1672712b2f30Ssthen return NULL;
1673712b2f30Ssthen }
1674712b2f30Ssthen
1675712b2f30Ssthen void
adjust_packet(struct entry * match,uint8_t ** answer_pkt,size_t * answer_len,uint8_t * query_pkt,size_t query_len)1676712b2f30Ssthen adjust_packet(struct entry* match, uint8_t** answer_pkt, size_t *answer_len,
1677712b2f30Ssthen uint8_t* query_pkt, size_t query_len)
1678712b2f30Ssthen {
1679712b2f30Ssthen uint8_t* orig = *answer_pkt;
1680712b2f30Ssthen size_t origlen = *answer_len;
1681712b2f30Ssthen uint8_t* res;
1682712b2f30Ssthen size_t reslen;
1683712b2f30Ssthen
1684712b2f30Ssthen /* perform the copy; if possible; must be uncompressed */
1685712b2f30Ssthen if(match->copy_query && origlen >= LDNS_HEADER_SIZE &&
1686712b2f30Ssthen query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0
1687712b2f30Ssthen && LDNS_QDCOUNT(orig)==0) {
1688712b2f30Ssthen /* no qname in output packet, insert it */
1689712b2f30Ssthen size_t dlen = get_qname_len(query_pkt, query_len);
1690712b2f30Ssthen reslen = origlen + dlen + 4;
1691712b2f30Ssthen res = (uint8_t*)malloc(reslen);
1692712b2f30Ssthen if(!res) {
1693712b2f30Ssthen verbose(1, "out of memory; send without adjust\n");
1694712b2f30Ssthen return;
1695712b2f30Ssthen }
1696712b2f30Ssthen /* copy the header, query, remainder */
1697712b2f30Ssthen memcpy(res, orig, LDNS_HEADER_SIZE);
1698712b2f30Ssthen memmove(res+LDNS_HEADER_SIZE, query_pkt+LDNS_HEADER_SIZE,
1699712b2f30Ssthen dlen+4);
1700712b2f30Ssthen memmove(res+LDNS_HEADER_SIZE+dlen+4, orig+LDNS_HEADER_SIZE,
1701712b2f30Ssthen reslen-(LDNS_HEADER_SIZE+dlen+4));
1702712b2f30Ssthen /* set QDCOUNT */
1703712b2f30Ssthen sldns_write_uint16(res+4, 1);
1704712b2f30Ssthen } else if(match->copy_query && origlen >= LDNS_HEADER_SIZE &&
1705712b2f30Ssthen query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0
1706712b2f30Ssthen && get_qname_len(orig, origlen) == 0) {
1707712b2f30Ssthen /* QDCOUNT(orig)!=0 but qlen == 0, therefore, an error */
1708712b2f30Ssthen verbose(1, "error: malformed qname; send without adjust\n");
1709712b2f30Ssthen res = memdup(orig, origlen);
1710712b2f30Ssthen reslen = origlen;
1711712b2f30Ssthen } else if(match->copy_query && origlen >= LDNS_HEADER_SIZE &&
1712712b2f30Ssthen query_len >= LDNS_HEADER_SIZE && LDNS_QDCOUNT(query_pkt)!=0
1713712b2f30Ssthen && LDNS_QDCOUNT(orig)!=0) {
1714712b2f30Ssthen /* in this case olen != 0 and QDCOUNT(orig)!=0 */
1715712b2f30Ssthen /* copy query section */
1716712b2f30Ssthen size_t dlen = get_qname_len(query_pkt, query_len);
1717712b2f30Ssthen size_t olen = get_qname_len(orig, origlen);
1718712b2f30Ssthen reslen = origlen + dlen - olen;
1719712b2f30Ssthen res = (uint8_t*)malloc(reslen);
1720712b2f30Ssthen if(!res) {
1721712b2f30Ssthen verbose(1, "out of memory; send without adjust\n");
1722712b2f30Ssthen return;
1723712b2f30Ssthen }
1724712b2f30Ssthen /* copy the header, query, remainder */
1725712b2f30Ssthen memcpy(res, orig, LDNS_HEADER_SIZE);
1726712b2f30Ssthen memmove(res+LDNS_HEADER_SIZE, query_pkt+LDNS_HEADER_SIZE,
1727712b2f30Ssthen dlen+4);
1728712b2f30Ssthen memmove(res+LDNS_HEADER_SIZE+dlen+4,
1729712b2f30Ssthen orig+LDNS_HEADER_SIZE+olen+4,
1730712b2f30Ssthen reslen-(LDNS_HEADER_SIZE+dlen+4));
1731712b2f30Ssthen } else {
1732712b2f30Ssthen res = memdup(orig, origlen);
1733712b2f30Ssthen reslen = origlen;
1734712b2f30Ssthen }
1735712b2f30Ssthen if(!res) {
1736712b2f30Ssthen verbose(1, "out of memory; send without adjust\n");
1737712b2f30Ssthen return;
1738712b2f30Ssthen }
1739712b2f30Ssthen /* copy the ID */
1740712b2f30Ssthen if(match->copy_id && reslen >= 2 && query_len >= 2)
1741712b2f30Ssthen res[1] = query_pkt[1];
1742712b2f30Ssthen if(match->copy_id && reslen >= 1 && query_len >= 1)
1743712b2f30Ssthen res[0] = query_pkt[0];
1744712b2f30Ssthen
1745712b2f30Ssthen if(match->copy_ednsdata_assume_clientsubnet) {
1746712b2f30Ssthen /** Assume there is only one EDNS option, which is ECS.
1747712b2f30Ssthen * Copy source mask from query to scope mask in reply. Assume
1748712b2f30Ssthen * rest of ECS data in response (eg address) matches the query.
1749712b2f30Ssthen */
1750712b2f30Ssthen uint8_t* walk_q = orig;
1751712b2f30Ssthen size_t walk_qlen = origlen;
1752712b2f30Ssthen uint8_t* walk_p = res;
1753712b2f30Ssthen size_t walk_plen = reslen;
1754712b2f30Ssthen
1755712b2f30Ssthen if(!pkt_find_edns_opt(&walk_q, &walk_qlen)) {
1756712b2f30Ssthen walk_qlen = 0;
1757712b2f30Ssthen }
1758712b2f30Ssthen if(!pkt_find_edns_opt(&walk_p, &walk_plen)) {
1759712b2f30Ssthen walk_plen = 0;
1760712b2f30Ssthen }
1761712b2f30Ssthen /* class + ttl + rdlen + optcode + optlen + ecs fam + ecs source
1762712b2f30Ssthen * + ecs scope = index 15 */
1763712b2f30Ssthen if(walk_qlen >= 15 && walk_plen >= 15) {
1764712b2f30Ssthen walk_p[15] = walk_q[14];
1765712b2f30Ssthen }
1766dc90232dSsthen if(match->increment_ecs_scope) {
1767dc90232dSsthen walk_p[15]++;
1768dc90232dSsthen }
1769712b2f30Ssthen }
1770712b2f30Ssthen
1771712b2f30Ssthen if(match->sleeptime > 0) {
1772712b2f30Ssthen verbose(3, "sleeping for %d seconds\n", match->sleeptime);
1773712b2f30Ssthen #ifdef HAVE_SLEEP
1774712b2f30Ssthen sleep(match->sleeptime);
1775712b2f30Ssthen #else
1776712b2f30Ssthen Sleep(match->sleeptime * 1000);
1777712b2f30Ssthen #endif
1778712b2f30Ssthen }
1779712b2f30Ssthen *answer_pkt = res;
1780712b2f30Ssthen *answer_len = reslen;
1781712b2f30Ssthen }
1782712b2f30Ssthen
1783712b2f30Ssthen /*
1784712b2f30Ssthen * Parses data buffer to a query, finds the correct answer
1785712b2f30Ssthen * and calls the given function for every packet to send.
1786712b2f30Ssthen */
1787712b2f30Ssthen 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)1788712b2f30Ssthen handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count,
1789712b2f30Ssthen enum transport_type transport, void (*sendfunc)(uint8_t*, size_t, void*),
1790712b2f30Ssthen void* userdata, FILE* verbose_out)
1791712b2f30Ssthen {
1792712b2f30Ssthen struct reply_packet *p;
1793712b2f30Ssthen uint8_t *outbuf = NULL;
1794712b2f30Ssthen size_t outlen = 0;
1795712b2f30Ssthen struct entry* entry = NULL;
1796712b2f30Ssthen
1797712b2f30Ssthen verbose(1, "query %d: id %d: %s %d bytes: ", ++(*count),
1798712b2f30Ssthen (int)(inlen>=2?LDNS_ID_WIRE(inbuf):0),
1799712b2f30Ssthen (transport==transport_tcp)?"TCP":"UDP", (int)inlen);
1800712b2f30Ssthen if(verbose_out) {
1801712b2f30Ssthen char* out = sldns_wire2str_pkt(inbuf, (size_t)inlen);
1802712b2f30Ssthen printf("%s\n", out);
1803712b2f30Ssthen free(out);
1804712b2f30Ssthen }
1805712b2f30Ssthen
1806712b2f30Ssthen /* fill up answer packet */
1807712b2f30Ssthen entry = find_match(entries, inbuf, (size_t)inlen, transport);
1808712b2f30Ssthen if(!entry || !entry->reply_list) {
1809712b2f30Ssthen verbose(1, "no answer packet for this query, no reply.\n");
1810712b2f30Ssthen return;
1811712b2f30Ssthen }
1812712b2f30Ssthen for(p = entry->reply_list; p; p = p->next)
1813712b2f30Ssthen {
1814712b2f30Ssthen verbose(3, "Answer pkt:\n");
1815712b2f30Ssthen if (p->reply_from_hex) {
1816712b2f30Ssthen /* try to adjust the hex packet, if it can be
1817712b2f30Ssthen * parsed, we can use adjust rules. if not,
1818712b2f30Ssthen * send packet literally */
1819712b2f30Ssthen /* still try to adjust ID if others fail */
1820712b2f30Ssthen outlen = sldns_buffer_limit(p->reply_from_hex);
1821712b2f30Ssthen outbuf = sldns_buffer_begin(p->reply_from_hex);
1822712b2f30Ssthen } else {
1823712b2f30Ssthen outbuf = p->reply_pkt;
1824712b2f30Ssthen outlen = p->reply_len;
1825712b2f30Ssthen }
1826712b2f30Ssthen if(!outbuf) {
1827712b2f30Ssthen verbose(1, "out of memory\n");
1828712b2f30Ssthen return;
1829712b2f30Ssthen }
1830712b2f30Ssthen /* copies outbuf in memory allocation */
1831712b2f30Ssthen adjust_packet(entry, &outbuf, &outlen, inbuf, (size_t)inlen);
1832712b2f30Ssthen verbose(1, "Answer packet size: %u bytes.\n", (unsigned int)outlen);
1833712b2f30Ssthen if(verbose_out) {
1834712b2f30Ssthen char* out = sldns_wire2str_pkt(outbuf, outlen);
1835712b2f30Ssthen printf("%s\n", out);
1836712b2f30Ssthen free(out);
1837712b2f30Ssthen }
1838712b2f30Ssthen if(p->packet_sleep) {
1839712b2f30Ssthen verbose(3, "sleeping for next packet %d secs\n",
1840712b2f30Ssthen p->packet_sleep);
1841712b2f30Ssthen #ifdef HAVE_SLEEP
1842712b2f30Ssthen sleep(p->packet_sleep);
1843712b2f30Ssthen #else
1844712b2f30Ssthen Sleep(p->packet_sleep * 1000);
1845712b2f30Ssthen #endif
1846712b2f30Ssthen verbose(3, "wakeup for next packet "
1847712b2f30Ssthen "(slept %d secs)\n", p->packet_sleep);
1848712b2f30Ssthen }
1849712b2f30Ssthen sendfunc(outbuf, outlen, userdata);
1850712b2f30Ssthen free(outbuf);
1851712b2f30Ssthen outbuf = NULL;
1852712b2f30Ssthen outlen = 0;
1853712b2f30Ssthen }
1854712b2f30Ssthen }
1855712b2f30Ssthen
1856712b2f30Ssthen /** delete the list of reply packets */
delete_replylist(struct reply_packet * replist)1857712b2f30Ssthen void delete_replylist(struct reply_packet* replist)
1858712b2f30Ssthen {
1859712b2f30Ssthen struct reply_packet *p=replist, *np;
1860712b2f30Ssthen while(p) {
1861712b2f30Ssthen np = p->next;
1862712b2f30Ssthen free(p->reply_pkt);
1863712b2f30Ssthen sldns_buffer_free(p->reply_from_hex);
1864712b2f30Ssthen sldns_buffer_free(p->raw_ednsdata);
1865712b2f30Ssthen free(p);
1866712b2f30Ssthen p=np;
1867712b2f30Ssthen }
1868712b2f30Ssthen }
1869712b2f30Ssthen
delete_entry(struct entry * list)1870712b2f30Ssthen void delete_entry(struct entry* list)
1871712b2f30Ssthen {
1872712b2f30Ssthen struct entry *p=list, *np;
1873712b2f30Ssthen while(p) {
1874712b2f30Ssthen np = p->next;
1875712b2f30Ssthen delete_replylist(p->reply_list);
1876712b2f30Ssthen free(p);
1877712b2f30Ssthen p = np;
1878712b2f30Ssthen }
1879712b2f30Ssthen }
1880