1 /*  Copyright (C) 2015 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 <stdlib.h>
18 #include <stdint.h>
19 #include <string.h>
20 #include <sys/socket.h>
21 #include <tap/basic.h>
22 
23 #include "libknot/yparser/yptrafo.h"
24 #include "libknot/libknot.h"
25 
int_test(const char * txt,int64_t num,yp_style_t s,int64_t min,int64_t max)26 static void int_test(const char *txt, int64_t num, yp_style_t s,
27                      int64_t min, int64_t max)
28 {
29 	int ret;
30 	uint8_t b[64];
31 	size_t b_len = sizeof(b);
32 	char t[64];
33 	size_t t_len = sizeof(t);
34 	yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
35 
36 	diag("integer \"%s\":", txt);
37 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
38 	is_int(KNOT_EOK, ret, "txt to bin");
39 	ok(yp_int(b) == num, "compare");
40 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, s | YP_SNOQUOTE);
41 	is_int(KNOT_EOK, ret, "bin to txt");
42 	ok(strlen(t) == t_len, "txt ret length");
43 	ok(strlen(txt) == t_len, "txt length");
44 	ok(memcmp(txt, t, t_len) == 0, "compare");
45 }
46 
int_bad_test(const char * txt,int code,yp_style_t s,int64_t min,int64_t max)47 static void int_bad_test(const char *txt, int code, yp_style_t s,
48                          int64_t min, int64_t max)
49 {
50 	int ret;
51 	uint8_t b[64];
52 	size_t b_len = sizeof(b);
53 	yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
54 
55 	diag("integer \"%s\":", txt);
56 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
57 	ok(ret == code, "invalid txt to bin");
58 }
59 
bool_test(const char * txt,bool val)60 static void bool_test(const char *txt, bool val)
61 {
62 	int ret;
63 	uint8_t b[64];
64 	size_t b_len = sizeof(b);
65 	char t[64];
66 	size_t t_len = sizeof(t);
67 	yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
68 
69 	diag("boolean \"%s\":", txt);
70 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
71 	is_int(KNOT_EOK, ret, "txt to bin");
72 	ok(yp_bool(b) == val, "compare");
73 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
74 	is_int(KNOT_EOK, ret, "bin to txt");
75 	ok(strlen(t) == t_len, "txt ret length");
76 	ok(strlen(txt) == t_len, "txt length");
77 	ok(memcmp(txt, t, t_len) == 0, "compare");
78 }
79 
bool_bad_test(const char * txt,int code)80 static void bool_bad_test(const char *txt, int code)
81 {
82 	int ret;
83 	uint8_t b[64];
84 	size_t b_len = sizeof(b);
85 	yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
86 
87 	diag("boolean \"%s\":", txt);
88 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
89 	ok(ret == code, "invalid txt to bin");
90 }
91 
opt_test(const char * txt,unsigned val,const knot_lookup_t * opts)92 static void opt_test(const char *txt, unsigned val, const knot_lookup_t *opts)
93 {
94 	int ret;
95 	uint8_t b[64];
96 	size_t b_len = sizeof(b);
97 	char t[64];
98 	size_t t_len = sizeof(t);
99 	yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
100 
101 	diag("option \"%s\":", txt);
102 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
103 	is_int(KNOT_EOK, ret, "txt to bin");
104 	ok(b_len == 1, "compare length");
105 	ok(yp_opt(b) == val, "compare");
106 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
107 	is_int(KNOT_EOK, ret, "bin to txt");
108 	ok(strlen(t) == t_len, "txt ret length");
109 	ok(strlen(txt) == t_len, "txt length");
110 	ok(memcmp(txt, t, t_len) == 0, "compare");
111 }
112 
opt_bad_test(const char * txt,int code,const knot_lookup_t * opts)113 static void opt_bad_test(const char *txt, int code, const knot_lookup_t *opts)
114 {
115 	int ret;
116 	uint8_t b[64];
117 	size_t b_len = sizeof(b);
118 	yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
119 
120 	diag("option \"%s\":", txt);
121 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
122 	ok(ret == code, "invalid txt to bin");
123 }
124 
str_test(const char * txt,const char * val)125 static void str_test(const char *txt, const char *val)
126 {
127 	int ret;
128 	uint8_t b[64];
129 	size_t b_len = sizeof(b);
130 	char t[64];
131 	size_t t_len = sizeof(t);
132 	yp_item_t i = { NULL, YP_TSTR, YP_VNONE };
133 
134 	diag("string \"%s\":", txt);
135 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
136 	is_int(KNOT_EOK, ret, "txt to bin");
137 	ok(b_len == strlen(txt) + 1, "compare length");
138 	ok(memcmp(yp_str(b), val, b_len) == 0, "compare");
139 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
140 	is_int(KNOT_EOK, ret, "bin to txt");
141 	ok(strlen(t) == t_len, "txt ret length");
142 	ok(strlen(txt) == t_len, "txt length");
143 	ok(memcmp(txt, t, t_len) == 0, "compare");
144 }
145 
addr_test(const char * txt,bool port)146 static void addr_test(const char *txt, bool port)
147 {
148 	int ret;
149 	uint8_t b[64];
150 	size_t b_len = sizeof(b);
151 	char t[64];
152 	size_t t_len = sizeof(t);
153 	yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
154 
155 	diag("address \"%s\":", txt);
156 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
157 	is_int(KNOT_EOK, ret, "txt to bin");
158 	bool no_port;
159 	yp_addr(b, &no_port);
160 	ok(no_port == port, "compare port presence");
161 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
162 	is_int(KNOT_EOK, ret, "bin to txt");
163 	ok(strlen(t) == t_len, "txt ret length");
164 	ok(strlen(txt) == t_len, "txt length");
165 	ok(memcmp(txt, t, t_len) == 0, "compare");
166 }
167 
addr_bad_test(const char * txt,int code)168 static void addr_bad_test(const char *txt, int code)
169 {
170 	int ret;
171 	uint8_t b[64];
172 	size_t b_len = sizeof(b);
173 	yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
174 
175 	diag("address \"%s\":", txt);
176 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
177 	ok(ret == code, "invalid txt to bin");
178 }
179 
addr_range_test(const char * txt)180 static void addr_range_test(const char *txt)
181 {
182 	int ret;
183 	uint8_t b[64];
184 	size_t b_len = sizeof(b);
185 	char t[64];
186 	size_t t_len = sizeof(t);
187 	yp_item_t i = { NULL, YP_TNET, YP_VNONE };
188 
189 	diag("address range \"%s\":", txt);
190 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
191 	is_int(KNOT_EOK, ret, "txt to bin");
192 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
193 	is_int(KNOT_EOK, ret, "bin to txt");
194 	ok(strlen(t) == t_len, "txt ret length");
195 	ok(strlen(txt) == t_len, "txt length");
196 	ok(memcmp(txt, t, t_len) == 0, "compare");
197 }
198 
addr_range_bad_test(const char * txt,int code)199 static void addr_range_bad_test(const char *txt, int code)
200 {
201 	int ret;
202 	uint8_t b[64];
203 	size_t b_len = sizeof(b);
204 	yp_item_t i = { NULL, YP_TNET, YP_VNONE };
205 
206 	diag("address range \"%s\":", txt);
207 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
208 	ok(ret == code, "invalid txt to bin");
209 }
210 
dname_test(const char * txt,const char * val)211 static void dname_test(const char *txt, const char *val)
212 {
213 	int ret;
214 	uint8_t b[64];
215 	size_t b_len = sizeof(b);
216 	char t[64];
217 	size_t t_len = sizeof(t);
218 	yp_item_t i = { NULL, YP_TDNAME, YP_VNONE };
219 
220 	diag("dname \"%s\":", txt);
221 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
222 	is_int(KNOT_EOK, ret, "txt to bin");
223 	ok(memcmp(yp_dname(b), val, b_len) == 0, "compare");
224 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
225 	is_int(KNOT_EOK, ret, "bin to txt");
226 	ok(strlen(t) == t_len, "txt ret length");
227 	ok(strlen(txt) == t_len, "txt length");
228 	ok(memcmp(txt, t, t_len) == 0, "compare");
229 }
230 
hex_test(const char * txt,const char * val,const char * txt_out)231 static void hex_test(const char *txt, const char *val, const char *txt_out)
232 {
233 	int ret;
234 	uint8_t b[64];
235 	size_t b_len = sizeof(b);
236 	char t[64];
237 	size_t t_len = sizeof(t);
238 	yp_item_t i = { NULL, YP_THEX, YP_VNONE };
239 
240 	if (txt_out == NULL) {
241 		txt_out = txt;
242 	}
243 
244 	diag("hex \"%s\":", txt);
245 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
246 	is_int(KNOT_EOK, ret, "txt to bin");
247 	ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
248 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
249 	is_int(KNOT_EOK, ret, "bin to txt");
250 	ok(strlen(t) == t_len, "txt ret length");
251 	ok(strlen(txt_out) == t_len, "txt length");
252 	ok(memcmp(txt_out, t, t_len) == 0, "compare");
253 }
254 
hex_bad_test(const char * txt,int code)255 static void hex_bad_test(const char *txt, int code)
256 {
257 	int ret;
258 	uint8_t b[64];
259 	size_t b_len = sizeof(b);
260 	yp_item_t i = { NULL, YP_THEX, YP_VNONE };
261 
262 	diag("hex \"%s\":", txt);
263 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
264 	ok(ret == code, "invalid txt to bin");
265 }
266 
base64_test(const char * txt,const char * val)267 static void base64_test(const char *txt, const char *val)
268 {
269 	int ret;
270 	uint8_t b[64];
271 	size_t b_len = sizeof(b);
272 	char t[64];
273 	size_t t_len = sizeof(t);
274 	yp_item_t i = { NULL, YP_TB64, YP_VNONE };
275 
276 	diag("base64 \"%s\":", txt);
277 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
278 	is_int(KNOT_EOK, ret, "txt to bin");
279 	ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
280 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
281 	is_int(KNOT_EOK, ret, "bin to txt");
282 	ok(strlen(t) == t_len, "txt ret length");
283 	ok(strlen(txt) == t_len, "txt length");
284 	ok(memcmp(txt, t, t_len) == 0, "compare");
285 }
286 
ref_test(const char * txt,bool val)287 static void ref_test(const char *txt, bool val)
288 {
289 	int ret;
290 	uint8_t b[64];
291 	size_t b_len = sizeof(b);
292 	char t[64];
293 	size_t t_len = sizeof(t);
294 	yp_item_t id = { NULL, YP_TBOOL, YP_VNONE };
295 	yp_item_t ref = { NULL, YP_TGRP, YP_VNONE };
296 	yp_item_t i = { NULL, YP_TREF, YP_VNONE };
297 	ref.var.g.id = &id;
298 	i.var.r.ref = &ref;
299 
300 	diag("reference to boolean \"%s\":", txt);
301 	ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
302 	is_int(KNOT_EOK, ret, "txt to bin");
303 	ok(yp_bool(b) == val, "compare");
304 	ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
305 	is_int(KNOT_EOK, ret, "bin to txt");
306 	ok(strlen(t) == t_len, "txt ret length");
307 	ok(strlen(txt) == t_len, "txt length");
308 	ok(memcmp(txt, t, t_len) == 0, "compare");
309 }
310 
main(int argc,char * argv[])311 int main(int argc, char *argv[])
312 {
313 	plan_lazy();
314 
315 	/* Integer tests. */
316 	int64_t min = -20000000000, max = 20000000000;
317 	int_test("5", 5, YP_SNONE, min, max);
318 	int_test("0", 0, YP_SNONE, min, max);
319 	int_test("-5", -5, YP_SNONE, min, max);
320 	int_test("20000000000", max, YP_SNONE, min, max);
321 	int_test("-20000000000", min, YP_SNONE, min, max);
322 	int_test("11B", 11LL * 1, YP_SSIZE, min, max);
323 	int_test("11K", 11LL * 1024, YP_SSIZE, min, max);
324 	int_test("11M", 11LL * 1024 * 1024, YP_SSIZE, min, max);
325 	int_test("11G", 11LL * 1024 * 1024 * 1024, YP_SSIZE, min, max);
326 	int_test("11s", 11LL * 1, YP_STIME, min, max);
327 	int_test("11m", 11LL * 60, YP_STIME, min, max);
328 	int_test("11h", 11LL * 3600, YP_STIME, min, max);
329 	int_test("11d", 11LL * 24 * 3600, YP_STIME, min, max);
330 	int_test("1025B", 1025LL, YP_SSIZE, min, max);
331 	int_test("61s", 61LL, YP_STIME, min, max);
332 	int_bad_test("20000000001", KNOT_ERANGE, YP_SNONE, min, max);
333 	int_bad_test("-20000000001", KNOT_ERANGE, YP_SNONE, min, max);
334 	int_bad_test("1x", KNOT_EINVAL, YP_SNONE, min, max);
335 	int_bad_test("1sx", KNOT_EINVAL, YP_STIME, min, max);
336 
337 	/* Boolean tests. */
338 	bool_test("on", true);
339 	bool_test("off", false);
340 	bool_bad_test("onx", KNOT_EINVAL);
341 	bool_bad_test("enable", KNOT_EINVAL);
342 
343 	/* Option tests. */
344 	static const knot_lookup_t opts[] = {
345 		{ 1,   "one" },
346 		{ 10,  "ten" },
347 		{ 255, "max" },
348 		{ 0, NULL }
349 	};
350 	opt_test("one", 1, opts);
351 	opt_test("ten", 10, opts);
352 	opt_test("max", 255, opts);
353 	opt_bad_test("onex", KNOT_EINVAL, opts);
354 	opt_bad_test("word", KNOT_EINVAL, opts);
355 
356 	/* String tests. */
357 	str_test("Test string!", "Test string!");
358 
359 	/* Address tests. */
360 	addr_test("192.168.123.1", true);
361 	addr_test("192.168.123.1@12345", false);
362 	addr_test("2001:db8::1", true);
363 	addr_test("::1@12345", false);
364 	addr_test("/tmp/test.sock", true);
365 	addr_test("eth1@53", true);
366 	addr_bad_test("192.168.123.x", KNOT_EINVAL);
367 	addr_bad_test("192.168.123.1@", KNOT_EINVAL);
368 	addr_bad_test("192.168.123.1@1x", KNOT_EINVAL);
369 	addr_bad_test("192.168.123.1@65536", KNOT_ERANGE);
370 
371 	/* Address range tests. */
372 	addr_range_test("1.1.1.1");
373 	addr_range_test("1.1.1.1/0");
374 	addr_range_test("1.1.1.1/32");
375 	addr_range_test("1.1.1.1-1.2.3.4");
376 	addr_range_test("::1");
377 	addr_range_test("::1/0");
378 	addr_range_test("::1/32");
379 	addr_range_test("1::-5::");
380 	addr_range_bad_test("unix", KNOT_EINVAL);
381 	addr_range_bad_test("1.1.1", KNOT_EINVAL);
382 	addr_range_bad_test("1.1.1.1/", KNOT_EINVAL);
383 	addr_range_bad_test("1.1.1.1/33", KNOT_ERANGE);
384 	addr_range_bad_test("1.1.1.1-", KNOT_EINVAL);
385 	addr_range_bad_test("1.1.1.1-::1", KNOT_EINVAL);
386 
387 	/* Dname tests. */
388 	dname_test("example.com.", "\x07""example""\x03""com""\x00");
389 
390 	/* Hex tests. */
391 	hex_test("", "", NULL);
392 	hex_test("0x", "", "");
393 	hex_test("Hello World!", "Hello World!", NULL);
394 	hex_test("0x0155FF", "\x01\x55\xFF", NULL);
395 	hex_bad_test("0xA", KNOT_EINVAL);
396 
397 	/* Base64 tests. */
398 	base64_test("Zm9vYmFy", "foobar");
399 
400 	/* Ref tests. */
401 	ref_test("on", true);
402 
403 	return 0;
404 }
405