1 /* $NetBSD: parse.c,v 1.3 2022/04/03 01:10:59 christos Exp $ */
2
3 /*
4 * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Internet Systems Consortium, Inc.
19 * PO Box 360
20 * Newmarket, NH 03857 USA
21 * <info@isc.org>
22 * https://www.isc.org/
23 *
24 */
25
26 #include <sys/cdefs.h>
27 __RCSID("$NetBSD: parse.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
28
29 #include "keama.h"
30
31 #include <sys/types.h>
32 #include <arpa/inet.h>
33 #include <ctype.h>
34 #include <netdb.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 static void config_min_valid_lifetime(struct element *, struct parse *);
39 static void config_def_valid_lifetime(struct element *, struct parse *);
40 static void config_max_valid_lifetime(struct element *, struct parse *);
41 static void config_file(struct element *, struct parse *);
42 static void config_sname(struct element *, struct parse *);
43 static void config_next_server(struct element *, struct parse *);
44 static void config_vendor_option_space(struct element *, struct parse *);
45 static void config_site_option_space(struct element *, struct parse *);
46 static struct element *default_qualifying_suffix(void);
47 static void config_qualifying_suffix(struct element *, struct parse *);
48 static void config_enable_updates(struct element *, struct parse *);
49 static void config_ddns_update_style(struct element *, struct parse *);
50 static void config_preferred_lifetime(struct element *, struct parse *);
51 static void config_match_client_id(struct element *, struct parse *);
52 static void config_echo_client_id(struct element *, struct parse *);
53
54 /*
55 static uint32_t getULong(const unsigned char *buf);
56 static int32_t getLong(const unsigned char *buf);
57 static uint32_t getUShort(const unsigned char *buf);
58 static int32_t getShort(const unsigned char *buf);
59 static uint32_t getUChar(const unsigned char *obuf);
60 */
61 static void putULong(unsigned char *obuf, uint32_t val);
62 static void putLong(unsigned char *obuf, int32_t val);
63 static void putUShort(unsigned char *obuf, uint32_t val);
64 static void putShort(unsigned char *obuf, int32_t val);
65 /*
66 static void putUChar(unsigned char *obuf, uint32_t val);
67 */
68
69 /*
70 static isc_boolean_t is_compound_expression(struct element *);
71 */
72 static enum expression_context op_context(enum expr_op);
73 static int op_val(enum expr_op);
74 static int op_precedence(enum expr_op, enum expr_op);
75 static enum expression_context expression_context(struct element *);
76 static enum expr_op expression(struct element *);
77
78 /* Skip to the semicolon ending the current statement. If we encounter
79 braces, the matching closing brace terminates the statement.
80 */
81 void
skip_to_semi(struct parse * cfile)82 skip_to_semi(struct parse *cfile)
83 {
84 skip_to_rbrace(cfile, 0);
85 }
86
87 /* Skips everything from the current point upto (and including) the given
88 number of right braces. If we encounter a semicolon but haven't seen a
89 left brace, consume it and return.
90 This lets us skip over:
91
92 statement;
93 statement foo bar { }
94 statement foo bar { statement { } }
95 statement}
96
97 ...et cetera. */
98 void
skip_to_rbrace(struct parse * cfile,int brace_count)99 skip_to_rbrace(struct parse *cfile, int brace_count)
100 {
101 enum dhcp_token token;
102 const char *val;
103
104 do {
105 token = peek_token(&val, NULL, cfile);
106 if (token == RBRACE) {
107 if (brace_count > 0) {
108 --brace_count;
109 }
110
111 if (brace_count == 0) {
112 /* Eat the brace and return. */
113 skip_token(&val, NULL, cfile);
114 return;
115 }
116 } else if (token == LBRACE) {
117 brace_count++;
118 } else if (token == SEMI && (brace_count == 0)) {
119 /* Eat the semicolon and return. */
120 skip_token(&val, NULL, cfile);
121 return;
122 } else if (token == EOL) {
123 /* EOL only happens when parsing /etc/resolv.conf,
124 and we treat it like a semicolon because the
125 resolv.conf file is line-oriented. */
126 skip_token(&val, NULL, cfile);
127 return;
128 }
129
130 /* Eat the current token */
131 token = next_token(&val, NULL, cfile);
132 } while (token != END_OF_FILE);
133 }
134
135 void
parse_semi(struct parse * cfile)136 parse_semi(struct parse *cfile)
137 {
138 enum dhcp_token token;
139 const char *val;
140
141 token = next_token(&val, NULL, cfile);
142 if (token != SEMI)
143 parse_error(cfile, "semicolon expected.");
144 }
145
146 /* string-parameter :== STRING SEMI */
147
148 void
parse_string(struct parse * cfile,char ** sptr,unsigned * lptr)149 parse_string(struct parse *cfile, char **sptr, unsigned *lptr)
150 {
151 const char *val;
152 enum dhcp_token token;
153 char *s;
154 unsigned len;
155
156 token = next_token(&val, &len, cfile);
157 if (token != STRING)
158 parse_error(cfile, "expecting a string");
159 s = (char *)malloc(len + 1);
160 parse_error(cfile, "no memory for string %s.", val);
161 memcpy(s, val, len + 1);
162
163 parse_semi(cfile);
164 if (sptr)
165 *sptr = s;
166 else
167 free(s);
168 if (lptr)
169 *lptr = len;
170 }
171
172 /*
173 * hostname :== IDENTIFIER
174 * | IDENTIFIER DOT
175 * | hostname DOT IDENTIFIER
176 */
177
178 struct string *
parse_host_name(struct parse * cfile)179 parse_host_name(struct parse *cfile)
180 {
181 const char *val;
182 enum dhcp_token token;
183 struct string *s = NULL;
184
185 /* Read a dotted hostname... */
186 do {
187 /* Read a token, which should be an identifier. */
188 token = peek_token(&val, NULL, cfile);
189 if (!is_identifier(token) && token != NUMBER)
190 break;
191 skip_token(&val, NULL, cfile);
192
193 /* Store this identifier... */
194 if (s == NULL)
195 s = makeString(-1, val);
196 else
197 appendString(s, val);
198 /* Look for a dot; if it's there, keep going, otherwise
199 we're done. */
200 token = peek_token(&val, NULL, cfile);
201 if (token == DOT) {
202 token = next_token(&val, NULL, cfile);
203 appendString(s, val);
204 }
205 } while (token == DOT);
206
207 return s;
208 }
209
210 /* ip-addr-or-hostname :== ip-address | hostname
211 ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
212
213 Parse an ip address or a hostname.
214
215 Note that RFC1123 permits hostnames to consist of all digits,
216 making it difficult to quickly disambiguate them from ip addresses.
217 */
218
219 struct string *
parse_ip_addr_or_hostname(struct parse * cfile,isc_boolean_t check_multi)220 parse_ip_addr_or_hostname(struct parse *cfile, isc_boolean_t check_multi)
221 {
222 const char *val;
223 enum dhcp_token token;
224 unsigned char addr[4];
225 unsigned len = sizeof(addr);
226 isc_boolean_t ipaddr = ISC_FALSE;
227 struct string *bin = NULL;
228
229 token = peek_token(&val, NULL, cfile);
230 if (token == NUMBER) {
231 /*
232 * a hostname may be numeric, but domain names must
233 * start with a letter, so we can disambiguate by
234 * looking ahead a few tokens. we save the parse
235 * context first, and restore it after we know what
236 * we're dealing with.
237 */
238 save_parse_state(cfile);
239 skip_token(NULL, NULL, cfile);
240 if (next_token(NULL, NULL, cfile) == DOT &&
241 next_token(NULL, NULL, cfile) == NUMBER)
242 ipaddr = ISC_TRUE;
243 restore_parse_state(cfile);
244
245 if (ipaddr)
246 bin = parse_numeric_aggregate(cfile, addr, &len,
247 DOT, 10, 8);
248 }
249
250 if ((bin == NULL) && (is_identifier(token) || token == NUMBER)) {
251 struct string *name;
252 struct hostent *h;
253
254 name = parse_host_name(cfile);
255 if (name == NULL)
256 return NULL;
257
258 if (resolve == fatal)
259 parse_error(cfile, "expected IPv4 address. got "
260 "hostname %s", name->content);
261 else if (resolve == pass)
262 return name;
263
264 /* from do_host_lookup */
265 h = gethostbyname(name->content);
266 if ((h == NULL) || (h->h_addr_list[0] == NULL))
267 parse_error(cfile, "%s: host unknown.", name->content);
268 if (check_multi && h->h_addr_list[1]) {
269 struct comment *comment;
270 char msg[128];
271
272 snprintf(msg, sizeof(msg),
273 "/// %s resolves into multiple addresses",
274 name->content);
275 comment = createComment(msg);
276 TAILQ_INSERT_TAIL(&cfile->comments, comment);
277 }
278 bin = makeString(4, h->h_addr_list[0]);
279 }
280
281 if (bin == NULL) {
282 if (token != RBRACE && token != LBRACE)
283 token = next_token(&val, NULL, cfile);
284 parse_error(cfile, "%s (%d): expecting IP address or hostname",
285 val, token);
286 }
287
288 return makeStringExt(bin->length, bin->content, 'I');
289 }
290
291 /*
292 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
293 */
294
295 struct string *
parse_ip_addr(struct parse * cfile)296 parse_ip_addr(struct parse *cfile)
297 {
298 unsigned char addr[4];
299 unsigned len = sizeof(addr);
300
301 return parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
302 }
303
304 /*
305 * Return true if every character in the string is hexadecimal.
306 */
307 static isc_boolean_t
is_hex_string(const char * s)308 is_hex_string(const char *s)
309 {
310 while (*s != '\0') {
311 if (!isxdigit((int)*s)) {
312 return ISC_FALSE;
313 }
314 s++;
315 }
316 return ISC_TRUE;
317 }
318
319 /*
320 * ip-address6 :== (complicated set of rules)
321 *
322 * See section 2.2 of RFC 1884 for details.
323 *
324 * We are lazy for this. We pull numbers, names, colons, and dots
325 * together and then throw the resulting string at the inet_pton()
326 * function.
327 */
328
329 struct string *
parse_ip6_addr(struct parse * cfile)330 parse_ip6_addr(struct parse *cfile)
331 {
332 enum dhcp_token token;
333 const char *val;
334 char addr[16];
335 int val_len;
336 char v6[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
337 int v6_len;
338
339 /*
340 * First token is non-raw. This way we eat any whitespace before
341 * our IPv6 address begins, like one would expect.
342 */
343 token = peek_token(&val, NULL, cfile);
344
345 /*
346 * Gather symbols.
347 */
348 v6_len = 0;
349 for (;;) {
350 if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
351 is_hex_string(val)) ||
352 (token == NUMBER) ||
353 (token == TOKEN_ADD) ||
354 (token == DOT) ||
355 (token == COLON)) {
356
357 next_raw_token(&val, NULL, cfile);
358 val_len = strlen(val);
359 if ((v6_len + val_len) >= sizeof(v6))
360 parse_error(cfile, "Invalid IPv6 address.");
361 memcpy(v6+v6_len, val, val_len);
362 v6_len += val_len;
363
364 } else {
365 break;
366 }
367 token = peek_raw_token(&val, NULL, cfile);
368 }
369 v6[v6_len] = '\0';
370
371 /*
372 * Use inet_pton() for actual work.
373 */
374 if (inet_pton(AF_INET6, v6, addr) <= 0)
375 parse_error(cfile, "Invalid IPv6 address.");
376 return makeString(16, addr);
377 }
378
379 /*
380 * Same as parse_ip6_addr() above, but returns the value as a text
381 * rather than in an address binary structure.
382 */
383 struct string *
parse_ip6_addr_txt(struct parse * cfile)384 parse_ip6_addr_txt(struct parse *cfile)
385 {
386 const struct string *bin;
387
388 bin = parse_ip6_addr(cfile);
389 return makeStringExt(bin->length, bin->content, '6');
390 }
391
392 /*
393 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
394 * hardware-type :== ETHERNET | TOKEN_RING | TOKEN_FDDI | INFINIBAND
395 * Note that INFINIBAND may not be useful for some items, such as classification
396 * as the hardware address won't always be available.
397 */
398
399 struct element *
parse_hardware_param(struct parse * cfile)400 parse_hardware_param(struct parse *cfile)
401 {
402 const char *val;
403 enum dhcp_token token;
404 isc_boolean_t ether = ISC_FALSE;
405 unsigned hlen;
406 struct string *t, *r;
407 struct element *hw;
408
409 token = next_token(&val, NULL, cfile);
410 if (token == ETHERNET)
411 ether = ISC_TRUE;
412 else {
413 r = makeString(-1, val);
414 appendString(r, " ");
415 }
416
417 /* Parse the hardware address information. Technically,
418 it would make a lot of sense to restrict the length of the
419 data we'll accept here to the length of a particular hardware
420 address type. Unfortunately, there are some broken clients
421 out there that put bogus data in the chaddr buffer, and we accept
422 that data in the lease file rather than simply failing on such
423 clients. Yuck. */
424 hlen = 0;
425 token = peek_token(&val, NULL, cfile);
426 if (token == SEMI)
427 parse_error(cfile, "empty hardware address");
428 t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
429 if (t == NULL)
430 parse_error(cfile, "can't get hardware address");
431 if (hlen > HARDWARE_ADDR_LEN)
432 parse_error(cfile, "hardware address too long");
433 token = next_token(&val, NULL, cfile);
434 if (token != SEMI)
435 parse_error(cfile, "expecting semicolon.");
436 if (ether)
437 r = makeStringExt(hlen, t->content, 'H');
438 else
439 concatString(r, makeStringExt(hlen,t->content, 'H'));
440 hw = createString(r);
441 TAILQ_CONCAT(&hw->comments, &cfile->comments);
442 if (!ether || (hlen != 6)) {
443 hw->skip = ISC_TRUE;
444 cfile->issue_counter++;
445 }
446 return hw;
447 }
448
449 /* No BNF for numeric aggregates - that's defined by the caller. What
450 this function does is to parse a sequence of numbers separated by
451 the token specified in separator. If max is zero, any number of
452 numbers will be parsed; otherwise, exactly max numbers are
453 expected. Base and size tell us how to internalize the numbers
454 once they've been tokenized.
455
456 buf - A pointer to space to return the parsed value, if it is null
457 then the function will allocate space for the return.
458
459 max - The maximum number of items to store. If zero there is no
460 maximum. When buf is null and the function needs to allocate space
461 it will do an allocation of max size at the beginning if max is non
462 zero. If max is zero then the allocation will be done later, after
463 the function has determined the size necessary for the incoming
464 string.
465
466 returns NULL on errors or a pointer to the string structure on success.
467 */
468
469 struct string *
parse_numeric_aggregate(struct parse * cfile,unsigned char * buf,unsigned * max,int separator,int base,unsigned size)470 parse_numeric_aggregate(struct parse *cfile, unsigned char *buf,
471 unsigned *max, int separator,
472 int base, unsigned size)
473 {
474 const char *val;
475 enum dhcp_token token;
476 unsigned char *bufp = buf, *s;
477 unsigned count = 0;
478 struct string *r = NULL, *t = NULL;
479
480 if (!bufp && *max) {
481 bufp = (unsigned char *)malloc(*max * size / 8);
482 if (!bufp)
483 parse_error(cfile, "no space for numeric aggregate");
484 }
485 s = bufp;
486 if (!s) {
487 r = allocString();
488 t = makeString(size / 8, "bigger than needed");
489 }
490
491 do {
492 if (count) {
493 token = peek_token(&val, NULL, cfile);
494 if (token != separator) {
495 if (!*max)
496 break;
497 if (token != RBRACE && token != LBRACE)
498 token = next_token(&val, NULL, cfile);
499 parse_error(cfile, "too few numbers.");
500 }
501 skip_token(&val, NULL, cfile);
502 }
503 token = next_token(&val, NULL, cfile);
504
505 if (token == END_OF_FILE)
506 parse_error(cfile, "unexpected end of file");
507
508 /* Allow NUMBER_OR_NAME if base is 16. */
509 if (token != NUMBER &&
510 (base != 16 || token != NUMBER_OR_NAME))
511 parse_error(cfile, "expecting numeric value.");
512 /* If we can, convert the number now; otherwise, build
513 a linked list of all the numbers. */
514 if (s) {
515 convert_num(cfile, s, val, base, size);
516 s += size / 8;
517 } else {
518 convert_num(cfile, (unsigned char *)t->content,
519 val, base, size);
520 concatString(r, t);
521 }
522 } while (++count != *max);
523
524 *max = count;
525 if (bufp)
526 r = makeString(count * size / 8, (char *)bufp);
527
528 return r;
529 }
530
531 void
convert_num(struct parse * cfile,unsigned char * buf,const char * str,int base,unsigned size)532 convert_num(struct parse *cfile, unsigned char *buf, const char *str,
533 int base, unsigned size)
534 {
535 const unsigned char *ptr = (const unsigned char *)str;
536 int negative = 0;
537 uint32_t val = 0;
538 int tval;
539 int max;
540
541 if (*ptr == '-') {
542 negative = 1;
543 ++ptr;
544 }
545
546 /* If base wasn't specified, figure it out from the data. */
547 if (!base) {
548 if (ptr[0] == '0') {
549 if (ptr[1] == 'x') {
550 base = 16;
551 ptr += 2;
552 } else if (isascii(ptr[1]) && isdigit(ptr[1])) {
553 base = 8;
554 ptr += 1;
555 } else {
556 base = 10;
557 }
558 } else {
559 base = 10;
560 }
561 }
562
563 do {
564 tval = *ptr++;
565 /* XXX assumes ASCII... */
566 if (tval >= 'a')
567 tval = tval - 'a' + 10;
568 else if (tval >= 'A')
569 tval = tval - 'A' + 10;
570 else if (tval >= '0')
571 tval -= '0';
572 else
573 parse_error(cfile, "Bogus number: %s.", str);
574 if (tval >= base)
575 parse_error(cfile,
576 "Bogus number %s: digit %d not in base %d",
577 str, tval, base);
578 val = val * base + tval;
579 } while (*ptr);
580
581 if (negative)
582 max = (1 << (size - 1));
583 else
584 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
585 if (val > max) {
586 switch (base) {
587 case 8:
588 parse_error(cfile,
589 "%s%lo exceeds max (%d) for precision.",
590 negative ? "-" : "",
591 (unsigned long)val, max);
592 break;
593 case 16:
594 parse_error(cfile,
595 "%s%lx exceeds max (%d) for precision.",
596 negative ? "-" : "",
597 (unsigned long)val, max);
598 break;
599 default:
600 parse_error(cfile,
601 "%s%lu exceeds max (%d) for precision.",
602 negative ? "-" : "",
603 (unsigned long)val, max);
604 break;
605 }
606 }
607
608 if (negative) {
609 switch (size) {
610 case 8:
611 *buf = -(unsigned long)val;
612 break;
613 case 16:
614 putShort(buf, -(long)val);
615 break;
616 case 32:
617 putLong(buf, -(long)val);
618 break;
619 default:
620 parse_error(cfile,
621 "Unexpected integer size: %d\n", size);
622 break;
623 }
624 } else {
625 switch (size) {
626 case 8:
627 *buf = (uint8_t)val;
628 break;
629 case 16:
630 putUShort (buf, (uint16_t)val);
631 break;
632 case 32:
633 putULong (buf, val);
634 break;
635 default:
636 parse_error(cfile,
637 "Unexpected integer size: %d\n", size);
638 }
639 }
640 }
641
642 /*
643 * option-name :== IDENTIFIER |
644 IDENTIFIER . IDENTIFIER
645 */
646
647 struct option *
parse_option_name(struct parse * cfile,isc_boolean_t allocate,isc_boolean_t * known)648 parse_option_name(struct parse *cfile,
649 isc_boolean_t allocate,
650 isc_boolean_t *known)
651 {
652 const char *val;
653 enum dhcp_token token;
654 const char *uname;
655 struct space *space;
656 struct option *option = NULL;
657 unsigned code;
658
659 token = next_token(&val, NULL, cfile);
660 if (!is_identifier(token))
661 parse_error(cfile,
662 "expecting identifier after option keyword.");
663
664 uname = strdup(val);
665 if (!uname)
666 parse_error(cfile, "no memory for uname information.");
667 token = peek_token(&val, NULL, cfile);
668 if (token == DOT) {
669 /* Go ahead and take the DOT token... */
670 skip_token(&val, NULL, cfile);
671
672 /* The next token should be an identifier... */
673 token = next_token(&val, NULL, cfile);
674 if (!is_identifier(token))
675 parse_error(cfile, "expecting identifier after '.'");
676
677 /* Look up the option name hash table for the specified
678 uname. */
679 space = space_lookup(uname);
680 if (space == NULL)
681 parse_error(cfile, "no option space named %s.", uname);
682 } else {
683 /* Use the default hash table, which contains all the
684 standard dhcp option names. */
685 val = uname;
686 space = space_lookup("dhcp");
687 }
688
689 option = option_lookup_name(space->old, val);
690
691 if (option) {
692 if (known && (option->status != isc_dhcp_unknown))
693 *known = ISC_TRUE;
694 } else if (space == space_lookup("server"))
695 parse_error(cfile, "unknown server option %s.", val);
696
697 /* If the option name is of the form unknown-[decimal], use
698 * the trailing decimal value to find the option definition.
699 * If there is no definition, construct one. This is to
700 * support legacy use of unknown options in config files or
701 * lease databases.
702 */
703 else if (strncasecmp(val, "unknown-", 8) == 0) {
704 code = atoi(val + 8);
705
706 /* Option code 0 is always illegal for us, thanks
707 * to the option decoder.
708 */
709 if (code == 0)
710 parse_error(cfile, "Option code 0 is illegal "
711 "in the %s space.", space->old);
712 if ((local_family == AF_INET) && (code == 255))
713 parse_error(cfile, "Option code 255 is illegal "
714 "in the %s space.", space->old);
715
716 /* It's odd to think of unknown option codes as
717 * being known, but this means we know what the
718 * parsed name is talking about.
719 */
720 if (known)
721 *known = ISC_TRUE;
722 option = option_lookup_code(space->old, code);
723
724 /* If we did not find an option of that code,
725 * manufacture an unknown-xxx option definition.
726 */
727 if (option == NULL) {
728 option = (struct option *)malloc(sizeof(*option));
729 /* DHCP code does not check allocation failure? */
730 memset(option, 0, sizeof(*option));
731 option->name = strdup(val);
732 option->space = space;
733 option->code = code;
734 /* Mark format as undefined */
735 option->format = "u";
736 push_option(option);
737 } else {
738 struct comment *comment;
739 char msg[256];
740
741 snprintf(msg, sizeof(msg),
742 "/// option %s.%s redefinition",
743 space->name, val);
744 comment = createComment(msg);
745 TAILQ_INSERT_TAIL(&cfile->comments, comment);
746 }
747 /* If we've been told to allocate, that means that this
748 * (might) be an option code definition, so we'll create
749 * an option structure and return it for the parent to
750 * decide.
751 */
752 } else if (allocate) {
753 option = (struct option *)malloc(sizeof(*option));
754 /* DHCP code does not check allocation failure? */
755 memset(option, 0, sizeof(*option));
756 option->name = strdup(val);
757 option->space = space;
758 /* Mark format as undefined */
759 option->format = "u";
760 push_option(option);
761 } else
762 parse_error(cfile, "no option named %s in space %s",
763 val, space->old);
764
765 return option;
766 }
767
768 /* IDENTIFIER[WIDTHS] SEMI
769 * WIDTHS ~= LENGTH WIDTH NUMBER
770 * CODE WIDTH NUMBER
771 */
772
773 void
parse_option_space_decl(struct parse * cfile)774 parse_option_space_decl(struct parse *cfile)
775 {
776 int token;
777 const char *val;
778 struct element *nu;
779 struct element *p;
780 struct space *universe;
781 int tsize = 1, lsize = 1;
782
783 skip_token(&val, NULL, cfile); /* Discard the SPACE token,
784 which was checked by the
785 caller. */
786 token = next_token(&val, NULL, cfile);
787 if (!is_identifier(token))
788 parse_error(cfile, "expecting identifier.");
789 nu = createMap();
790 nu->skip = ISC_TRUE;
791
792 /* Expect it will be usable in Kea */
793 universe = (struct space *)malloc(sizeof(*universe));
794 if (universe == NULL)
795 parse_error(cfile, "No memory for new option space.");
796 memset(universe, 0, sizeof(*universe));
797 universe->old = strdup(val);
798 universe->name = universe->old;
799 push_space(universe);
800
801 do {
802 token = next_token(&val, NULL, cfile);
803 switch(token) {
804 case SEMI:
805 break;
806
807 case CODE:
808 if (mapSize(nu) == 0) {
809 cfile->issue_counter++;
810 mapSet(nu,
811 createString(
812 makeString(-1, universe->old)),
813 "name");
814 }
815 token = next_token(&val, NULL, cfile);
816 if (token != WIDTH)
817 parse_error(cfile, "expecting width token.");
818
819 token = next_token(&val, NULL, cfile);
820 if (token != NUMBER)
821 parse_error(cfile,
822 "expecting number 1, 2, 4.");
823
824 tsize = atoi(val);
825 p = NULL;
826 if ((local_family == AF_INET) && (tsize != 1)) {
827 struct comment *comment;
828
829 comment = createComment("/// Only code width "
830 "1 is supported");
831 p = createInt(tsize);
832 TAILQ_INSERT_TAIL(&p->comments, comment);
833 } else if ((local_family == AF_INET6) &&
834 (tsize != 2)) {
835 struct comment *comment;
836
837 comment = createComment("/// Only code width "
838 "2 is supported");
839 p = createInt(tsize);
840 TAILQ_INSERT_TAIL(&p->comments, comment);
841 }
842 if (p != NULL)
843 mapSet(nu, p, "code-width");
844 break;
845
846 case LENGTH:
847 if (mapSize(nu) == 0) {
848 cfile->issue_counter++;
849 mapSet(nu,
850 createString(
851 makeString(-1, universe->old)),
852 "name");
853 }
854 token = next_token(&val, NULL, cfile);
855 if (token != WIDTH)
856 parse_error(cfile, "expecting width token.");
857
858 token = next_token(&val, NULL, cfile);
859 if (token != NUMBER)
860 parse_error(cfile, "expecting number 1 or 2.");
861
862 lsize = atoi(val);
863 p = NULL;
864 if ((local_family == AF_INET) && (lsize != 1)) {
865 struct comment *comment;
866
867 comment = createComment("/// Only length "
868 "width 1 is "
869 "supported");
870 p = createInt(lsize);
871 TAILQ_INSERT_TAIL(&p->comments, comment);
872 } else if ((local_family == AF_INET6) &&
873 (lsize != 2)) {
874 struct comment *comment;
875
876 comment = createComment("/// Only length "
877 "width 2 is "
878 "supported");
879 p = createInt(lsize);
880 TAILQ_INSERT_TAIL(&p->comments, comment);
881 }
882 if (p != NULL)
883 mapSet(nu, p, "length-width");
884 break;
885
886 case HASH:
887 token = next_token(&val, NULL, cfile);
888 if (token != SIZE)
889 parse_error(cfile, "expecting size token.");
890
891 token = next_token(&val, NULL, cfile);
892 if (token != NUMBER)
893 parse_error(cfile,
894 "expecting a 10base number");
895 break;
896
897 default:
898 parse_error(cfile, "Unexpected token.");
899 }
900 } while (token != SEMI);
901
902 if (mapSize(nu) > 1)
903 mapSet(cfile->stack[1], nu, "option-space");
904 }
905
906 /* This is faked up to look good right now. Ideally, this should do a
907 recursive parse and allow arbitrary data structure definitions, but for
908 now it just allows you to specify a single type, an array of single types,
909 a sequence of types, or an array of sequences of types.
910
911 ocd :== NUMBER EQUALS ocsd SEMI
912
913 ocsd :== ocsd_type |
914 ocsd_type_sequence |
915 ARRAY OF ocsd_simple_type_sequence
916
917 ocsd_type_sequence :== LBRACE ocsd_types RBRACE
918
919 ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE
920
921 ocsd_types :== ocsd_type |
922 ocsd_types ocsd_type
923
924 ocsd_type :== ocsd_simple_type |
925 ARRAY OF ocsd_simple_type
926
927 ocsd_simple_types :== ocsd_simple_type |
928 ocsd_simple_types ocsd_simple_type
929
930 ocsd_simple_type :== BOOLEAN |
931 INTEGER NUMBER |
932 SIGNED INTEGER NUMBER |
933 UNSIGNED INTEGER NUMBER |
934 IP-ADDRESS |
935 TEXT |
936 STRING |
937 ENCAPSULATE identifier */
938
939 void
parse_option_code_definition(struct parse * cfile,struct option * option)940 parse_option_code_definition(struct parse *cfile, struct option *option)
941 {
942 const char *val;
943 enum dhcp_token token;
944 struct element *def;
945 unsigned code;
946 unsigned arrayp = 0;
947 isc_boolean_t is_array = ISC_FALSE;
948 int recordp = 0;
949 isc_boolean_t no_more_in_record = ISC_FALSE;
950 char *type;
951 isc_boolean_t is_signed;
952 isc_boolean_t has_encapsulation = ISC_FALSE;
953 isc_boolean_t not_supported = ISC_FALSE;
954 struct string *encapsulated;
955 struct string *datatype;
956 struct string *saved;
957 struct string *format;
958 struct element *optdef;
959
960 if (option->space->status == special) {
961 parse_vendor_code_definition(cfile, option);
962 return;
963 }
964
965 /* Put the option in the definition */
966 def = createMap();
967 mapSet(def,
968 createString(makeString(-1, option->space->name)),
969 "space");
970 mapSet(def, createString(makeString(-1, option->name)), "name");
971 TAILQ_CONCAT(&def->comments, &cfile->comments);
972
973 /* Parse the option code. */
974 token = next_token(&val, NULL, cfile);
975 if (token != NUMBER)
976 parse_error(cfile, "expecting option code number.");
977 TAILQ_CONCAT(&def->comments, &cfile->comments);
978 code = atoi(val);
979 mapSet(def, createInt(code), "code");
980
981 /* We have the code so we can get the real option now */
982 if (option->code == 0) {
983 struct option *from_code = NULL;
984
985 option->code = code;
986 from_code = option_lookup_code(option->space->old, code);
987 if (from_code != NULL) {
988 option->status = from_code->status;
989 option->format = from_code->format;
990 }
991 }
992
993 /* Redefinitions are not allowed */
994 if ((option->status != dynamic) ||
995 (strcmp(option->format, "u") != 0)) {
996 struct comment *comment;
997
998 comment = createComment("/// Kea does not allow redefinition "
999 "of options");
1000 TAILQ_INSERT_TAIL(&def->comments, comment);
1001 def->skip = ISC_TRUE;
1002 cfile->issue_counter++;
1003 /* Avoid option-data per name */
1004 option->status = kea_unknown;
1005 }
1006
1007 token = next_token(&val, NULL, cfile);
1008 if (token != EQUAL)
1009 parse_error(cfile, "expecting \"=\"");
1010 saved = allocString();
1011
1012 /* See if this is an array. */
1013 token = next_token(&val, NULL, cfile);
1014 if (token == ARRAY) {
1015 token = next_token(&val, NULL, cfile);
1016 if (token != OF)
1017 parse_error(cfile, "expecting \"of\".");
1018 arrayp = 1;
1019 token = next_token(&val, NULL, cfile);
1020 appendString(saved, "array of");
1021 }
1022
1023 if (token == LBRACE) {
1024 recordp = 1;
1025 token = next_token(&val, NULL, cfile);
1026 if (arrayp)
1027 appendString(saved, " ");
1028 appendString(saved, "{");
1029 }
1030
1031 /* At this point we're expecting a data type. */
1032 datatype = allocString();
1033 /* We record the format essentially for the binary one */
1034 format = allocString();
1035 next_type:
1036 if (saved->length > 0)
1037 appendString(saved, " ");
1038 type = NULL;
1039 if (has_encapsulation)
1040 parse_error(cfile,
1041 "encapsulate must always be the last item.");
1042
1043 switch (token) {
1044 case ARRAY:
1045 if (arrayp)
1046 parse_error(cfile, "no nested arrays.");
1047 if (recordp) {
1048 struct comment *comment;
1049
1050 comment = createComment("/// unsupported array "
1051 "inside a record");
1052 TAILQ_INSERT_TAIL(&def->comments, comment);
1053 not_supported = ISC_TRUE;
1054 cfile->issue_counter++;
1055 }
1056 token = next_token(&val, NULL, cfile);
1057 if (token != OF)
1058 parse_error(cfile, "expecting \"of\".");
1059 arrayp = recordp + 1;
1060 token = next_token(&val, NULL, cfile);
1061 if ((recordp) && (token == LBRACE))
1062 parse_error(cfile,
1063 "only uniform array inside record.");
1064 appendString(saved, "array of");
1065 if (token == LBRACE) {
1066 struct comment *comment;
1067
1068 comment = createComment("/// unsupported record "
1069 "inside an array");
1070 TAILQ_INSERT_TAIL(&def->comments, comment);
1071 not_supported = ISC_TRUE;
1072 cfile->issue_counter++;
1073 appendString(saved, " {");
1074 }
1075 goto next_type;
1076 case BOOLEAN:
1077 type = "boolean";
1078 appendString(format, "f");
1079 break;
1080 case INTEGER:
1081 is_signed = ISC_TRUE;
1082 parse_integer:
1083 token = next_token(&val, NULL, cfile);
1084 if (token != NUMBER)
1085 parse_error(cfile, "expecting number.");
1086 switch (atoi(val)) {
1087 case 8:
1088 if (is_signed) {
1089 type = "int8";
1090 appendString(format, "b");
1091 } else {
1092 type = "uint8";
1093 appendString(format, "B");
1094 }
1095 break;
1096 case 16:
1097 if (is_signed) {
1098 type = "int16";
1099 appendString(format, "s");
1100 } else {
1101 type = "uint16";
1102 appendString(format, "S");
1103 }
1104 break;
1105 case 32:
1106 if (is_signed) {
1107 type = "int32";
1108 appendString(format, "l");
1109 } else {
1110 type = "uint32";
1111 appendString(format, "L");
1112 }
1113 break;
1114 default:
1115 parse_error(cfile,
1116 "%s bit precision is not supported.", val);
1117 }
1118 break;
1119 case SIGNED:
1120 is_signed = ISC_TRUE;
1121 parse_signed:
1122 token = next_token(&val, NULL, cfile);
1123 if (token != INTEGER)
1124 parse_error(cfile, "expecting \"integer\" keyword.");
1125 goto parse_integer;
1126 case UNSIGNED:
1127 is_signed = ISC_FALSE;
1128 goto parse_signed;
1129
1130 case IP_ADDRESS:
1131 type = "ipv4-address";
1132 appendString(format, "I");
1133 break;
1134 case IP6_ADDRESS:
1135 type = "ipv6-address";
1136 appendString(format, "6");
1137 break;
1138 case DOMAIN_NAME:
1139 type = "fqdn";
1140 appendString(format, "d");
1141 goto no_arrays;
1142 case DOMAIN_LIST:
1143 /* Consume optional compression indicator. */
1144 token = peek_token(&val, NULL, cfile);
1145 appendString(format, "D");
1146 type = "fqdn";
1147 is_array = ISC_TRUE;
1148 if (token == COMPRESSED) {
1149 if (local_family == AF_INET6)
1150 parse_error(cfile, "domain list in DHCPv6 "
1151 "MUST NOT be compressed");
1152 skip_token(&val, NULL, cfile);
1153 appendString(format, "c");
1154 appendString(saved, "compressed ");
1155 }
1156 appendString(saved, "list of ");
1157 goto no_arrays;
1158 case TEXT:
1159 type = "string";
1160 appendString(format, "t");
1161 no_arrays:
1162 if (arrayp)
1163 parse_error(cfile, "arrays of text strings not %s",
1164 "yet supported.");
1165 no_more_in_record = ISC_TRUE;
1166 break;
1167 case STRING_TOKEN:
1168 /* can be binary too */
1169 type = "string";
1170 appendString(format, "x");
1171 goto no_arrays;
1172
1173 case ENCAPSULATE:
1174 token = next_token(&val, NULL, cfile);
1175 if (!is_identifier(token))
1176 parse_error(cfile,
1177 "expecting option space identifier");
1178 encapsulated = makeString(-1, val);
1179 has_encapsulation = ISC_TRUE;
1180 appendString(format, "E");
1181 appendString(format, val);
1182 appendString(format, ".");
1183 appendString(saved, "encapsulate ");
1184 appendString(saved, val);
1185 if (datatype->length == 0)
1186 type = "empty";
1187 break;
1188
1189 case ZEROLEN:
1190 type = "empty";
1191 appendString(format, "Z");
1192 if (arrayp)
1193 parse_error(cfile, "array incompatible with zerolen.");
1194 no_more_in_record = ISC_TRUE;
1195 break;
1196
1197 default:
1198 parse_error(cfile, "unknown data type %s", val);
1199 }
1200 appendString(saved, type);
1201 appendString(datatype, type);
1202
1203 if (recordp) {
1204 token = next_token(&val, NULL, cfile);
1205 if (arrayp > recordp) {
1206 is_array = ISC_TRUE;
1207 arrayp = 0;
1208 appendString(format, "a");
1209 }
1210 if (token == COMMA) {
1211 if (no_more_in_record) {
1212 char last;
1213
1214 last = format->content[format->length - 1];
1215 parse_error(cfile,
1216 "%s must be at end of record.",
1217 last == 't' ? "text" : "string");
1218 }
1219 token = next_token(&val, NULL, cfile);
1220 appendString(saved, ",");
1221 appendString(datatype, ", ");
1222 goto next_type;
1223 }
1224 if (token != RBRACE)
1225 parse_error(cfile, "expecting right brace.");
1226 appendString(saved, "}");
1227 }
1228 parse_semi(cfile);
1229 if (has_encapsulation && arrayp)
1230 parse_error(cfile,
1231 "Arrays of encapsulations don't make sense.");
1232 if (arrayp)
1233 appendString(format, (arrayp > recordp) ? "a" : "A");
1234 if (is_array || arrayp) {
1235 struct element *array_def;
1236
1237 array_def = createBool(ISC_TRUE);
1238 if (not_supported)
1239 array_def->skip = ISC_TRUE;
1240 mapSet(def, array_def, "array");
1241 }
1242
1243 if (not_supported) {
1244 struct element *type_def;
1245 struct element *saved_def;
1246 struct comment *comment;
1247
1248 saved_def = createString(saved);
1249 saved_def->skip = ISC_TRUE;
1250 mapSet(def, saved_def, "definition");
1251 type_def = createString(makeString(-1, "binary"));
1252 comment = createComment("/// Option definition is not "
1253 "compatible with Kea");
1254 TAILQ_INSERT_TAIL(&type_def->comments, comment);
1255 comment = createComment("/// Fallback to full binary");
1256 TAILQ_INSERT_TAIL(&type_def->comments, comment);
1257 mapSet(def, type_def, "type");
1258 } else if (recordp) {
1259 mapSet(def, createString(datatype), "record-types");
1260 mapSet(def, createString(makeString(-1, "record")), "type");
1261 } else
1262 mapSet(def, createString(datatype), "type");
1263
1264 /* Force full binary when the format is not supported by Kea */
1265 if (not_supported)
1266 appendString(format, "Y");
1267 option->format = format->content;
1268
1269 if (has_encapsulation)
1270 mapSet(def, createString(encapsulated), "encapsulate");
1271
1272 optdef = mapGet(cfile->stack[1], "option-def");
1273 if (optdef == NULL) {
1274 optdef = createList();
1275 mapSet(cfile->stack[1], optdef, "option-def");
1276 }
1277 listPush(optdef, def);
1278 }
1279
1280 /*
1281 * Specialized version of parse_option_code_definition for vendor options
1282 * DHCPv4 vivso (code 125, space vendor) and DHCPv6 vendor-opts (17,
1283 * space vsio). The syntax is a subnet:
1284 * vcd :== NUMBER EQUALS ENCAPSULATE identifier SEMI
1285 */
1286
1287 void
parse_vendor_code_definition(struct parse * cfile,struct option * option)1288 parse_vendor_code_definition(struct parse *cfile, struct option *option)
1289 {
1290 const char *val;
1291 enum dhcp_token token;
1292 struct string *id;
1293 struct string *space;
1294 struct space *universe;
1295 struct string *name;
1296 unsigned code;
1297 struct element *vendor;
1298
1299 space = makeString(-1, "vendor-");
1300
1301 /* Parse the option code / vendor id. */
1302 token = next_token(&val, NULL, cfile);
1303 if (token != NUMBER)
1304 parse_error(cfile, "expecting option code number.");
1305 id = makeString(-1, val);
1306 appendString(space, val);
1307
1308
1309 token = next_token(&val, NULL, cfile);
1310 if (token != EQUAL)
1311 parse_error(cfile, "expecting \"=\"");
1312 token = next_token(&val, NULL, cfile);
1313 if (token != ENCAPSULATE)
1314 parse_error(cfile, "expecting encapsulate");
1315 token = next_token(&val, NULL, cfile);
1316 if (!is_identifier(token))
1317 parse_error(cfile, "expecting option space identifier");
1318 universe = space_lookup(val);
1319 if (universe == NULL)
1320 parse_error(cfile, "unknown option space %s", val);
1321 /* Map the universe to vendor-<code> */
1322 universe->name = space->content;
1323 /* Create the vendor option */
1324 vendor = createMap();
1325 if (local_family == AF_INET) {
1326 space = makeString(-1, "dhcp4");
1327 name = makeString(-1, "vivso-suboptions");
1328 code = DHO_VIVSO_SUBOPTIONS;
1329 } else {
1330 space =makeString(-1, "dhcp6");
1331 name = makeString(-1, "vendor-opts");
1332 code = D6O_VENDOR_OPTS;
1333 }
1334 mapSet(vendor, createString(space), "space");
1335 mapSet(vendor, createString(name), "name");
1336 mapSet(vendor, createInt(code), "code");
1337 mapSet(vendor, createString(id), "data");
1338 universe->vendor = vendor;
1339 parse_semi(cfile);
1340 }
1341
1342 struct string *
convert_format(const char * fmt,isc_boolean_t * is_array,isc_boolean_t * encapsulate)1343 convert_format(const char *fmt, isc_boolean_t *is_array,
1344 isc_boolean_t *encapsulate)
1345 {
1346 struct string *datatype;
1347 const char *g;
1348
1349 if ((strchr(fmt, 'A') != NULL) || (strchr(fmt, 'a') != NULL) ||
1350 (strchr(fmt, 'D') != NULL))
1351 *is_array = ISC_TRUE;
1352
1353 if (strchr(fmt, 'E') != NULL)
1354 *encapsulate = ISC_TRUE;
1355
1356 if ((strchr(fmt, 'Y') != NULL) || (strchr(fmt, 'A') != NULL) ||
1357 (strchr(fmt, 'E') != NULL) || (strchr(fmt, 'o') != NULL) ||
1358 (*fmt == 'X') || (*fmt == 'u'))
1359 return makeString(-1, "binary");
1360
1361 datatype = allocString();
1362
1363 do {
1364 if (datatype->length != 0)
1365 appendString(datatype, ", ");
1366
1367 switch (*fmt) {
1368 case 'U':
1369 case 't':
1370 case 'x':
1371 appendString(datatype, "string");
1372 break;
1373 case 'I':
1374 appendString(datatype, "ipv4-address");
1375 break;
1376 case '6':
1377 appendString(datatype, "ipv6-address");
1378 break;
1379 case 'l':
1380 appendString(datatype, "int32");
1381 break;
1382 case 'L':
1383 case 'T':
1384 appendString(datatype, "uint32");
1385 break;
1386 case 's':
1387 appendString(datatype, "int16");
1388 break;
1389 case 'S':
1390 appendString(datatype, "uint16");
1391 break;
1392 case 'b':
1393 appendString(datatype, "int8");
1394 break;
1395 case 'B':
1396 appendString(datatype, "uint8");
1397 break;
1398 case 'f':
1399 appendString(datatype, "boolean");
1400 break;
1401 case 'E':
1402 case 'N':
1403 g = strchr(fmt, '.');
1404 if (g == NULL)
1405 return makeString(-1, "bad?!");
1406 if (*fmt == 'N')
1407 return makeString(-1, "unsupported?!");
1408 fmt = g;
1409 break;
1410 case 'X':
1411 appendString(datatype, "binary");
1412 break;
1413 case 'd':
1414 case 'D':
1415 appendString(datatype, "fqdn");
1416 break;
1417 case 'Z':
1418 appendString(datatype, "empty");
1419 break;
1420 case 'A':
1421 case 'a':
1422 case 'c':
1423 /* ignored */
1424 break;
1425 default:
1426 return makeString(-1, "unknown?!");
1427 }
1428 fmt++;
1429 } while (*fmt != '\0');
1430
1431 return datatype;
1432 }
1433
1434 /*
1435 * base64 :== NUMBER_OR_STRING
1436 */
1437
1438 struct string *
parse_base64(struct parse * cfile)1439 parse_base64(struct parse *cfile)
1440 {
1441 const char *val;
1442 unsigned i;
1443 static unsigned char
1444 from64[] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */
1445 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */
1446 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */
1447 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */
1448 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */
1449 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */
1450 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */
1451 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */
1452 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */
1453 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */
1454 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */
1455 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */
1456 struct string *t;
1457 struct string *r;
1458 isc_boolean_t valid_base64;
1459
1460 r = allocString();
1461
1462 /* It's possible for a + or a / to cause a base64 quantity to be
1463 tokenized into more than one token, so we have to parse them all
1464 in before decoding. */
1465 do {
1466 unsigned l;
1467
1468 (void)next_token(&val, &l, cfile);
1469 t = makeString(l, val);
1470 concatString(r, t);
1471 (void)peek_token(&val, NULL, cfile);
1472 valid_base64 = ISC_TRUE;
1473 for (i = 0; val[i]; i++) {
1474 /* Check to see if the character is valid. It
1475 may be out of range or within the right range
1476 but not used in the mapping */
1477 if (((val[i] < ' ') || (val[i] > 'z')) ||
1478 ((from64[val[i] - ' '] > 63) && (val[i] != '='))) {
1479 valid_base64 = ISC_FALSE;
1480 break; /* no need to continue for loop */
1481 }
1482 }
1483 } while (valid_base64);
1484
1485 return r;
1486 }
1487
1488 /*
1489 * colon-separated-hex-list :== NUMBER |
1490 * NUMBER COLON colon-separated-hex-list
1491 */
1492
1493 struct string *
parse_cshl(struct parse * cfile)1494 parse_cshl(struct parse *cfile)
1495 {
1496 uint8_t ibuf;
1497 char tbuf[4];
1498 isc_boolean_t first = ISC_TRUE;
1499 struct string *data;
1500 enum dhcp_token token;
1501 const char *val;
1502
1503 data = allocString();
1504
1505 for (;;) {
1506 token = next_token(&val, NULL, cfile);
1507 if (token != NUMBER && token != NUMBER_OR_NAME)
1508 parse_error(cfile, "expecting hexadecimal number.");
1509 convert_num(cfile, &ibuf, val, 16, 8);
1510 if (first)
1511 snprintf(tbuf, sizeof(tbuf), "%02hhx", ibuf);
1512 else
1513 snprintf(tbuf, sizeof(tbuf), ":%02hhx", ibuf);
1514 first = ISC_FALSE;
1515 appendString(data, tbuf);
1516
1517 token = peek_token(&val, NULL, cfile);
1518 if (token != COLON)
1519 break;
1520 skip_token(&val, NULL, cfile);
1521 }
1522
1523 return data;
1524 }
1525
1526 /* Same but without colons in output */
1527
1528 struct string *
parse_hexa(struct parse * cfile)1529 parse_hexa(struct parse *cfile)
1530 {
1531 uint8_t ibuf;
1532 char tbuf[4];
1533 struct string *data;
1534 enum dhcp_token token;
1535 const char *val;
1536
1537 data = allocString();
1538
1539 for (;;) {
1540 token = next_token(&val, NULL, cfile);
1541 if (token != NUMBER && token != NUMBER_OR_NAME)
1542 parse_error(cfile, "expecting hexadecimal number.");
1543 convert_num(cfile, &ibuf, val, 16, 8);
1544 snprintf(tbuf, sizeof(tbuf), "%02hhx", ibuf);
1545 appendString(data, tbuf);
1546
1547 token = peek_token(&val, NULL, cfile);
1548 if (token != COLON)
1549 break;
1550 skip_token(&val, NULL, cfile);
1551 }
1552
1553 return data;
1554 }
1555
1556 /*
1557 * executable-statements :== executable-statement executable-statements |
1558 * executable-statement
1559 *
1560 * executable-statement :==
1561 * IF if-statement |
1562 * ADD class-name SEMI |
1563 * BREAK SEMI |
1564 * OPTION option-parameter SEMI |
1565 * SUPERSEDE option-parameter SEMI |
1566 * PREPEND option-parameter SEMI |
1567 * APPEND option-parameter SEMI
1568 */
1569
1570 isc_boolean_t
parse_executable_statements(struct element * statements,struct parse * cfile,isc_boolean_t * lose,enum expression_context case_context)1571 parse_executable_statements(struct element *statements,
1572 struct parse *cfile, isc_boolean_t *lose,
1573 enum expression_context case_context)
1574 {
1575 if (statements->type != ELEMENT_LIST)
1576 parse_error(cfile, "statements is not a list?");
1577 for (;;) {
1578 struct element *statement;
1579
1580 statement = createMap();
1581 TAILQ_CONCAT(&statement->comments, &cfile->comments);
1582 if (!parse_executable_statement(statement, cfile, lose,
1583 case_context, ISC_FALSE))
1584 break;
1585 TAILQ_CONCAT(&statement->comments, &cfile->comments);
1586 listPush(statements, statement);
1587 }
1588 if (!*lose)
1589 return ISC_TRUE;
1590
1591 return ISC_FALSE;
1592 }
1593
1594 isc_boolean_t
parse_executable_statement(struct element * result,struct parse * cfile,isc_boolean_t * lose,enum expression_context case_context,isc_boolean_t direct)1595 parse_executable_statement(struct element *result,
1596 struct parse *cfile, isc_boolean_t *lose,
1597 enum expression_context case_context,
1598 isc_boolean_t direct)
1599 {
1600 unsigned len;
1601 enum dhcp_token token;
1602 const char *val;
1603 struct element *st;
1604 struct option *option;
1605 struct element *var;
1606 struct element *pri;
1607 struct element *expr;
1608 isc_boolean_t known;
1609 int flag;
1610 int i;
1611 struct element *zone;
1612 struct string *s;
1613 static isc_boolean_t log_warning = ISC_TRUE;
1614
1615 token = peek_token(&val, NULL, cfile);
1616 switch (token) {
1617 case DB_TIME_FORMAT:
1618 skip_token(&val, NULL, cfile);
1619 token = next_token(&val, NULL, cfile);
1620 if (token == DEFAULT)
1621 s = makeString(-1, val);
1622 else if (token == LOCAL)
1623 s = makeString(-1, val);
1624 else
1625 parse_error(cfile, "Expecting 'local' or 'default'.");
1626
1627 token = next_token(&val, NULL, cfile);
1628 if (token != SEMI)
1629 parse_error(cfile, "Expecting a semicolon.");
1630 st = createString(s);
1631 st->skip = ISC_TRUE;
1632 cfile->issue_counter++;
1633 mapSet(result, st, "db-time-format");
1634
1635 /* We're done here. */
1636 return ISC_TRUE;
1637
1638 case IF:
1639 skip_token(&val, NULL, cfile);
1640 return parse_if_statement(result, cfile, lose);
1641
1642 case TOKEN_ADD:
1643 skip_token(&val, NULL, cfile);
1644 token = next_token(&val, NULL, cfile);
1645 if (token != STRING)
1646 parse_error(cfile, "expecting class name.");
1647 s = makeString(-1, val);
1648 parse_semi(cfile);
1649 st = createString(s);
1650 st->skip = ISC_TRUE;
1651 cfile->issue_counter++;
1652 mapSet(result, st, "add-class");
1653 break;
1654
1655 case BREAK:
1656 skip_token(&val, NULL, cfile);
1657 s = makeString(-1, val);
1658 parse_semi(cfile);
1659 st = createNull();
1660 st->skip = ISC_TRUE;
1661 cfile->issue_counter++;
1662 mapSet(result, st, "break");
1663 break;
1664
1665 case SEND:
1666 skip_token(&val, NULL, cfile);
1667 known = ISC_FALSE;
1668 option = parse_option_name(cfile, ISC_FALSE, &known);
1669 if (option == NULL) {
1670 *lose = ISC_TRUE;
1671 return ISC_FALSE;
1672 }
1673 return parse_option_statement(result, cfile, option,
1674 send_option_statement);
1675
1676 case SUPERSEDE:
1677 case OPTION:
1678 skip_token(&val, NULL, cfile);
1679 known = ISC_FALSE;
1680 option = parse_option_name(cfile, ISC_FALSE, &known);
1681 if (option == NULL) {
1682 *lose = ISC_TRUE;
1683 return ISC_FALSE;
1684 }
1685 return parse_option_statement(result, cfile, option,
1686 supersede_option_statement);
1687
1688 case ALLOW:
1689 flag = 1;
1690 goto pad;
1691 case DENY:
1692 flag = 0;
1693 goto pad;
1694 case IGNORE:
1695 flag = 2;
1696 pad:
1697 skip_token(&val, NULL, cfile);
1698 st = parse_allow_deny(cfile, flag);
1699 mapSet(result, st, "config");
1700 break;
1701
1702 case DEFAULT:
1703 skip_token(&val, NULL, cfile);
1704 token = peek_token(&val, NULL, cfile);
1705 if (token == COLON)
1706 goto switch_default;
1707 known = ISC_FALSE;
1708 option = parse_option_name(cfile, ISC_FALSE, &known);
1709 if (option == NULL) {
1710 *lose = ISC_TRUE;
1711 return ISC_FALSE;
1712 }
1713 return parse_option_statement(result, cfile, option,
1714 default_option_statement);
1715 case PREPEND:
1716 skip_token(&val, NULL, cfile);
1717 known = ISC_FALSE;
1718 option = parse_option_name(cfile, ISC_FALSE, &known);
1719 if (option == NULL) {
1720 *lose = ISC_TRUE;
1721 return ISC_FALSE;
1722 }
1723 return parse_option_statement(result, cfile, option,
1724 prepend_option_statement);
1725 case APPEND:
1726 skip_token(&val, NULL, cfile);
1727 known = ISC_FALSE;
1728 option = parse_option_name(cfile, ISC_FALSE, &known);
1729 if (option == NULL) {
1730 *lose = ISC_TRUE;
1731 return ISC_FALSE;
1732 }
1733 return parse_option_statement(result, cfile, option,
1734 append_option_statement);
1735
1736 case ON:
1737 skip_token(&val, NULL, cfile);
1738 return parse_on_statement(result, cfile, lose);
1739
1740 case SWITCH:
1741 skip_token(&val, NULL, cfile);
1742 return parse_switch_statement(result, cfile, lose);
1743
1744 case CASE:
1745 skip_token(&val, NULL, cfile);
1746 if (case_context == context_any)
1747 parse_error(cfile,
1748 "case statement in inappropriate scope.");
1749 return parse_case_statement(result,
1750 cfile, lose, case_context);
1751
1752 switch_default:
1753 skip_token(&val, NULL, cfile);
1754 if (case_context == context_any)
1755 parse_error(cfile, "switch default statement in %s",
1756 "inappropriate scope.");
1757 s = makeString(-1, "default");
1758 st = createNull();
1759 st->skip = ISC_TRUE;
1760 cfile->issue_counter++;
1761 mapSet(result, st, "default");
1762 return ISC_TRUE;
1763
1764 case DEFINE:
1765 case TOKEN_SET:
1766 skip_token(&val, NULL, cfile);
1767 if (token == DEFINE)
1768 flag = 1;
1769 else
1770 flag = 0;
1771
1772 token = next_token(&val, NULL, cfile);
1773 if (token != NAME && token != NUMBER_OR_NAME)
1774 parse_error(cfile,
1775 "%s can't be a variable name", val);
1776 st = createMap();
1777 st->skip = ISC_TRUE;
1778 cfile->issue_counter++;
1779 mapSet(result, st, flag ? "define" : "set");
1780 var = createString(makeString(-1, val));
1781 mapSet(st, var, "name");
1782 token = next_token(&val, NULL, cfile);
1783
1784 if (token == LPAREN) {
1785 struct element *func;
1786 struct string *args;
1787
1788 func = createMap();
1789 args = allocString();
1790 do {
1791 token = next_token(&val, NULL, cfile);
1792 if (token == RPAREN)
1793 break;
1794 if (token != NAME && token != NUMBER_OR_NAME)
1795 parse_error(cfile,
1796 "expecting argument name");
1797 if (args->length > 0)
1798 appendString(args, ", ");
1799 appendString(args, val);
1800 token = next_token(&val, NULL, cfile);
1801 } while (token == COMMA);
1802
1803 if (token != RPAREN) {
1804 parse_error(cfile, "expecting right paren.");
1805 badx:
1806 skip_to_semi(cfile);
1807 *lose = ISC_TRUE;
1808 return ISC_FALSE;
1809 }
1810 mapSet(func, createString(args), "arguments");
1811
1812 token = next_token(&val, NULL, cfile);
1813 if (token != LBRACE)
1814 parse_error(cfile, "expecting left brace.");
1815
1816 expr = createList();
1817 if (!parse_executable_statements(expr, cfile,
1818 lose, case_context)) {
1819 if (*lose)
1820 goto badx;
1821 }
1822 mapSet(func, expr, "body");
1823 mapSet(st, func, "function");
1824
1825 token = next_token(&val, NULL, cfile);
1826 if (token != RBRACE)
1827 parse_error(cfile, "expecting rigt brace.");
1828 } else {
1829 if (token != EQUAL)
1830 parse_error(cfile,
1831 "expecting '=' in %s statement.",
1832 flag ? "define" : "set");
1833
1834 expr = createMap();
1835 if (!parse_expression(expr, cfile, lose, context_any,
1836 NULL, expr_none)) {
1837 if (!*lose)
1838 parse_error(cfile,
1839 "expecting expression.");
1840 else
1841 *lose = ISC_TRUE;
1842 skip_to_semi(cfile);
1843 return ISC_FALSE;
1844 }
1845 mapSet(st, expr, "value");
1846 parse_semi(cfile);
1847 }
1848 break;
1849
1850 case UNSET:
1851 skip_token(&val, NULL, cfile);
1852 token = next_token(&val, NULL, cfile);
1853 if (token != NAME && token != NUMBER_OR_NAME)
1854 parse_error(cfile, "%s can't be a variable name", val);
1855
1856 st = createMap();
1857 st->skip = ISC_TRUE;
1858 cfile->issue_counter++;
1859 mapSet(result, st, "unset");
1860 var = createString(makeString(-1, val));
1861 mapSet(st, var, "name");
1862 parse_semi(cfile);
1863 break;
1864
1865 case EVAL:
1866 skip_token(&val, NULL, cfile);
1867 expr = createMap();
1868
1869 if (!parse_expression(expr, cfile, lose,
1870 context_data, /* XXX */
1871 NULL, expr_none)) {
1872 if (!*lose)
1873 parse_error(cfile,
1874 "expecting data expression.");
1875 else
1876 *lose = ISC_TRUE;
1877 skip_to_semi(cfile);
1878 return ISC_FALSE;
1879 }
1880 mapSet(result, expr, "eval");
1881 parse_semi(cfile);
1882 break;
1883
1884 case EXECUTE:
1885 skip_token(&val, NULL, cfile);
1886 expr = createMap();
1887
1888 token = next_token(&val, NULL, cfile);
1889 if (token != LPAREN)
1890 parse_error(cfile, "left parenthesis expected.");
1891
1892 token = next_token(&val, &len, cfile);
1893 if (token != STRING)
1894 parse_error(cfile, "Expecting a quoted string.");
1895 mapSet(expr, createString(makeString(len, val)), "command");
1896
1897 st = createList();
1898
1899 while ((token = next_token(&val, NULL, cfile)) == COMMA) {
1900 var = createMap();
1901 if (!parse_data_expression(var, cfile, lose)) {
1902 if (!*lose)
1903 parse_error(cfile,
1904 "expecting expression.");
1905 skip_to_semi(cfile);
1906 *lose = ISC_TRUE;
1907 return ISC_FALSE;
1908 }
1909 listPush(st, var);
1910 }
1911 mapSet(expr, st, "arguments");
1912
1913 if (token != RPAREN)
1914 parse_error(cfile, "right parenthesis expected.");
1915 parse_semi(cfile);
1916 mapSet(result, expr, "execute");
1917 break;
1918
1919 case RETURN:
1920 skip_token(&val, NULL, cfile);
1921
1922 expr = createMap();
1923
1924 if (!parse_expression(expr, cfile, lose, context_data,
1925 NULL, expr_none)) {
1926 if (!*lose)
1927 parse_error(cfile,
1928 "expecting data expression.");
1929 else
1930 *lose = ISC_TRUE;
1931 skip_to_semi(cfile);
1932 return ISC_FALSE;
1933 }
1934 mapSet(result, expr, "return");
1935 parse_semi(cfile);
1936 break;
1937
1938 case LOG:
1939 skip_token(&val, NULL, cfile);
1940
1941 st = createMap();
1942 st->skip = ISC_TRUE;
1943 cfile->issue_counter++;
1944 mapSet(result, st, "log");
1945 if (log_warning) {
1946 struct comment *comment;
1947
1948 comment = createComment("/// Kea does not support "
1949 "yet log statements");
1950 TAILQ_INSERT_TAIL(&st->comments, comment);
1951 comment= createComment("/// Reference Kea #234");
1952 TAILQ_INSERT_TAIL(&st->comments, comment);
1953 log_warning = ISC_FALSE;
1954 }
1955
1956 token = next_token(&val, NULL, cfile);
1957 if (token != LPAREN)
1958 parse_error(cfile, "left parenthesis expected.");
1959
1960 token = peek_token(&val, NULL, cfile);
1961 i = 1;
1962 if (token == FATAL)
1963 s = makeString(-1, val);
1964 else if (token == ERROR)
1965 s = makeString(-1, val);
1966 else if (token == TOKEN_DEBUG)
1967 s = makeString(-1, val);
1968 else if (token == INFO)
1969 s = makeString(-1, val);
1970 else {
1971 s = makeString(-1, "DEBUG");
1972 i = 0;
1973 }
1974 if (i) {
1975 skip_token(&val, NULL, cfile);
1976 token = next_token(&val, NULL, cfile);
1977 if (token != COMMA)
1978 parse_error(cfile, "comma expected.");
1979 }
1980 pri = createString(s);
1981 mapSet(st, pri, "priority");
1982
1983 expr = createMap();
1984 if (!parse_data_expression(expr, cfile, lose)) {
1985 skip_to_semi(cfile);
1986 *lose = ISC_TRUE;
1987 return ISC_FALSE;
1988 }
1989 mapSet(st, expr, "message");
1990
1991 token = next_token(&val, NULL, cfile);
1992 if (token != RPAREN)
1993 parse_error(cfile, "right parenthesis expected.");
1994
1995 token = next_token(&val, NULL, cfile);
1996 if (token != SEMI)
1997 parse_error(cfile, "semicolon expected.");
1998 break;
1999
2000 case PARSE_VENDOR_OPT:
2001 /* The parse-vendor-option; The statement has no arguments.
2002 * We simply set up the statement and when it gets executed it
2003 * will find all information it needs in the packet and options.
2004 */
2005 skip_token(&val, NULL, cfile);
2006 parse_semi(cfile);
2007
2008 /* Done by Kea after classification so this statement
2009 * silently does not translate */
2010 break;
2011
2012 /* Not really a statement, but we parse it here anyway
2013 because it's appropriate for all DHCP agents with
2014 parsers. */
2015 case ZONE:
2016 skip_token(&val, NULL, cfile);
2017 zone = createMap();
2018 zone->skip = ISC_TRUE;
2019 cfile->issue_counter++;
2020 mapSet(result, zone, "zone");
2021
2022 s = parse_host_name(cfile);
2023 if (s == NULL) {
2024 parse_error(cfile, "expecting hostname.");
2025 badzone:
2026 *lose = ISC_TRUE;
2027 skip_to_semi(cfile);
2028 return ISC_FALSE;
2029 }
2030 if (s->content[s->length - 1] != '.')
2031 appendString(s, ".");
2032 mapSet(zone, createString(s), "name");
2033 if (!parse_zone(zone, cfile))
2034 goto badzone;
2035 return ISC_TRUE;
2036
2037 /* Also not really a statement, but same idea as above. */
2038 case KEY:
2039 skip_token(&val, NULL, cfile);
2040 if (!parse_key(result, cfile)) {
2041 /* Kea TODO */
2042 *lose = ISC_TRUE;
2043 return ISC_FALSE;
2044 }
2045 return ISC_TRUE;
2046
2047 default:
2048 if (is_identifier(token)) {
2049 /* the config universe is the server one */
2050 option = option_lookup_name("server", val);
2051 if (option) {
2052 skip_token(&val, NULL, cfile);
2053 result->skip = ISC_TRUE;
2054 cfile->issue_counter++;
2055 return parse_config_statement
2056 (direct ? NULL : result,
2057 cfile, option,
2058 supersede_option_statement);
2059 }
2060 }
2061
2062 if (token == NUMBER_OR_NAME || token == NAME) {
2063 /* This is rather ugly. Since function calls are
2064 data expressions, fake up an eval statement. */
2065 expr = createMap();
2066
2067 if (!parse_expression(expr, cfile, lose, context_data,
2068 NULL, expr_none)) {
2069 if (!*lose)
2070 parse_error(cfile, "expecting "
2071 "function call.");
2072 else
2073 *lose = ISC_TRUE;
2074 skip_to_semi(cfile);
2075 return ISC_FALSE;
2076 }
2077 mapSet(result, expr, "eval");
2078 parse_semi(cfile);
2079 break;
2080 }
2081
2082 *lose = ISC_FALSE;
2083 return ISC_FALSE;
2084 }
2085
2086 return ISC_TRUE;
2087 }
2088
2089 /* zone-statements :== zone-statement |
2090 zone-statement zone-statements
2091 zone-statement :==
2092 PRIMARY ip-addresses SEMI |
2093 SECONDARY ip-addresses SEMI |
2094 PRIMARY6 ip-address6 SEMI |
2095 SECONDARY6 ip-address6 SEMI |
2096 key-reference SEMI
2097 ip-addresses :== ip-addr-or-hostname |
2098 ip-addr-or-hostname COMMA ip-addresses
2099 key-reference :== KEY STRING |
2100 KEY identifier */
2101
2102 isc_boolean_t
parse_zone(struct element * zone,struct parse * cfile)2103 parse_zone(struct element *zone, struct parse *cfile)
2104 {
2105 int token;
2106 const char *val;
2107 struct element *values;
2108 struct string *key_name;
2109 isc_boolean_t done = ISC_FALSE;
2110
2111 token = next_token(&val, NULL, cfile);
2112 if (token != LBRACE)
2113 parse_error(cfile, "expecting left brace");
2114
2115 do {
2116 token = peek_token(&val, NULL, cfile);
2117 switch (token) {
2118 case PRIMARY:
2119 if (mapContains(zone, "primary"))
2120 parse_error(cfile, "more than one primary.");
2121 values = createList();
2122 mapSet(zone, values, "primary");
2123 goto consemup;
2124
2125 case SECONDARY:
2126 if (mapContains(zone, "secondary"))
2127 parse_error(cfile, "more than one secondary.");
2128 values = createList();
2129 mapSet(zone, values, "secondary");
2130 consemup:
2131 skip_token(&val, NULL, cfile);
2132 do {
2133 struct string *value;
2134
2135 value = parse_ip_addr_or_hostname(cfile,
2136 ISC_FALSE);
2137 if (value == NULL)
2138 parse_error(cfile,
2139 "expecting IP addr or "
2140 "hostname.");
2141 listPush(values, createString(value));
2142 token = next_token(&val, NULL, cfile);
2143 } while (token == COMMA);
2144 if (token != SEMI)
2145 parse_error(cfile, "expecting semicolon.");
2146 break;
2147
2148 case PRIMARY6:
2149 if (mapContains(zone, "primary6"))
2150 parse_error(cfile, "more than one primary6.");
2151 values = createList();
2152 mapSet(zone, values, "primary6");
2153 goto consemup6;
2154
2155 case SECONDARY6:
2156 if (mapContains(zone, "secondary6"))
2157 parse_error(cfile, "more than one secondary6.");
2158 values = createList();
2159 mapSet(zone, values, "secondary6");
2160 consemup6:
2161 skip_token(&val, NULL, cfile);
2162 do {
2163 struct string *addr;
2164
2165 addr = parse_ip6_addr_txt(cfile);
2166 if (addr == NULL)
2167 parse_error(cfile, "expecting IPv6 addr.");
2168 listPush(values, createString(addr));
2169 token = next_token(&val, NULL, cfile);
2170 } while (token == COMMA);
2171 if (token != SEMI)
2172 parse_error(cfile, "expecting semicolon.");
2173 break;
2174
2175 case KEY:
2176 skip_token(&val, NULL, cfile);
2177 token = peek_token(&val, NULL, cfile);
2178 if (token == STRING) {
2179 skip_token(&val, NULL, cfile);
2180 key_name = makeString(-1, val);
2181 } else {
2182 key_name = parse_host_name(cfile);
2183 if (!key_name)
2184 parse_error(cfile, "expecting key name.");
2185 }
2186 if (mapContains(zone, "key"))
2187 parse_error(cfile, "Multiple key definitions");
2188 mapSet(zone, createString(key_name), "key");
2189 parse_semi(cfile);
2190 break;
2191
2192 default:
2193 done = 1;
2194 break;
2195 }
2196 } while (!done);
2197
2198 token = next_token(&val, NULL, cfile);
2199 if (token != RBRACE)
2200 parse_error(cfile, "expecting right brace.");
2201 return (1);
2202 }
2203
2204 /* key-statements :== key-statement |
2205 key-statement key-statements
2206 key-statement :==
2207 ALGORITHM host-name SEMI |
2208 secret-definition SEMI
2209 secret-definition :== SECRET base64val |
2210 SECRET STRING
2211
2212 Kea: where to put this? It is a D2 value */
2213
2214 isc_boolean_t
parse_key(struct element * result,struct parse * cfile)2215 parse_key(struct element* result, struct parse *cfile)
2216 {
2217 int token;
2218 const char *val;
2219 isc_boolean_t done = ISC_FALSE;
2220 struct element *key;
2221 struct string *alg;
2222 struct string *sec;
2223 struct element *keys;
2224 char *s;
2225
2226 key = createMap();
2227 key->skip = ISC_TRUE;
2228 cfile->issue_counter++;
2229
2230 token = peek_token(&val, NULL, cfile);
2231 if (token == STRING) {
2232 skip_token(&val, NULL, cfile);
2233 mapSet(key, createString(makeString(-1, val)), "name");
2234 } else {
2235 struct string *name;
2236
2237 name = parse_host_name(cfile);
2238 if (name == NULL)
2239 parse_error(cfile, "expecting key name.");
2240 mapSet(key, createString(name), "name");
2241 }
2242
2243 token = next_token(&val, NULL, cfile);
2244 if (token != LBRACE)
2245 parse_error(cfile, "expecting left brace");
2246
2247 do {
2248 token = next_token(&val, NULL, cfile);
2249 switch (token) {
2250 case ALGORITHM:
2251 if (mapContains(key, "algorithm"))
2252 parse_error(cfile, "key: too many algorithms");
2253 alg = parse_host_name(cfile);
2254 if (alg == NULL)
2255 parse_error(cfile,
2256 "expecting key algorithm name.");
2257 parse_semi(cfile);
2258 /* If the algorithm name isn't an FQDN, tack on
2259 the .SIG-ALG.REG.NET. domain. */
2260 s = strrchr(alg->content, '.');
2261 if (!s)
2262 appendString(alg, ".SIG-ALG.REG.INT.");
2263 /* If there is no trailing '.', hack one in. */
2264 else
2265 appendString(alg, ".");
2266 mapSet(key, createString(alg), "algorithm");
2267 break;
2268
2269 case SECRET:
2270 if (mapContains(key, "secret"))
2271 parse_error(cfile, "key: too many secrets");
2272
2273 sec = parse_base64(cfile);
2274 if (sec == NULL) {
2275 skip_to_rbrace(cfile, 1);
2276 return ISC_FALSE;
2277 }
2278 mapSet(key, createString(sec), "secret");
2279
2280 parse_semi(cfile);
2281 break;
2282
2283 default:
2284 done = ISC_TRUE;
2285 break;
2286 }
2287 } while (!done);
2288 if (token != RBRACE)
2289 parse_error(cfile, "expecting right brace.");
2290 /* Allow the BIND 8 syntax, which has a semicolon after each
2291 closing brace. */
2292 token = peek_token(&val, NULL, cfile);
2293 if (token == SEMI)
2294 skip_token(&val, NULL, cfile);
2295
2296 /* Remember the key. */
2297 keys = mapGet(result, "tsig-keys");
2298 if (keys == NULL) {
2299 keys = createList();
2300 mapSet(result, keys, "tsig-keys");
2301 }
2302 listPush(keys, key);
2303 return ISC_TRUE;
2304 }
2305
2306 /*
2307 * on-statement :== event-types LBRACE executable-statements RBRACE
2308 * event-types :== event-type OR event-types |
2309 * event-type
2310 * event-type :== EXPIRY | COMMIT | RELEASE
2311 */
2312
2313 isc_boolean_t
parse_on_statement(struct element * result,struct parse * cfile,isc_boolean_t * lose)2314 parse_on_statement(struct element *result,
2315 struct parse *cfile,
2316 isc_boolean_t *lose)
2317 {
2318 enum dhcp_token token;
2319 const char *val;
2320 struct element *statement;
2321 struct string *cond;
2322 struct element *body;
2323
2324 statement = createMap();
2325 statement->skip = ISC_TRUE;
2326 cfile->issue_counter++;
2327 mapSet(result, statement, "on");
2328
2329 cond = allocString();
2330 do {
2331 token = next_token(&val, NULL, cfile);
2332 switch (token) {
2333 case EXPIRY:
2334 case COMMIT:
2335 case RELEASE:
2336 case TRANSMISSION:
2337 appendString(cond, val);
2338 break;
2339
2340 default:
2341 parse_error(cfile, "expecting a lease event type");
2342 }
2343 token = next_token(&val, NULL, cfile);
2344 if (token == OR)
2345 appendString(cond, " or ");
2346 } while (token == OR);
2347
2348 mapSet(statement, createString(cond), "condition");
2349
2350 /* Semicolon means no statements. */
2351 if (token == SEMI)
2352 return ISC_TRUE;
2353
2354 if (token != LBRACE)
2355 parse_error(cfile, "left brace expected.");
2356
2357 body = createList();
2358 if (!parse_executable_statements(body, cfile, lose, context_any)) {
2359 if (*lose) {
2360 /* Try to even things up. */
2361 do {
2362 token = next_token(&val, NULL, cfile);
2363 } while (token != END_OF_FILE && token != RBRACE);
2364 return ISC_FALSE;
2365 }
2366 }
2367 mapSet(statement, body, "body");
2368 token = next_token(&val, NULL, cfile);
2369 if (token != RBRACE)
2370 parse_error(cfile, "right brace expected.");
2371 return ISC_TRUE;
2372 }
2373
2374 /*
2375 * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE
2376 *
2377 */
2378
2379 isc_boolean_t
parse_switch_statement(struct element * result,struct parse * cfile,isc_boolean_t * lose)2380 parse_switch_statement(struct element *result,
2381 struct parse *cfile,
2382 isc_boolean_t *lose)
2383 {
2384 enum dhcp_token token;
2385 const char *val;
2386 struct element *statement;
2387 struct element *cond;
2388 struct element *body;
2389
2390 statement = createMap();
2391 statement->skip = ISC_TRUE;
2392 cfile->issue_counter++;
2393 mapSet(result, statement, "switch");
2394
2395 token = next_token(&val, NULL, cfile);
2396 if (token != LPAREN) {
2397 parse_error(cfile, "expecting left brace.");
2398 *lose = ISC_TRUE;
2399 skip_to_semi(cfile);
2400 return ISC_FALSE;
2401 }
2402
2403 cond = createMap();
2404 if (!parse_expression(cond, cfile, lose, context_data_or_numeric,
2405 NULL, expr_none)) {
2406 if (!*lose)
2407 parse_error(cfile,
2408 "expecting data or numeric expression.");
2409 return ISC_FALSE;
2410 }
2411 mapSet(statement, cond, "condition");
2412
2413 token = next_token(&val, NULL, cfile);
2414 if (token != RPAREN)
2415 parse_error(cfile, "right paren expected.");
2416
2417 token = next_token(&val, NULL, cfile);
2418 if (token != LBRACE)
2419 parse_error(cfile, "left brace expected.");
2420
2421 body = createList();
2422 if (!parse_executable_statements(body, cfile, lose,
2423 (is_data_expression(cond) ? context_data : context_numeric))) {
2424 if (*lose) {
2425 skip_to_rbrace(cfile, 1);
2426 return ISC_FALSE;
2427 }
2428 }
2429 mapSet(statement, body, "body");
2430 token = next_token(&val, NULL, cfile);
2431 if (token != RBRACE)
2432 parse_error(cfile, "right brace expected.");
2433 return ISC_TRUE;
2434 }
2435
2436 /*
2437 * case-statement :== CASE expr COLON
2438 *
2439 */
2440
2441 isc_boolean_t
parse_case_statement(struct element * result,struct parse * cfile,isc_boolean_t * lose,enum expression_context case_context)2442 parse_case_statement(struct element *result,
2443 struct parse *cfile,
2444 isc_boolean_t *lose,
2445 enum expression_context case_context)
2446 {
2447 enum dhcp_token token;
2448 const char *val;
2449 struct element *expr;
2450
2451 expr = createMap();
2452 if (!parse_expression(expr, cfile, lose, case_context,
2453 NULL, expr_none))
2454 {
2455 if (!*lose)
2456 parse_error(cfile, "expecting %s expression.",
2457 (case_context == context_data
2458 ? "data" : "numeric"));
2459 *lose = ISC_TRUE;
2460 skip_to_semi(cfile);
2461 return ISC_FALSE;
2462 }
2463
2464 token = next_token(&val, NULL, cfile);
2465 if (token != COLON)
2466 parse_error(cfile, "colon expected.");
2467 mapSet(result, expr, "case");
2468 return ISC_TRUE;
2469 }
2470
2471 /*
2472 * if-statement :== boolean-expression LBRACE executable-statements RBRACE
2473 * else-statement
2474 *
2475 * else-statement :== <null> |
2476 * ELSE LBRACE executable-statements RBRACE |
2477 * ELSE IF if-statement |
2478 * ELSIF if-statement
2479 */
2480
2481 isc_boolean_t
parse_if_statement(struct element * result,struct parse * cfile,isc_boolean_t * lose)2482 parse_if_statement(struct element *result,
2483 struct parse *cfile,
2484 isc_boolean_t *lose)
2485 {
2486 enum dhcp_token token;
2487 const char *val;
2488 isc_boolean_t parenp;
2489 struct element *statement;
2490 struct element *cond;
2491 struct element *branch;
2492
2493 statement = createMap();
2494 statement->skip = ISC_TRUE;
2495 cfile->issue_counter++;
2496
2497 mapSet(result, statement, "if");
2498
2499 token = peek_token(&val, NULL, cfile);
2500 if (token == LPAREN) {
2501 parenp = ISC_TRUE;
2502 skip_token(&val, NULL, cfile);
2503 } else
2504 parenp = ISC_FALSE;
2505
2506 cond = createMap();
2507 if (!parse_boolean_expression(cond, cfile, lose)) {
2508 if (!*lose)
2509 parse_error(cfile, "boolean expression expected.");
2510 *lose = ISC_TRUE;
2511 return ISC_FALSE;
2512 }
2513 mapSet(statement, cond, "condition");
2514 if (parenp) {
2515 token = next_token(&val, NULL, cfile);
2516 if (token != RPAREN)
2517 parse_error(cfile, "expecting right paren.");
2518 }
2519 token = next_token(&val, NULL, cfile);
2520 if (token != LBRACE)
2521 parse_error(cfile, "left brace expected.");
2522 branch = createList();
2523 if (!parse_executable_statements(branch, cfile, lose, context_any)) {
2524 if (*lose) {
2525 /* Try to even things up. */
2526 do {
2527 token = next_token(&val, NULL, cfile);
2528 } while (token != END_OF_FILE && token != RBRACE);
2529 return ISC_FALSE;
2530 }
2531 }
2532 mapSet(statement, branch, "then");
2533 token = next_token(&val, NULL, cfile);
2534 if (token != RBRACE)
2535 parse_error(cfile, "right brace expected.");
2536 token = peek_token(&val, NULL, cfile);
2537 if (token == ELSE) {
2538 skip_token(&val, NULL, cfile);
2539 token = peek_token(&val, NULL, cfile);
2540 if (token == IF) {
2541 skip_token(&val, NULL, cfile);
2542 branch = createMap();
2543 if (!parse_if_statement(branch, cfile, lose)) {
2544 if (!*lose)
2545 parse_error(cfile,
2546 "expecting if statement");
2547 *lose = ISC_TRUE;
2548 return ISC_FALSE;
2549 }
2550 } else if (token != LBRACE)
2551 parse_error(cfile, "left brace or if expected.");
2552 else {
2553 skip_token(&val, NULL, cfile);
2554 branch = createList();
2555 if (!parse_executable_statements(branch, cfile,
2556 lose, context_any))
2557 return ISC_FALSE;
2558 token = next_token(&val, NULL, cfile);
2559 if (token != RBRACE)
2560 parse_error(cfile, "right brace expected.");
2561 }
2562 mapSet(statement, branch, "else");
2563 } else if (token == ELSIF) {
2564 skip_token(&val, NULL, cfile);
2565 branch = createMap();
2566 if (!parse_if_statement(branch, cfile, lose)) {
2567 if (!*lose)
2568 parse_error(cfile,
2569 "expecting conditional.");
2570 *lose = ISC_TRUE;
2571 return ISC_FALSE;
2572 }
2573 mapSet(statement, branch, "else");
2574 }
2575
2576 return ISC_TRUE;
2577 }
2578
2579 /*
2580 * boolean_expression :== CHECK STRING |
2581 * NOT boolean-expression |
2582 * data-expression EQUAL data-expression |
2583 * data-expression BANG EQUAL data-expression |
2584 * data-expression REGEX_MATCH data-expression |
2585 * boolean-expression AND boolean-expression |
2586 * boolean-expression OR boolean-expression
2587 * EXISTS OPTION-NAME
2588 */
2589
2590 isc_boolean_t
parse_boolean_expression(struct element * expr,struct parse * cfile,isc_boolean_t * lose)2591 parse_boolean_expression(struct element *expr,
2592 struct parse *cfile,
2593 isc_boolean_t *lose)
2594 {
2595 /* Parse an expression... */
2596 if (!parse_expression(expr, cfile, lose, context_boolean,
2597 NULL, expr_none))
2598 return ISC_FALSE;
2599
2600 if (!is_boolean_expression(expr) &&
2601 !mapContains(expr, "variable-reference") &&
2602 !mapContains(expr, "funcall"))
2603 parse_error(cfile, "Expecting a boolean expression.");
2604 return ISC_TRUE;
2605 }
2606
2607 /* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
2608
2609 isc_boolean_t
parse_boolean(struct parse * cfile)2610 parse_boolean(struct parse *cfile)
2611 {
2612 const char *val;
2613 isc_boolean_t rv;
2614
2615 (void)next_token(&val, NULL, cfile);
2616 if (!strcasecmp (val, "true")
2617 || !strcasecmp (val, "on"))
2618 rv = ISC_TRUE;
2619 else if (!strcasecmp (val, "false")
2620 || !strcasecmp (val, "off"))
2621 rv = ISC_FALSE;
2622 else
2623 parse_error(cfile,
2624 "boolean value (true/false/on/off) expected");
2625 parse_semi(cfile);
2626 return rv;
2627 }
2628
2629 /*
2630 * data_expression :== SUBSTRING LPAREN data-expression COMMA
2631 * numeric-expression COMMA
2632 * numeric-expression RPAREN |
2633 * CONCAT LPAREN data-expression COMMA
2634 * data-expression RPAREN
2635 * SUFFIX LPAREN data_expression COMMA
2636 * numeric-expression RPAREN |
2637 * LCASE LPAREN data_expression RPAREN |
2638 * UCASE LPAREN data_expression RPAREN |
2639 * OPTION option_name |
2640 * HARDWARE |
2641 * PACKET LPAREN numeric-expression COMMA
2642 * numeric-expression RPAREN |
2643 * V6RELAY LPAREN numeric-expression COMMA
2644 * data-expression RPAREN |
2645 * STRING |
2646 * colon_separated_hex_list
2647 */
2648
2649 isc_boolean_t
parse_data_expression(struct element * expr,struct parse * cfile,isc_boolean_t * lose)2650 parse_data_expression(struct element *expr,
2651 struct parse *cfile,
2652 isc_boolean_t *lose)
2653 {
2654 /* Parse an expression... */
2655 if (!parse_expression(expr, cfile, lose, context_data,
2656 NULL, expr_none))
2657 return ISC_FALSE;
2658
2659 if (!is_data_expression(expr) &&
2660 !mapContains(expr, "variable-reference") &&
2661 !mapContains(expr, "funcall"))
2662 parse_error(cfile, "Expecting a data expression.");
2663 return ISC_TRUE;
2664 }
2665
2666 /*
2667 * numeric-expression :== EXTRACT_INT LPAREN data-expression
2668 * COMMA number RPAREN |
2669 * NUMBER
2670 */
2671
2672 isc_boolean_t
parse_numeric_expression(struct element * expr,struct parse * cfile,isc_boolean_t * lose)2673 parse_numeric_expression(struct element *expr,
2674 struct parse *cfile,
2675 isc_boolean_t *lose)
2676 {
2677 /* Parse an expression... */
2678 if (!parse_expression(expr, cfile, lose, context_numeric,
2679 NULL, expr_none))
2680 return ISC_FALSE;
2681
2682 if (!is_numeric_expression(expr) &&
2683 !mapContains(expr, "variable-reference") &&
2684 !mapContains(expr, "funcall"))
2685 parse_error(cfile, "Expecting a numeric expression.");
2686 return ISC_TRUE;
2687 }
2688
2689 /* Parse a subexpression that does not contain a binary operator. */
2690
2691 isc_boolean_t
parse_non_binary(struct element * expr,struct parse * cfile,isc_boolean_t * lose,enum expression_context context)2692 parse_non_binary(struct element *expr,
2693 struct parse *cfile,
2694 isc_boolean_t *lose,
2695 enum expression_context context)
2696 {
2697 enum dhcp_token token;
2698 const char *val;
2699 struct element *nexp;
2700 struct element *arg;
2701 struct element *chain;
2702 struct string *data;
2703 struct comment *comment;
2704 struct option *option;
2705 isc_boolean_t known;
2706 unsigned len;
2707
2708 token = peek_token(&val, NULL, cfile);
2709
2710 /* Check for unary operators... */
2711 switch (token) {
2712 case CHECK:
2713 skip_token(&val, NULL, cfile);
2714 token = next_token(&val, NULL, cfile);
2715 if (token != STRING)
2716 parse_error(cfile, "string expected.");
2717 nexp = createString(makeString(-1, val));
2718 nexp->skip = ISC_TRUE;
2719 cfile->issue_counter++;
2720 mapSet(expr, nexp, "check");
2721 break;
2722
2723 case TOKEN_NOT:
2724 skip_token(&val, NULL, cfile);
2725 nexp = createMap();
2726
2727 if (!parse_non_binary(nexp, cfile, lose, context_boolean)) {
2728 if (!*lose)
2729 parse_error(cfile, "expression expected");
2730 *lose = ISC_TRUE;
2731 return ISC_FALSE;
2732 }
2733 if (!is_boolean_expression(nexp))
2734 parse_error(cfile, "boolean expression expected");
2735 if (!nexp->skip) {
2736 nexp->skip = ISC_TRUE;
2737 cfile->issue_counter++;
2738 }
2739 mapSet(expr, nexp, "not");
2740 break;
2741
2742 case LPAREN:
2743 skip_token(&val, NULL, cfile);
2744 if (!parse_expression(expr, cfile, lose, context,
2745 NULL, expr_none)) {
2746 if (!*lose)
2747 parse_error(cfile, "expression expected");
2748 *lose = ISC_TRUE;
2749 return ISC_FALSE;
2750 }
2751 token = next_token(&val, NULL, cfile);
2752 if (token != RPAREN)
2753 parse_error(cfile, "right paren expected");
2754 break;
2755
2756 case EXISTS:
2757 skip_token(&val, NULL, cfile);
2758 known = ISC_FALSE;
2759 option = parse_option_name(cfile, ISC_FALSE, &known);
2760 if (option == NULL) {
2761 *lose = ISC_TRUE;
2762 return ISC_FALSE;;
2763 }
2764 nexp = createMap();
2765 /* push infos to get it back trying to reduce it */
2766 mapSet(nexp,
2767 createString(makeString(-1, option->space->old)),
2768 "universe");
2769 mapSet(nexp,
2770 createString(makeString(-1, option->name)),
2771 "name");
2772 mapSet(nexp, createInt(option->code), "code");
2773 nexp->skip = ISC_TRUE;
2774 cfile->issue_counter++;
2775 mapSet(expr, nexp, "exists");
2776 break;
2777
2778 case STATIC:
2779 skip_token(&val, NULL, cfile);
2780 nexp = createNull();
2781 nexp->skip = ISC_TRUE;
2782 cfile->issue_counter++;
2783 mapSet(expr, nexp, "static");
2784 break;
2785
2786 case KNOWN:
2787 skip_token(&val, NULL, cfile);
2788 nexp = createNull();
2789 nexp->skip = ISC_TRUE;
2790 cfile->issue_counter++;
2791 mapSet(expr, nexp, "known");
2792 break;
2793
2794 case SUBSTRING:
2795 skip_token(&val, NULL, cfile);
2796 nexp = createMap();
2797 nexp->skip = ISC_TRUE;
2798 cfile->issue_counter++;
2799 mapSet(expr, nexp, "substring");
2800
2801 token = next_token(&val, NULL, cfile);
2802 if (token != LPAREN) {
2803 nolparen:
2804 parse_error(cfile, "left parenthesis expected.");
2805 }
2806
2807 arg = createMap();
2808 if (!parse_data_expression(arg, cfile, lose)) {
2809 nodata:
2810 if (!*lose)
2811 parse_error(cfile,
2812 "expecting data expression.");
2813 return ISC_FALSE;
2814 }
2815 mapSet(nexp, arg, "expression");
2816
2817 token = next_token(&val, NULL, cfile);
2818 if (token != COMMA) {
2819 nocomma:
2820 parse_error(cfile, "comma expected.");
2821 }
2822
2823 arg = createMap();
2824 if (!parse_numeric_expression(arg, cfile, lose)) {
2825 nonum:
2826 if (!*lose)
2827 parse_error(cfile,
2828 "expecting numeric expression.");
2829 return ISC_FALSE;
2830 }
2831 mapSet(nexp, arg, "offset");
2832
2833 token = next_token(&val, NULL, cfile);
2834 if (token != COMMA)
2835 goto nocomma;
2836
2837 arg = createMap();
2838 if (!parse_numeric_expression(arg, cfile, lose))
2839 goto nonum;
2840 mapSet(nexp, arg, "length");
2841
2842 token = next_token(&val, NULL, cfile);
2843 if (token != RPAREN) {
2844 norparen:
2845 parse_error(cfile, "right parenthesis expected.");
2846 }
2847 break;
2848
2849 case SUFFIX:
2850 skip_token(&val, NULL, cfile);
2851 nexp = createMap();
2852 nexp->skip = ISC_TRUE;
2853 cfile->issue_counter++;
2854 mapSet(expr, nexp, "suffix");
2855
2856 token = next_token(&val, NULL, cfile);
2857 if (token != LPAREN)
2858 goto nolparen;
2859
2860 arg = createMap();
2861 if (!parse_data_expression(arg, cfile, lose))
2862 goto nodata;
2863 mapSet(nexp, arg, "expression");
2864
2865 token = next_token(&val, NULL, cfile);
2866 if (token != COMMA)
2867 goto nocomma;
2868
2869 arg = createMap();
2870 if (!parse_numeric_expression(arg, cfile, lose))
2871 goto nonum;
2872 mapSet(nexp, arg, "length");
2873
2874 token = next_token(&val, NULL, cfile);
2875 if (token != RPAREN)
2876 goto norparen;
2877 break;
2878
2879 case LCASE:
2880 skip_token(&val, NULL, cfile);
2881 nexp = createMap();
2882
2883 token = next_token(&val, NULL, cfile);
2884 if (token != LPAREN)
2885 goto nolparen;
2886
2887 if (!parse_data_expression(nexp, cfile, lose))
2888 goto nodata;
2889
2890 token = next_token(&val, NULL, cfile);
2891 if (token != RPAREN)
2892 goto norparen;
2893 if (!nexp->skip) {
2894 nexp->skip = ISC_TRUE;
2895 cfile->issue_counter++;
2896 }
2897 mapSet(expr, nexp, "lowercase");
2898 break;
2899
2900 case UCASE:
2901 skip_token(&val, NULL, cfile);
2902 nexp = createMap();
2903
2904 token = next_token(&val, NULL, cfile);
2905 if (token != LPAREN)
2906 goto nolparen;
2907
2908 if (!parse_data_expression(nexp, cfile, lose))
2909 goto nodata;
2910
2911 token = next_token(&val, NULL, cfile);
2912 if (token != RPAREN)
2913 goto norparen;
2914 if (!nexp->skip) {
2915 nexp->skip = ISC_TRUE;
2916 cfile->issue_counter++;
2917 }
2918 mapSet(expr, nexp, "uppercase");
2919 break;
2920
2921 case CONCAT:
2922 skip_token(&val, NULL, cfile);
2923 nexp = createMap();
2924 nexp->skip = ISC_TRUE;
2925 cfile->issue_counter++;
2926 mapSet(expr, nexp, "concat");
2927
2928 token = next_token(&val, NULL, cfile);
2929 if (token != LPAREN)
2930 goto nolparen;
2931
2932 arg = createMap();
2933 if (!parse_data_expression(arg, cfile, lose))
2934 goto nodata;
2935 mapSet(nexp, arg, "left");
2936
2937 token = next_token(&val, NULL, cfile);
2938 if (token != COMMA)
2939 goto nocomma;
2940
2941 concat_another:
2942 arg = createMap();
2943 if (!parse_data_expression(arg, cfile, lose))
2944 goto nodata;
2945
2946 token = next_token(&val, NULL, cfile);
2947
2948 if (token == COMMA) {
2949 chain = createMap();
2950 mapSet(nexp, chain, "right");
2951 nexp = createMap();
2952 mapSet(chain, nexp, "concat");
2953 mapSet(nexp, arg, "left");
2954 goto concat_another;
2955 }
2956 mapSet(nexp, arg, "right");
2957
2958 if (token != RPAREN)
2959 goto norparen;
2960 break;
2961
2962 case BINARY_TO_ASCII:
2963 skip_token(&val, NULL, cfile);
2964 nexp = createMap();
2965 nexp->skip = ISC_TRUE;
2966 cfile->issue_counter++;
2967 mapSet(expr, nexp, "binary-to-ascii");
2968
2969 token = next_token(&val, NULL, cfile);
2970 if (token != LPAREN)
2971 goto nolparen;
2972
2973 arg = createMap();
2974 if (!parse_numeric_expression(arg, cfile, lose))
2975 goto nodata;
2976 mapSet(nexp, arg, "base");
2977
2978 token = next_token(&val, NULL, cfile);
2979 if (token != COMMA)
2980 goto nocomma;
2981
2982 arg = createMap();
2983 if (!parse_numeric_expression(arg, cfile, lose))
2984 goto nodata;
2985 mapSet(nexp, arg, "width");
2986
2987 token = next_token(&val, NULL, cfile);
2988 if (token != COMMA)
2989 goto nocomma;
2990
2991 arg = createMap();
2992 if (!parse_data_expression(arg, cfile, lose))
2993 goto nodata;
2994 mapSet(nexp, arg, "separator");
2995
2996 token = next_token(&val, NULL, cfile);
2997 if (token != COMMA)
2998 goto nocomma;
2999
3000 arg = createMap();
3001 if (!parse_data_expression(arg, cfile, lose))
3002 goto nodata;
3003 mapSet(nexp, arg, "buffer");
3004
3005 token = next_token(&val, NULL, cfile);
3006 if (token != RPAREN)
3007 goto norparen;
3008 break;
3009
3010 case REVERSE:
3011 skip_token(&val, NULL, cfile);
3012 nexp = createMap();
3013 nexp->skip = ISC_TRUE;
3014 cfile->issue_counter++;
3015 mapSet(expr, nexp, "reverse");
3016
3017 token = next_token(&val, NULL, cfile);
3018 if (token != LPAREN)
3019 goto nolparen;
3020
3021 arg = createMap();
3022 if (!(parse_numeric_expression(arg, cfile, lose)))
3023 goto nodata;
3024 mapSet(nexp, arg, "width");
3025
3026 token = next_token(&val, NULL, cfile);
3027 if (token != COMMA)
3028 goto nocomma;
3029
3030 arg = createMap();
3031 if (!(parse_data_expression(arg, cfile, lose)))
3032 goto nodata;
3033 mapSet(nexp, arg, "buffer");
3034
3035 token = next_token(&val, NULL, cfile);
3036 if (token != RPAREN)
3037 goto norparen;
3038 break;
3039
3040 case PICK:
3041 /* pick (a, b, c) actually produces an internal representation
3042 that looks like pick (a, pick (b, pick (c, nil))). */
3043 skip_token(&val, NULL, cfile);
3044 nexp = createList();
3045 nexp->skip = ISC_TRUE;
3046 cfile->issue_counter++;
3047 mapSet(expr, nexp, "pick-first-value");
3048
3049 token = next_token(&val, NULL, cfile);
3050 if (token != LPAREN)
3051 goto nolparen;
3052
3053 do {
3054 arg = createMap();
3055 if (!(parse_data_expression(arg, cfile, lose)))
3056 goto nodata;
3057 listPush(nexp, arg);
3058
3059 token = next_token(&val, NULL, cfile);
3060 } while (token == COMMA);
3061
3062 if (token != RPAREN)
3063 goto norparen;
3064 break;
3065
3066 case OPTION:
3067 case CONFIG_OPTION:
3068 skip_token(&val, NULL, cfile);
3069 known = ISC_FALSE;
3070 option = parse_option_name(cfile, ISC_FALSE, &known);
3071 if (option == NULL) {
3072 *lose = ISC_TRUE;
3073 return ISC_FALSE;
3074 }
3075 nexp = createMap();
3076 mapSet(nexp,
3077 createString(makeString(-1, option->space->old)),
3078 "universe");
3079 mapSet(nexp,
3080 createString(makeString(-1, option->name)),
3081 "name");
3082 mapSet(nexp, createInt(option->code), "code");
3083 nexp->skip = ISC_TRUE;
3084 cfile->issue_counter++;
3085 if (token == OPTION)
3086 mapSet(expr, nexp, "option");
3087 else {
3088 createComment("/// config-option is "
3089 "not supported by Kea");
3090 TAILQ_CONCAT(&nexp->comments, &cfile->comments);
3091 mapSet(expr, nexp, "config-option");
3092 }
3093 break;
3094
3095 case HARDWARE:
3096 skip_token(&val, NULL, cfile);
3097 nexp = createNull();
3098 nexp->skip = ISC_TRUE;
3099 cfile->issue_counter++;
3100 mapSet(expr, nexp, "hardware");
3101 break;
3102
3103 case LEASED_ADDRESS:
3104 skip_token(&val, NULL, cfile);
3105 nexp = createNull();
3106 nexp->skip = ISC_TRUE;
3107 cfile->issue_counter++;
3108 mapSet(expr, nexp, "leased-address");
3109 break;
3110
3111 case CLIENT_STATE:
3112 skip_token(&val, NULL, cfile);
3113 nexp = createNull();
3114 nexp->skip = ISC_TRUE;
3115 cfile->issue_counter++;
3116 mapSet(expr, nexp, "client-state");
3117 break;
3118
3119 case FILENAME:
3120 skip_token(&val, NULL, cfile);
3121 nexp = createNull();
3122 nexp->skip = ISC_TRUE;
3123 cfile->issue_counter++;
3124 mapSet(expr, nexp, "filename");
3125 break;
3126
3127 case SERVER_NAME:
3128 skip_token(&val, NULL, cfile);
3129 nexp = createNull();
3130 nexp->skip = ISC_TRUE;
3131 cfile->issue_counter++;
3132 mapSet(expr, nexp, "server-name");
3133 break;
3134
3135 case LEASE_TIME:
3136 skip_token(&val, NULL, cfile);
3137 nexp = createNull();
3138 nexp->skip = ISC_TRUE;
3139 cfile->issue_counter++;
3140 mapSet(expr, nexp, "lease-time");
3141 break;
3142
3143 case TOKEN_NULL:
3144 skip_token(&val, NULL, cfile);
3145 /* can look at context to return directly ""? */
3146 nexp = createNull();
3147 nexp->skip = ISC_TRUE;
3148 cfile->issue_counter++;
3149 mapSet(expr, nexp, "null");
3150 break;
3151
3152 case HOST_DECL_NAME:
3153 skip_token(&val, NULL, cfile);
3154 nexp = createNull();
3155 nexp->skip = ISC_TRUE;
3156 cfile->issue_counter++;
3157 mapSet(expr, nexp, "host-decl-name");
3158 break;
3159
3160 case PACKET:
3161 skip_token(&val, NULL, cfile);
3162 nexp = createMap();
3163 nexp->skip = ISC_TRUE;
3164 cfile->issue_counter++;
3165 mapSet(expr, nexp, "packet");
3166
3167 token = next_token(&val, NULL, cfile);
3168 if (token != LPAREN)
3169 goto nolparen;
3170
3171 arg = createMap();
3172 if (!parse_numeric_expression(arg, cfile, lose))
3173 goto nonum;
3174 mapSet(nexp, arg, "offset");
3175
3176 token = next_token(&val, NULL, cfile);
3177 if (token != COMMA)
3178 goto nocomma;
3179
3180 arg = createMap();
3181 if (!parse_numeric_expression(arg, cfile, lose))
3182 goto nonum;
3183 mapSet(nexp, arg, "length");
3184
3185 token = next_token(&val, NULL, cfile);
3186 if (token != RPAREN)
3187 goto norparen;
3188 break;
3189
3190 case STRING:
3191 skip_token(&val, &len, cfile);
3192 resetString(expr, makeString(len, val));
3193 break;
3194
3195 case EXTRACT_INT:
3196 skip_token(&val, NULL, cfile);
3197 nexp = createMap();
3198 nexp->skip = ISC_TRUE;
3199 cfile->issue_counter++;
3200
3201 token = next_token(&val, NULL, cfile);
3202 if (token != LPAREN)
3203 parse_error(cfile, "left parenthesis expected.");
3204
3205 if (!parse_data_expression(nexp, cfile, lose)) {
3206 if (!*lose)
3207 parse_error(cfile,
3208 "expecting data expression.");
3209 return ISC_FALSE;
3210 }
3211
3212 token = next_token(&val, NULL, cfile);
3213 if (token != COMMA)
3214 parse_error(cfile, "comma expected.");
3215
3216 token = next_token(&val, NULL, cfile);
3217 if (token != NUMBER)
3218 parse_error(cfile, "number expected.");
3219 switch (atoi(val)) {
3220 case 8:
3221 mapSet(expr, nexp, "extract-int8");
3222 break;
3223
3224 case 16:
3225 mapSet(expr, nexp, "extract-int16");
3226 break;
3227
3228 case 32:
3229 mapSet(expr, nexp, "extract-int32");
3230 break;
3231
3232 default:
3233 parse_error(cfile, "unsupported integer size %s", val);
3234 }
3235
3236 token = next_token(&val, NULL, cfile);
3237 if (token != RPAREN)
3238 parse_error(cfile, "right parenthesis expected.");
3239 break;
3240
3241 case ENCODE_INT:
3242 skip_token(&val, NULL, cfile);
3243 nexp = createMap();
3244 nexp->skip = ISC_TRUE;
3245 cfile->issue_counter++;
3246
3247 token = next_token(&val, NULL, cfile);
3248 if (token != LPAREN)
3249 parse_error(cfile, "left parenthesis expected.");
3250
3251 if (!parse_numeric_expression(nexp, cfile, lose))
3252 parse_error(cfile, "expecting numeric expression.");
3253
3254 token = next_token(&val, NULL, cfile);
3255 if (token != COMMA)
3256 parse_error(cfile, "comma expected.");
3257
3258 token = next_token(&val, NULL, cfile);
3259 if (token != NUMBER)
3260 parse_error(cfile, "number expected.");
3261 switch (atoi(val)) {
3262 case 8:
3263 mapSet(expr, nexp, "encode-int8");
3264 break;
3265
3266 case 16:
3267 mapSet(expr, nexp, "encode-int16");
3268 break;
3269
3270 case 32:
3271 mapSet(expr, nexp, "encode-int32");
3272 break;
3273
3274 default:
3275 parse_error(cfile, "unsupported integer size %s", val);
3276 }
3277
3278 token = next_token(&val, NULL, cfile);
3279 if (token != RPAREN)
3280 parse_error(cfile, "right parenthesis expected.");
3281 break;
3282
3283 case NUMBER:
3284 /* If we're in a numeric context, this should just be a
3285 number, by itself. */
3286 if (context == context_numeric ||
3287 context == context_data_or_numeric) {
3288 skip_token(&val, NULL, cfile);
3289 /* can also return a const-int */
3290 resetInt(expr, atoi(val));
3291 break;
3292 }
3293
3294 case NUMBER_OR_NAME:
3295 /* Return a const-data to make a difference with
3296 a string literal. createHexa() adds 0x */
3297 mapSet(expr, createHexa(parse_hexa(cfile)), "const-data");
3298 break;
3299
3300 case NS_FORMERR:
3301 skip_token(&val, NULL, cfile);
3302 #ifndef FORMERR
3303 #define FORMERR 1
3304 #endif
3305 resetInt(expr, FORMERR);
3306 comment = createComment("/// constant FORMERR(1)");
3307 TAILQ_INSERT_TAIL(&expr->comments, comment);
3308 break;
3309
3310 case NS_NOERROR:
3311 skip_token(&val, NULL, cfile);
3312 #ifndef ISC_R_SUCCESS
3313 #define ISC_R_SUCCESS 0
3314 #endif
3315 resetInt(expr, ISC_R_SUCCESS);
3316 comment = createComment("/// constant ISC_R_SUCCESS(0)");
3317 TAILQ_INSERT_TAIL(&expr->comments, comment);
3318 break;
3319
3320 case NS_NOTAUTH:
3321 skip_token(&val, NULL, cfile);
3322 #ifndef DHCP_R_NOTAUTH
3323 #define DHCP_R_NOTAUTH ((6 << 16) + 21)
3324 #endif
3325 resetInt(expr, DHCP_R_NOTAUTH);
3326 comment = createComment("/// constant DHCP_R_NOTAUTH(393237)");
3327 TAILQ_INSERT_TAIL(&expr->comments, comment);
3328 break;
3329
3330 case NS_NOTIMP:
3331 skip_token(&val, NULL, cfile);
3332 #ifndef ISC_R_NOTIMPLEMENTED
3333 #define ISC_R_NOTIMPLEMENTED 27
3334 #endif
3335 resetInt(expr, ISC_R_NOTIMPLEMENTED);
3336 comment = createComment("/// constant ISC_R_NOTIMPLEMENTED(27)");
3337 TAILQ_INSERT_TAIL(&expr->comments, comment);
3338 break;
3339
3340 case NS_NOTZONE:
3341 skip_token(&val, NULL, cfile);
3342 #ifndef DHCP_R_NOTZONE
3343 #define DHCP_R_NOTZONE ((6 << 16) + 22)
3344 #endif
3345 resetInt(expr, DHCP_R_NOTZONE);
3346 comment = createComment("/// constant DHCP_R_NOTZONE(393238)");
3347 TAILQ_INSERT_TAIL(&expr->comments, comment);
3348 break;
3349
3350 case NS_NXDOMAIN:
3351 skip_token(&val, NULL, cfile);
3352 #ifndef DHCP_R_NXDOMAIN
3353 #define DHCP_R_NXDOMAIN ((6 << 16) + 15)
3354 #endif
3355 resetInt(expr, DHCP_R_NXDOMAIN);
3356 comment = createComment("/// constant DHCP_R_NXDOMAIN(393231)");
3357 TAILQ_INSERT_TAIL(&expr->comments, comment);
3358 break;
3359
3360 case NS_NXRRSET:
3361 skip_token(&val, NULL, cfile);
3362 #ifndef DHCP_R_NXRRSET
3363 #define DHCP_R_NXRRSET ((6 << 16) + 20)
3364 #endif
3365 resetInt(expr, DHCP_R_NXRRSET);
3366 comment = createComment("/// constant DHCP_R_NXRRSET(393236)");
3367 TAILQ_INSERT_TAIL(&expr->comments, comment);
3368 break;
3369
3370 case NS_REFUSED:
3371 skip_token(&val, NULL, cfile);
3372 #ifndef DHCP_R_REFUSED
3373 #define DHCP_R_REFUSED ((6 << 16) + 17)
3374 #endif
3375 resetInt(expr, DHCP_R_REFUSED);
3376 comment = createComment("/// constant DHCP_R_REFUSED(393233)");
3377 TAILQ_INSERT_TAIL(&expr->comments, comment);
3378 break;
3379
3380 case NS_SERVFAIL:
3381 skip_token(&val, NULL, cfile);
3382 #ifndef DHCP_R_SERVFAIL
3383 #define DHCP_R_SERVFAIL ((6 << 16) + 14)
3384 #endif
3385 resetInt(expr, DHCP_R_SERVFAIL);
3386 comment = createComment("/// constant DHCP_R_SERVFAIL(393230)");
3387 TAILQ_INSERT_TAIL(&expr->comments, comment);
3388 break;
3389
3390 case NS_YXDOMAIN:
3391 skip_token(&val, NULL, cfile);
3392 #ifndef DHCP_R_YXDOMAIN
3393 #define DHCP_R_YXDOMAIN ((6 << 16) + 18)
3394 #endif
3395 resetInt(expr, DHCP_R_YXDOMAIN);
3396 comment = createComment("/// constant DHCP_R_YXDOMAIN(393234)");
3397 TAILQ_INSERT_TAIL(&expr->comments, comment);
3398 break;
3399
3400 case NS_YXRRSET:
3401 skip_token(&val, NULL, cfile);
3402 #ifndef DHCP_R_YXRRSET
3403 #define DHCP_R_YXRRSET ((6 << 16) + 19)
3404 #endif
3405 resetInt(expr, DHCP_R_YXRRSET);
3406 comment = createComment("/// constant DHCP_R_YXRRSET(393235)");
3407 TAILQ_INSERT_TAIL(&expr->comments, comment);
3408 break;
3409
3410 case BOOTING:
3411 skip_token(&val, NULL, cfile);
3412 #ifndef S_INIT
3413 #define S_INIT 2
3414 #endif
3415 resetInt(expr, S_INIT);
3416 comment = createComment("/// constant S_INIT(2)");
3417 TAILQ_INSERT_TAIL(&expr->comments, comment);
3418 break;
3419
3420 case REBOOT:
3421 skip_token(&val, NULL, cfile);
3422 #ifndef S_REBOOTING
3423 #define S_REBOOTING 1
3424 #endif
3425 resetInt(expr, S_REBOOTING);
3426 comment = createComment("/// constant S_REBOOTING(1)");
3427 TAILQ_INSERT_TAIL(&expr->comments, comment);
3428 break;
3429
3430 case SELECT:
3431 skip_token(&val, NULL, cfile);
3432 #ifndef S_SELECTING
3433 #define S_SELECTING 3
3434 #endif
3435 resetInt(expr, S_SELECTING);
3436 comment = createComment("/// constant S_SELECTING(3)");
3437 TAILQ_INSERT_TAIL(&expr->comments, comment);
3438 break;
3439
3440 case REQUEST:
3441 skip_token(&val, NULL, cfile);
3442 #ifndef S_REQUESTING
3443 #define S_REQUESTING 4
3444 #endif
3445 resetInt(expr, S_REQUESTING);
3446 comment = createComment("/// constant S_REQUESTING(4)");
3447 TAILQ_INSERT_TAIL(&expr->comments, comment);
3448 break;
3449
3450 case BOUND:
3451 skip_token(&val, NULL, cfile);
3452 #ifndef S_BOUND
3453 #define S_BOUND 5
3454 #endif
3455 resetInt(expr, S_BOUND);
3456 comment = createComment("/// constant S_BOUND(5)");
3457 TAILQ_INSERT_TAIL(&expr->comments, comment);
3458 break;
3459
3460 case RENEW:
3461 skip_token(&val, NULL, cfile);
3462 #ifndef S_RENEWING
3463 #define S_RENEWING 6
3464 #endif
3465 resetInt(expr, S_RENEWING);
3466 comment = createComment("/// constant S_RENEWING(6)");
3467 TAILQ_INSERT_TAIL(&expr->comments, comment);
3468 break;
3469
3470 case REBIND:
3471 skip_token(&val, NULL, cfile);
3472 #ifndef S_REBINDING
3473 #define S_REBINDING 7
3474 #endif
3475 resetInt(expr, S_REBINDING);
3476 comment = createComment("/// constant S_REBINDING(7)");
3477 TAILQ_INSERT_TAIL(&expr->comments, comment);
3478 break;
3479
3480 case DEFINED:
3481 skip_token(&val, NULL, cfile);
3482 token = next_token(&val, NULL, cfile);
3483 if (token != LPAREN)
3484 goto nolparen;
3485
3486 token = next_token(&val, NULL, cfile);
3487 if (token != NAME && token != NUMBER_OR_NAME)
3488 parse_error(cfile, "%s can't be a variable name", val);
3489
3490 nexp = createString(makeString(-1, val));
3491 nexp->skip = ISC_TRUE;
3492 cfile->issue_counter++;
3493 mapSet(expr, nexp, "variable-exists");
3494 token = next_token(&val, NULL, cfile);
3495 if (token != RPAREN)
3496 goto norparen;
3497 break;
3498
3499 /* This parses 'gethostname()'. */
3500 case GETHOSTNAME:
3501 skip_token(&val, NULL, cfile);
3502 nexp = createNull();
3503 nexp->skip = ISC_TRUE;
3504 cfile->issue_counter++;
3505 mapSet(expr, nexp, "gethostname");
3506
3507 token = next_token(NULL, NULL, cfile);
3508 if (token != LPAREN)
3509 goto nolparen;
3510
3511 token = next_token(NULL, NULL, cfile);
3512 if (token != RPAREN)
3513 goto norparen;
3514 break;
3515
3516 case GETHOSTBYNAME:
3517 skip_token(&val, NULL, cfile);
3518 token = next_token(NULL, NULL, cfile);
3519 if (token != LPAREN)
3520 goto nolparen;
3521
3522 /* The argument is a quoted string. */
3523 token = next_token(&val, NULL, cfile);
3524 if (token != STRING)
3525 parse_error(cfile, "Expecting quoted literal: "
3526 "\"foo.example.com\"");
3527 nexp = createString(makeString(-1, val));
3528 nexp->skip = ISC_TRUE;
3529 cfile->issue_counter++;
3530 mapSet(expr, nexp, "gethostbyname");
3531
3532 token = next_token(NULL, NULL, cfile);
3533 if (token != RPAREN)
3534 goto norparen;
3535 break;
3536
3537 case V6RELAY:
3538 skip_token(&val, NULL, cfile);
3539 nexp = createMap();
3540 nexp->skip = ISC_TRUE;
3541 cfile->issue_counter++;
3542 mapSet(expr, nexp, "v6relay");
3543
3544 token = next_token(&val, NULL, cfile);
3545 if (token != LPAREN)
3546 goto nolparen;
3547
3548 arg = createMap();
3549 if (!parse_numeric_expression(arg, cfile, lose))
3550 goto nodata;
3551 mapSet(nexp, arg, "relay");
3552
3553 token = next_token(&val, NULL, cfile);
3554 if (token != COMMA)
3555 goto nocomma;
3556
3557 arg = createMap();
3558 if (!parse_data_expression(arg, cfile, lose))
3559 goto nodata;
3560 mapSet(nexp, arg, "relay-option");
3561
3562 token = next_token(&val, NULL, cfile);
3563
3564 if (token != RPAREN)
3565 goto norparen;
3566 break;
3567
3568 /* Not a valid start to an expression... */
3569 default:
3570 if (token != NAME && token != NUMBER_OR_NAME)
3571 return ISC_FALSE;
3572
3573 skip_token(&val, NULL, cfile);
3574
3575 /* Save the name of the variable being referenced. */
3576 data = makeString(-1, val);
3577
3578 /* Simple variable reference, as far as we can tell. */
3579 token = peek_token(&val, NULL, cfile);
3580 if (token != LPAREN) {
3581 nexp = createString(data);
3582 nexp->skip = ISC_TRUE;
3583 cfile->issue_counter++;
3584 mapSet(expr, nexp, "variable-reference");
3585 break;
3586 }
3587
3588 skip_token(&val, NULL, cfile);
3589 nexp = createMap();
3590 nexp->skip = ISC_TRUE;
3591 cfile->issue_counter++;
3592 mapSet(expr, nexp, "funcall");
3593 chain = createString(data);
3594 mapSet(nexp, chain, "name");
3595
3596 /* Now parse the argument list. */
3597 chain = createList();
3598 do {
3599 arg = createMap();
3600 if (!parse_expression(arg, cfile, lose, context_any,
3601 NULL, expr_none)) {
3602 if (!*lose)
3603 parse_error(cfile,
3604 "expecting expression.");
3605 skip_to_semi(cfile);
3606 return ISC_FALSE;
3607 }
3608 listPush(chain, arg);
3609 token = next_token(&val, NULL, cfile);
3610 } while (token == COMMA);
3611 if (token != RPAREN)
3612 parse_error(cfile, "Right parenthesis expected.");
3613 mapSet(nexp, chain, "arguments");
3614 break;
3615 }
3616 return ISC_TRUE;
3617 }
3618
3619 /* Parse an expression. */
3620
3621 isc_boolean_t
parse_expression(struct element * expr,struct parse * cfile,isc_boolean_t * lose,enum expression_context context,struct element * lhs,enum expr_op binop)3622 parse_expression(struct element *expr, struct parse *cfile,
3623 isc_boolean_t *lose, enum expression_context context,
3624 struct element *lhs, enum expr_op binop)
3625 {
3626 enum dhcp_token token;
3627 const char *val;
3628 struct element *rhs, *tmp;
3629 enum expr_op next_op;
3630 enum expression_context
3631 lhs_context = context_any,
3632 rhs_context = context_any;
3633 const char *binop_name;
3634
3635 new_rhs:
3636 rhs = createMap();
3637 if (!parse_non_binary(rhs, cfile, lose, context)) {
3638 /* If we already have a left-hand side, then it's not
3639 okay for there not to be a right-hand side here, so
3640 we need to flag it as an error. */
3641 if (lhs)
3642 if (!*lose)
3643 parse_error(cfile,
3644 "expecting right-hand side.");
3645 return ISC_FALSE;
3646 }
3647
3648 /* At this point, rhs contains either an entire subexpression,
3649 or at least a left-hand-side. If we do not see a binary token
3650 as the next token, we're done with the expression. */
3651
3652 token = peek_token(&val, NULL, cfile);
3653 switch (token) {
3654 case BANG:
3655 skip_token(&val, NULL, cfile);
3656 token = peek_token(&val, NULL, cfile);
3657 if (token != EQUAL)
3658 parse_error(cfile, "! in boolean context without =");
3659 next_op = expr_not_equal;
3660 context = expression_context(rhs);
3661 break;
3662
3663 case EQUAL:
3664 next_op = expr_equal;
3665 context = expression_context(rhs);
3666 break;
3667
3668 case TILDE:
3669 skip_token(&val, NULL, cfile);
3670 token = peek_token(&val, NULL, cfile);
3671
3672 if (token == TILDE)
3673 next_op = expr_iregex_match;
3674 else if (token == EQUAL)
3675 next_op = expr_regex_match;
3676 else
3677 parse_error(cfile, "expecting ~= or ~~ operator");
3678
3679 context = expression_context(rhs);
3680 break;
3681
3682 case AND:
3683 next_op = expr_and;
3684 context = expression_context(rhs);
3685 break;
3686
3687 case OR:
3688 next_op = expr_or;
3689 context = expression_context(rhs);
3690 break;
3691
3692 case PLUS:
3693 next_op = expr_add;
3694 context = expression_context(rhs);
3695 break;
3696
3697 case MINUS:
3698 next_op = expr_subtract;
3699 context = expression_context(rhs);
3700 break;
3701
3702 case SLASH:
3703 next_op = expr_divide;
3704 context = expression_context(rhs);
3705 break;
3706
3707 case ASTERISK:
3708 next_op = expr_multiply;
3709 context = expression_context(rhs);
3710 break;
3711
3712 case PERCENT:
3713 next_op = expr_remainder;
3714 context = expression_context(rhs);
3715 break;
3716
3717 case AMPERSAND:
3718 next_op = expr_binary_and;
3719 context = expression_context(rhs);
3720 break;
3721
3722 case PIPE:
3723 next_op = expr_binary_or;
3724 context = expression_context(rhs);
3725 break;
3726
3727 case CARET:
3728 next_op = expr_binary_xor;
3729 context = expression_context(rhs);
3730 break;
3731
3732 default:
3733 next_op = expr_none;
3734 }
3735
3736 /* If we have no lhs yet, we just parsed it. */
3737 if (!lhs) {
3738 /* If there was no operator following what we just parsed,
3739 then we're done - return it. */
3740 if (next_op == expr_none) {
3741 resetBy(expr, rhs);
3742 return ISC_TRUE;
3743 }
3744
3745 lhs = rhs;
3746 rhs = NULL;
3747 binop = next_op;
3748 skip_token(&val, NULL, cfile);
3749 goto new_rhs;
3750 }
3751
3752 /* If the next binary operator is of greater precedence than the
3753 * current operator, then rhs we have parsed so far is actually
3754 * the lhs of the next operator. To get this value, we have to
3755 * recurse.
3756 */
3757 if (binop != expr_none && next_op != expr_none &&
3758 op_precedence(binop, next_op) < 0) {
3759
3760 /* Eat the subexpression operator token, which we pass to
3761 * parse_expression...we only peek()'d earlier.
3762 */
3763 skip_token(&val, NULL, cfile);
3764
3765 /* Continue parsing of the right hand side with that token. */
3766 tmp = rhs;
3767 rhs = createMap();
3768 if (!parse_expression(rhs, cfile, lose, op_context(next_op),
3769 tmp, next_op)) {
3770 if (!*lose)
3771 parse_error(cfile,
3772 "expecting a subexpression");
3773 return ISC_FALSE;
3774 }
3775 next_op = expr_none;
3776 }
3777
3778 binop_name = "none";
3779 if (binop != expr_none) {
3780 rhs_context = expression_context(rhs);
3781 lhs_context = expression_context(lhs);
3782
3783 if ((rhs_context != context_any) &&
3784 (lhs_context != context_any) &&
3785 (rhs_context != lhs_context))
3786 parse_error(cfile, "illegal expression relating "
3787 "different types");
3788
3789 switch (binop) {
3790 case expr_not_equal:
3791 binop_name = "not-equal";
3792 goto data_numeric;
3793 case expr_equal:
3794 binop_name = "equal";
3795 data_numeric:
3796 if ((rhs_context != context_data_or_numeric) &&
3797 (rhs_context != context_data) &&
3798 (rhs_context != context_numeric) &&
3799 (rhs_context != context_any))
3800 parse_error(cfile, "expecting data/numeric "
3801 "expression");
3802 break;
3803
3804 case expr_iregex_match:
3805 binop_name = "iregex-match";
3806 break;
3807
3808 case expr_regex_match:
3809 binop_name = "regex-match";
3810 if (expression_context(rhs) != context_data)
3811 parse_error(cfile,
3812 "expecting data expression");
3813 break;
3814
3815 case expr_and:
3816 binop_name = "and";
3817 goto boolean;
3818 case expr_or:
3819 binop_name = "or";
3820 boolean:
3821 if ((rhs_context != context_boolean) &&
3822 (rhs_context != context_any)) {
3823 parse_error(cfile,
3824 "expecting boolean expressions");
3825 }
3826 break;
3827
3828 case expr_add:
3829 binop_name = "add";
3830 goto numeric;
3831 case expr_subtract:
3832 binop_name = "subtract";
3833 goto numeric;
3834 case expr_divide:
3835 binop_name = "divide";
3836 goto numeric;
3837 case expr_multiply:
3838 binop_name = "multiply";
3839 goto numeric;
3840 case expr_remainder:
3841 binop_name = "remainder";
3842 goto numeric;
3843 case expr_binary_and:
3844 binop_name = "binary-and";
3845 goto numeric;
3846 case expr_binary_or:
3847 binop_name = "binary-or";
3848 goto numeric;
3849 case expr_binary_xor:
3850 binop_name = "binary-xor";
3851 numeric:
3852 if ((rhs_context != context_numeric) &&
3853 (rhs_context != context_any))
3854 parse_error(cfile,
3855 "expecting numeric expressions");
3856 break;
3857
3858 default:
3859 break;
3860 }
3861 }
3862
3863 /* Now, if we didn't find a binary operator, we're done parsing
3864 this subexpression, so combine it with the preceding binary
3865 operator and return the result. */
3866 if (next_op == expr_none) {
3867 tmp = createMap();
3868 tmp->skip = ISC_TRUE;
3869 mapSet(expr, tmp, binop_name);
3870 /* All the binary operators' data union members
3871 are the same, so we'll cheat and use the member
3872 for the equals operator. */
3873 mapSet(tmp, lhs, "left");
3874 mapSet(tmp, rhs, "right");
3875 return ISC_TRUE;;
3876 }
3877
3878 /* Eat the operator token - we now know it was a binary operator... */
3879 skip_token(&val, NULL, cfile);
3880
3881 /* Now combine the LHS and the RHS using binop. */
3882 tmp = createMap();
3883 tmp->skip = ISC_TRUE;
3884
3885 /* Store the LHS and RHS. */
3886 mapSet(tmp, lhs, "left");
3887 mapSet(tmp, rhs, "right");
3888
3889 lhs = createMap();
3890 mapSet(lhs, tmp, binop_name);
3891
3892 tmp = NULL;
3893 rhs = NULL;
3894
3895 binop = next_op;
3896 goto new_rhs;
3897 }
3898
3899 /* Escape embedded commas, detected heading and leading space */
3900 struct string *
escape_option_string(unsigned len,const char * val,isc_boolean_t * require_binary,isc_boolean_t * modified)3901 escape_option_string(unsigned len, const char *val,
3902 isc_boolean_t *require_binary,
3903 isc_boolean_t *modified)
3904 {
3905 struct string *result;
3906 struct string *add;
3907 unsigned i;
3908 char s[2];
3909
3910 result = allocString();
3911 add = allocString();
3912 if ((len > 0) && (isspace(val[0]) || isspace(val[len - 1]))) {
3913 *require_binary = ISC_TRUE;
3914 return result;
3915 }
3916 for (i = 0; i < len; i++) {
3917 if (val[i] == ',') {
3918 add->length = 2;
3919 add->content = "\\,";
3920 *modified = ISC_TRUE;
3921 } else {
3922 add->length = 1;
3923 s[0] = val[i];
3924 s[1] = 0;
3925 add->content = s;
3926 }
3927 concatString(result, add);
3928 }
3929 free(add);
3930 return result;
3931 }
3932
3933 isc_boolean_t
parse_option_data(struct element * expr,struct parse * cfile,struct option * option)3934 parse_option_data(struct element *expr,
3935 struct parse *cfile,
3936 struct option *option)
3937 {
3938 const char *val;
3939 const char *fmt;
3940 enum dhcp_token token;
3941 unsigned len;
3942 struct string *data;
3943 struct string *saved;
3944 struct string *item;
3945 struct element *elem;
3946 struct comment *comment;
3947 isc_boolean_t require_binary = ISC_FALSE;
3948 isc_boolean_t canon_bool = ISC_FALSE;
3949 isc_boolean_t modified = ISC_FALSE;
3950
3951 /* Save the initial content */
3952 saved = allocString();
3953 save_parse_state(cfile);
3954 for (;;) {
3955 token = next_raw_token(&val, &len, cfile);
3956 if ((token == SEMI) || (token == END_OF_FILE))
3957 break;
3958 item = makeString(len, val);
3959 if (token == STRING) {
3960 appendString(saved, "\"");
3961 concatString(saved, item);
3962 appendString(saved, "\"");
3963 } else
3964 concatString(saved, item);
3965 }
3966 restore_parse_state(cfile);
3967
3968 elem = createString(saved);
3969 elem->skip = ISC_TRUE;
3970 mapSet(expr, elem, "original-data");
3971
3972 /* Check for binary case */
3973
3974 fmt = option->format;
3975
3976 if ((fmt == NULL) || (*fmt == 0))
3977 parse_error(cfile, "unknown format for option %s.%s\n",
3978 option->space->name, option->name);
3979
3980 if ((strchr(fmt, 'Y') != NULL) || (strchr(fmt, 'A') != NULL) ||
3981 (strchr(fmt, 'E') != NULL) || (strchr(fmt, 'o') != NULL) ||
3982 (*fmt == 'X') || (*fmt == 'u'))
3983 return parse_option_binary(expr, cfile, option, ISC_FALSE);
3984
3985 if (strchr(fmt, 'N') != NULL)
3986 parse_error(cfile, "unsupported format %s for option %s.%s\n",
3987 fmt, option->space->name, option->name);
3988
3989 data = allocString();
3990
3991 save_parse_state(cfile);
3992 /* Just collect data expecting ISC DHCP and Kea are compatible */
3993 do {
3994 /* Set fmt one char back for 'a'. */
3995 if ((fmt != option->format) && (*fmt == 'a'))
3996 fmt -= 1;
3997
3998 do {
3999 if (*fmt == 'a')
4000 break;
4001 if (data->length != 0)
4002 appendString(data, ", ");
4003 item = parse_option_token(cfile, fmt, &require_binary,
4004 &canon_bool, &modified);
4005 if ((*fmt == 'D') && (fmt[1] == 'c'))
4006 fmt++;
4007 if (require_binary) {
4008 restore_parse_state(cfile);
4009 return parse_option_binary(expr, cfile, option,
4010 item == NULL);
4011 }
4012 if (item == NULL)
4013 parse_error(cfile, "parse_option_data failed");
4014 concatString(data, item);
4015 fmt++;
4016 } while (*fmt != '\0');
4017
4018 if (*fmt == 'a') {
4019 token = peek_token(&val, NULL, cfile);
4020 /* Comma means: continue with next element in array */
4021 if (token == COMMA) {
4022 skip_token(&val, NULL, cfile);
4023 continue;
4024 }
4025 /* no comma: end of array.
4026 end of string means: leave the loop */
4027 if (fmt[1] == '\0')
4028 break;
4029 /* 'a' means: go on with next char */
4030 if (*fmt == 'a') {
4031 fmt++;
4032 continue;
4033 }
4034 }
4035 } while (*fmt == 'a');
4036
4037 if (!modified || eqString(saved, data))
4038 mapRemove(expr, "original-data");
4039
4040 elem = createString(data);
4041 if (canon_bool) {
4042 comment = createComment("/// canonized booleans to "
4043 "lowercase true or false");
4044 TAILQ_INSERT_TAIL(&elem->comments, comment);
4045 }
4046 mapSet(expr, elem, "data");
4047
4048 return ISC_TRUE;
4049 }
4050
4051 isc_boolean_t
parse_option_binary(struct element * expr,struct parse * cfile,struct option * option,isc_boolean_t ambiguous)4052 parse_option_binary(struct element *expr, struct parse *cfile,
4053 struct option *option, isc_boolean_t ambiguous)
4054 {
4055 const char *val;
4056 const char *fmt;
4057 enum dhcp_token token;
4058 struct string *data;
4059 struct string *item;
4060 struct element *elem;
4061 struct comment *comment;
4062 const char *g;
4063
4064 data = allocString();
4065 fmt = option->format;
4066
4067 mapSet(expr, createBool(ISC_FALSE), "csv-format");
4068
4069 /* Just collect data expecting ISC DHCP and Kea are compatible */
4070 do {
4071 /* Set fmt to start of format for 'A' and one char back
4072 * for 'a'.
4073 */
4074 if ((fmt != option->format) && (*fmt == 'a'))
4075 fmt -= 1;
4076 else if (*fmt == 'A')
4077 fmt = option->format;
4078
4079 do {
4080 if ((*fmt == 'A') || (*fmt == 'a'))
4081 break;
4082 if (*fmt == 'o') {
4083 /* consume the optional flag */
4084 fmt++;
4085 continue;
4086 }
4087
4088 if (fmt[1] == 'o') {
4089 /*
4090 * A value for the current format is
4091 * optional - check to see if the next
4092 * token is a semi-colon if so we don't
4093 * need to parse it and doing so would
4094 * consume the semi-colon which our
4095 * caller is expecting to parse
4096 */
4097 token = peek_token(&val, NULL, cfile);
4098 if (token == SEMI) {
4099 fmt++;
4100 continue;
4101 }
4102 }
4103
4104 item = parse_option_token_binary(cfile, fmt);
4105 switch (*fmt) {
4106 case 'E':
4107 g = strchr(fmt, '.');
4108 if (g == NULL)
4109 parse_error(cfile,
4110 "malformed encapsulation "
4111 "format (bug!)");
4112 fmt = g;
4113 break;
4114 case 'D':
4115 if (fmt[1] == 'c')
4116 fmt++;
4117 break;
4118 case 'N':
4119 g = strchr(fmt, '.');
4120 if (g == NULL)
4121 parse_error(cfile,
4122 "malformed enumeration "
4123 "format (bug!)");
4124 fmt = g;
4125 break;
4126 }
4127 if (item != NULL)
4128 concatString(data, item);
4129 else if (fmt[1] != 'o')
4130 parse_error(cfile, "parse_option_token_binary "
4131 "failed");
4132 fmt++;
4133 } while (*fmt != '\0');
4134
4135 if ((*fmt == 'A') || (*fmt == 'a')) {
4136 token = peek_token(&val, NULL, cfile);
4137 /* Comma means: continue with next element in array */
4138 if (token == COMMA) {
4139 skip_token(&val, NULL, cfile);
4140 continue;
4141 }
4142 /* no comma: end of array.
4143 'A' or end of string means: leave the loop */
4144 if ((*fmt == 'A') || (fmt[1] == '\0'))
4145 break;
4146 /* 'a' means: go on with next char */
4147 if (*fmt == 'a') {
4148 fmt++;
4149 continue;
4150 }
4151 }
4152 } while ((*fmt == 'A') || (*fmt == 'a'));
4153
4154 elem = mapGet(expr, "original-data");
4155 if ((elem != NULL) && eqString(stringValue(elem), data))
4156 mapRemove(expr, "original-data");
4157
4158 elem = createString(data);
4159 if (ambiguous) {
4160 comment = createComment("/// Please consider to change "
4161 "last type in the record to binary");
4162 TAILQ_INSERT_TAIL(&elem->comments, comment);
4163 comment = createComment("/// Reference Kea #246");
4164 TAILQ_INSERT_TAIL(&elem->comments, comment);
4165 expr->skip = ISC_TRUE;
4166 cfile->issue_counter++;
4167 }
4168 mapSet(expr, elem, "data");
4169
4170 return ISC_TRUE;
4171 }
4172
4173 struct string *
parse_option_textbin(struct parse * cfile,struct option * option)4174 parse_option_textbin(struct parse *cfile, struct option *option)
4175 {
4176 struct element *expr;
4177 struct element *data;
4178 const char *fmt;
4179
4180 expr = createMap();
4181 fmt = option->format;
4182
4183 if ((fmt == NULL) || (*fmt == 0))
4184 parse_error(cfile, "unknown format for option %s.%s\n",
4185 option->space->name, option->name);
4186
4187 if (strcmp(fmt, "t") != 0) {
4188 if (!parse_option_binary(expr, cfile, option, ISC_FALSE))
4189 parse_error(cfile, "can't parse binary option data");
4190 data = mapGet(expr, "data");
4191 if (data == NULL)
4192 parse_error(cfile, "can't get binary option data");
4193 if (data->type != ELEMENT_STRING)
4194 parse_error(cfile, "option data must be binary");
4195 return stringValue(data);
4196 }
4197
4198 if (!parse_option_data(expr, cfile, option))
4199 parse_error(cfile, "can't parse text option data");
4200 data = mapGet(expr, "data");
4201 if (data == NULL)
4202 parse_error(cfile, "can't get test option data");
4203 if (data->type != ELEMENT_STRING)
4204 parse_error(cfile, "option data must be a string");
4205 return quote(stringValue(data));
4206 }
4207
4208 /* option-statement :== identifier DOT identifier <syntax> SEMI
4209 | identifier <syntax> SEMI
4210
4211 Option syntax is handled specially through format strings, so it
4212 would be painful to come up with BNF for it. However, it always
4213 starts as above and ends in a SEMI. */
4214
4215 isc_boolean_t
parse_option_statement(struct element * result,struct parse * cfile,struct option * option,enum statement_op op)4216 parse_option_statement(struct element *result,
4217 struct parse *cfile,
4218 struct option *option,
4219 enum statement_op op)
4220 {
4221 const char *val;
4222 enum dhcp_token token;
4223 struct element *expr;
4224 struct element *opt_data;
4225 struct element *opt_data_list;
4226 isc_boolean_t lose;
4227 size_t where;
4228
4229 if (option->space == space_lookup("server"))
4230 return parse_config_statement(result, cfile, option, op);
4231
4232 opt_data = createMap();
4233 TAILQ_CONCAT(&opt_data->comments, &cfile->comments);
4234 mapSet(opt_data,
4235 createString(makeString(-1, option->space->name)), "space");
4236 mapSet(opt_data, createString(makeString(-1, option->name)), "name");
4237 mapSet(opt_data, createInt(option->code), "code");
4238 if (option->status == kea_unknown) {
4239 opt_data->skip = ISC_TRUE;
4240 cfile->issue_counter++;
4241 }
4242 if (op != supersede_option_statement) {
4243 struct string *msg;
4244 struct comment *comment;
4245
4246 msg = makeString(-1, "/// Kea does not support option data ");
4247 appendString(msg, "set variants (");
4248 switch (op) {
4249 case send_option_statement:
4250 appendString(msg, "send");
4251 break;
4252 case supersede_option_statement:
4253 appendString(msg, "supersede");
4254 break;
4255 case default_option_statement:
4256 appendString(msg, "default");
4257 break;
4258 case prepend_option_statement:
4259 appendString(msg, "prepend");
4260 break;
4261 case append_option_statement:
4262 appendString(msg, "append");
4263 break;
4264 default:
4265 appendString(msg, "???");
4266 break;
4267 }
4268 appendString(msg, ")");
4269 comment = createComment(msg->content);
4270 TAILQ_INSERT_TAIL(&opt_data->comments, comment);
4271 }
4272
4273 /* Setting PRL is a standard hack */
4274 if ((option->space == space_lookup("dhcp")) &&
4275 (option->code == 55)) {
4276 struct comment *comment;
4277
4278 comment = createComment("/// Possible PRL hack");
4279 TAILQ_INSERT_TAIL(&opt_data->comments, comment);
4280 comment = createComment("/// Consider setting \"always-send\" "
4281 "to true when setting data "
4282 "for relevant options, cf Kea #250");
4283 TAILQ_INSERT_TAIL(&opt_data->comments, comment);
4284 }
4285
4286 /* Setting ORO is a standard hack */
4287 if ((option->space == space_lookup("dhcp6")) &&
4288 (option->code == 6)) {
4289 struct comment *comment;
4290
4291 comment = createComment("/// Possible ORO hack");
4292 TAILQ_INSERT_TAIL(&opt_data->comments, comment);
4293 comment = createComment("/// Consider setting \"always-send\" "
4294 "to true when setting data "
4295 "for relevant options, cf Kea #250");
4296 TAILQ_INSERT_TAIL(&opt_data->comments, comment);
4297 }
4298
4299 token = peek_token(&val, NULL, cfile);
4300 /* We should keep a list of defined empty options */
4301 if ((token == SEMI) && (option->format[0] != 'Z')) {
4302 /* Eat the semicolon... */
4303 /*
4304 * XXXSK: I'm not sure why we should ever get here, but we
4305 * do during our startup. This confuses things if
4306 * we are parsing a zero-length option, so don't
4307 * eat the semicolon token in that case.
4308 */
4309 skip_token(&val, NULL, cfile);
4310 } else if (token == EQUAL) {
4311 struct element *data;
4312 isc_boolean_t modified = ISC_FALSE;
4313
4314 /* Eat the equals sign. */
4315 skip_token(&val, NULL, cfile);
4316
4317 /* Parse a data expression and use its value for the data. */
4318 expr = createMap();
4319 if (!parse_data_expression(expr, cfile, &lose)) {
4320 /* In this context, we must have an executable
4321 statement, so if we found something else, it's
4322 still an error. */
4323 if (!lose)
4324 parse_error(cfile,
4325 "expecting a data expression.");
4326 return ISC_FALSE;
4327 }
4328 /* evaluate the expression */
4329 expr = eval_data_expression(expr, &modified);
4330
4331 mapSet(opt_data, createBool(ISC_FALSE), "csv-format");
4332
4333 if (expr->type == ELEMENT_STRING) {
4334 struct string *s;
4335 struct string *r;
4336
4337 s = stringValue(expr);
4338 expr->skip = ISC_TRUE;
4339 mapSet(opt_data, expr, "original-data");
4340
4341 r = makeStringExt(s->length, s->content, 'X');
4342 data = createString(r);
4343 mapSet(opt_data, data, "data");
4344 } else if ((expr->type == ELEMENT_MAP) &&
4345 mapContains(expr, "const-data")) {
4346 struct element *value;
4347 struct string *r;
4348
4349 value = mapGet(expr, "const-data");
4350 if ((value == NULL) || (value->type != ELEMENT_STRING))
4351 parse_error(cfile, "can't get const-data");
4352 r = hexaValue(value);
4353 data = createString(r);
4354 mapSet(opt_data, data, "data");
4355 } else {
4356 opt_data->skip = ISC_TRUE;
4357 cfile->issue_counter++;
4358 mapSet(opt_data, expr, "expression");
4359 }
4360 } else {
4361 if (!parse_option_data(opt_data, cfile, option))
4362 return ISC_FALSE;
4363 }
4364
4365 parse_semi(cfile);
4366
4367 if (result != NULL) {
4368 opt_data->skip = ISC_TRUE;
4369 mapSet(result, opt_data, "option");
4370 return ISC_TRUE;
4371 }
4372
4373 for (where = cfile->stack_top; where > 0; --where) {
4374 if (cfile->stack[where]->kind != PARAMETER)
4375 break;
4376 }
4377
4378 opt_data_list = mapGet(cfile->stack[where], "option-data");
4379 if (opt_data_list == NULL) {
4380 opt_data_list = createList();
4381 mapSet(cfile->stack[where], opt_data_list, "option-data");
4382 }
4383 if (!opt_data->skip && (option->space->vendor != NULL))
4384 add_option_data(option->space->vendor, opt_data_list);
4385 listPush(opt_data_list, opt_data);
4386
4387 return ISC_TRUE;
4388 }
4389
4390 /* Text version of parse_option_token */
4391
4392 struct string *
parse_option_token(struct parse * cfile,const char * fmt,isc_boolean_t * require_binary,isc_boolean_t * canon_bool,isc_boolean_t * modified)4393 parse_option_token(struct parse *cfile, const char *fmt,
4394 isc_boolean_t *require_binary,
4395 isc_boolean_t *canon_bool,
4396 isc_boolean_t *modified)
4397 {
4398 const char *val;
4399 enum dhcp_token token;
4400 unsigned len;
4401 struct string *item;
4402
4403 switch (*fmt) {
4404 case 'U':
4405 token = next_token(&val, &len, cfile);
4406 if (!is_identifier(token))
4407 parse_error(cfile, "expecting identifier.");
4408 return makeString(len, val);
4409 case 'x':
4410 token = peek_token(&val, NULL, cfile);
4411 if (token == NUMBER_OR_NAME || token == NUMBER) {
4412 *require_binary = ISC_TRUE;
4413 return NULL;
4414 }
4415 token = next_token(&val, &len, cfile);
4416 if (token != STRING)
4417 parse_error(cfile, "expecting string "
4418 "or hexadecimal data.");
4419 /* STRING can return embedded unexpected characters */
4420 return escape_option_string(len, val, require_binary,
4421 modified);
4422 case 'X':
4423 token = peek_token(&val, NULL, cfile);
4424 if (token == NUMBER_OR_NAME || token == NUMBER) {
4425 return parse_hexa(cfile);
4426 }
4427 token = next_token(&val, &len, cfile);
4428 if (token != STRING)
4429 parse_error(cfile, "expecting string "
4430 "or hexadecimal data.");
4431 return makeStringExt(len, val, 'X');
4432
4433 case 'D': /* Domain list... */
4434 *modified = ISC_TRUE;
4435 return parse_domain_list(cfile, ISC_FALSE);
4436
4437 case 'd': /* Domain name... */
4438 *modified = ISC_TRUE;
4439 item = parse_host_name(cfile);
4440 if (item == NULL)
4441 parse_error(cfile, "not a valid domain name.");
4442 return item;
4443
4444 case 't': /* Text string... */
4445 token = next_token(&val, &len, cfile);
4446 if (token != STRING && !is_identifier(token))
4447 parse_error(cfile, "expecting string.");
4448 /* STRING can return embedded unexpected characters */
4449 return escape_option_string(len, val, require_binary,
4450 modified);
4451
4452 case 'I': /* IP address or hostname. */
4453 *modified = ISC_TRUE;
4454 return parse_ip_addr_or_hostname(cfile, ISC_FALSE);
4455
4456 case '6': /* IPv6 address. */
4457 *modified = ISC_TRUE;
4458 return parse_ip6_addr_txt(cfile);
4459
4460 case 'T': /* Lease interval. */
4461 token = next_token(&val, NULL, cfile);
4462 if (token == INFINITE)
4463 return makeString(-1, "0xffffffff");
4464 goto check_number;
4465
4466 case 'L': /* Unsigned 32-bit integer... */
4467 case 'l':
4468 case 's': /* Signed 16-bit integer. */
4469 case 'S': /* Unsigned 16-bit integer. */
4470 case 'b': /* Signed 8-bit integer. */
4471 case 'B': /* Unsigned 8-bit integer. */
4472 token = next_token(&val, NULL, cfile);
4473 check_number:
4474 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4475 parse_error(cfile, "expecting number.");
4476 /* check octal */
4477 if (val[0] == '0' && isascii(val[1]) && isdigit(val[1]))
4478 *require_binary = ISC_TRUE;
4479 return makeString(-1, val);
4480
4481 case 'f': /* Boolean flag. */
4482 token = next_token(&val, NULL, cfile);
4483 if (!is_identifier(token))
4484 parse_error(cfile, "expecting identifier.");
4485 if (strcasecmp(val, "true") == 0)
4486 return makeString(-1, "true");
4487 if (strcasecmp(val, "on") == 0) {
4488 *canon_bool = ISC_TRUE;
4489 *modified = ISC_TRUE;
4490 return makeString(-1, "true");
4491 }
4492 if (strcasecmp(val, "false") == 0)
4493 return makeString(-1, "false");
4494 if (strcasecmp(val, "off") == 0) {
4495 *canon_bool = ISC_TRUE;
4496 *modified = ISC_TRUE;
4497 return makeString(-1, "false");
4498 }
4499 parse_error(cfile, "expecting boolean.");
4500
4501 case 'Z': /* Zero-length option. */
4502 token = peek_token(&val, NULL, cfile);
4503 if (token != SEMI)
4504 parse_error(cfile, "semicolon expected.");
4505 return allocString();
4506
4507 default:
4508 parse_error(cfile, "Bad format '%c' in parse_option_token.",
4509 *fmt);
4510 }
4511 }
4512
4513 /* Binary (aka hexadecimal) version of parse_option_token */
4514
4515 struct string *
parse_option_token_binary(struct parse * cfile,const char * fmt)4516 parse_option_token_binary(struct parse *cfile, const char *fmt)
4517 {
4518 const char *val;
4519 enum dhcp_token token;
4520 unsigned len;
4521 struct string *item;
4522 uint8_t buf[4];
4523
4524 switch (*fmt) {
4525 case 'U':
4526 token = next_token(&val, &len, cfile);
4527 if (!is_identifier(token)) {
4528 if (fmt[1] == 'o')
4529 return NULL;
4530 parse_error(cfile, "expecting identifier.");
4531 }
4532 return makeStringExt(len, val, 'X');
4533 case 'E':
4534 case 'X':
4535 case 'x':
4536 case 'u':
4537 token = peek_token(&val, NULL, cfile);
4538 if (token == NUMBER_OR_NAME || token == NUMBER)
4539 return parse_hexa(cfile);
4540 token = next_token(&val, &len, cfile);
4541 if (token != STRING) {
4542 if (fmt[1] == 'o')
4543 return NULL;
4544 parse_error(cfile, "expecting string "
4545 "or hexadecimal data.");
4546 }
4547 return makeStringExt(len, val, 'X');
4548
4549 case 'D': /* Domain list... */
4550 item = parse_domain_list(cfile, ISC_TRUE);
4551 if (item == NULL) {
4552 if (fmt[1] == 'o')
4553 return NULL;
4554 parse_error(cfile, "parse_domain_list failed");
4555 }
4556 return NULL;
4557
4558 case 'd': /* Domain name... */
4559 item = parse_host_name(cfile);
4560 if (item == NULL)
4561 parse_error(cfile, "not a valid domain name.");
4562 item = makeStringExt(item->length, item->content, 'd');
4563 if (item == NULL)
4564 parse_error(cfile, "too long domain name.");
4565 return makeStringExt(item->length, item->content, 'X');
4566
4567 case 't': /* Text string... */
4568 token = next_token(&val, &len, cfile);
4569 if (token != STRING && !is_identifier(token)) {
4570 if (fmt[1] == 'o')
4571 return NULL;
4572 parse_error(cfile, "expecting string.");
4573 }
4574 return makeStringExt(len, val, 'X');
4575
4576 case 'I': /* IP address or hostname. */
4577 item = parse_ip_addr_or_hostname(cfile, ISC_FALSE);
4578 return makeStringExt(item->length, item->content, 'i');
4579
4580 case '6': /* IPv6 address. */
4581 item = parse_ip6_addr(cfile);
4582 return makeStringExt(item->length, item->content, 'X');
4583
4584 case 'T': /* Lease interval. */
4585 token = next_token(&val, NULL, cfile);
4586 if (token == INFINITE)
4587 return makeString(-1, "ffffffff");
4588 goto check_number;
4589
4590 case 'L': /* Unsigned 32-bit integer... */
4591 case 'l': /* Signed 32-bit integer... */
4592 token = next_token(&val, NULL, cfile);
4593 check_number:
4594 if ((token != NUMBER) && (token != NUMBER_OR_NAME)) {
4595 need_number:
4596 if (fmt[1] == 'o')
4597 return NULL;
4598 parse_error(cfile, "expecting number.");
4599 }
4600 convert_num(cfile, buf, val, 0, 32);
4601 return makeStringExt(4, (const char *)buf, 'X');
4602
4603 case 's': /* Signed 16-bit integer. */
4604 case 'S': /* Unsigned 16-bit integer. */
4605 token = next_token(&val, NULL, cfile);
4606 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4607 goto need_number;
4608 convert_num(cfile, buf, val, 0, 16);
4609 return makeStringExt(2, (const char *)buf, 'X');
4610
4611 case 'b': /* Signed 8-bit integer. */
4612 case 'B': /* Unsigned 8-bit integer. */
4613 token = next_token(&val, NULL, cfile);
4614 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4615 goto need_number;
4616 convert_num(cfile, buf, val, 0, 8);
4617 return makeStringExt(1, (const char *)buf, 'X');
4618
4619 case 'f': /* Boolean flag. */
4620 token = next_token(&val, NULL, cfile);
4621 if (!is_identifier(token)) {
4622 if (fmt[1] == 'o')
4623 return NULL;
4624 parse_error(cfile, "expecting identifier.");
4625 }
4626 if ((strcasecmp(val, "true") == 0) ||
4627 (strcasecmp(val, "on") == 0))
4628 return makeString(-1, "01");
4629 if ((strcasecmp(val, "false") == 0) ||
4630 (strcasecmp(val, "off") == 0))
4631 return makeString(-1, "00");
4632 if (strcasecmp(val, "ignore") == 0)
4633 return makeString(-1, "02");
4634 if (fmt[1] == 'o')
4635 return NULL;
4636 parse_error(cfile, "expecting boolean.");
4637
4638 case 'Z': /* Zero-length option. */
4639 token = peek_token(&val, NULL, cfile);
4640 if (token != SEMI)
4641 parse_error(cfile, "semicolon expected.");
4642 return allocString();
4643
4644 default:
4645 parse_error(cfile, "Bad format '%c' in parse_option_token.",
4646 *fmt);
4647 }
4648 }
4649
4650 struct string *
parse_domain_list(struct parse * cfile,isc_boolean_t binary)4651 parse_domain_list(struct parse *cfile, isc_boolean_t binary)
4652 {
4653 const char *val;
4654 enum dhcp_token token;
4655 unsigned len;
4656 struct string *result;
4657
4658 token = SEMI;
4659 result = allocString();
4660
4661 do {
4662 /* Consume the COMMA token if peeked. */
4663 if (token == COMMA) {
4664 skip_token(&val, NULL, cfile);
4665 if (!binary)
4666 appendString(result, ", ");
4667 }
4668
4669 /* Get next (or first) value. */
4670 token = next_token(&val, &len, cfile);
4671
4672 if (token != STRING)
4673 parse_error(cfile, "Expecting a domain string.");
4674
4675 /* Just pack the names in series into the buffer. */
4676 if (binary) {
4677 struct string *item;
4678
4679 item = makeStringExt(len, val, 'd');
4680 if (item == NULL)
4681 parse_error(cfile, "not a valid domain name.");
4682 item = makeStringExt(item->length, item->content, 'X');
4683 concatString(result, item);
4684 } else
4685 concatString(result, makeString(len, val));
4686
4687 token = peek_token(&val, NULL, cfile);
4688 } while (token == COMMA);
4689
4690 return result;
4691 }
4692
4693 /* Specialized version of parse_option_data working on config
4694 * options which are scalar (I6LSBtTfUXdNxxx.) only. */
4695
4696 isc_boolean_t
parse_config_data(struct element * expr,struct parse * cfile,struct option * option)4697 parse_config_data(struct element *expr,
4698 struct parse *cfile,
4699 struct option *option)
4700 {
4701 const char *val;
4702 enum dhcp_token token;
4703 struct string *data;
4704 struct element *elem;
4705 unsigned len;
4706 uint32_t u32;
4707 uint16_t u16;
4708 uint8_t u8;
4709
4710 token = peek_token(&val, NULL, cfile);
4711
4712 if (token == END_OF_FILE)
4713 parse_error(cfile, "unexpected end of file");
4714 if (token == SEMI)
4715 parse_error(cfile, "empty config option");
4716 if (token == COMMA)
4717 parse_error(cfile, "multiple value config option");
4718
4719 /* from parse_option_token */
4720
4721 switch (option->format[0]) {
4722 case 'U': /* universe */
4723 token = next_token(&val, &len, cfile);
4724 if (!is_identifier(token))
4725 parse_error(cfile, "expecting identifier.");
4726 elem = createString(makeString(len, val));
4727 break;
4728
4729 case 'X': /* string or binary */
4730 token = next_token(&val, &len, cfile);
4731 if (token == NUMBER_OR_NAME || token == NUMBER)
4732 data = parse_cshl(cfile);
4733 else if (token == STRING)
4734 data = makeString(len, val);
4735 else
4736 parse_error(cfile, "expecting string "
4737 "or hexadecimal data.");
4738 elem = createString(data);
4739 break;
4740
4741 case 'd': /* FQDN */
4742 data = parse_host_name(cfile);
4743 if (data == NULL)
4744 parse_error(cfile, "not a valid domain name.");
4745 elem = createString(data);
4746 break;
4747
4748 case 't': /* text */
4749 token = next_token(&val, &len, cfile);
4750 elem = createString(makeString(len, val));
4751 break;
4752
4753 case 'N': /* enumeration */
4754 token = next_token(&val, &len, cfile);
4755 if (!is_identifier(token))
4756 parse_error(cfile, "identifier expected");
4757 elem = createString(makeString(len, val));
4758 break;
4759
4760 case 'I': /* IP address or hostname. */
4761 data = parse_ip_addr_or_hostname(cfile, ISC_FALSE);
4762 if (data == NULL)
4763 parse_error(cfile, "expecting IP address of hostname");
4764 elem = createString(data);
4765 break;
4766
4767 case '6': /* IPv6 address. */
4768 data = parse_ip6_addr_txt(cfile);
4769 if (data == NULL)
4770 parse_error(cfile, "expecting IPv6 address");
4771 elem = createString(data);
4772 break;
4773
4774 case 'T': /* Lease interval. */
4775 token = next_token(&val, NULL, cfile);
4776 if (token != INFINITE)
4777 goto check_number;
4778 elem = createInt(-1);
4779 break;
4780
4781 case 'L': /* Unsigned 32-bit integer... */
4782 token = next_token(&val, NULL, cfile);
4783 check_number:
4784 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4785 parse_error(cfile, "expecting number.");
4786 convert_num(cfile, (unsigned char *)&u32, val, 0, 32);
4787 elem = createInt(ntohl(u32));
4788 break;
4789
4790 case 'S': /* Unsigned 16-bit integer. */
4791 token = next_token(&val, NULL, cfile);
4792 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4793 parse_error(cfile, "expecting number.");
4794 convert_num(cfile, (unsigned char *)&u16, val, 0, 16);
4795 elem = createInt(ntohs(u16));
4796 break;
4797
4798 case 'B': /* Unsigned 8-bit integer. */
4799 token = next_token(&val, NULL, cfile);
4800 if ((token != NUMBER) && (token != NUMBER_OR_NAME))
4801 parse_error(cfile, "expecting number.");
4802 convert_num(cfile, (unsigned char *)&u8, val, 0, 8);
4803 elem = createInt(ntohs(u8));
4804 break;
4805
4806 case 'f':
4807 token = next_token(&val, NULL, cfile);
4808 if (!is_identifier(token))
4809 parse_error(cfile, "expecting boolean.");
4810 if ((strcasecmp(val, "true") == 0) ||
4811 (strcasecmp(val, "on") == 0))
4812 elem = createBool(ISC_TRUE);
4813 else if ((strcasecmp(val, "false") == 0) ||
4814 (strcasecmp(val, "off") == 0))
4815 elem = createBool(ISC_FALSE);
4816 else if (strcasecmp(val, "ignore") == 0) {
4817 elem = createNull();
4818 elem->skip = ISC_TRUE;
4819 } else
4820 parse_error(cfile, "expecting boolean.");
4821 break;
4822
4823 default:
4824 parse_error(cfile, "Bad format '%c' in parse_config_data.",
4825 option->format[0]);
4826 }
4827
4828 mapSet(expr, elem, "value");
4829
4830 return ISC_TRUE;
4831 }
4832
4833 /* Specialized version of parse_option_statement for config options */
4834
4835 isc_boolean_t
parse_config_statement(struct element * result,struct parse * cfile,struct option * option,enum statement_op op)4836 parse_config_statement(struct element *result,
4837 struct parse *cfile,
4838 struct option *option,
4839 enum statement_op op)
4840 {
4841 const char *val;
4842 enum dhcp_token token;
4843 struct comments *comments;
4844 struct element *expr;
4845 struct element *config;
4846 struct element *config_list;
4847 isc_boolean_t lose;
4848 size_t where;
4849
4850 config = createMap();
4851 TAILQ_CONCAT(&config->comments, &cfile->comments);
4852 comments = get_config_comments(option->code);
4853 TAILQ_CONCAT(&config->comments, comments);
4854 mapSet(config, createString(makeString(-1, option->name)), "name");
4855 mapSet(config, createInt(option->code), "code");
4856 if (option->status == kea_unknown) {
4857 config->skip = ISC_TRUE;
4858 cfile->issue_counter++;
4859 }
4860 if (op != supersede_option_statement) {
4861 struct string *msg;
4862 struct comment *comment;
4863
4864 msg = makeString(-1, "/// Kea does not support option data ");
4865 appendString(msg, "set variants (");
4866 switch (op) {
4867 case send_option_statement:
4868 appendString(msg, "send");
4869 break;
4870 case supersede_option_statement:
4871 appendString(msg, "supersede");
4872 break;
4873 case default_option_statement:
4874 appendString(msg, "default");
4875 break;
4876 case prepend_option_statement:
4877 appendString(msg, "prepend");
4878 break;
4879 case append_option_statement:
4880 appendString(msg, "append");
4881 break;
4882 default:
4883 appendString(msg, "???");
4884 break;
4885 }
4886 appendString(msg, ")");
4887 comment = createComment(msg->content);
4888 TAILQ_INSERT_TAIL(&config->comments, comment);
4889 }
4890
4891 token = peek_token(&val, NULL, cfile);
4892 /* We should keep a list of defined empty options */
4893 if ((token == SEMI) && (option->format[0] != 'Z')) {
4894 /* Eat the semicolon... */
4895 /*
4896 * XXXSK: I'm not sure why we should ever get here, but we
4897 * do during our startup. This confuses things if
4898 * we are parsing a zero-length option, so don't
4899 * eat the semicolon token in that case.
4900 */
4901 skip_token(&val, NULL, cfile);
4902 } else if (token == EQUAL) {
4903 /* Eat the equals sign. */
4904 skip_token(&val, NULL, cfile);
4905
4906 /* Parse a data expression and use its value for the data. */
4907 expr = createMap();
4908 if (!parse_data_expression(expr, cfile, &lose)) {
4909 /* In this context, we must have an executable
4910 statement, so if we found something else, it's
4911 still an error. */
4912 if (!lose)
4913 parse_error(cfile,
4914 "expecting a data expression.");
4915 return ISC_FALSE;
4916 }
4917 mapSet(config, expr, "value");
4918 } else {
4919 if (!parse_config_data(config, cfile, option))
4920 return ISC_FALSE;
4921 }
4922
4923 parse_semi(cfile);
4924
4925 if (result != NULL) {
4926 config->skip = ISC_TRUE;
4927 mapSet(result, config, "config");
4928 return ISC_TRUE;
4929 }
4930
4931 for (where = cfile->stack_top; where > 0; --where) {
4932 if ((cfile->stack[where]->kind == PARAMETER) ||
4933 (cfile->stack[where]->kind == POOL_DECL))
4934 continue;
4935 break;
4936 }
4937
4938 if (option->status != special) {
4939 config_list = mapGet(cfile->stack[where], "config");
4940 if (config_list == NULL) {
4941 config_list = createList();
4942 config_list->skip = ISC_TRUE;
4943 mapSet(cfile->stack[where], config_list, "config");
4944 }
4945 listPush(config_list, config);
4946 return ISC_TRUE;
4947 }
4948
4949 /* deal with all special cases */
4950
4951 switch (option->code) {
4952 case 1: /* default-lease-time */
4953 config_def_valid_lifetime(config, cfile);
4954 break;
4955 case 2: /* max-lease-time */
4956 config_max_valid_lifetime(config, cfile);
4957 break;
4958 case 3: /* min-lease-time */
4959 config_min_valid_lifetime(config, cfile);
4960 break;
4961 case 15: /* filename */
4962 config_file(config, cfile);
4963 break;
4964 case 16: /* server-name */
4965 config_sname(config, cfile);
4966 break;
4967 case 17: /* next-server */
4968 config_next_server(config, cfile);
4969 break;
4970 case 18: /* authoritative */
4971 parse_error(cfile, "authoritative is a statement, "
4972 "here it is used as a config option");
4973 case 19: /* vendor-option-space */
4974 config_vendor_option_space(config, cfile);
4975 break;
4976 case 21: /* site-option-space */
4977 config_site_option_space(config, cfile);
4978 break;
4979 case 23: /* ddns-domainname */
4980 config_qualifying_suffix(config, cfile);
4981 break;
4982 case 30: /* ddns-updates */
4983 config_enable_updates(config, cfile);
4984 break;
4985 case 39: /* ddns-update-style */
4986 config_ddns_update_style(config, cfile);
4987 break;
4988 case 53: /* preferred-lifetime */
4989 config_preferred_lifetime(config, cfile);
4990 break;
4991 case 82: /* ignore-client-uids */
4992 config_match_client_id(config, cfile);
4993 break;
4994 case 85: /* echo-client-id */
4995 config_echo_client_id(config, cfile);
4996 break;
4997 default:
4998 parse_error(cfile, "unsupported config option %s (%u)",
4999 option->name, option->code);
5000 }
5001
5002 return ISC_TRUE;
5003 }
5004
5005 static void
config_def_valid_lifetime(struct element * config,struct parse * cfile)5006 config_def_valid_lifetime(struct element *config, struct parse *cfile)
5007 {
5008 struct element *value;
5009 struct comment *comment;
5010 size_t scope;
5011 isc_boolean_t pop_from_pool = ISC_FALSE;
5012
5013 value = mapGet(config, "value");
5014
5015 for (scope = cfile->stack_top; scope > 0; --scope) {
5016 int kind = cfile->stack[scope]->kind;
5017
5018 if (kind == PARAMETER)
5019 continue;
5020 if ((kind == ROOT_GROUP) ||
5021 (kind == SHARED_NET_DECL) ||
5022 (kind == SUBNET_DECL) ||
5023 (kind == GROUP_DECL))
5024 break;
5025 if (kind == POOL_DECL) {
5026 pop_from_pool = ISC_TRUE;
5027 continue;
5028 }
5029 comment = createComment("/// default-valid-lifetime in "
5030 "unsupported scope");
5031 TAILQ_INSERT_TAIL(&value->comments, comment);
5032 value->skip = ISC_TRUE;
5033 cfile->issue_counter++;
5034 break;
5035 }
5036 if (pop_from_pool) {
5037 comment= createComment("/// default-valid-lifetime moved from "
5038 "an internal pool scope");
5039 TAILQ_INSERT_TAIL(&value->comments, comment);
5040 }
5041 mapSet(cfile->stack[scope], value, "valid-lifetime");
5042 }
5043
5044 static void
config_min_valid_lifetime(struct element * config,struct parse * cfile)5045 config_min_valid_lifetime(struct element *config, struct parse *cfile)
5046 {
5047 struct element *value;
5048 struct comment *comment;
5049 size_t scope;
5050 isc_boolean_t pop_from_pool = ISC_FALSE;
5051
5052 value = mapGet(config, "value");
5053
5054 for (scope = cfile->stack_top; scope > 0; --scope) {
5055 int kind = cfile->stack[scope]->kind;
5056
5057 if (kind == PARAMETER)
5058 continue;
5059 if ((kind == ROOT_GROUP) ||
5060 (kind == SHARED_NET_DECL) ||
5061 (kind == SUBNET_DECL) ||
5062 (kind == GROUP_DECL))
5063 break;
5064 if (kind == POOL_DECL) {
5065 pop_from_pool = ISC_TRUE;
5066 continue;
5067 }
5068 comment = createComment("/// min-valid-lifetime in "
5069 "unsupported scope");
5070 TAILQ_INSERT_TAIL(&value->comments, comment);
5071 value->skip = ISC_TRUE;
5072 cfile->issue_counter++;
5073 break;
5074 }
5075 if (pop_from_pool) {
5076 comment= createComment("/// min-valid-lifetime moved from "
5077 "an internal pool scope");
5078 TAILQ_INSERT_TAIL(&value->comments, comment);
5079 }
5080 mapSet(cfile->stack[scope], value, "min-valid-lifetime");
5081 }
5082
5083 static void
config_max_valid_lifetime(struct element * config,struct parse * cfile)5084 config_max_valid_lifetime(struct element *config, struct parse *cfile)
5085 {
5086 struct element *value;
5087 struct comment *comment;
5088 size_t scope;
5089 isc_boolean_t pop_from_pool = ISC_FALSE;
5090
5091 value = mapGet(config, "value");
5092
5093 for (scope = cfile->stack_top; scope > 0; --scope) {
5094 int kind = cfile->stack[scope]->kind;
5095
5096 if (kind == PARAMETER)
5097 continue;
5098 if ((kind == ROOT_GROUP) ||
5099 (kind == SHARED_NET_DECL) ||
5100 (kind == SUBNET_DECL) ||
5101 (kind == GROUP_DECL))
5102 break;
5103 if (kind == POOL_DECL) {
5104 pop_from_pool = ISC_TRUE;
5105 continue;
5106 }
5107 comment = createComment("/// max-valid-lifetime in "
5108 "unsupported scope");
5109 TAILQ_INSERT_TAIL(&value->comments, comment);
5110 value->skip = ISC_TRUE;
5111 cfile->issue_counter++;
5112 break;
5113 }
5114 if (pop_from_pool) {
5115 comment= createComment("/// max-valid-lifetime moved from "
5116 "an internal pool scope");
5117 TAILQ_INSERT_TAIL(&value->comments, comment);
5118 }
5119 mapSet(cfile->stack[scope], value, "max-valid-lifetime");
5120 }
5121
5122 static void
config_file(struct element * config,struct parse * cfile)5123 config_file(struct element *config, struct parse *cfile)
5124 {
5125 struct element *value;
5126 struct comment *comment;
5127 size_t scope;
5128 isc_boolean_t popped = ISC_FALSE;
5129
5130 if (local_family != AF_INET)
5131 parse_error(cfile, "boot-file-name is DHCPv4 only");
5132
5133 value = mapGet(config, "value");
5134
5135 for (scope = cfile->stack_top; scope > 0; --scope) {
5136 int kind = cfile->stack[scope]->kind;
5137
5138 if (kind == PARAMETER)
5139 continue;
5140 if ((kind == HOST_DECL) ||
5141 (kind == CLASS_DECL) ||
5142 (kind == GROUP_DECL))
5143 break;
5144 if (kind == ROOT_GROUP) {
5145 popped = ISC_TRUE;
5146 break;
5147 }
5148 }
5149 if (popped) {
5150 comment = createComment("/// boot-file-name was defined in "
5151 "an unsupported scope");
5152 TAILQ_INSERT_TAIL(&value->comments, comment);
5153 value->skip = ISC_TRUE;
5154 cfile->issue_counter++;
5155 }
5156 mapSet(cfile->stack[scope], value, "boot-file-name");
5157 }
5158
5159 static void
config_sname(struct element * config,struct parse * cfile)5160 config_sname(struct element *config, struct parse *cfile)
5161 {
5162 struct element *value;
5163 struct comment *comment;
5164 size_t scope;
5165 isc_boolean_t popped = ISC_FALSE;
5166
5167 if (local_family != AF_INET)
5168 parse_error(cfile, "server-hostname is DHCPv4 only");
5169
5170 value = mapGet(config, "value");
5171
5172 for (scope = cfile->stack_top; scope > 0; --scope) {
5173 int kind = cfile->stack[scope]->kind;
5174
5175 if (kind == PARAMETER)
5176 continue;
5177 if ((kind == HOST_DECL) ||
5178 (kind == CLASS_DECL) ||
5179 (kind == GROUP_DECL))
5180 break;
5181 if (kind == ROOT_GROUP) {
5182 popped = ISC_TRUE;
5183 break;
5184 }
5185 }
5186 if (popped) {
5187 comment = createComment("/// server-hostname was defined in "
5188 "an unsupported scope");
5189 TAILQ_INSERT_TAIL(&value->comments, comment);
5190 value->skip = ISC_TRUE;
5191 cfile->issue_counter++;
5192 }
5193 mapSet(cfile->stack[scope], value, "server-hostname");
5194 }
5195
5196 static void
config_next_server(struct element * config,struct parse * cfile)5197 config_next_server(struct element *config, struct parse *cfile)
5198 {
5199 struct element *value;
5200 struct comment *comment;
5201 size_t scope;
5202 isc_boolean_t popped = ISC_FALSE;
5203
5204 if (local_family != AF_INET)
5205 parse_error(cfile, "next-server is DHCPv4 only");
5206
5207 value = mapGet(config, "value");
5208
5209 for (scope = cfile->stack_top; scope > 0; --scope) {
5210 int kind = cfile->stack[scope]->kind;
5211
5212 if (kind == PARAMETER)
5213 continue;
5214 if ((kind == ROOT_GROUP) ||
5215 (kind == HOST_DECL) ||
5216 (kind == CLASS_DECL) ||
5217 (kind == SUBNET_DECL) ||
5218 (kind == GROUP_DECL))
5219 break;
5220 popped = ISC_TRUE;
5221 }
5222 if (popped) {
5223 comment = createComment("/// next-server moved from "
5224 "an internal unsupported scope");
5225 TAILQ_INSERT_TAIL(&value->comments, comment);
5226 }
5227 mapSet(cfile->stack[scope], value, "next-server");
5228 }
5229
5230 static void
config_vendor_option_space(struct element * config,struct parse * cfile)5231 config_vendor_option_space(struct element *config, struct parse *cfile)
5232 {
5233 struct element *defs;
5234 struct element *def;
5235 struct element *opts;
5236 struct element *opt;
5237 struct element *space;
5238
5239 if (local_family != AF_INET)
5240 parse_error(cfile, "vendor-option-space is DHCPv4 only");
5241
5242 /* create local option definition */
5243 def = createMap();
5244 mapSet(def,
5245 createString(makeString(-1, "vendor-encapsulated-options")),
5246 "name");
5247 mapSet(def, createInt(43), "code");
5248 mapSet(def, createString(makeString(-1, "empty")), "type");
5249 space = mapGet(config, "value");
5250 if (space == NULL)
5251 parse_error(cfile, "vendor-option-space has no value");
5252 if (space->type != ELEMENT_STRING)
5253 parse_error(cfile,
5254 "vendor-option-space value is not a string");
5255 mapSet(def, space, "encapsulate");
5256
5257 /* add it */
5258 defs = mapGet(cfile->stack[cfile->stack_top], "option-def");
5259 if (defs == NULL) {
5260 defs = createList();
5261 mapSet(cfile->stack[cfile->stack_top], defs, "option-def");
5262 } else {
5263 size_t i;
5264
5265 /* Look for duplicate */
5266 for (i = 0; i < listSize(defs); i++) {
5267 struct element *item;
5268 struct element *code;
5269 struct element *old;
5270
5271 item = listGet(defs, i);
5272 if ((item == NULL) || (item->type != ELEMENT_MAP))
5273 continue;
5274 code = mapGet(item, "code");
5275 if ((code == NULL) ||
5276 (code->type != ELEMENT_INTEGER) ||
5277 (intValue(code) != 43))
5278 continue;
5279 old = mapGet(item, "encapsulate");
5280 if ((old == NULL) || (old->type != ELEMENT_STRING))
5281 continue;
5282 if (eqString(stringValue(space), stringValue(old)))
5283 return;
5284 }
5285 }
5286 listPush(defs, def);
5287
5288 /* add a data too assuming at least one suboption exists */
5289 opt = createMap();
5290 mapSet(opt,
5291 createString(makeString(-1, "vendor-encapsulated-options")),
5292 "name");
5293 mapSet(opt, createInt(43), "code");
5294 opts = mapGet(cfile->stack[cfile->stack_top], "option-data");
5295 if (opts == NULL) {
5296 opts = createList();
5297 mapSet(cfile->stack[cfile->stack_top], opts, "option-data");
5298 }
5299 listPush(opts, opt);
5300 }
5301
5302 static void
config_site_option_space(struct element * config,struct parse * cfile)5303 config_site_option_space(struct element *config, struct parse *cfile)
5304 {
5305 struct element *defs;
5306 struct element *space;
5307 struct string *msg;
5308 struct comment *comment;
5309
5310 if (local_family != AF_INET)
5311 parse_error(cfile, "site-option-space is DHCPv4 only");
5312
5313 space = mapGet(config, "value");
5314 if (space == NULL)
5315 parse_error(cfile, "site-option-space has no value");
5316 if (space->type != ELEMENT_STRING)
5317 parse_error(cfile, "site-option-space value is not a string");
5318
5319 defs = mapGet(cfile->stack[cfile->stack_top], "option-def");
5320 if (defs == NULL) {
5321 defs = createList();
5322 mapSet(cfile->stack[cfile->stack_top], defs, "option-def");
5323 }
5324
5325 msg = makeString(-1, "/// site-option-space '");
5326 concatString(msg, stringValue(space));
5327 appendString(msg, "'");
5328 comment = createComment(msg->content);
5329 TAILQ_INSERT_TAIL(&defs->comments, comment);
5330 msg = makeString(-1, "/// Please to move private (code 224..254)");
5331 appendString(msg, " option definitions from '");
5332 concatString(msg, stringValue(space));
5333 appendString(msg, "' to 'dhcp4' space");
5334 comment = createComment(msg->content);
5335 TAILQ_INSERT_TAIL(&defs->comments, comment);
5336 }
5337
5338 static struct element *
default_qualifying_suffix(void)5339 default_qualifying_suffix(void)
5340 {
5341 struct element *qs;
5342 struct comment *comment;
5343
5344 qs = createString(allocString());
5345 comment = createComment("/// Unspecified ddns-domainname (default "
5346 "domain-name option value)");
5347 TAILQ_INSERT_TAIL(&qs->comments, comment);
5348 comment = createComment("/// Kea requires a qualifying-suffix");
5349 TAILQ_INSERT_TAIL(&qs->comments, comment);
5350 comment = createComment("/// Initialized to \"\": please put a value");
5351 TAILQ_INSERT_TAIL(&qs->comments, comment);
5352 return qs;
5353 }
5354
5355 static void
config_qualifying_suffix(struct element * config,struct parse * cfile)5356 config_qualifying_suffix(struct element *config, struct parse *cfile)
5357 {
5358 struct element *value;
5359 size_t scope;
5360
5361 value = mapGet(config, "value");
5362
5363 for (scope = cfile->stack_top; scope > 0; --scope)
5364 if ((cfile->stack[scope]->kind != PARAMETER) ||
5365 (cfile->stack[scope]->kind != POOL_DECL))
5366 break;
5367 if (cfile->stack[scope]->kind != ROOT_GROUP) {
5368 struct comment *comment;
5369
5370 comment = createComment("/// Only global qualifying-suffix "
5371 "is supported");
5372 TAILQ_INSERT_TAIL(&value->comments, comment);
5373 value->skip = ISC_TRUE;
5374 cfile->issue_counter++;
5375 mapSet(cfile->stack[scope], value, "qualifying-suffix");
5376 } else {
5377 struct element *d2;
5378
5379 d2 = mapGet(cfile->stack[1], "dhcp-ddns");
5380 if (d2 == NULL) {
5381 d2 = createMap();
5382 mapSet(d2, createBool(ISC_FALSE), "enable-updates");
5383 mapSet(cfile->stack[1], d2, "dhcp-ddns");
5384 } else if (mapContains(d2, "qualifying-suffix"))
5385 mapRemove(d2, "qualifying-suffix");
5386 mapSet(d2, value, "qualifying-suffix");
5387 }
5388 }
5389
5390 static void
config_enable_updates(struct element * config,struct parse * cfile)5391 config_enable_updates(struct element *config, struct parse *cfile)
5392 {
5393 struct element *value;
5394 size_t scope;
5395
5396 value = mapGet(config, "value");
5397
5398 for (scope = cfile->stack_top; scope > 0; --scope)
5399 if ((cfile->stack[scope]->kind != PARAMETER) ||
5400 (cfile->stack[scope]->kind != POOL_DECL))
5401 break;
5402 if (cfile->stack[scope]->kind != ROOT_GROUP) {
5403 struct comment *comment;
5404
5405 comment = createComment("/// Only global enable-updates "
5406 "is supported");
5407 TAILQ_INSERT_TAIL(&value->comments, comment);
5408 value->skip = ISC_TRUE;
5409 cfile->issue_counter++;
5410 mapSet(cfile->stack[scope], value, "enable-updates");
5411 } else {
5412 struct element *d2;
5413
5414 d2 = mapGet(cfile->stack[1], "dhcp-ddns");
5415 if (d2 == NULL) {
5416 d2 = createMap();
5417 mapSet(cfile->stack[1], d2, "dhcp-ddns");
5418 if (boolValue(value)) {
5419 struct element *qs;
5420
5421 qs = default_qualifying_suffix();
5422 mapSet(d2, qs, "qualifying-suffix");
5423 }
5424 } else if (mapContains(d2, "enable-updates"))
5425 mapRemove(d2, "enable-updates");
5426 mapSet(d2, value, "enable-updates");
5427 }
5428 }
5429
5430 static void
config_ddns_update_style(struct element * config,struct parse * cfile)5431 config_ddns_update_style(struct element *config, struct parse *cfile)
5432 {
5433 struct element *value;
5434 isc_boolean_t enable = ISC_TRUE;
5435 size_t scope;
5436
5437 value = mapGet(config, "value");
5438 if (strcmp(stringValue(value)->content, "standard") == 0)
5439 enable = ISC_TRUE;
5440 else if (strcmp(stringValue(value)->content, "none") == 0)
5441 enable = ISC_FALSE;
5442 else {
5443 struct string *msg;
5444 struct comment *comment;
5445
5446 for (scope = cfile->stack_top; scope > 0; --scope)
5447 if ((cfile->stack[scope]->kind != PARAMETER) ||
5448 (cfile->stack[scope]->kind != POOL_DECL))
5449 break;
5450 msg = makeString(-1, "/// Unsupported ddns-update-style ");
5451 concatString(msg, stringValue(value));
5452 comment = createComment(msg->content);
5453 TAILQ_INSERT_TAIL(&value->comments, comment);
5454 value->skip = ISC_TRUE;
5455 cfile->issue_counter++;
5456 mapSet(cfile->stack[scope], value, "ddns-update-style");
5457 }
5458
5459 for (scope = cfile->stack_top; scope > 0; --scope)
5460 if ((cfile->stack[scope]->kind != PARAMETER) ||
5461 (cfile->stack[scope]->kind != POOL_DECL))
5462 break;
5463 if (cfile->stack[scope]->kind != ROOT_GROUP) {
5464 struct comment *comment;
5465
5466 comment = createComment("/// Only global ddns-update-style "
5467 "is supported");
5468 TAILQ_INSERT_TAIL(&value->comments, comment);
5469 value->skip = ISC_TRUE;
5470 cfile->issue_counter++;
5471 mapSet(cfile->stack[scope], value, "ddns-update-style");
5472 } else {
5473 struct element *d2;
5474
5475 /* map ddns-update-style into enable-updates */
5476 value = createBool(enable);
5477 d2 = mapGet(cfile->stack[1], "dhcp-ddns");
5478 if (d2 == NULL) {
5479 d2 = createMap();
5480 mapSet(cfile->stack[1], d2, "dhcp-ddns");
5481 if (boolValue(value)) {
5482 struct element *qs;
5483
5484 qs = default_qualifying_suffix();
5485 mapSet(d2, qs, "qualifying-suffix");
5486 }
5487 } else if (mapContains(d2, "enable-updates"))
5488 mapRemove(d2, "enable-updates");
5489 mapSet(d2, value, "enable-updates");
5490 }
5491 }
5492
5493 static void
config_preferred_lifetime(struct element * config,struct parse * cfile)5494 config_preferred_lifetime(struct element *config, struct parse *cfile)
5495 {
5496 struct element *value;
5497 struct element *child;
5498 struct comment *comment;
5499 size_t scope;
5500 isc_boolean_t pop_from_pool = ISC_FALSE;
5501
5502 if (local_family != AF_INET6)
5503 parse_error(cfile, "preferred-lifetime is DHCPv6 only");
5504
5505 value = mapGet(config, "value");
5506
5507 for (scope = cfile->stack_top; scope > 0; --scope) {
5508 int kind = cfile->stack[scope]->kind;
5509
5510 if (kind == PARAMETER)
5511 continue;
5512 if ((kind == ROOT_GROUP) ||
5513 (kind == SHARED_NET_DECL) ||
5514 (kind == SUBNET_DECL) ||
5515 (kind == GROUP_DECL))
5516 break;
5517 if (kind == POOL_DECL) {
5518 pop_from_pool = ISC_TRUE;
5519 continue;
5520 }
5521 comment = createComment("/// preferred-lifetime in "
5522 "unsupported scope");
5523 TAILQ_INSERT_TAIL(&value->comments, comment);
5524 value->skip = ISC_TRUE;
5525 cfile->issue_counter++;
5526 break;
5527 }
5528 if (pop_from_pool) {
5529 comment = createComment("/// preferred-lifetime moved from "
5530 "an internal pool scope");
5531 TAILQ_INSERT_TAIL(&value->comments, comment);
5532 /* if there is another specified value and we are
5533 * enough lucky to have already got it... */
5534 if (mapContains(cfile->stack[scope], "preferred-lifetime")) {
5535 comment = createComment("/// Avoid to overwrite "
5536 "current value...");
5537 TAILQ_INSERT_TAIL(&value->comments, comment);
5538 value->skip = ISC_TRUE;
5539 }
5540 }
5541 mapSet(cfile->stack[scope], value, "preferred-lifetime");
5542 /* derive T1 and T2 */
5543 child = createInt(intValue(value) / 2);
5544 child->skip = value->skip;
5545 mapSet(cfile->stack[scope], child, "renew-timer");
5546 child = createInt(intValue(value) * 4 / 5);
5547 child->skip = value->skip;
5548 mapSet(cfile->stack[scope], child, "rebind-timer");
5549 }
5550
5551 static void
config_match_client_id(struct element * config,struct parse * cfile)5552 config_match_client_id(struct element *config, struct parse *cfile)
5553 {
5554 struct element *value;
5555 struct comment *comment;
5556 size_t scope;
5557 isc_boolean_t pop_from_pool = ISC_FALSE;
5558
5559 if (local_family != AF_INET)
5560 parse_error(cfile, "ignore-client-uids is DHCPv4 only");
5561
5562 value = mapGet(config, "value");
5563 /* match-client-id is !ignore-client-uids */
5564 value = createBool(!boolValue(value));
5565
5566 for (scope = cfile->stack_top; scope > 0; --scope) {
5567 int kind = cfile->stack[scope]->kind;
5568
5569 if (kind == PARAMETER)
5570 continue;
5571 if ((kind == ROOT_GROUP) ||
5572 (kind == SHARED_NET_DECL) ||
5573 (kind == SUBNET_DECL) ||
5574 (kind == GROUP_DECL))
5575 break;
5576 if (kind == POOL_DECL) {
5577 pop_from_pool = ISC_TRUE;
5578 continue;
5579 }
5580 comment = createComment("/// match-client-id in unsupported "
5581 "scope");
5582 TAILQ_INSERT_TAIL(&value->comments, comment);
5583 value->skip = ISC_TRUE;
5584 cfile->issue_counter++;
5585 break;
5586 }
5587 if (pop_from_pool) {
5588 comment= createComment("/// match-client-id moved from "
5589 "an internal pool scope");
5590 TAILQ_INSERT_TAIL(&value->comments, comment);
5591 }
5592 mapSet(cfile->stack[scope], value, "match-client-id");
5593 }
5594
5595 static void
config_echo_client_id(struct element * config,struct parse * cfile)5596 config_echo_client_id(struct element *config, struct parse *cfile)
5597 {
5598 struct element *value;
5599 struct comment *comment;
5600 size_t scope;
5601
5602 if (local_family != AF_INET)
5603 parse_error(cfile, "echo-client-id is DHCPv4 only");
5604
5605 value = mapGet(config, "value");
5606
5607 for (scope = cfile->stack_top; scope > 0; --scope) {
5608 int kind = cfile->stack[scope]->kind;
5609
5610 if (kind == PARAMETER)
5611 continue;
5612 if (kind == ROOT_GROUP)
5613 break;
5614 comment = createComment("/// Only global echo-client-id "
5615 "is supported");
5616 TAILQ_INSERT_TAIL(&value->comments, comment);
5617 value->skip = ISC_TRUE;
5618 cfile->issue_counter++;
5619 }
5620 mapSet(cfile->stack[scope], value, "echo-client-id");
5621 }
5622
5623 /* parse_error moved to keama.c */
5624
5625 /* From omapi/convert.c */
5626 /*
5627 static uint32_t
5628 getULong(const unsigned char *buf)
5629 {
5630 uint32_t ibuf;
5631
5632 memcpy(&ibuf, buf, sizeof(uint32_t));
5633 return ntohl(ibuf);
5634 }
5635
5636 static int32_t
5637 getLong(const unsigned char *buf)
5638 {
5639 int32_t ibuf;
5640
5641 memcpy(&ibuf, buf, sizeof(int32_t));
5642 return ntohl(ibuf);
5643 }
5644
5645 static uint32_t
5646 getUShort(const unsigned char *buf)
5647 {
5648 unsigned short ibuf;
5649
5650 memcpy(&ibuf, buf, sizeof(uint16_t));
5651 return ntohs(ibuf);
5652 }
5653
5654 static int32_t
5655 getShort(const unsigned char *buf)
5656 {
5657 short ibuf;
5658
5659 memcpy(&ibuf, buf, sizeof(int16_t));
5660 return ntohs(ibuf);
5661 }
5662
5663 static uint32_t
5664 getUChar(const unsigned char *obuf)
5665 {
5666 return obuf[0];
5667 }
5668 */
5669 static void
putULong(unsigned char * obuf,uint32_t val)5670 putULong(unsigned char *obuf, uint32_t val)
5671 {
5672 uint32_t tmp = htonl(val);
5673 memcpy(obuf, &tmp, sizeof(tmp));
5674 }
5675
5676 static void
putLong(unsigned char * obuf,int32_t val)5677 putLong(unsigned char *obuf, int32_t val)
5678 {
5679 int32_t tmp = htonl(val);
5680 memcpy(obuf, &tmp, sizeof(tmp));
5681 }
5682
5683 static void
putUShort(unsigned char * obuf,uint32_t val)5684 putUShort(unsigned char *obuf, uint32_t val)
5685 {
5686 uint16_t tmp = htons(val);
5687 memcpy(obuf, &tmp, sizeof(tmp));
5688 }
5689
5690 static void
putShort(unsigned char * obuf,int32_t val)5691 putShort(unsigned char *obuf, int32_t val)
5692 {
5693 int16_t tmp = htons(val);
5694 memcpy(obuf, &tmp, sizeof(tmp));
5695 }
5696 /*
5697 static void
5698 putUChar(unsigned char *obuf, uint32_t val)
5699 {
5700 *obuf = val;
5701 }
5702 */
5703 /* From common/tree.c */
5704
5705 isc_boolean_t
is_boolean_expression(struct element * expr)5706 is_boolean_expression(struct element *expr)
5707 {
5708 if (expr->type == ELEMENT_BOOLEAN)
5709 return ISC_TRUE;
5710 if (expr->type != ELEMENT_MAP)
5711 return ISC_FALSE;
5712 return (mapContains(expr, "check") ||
5713 mapContains(expr, "exists") ||
5714 mapContains(expr, "variable-exists") ||
5715 mapContains(expr, "equal") ||
5716 mapContains(expr, "not-equal") ||
5717 mapContains(expr, "regex-match") ||
5718 mapContains(expr, "iregex-match") ||
5719 mapContains(expr, "and") ||
5720 mapContains(expr, "or") ||
5721 mapContains(expr, "not") ||
5722 mapContains(expr, "known") ||
5723 mapContains(expr, "static"));
5724 }
5725
5726 isc_boolean_t
is_data_expression(struct element * expr)5727 is_data_expression(struct element *expr)
5728 {
5729 if (expr->type == ELEMENT_STRING)
5730 return ISC_TRUE;
5731 if (expr->type != ELEMENT_MAP)
5732 return ISC_FALSE;
5733 return (mapContains(expr, "substring") ||
5734 mapContains(expr, "suffix") ||
5735 mapContains(expr, "lowercase") ||
5736 mapContains(expr, "uppercase") ||
5737 mapContains(expr, "option") ||
5738 mapContains(expr, "hardware") ||
5739 mapContains(expr, "hw-type") ||
5740 mapContains(expr, "hw-address") ||
5741 mapContains(expr, "const-data") ||
5742 mapContains(expr, "packet") ||
5743 mapContains(expr, "concat") ||
5744 mapContains(expr, "encapsulate") ||
5745 mapContains(expr, "encode-int8") ||
5746 mapContains(expr, "encode-int16") ||
5747 mapContains(expr, "encode-int32") ||
5748 mapContains(expr, "gethostbyname") ||
5749 mapContains(expr, "binary-to-ascii") ||
5750 mapContains(expr, "filename") ||
5751 mapContains(expr, "server-name") ||
5752 mapContains(expr, "reverse") ||
5753 mapContains(expr, "pick-first-value") ||
5754 mapContains(expr, "host-decl-name") ||
5755 mapContains(expr, "leased-address") ||
5756 mapContains(expr, "config-option") ||
5757 mapContains(expr, "null") ||
5758 mapContains(expr, "gethostname") ||
5759 mapContains(expr, "v6relay"));
5760 }
5761
5762 isc_boolean_t
is_numeric_expression(struct element * expr)5763 is_numeric_expression(struct element *expr)
5764 {
5765 if (expr->type == ELEMENT_INTEGER)
5766 return ISC_TRUE;
5767 if (expr->type != ELEMENT_MAP)
5768 return ISC_FALSE;
5769 return (mapContains(expr, "extract-int8") ||
5770 mapContains(expr, "extract-int16") ||
5771 mapContains(expr, "extract-int32") ||
5772 mapContains(expr, "const-int") ||
5773 mapContains(expr, "lease-time") ||
5774 mapContains(expr, "add") ||
5775 mapContains(expr, "subtract") ||
5776 mapContains(expr, "multiply") ||
5777 mapContains(expr, "divide") ||
5778 mapContains(expr, "remainder") ||
5779 mapContains(expr, "binary-and") ||
5780 mapContains(expr, "binary-or") ||
5781 mapContains(expr, "binary-xor") ||
5782 mapContains(expr, "client-state"));
5783 }
5784 /*
5785 static isc_boolean_t
5786 is_compound_expression(struct element *expr)
5787 {
5788 return (mapContains(expr, "substring") ||
5789 mapContains(expr, "suffix") ||
5790 mapContains(expr, "option") ||
5791 mapContains(expr, "concat") ||
5792 mapContains(expr, "encode-int8") ||
5793 mapContains(expr, "encode-int16") ||
5794 mapContains(expr, "encode-int32") ||
5795 mapContains(expr, "binary-to-ascii") ||
5796 mapContains(expr, "reverse") ||
5797 mapContains(expr, "pick-first-value") ||
5798 mapContains(expr, "config-option") ||
5799 mapContains(expr, "extract-int8") ||
5800 mapContains(expr, "extract-int16") ||
5801 mapContains(expr, "extract-int32") ||
5802 mapContains(expr, "v6relay"));
5803 }
5804 */
5805 static enum expression_context
op_context(enum expr_op op)5806 op_context(enum expr_op op)
5807 {
5808 switch (op) {
5809 /* XXX Why aren't these specific? */
5810 case expr_none:
5811 case expr_match:
5812 case expr_static:
5813 case expr_check:
5814 case expr_substring:
5815 case expr_suffix:
5816 case expr_lcase:
5817 case expr_ucase:
5818 case expr_concat:
5819 case expr_encapsulate:
5820 case expr_host_lookup:
5821 case expr_not:
5822 case expr_option:
5823 case expr_hardware:
5824 case expr_hw_type:
5825 case expr_hw_address:
5826 case expr_packet:
5827 case expr_const_data:
5828 case expr_extract_int8:
5829 case expr_extract_int16:
5830 case expr_extract_int32:
5831 case expr_encode_int8:
5832 case expr_encode_int16:
5833 case expr_encode_int32:
5834 case expr_const_int:
5835 case expr_exists:
5836 case expr_variable_exists:
5837 case expr_known:
5838 case expr_binary_to_ascii:
5839 case expr_reverse:
5840 case expr_filename:
5841 case expr_sname:
5842 case expr_pick_first_value:
5843 case expr_host_decl_name:
5844 case expr_config_option:
5845 case expr_leased_address:
5846 case expr_lease_time:
5847 case expr_null:
5848 case expr_variable_reference:
5849 case expr_ns_add:
5850 case expr_ns_delete:
5851 case expr_ns_exists:
5852 case expr_ns_not_exists:
5853 case expr_dns_transaction:
5854 case expr_arg:
5855 case expr_funcall:
5856 case expr_function:
5857 case expr_gethostname:
5858 case expr_v6relay:
5859 case expr_concat_dclist:
5860 return context_any;
5861
5862 case expr_equal:
5863 case expr_not_equal:
5864 case expr_regex_match:
5865 case expr_iregex_match:
5866 return context_data;
5867
5868 case expr_and:
5869 return context_boolean;
5870
5871 case expr_or:
5872 return context_boolean;
5873
5874 case expr_add:
5875 case expr_subtract:
5876 case expr_multiply:
5877 case expr_divide:
5878 case expr_remainder:
5879 case expr_binary_and:
5880 case expr_binary_or:
5881 case expr_binary_xor:
5882 case expr_client_state:
5883 return context_numeric;
5884 }
5885 return context_any;
5886 }
5887
5888 static int
op_val(enum expr_op op)5889 op_val(enum expr_op op)
5890 {
5891 switch (op) {
5892 case expr_none:
5893 case expr_match:
5894 case expr_static:
5895 case expr_check:
5896 case expr_substring:
5897 case expr_suffix:
5898 case expr_lcase:
5899 case expr_ucase:
5900 case expr_concat:
5901 case expr_encapsulate:
5902 case expr_host_lookup:
5903 case expr_not:
5904 case expr_option:
5905 case expr_hardware:
5906 case expr_hw_type:
5907 case expr_hw_address:
5908 case expr_packet:
5909 #ifdef keep_expr_const_data_precedence
5910 case expr_const_data:
5911 #endif
5912 case expr_extract_int8:
5913 case expr_extract_int16:
5914 case expr_extract_int32:
5915 case expr_encode_int8:
5916 case expr_encode_int16:
5917 case expr_encode_int32:
5918 case expr_const_int:
5919 case expr_exists:
5920 case expr_variable_exists:
5921 case expr_known:
5922 case expr_binary_to_ascii:
5923 case expr_reverse:
5924 case expr_filename:
5925 case expr_sname:
5926 case expr_pick_first_value:
5927 case expr_host_decl_name:
5928 case expr_config_option:
5929 case expr_leased_address:
5930 case expr_lease_time:
5931 case expr_dns_transaction:
5932 case expr_null:
5933 case expr_variable_reference:
5934 case expr_ns_add:
5935 case expr_ns_delete:
5936 case expr_ns_exists:
5937 case expr_ns_not_exists:
5938 case expr_arg:
5939 case expr_funcall:
5940 case expr_function:
5941 /* XXXDPN: Need to assign sane precedences to these. */
5942 case expr_binary_and:
5943 case expr_binary_or:
5944 case expr_binary_xor:
5945 case expr_client_state:
5946 case expr_gethostname:
5947 case expr_v6relay:
5948 case expr_concat_dclist:
5949 return 100;
5950
5951 case expr_equal:
5952 case expr_not_equal:
5953 case expr_regex_match:
5954 case expr_iregex_match:
5955 return 4;
5956
5957 case expr_or:
5958 case expr_and:
5959 return 3;
5960
5961 case expr_add:
5962 case expr_subtract:
5963 return 2;
5964
5965 case expr_multiply:
5966 case expr_divide:
5967 case expr_remainder:
5968 return 1;
5969 #ifndef keep_expr_const_data_precedence
5970 case expr_const_data:
5971 return 0;
5972 #endif
5973 }
5974 return 100;
5975 }
5976
5977 static int
op_precedence(enum expr_op op1,enum expr_op op2)5978 op_precedence(enum expr_op op1, enum expr_op op2)
5979 {
5980 return op_val(op1) - op_val(op2);
5981 }
5982
5983 static enum expression_context
expression_context(struct element * expr)5984 expression_context(struct element *expr)
5985 {
5986 if (is_data_expression(expr))
5987 return context_data;
5988 if (is_numeric_expression(expr))
5989 return context_numeric;
5990 if (is_boolean_expression(expr))
5991 return context_boolean;
5992 return context_any;
5993 }
5994
5995 static enum expr_op
expression(struct element * expr)5996 expression(struct element *expr)
5997 {
5998 if (expr->type != ELEMENT_MAP)
5999 return expr_none;
6000 if (mapContains(expr, "match"))
6001 return expr_match;
6002 if (mapContains(expr, "check"))
6003 return expr_check;
6004 if (mapContains(expr, "equal"))
6005 return expr_equal;
6006 if (mapContains(expr, "substring"))
6007 return expr_substring;
6008 if (mapContains(expr, "suffix"))
6009 return expr_suffix;
6010 if (mapContains(expr, "concat"))
6011 return expr_concat;
6012 if (mapContains(expr, "and"))
6013 return expr_and;
6014 if (mapContains(expr, "or"))
6015 return expr_or;
6016 if (mapContains(expr, "not"))
6017 return expr_not;
6018 if (mapContains(expr, "option"))
6019 return expr_option;
6020 if (mapContains(expr, "hardware"))
6021 return expr_hardware;
6022 if (mapContains(expr, "hw-type"))
6023 return expr_hw_type;
6024 if (mapContains(expr, "hw-address"))
6025 return expr_hw_address;
6026 if (mapContains(expr, "packet"))
6027 return expr_packet;
6028 if (mapContains(expr, "const-data"))
6029 return expr_const_data;
6030 if (mapContains(expr, "extract-int8"))
6031 return expr_extract_int8;
6032 if (mapContains(expr, "extract-int16"))
6033 return expr_extract_int16;
6034 if (mapContains(expr, "extract-int32"))
6035 return expr_extract_int32;
6036 if (mapContains(expr, "encode-int8"))
6037 return expr_encode_int8;
6038 if (mapContains(expr, "encode-int16"))
6039 return expr_encode_int16;
6040 if (mapContains(expr, "encode-int32"))
6041 return expr_encode_int32;
6042 if (mapContains(expr, "const-int"))
6043 return expr_const_int;
6044 if (mapContains(expr, "exists"))
6045 return expr_exists;
6046 if (mapContains(expr, "encapsulate"))
6047 return expr_encapsulate;
6048 if (mapContains(expr, "known"))
6049 return expr_known;
6050 if (mapContains(expr, "reverse"))
6051 return expr_reverse;
6052 if (mapContains(expr, "leased-address"))
6053 return expr_leased_address;
6054 if (mapContains(expr, "binary-to-ascii"))
6055 return expr_binary_to_ascii;
6056 if (mapContains(expr, "config-option"))
6057 return expr_config_option;
6058 if (mapContains(expr, "host-decl-name"))
6059 return expr_host_decl_name;
6060 if (mapContains(expr, "pick-first-value"))
6061 return expr_pick_first_value;
6062 if (mapContains(expr, "lease-time"))
6063 return expr_lease_time;
6064 if (mapContains(expr, "static"))
6065 return expr_static;
6066 if (mapContains(expr, "not-equal"))
6067 return expr_not_equal;
6068 if (mapContains(expr, "null"))
6069 return expr_null;
6070 if (mapContains(expr, "variable-exists"))
6071 return expr_variable_exists;
6072 if (mapContains(expr, "variable-reference"))
6073 return expr_variable_reference;
6074 if (mapContains(expr, "filename"))
6075 return expr_filename;
6076 if (mapContains(expr, "server-name"))
6077 return expr_sname;
6078 if (mapContains(expr, "arguments"))
6079 return expr_arg;
6080 if (mapContains(expr, "funcall"))
6081 return expr_funcall;
6082 if (mapContains(expr, "function"))
6083 return expr_function;
6084 if (mapContains(expr, "add"))
6085 return expr_add;
6086 if (mapContains(expr, "subtract"))
6087 return expr_subtract;
6088 if (mapContains(expr, "multiply"))
6089 return expr_multiply;
6090 if (mapContains(expr, "divide"))
6091 return expr_divide;
6092 if (mapContains(expr, "remainder"))
6093 return expr_remainder;
6094 if (mapContains(expr, "binary-and"))
6095 return expr_binary_and;
6096 if (mapContains(expr, "binary-or"))
6097 return expr_binary_or;
6098 if (mapContains(expr, "binary-xor"))
6099 return expr_binary_xor;
6100 if (mapContains(expr, "client-state"))
6101 return expr_client_state;
6102 if (mapContains(expr, "uppercase"))
6103 return expr_ucase;
6104 if (mapContains(expr, "lowercase"))
6105 return expr_lcase;
6106 if (mapContains(expr, "regex-match"))
6107 return expr_regex_match;
6108 if (mapContains(expr, "iregex-match"))
6109 return expr_iregex_match;
6110 if (mapContains(expr, "gethostname"))
6111 return expr_gethostname;
6112 if (mapContains(expr, "v6relay"))
6113 return expr_v6relay;
6114 if (TAILQ_EMPTY(&expr->value.map_value)) {
6115 fprintf(stderr, "empty expression");
6116 if (expr->key != NULL)
6117 fprintf(stderr, " for %s", expr->key);
6118 } else {
6119 struct element *item;
6120 isc_boolean_t first = ISC_TRUE;
6121
6122 TAILQ_FOREACH(item, &expr->value.map_value) {
6123 const char *key;
6124
6125 key = item->key;
6126 if (key == NULL)
6127 continue;
6128 if (first)
6129 fprintf(stderr, ": %s", key);
6130 else
6131 fprintf(stderr, ", %s", key);
6132 first = ISC_FALSE;
6133 }
6134 }
6135 fputs("\n", stderr);
6136 return expr_none;
6137 }
6138
6139 int
expr_precedence(enum expr_op op,struct element * expr)6140 expr_precedence(enum expr_op op, struct element *expr)
6141 {
6142 if (expr->type != ELEMENT_MAP)
6143 return op_val(op);
6144 return op_val(op) - op_val(expression(expr));
6145 }
6146