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