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