xref: /openbsd/usr.sbin/nsd/configparser.y (revision 898184e3)
1 /*
2  * configparser.y -- yacc grammar for NSD configuration files
3  *
4  * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 %{
11 #include "config.h"
12 
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <assert.h>
18 
19 #include "options.h"
20 #include "util.h"
21 #include "rrl.h"
22 #include "configyyrename.h"
23 int c_lex(void);
24 void c_error(const char *message);
25 
26 #ifdef __cplusplus
27 extern "C"
28 #endif /* __cplusplus */
29 
30 /* these need to be global, otherwise they cannot be used inside yacc */
31 extern config_parser_state_t* cfg_parser;
32 static int server_settings_seen = 0;
33 
34 #if 0
35 #define OUTYY(s)  printf s /* used ONLY when debugging */
36 #else
37 #define OUTYY(s)
38 #endif
39 
40 %}
41 %union {
42 	char*	str;
43 }
44 
45 %token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
46 %token <str> STRING
47 %token VAR_SERVER VAR_NAME VAR_IP_ADDRESS VAR_DEBUG_MODE
48 %token VAR_IP4_ONLY VAR_IP6_ONLY VAR_DATABASE VAR_IDENTITY VAR_NSID VAR_LOGFILE
49 %token VAR_SERVER_COUNT VAR_TCP_COUNT VAR_PIDFILE VAR_PORT VAR_STATISTICS
50 %token VAR_ZONESTATSFILE VAR_CHROOT VAR_USERNAME VAR_ZONESDIR
51 %token VAR_XFRDFILE VAR_DIFFFILE
52 %token VAR_XFRD_RELOAD_TIMEOUT VAR_TCP_QUERY_COUNT VAR_TCP_TIMEOUT
53 %token VAR_IPV4_EDNS_SIZE VAR_IPV6_EDNS_SIZE
54 %token VAR_ZONEFILE
55 %token VAR_ZONE
56 %token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR
57 %token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK
58 %token VAR_KEY
59 %token VAR_ALGORITHM VAR_SECRET
60 %token VAR_AXFR VAR_UDP
61 %token VAR_VERBOSITY VAR_HIDE_VERSION
62 %token VAR_RRL_SIZE VAR_RRL_RATELIMIT VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
63 
64 %%
65 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
66 toplevelvar: serverstart contents_server | zonestart contents_zone |
67 	keystart contents_key;
68 
69 /* server: declaration */
70 serverstart: VAR_SERVER
71 	{ OUTYY(("\nP(server:)\n"));
72 		if(server_settings_seen) {
73 			yyerror("duplicate server: element.");
74 		}
75 		server_settings_seen = 1;
76 	}
77 	;
78 contents_server: contents_server content_server | ;
79 content_server: server_ip_address | server_debug_mode | server_ip4_only |
80 	server_ip6_only | server_database | server_identity | server_nsid | server_logfile |
81 	server_server_count | server_tcp_count | server_pidfile | server_port |
82 	server_statistics | server_zonestatsfile | server_chroot |
83 	server_username | server_zonesdir |
84 	server_difffile | server_xfrdfile | server_xfrd_reload_timeout |
85 	server_tcp_query_count | server_tcp_timeout | server_ipv4_edns_size |
86 	server_ipv6_edns_size | server_verbosity | server_hide_version |
87 	server_rrl_size | server_rrl_ratelimit | server_rrl_whitelist_ratelimit;
88 server_ip_address: VAR_IP_ADDRESS STRING
89 	{
90 		OUTYY(("P(server_ip_address:%s)\n", $2));
91 		if(cfg_parser->current_ip_address_option) {
92 			cfg_parser->current_ip_address_option->next =
93 				(ip_address_option_t*)region_alloc(
94 				cfg_parser->opt->region, sizeof(ip_address_option_t));
95 			cfg_parser->current_ip_address_option =
96 				cfg_parser->current_ip_address_option->next;
97 			cfg_parser->current_ip_address_option->next=0;
98 		} else {
99 			cfg_parser->current_ip_address_option =
100 				(ip_address_option_t*)region_alloc(
101 				cfg_parser->opt->region, sizeof(ip_address_option_t));
102 			cfg_parser->current_ip_address_option->next=0;
103 			cfg_parser->opt->ip_addresses = cfg_parser->current_ip_address_option;
104 		}
105 
106 		cfg_parser->current_ip_address_option->address =
107 			region_strdup(cfg_parser->opt->region, $2);
108 	}
109 	;
110 server_debug_mode: VAR_DEBUG_MODE STRING
111 	{
112 		OUTYY(("P(server_debug_mode:%s)\n", $2));
113 		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
114 			yyerror("expected yes or no.");
115 		else cfg_parser->opt->debug_mode = (strcmp($2, "yes")==0);
116 	}
117 	;
118 server_verbosity: VAR_VERBOSITY STRING
119 	{
120 		OUTYY(("P(server_verbosity:%s)\n", $2));
121 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
122 			yyerror("number expected");
123 		else cfg_parser->opt->verbosity = atoi($2);
124 	}
125 	;
126 server_hide_version: VAR_HIDE_VERSION STRING
127 	{
128 		OUTYY(("P(server_hide_version:%s)\n", $2));
129 		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
130 			yyerror("expected yes or no.");
131 		else cfg_parser->opt->hide_version = (strcmp($2, "yes")==0);
132 	}
133 	;
134 server_ip4_only: VAR_IP4_ONLY STRING
135 	{
136 		OUTYY(("P(server_ip4_only:%s)\n", $2));
137 		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
138 			yyerror("expected yes or no.");
139 		else cfg_parser->opt->ip4_only = (strcmp($2, "yes")==0);
140 	}
141 	;
142 server_ip6_only: VAR_IP6_ONLY STRING
143 	{
144 		OUTYY(("P(server_ip6_only:%s)\n", $2));
145 		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
146 			yyerror("expected yes or no.");
147 		else cfg_parser->opt->ip6_only = (strcmp($2, "yes")==0);
148 	}
149 	;
150 server_database: VAR_DATABASE STRING
151 	{
152 		OUTYY(("P(server_database:%s)\n", $2));
153 		cfg_parser->opt->database = region_strdup(cfg_parser->opt->region, $2);
154 	}
155 	;
156 server_identity: VAR_IDENTITY STRING
157 	{
158 		OUTYY(("P(server_identity:%s)\n", $2));
159 		cfg_parser->opt->identity = region_strdup(cfg_parser->opt->region, $2);
160 	}
161 	;
162 server_nsid: VAR_NSID STRING
163 	{
164 		unsigned char* nsid = 0;
165 		uint16_t nsid_len = 0;
166 
167 		OUTYY(("P(server_nsid:%s)\n", $2));
168 
169 		if (strlen($2) % 2 != 0) {
170 			yyerror("the NSID must be a hex string of an even length.");
171 		} else {
172 			nsid_len = strlen($2) / 2;
173 			nsid = xalloc(nsid_len);
174 			if (hex_pton($2, nsid, nsid_len) == -1)
175 				yyerror("hex string cannot be parsed in NSID.");
176 			else
177 				cfg_parser->opt->nsid = region_strdup(cfg_parser->opt->region, $2);
178 			free(nsid);
179 		}
180 	}
181 	;
182 server_logfile: VAR_LOGFILE STRING
183 	{
184 		OUTYY(("P(server_logfile:%s)\n", $2));
185 		cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2);
186 	}
187 	;
188 server_server_count: VAR_SERVER_COUNT STRING
189 	{
190 		OUTYY(("P(server_server_count:%s)\n", $2));
191 		if(atoi($2) <= 0)
192 			yyerror("number greater than zero expected");
193 		else cfg_parser->opt->server_count = atoi($2);
194 	}
195 	;
196 server_tcp_count: VAR_TCP_COUNT STRING
197 	{
198 		OUTYY(("P(server_tcp_count:%s)\n", $2));
199 		if(atoi($2) <= 0)
200 			yyerror("number greater than zero expected");
201 		else cfg_parser->opt->tcp_count = atoi($2);
202 	}
203 	;
204 server_pidfile: VAR_PIDFILE STRING
205 	{
206 		OUTYY(("P(server_pidfile:%s)\n", $2));
207 		cfg_parser->opt->pidfile = region_strdup(cfg_parser->opt->region, $2);
208 	}
209 	;
210 server_port: VAR_PORT STRING
211 	{
212 		OUTYY(("P(server_port:%s)\n", $2));
213 		cfg_parser->opt->port = region_strdup(cfg_parser->opt->region, $2);
214 	}
215 	;
216 server_statistics: VAR_STATISTICS STRING
217 	{
218 		OUTYY(("P(server_statistics:%s)\n", $2));
219 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
220 			yyerror("number expected");
221 		else cfg_parser->opt->statistics = atoi($2);
222 	}
223 	;
224 server_zonestatsfile: VAR_ZONESTATSFILE STRING
225 	{
226 		OUTYY(("P(server_zonestatsfile:%s)\n", $2));
227 		cfg_parser->opt->zonestatsfile = region_strdup(cfg_parser->opt->region, $2);
228 	}
229 	;
230 server_chroot: VAR_CHROOT STRING
231 	{
232 		OUTYY(("P(server_chroot:%s)\n", $2));
233 		cfg_parser->opt->chroot = region_strdup(cfg_parser->opt->region, $2);
234 	}
235 	;
236 server_username: VAR_USERNAME STRING
237 	{
238 		OUTYY(("P(server_username:%s)\n", $2));
239 		cfg_parser->opt->username = region_strdup(cfg_parser->opt->region, $2);
240 	}
241 	;
242 server_zonesdir: VAR_ZONESDIR STRING
243 	{
244 		OUTYY(("P(server_zonesdir:%s)\n", $2));
245 		cfg_parser->opt->zonesdir = region_strdup(cfg_parser->opt->region, $2);
246 	}
247 	;
248 server_difffile: VAR_DIFFFILE STRING
249 	{
250 		OUTYY(("P(server_difffile:%s)\n", $2));
251 		cfg_parser->opt->difffile = region_strdup(cfg_parser->opt->region, $2);
252 	}
253 	;
254 server_xfrdfile: VAR_XFRDFILE STRING
255 	{
256 		OUTYY(("P(server_xfrdfile:%s)\n", $2));
257 		cfg_parser->opt->xfrdfile = region_strdup(cfg_parser->opt->region, $2);
258 	}
259 	;
260 server_xfrd_reload_timeout: VAR_XFRD_RELOAD_TIMEOUT STRING
261 	{
262 		OUTYY(("P(server_xfrd_reload_timeout:%s)\n", $2));
263 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
264 			yyerror("number expected");
265 		cfg_parser->opt->xfrd_reload_timeout = atoi($2);
266 	}
267 	;
268 server_tcp_query_count: VAR_TCP_QUERY_COUNT STRING
269 	{
270 		OUTYY(("P(server_tcp_query_count:%s)\n", $2));
271 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
272 			yyerror("number expected");
273 		cfg_parser->opt->tcp_query_count = atoi($2);
274 	}
275 	;
276 server_tcp_timeout: VAR_TCP_TIMEOUT STRING
277 	{
278 		OUTYY(("P(server_tcp_timeout:%s)\n", $2));
279 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
280 			yyerror("number expected");
281 		cfg_parser->opt->tcp_timeout = atoi($2);
282 	}
283 	;
284 server_ipv4_edns_size: VAR_IPV4_EDNS_SIZE STRING
285 	{
286 		OUTYY(("P(server_ipv4_edns_size:%s)\n", $2));
287 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
288 			yyerror("number expected");
289 		cfg_parser->opt->ipv4_edns_size = atoi($2);
290 	}
291 	;
292 server_ipv6_edns_size: VAR_IPV6_EDNS_SIZE STRING
293 	{
294 		OUTYY(("P(server_ipv6_edns_size:%s)\n", $2));
295 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
296 			yyerror("number expected");
297 		cfg_parser->opt->ipv6_edns_size = atoi($2);
298 	}
299 	;
300 server_rrl_size: VAR_RRL_SIZE STRING
301 	{
302 		OUTYY(("P(server_rrl_size:%s)\n", $2));
303 #ifdef RATELIMIT
304 		if(atoi($2) <= 0)
305 			yyerror("number greater than zero expected");
306 		cfg_parser->opt->rrl_size = atoi($2);
307 #endif
308 	}
309 	;
310 server_rrl_ratelimit: VAR_RRL_RATELIMIT STRING
311 	{
312 		OUTYY(("P(server_rrl_ratelimit:%s)\n", $2));
313 #ifdef RATELIMIT
314 		cfg_parser->opt->rrl_ratelimit = atoi($2);
315 #endif
316 	}
317 	;
318 server_rrl_whitelist_ratelimit: VAR_RRL_WHITELIST_RATELIMIT STRING
319 	{
320 		OUTYY(("P(server_rrl_whitelist_ratelimit:%s)\n", $2));
321 #ifdef RATELIMIT
322 		cfg_parser->opt->rrl_whitelist_ratelimit = atoi($2);
323 #endif
324 	}
325 	;
326 
327 /* zone: declaration */
328 zonestart: VAR_ZONE
329 	{
330 		OUTYY(("\nP(zone:)\n"));
331 		if(cfg_parser->current_zone) {
332 			if(!cfg_parser->current_zone->name)
333 				c_error("previous zone has no name");
334 			else {
335 				if(!nsd_options_insert_zone(cfg_parser->opt,
336 					cfg_parser->current_zone))
337 					c_error("duplicate zone");
338 			}
339 			if(!cfg_parser->current_zone->zonefile)
340 				c_error("previous zone has no zonefile");
341 		}
342 		cfg_parser->current_zone = zone_options_create(cfg_parser->opt->region);
343 		cfg_parser->current_allow_notify = 0;
344 		cfg_parser->current_request_xfr = 0;
345 		cfg_parser->current_notify = 0;
346 		cfg_parser->current_provide_xfr = 0;
347 		cfg_parser->current_outgoing_interface = 0;
348 	}
349 	;
350 contents_zone: contents_zone content_zone | content_zone;
351 content_zone: zone_name | zone_zonefile | zone_allow_notify |
352 	zone_request_xfr | zone_notify | zone_notify_retry | zone_provide_xfr |
353 	zone_outgoing_interface | zone_allow_axfr_fallback | zone_rrl_whitelist;
354 zone_name: VAR_NAME STRING
355 	{
356 		OUTYY(("P(zone_name:%s)\n", $2));
357 #ifndef NDEBUG
358 		assert(cfg_parser->current_zone);
359 #endif
360 		cfg_parser->current_zone->name = region_strdup(cfg_parser->opt->region, $2);
361 	}
362 	;
363 zone_zonefile: VAR_ZONEFILE STRING
364 	{
365 		OUTYY(("P(zone_zonefile:%s)\n", $2));
366 #ifndef NDEBUG
367 		assert(cfg_parser->current_zone);
368 #endif
369 		cfg_parser->current_zone->zonefile = region_strdup(cfg_parser->opt->region, $2);
370 	}
371 	;
372 zone_allow_notify: VAR_ALLOW_NOTIFY STRING STRING
373 	{
374 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
375 		OUTYY(("P(zone_allow_notify:%s %s)\n", $2, $3));
376 		if(cfg_parser->current_allow_notify)
377 			cfg_parser->current_allow_notify->next = acl;
378 		else
379 			cfg_parser->current_zone->allow_notify = acl;
380 		cfg_parser->current_allow_notify = acl;
381 	}
382 	;
383 zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data
384 	{
385 	}
386 	;
387 zone_request_xfr_data: STRING STRING
388 	{
389 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $1, $2);
390 		OUTYY(("P(zone_request_xfr:%s %s)\n", $1, $2));
391 		if(acl->blocked) c_error("blocked address used for request-xfr");
392 		if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
393 		if(cfg_parser->current_request_xfr)
394 			cfg_parser->current_request_xfr->next = acl;
395 		else
396 			cfg_parser->current_zone->request_xfr = acl;
397 		cfg_parser->current_request_xfr = acl;
398 	}
399 	| VAR_AXFR STRING STRING
400 	{
401 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
402 		acl->use_axfr_only = 1;
403 		OUTYY(("P(zone_request_xfr:%s %s)\n", $2, $3));
404 		if(acl->blocked) c_error("blocked address used for request-xfr");
405 		if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
406 		if(cfg_parser->current_request_xfr)
407 			cfg_parser->current_request_xfr->next = acl;
408 		else
409 			cfg_parser->current_zone->request_xfr = acl;
410 		cfg_parser->current_request_xfr = acl;
411 	}
412 	| VAR_UDP STRING STRING
413 	{
414 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
415 		acl->allow_udp = 1;
416 		OUTYY(("P(zone_request_xfr:%s %s)\n", $2, $3));
417 		if(acl->blocked) c_error("blocked address used for request-xfr");
418 		if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
419 		if(cfg_parser->current_request_xfr)
420 			cfg_parser->current_request_xfr->next = acl;
421 		else
422 			cfg_parser->current_zone->request_xfr = acl;
423 		cfg_parser->current_request_xfr = acl;
424 	}
425 	;
426 zone_notify: VAR_NOTIFY STRING STRING
427 	{
428 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
429 		OUTYY(("P(zone_notify:%s %s)\n", $2, $3));
430 		if(acl->blocked) c_error("blocked address used for notify");
431 		if(acl->rangetype!=acl_range_single) c_error("address range used for notify");
432 		if(cfg_parser->current_notify)
433 			cfg_parser->current_notify->next = acl;
434 		else
435 			cfg_parser->current_zone->notify = acl;
436 		cfg_parser->current_notify = acl;
437 	}
438 	;
439 zone_notify_retry: VAR_NOTIFY_RETRY STRING
440 	{
441 		OUTYY(("P(zone_notify_retry:%s)\n", $2));
442 		if(atoi($2) == 0 && strcmp($2, "0") != 0)
443 			yyerror("number expected");
444 		else cfg_parser->current_zone->notify_retry = atoi($2);
445 	}
446 	;
447 zone_provide_xfr: VAR_PROVIDE_XFR STRING STRING
448 	{
449 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
450 		OUTYY(("P(zone_provide_xfr:%s %s)\n", $2, $3));
451 		if(cfg_parser->current_provide_xfr)
452 			cfg_parser->current_provide_xfr->next = acl;
453 		else
454 			cfg_parser->current_zone->provide_xfr = acl;
455 		cfg_parser->current_provide_xfr = acl;
456 	}
457 	;
458 zone_outgoing_interface: VAR_OUTGOING_INTERFACE STRING
459 	{
460 		acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $2, "NOKEY");
461 		OUTYY(("P(zone_outgoing_interface:%s)\n", $2));
462 		if(acl->rangetype!=acl_range_single) c_error("address range used for outgoing interface");
463 		if(cfg_parser->current_outgoing_interface)
464 			cfg_parser->current_outgoing_interface->next = acl;
465 		else
466 			cfg_parser->current_zone->outgoing_interface = acl;
467 		cfg_parser->current_outgoing_interface = acl;
468 	}
469 	;
470 zone_allow_axfr_fallback: VAR_ALLOW_AXFR_FALLBACK STRING
471 	{
472 		OUTYY(("P(zone_allow_axfr_fallback:%s)\n", $2));
473 		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
474 			yyerror("expected yes or no.");
475 		else cfg_parser->current_zone->allow_axfr_fallback = (strcmp($2, "yes")==0);
476 	}
477 	;
478 zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
479 	{
480 		OUTYY(("P(zone_rrl_whitelist:%s)\n", $2));
481 #ifdef RATELIMIT
482 		cfg_parser->current_zone->rrl_whitelist |= rrlstr2type($2);
483 #endif
484 	}
485 	;
486 
487 /* key: declaration */
488 keystart: VAR_KEY
489 	{
490 		OUTYY(("\nP(key:)\n"));
491 		if(cfg_parser->current_key) {
492 			if(!cfg_parser->current_key->name) c_error("previous key has no name");
493 			if(!cfg_parser->current_key->algorithm) c_error("previous key has no algorithm");
494 			if(!cfg_parser->current_key->secret) c_error("previous key has no secret blob");
495 			cfg_parser->current_key->next = key_options_create(cfg_parser->opt->region);
496 			cfg_parser->current_key = cfg_parser->current_key->next;
497 		} else {
498 			cfg_parser->current_key = key_options_create(cfg_parser->opt->region);
499                 	cfg_parser->opt->keys = cfg_parser->current_key;
500 		}
501 		cfg_parser->opt->numkeys++;
502 	}
503 	;
504 contents_key: contents_key content_key | content_key;
505 content_key: key_name | key_algorithm | key_secret;
506 key_name: VAR_NAME STRING
507 	{
508 		OUTYY(("P(key_name:%s)\n", $2));
509 #ifndef NDEBUG
510 		assert(cfg_parser->current_key);
511 #endif
512 		cfg_parser->current_key->name = region_strdup(cfg_parser->opt->region, $2);
513 	}
514 	;
515 key_algorithm: VAR_ALGORITHM STRING
516 	{
517 		OUTYY(("P(key_algorithm:%s)\n", $2));
518 #ifndef NDEBUG
519 		assert(cfg_parser->current_key);
520 #endif
521 		cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, $2);
522 	}
523 	;
524 key_secret: VAR_SECRET STRING
525 	{
526 		OUTYY(("key_secret:%s)\n", $2));
527 #ifndef NDEBUG
528 		assert(cfg_parser->current_key);
529 #endif
530 		cfg_parser->current_key->secret = region_strdup(cfg_parser->opt->region, $2);
531 	}
532 	;
533 
534 %%
535 
536 /* parse helper routines could be here */
537