1 /*  Copyright (C) 2021 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/yparser.h"
23 #include "libknot/libknot.h"
24 
25 const char *syntax_ok =
26 	"#comment\n"
27 	" # comment\n"
28 	"a:\n"
29 	"a :\n"
30 	"a : #comment\n"
31 	"\n"
32 	"b: \"b\"\n"
33 	"b: b #comment\n"
34 	"b : b\n"
35 	"b: [ b] # comment\n"
36 	"b: [b ]\n"
37 	"b: [ b ]\n"
38 	"\n"
39 	" f: \"f\"\n"
40 	" f: f #comment\n"
41 	" f : f\n"
42 	" f: [ f] # comment\n"
43 	" f: [f ]\n"
44 	" f: [ f ]\n"
45 	" f: [ \"f\" ]\n"
46 	"\n"
47 	"c: [a,b]\n"
48 	"c: [a, b]\n"
49 	"c: [a ,b]\n"
50 	"c: [a , b]\n"
51 	"c: [ a , b ]\n"
52 	"c: [ \"a\" , \"b\" ]\n"
53 	"\n"
54 	"- d: d\n"
55 	"- d : d # comment\n"
56 	"\n"
57 	"e: \"a#b' c[d,]\"\n"
58 	"\n"
59 	"zone:\n"
60 	"#comment\n"
61 	" # comment\n"
62 	"  -   domain: example. # comment\n"
63 	"      master: bind\n"
64 	"  - domain: example.\n"
65 	"    master: bind\n"
66 	"zone2:\n"
67 	"    - a: b # different indentation";
68 
69 const char *syntax_error1 =
70 	"f:\n"
71 	"  -  a: b\n"
72 	"   - b: c\n";
73 
74 const char *syntax_error2 =
75 	"f:\n"
76 	"  -  a: b\n"
77 	"      c: d\n";
78 
79 const char *syntax_error3 =
80 	"f:\n"
81 	"   a: b\n"
82 	"  c: d\n";
83 
84 const char *tab_error1 =
85 	"a:\n"
86 	"b:\t\n";
87 
88 const char *tab_error2 =
89 	"a:\n"
90 	"b: c\t\n";
91 
92 const char *tab_error3 =
93 	"a:\n"
94 	"\t\n";
95 
96 const char *dname_ok =
97 	".:\n"
98 	"dom-ain:\n"
99 	"\\070-\\071.\\072.:\n"
100 	"*.wildchar.com:\n"
101 	"_ldap._tcp.example.com:\n";
102 
103 const char *quotes_ok =
104 	"g: \"\"\n"
105 	"g: a\\ b\n"
106 	"g: \"\\# 1 00\"\n"
107 	"g: \"\\\"\\\"\"\n"
108 	"g: \" a \\\" b \\\" \\\"c\\\" \"\n"
109 	"g: \"\\@ \\[ \\# \\, \\]\"\n";
110 
111 const char *utf8_ok =
112 	"key: příšera\n";
113 
test_syntax_ok(yp_parser_t * yp)114 static void test_syntax_ok(yp_parser_t *yp)
115 {
116 	// OK input.
117 	int ret = yp_set_input_string(yp, syntax_ok, strlen(syntax_ok));
118 	is_int(KNOT_EOK, ret, "set input string");
119 
120 	size_t line = 3;
121 	for (int i = 0; i < 3; i++) {
122 		ret = yp_parse(yp);
123 		is_int(KNOT_EOK, ret, "parse %i. key0", i);
124 		ok(yp->key_len == 1 && yp->key[0] == 'a' &&
125 		   yp->data_len == 0 && yp->event == YP_EKEY0 &&
126 		   yp->line_count == line + i, "compare %i. key0", i);
127 	}
128 
129 	line += 4;
130 	for (int i = 0; i < 6; i++) {
131 		ret = yp_parse(yp);
132 		is_int(KNOT_EOK, ret, "parse %i. key0 with value", i);
133 		ok(yp->key_len == 1 && yp->key[0] == 'b' &&
134 		   yp->data_len == 1 && yp->data[0] == 'b' &&
135 		   yp->event == YP_EKEY0 && yp->line_count == line + i,
136 		   "compare %i. key0 with value", i);
137 	}
138 
139 	line += 7;
140 	for (int i = 0; i < 7; i++) {
141 		ret = yp_parse(yp);
142 		is_int(KNOT_EOK, ret, "parse %i. key1 with value", i);
143 		ok(yp->key_len == 1 && yp->key[0] == 'f' &&
144 		   yp->data_len == 1 && yp->data[0] == 'f' &&
145 		   yp->event == YP_EKEY1 && yp->line_count == line + i,
146 		   "compare %i. key1 with value", i);
147 	}
148 
149 	line += 8;
150 	for (int i = 0; i < 6; i++) {
151 		ret = yp_parse(yp);
152 		is_int(KNOT_EOK, ret, "parse %i. key0 with first value", i);
153 		ok(yp->key_len == 1 && yp->key[0] == 'c' &&
154 		   yp->data_len == 1 && yp->data[0] == 'a' &&
155 		   yp->event == YP_EKEY0 && yp->line_count == line + i,
156 		   "compare %i. key0 with first value", i);
157 
158 		ret = yp_parse(yp);
159 		is_int(KNOT_EOK, ret, "parse %i. key0 with second value", i);
160 		ok(yp->key_len == 1 && yp->key[0] == 'c' &&
161 		   yp->data_len == 1 && yp->data[0] == 'b' &&
162 		   yp->event == YP_EKEY0 && yp->line_count == line + i,
163 		   "compare %i. key0 with second value", i);
164 	}
165 
166 	line += 7;
167 	for (int i = 0; i < 2; i++) {
168 		ret = yp_parse(yp);
169 		is_int(KNOT_EOK, ret, "parse %i. id", i);
170 		ok(yp->key_len == 1 && yp->key[0] == 'd' &&
171 		   yp->data_len == 1 && yp->data[0] == 'd' &&
172 		   yp->event == YP_EID && yp->line_count == line + i,
173 		   "compare %i. id", i);
174 	}
175 
176 	line += 3;
177 	ret = yp_parse(yp);
178 	is_int(KNOT_EOK, ret, "parse key0 with quoted value");
179 	ok(yp->key_len == 1 && yp->key[0] == 'e' && yp->data_len == 10 &&
180 	   memcmp(yp->data, "a#b' c[d,]", yp->data_len) == 0 &&
181 	   yp->event == YP_EKEY0 && yp->line_count == line,
182 	   "compare key0 with quoted value");
183 
184 	line += 2;
185 	ret = yp_parse(yp);
186 	is_int(KNOT_EOK, ret, "parse key0");
187 	ok(yp->key_len == 4 && strcmp(yp->key, "zone") == 0 &&
188 	   yp->data_len == 0 &&
189 	   yp->event == YP_EKEY0 && yp->line_count == line,
190 	   "compare key0 value");
191 
192 	line += 3;
193 	for (int i = 0; i < 2; i++) {
194 		ret = yp_parse(yp);
195 		is_int(KNOT_EOK, ret, "parse %i. id", i);
196 		ok(yp->key_len == 6 && strcmp(yp->key, "domain") == 0 &&
197 		   yp->data_len == 8 && strcmp(yp->data, "example.") == 0 &&
198 		   yp->event == YP_EID && yp->line_count == line + 2 * i,
199 		   "compare id");
200 		ret = yp_parse(yp);
201 		is_int(KNOT_EOK, ret, "parse %i. key1", i);
202 		ok(yp->key_len == 6 && strcmp(yp->key, "master") == 0 &&
203 		   yp->data_len == 4 && strcmp(yp->data, "bind") == 0 &&
204 		   yp->event == YP_EKEY1 && yp->line_count == line + 2 * i + 1,
205 		   "compare key1");
206 	}
207 
208 	line += 4;
209 	ret = yp_parse(yp);
210 	is_int(KNOT_EOK, ret, "parse key0");
211 	ok(yp->key_len == 5 && strcmp(yp->key, "zone2") == 0 &&
212 	   yp->data_len == 0 &&
213 	   yp->event == YP_EKEY0 && yp->line_count == line,
214 	   "compare key0 value");
215 	ret = yp_parse(yp);
216 	is_int(KNOT_EOK, ret, "parse key1");
217 	ok(yp->key_len == 1 && strcmp(yp->key, "a") == 0 &&
218 	   yp->data_len == 1 && strcmp(yp->data, "b") == 0 &&
219 	   yp->event == YP_EID && yp->line_count == line + 1,
220 	   "compare key1 value");
221 
222 	ret = yp_parse(yp);
223 	is_int(KNOT_EOF, ret, "parse EOF");
224 }
225 
test_syntax_error(yp_parser_t * yp,const char * input)226 static void test_syntax_error(yp_parser_t *yp, const char *input)
227 {
228 	static int count = 1;
229 
230 	int ret = yp_set_input_string(yp, input, strlen(input));
231 	is_int(KNOT_EOK, ret, "set syntax error input string %i", count++);
232 	ret = yp_parse(yp);
233 	is_int(KNOT_EOK, ret, "parse key0");
234 	ret = yp_parse(yp);
235 	is_int(KNOT_EOK, ret, "parse key1");
236 	ret = yp_parse(yp);
237 	is_int(KNOT_YP_EINVAL_INDENT, ret, "parse key1 - invalid indentation");
238 	is_int(yp->line_count, 3, "invalid indentation line");
239 }
240 
test_tab_error(yp_parser_t * yp,const char * input)241 static void test_tab_error(yp_parser_t *yp, const char *input)
242 {
243 	static int count = 1;
244 
245 	int ret = yp_set_input_string(yp, input, strlen(input));
246 	is_int(KNOT_EOK, ret, "set tab error input string %i", count++);
247 	ret = yp_parse(yp);
248 	is_int(KNOT_EOK, ret, "parse key0");
249 	ret = yp_parse(yp);
250 	is_int(KNOT_YP_ECHAR_TAB, ret, "invalid tabulator");
251 	is_int(yp->line_count, 2, "invalid tabulator line");
252 }
253 
test_dname(yp_parser_t * yp)254 static void test_dname(yp_parser_t *yp)
255 {
256 #define CHECK_DNAME(str) \
257 	ret = yp_parse(yp); \
258 	is_int(KNOT_EOK, ret, "parse dname " str); \
259 	ok(yp->key_len == strlen(str) && strcmp(yp->key, str) == 0 && yp->data_len == 0 && \
260 	   yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
261 
262 	// Dname key value.
263 	int ret = yp_set_input_string(yp, dname_ok, strlen(dname_ok));
264 	is_int(KNOT_EOK, ret, "set input string");
265 
266 	size_t line = 1;
267 	CHECK_DNAME(".");
268 	CHECK_DNAME("dom-ain");
269 	CHECK_DNAME("\\070-\\071.\\072.");
270 	CHECK_DNAME("*.wildchar.com");
271 	CHECK_DNAME("_ldap._tcp.example.com");
272 }
273 
test_quotes(yp_parser_t * yp)274 static void test_quotes(yp_parser_t *yp)
275 {
276 #define CHECK_QUOTE(str) \
277 	ret = yp_parse(yp); \
278 	is_int(KNOT_EOK, ret, "parse quoted " str); \
279 	ok(yp->key_len == 1 && yp->key[0] == 'g' && \
280 	   yp->data_len == strlen(str) && strcmp(yp->data, str) == 0 && \
281 	   yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
282 
283 	int ret = yp_set_input_string(yp, quotes_ok, strlen(quotes_ok));
284 	is_int(KNOT_EOK, ret, "set input string");
285 
286 	size_t line = 1;
287 	CHECK_QUOTE("");
288 	CHECK_QUOTE("a\\ b");
289 	CHECK_QUOTE("\\# 1 00");
290 	CHECK_QUOTE("\"\"");
291 	CHECK_QUOTE(" a \" b \" \"c\" ");
292 	CHECK_QUOTE("\\@ \\[ \\# \\, \\]");
293 }
294 
295 // Check that wrong wildcard dname is NOT parsed as valid dname.
test_wildcard(yp_parser_t * yp)296 static void test_wildcard(yp_parser_t *yp)
297 {
298 #define CHECK_NOT_WILDCARD(str) \
299 	ret = yp_set_input_string(yp, str, strlen(str)); \
300 	is_int(KNOT_EOK, ret, "set input string");\
301 	ret = yp_parse(yp); \
302 	is_int(KNOT_EPARSEFAIL, ret, str " is not wildcard"); \
303 	ok(yp->key_len != strlen(str) || strcmp(yp->key, str) != 0 || \
304 	   yp->event != YP_EKEY0, "compare " str);
305 
306 	int ret;
307 	CHECK_NOT_WILDCARD("a.*.example.com.");
308 	CHECK_NOT_WILDCARD("**.example.com.");
309 	CHECK_NOT_WILDCARD("*example.com.");
310 }
311 
test_utf8(yp_parser_t * yp)312 static void test_utf8(yp_parser_t *yp)
313 {
314 	int ret = yp_set_input_string(yp, utf8_ok, strlen(utf8_ok));
315 	is_int(KNOT_EOK, ret, "set input string");
316 
317 	ret = yp_parse(yp);
318 	is_int(KNOT_EOK, ret, "parse key with a value in UTF-8");
319 	ok(yp->key_len == 3 && strcmp(yp->key, "key") == 0 &&
320 	   yp->data_len == 10 && strcmp(yp->data, "p""\xC5\x99\xC3\xAD\xC5\xA1""era") == 0,
321 	   "compare UTF-8 value");
322 }
323 
main(int argc,char * argv[])324 int main(int argc, char *argv[])
325 {
326 	plan_lazy();
327 
328 	yp_parser_t yp;
329 	yp_init(&yp);
330 
331 	test_syntax_ok(&yp);
332 	test_syntax_error(&yp, syntax_error1);
333 	test_syntax_error(&yp, syntax_error2);
334 	test_syntax_error(&yp, syntax_error3);
335 	test_tab_error(&yp, tab_error1);
336 	test_tab_error(&yp, tab_error2);
337 	test_tab_error(&yp, tab_error3);
338 	test_dname(&yp);
339 	test_quotes(&yp);
340 	test_wildcard(&yp);
341 	test_utf8(&yp);
342 
343 	yp_deinit(&yp);
344 
345 	return 0;
346 }
347