1 /*
2 * configparser.y -- yacc grammar for NSD configuration files
3 *
4 * Copyright (c) 2001-2019, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9
10 %{
11 #include "config.h"
12
13 #include <assert.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
17
18 #include "options.h"
19 #include "util.h"
20 #include "dname.h"
21 #include "tsig.h"
22 #include "rrl.h"
23
24 int yylex(void);
25
26 #ifdef __cplusplus
27 extern "C"
28 #endif
29
30 /* these need to be global, otherwise they cannot be used inside yacc */
31 extern config_parser_state_type *cfg_parser;
32
33 static void append_acl(struct acl_options **list, struct acl_options *acl);
34 static void add_to_last_acl(struct acl_options **list, char *ac);
35 static int parse_boolean(const char *str, int *bln);
36 static int parse_catalog_role(const char *str, int *role);
37 static int parse_expire_expr(const char *str, long long *num, uint8_t *expr);
38 static int parse_number(const char *str, long long *num);
39 static int parse_range(const char *str, long long *low, long long *high);
40
41 struct component {
42 struct component *next;
43 char *str;
44 };
45
46 %}
47
48 %union {
49 char *str;
50 long long llng;
51 int bln;
52 struct ip_address_option *ip;
53 struct range_option *range;
54 struct cpu_option *cpu;
55 char **strv;
56 struct component *comp;
57 int role;
58 }
59
60 %token <str> STRING
61 %type <llng> number
62 %type <bln> boolean
63 %type <ip> ip_address
64 %type <llng> service_cpu_affinity
65 %type <cpu> cpus
66 %type <strv> command
67 %type <comp> arguments
68 %type <role> catalog_role
69
70 /* server */
71 %token VAR_SERVER
72 %token VAR_SERVER_COUNT
73 %token VAR_IP_ADDRESS
74 %token VAR_IP_TRANSPARENT
75 %token VAR_IP_FREEBIND
76 %token VAR_REUSEPORT
77 %token VAR_SEND_BUFFER_SIZE
78 %token VAR_RECEIVE_BUFFER_SIZE
79 %token VAR_DEBUG_MODE
80 %token VAR_IP4_ONLY
81 %token VAR_IP6_ONLY
82 %token VAR_DO_IP4
83 %token VAR_DO_IP6
84 %token VAR_PORT
85 %token VAR_USE_SYSTEMD
86 %token VAR_VERBOSITY
87 %token VAR_USERNAME
88 %token VAR_CHROOT
89 %token VAR_ZONESDIR
90 %token VAR_ZONELISTFILE
91 %token VAR_DATABASE
92 %token VAR_LOGFILE
93 %token VAR_LOG_ONLY_SYSLOG
94 %token VAR_PIDFILE
95 %token VAR_DIFFFILE
96 %token VAR_XFRDFILE
97 %token VAR_XFRDIR
98 %token VAR_HIDE_VERSION
99 %token VAR_HIDE_IDENTITY
100 %token VAR_VERSION
101 %token VAR_IDENTITY
102 %token VAR_NSID
103 %token VAR_TCP_COUNT
104 %token VAR_TCP_REJECT_OVERFLOW
105 %token VAR_TCP_QUERY_COUNT
106 %token VAR_TCP_TIMEOUT
107 %token VAR_TCP_MSS
108 %token VAR_OUTGOING_TCP_MSS
109 %token VAR_IPV4_EDNS_SIZE
110 %token VAR_IPV6_EDNS_SIZE
111 %token VAR_STATISTICS
112 %token VAR_XFRD_RELOAD_TIMEOUT
113 %token VAR_LOG_TIME_ASCII
114 %token VAR_ROUND_ROBIN
115 %token VAR_MINIMAL_RESPONSES
116 %token VAR_CONFINE_TO_ZONE
117 %token VAR_REFUSE_ANY
118 %token VAR_ZONEFILES_CHECK
119 %token VAR_ZONEFILES_WRITE
120 %token VAR_RRL_SIZE
121 %token VAR_RRL_RATELIMIT
122 %token VAR_RRL_SLIP
123 %token VAR_RRL_IPV4_PREFIX_LENGTH
124 %token VAR_RRL_IPV6_PREFIX_LENGTH
125 %token VAR_RRL_WHITELIST_RATELIMIT
126 %token VAR_TLS_SERVICE_KEY
127 %token VAR_TLS_SERVICE_PEM
128 %token VAR_TLS_SERVICE_OCSP
129 %token VAR_TLS_PORT
130 %token VAR_TLS_CERT_BUNDLE
131 %token VAR_PROXY_PROTOCOL_PORT
132 %token VAR_CPU_AFFINITY
133 %token VAR_XFRD_CPU_AFFINITY
134 %token <llng> VAR_SERVER_CPU_AFFINITY
135 %token VAR_DROP_UPDATES
136 %token VAR_XFRD_TCP_MAX
137 %token VAR_XFRD_TCP_PIPELINE
138
139 /* dnstap */
140 %token VAR_DNSTAP
141 %token VAR_DNSTAP_ENABLE
142 %token VAR_DNSTAP_SOCKET_PATH
143 %token VAR_DNSTAP_IP
144 %token VAR_DNSTAP_TLS
145 %token VAR_DNSTAP_TLS_SERVER_NAME
146 %token VAR_DNSTAP_TLS_CERT_BUNDLE
147 %token VAR_DNSTAP_TLS_CLIENT_KEY_FILE
148 %token VAR_DNSTAP_TLS_CLIENT_CERT_FILE
149 %token VAR_DNSTAP_SEND_IDENTITY
150 %token VAR_DNSTAP_SEND_VERSION
151 %token VAR_DNSTAP_IDENTITY
152 %token VAR_DNSTAP_VERSION
153 %token VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES
154 %token VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES
155
156 /* remote-control */
157 %token VAR_REMOTE_CONTROL
158 %token VAR_CONTROL_ENABLE
159 %token VAR_CONTROL_INTERFACE
160 %token VAR_CONTROL_PORT
161 %token VAR_SERVER_KEY_FILE
162 %token VAR_SERVER_CERT_FILE
163 %token VAR_CONTROL_KEY_FILE
164 %token VAR_CONTROL_CERT_FILE
165
166 /* key */
167 %token VAR_KEY
168 %token VAR_ALGORITHM
169 %token VAR_SECRET
170
171 /* xot auth */
172 %token VAR_TLS_AUTH
173 %token VAR_TLS_AUTH_DOMAIN_NAME
174 %token VAR_TLS_AUTH_CLIENT_CERT
175 %token VAR_TLS_AUTH_CLIENT_KEY
176 %token VAR_TLS_AUTH_CLIENT_KEY_PW
177
178 /* pattern */
179 %token VAR_PATTERN
180 %token VAR_NAME
181 %token VAR_ZONEFILE
182 %token VAR_NOTIFY
183 %token VAR_PROVIDE_XFR
184 %token VAR_ALLOW_QUERY
185 %token VAR_AXFR
186 %token VAR_UDP
187 %token VAR_NOTIFY_RETRY
188 %token VAR_ALLOW_NOTIFY
189 %token VAR_REQUEST_XFR
190 %token VAR_ALLOW_AXFR_FALLBACK
191 %token VAR_OUTGOING_INTERFACE
192 %token VAR_ANSWER_COOKIE
193 %token VAR_COOKIE_SECRET
194 %token VAR_COOKIE_SECRET_FILE
195 %token VAR_MAX_REFRESH_TIME
196 %token VAR_MIN_REFRESH_TIME
197 %token VAR_MAX_RETRY_TIME
198 %token VAR_MIN_RETRY_TIME
199 %token VAR_MIN_EXPIRE_TIME
200 %token VAR_MULTI_PRIMARY_CHECK
201 %token VAR_SIZE_LIMIT_XFR
202 %token VAR_ZONESTATS
203 %token VAR_INCLUDE_PATTERN
204 %token VAR_STORE_IXFR
205 %token VAR_IXFR_SIZE
206 %token VAR_IXFR_NUMBER
207 %token VAR_CREATE_IXFR
208 %token VAR_CATALOG
209 %token VAR_CATALOG_MEMBER_PATTERN
210 %token VAR_CATALOG_PRODUCER_ZONE
211
212 /* zone */
213 %token VAR_ZONE
214 %token VAR_RRL_WHITELIST
215
216 /* socket options */
217 %token VAR_SERVERS
218 %token VAR_BINDTODEVICE
219 %token VAR_SETFIB
220
221 /* verify */
222 %token VAR_VERIFY
223 %token VAR_ENABLE
224 %token VAR_VERIFY_ZONE
225 %token VAR_VERIFY_ZONES
226 %token VAR_VERIFIER
227 %token VAR_VERIFIER_COUNT
228 %token VAR_VERIFIER_FEED_ZONE
229 %token VAR_VERIFIER_TIMEOUT
230
231 %%
232
233 blocks:
234 /* may be empty */
235 | blocks block ;
236
237 block:
238 server
239 | dnstap
240 | remote_control
241 | key
242 | tls_auth
243 | pattern
244 | zone
245 | verify ;
246
247 server:
248 VAR_SERVER server_block ;
249
250 server_block:
251 server_block server_option | ;
252
253 server_option:
254 VAR_IP_ADDRESS ip_address
255 {
256 struct ip_address_option *ip = cfg_parser->opt->ip_addresses;
257
258 if(ip == NULL) {
259 cfg_parser->opt->ip_addresses = $2;
260 } else {
261 while(ip->next) { ip = ip->next; }
262 ip->next = $2;
263 }
264
265 cfg_parser->ip = $2;
266 }
267 socket_options
268 {
269 cfg_parser->ip = NULL;
270 }
271 | VAR_SERVER_COUNT number
272 {
273 if ($2 > 0) {
274 cfg_parser->opt->server_count = (int)$2;
275 } else {
276 yyerror("expected a number greater than zero");
277 }
278 }
279 | VAR_IP_TRANSPARENT boolean
280 { cfg_parser->opt->ip_transparent = $2; }
281 | VAR_IP_FREEBIND boolean
282 { cfg_parser->opt->ip_freebind = $2; }
283 | VAR_SEND_BUFFER_SIZE number
284 { cfg_parser->opt->send_buffer_size = (int)$2; }
285 | VAR_RECEIVE_BUFFER_SIZE number
286 { cfg_parser->opt->receive_buffer_size = (int)$2; }
287 | VAR_DEBUG_MODE boolean
288 { cfg_parser->opt->debug_mode = $2; }
289 | VAR_USE_SYSTEMD boolean
290 { /* ignored, obsolete */ }
291 | VAR_HIDE_VERSION boolean
292 { cfg_parser->opt->hide_version = $2; }
293 | VAR_HIDE_IDENTITY boolean
294 { cfg_parser->opt->hide_identity = $2; }
295 | VAR_DROP_UPDATES boolean
296 { cfg_parser->opt->drop_updates = $2; }
297 | VAR_IP4_ONLY boolean
298 { if($2) { cfg_parser->opt->do_ip4 = 1; cfg_parser->opt->do_ip6 = 0; } }
299 | VAR_IP6_ONLY boolean
300 { if($2) { cfg_parser->opt->do_ip4 = 0; cfg_parser->opt->do_ip6 = 1; } }
301 | VAR_DO_IP4 boolean
302 { cfg_parser->opt->do_ip4 = $2; }
303 | VAR_DO_IP6 boolean
304 { cfg_parser->opt->do_ip6 = $2; }
305 | VAR_DATABASE STRING
306 { /* ignored, obsolete */ }
307 | VAR_IDENTITY STRING
308 { cfg_parser->opt->identity = region_strdup(cfg_parser->opt->region, $2); }
309 | VAR_VERSION STRING
310 { cfg_parser->opt->version = region_strdup(cfg_parser->opt->region, $2); }
311 | VAR_NSID STRING
312 {
313 unsigned char* nsid = 0;
314 size_t nsid_len = strlen($2);
315
316 if (strncasecmp($2, "ascii_", 6) == 0) {
317 nsid_len -= 6; /* discard "ascii_" */
318 if(nsid_len < 65535) {
319 cfg_parser->opt->nsid = region_alloc(cfg_parser->opt->region, nsid_len*2+1);
320 hex_ntop((uint8_t*)$2+6, nsid_len, (char*)cfg_parser->opt->nsid, nsid_len*2+1);
321 } else {
322 yyerror("NSID too long");
323 }
324 } else if (nsid_len % 2 != 0) {
325 yyerror("the NSID must be a hex string of an even length.");
326 } else {
327 nsid_len = nsid_len / 2;
328 if(nsid_len < 65535) {
329 nsid = xalloc(nsid_len);
330 if (hex_pton($2, nsid, nsid_len) == -1) {
331 yyerror("hex string cannot be parsed in NSID.");
332 } else {
333 cfg_parser->opt->nsid = region_strdup(cfg_parser->opt->region, $2);
334 }
335 free(nsid);
336 } else {
337 yyerror("NSID too long");
338 }
339 }
340 }
341 | VAR_LOGFILE STRING
342 { cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2); }
343 | VAR_LOG_ONLY_SYSLOG boolean
344 { cfg_parser->opt->log_only_syslog = $2; }
345 | VAR_TCP_COUNT number
346 {
347 if ($2 > 0) {
348 cfg_parser->opt->tcp_count = (int)$2;
349 } else {
350 yyerror("expected a number greater than zero");
351 }
352 }
353 | VAR_TCP_REJECT_OVERFLOW boolean
354 { cfg_parser->opt->tcp_reject_overflow = $2; }
355 | VAR_TCP_QUERY_COUNT number
356 { cfg_parser->opt->tcp_query_count = (int)$2; }
357 | VAR_TCP_TIMEOUT number
358 { cfg_parser->opt->tcp_timeout = (int)$2; }
359 | VAR_TCP_MSS number
360 { cfg_parser->opt->tcp_mss = (int)$2; }
361 | VAR_OUTGOING_TCP_MSS number
362 { cfg_parser->opt->outgoing_tcp_mss = (int)$2; }
363 | VAR_IPV4_EDNS_SIZE number
364 { cfg_parser->opt->ipv4_edns_size = (size_t)$2; }
365 | VAR_IPV6_EDNS_SIZE number
366 { cfg_parser->opt->ipv6_edns_size = (size_t)$2; }
367 | VAR_PIDFILE STRING
368 { cfg_parser->opt->pidfile = region_strdup(cfg_parser->opt->region, $2); }
369 | VAR_PORT number
370 {
371 /* port number, stored as a string */
372 char buf[16];
373 (void)snprintf(buf, sizeof(buf), "%lld", $2);
374 cfg_parser->opt->port = region_strdup(cfg_parser->opt->region, buf);
375 }
376 | VAR_REUSEPORT boolean
377 { cfg_parser->opt->reuseport = $2; }
378 | VAR_STATISTICS number
379 { cfg_parser->opt->statistics = (int)$2; }
380 | VAR_CHROOT STRING
381 { cfg_parser->opt->chroot = region_strdup(cfg_parser->opt->region, $2); }
382 | VAR_USERNAME STRING
383 { cfg_parser->opt->username = region_strdup(cfg_parser->opt->region, $2); }
384 | VAR_ZONESDIR STRING
385 { cfg_parser->opt->zonesdir = region_strdup(cfg_parser->opt->region, $2); }
386 | VAR_ZONELISTFILE STRING
387 { cfg_parser->opt->zonelistfile = region_strdup(cfg_parser->opt->region, $2); }
388 | VAR_DIFFFILE STRING
389 { /* ignored, obsolete */ }
390 | VAR_XFRDFILE STRING
391 { cfg_parser->opt->xfrdfile = region_strdup(cfg_parser->opt->region, $2); }
392 | VAR_XFRDIR STRING
393 { cfg_parser->opt->xfrdir = region_strdup(cfg_parser->opt->region, $2); }
394 | VAR_XFRD_RELOAD_TIMEOUT number
395 { cfg_parser->opt->xfrd_reload_timeout = (int)$2; }
396 | VAR_VERBOSITY number
397 { cfg_parser->opt->verbosity = (int)$2; }
398 | VAR_RRL_SIZE number
399 {
400 #ifdef RATELIMIT
401 if ($2 > 0) {
402 cfg_parser->opt->rrl_size = (size_t)$2;
403 } else {
404 yyerror("expected a number greater than zero");
405 }
406 #endif
407 }
408 | VAR_RRL_RATELIMIT number
409 {
410 #ifdef RATELIMIT
411 cfg_parser->opt->rrl_ratelimit = (size_t)$2;
412 #endif
413 }
414 | VAR_RRL_SLIP number
415 {
416 #ifdef RATELIMIT
417 cfg_parser->opt->rrl_slip = (size_t)$2;
418 #endif
419 }
420 | VAR_RRL_IPV4_PREFIX_LENGTH number
421 {
422 #ifdef RATELIMIT
423 if ($2 > 32) {
424 yyerror("invalid IPv4 prefix length");
425 } else {
426 cfg_parser->opt->rrl_ipv4_prefix_length = (size_t)$2;
427 }
428 #endif
429 }
430 | VAR_RRL_IPV6_PREFIX_LENGTH number
431 {
432 #ifdef RATELIMIT
433 if ($2 > 64) {
434 yyerror("invalid IPv6 prefix length");
435 } else {
436 cfg_parser->opt->rrl_ipv6_prefix_length = (size_t)$2;
437 }
438 #endif
439 }
440 | VAR_RRL_WHITELIST_RATELIMIT number
441 {
442 #ifdef RATELIMIT
443 cfg_parser->opt->rrl_whitelist_ratelimit = (size_t)$2;
444 #endif
445 }
446 | VAR_ZONEFILES_CHECK boolean
447 { cfg_parser->opt->zonefiles_check = $2; }
448 | VAR_ZONEFILES_WRITE number
449 { cfg_parser->opt->zonefiles_write = (int)$2; }
450 | VAR_LOG_TIME_ASCII boolean
451 {
452 cfg_parser->opt->log_time_ascii = $2;
453 log_time_asc = cfg_parser->opt->log_time_ascii;
454 }
455 | VAR_ROUND_ROBIN boolean
456 {
457 cfg_parser->opt->round_robin = $2;
458 round_robin = cfg_parser->opt->round_robin;
459 }
460 | VAR_MINIMAL_RESPONSES boolean
461 {
462 cfg_parser->opt->minimal_responses = $2;
463 minimal_responses = cfg_parser->opt->minimal_responses;
464 }
465 | VAR_CONFINE_TO_ZONE boolean
466 { cfg_parser->opt->confine_to_zone = $2; }
467 | VAR_REFUSE_ANY boolean
468 { cfg_parser->opt->refuse_any = $2; }
469 | VAR_TLS_SERVICE_KEY STRING
470 { cfg_parser->opt->tls_service_key = region_strdup(cfg_parser->opt->region, $2); }
471 | VAR_TLS_SERVICE_OCSP STRING
472 { cfg_parser->opt->tls_service_ocsp = region_strdup(cfg_parser->opt->region, $2); }
473 | VAR_TLS_SERVICE_PEM STRING
474 { cfg_parser->opt->tls_service_pem = region_strdup(cfg_parser->opt->region, $2); }
475 | VAR_TLS_PORT number
476 {
477 /* port number, stored as string */
478 char buf[16];
479 (void)snprintf(buf, sizeof(buf), "%lld", $2);
480 cfg_parser->opt->tls_port = region_strdup(cfg_parser->opt->region, buf);
481 }
482 | VAR_TLS_CERT_BUNDLE STRING
483 { cfg_parser->opt->tls_cert_bundle = region_strdup(cfg_parser->opt->region, $2); }
484 | VAR_PROXY_PROTOCOL_PORT number
485 {
486 struct proxy_protocol_port_list* elem = region_alloc_zero(
487 cfg_parser->opt->region, sizeof(*elem));
488 elem->port = $2;
489 elem->next = cfg_parser->opt->proxy_protocol_port;
490 cfg_parser->opt->proxy_protocol_port = elem;
491 }
492 | VAR_ANSWER_COOKIE boolean
493 { cfg_parser->opt->answer_cookie = $2; }
494 | VAR_COOKIE_SECRET STRING
495 { cfg_parser->opt->cookie_secret = region_strdup(cfg_parser->opt->region, $2); }
496 | VAR_COOKIE_SECRET_FILE STRING
497 { cfg_parser->opt->cookie_secret_file = region_strdup(cfg_parser->opt->region, $2); }
498 | VAR_XFRD_TCP_MAX number
499 { cfg_parser->opt->xfrd_tcp_max = (int)$2; }
500 | VAR_XFRD_TCP_PIPELINE number
501 { cfg_parser->opt->xfrd_tcp_pipeline = (int)$2; }
502 | VAR_CPU_AFFINITY cpus
503 {
504 cfg_parser->opt->cpu_affinity = $2;
505 }
506 | service_cpu_affinity number
507 {
508 if($2 < 0) {
509 yyerror("expected a non-negative number");
510 YYABORT;
511 } else {
512 struct cpu_map_option *opt, *tail;
513
514 opt = cfg_parser->opt->service_cpu_affinity;
515 while(opt && opt->service != $1) { opt = opt->next; }
516
517 if(opt) {
518 opt->cpu = $2;
519 } else {
520 opt = region_alloc_zero(cfg_parser->opt->region, sizeof(*opt));
521 opt->service = (int)$1;
522 opt->cpu = (int)$2;
523
524 tail = cfg_parser->opt->service_cpu_affinity;
525 if(tail) {
526 while(tail->next) { tail = tail->next; }
527 tail->next = opt;
528 } else {
529 cfg_parser->opt->service_cpu_affinity = opt;
530 }
531 }
532 }
533 }
534 ;
535
536 socket_options:
537 | socket_options socket_option ;
538
539 socket_option:
540 VAR_SERVERS STRING
541 {
542 char *tok, *ptr, *str;
543 struct range_option *servers = NULL;
544 long long first, last;
545
546 /* user may specify "0 1", "0" "1", 0 1 or a combination thereof */
547 for(str = $2; (tok = strtok_r(str, " \t", &ptr)); str = NULL) {
548 struct range_option *opt =
549 region_alloc(cfg_parser->opt->region, sizeof(*opt));
550 first = last = 0;
551 if(!parse_range(tok, &first, &last)) {
552 yyerror("invalid server range '%s'", tok);
553 YYABORT;
554 }
555 assert(first >= 0);
556 assert(last >= 0);
557 opt->next = NULL;
558 opt->first = (int)first;
559 opt->last = (int)last;
560 if(servers) {
561 servers = servers->next = opt;
562 } else {
563 servers = cfg_parser->ip->servers = opt;
564 }
565 }
566 }
567 | VAR_BINDTODEVICE boolean
568 { cfg_parser->ip->dev = $2; }
569 | VAR_SETFIB number
570 { cfg_parser->ip->fib = $2; }
571 ;
572
573 cpus:
574 { $$ = NULL; }
575 | cpus STRING
576 {
577 char *tok, *ptr, *str;
578 struct cpu_option *tail;
579 long long cpu;
580
581 str = $2;
582 $$ = tail = $1;
583 if(tail) {
584 while(tail->next) { tail = tail->next; }
585 }
586
587 /* Users may specify "0 1", "0" "1", 0 1 or a combination thereof. */
588 for(str = $2; (tok = strtok_r(str, " \t", &ptr)); str = NULL) {
589 struct cpu_option *opt =
590 region_alloc_zero(cfg_parser->opt->region, sizeof(*opt));
591 cpu = 0;
592 if(!parse_number(tok, &cpu) || cpu < 0) {
593 yyerror("expected a positive number");
594 YYABORT;
595 }
596 assert(cpu >=0);
597 opt->cpu = (int)cpu;
598 if(tail) {
599 tail->next = opt;
600 tail = opt;
601 } else {
602 $$ = tail = opt;
603 }
604 }
605 }
606 ;
607
608 service_cpu_affinity:
609 VAR_XFRD_CPU_AFFINITY
610 { $$ = -1; }
611 | VAR_SERVER_CPU_AFFINITY
612 {
613 if($1 <= 0) {
614 yyerror("invalid server identifier");
615 YYABORT;
616 }
617 $$ = $1;
618 }
619 ;
620
621 dnstap:
622 VAR_DNSTAP dnstap_block ;
623
624 dnstap_block:
625 dnstap_block dnstap_option | ;
626
627 dnstap_option:
628 VAR_DNSTAP_ENABLE boolean
629 { cfg_parser->opt->dnstap_enable = $2; }
630 | VAR_DNSTAP_SOCKET_PATH STRING
631 { cfg_parser->opt->dnstap_socket_path = region_strdup(cfg_parser->opt->region, $2); }
632 | VAR_DNSTAP_IP STRING
633 { cfg_parser->opt->dnstap_ip = region_strdup(cfg_parser->opt->region, $2); }
634 | VAR_DNSTAP_TLS boolean
635 { cfg_parser->opt->dnstap_tls = $2; }
636 | VAR_DNSTAP_TLS_SERVER_NAME STRING
637 { cfg_parser->opt->dnstap_tls_server_name = region_strdup(cfg_parser->opt->region, $2); }
638 | VAR_DNSTAP_TLS_CERT_BUNDLE STRING
639 { cfg_parser->opt->dnstap_tls_cert_bundle = region_strdup(cfg_parser->opt->region, $2); }
640 | VAR_DNSTAP_TLS_CLIENT_KEY_FILE STRING
641 { cfg_parser->opt->dnstap_tls_client_key_file = region_strdup(cfg_parser->opt->region, $2); }
642 | VAR_DNSTAP_TLS_CLIENT_CERT_FILE STRING
643 { cfg_parser->opt->dnstap_tls_client_cert_file = region_strdup(cfg_parser->opt->region, $2); }
644 | VAR_DNSTAP_SEND_IDENTITY boolean
645 { cfg_parser->opt->dnstap_send_identity = $2; }
646 | VAR_DNSTAP_SEND_VERSION boolean
647 { cfg_parser->opt->dnstap_send_version = $2; }
648 | VAR_DNSTAP_IDENTITY STRING
649 { cfg_parser->opt->dnstap_identity = region_strdup(cfg_parser->opt->region, $2); }
650 | VAR_DNSTAP_VERSION STRING
651 { cfg_parser->opt->dnstap_version = region_strdup(cfg_parser->opt->region, $2); }
652 | VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES boolean
653 { cfg_parser->opt->dnstap_log_auth_query_messages = $2; }
654 | VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES boolean
655 { cfg_parser->opt->dnstap_log_auth_response_messages = $2; }
656 ;
657
658 remote_control:
659 VAR_REMOTE_CONTROL remote_control_block ;
660
661 remote_control_block:
662 remote_control_block remote_control_option | ;
663
664 remote_control_option:
665 VAR_CONTROL_ENABLE boolean
666 { cfg_parser->opt->control_enable = $2; }
667 | VAR_CONTROL_INTERFACE ip_address
668 {
669 struct ip_address_option *ip = cfg_parser->opt->control_interface;
670 if(ip == NULL) {
671 cfg_parser->opt->control_interface = $2;
672 } else {
673 while(ip->next != NULL) { ip = ip->next; }
674 ip->next = $2;
675 }
676 }
677 | VAR_CONTROL_PORT number
678 {
679 if($2 == 0) {
680 yyerror("control port number expected");
681 } else {
682 cfg_parser->opt->control_port = (int)$2;
683 }
684 }
685 | VAR_SERVER_KEY_FILE STRING
686 { cfg_parser->opt->server_key_file = region_strdup(cfg_parser->opt->region, $2); }
687 | VAR_SERVER_CERT_FILE STRING
688 { cfg_parser->opt->server_cert_file = region_strdup(cfg_parser->opt->region, $2); }
689 | VAR_CONTROL_KEY_FILE STRING
690 { cfg_parser->opt->control_key_file = region_strdup(cfg_parser->opt->region, $2); }
691 | VAR_CONTROL_CERT_FILE STRING
692 { cfg_parser->opt->control_cert_file = region_strdup(cfg_parser->opt->region, $2); }
693 ;
694
695 tls_auth:
696 VAR_TLS_AUTH
697 {
698 tls_auth_options_type *tls_auth = tls_auth_options_create(cfg_parser->opt->region);
699 assert(cfg_parser->tls_auth == NULL);
700 cfg_parser->tls_auth = tls_auth;
701 }
702 tls_auth_block
703 {
704 struct tls_auth_options *tls_auth = cfg_parser->tls_auth;
705 if(tls_auth->name == NULL) {
706 yyerror("tls-auth has no name");
707 } else if(tls_auth->auth_domain_name == NULL) {
708 yyerror("tls-auth %s has no auth-domain-name", tls_auth->name);
709 } else if(tls_auth_options_find(cfg_parser->opt, tls_auth->name)) {
710 yyerror("duplicate tls-auth %s", tls_auth->name);
711 } else {
712 tls_auth_options_insert(cfg_parser->opt, tls_auth);
713 cfg_parser->tls_auth = NULL;
714 }
715 } ;
716
717 tls_auth_block:
718 tls_auth_block tls_auth_option | ;
719
720 tls_auth_option:
721 VAR_NAME STRING
722 {
723 dname_type *dname;
724 dname = (dname_type *)dname_parse(cfg_parser->opt->region, $2);
725 cfg_parser->tls_auth->name = region_strdup(cfg_parser->opt->region, $2);
726 if(dname == NULL) {
727 yyerror("bad tls-auth name %s", $2);
728 } else {
729 region_recycle(cfg_parser->opt->region, dname, dname_total_size(dname));
730 }
731 }
732 | VAR_TLS_AUTH_DOMAIN_NAME STRING
733 {
734 cfg_parser->tls_auth->auth_domain_name = region_strdup(cfg_parser->opt->region, $2);
735 }
736 | VAR_TLS_AUTH_CLIENT_CERT STRING
737 {
738 cfg_parser->tls_auth->client_cert = region_strdup(cfg_parser->opt->region, $2);
739 }
740 | VAR_TLS_AUTH_CLIENT_KEY STRING
741 {
742 cfg_parser->tls_auth->client_key = region_strdup(cfg_parser->opt->region, $2);
743 }
744 | VAR_TLS_AUTH_CLIENT_KEY_PW STRING
745 {
746 cfg_parser->tls_auth->client_key_pw = region_strdup(cfg_parser->opt->region, $2);
747 }
748 ;
749
750 key:
751 VAR_KEY
752 {
753 key_options_type *key = key_options_create(cfg_parser->opt->region);
754 key->algorithm = region_strdup(cfg_parser->opt->region, "sha256");
755 assert(cfg_parser->key == NULL);
756 cfg_parser->key = key;
757 }
758 key_block
759 {
760 struct key_options *key = cfg_parser->key;
761 if(key->name == NULL) {
762 yyerror("tsig key has no name");
763 } else if(key->algorithm == NULL) {
764 yyerror("tsig key %s has no algorithm", key->name);
765 } else if(key->secret == NULL) {
766 yyerror("tsig key %s has no secret blob", key->name);
767 } else if(key_options_find(cfg_parser->opt, key->name)) {
768 yyerror("duplicate tsig key %s", key->name);
769 } else {
770 key_options_insert(cfg_parser->opt, key);
771 cfg_parser->key = NULL;
772 }
773 } ;
774
775 key_block:
776 key_block key_option | ;
777
778 key_option:
779 VAR_NAME STRING
780 {
781 dname_type *dname;
782
783 dname = (dname_type *)dname_parse(cfg_parser->opt->region, $2);
784 cfg_parser->key->name = region_strdup(cfg_parser->opt->region, $2);
785 if(dname == NULL) {
786 yyerror("bad tsig key name %s", $2);
787 } else {
788 region_recycle(cfg_parser->opt->region, dname, dname_total_size(dname));
789 }
790 }
791 | VAR_ALGORITHM STRING
792 {
793 if(tsig_get_algorithm_by_name($2) == NULL) {
794 yyerror("bad tsig key algorithm %s", $2);
795 } else {
796 cfg_parser->key->algorithm = region_strdup(cfg_parser->opt->region, $2);
797 }
798 }
799 | VAR_SECRET STRING
800 {
801 uint8_t data[16384];
802 int size;
803
804 cfg_parser->key->secret = region_strdup(cfg_parser->opt->region, $2);
805 size = __b64_pton($2, data, sizeof(data));
806 if(size == -1) {
807 yyerror("cannot base64 decode tsig secret %s",
808 cfg_parser->key->name?
809 cfg_parser->key->name:"");
810 } else if(size != 0) {
811 memset(data, 0xdd, size); /* wipe secret */
812 }
813 } ;
814
815
816 zone:
817 VAR_ZONE
818 {
819 assert(cfg_parser->pattern == NULL);
820 assert(cfg_parser->zone == NULL);
821 cfg_parser->zone = zone_options_create(cfg_parser->opt->region);
822 cfg_parser->zone->part_of_config = 1;
823 cfg_parser->zone->pattern = cfg_parser->pattern =
824 pattern_options_create(cfg_parser->opt->region);
825 cfg_parser->zone->pattern->implicit = 1;
826 }
827 zone_block
828 {
829 assert(cfg_parser->zone != NULL);
830 if(cfg_parser->zone->name == NULL) {
831 yyerror("zone has no name");
832 } else if(!nsd_options_insert_zone(cfg_parser->opt, cfg_parser->zone)) {
833 yyerror("duplicate zone %s", cfg_parser->zone->name);
834 } else if(!nsd_options_insert_pattern(cfg_parser->opt, cfg_parser->zone->pattern)) {
835 yyerror("duplicate pattern %s", cfg_parser->zone->pattern->pname);
836 }
837 cfg_parser->pattern = NULL;
838 cfg_parser->zone = NULL;
839 } ;
840
841 zone_block:
842 zone_block zone_option | ;
843
844 zone_option:
845 VAR_NAME STRING
846 {
847 const char *marker = PATTERN_IMPLICIT_MARKER;
848 char *pname = region_alloc(cfg_parser->opt->region, strlen($2) + strlen(marker) + 1);
849 memmove(pname, marker, strlen(marker));
850 memmove(pname + strlen(marker), $2, strlen($2) + 1);
851 cfg_parser->zone->pattern->pname = pname;
852 cfg_parser->zone->name = region_strdup(cfg_parser->opt->region, $2);
853 if(pattern_options_find(cfg_parser->opt, pname)) {
854 yyerror("zone %s cannot be created because implicit pattern %s "
855 "already exists", $2, pname);
856 }
857 }
858 | pattern_or_zone_option ;
859
860 pattern:
861 VAR_PATTERN
862 {
863 assert(cfg_parser->pattern == NULL);
864 cfg_parser->pattern = pattern_options_create(cfg_parser->opt->region);
865 }
866 pattern_block
867 {
868 pattern_options_type *pattern = cfg_parser->pattern;
869 if(pattern->pname == NULL) {
870 yyerror("pattern has no name");
871 } else if(!nsd_options_insert_pattern(cfg_parser->opt, pattern)) {
872 yyerror("duplicate pattern %s", pattern->pname);
873 }
874 cfg_parser->pattern = NULL;
875 } ;
876
877 pattern_block:
878 pattern_block pattern_option | ;
879
880 pattern_option:
881 VAR_NAME STRING
882 {
883 if(strchr($2, ' ')) {
884 yyerror("space is not allowed in pattern name: '%s'", $2);
885 }
886 cfg_parser->pattern->pname = region_strdup(cfg_parser->opt->region, $2);
887 }
888 | pattern_or_zone_option ;
889
890 pattern_or_zone_option:
891 VAR_RRL_WHITELIST STRING
892 {
893 #ifdef RATELIMIT
894 cfg_parser->pattern->rrl_whitelist |= rrlstr2type($2);
895 #endif
896 }
897 | VAR_ZONEFILE STRING
898 { cfg_parser->pattern->zonefile = region_strdup(cfg_parser->opt->region, $2); }
899 | VAR_ZONESTATS STRING
900 { cfg_parser->pattern->zonestats = region_strdup(cfg_parser->opt->region, $2); }
901 | VAR_SIZE_LIMIT_XFR number
902 {
903 if($2 > 0) {
904 cfg_parser->pattern->size_limit_xfr = (int)$2;
905 } else {
906 yyerror("expected a number greater than zero");
907 }
908 }
909 | VAR_MULTI_PRIMARY_CHECK boolean
910 { cfg_parser->pattern->multi_primary_check = (int)$2; }
911 | VAR_INCLUDE_PATTERN STRING
912 { config_apply_pattern(cfg_parser->pattern, $2); }
913 | VAR_REQUEST_XFR STRING STRING
914 {
915 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
916 if(cfg_parser->pattern->catalog_role == CATALOG_ROLE_PRODUCER)
917 yyerror("catalog producer zones cannot be secondary zones");
918 if(acl->blocked)
919 yyerror("blocked address used for request-xfr");
920 if(acl->rangetype != acl_range_single)
921 yyerror("address range used for request-xfr");
922 append_acl(&cfg_parser->pattern->request_xfr, acl);
923 }
924 tlsauth_option
925 { }
926 | VAR_REQUEST_XFR VAR_AXFR STRING STRING
927 {
928 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $3, $4);
929 acl->use_axfr_only = 1;
930 if(acl->blocked)
931 yyerror("blocked address used for request-xfr");
932 if(acl->rangetype != acl_range_single)
933 yyerror("address range used for request-xfr");
934 append_acl(&cfg_parser->pattern->request_xfr, acl);
935 }
936 tlsauth_option
937 { }
938 | VAR_REQUEST_XFR VAR_UDP STRING STRING
939 {
940 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $3, $4);
941 acl->allow_udp = 1;
942 if(acl->blocked)
943 yyerror("blocked address used for request-xfr");
944 if(acl->rangetype != acl_range_single)
945 yyerror("address range used for request-xfr");
946 append_acl(&cfg_parser->pattern->request_xfr, acl);
947 }
948 | VAR_ALLOW_NOTIFY STRING STRING
949 {
950 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
951 append_acl(&cfg_parser->pattern->allow_notify, acl);
952 }
953 | VAR_NOTIFY STRING STRING
954 {
955 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
956 if(acl->blocked)
957 yyerror("blocked address used for notify");
958 if(acl->rangetype != acl_range_single)
959 yyerror("address range used for notify");
960 append_acl(&cfg_parser->pattern->notify, acl);
961 }
962 | VAR_PROVIDE_XFR STRING STRING
963 {
964 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
965 append_acl(&cfg_parser->pattern->provide_xfr, acl);
966 }
967 | VAR_ALLOW_QUERY STRING STRING
968 {
969 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
970 append_acl(&cfg_parser->pattern->allow_query, acl);
971 }
972 | VAR_OUTGOING_INTERFACE STRING
973 {
974 acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, "NOKEY");
975 append_acl(&cfg_parser->pattern->outgoing_interface, acl);
976 }
977 | VAR_ALLOW_AXFR_FALLBACK boolean
978 {
979 cfg_parser->pattern->allow_axfr_fallback = $2;
980 cfg_parser->pattern->allow_axfr_fallback_is_default = 0;
981 }
982 | VAR_NOTIFY_RETRY number
983 {
984 cfg_parser->pattern->notify_retry = $2;
985 cfg_parser->pattern->notify_retry_is_default = 0;
986 }
987 | VAR_MAX_REFRESH_TIME number
988 {
989 cfg_parser->pattern->max_refresh_time = $2;
990 cfg_parser->pattern->max_refresh_time_is_default = 0;
991 }
992 | VAR_MIN_REFRESH_TIME number
993 {
994 cfg_parser->pattern->min_refresh_time = $2;
995 cfg_parser->pattern->min_refresh_time_is_default = 0;
996 }
997 | VAR_MAX_RETRY_TIME number
998 {
999 cfg_parser->pattern->max_retry_time = $2;
1000 cfg_parser->pattern->max_retry_time_is_default = 0;
1001 }
1002 | VAR_MIN_RETRY_TIME number
1003 {
1004 cfg_parser->pattern->min_retry_time = $2;
1005 cfg_parser->pattern->min_retry_time_is_default = 0;
1006 }
1007 | VAR_MIN_EXPIRE_TIME STRING
1008 {
1009 long long num;
1010 uint8_t expr;
1011
1012 if (!parse_expire_expr($2, &num, &expr)) {
1013 yyerror("expected an expire time in seconds or \"refresh+retry+1\"");
1014 YYABORT; /* trigger a parser error */
1015 }
1016 cfg_parser->pattern->min_expire_time = num;
1017 cfg_parser->pattern->min_expire_time_expr = expr;
1018 }
1019 | VAR_STORE_IXFR boolean
1020 {
1021 cfg_parser->pattern->store_ixfr = $2;
1022 cfg_parser->pattern->store_ixfr_is_default = 0;
1023 }
1024 | VAR_IXFR_SIZE number
1025 {
1026 cfg_parser->pattern->ixfr_size = $2;
1027 cfg_parser->pattern->ixfr_size_is_default = 0;
1028 }
1029 | VAR_IXFR_NUMBER number
1030 {
1031 cfg_parser->pattern->ixfr_number = $2;
1032 cfg_parser->pattern->ixfr_number_is_default = 0;
1033 }
1034 | VAR_CREATE_IXFR boolean
1035 {
1036 cfg_parser->pattern->create_ixfr = $2;
1037 cfg_parser->pattern->create_ixfr_is_default = 0;
1038 }
1039 | VAR_VERIFY_ZONE boolean
1040 { cfg_parser->pattern->verify_zone = $2; }
1041 | VAR_VERIFIER command
1042 { cfg_parser->pattern->verifier = $2; }
1043 | VAR_VERIFIER_FEED_ZONE boolean
1044 { cfg_parser->pattern->verifier_feed_zone = $2; }
1045 | VAR_VERIFIER_TIMEOUT number
1046 { cfg_parser->pattern->verifier_timeout = $2; }
1047 | VAR_CATALOG catalog_role
1048 {
1049 if($2 == CATALOG_ROLE_PRODUCER && cfg_parser->pattern->request_xfr)
1050 yyerror("catalog producer zones cannot be secondary zones");
1051 cfg_parser->pattern->catalog_role = $2;
1052 cfg_parser->pattern->catalog_role_is_default = 0;
1053 }
1054 | VAR_CATALOG_MEMBER_PATTERN STRING
1055 {
1056 cfg_parser->pattern->catalog_member_pattern = region_strdup(cfg_parser->opt->region, $2);
1057 }
1058 | VAR_CATALOG_PRODUCER_ZONE STRING
1059 {
1060 dname_type *dname;
1061
1062 if(cfg_parser->zone) {
1063 yyerror("catalog-producer-zone option is for patterns only and cannot "
1064 "be used in a zone clause");
1065 } else if(!(dname = (dname_type *)dname_parse(cfg_parser->opt->region, $2))) {
1066 yyerror("bad catalog producer name %s", $2);
1067 } else {
1068 region_recycle(cfg_parser->opt->region, dname, dname_total_size(dname));
1069 cfg_parser->pattern->catalog_producer_zone = region_strdup(cfg_parser->opt->region, $2);
1070 }
1071 };
1072
1073 verify:
1074 VAR_VERIFY verify_block ;
1075
1076 verify_block:
1077 verify_block verify_option | ;
1078
1079 verify_option:
1080 VAR_ENABLE boolean
1081 { cfg_parser->opt->verify_enable = $2; }
1082 | VAR_IP_ADDRESS ip_address
1083 {
1084 struct ip_address_option *ip = cfg_parser->opt->verify_ip_addresses;
1085 if(!ip) {
1086 cfg_parser->opt->verify_ip_addresses = $2;
1087 } else {
1088 while(ip->next) { ip = ip->next; }
1089 ip->next = $2;
1090 }
1091 }
1092 | VAR_PORT number
1093 {
1094 /* port number, stored as a string */
1095 char buf[16];
1096 (void)snprintf(buf, sizeof(buf), "%lld", $2);
1097 cfg_parser->opt->verify_port = region_strdup(cfg_parser->opt->region, buf);
1098 }
1099 | VAR_VERIFY_ZONES boolean
1100 { cfg_parser->opt->verify_zones = $2; }
1101 | VAR_VERIFIER command
1102 { cfg_parser->opt->verifier = $2; }
1103 | VAR_VERIFIER_COUNT number
1104 { cfg_parser->opt->verifier_count = (int)$2; }
1105 | VAR_VERIFIER_TIMEOUT number
1106 { cfg_parser->opt->verifier_timeout = (int)$2; }
1107 | VAR_VERIFIER_FEED_ZONE boolean
1108 { cfg_parser->opt->verifier_feed_zone = $2; } ;
1109
1110 command:
1111 STRING arguments
1112 {
1113 char **argv;
1114 size_t argc = 1;
1115 for(struct component *i = $2; i; i = i->next) {
1116 argc++;
1117 }
1118 argv = region_alloc_zero(
1119 cfg_parser->opt->region, (argc + 1) * sizeof(char *));
1120 argc = 0;
1121 argv[argc++] = $1;
1122 for(struct component *j, *i = $2; i; i = j) {
1123 j = i->next;
1124 argv[argc++] = i->str;
1125 region_recycle(cfg_parser->opt->region, i, sizeof(*i));
1126 }
1127 $$ = argv;
1128 } ;
1129
1130 arguments:
1131 { $$ = NULL; }
1132 | arguments STRING
1133 {
1134 struct component *comp = region_alloc_zero(
1135 cfg_parser->opt->region, sizeof(*comp));
1136 comp->str = region_strdup(cfg_parser->opt->region, $2);
1137 if($1) {
1138 struct component *tail = $1;
1139 while(tail->next) {
1140 tail = tail->next;
1141 }
1142 tail->next = comp;
1143 $$ = $1;
1144 } else {
1145 $$ = comp;
1146 }
1147 } ;
1148
1149 ip_address:
1150 STRING
1151 {
1152 struct ip_address_option *ip = region_alloc_zero(
1153 cfg_parser->opt->region, sizeof(*ip));
1154 ip->address = region_strdup(cfg_parser->opt->region, $1);
1155 ip->fib = -1;
1156 $$ = ip;
1157 } ;
1158
1159 number:
1160 STRING
1161 {
1162 if(!parse_number($1, &$$)) {
1163 yyerror("expected a number");
1164 YYABORT; /* trigger a parser error */
1165 }
1166 } ;
1167
1168 boolean:
1169 STRING
1170 {
1171 if(!parse_boolean($1, &$$)) {
1172 yyerror("expected yes or no");
1173 YYABORT; /* trigger a parser error */
1174 }
1175 } ;
1176
1177 tlsauth_option:
1178 | STRING
1179 { char *tls_auth_name = region_strdup(cfg_parser->opt->region, $1);
1180 add_to_last_acl(&cfg_parser->pattern->request_xfr, tls_auth_name);} ;
1181
1182 catalog_role:
1183 STRING
1184 {
1185 if(!parse_catalog_role($1, &$$)) {
1186 yyerror("expected consumer or producer");
1187 YYABORT; /* trigger a parser error */
1188 }
1189 } ;
1190
1191 %%
1192
1193 static void
1194 append_acl(struct acl_options **list, struct acl_options *acl)
1195 {
1196 assert(list != NULL);
1197
1198 if(*list == NULL) {
1199 *list = acl;
1200 } else {
1201 struct acl_options *tail = *list;
1202 while(tail->next != NULL)
1203 tail = tail->next;
1204 tail->next = acl;
1205 }
1206 }
1207
1208 static void
add_to_last_acl(struct acl_options ** list,char * tls_auth_name)1209 add_to_last_acl(struct acl_options **list, char *tls_auth_name)
1210 {
1211 struct acl_options *tail = *list;
1212 assert(list != NULL);
1213 assert(*list != NULL);
1214 while(tail->next != NULL)
1215 tail = tail->next;
1216 tail->tls_auth_name = tls_auth_name;
1217 }
1218
1219 static int
parse_boolean(const char * str,int * bln)1220 parse_boolean(const char *str, int *bln)
1221 {
1222 if(strcmp(str, "yes") == 0) {
1223 *bln = 1;
1224 } else if(strcmp(str, "no") == 0) {
1225 *bln = 0;
1226 } else {
1227 return 0;
1228 }
1229
1230 return 1;
1231 }
1232
1233 static int
parse_expire_expr(const char * str,long long * num,uint8_t * expr)1234 parse_expire_expr(const char *str, long long *num, uint8_t *expr)
1235 {
1236 if(parse_number(str, num)) {
1237 *expr = EXPIRE_TIME_HAS_VALUE;
1238 return 1;
1239 }
1240 if(strcmp(str, REFRESHPLUSRETRYPLUS1_STR) == 0) {
1241 *num = 0;
1242 *expr = REFRESHPLUSRETRYPLUS1;
1243 return 1;
1244 }
1245 return 0;
1246 }
1247
1248 static int
parse_number(const char * str,long long * num)1249 parse_number(const char *str, long long *num)
1250 {
1251 /* ensure string consists entirely of digits */
1252 size_t pos = 0;
1253 while(str[pos] >= '0' && str[pos] <= '9') {
1254 pos++;
1255 }
1256
1257 if(pos != 0 && str[pos] == '\0') {
1258 *num = strtoll(str, NULL, 10);
1259 return 1;
1260 }
1261
1262 return 0;
1263 }
1264
1265 static int
parse_range(const char * str,long long * low,long long * high)1266 parse_range(const char *str, long long *low, long long *high)
1267 {
1268 const char *ptr = str;
1269 long long num[2];
1270
1271 /* require range to begin with a number */
1272 if(*ptr < '0' || *ptr > '9') {
1273 return 0;
1274 }
1275
1276 num[0] = strtoll(ptr, (char **)&ptr, 10);
1277
1278 /* require number to be followed by nothing at all or a dash */
1279 if(*ptr == '\0') {
1280 *low = num[0];
1281 *high = num[0];
1282 return 1;
1283 } else if(*ptr != '-') {
1284 return 0;
1285 }
1286
1287 ++ptr;
1288 /* require dash to be followed by a number */
1289 if(*ptr < '0' || *ptr > '9') {
1290 return 0;
1291 }
1292
1293 num[1] = strtoll(ptr, (char **)&ptr, 10);
1294
1295 /* require number to be followed by nothing at all */
1296 if(*ptr == '\0') {
1297 if(num[0] < num[1]) {
1298 *low = num[0];
1299 *high = num[1];
1300 } else {
1301 *low = num[1];
1302 *high = num[0];
1303 }
1304 return 1;
1305 }
1306
1307 return 0;
1308 }
1309
1310 static int
parse_catalog_role(const char * str,int * role)1311 parse_catalog_role(const char *str, int *role)
1312 {
1313 if(strcasecmp(str, "consumer") == 0) {
1314 *role = CATALOG_ROLE_CONSUMER;
1315 } else if(strcmp(str, "producer") == 0) {
1316 *role = CATALOG_ROLE_PRODUCER;
1317 } else {
1318 return 0;
1319 }
1320 return 1;
1321 }
1322
1323
1324