1 /*  Copyright (C) 2019 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 <tap/basic.h>
21 
22 #include "libknot/yparser/ypschema.h"
23 #include "libknot/yparser/yptrafo.h"
24 #include "libknot/libknot.h"
25 
26 #define C_ID		"\x02""id"
27 #define C_INT		"\x07""integer"
28 #define C_BOOL		"\x04""bool"
29 #define C_OPT		"\x06""option"
30 #define C_STR		"\x06""string"
31 #define C_ADDR		"\x07""address"
32 #define C_DNAME		"\x05""dname"
33 #define C_HEX		"\x03""hex"
34 #define C_BASE64	"\x06""base64"
35 #define C_DATA		"\x04""data"
36 #define C_REF		"\x09""reference"
37 #define C_GRP		"\x05""group"
38 #define C_MULTIGRP	"\x0B""multi-group"
39 
40 static const yp_item_t group[] = {
41 	{ C_INT, YP_TINT, YP_VINT = { 0, 100, YP_NIL } },
42 	{ C_STR, YP_TSTR, YP_VNONE, YP_FMULTI },
43 	{ NULL }
44 };
45 
46 static const yp_item_t multi_group[] = {
47 	{ C_ID,     YP_TSTR, YP_VNONE },
48 	{ C_HEX,    YP_THEX, YP_VNONE },
49 	{ C_BASE64, YP_TB64, YP_VNONE },
50 	{ NULL }
51 };
52 
53 static const knot_lookup_t opts[] = {
54 	{ 1,   "one" },
55 	{ 10,  "ten" },
56 	{ 0, NULL }
57 	};
58 
59 static const yp_item_t static_schema[] = {
60 	{ C_OPT,      YP_TOPT,   YP_VOPT = { opts } },
61 	{ C_BOOL,     YP_TBOOL,  YP_VNONE },
62 	{ C_DNAME,    YP_TDNAME, YP_VNONE },
63 	{ C_GRP,      YP_TGRP,   YP_VGRP = { group } },
64 	{ C_MULTIGRP, YP_TGRP,   YP_VGRP = { multi_group }, YP_FMULTI },
65 	{ C_REF,      YP_TREF,   YP_VREF = { C_MULTIGRP } },
66 	{ C_DATA,     YP_TDATA,  YP_VNONE },
67 	{ NULL }
68 };
69 
schema_find_test(void)70 static void schema_find_test(void)
71 {
72 	yp_item_t *schema = NULL;
73 
74 	int ret = yp_schema_copy(&schema, static_schema);
75 	is_int(KNOT_EOK, ret, "schema copy");
76 
77 	const yp_item_t *i = yp_schema_find(C_OPT, NULL, schema);
78 	ok(i != NULL, "schema find");
79 	if (i == NULL) {
80 		goto error_schema;
81 	}
82 	ok(strcmp(&i->name[1], &C_OPT[1]) == 0, "name check");
83 
84 	i = yp_schema_find(C_STR, C_GRP, schema);
85 	ok(i != NULL, "schema find with parent");
86 	if (i == NULL) {
87 		goto error_schema;
88 	}
89 	ok(strcmp(&i->name[1], &C_STR[1]) == 0, "name check");
90 
91 	i = yp_schema_find(C_ADDR, NULL, schema);
92 	ok(i == NULL, "schema not find");
93 
94 	i = yp_schema_find(C_ADDR, C_GRP, schema);
95 	ok(i == NULL, "schema not find with parent");
96 
97 error_schema:
98 	yp_schema_free(schema);
99 }
100 
schema_merge_test(void)101 static void schema_merge_test(void)
102 {
103 	static const yp_item_t items1[] = {
104 		{ "\x01""1", YP_TSTR,  YP_VNONE },
105 		{ "\x01""2", YP_TSTR,  YP_VNONE },
106 		{ NULL }
107 	};
108 
109 	static const yp_item_t items2[] = {
110 		{ "\x01""3", YP_TSTR,  YP_VNONE },
111 		{ "\x01""4", YP_TSTR,  YP_VNONE },
112 		{ NULL }
113 	};
114 
115 	yp_item_t *schema = NULL;
116 	yp_item_t *tmp = NULL;
117 
118 	int ret = yp_schema_copy(&tmp, items1);
119 	is_int(KNOT_EOK, ret, "schema copy");
120 
121 	ret = yp_schema_merge(&schema, items1, items2);
122 	is_int(KNOT_EOK, ret, "schema merge");
123 
124 	yp_schema_free(tmp);
125 
126 	for (uint8_t i = 0; i < 4; i++) {
127 		yp_name_t name[3] = { '\x01', '1' + i };
128 		const yp_item_t *item = yp_schema_find(name, NULL, schema);
129 		ok(item != NULL, "schema find");
130 	}
131 
132 	yp_schema_free(schema);
133 }
134 
135 #define SET_INPUT_STR(str) \
136 	ret = yp_set_input_string(yp, str, strlen(str)); \
137 	is_int(KNOT_EOK, ret, "set input string");
138 
139 #define PARSER_CHECK(depth) \
140 	ret = yp_parse(yp); \
141 	is_int(KNOT_EOK, ret, "parse"); \
142 	ret = yp_schema_check_parser(ctx, yp); \
143 	is_int(KNOT_EOK, ret, "check parser"); \
144 	node = &ctx->nodes[ctx->current]; \
145 	parent = node->parent; \
146 	ok(ctx->current == depth, "depth check");
147 
148 #define PARSER_RET_CHECK(code) \
149 	ret = yp_parse(yp); \
150 	is_int(KNOT_EOK, ret, "parse"); \
151 	ret = yp_schema_check_parser(ctx, yp); \
152 	ok(ret == code, "return check parser");
153 
parser_test(void)154 static void parser_test(void)
155 {
156 	yp_parser_t yparser;
157 	yp_parser_t *yp = &yparser;
158 	yp_item_t *schema = NULL;
159 	yp_check_ctx_t *ctx = NULL;
160 
161 	yp_init(yp);
162 
163 	int ret = yp_schema_copy(&schema, static_schema);
164 	is_int(KNOT_EOK, ret, "schema copy");
165 	if (ret != KNOT_EOK) {
166 		goto error_parser;
167 	}
168 
169 	ctx = yp_schema_check_init(&schema);
170 	ok(ctx != NULL, "create check ctx");
171 	if (ctx == NULL) {
172 		goto error_parser;
173 	}
174 
175 	yp_node_t *node;
176 	yp_node_t *parent;
177 	const yp_item_t *id;
178 
179 	diag("parser key0 test");
180 	SET_INPUT_STR("option: one");
181 	PARSER_CHECK(0);
182 	ok(strcmp(node->item->name + 1, "option") == 0, "name check");
183 	ok(node->item->type == YP_TOPT, "type check");
184 	ok(yp_opt(node->data) == 1, "value check");
185 
186 	diag("parser group test");
187 	SET_INPUT_STR("group:\n integer: 20\n string: [short, \"long string\"]");
188 	PARSER_CHECK(0);
189 	ok(strcmp(node->item->name + 1, "group") == 0, "name check");
190 	ok(node->item->type == YP_TGRP, "type check");
191 	ok(node->data_len == 0, "value length check");
192 	PARSER_CHECK(1);
193 	ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
194 	ok(node->item->type == YP_TINT, "type check");
195 	ok(yp_int(node->data) == 20, "value check");
196 	PARSER_CHECK(1);
197 	ok(strcmp(node->item->name + 1, "string") == 0, "name check");
198 	ok(node->item->type == YP_TSTR, "type check");
199 	ok(strcmp(yp_str(node->data), "short") == 0, "value check");
200 	PARSER_CHECK(1);
201 	ok(strcmp(node->item->name + 1, "string") == 0, "name check");
202 	ok(node->item->type == YP_TSTR, "type check");
203 	ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
204 
205 	diag("parser multi-group test");
206 	SET_INPUT_STR("multi-group:\n - id: foo\n   base64: Zm9vYmFy\nreference: foo");
207 	PARSER_CHECK(0);
208 	ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
209 	ok(node->item->type == YP_TGRP, "type check");
210 	ok(node->data_len == 0, "value length check");
211 	PARSER_CHECK(0);
212 	ok(node->id_len > 0, "id check");
213 	ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
214 	ok(node->item->type == YP_TGRP, "type check");
215 	ok(node->data_len == 0, "value length check");
216 	id = node->item->var.g.id;
217 	ok(strcmp(id->name + 1, "id") == 0, "name check");
218 	ok(id->type == YP_TSTR, "type check");
219 	ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
220 	PARSER_CHECK(1);
221 	id = parent->item->var.g.id;
222 	ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
223 	ok(parent->item->type == YP_TGRP, "type check");
224 	ok(parent->data_len == 0, "value length check");
225 	ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
226 	ok(strcmp(id->name + 1, "id") == 0, "name check");
227 	ok(id->type == YP_TSTR, "type check");
228 	ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
229 	ok(node->item->type == YP_TB64, "type check");
230 	ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
231 	   "value check");
232 	ok(node->id_len == 0, "id length check");
233 	PARSER_CHECK(0);
234 	ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
235 	ok(node->item->type == YP_TREF, "type check");
236 	ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
237 
238 	diag("parser check return");
239 	SET_INPUT_STR("unknown:");
240 	PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
241 
242 	SET_INPUT_STR("group:\n unknown:");
243 	PARSER_RET_CHECK(KNOT_EOK);
244 	PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
245 
246 	SET_INPUT_STR("group:\n - unknown: data");
247 	PARSER_RET_CHECK(KNOT_EOK);
248 	PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
249 
250 	SET_INPUT_STR("group:\n - hex: data");
251 	PARSER_RET_CHECK(KNOT_EOK);
252 	PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
253 
254 	SET_INPUT_STR("dname:");
255 	PARSER_RET_CHECK(KNOT_EINVAL);
256 
257 	SET_INPUT_STR("group: data");
258 	PARSER_RET_CHECK(KNOT_YP_ENOTSUP_DATA);
259 
260 	SET_INPUT_STR("group:\n integer:");
261 	PARSER_RET_CHECK(KNOT_EOK);
262 	PARSER_RET_CHECK(KNOT_EINVAL);
263 
264 	SET_INPUT_STR("multi-group:\n id:");
265 	PARSER_RET_CHECK(KNOT_EOK);
266 	PARSER_RET_CHECK(KNOT_YP_ENODATA);
267 
268 	SET_INPUT_STR("multi-group:\n hex:");
269 	PARSER_RET_CHECK(KNOT_EOK);
270 	PARSER_RET_CHECK(KNOT_YP_ENOID);
271 
272 error_parser:
273 	yp_schema_check_deinit(ctx);
274 	yp_schema_free(schema);
275 	yp_deinit(yp);
276 }
277 
278 #define STR_CHECK(depth, key0, key1, id, data) \
279 	ret = yp_schema_check_str(ctx, key0, key1, id, data); \
280 	is_int(KNOT_EOK, ret, "check str"); \
281 	ok(ctx->current == depth, "depth check"); \
282 	node = &ctx->nodes[ctx->current]; \
283 	parent = node->parent;
284 
285 #define STR_RET_CHECK(code, key0, key1, id, data) \
286 	ret = yp_schema_check_str(ctx, key0, key1, id, data); \
287 	ok(ret == code, "return check str");
288 
str_test(void)289 static void str_test(void)
290 {
291 	yp_item_t *schema;
292 	yp_check_ctx_t *ctx = NULL;
293 
294 	int ret = yp_schema_copy(&schema, static_schema);
295 	is_int(KNOT_EOK, ret, "schema copy");
296 	if (ret != KNOT_EOK) {
297 		goto error_str;
298 	}
299 
300 	ctx = yp_schema_check_init(&schema);
301 	ok(ctx != NULL, "create check ctx");
302 	if (ctx == NULL) {
303 		goto error_str;
304 	}
305 
306 	yp_node_t *node;
307 	yp_node_t *parent;
308 	const yp_item_t *id;
309 
310 	diag("str key0 test");
311 	STR_CHECK(0, "option", NULL, NULL, "one");
312 	ok(strcmp(node->item->name + 1, "option") == 0, "name check");
313 	ok(node->item->type == YP_TOPT, "type check");
314 	ok(yp_opt(node->data) == 1, "value check");
315 
316 	diag("str group test");
317 	STR_CHECK(0, "group", NULL, NULL, NULL);
318 	ok(strcmp(node->item->name + 1, "group") == 0, "name check");
319 	ok(node->item->type == YP_TGRP, "type check");
320 	ok(node->data_len == 0, "value length check");
321 	STR_CHECK(1, "group", "integer", NULL, "20");
322 	ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
323 	ok(node->item->type == YP_TINT, "type check");
324 	ok(yp_int(node->data) == 20, "value check");
325 	STR_CHECK(1, "group", "string", NULL, "short");
326 	ok(strcmp(node->item->name + 1, "string") == 0, "name check");
327 	ok(node->item->type == YP_TSTR, "type check");
328 	ok(strcmp(yp_str(node->data), "short") == 0, "value check");
329 	STR_CHECK(1, "group", "string", NULL, "long string");
330 	ok(strcmp(node->item->name + 1, "string") == 0, "name check");
331 	ok(node->item->type == YP_TSTR, "type check");
332 	ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
333 
334 	diag("str multi-group test");
335 	STR_CHECK(0, "multi-group", NULL, NULL, NULL);
336 	ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
337 	ok(node->item->type == YP_TGRP, "type check");
338 	ok(node->data_len == 0, "value length check");
339 	STR_CHECK(0, "multi-group", NULL, "foo", NULL);
340 	ok(node->id_len > 0, "id check");
341 	ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
342 	ok(node->item->type == YP_TGRP, "type check");
343 	ok(node->data_len == 0, "value length check");
344 	id = node->item->var.g.id;
345 	ok(strcmp(id->name + 1, "id") == 0, "name check");
346 	ok(id->type == YP_TSTR, "type check");
347 	ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
348 	STR_CHECK(1, "multi-group", "base64", "foo", "Zm9vYmFy");
349 	id = parent->item->var.g.id;
350 	ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
351 	ok(parent->item->type == YP_TGRP, "type check");
352 	ok(parent->data_len == 0, "value length check");
353 	ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
354 	ok(strcmp(id->name + 1, "id") == 0, "name check");
355 	ok(id->type == YP_TSTR, "type check");
356 	ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
357 	ok(node->item->type == YP_TB64, "type check");
358 	ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
359 	   "value check");
360 	ok(node->id_len == 0, "id length check");
361 	STR_CHECK(0, "reference", NULL, NULL, "foo");
362 	ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
363 	ok(node->item->type == YP_TREF, "type check");
364 	ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
365 
366 	diag("str check return");
367 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  "",        "",        "",   "");
368 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  NULL,      NULL,      NULL, NULL);
369 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  "unknown", NULL,      NULL, NULL);
370 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  NULL,      "unknown", NULL, NULL);
371 	STR_RET_CHECK(KNOT_EINVAL,          "dname",   "",        "",   "");
372 	STR_RET_CHECK(KNOT_EOK,             "dname",   NULL,      NULL, NULL);
373 	STR_RET_CHECK(KNOT_EOK,             "dname",   NULL,      NULL, ".");
374 	STR_RET_CHECK(KNOT_EINVAL,          "dname",   NULL,      NULL, "..");
375 	STR_RET_CHECK(KNOT_YP_ENOTSUP_ID,   "dname",   NULL,      "id", NULL);
376 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  "dname",   "unknown", NULL, NULL);
377 
378 	STR_RET_CHECK(KNOT_EOK,             "group",   "",        "",   "");
379 	STR_RET_CHECK(KNOT_EOK,             "group",   NULL,      NULL, NULL);
380 	STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "group",   "",        "",   "data");
381 	STR_RET_CHECK(KNOT_YP_EINVAL_ITEM,  "group",   "unknown", NULL, NULL);
382 	STR_RET_CHECK(KNOT_EOK,             "group",   "string",  NULL, NULL);
383 	STR_RET_CHECK(KNOT_EOK,             "group",   "string",  NULL, "data");
384 	STR_RET_CHECK(KNOT_EOK,             "group",   "string",  NULL, "");
385 	STR_RET_CHECK(KNOT_YP_ENOTSUP_ID,   "group",   "",        "id", NULL);
386 	STR_RET_CHECK(KNOT_YP_ENOTSUP_ID,   "group",   "string",  "id", NULL);
387 
388 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "",    "",      "");
389 	STR_RET_CHECK(KNOT_EOK,             "multi-group", NULL,  NULL,    NULL);
390 	STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL,  NULL,    "data");
391 	STR_RET_CHECK(KNOT_EOK,             "multi-group", NULL,  "idval", NULL);
392 	STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL,  "idval", "data");
393 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "hex", "idval", NULL);
394 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "hex", "idval", "data");
395 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "hex", NULL,    NULL);
396 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "hex", NULL,    "data");
397 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "id",  "",      NULL);
398 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "id",  NULL,    "idval");
399 	STR_RET_CHECK(KNOT_EOK,             "multi-group", "id",  "idval", NULL);
400 	STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", "id",  "idval", "data");
401 
402 error_str:
403 	yp_schema_check_deinit(ctx);
404 	yp_schema_free(schema);
405 }
406 
main(int argc,char * argv[])407 int main(int argc, char *argv[])
408 {
409 	plan_lazy();
410 
411 	schema_find_test();
412 	schema_merge_test();
413 	parser_test();
414 	str_test();
415 
416 	return 0;
417 }
418