1 /*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 #include <tap/basic.h>
19 
20 #include "libknot/packet/rrset-wire.h"
21 #include "libknot/descriptor.h"
22 #include "libknot/errcode.h"
23 
24 // Wire initializers
25 
26 #define MESSAGE_HEADER(AN, AUTH, ADD)  0xd4, 0xec, 0x81, 0xa0, 0x00, 0x01, \
27                                        0x00, AN, 0x00, AUTH, 0x00, ADD
28 
29 #define QUERY(qname, type) qname, 0x00, type, 0x00, 0x01
30 
31 #define RR_HEADER(owner, type, rdlength0, rdlength1) owner, 0x00, type, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, rdlength0, rdlength1
32 
33 #define QNAME_POINTER 0xc0, 0x0c
34 
35 // Initializers' sizes
36 
37 #define QUERY_SIZE 12 + 4
38 #define RR_HEADER_SIZE 10
39 
40 // Sample domain names
41 
42 #define QNAME 0x03, 0x6e, 0x69, 0x63, 0x02, 0x63, 0x7a, 0x00
43 #define QNAME_SIZE 8
44 #define QNAME_LONG \
45 0x3f,'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
46 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', \
47 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
48 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'w', 'y', \
49 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,\
50 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
51 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', \
52 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
53 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', \
54 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,'a', \
55 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
56 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', \
57 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
58 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', \
59 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'i', 'k', 0x3d,'a', 'b', \
60 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
61 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', \
62 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
63 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', 'b', \
64 'c', 'd', 'e', 'f', 'g', 'h', 'i', 0x00
65 #define QNAME_LONG_SIZE 255
66 #define POINTER_SIZE 2
67 
68 struct wire_data {
69 	uint8_t wire[65535];
70 	size_t size;
71 	size_t pos;
72 	int code;
73 	const char *msg;
74 };
75 
76 #define FROM_CASE_COUNT 17
77 
78 static const struct wire_data FROM_CASES[FROM_CASE_COUNT] = {
79 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A)},
80   .size = QUERY_SIZE + QNAME_SIZE,
81   .pos = QUERY_SIZE + QNAME_SIZE,
82   .code = KNOT_EMALF,
83   .msg = "No header" },
84 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A), 0x00, 0x00, 0x01},
85   .size = QUERY_SIZE + QNAME_SIZE + 3,
86   .pos = QUERY_SIZE + QNAME_SIZE,
87   .code = KNOT_EMALF,
88   .msg = "Partial header" },
89 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
90             RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04) },
91   .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2,
92   .pos = QUERY_SIZE + QNAME_SIZE,
93   .code = KNOT_EMALF,
94   .msg = "No RDATA" },
95 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
96             RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01 },
97   .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 1,
98   .pos = QUERY_SIZE + QNAME_SIZE,
99   .code = KNOT_EMALF,
100   .msg = "Partial RDATA" },
101 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
102             RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01, 0x02, 0x03, 0x04 },
103   .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 4,
104   .pos = QUERY_SIZE + QNAME_SIZE,
105   .code = KNOT_EOK,
106   .msg = "OK RDATA" },
107 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
108             RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x05), 0x01, 0x02, 0x03, 0x04, 0x05 },
109   .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 5,
110   .pos = QUERY_SIZE + QNAME_SIZE,
111   .code = KNOT_EMALF,
112   .msg = "Trailing RDATA" },
113 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SOA),
114             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SOA, 0x00, 0x18), QNAME_POINTER, QNAME_POINTER,
115             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
117   .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_LONG_SIZE + 6 + 20,
118   .pos = QUERY_SIZE + QNAME_LONG_SIZE,
119   .code = KNOT_EOK,
120   .msg = "Max DNAME" },
121 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_SIG),
122             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xdb),
123             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME },
125   .size = 65535,
126   .pos = QUERY_SIZE + QNAME_SIZE,
127   .code = KNOT_EOK,
128   .msg = "Max RDLENGTH" },
129 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SIG),
130             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xff),
131             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME_POINTER },
133   .size = 65535 + QNAME_LONG_SIZE + QUERY_SIZE + RR_HEADER_SIZE + 2,
134   .pos = QUERY_SIZE + QNAME_LONG_SIZE,
135   .code = KNOT_EMALF,
136   .msg = "Max RDLENGTH + compression"},
137 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NSEC),
138             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NSEC, 0x00, 0x03),
139             QNAME_POINTER, 0x00},
140   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 2 + 1,
141   .pos = QUERY_SIZE + QNAME_SIZE,
142   .code = KNOT_EOK,
143   .msg = "DNAME wrong compression"},
144 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
145             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x01),
146             0x00},
147   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 1,
148   .pos = QUERY_SIZE + QNAME_SIZE,
149   .code = KNOT_EMALF,
150   .msg = "NAPTR missing header"},
151 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
152             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
153             0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, QNAME_POINTER},
154   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 9,
155   .pos = QUERY_SIZE + QNAME_SIZE,
156   .code = KNOT_EMALF,
157   .msg = "NAPTR bad offset"},
158 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
159             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
160             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
161   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 7,
162   .pos = QUERY_SIZE + QNAME_SIZE,
163   .code = KNOT_EMALF,
164   .msg = "NAPTR no DNAME"},
165 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
166             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x0c),
167             0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, QNAME_POINTER},
168   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 10 + 2,
169   .pos = QUERY_SIZE + QNAME_SIZE,
170   .code = KNOT_EOK,
171   .msg = "NAPTR valid"},
172 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_APL),
173             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_APL, 0x00, 0x00) },
174   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
175   .pos = QUERY_SIZE + QNAME_SIZE,
176   .code = KNOT_EOK,
177   .msg = "Valid 0 RDATA"},
178 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_TXT),
179             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_TXT, 0x00, 0x00) },
180   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
181   .pos = QUERY_SIZE + QNAME_SIZE,
182   .code = KNOT_EMALF,
183   .msg = "Invalid 0 RDATA"},
184 { .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_PX),
185             RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_PX, 0x00, 0x06),
186             0x00, 0x00, QNAME_POINTER, QNAME_POINTER },
187   .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 6,
188   .pos = QUERY_SIZE + QNAME_SIZE,
189   .code = KNOT_EOK,
190   .msg = "Obsolete RR type"},
191 };
192 
193 #define TEST_CASE_FROM(rrset, i) size_t _pos##i = FROM_CASES[i].pos; \
194 	ok(knot_rrset_rr_from_wire(FROM_CASES[i].wire, &_pos##i, FROM_CASES[i].size, \
195 	rrset, NULL, true) == FROM_CASES[i].code, "rrset wire: %s", FROM_CASES[i].msg)
196 
test_inputs(void)197 static void test_inputs(void)
198 {
199 	for (size_t i = 0; i < FROM_CASE_COUNT; ++i) {
200 		knot_rrset_t rrset;
201 		knot_rrset_init_empty(&rrset);
202 		TEST_CASE_FROM(&rrset, i);
203 		knot_rrset_clear(&rrset, NULL);
204 	}
205 }
206 
check_canon(uint8_t * wire,size_t size,size_t pos,bool canon,knot_dname_t * qname,knot_dname_t * dname)207 static void check_canon(uint8_t *wire, size_t size, size_t pos, bool canon,
208                         knot_dname_t *qname, knot_dname_t *dname)
209 {
210 	knot_rrset_t rrset;
211 	knot_rrset_init_empty(&rrset);
212 
213 	int ret = knot_rrset_rr_from_wire(wire, &pos, size, &rrset, NULL, canon);
214 	is_int(KNOT_EOK, ret, "OK %s canonization", canon ? "with" : "without");
215 	ok(memcmp(rrset.owner, qname, knot_dname_size(qname)) == 0, "compare owner");
216 
217 	uint8_t *rdata = rrset.rrs.rdata->data;
218 	ok(memcmp(rdata, dname, knot_dname_size(dname)) == 0, "compare rdata dname");
219 
220 	knot_rrset_clear(&rrset, NULL);
221 }
222 
test_canonization(void)223 static void test_canonization(void)
224 {
225 	#define UPP_QNAME_SIZE	5
226 	#define UPP_QNAME 0x01, 0x41, 0x01, 0x5a, 0x00	// A.Z.
227 	#define LOW_QNAME 0x01, 0x61, 0x01, 0x7a, 0x00	// a.z.
228 
229 	#define UPP_DNAME_SIZE	3
230 	#define UPP_DNAME 0x01, 0x58, 0x00	// X.
231 	#define LOW_DNAME 0x01, 0x78, 0x00	// x.
232 
233 	uint8_t wire[] = {
234 		MESSAGE_HEADER(1, 0, 0), QUERY(UPP_QNAME, KNOT_RRTYPE_NS),
235 		RR_HEADER(UPP_QNAME, KNOT_RRTYPE_NS, 0x00, UPP_DNAME_SIZE), UPP_DNAME
236 	};
237 	size_t size = QUERY_SIZE + RR_HEADER_SIZE + UPP_QNAME_SIZE * 2 + UPP_DNAME_SIZE;
238 	size_t pos = QUERY_SIZE + UPP_QNAME_SIZE;
239 
240 	knot_dname_t upp_qname[] = { UPP_QNAME };
241 	knot_dname_t upp_dname[] = { UPP_DNAME };
242 	check_canon(wire, size, pos, false, upp_qname, upp_dname);
243 
244 	knot_dname_t low_qname[] = { LOW_QNAME };
245 	knot_dname_t low_dname[] = { LOW_DNAME };
246 	check_canon(wire, size, pos, true, low_qname, low_dname);
247 }
248 
main(int argc,char * argv[])249 int main(int argc, char *argv[])
250 {
251 	plan_lazy();
252 
253 	diag("Test NULL parameters");
254 	int ret = knot_rrset_rr_from_wire(NULL, NULL, 0, NULL, NULL, true);
255 	is_int(KNOT_EINVAL, ret, "rr wire: Invalid params");
256 
257 	diag("Test various inputs");
258 	test_inputs();
259 
260 	diag("Test canonization");
261 	test_canonization();
262 
263 	return 0;
264 }
265