xref: /netbsd-src/external/mpl/dhcp/dist/keama/reduce.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: reduce.c,v 1.3 2022/04/03 01:10:59 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  *   Internet Systems Consortium, Inc.
19  *   PO Box 360
20  *   Newmarket, NH 03857 USA
21  *   <info@isc.org>
22  *   https://www.isc.org/
23  *
24  */
25 
26 #include <sys/cdefs.h>
27 __RCSID("$NetBSD: reduce.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
28 
29 #include "keama.h"
30 
31 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <arpa/inet.h>
34 #include <ctype.h>
35 #include <netdb.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 static struct element *reduce_equal_expression(struct element *left,
42 					       struct element *right);
43 static void debug(const char* fmt, ...);
44 
45 /*
46  * boolean_expression :== CHECK STRING |
47  *                        NOT boolean-expression |
48  *                        data-expression EQUAL data-expression |
49  *                        data-expression BANG EQUAL data-expression |
50  *                        data-expression REGEX_MATCH data-expression |
51  *                        boolean-expression AND boolean-expression |
52  *                        boolean-expression OR boolean-expression
53  *                        EXISTS OPTION-NAME
54  */
55 
56 struct element *
reduce_boolean_expression(struct element * expr)57 reduce_boolean_expression(struct element *expr)
58 {
59 	/* trivial case: already done */
60 	if (expr->type == ELEMENT_BOOLEAN)
61 		return expr;
62 
63 	/*
64 	 * From is_boolean_expression
65 	 */
66 
67 	if (expr->type != ELEMENT_MAP)
68 		return NULL;
69 
70 	/* check */
71 	if (mapContains(expr, "check"))
72 		/*
73 		 * syntax := { "check": <collection_name> }
74 		 * semantic: check_collection
75 		 *  on server try to match classes of the collection
76 		 */
77 		return NULL;
78 
79 
80 	/* exists */
81 	if (mapContains(expr, "exists")) {
82 		/*
83 		 * syntax := { "exists":
84 		 *             { "universe": <option_space_old>,
85 		 *               "name":  <option_name> }
86 		 *           }
87 		 * semantic: check universe/code from incoming packet
88 		 */
89 		struct element *arg;
90 		struct element *universe;
91 		struct element *name;
92 		struct option *option;
93 		char result[80];
94 
95 		arg = mapGet(expr, "exists");
96 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
97 			debug("can't get exists argument");
98 			return NULL;
99 		}
100 		universe = mapGet(arg, "universe");
101 		if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
102 			debug("can't get exists option universe");
103 			return NULL;
104 		}
105 		name = mapGet(arg, "name");
106 		if ((name == NULL) || (name->type != ELEMENT_STRING)) {
107 			debug("can't get exists option name");
108 			return NULL;
109 		}
110 		option = option_lookup_name(stringValue(universe)->content,
111 					    stringValue(name)->content);
112 		if ((option == NULL) || (option->code == 0))
113 			return NULL;
114 		if (((local_family == AF_INET) &&
115 		     (strcmp(option->space->name, "dhcp4") != 0)) ||
116 		    ((local_family == AF_INET6) &&
117 		     (strcmp(option->space->name, "dhcp6") != 0)))
118 			return NULL;
119 		snprintf(result, sizeof(result),
120 			 "option[%u].exists", option->code);
121 		return createString(makeString(-1, result));
122 	}
123 
124 	/* variable-exists */
125 	if (mapContains(expr, "variable-exists"))
126 		/*
127 		 * syntax := { "variable-exists": <variable_name> }
128 		 * semantics: find_binding(scope, name)
129 		 */
130 		return NULL;
131 
132 	/* equal */
133 	if (mapContains(expr, "equal")) {
134 		/*
135 		 * syntax := { "equal":
136 		 *             { "left":  <expression>,
137 		 *               "right": <expression> }
138 		 *           }
139 		 * semantics: evaluate branches and return true
140 		 * if same type and same value
141 		 */
142 		struct element *arg;
143 		struct element *left;
144 		struct element *right;
145 
146 		arg = mapGet(expr, "equal");
147 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
148 			debug("can't get equal argument");
149 			return NULL;
150 		}
151 		left = mapGet(arg, "left");
152 		if (left == NULL) {
153 			debug("can't get equal left branch");
154 			return NULL;
155 		}
156 		right = mapGet(arg, "right");
157 		if (right == NULL) {
158 			debug("can't get equal right branch");
159 			return NULL;
160 		}
161 		return reduce_equal_expression(left, right);
162 	}
163 
164 	/* not-equal */
165 	if (mapContains(expr, "not-equal")) {
166 		/*
167 		 * syntax := { "not-equal":
168 		 *             { "left":  <expression>,
169                  *               "right": <expression> }
170                  *           }
171                  * semantics: evaluate branches and return true
172                  * if different type or different value
173                  */
174 		struct element *arg;
175 		struct element *left;
176 		struct element *right;
177 		struct element *equal;
178 		struct string *result;
179 
180 		arg = mapGet(expr, "not-equal");
181 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
182 			debug("can't get not-equal argument");
183 			return NULL;
184 		}
185 		left = mapGet(arg, "left");
186 		if (left == NULL) {
187 			debug("can't get not-equal left branch");
188 			return NULL;
189 		}
190 		right = mapGet(arg, "right");
191 		if (right == NULL) {
192 			debug("can't get not-equal right branch");
193 			return NULL;
194 		}
195 		equal = reduce_equal_expression(left, right);
196 		if ((equal == NULL) || (equal->type != ELEMENT_STRING))
197 			return NULL;
198 		result = makeString(-1, "not (");
199 		concatString(result, stringValue(equal));
200 		appendString(result, ")");
201 		return createString(result);
202 	}
203 
204 	/* regex-match */
205 	if (mapContains(expr, "regex-match"))
206 		/*
207 		 * syntax := { "regex-match":
208 		 *             { "left":  <data_expression>,
209 		 *               "right": <data_expression> }
210 		 *           }
211 		 * semantics: evaluate branches, compile right as a
212 		 * regex and apply it to left
213 		 */
214 		return NULL;
215 
216 	/* iregex-match */
217 	if (mapContains(expr, "iregex-match"))
218 		/*
219 		 * syntax := { "regex-match":
220 		 *             { "left":  <data_expression>,
221 		 *               "right": <data_expression> }
222 		 *           }
223 		 * semantics: evaluate branches, compile right as a
224 		 * case insensistive regex and apply it to left
225 		 */
226 		return NULL;
227 
228 	/* and */
229 	if (mapContains(expr, "and")) {
230 		/*
231 		 * syntax := { "and":
232 		 *             { "left":  <boolean_expression>,
233 		 *               "right": <boolean_expression> }
234 		 *           }
235 		 * semantics: evaluate branches, return true
236 		 * if both are true
237 		 */
238 		struct element *arg;
239 		struct element *left;
240 		struct element *right;
241 		struct string *result;
242 
243 		arg = mapGet(expr, "and");
244 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
245 			debug("can't get and argument");
246 			return NULL;
247 		}
248 		left = mapGet(arg, "left");
249 		if (left == NULL) {
250 			debug("can't get and left branch");
251 			return NULL;
252 		}
253 		right = mapGet(arg, "right");
254 		if (right == NULL) {
255 			debug("can't get and right branch");
256 			return NULL;
257 		}
258 		left = reduce_boolean_expression(left);
259 		if ((left == NULL) || (left->type != ELEMENT_STRING))
260 			return NULL;
261 		right = reduce_boolean_expression(right);
262 		if ((right == NULL) || (right->type != ELEMENT_STRING))
263 			return NULL;
264 		result = makeString(-1, "(");
265 		concatString(result, stringValue(left));
266 		appendString(result, ") and (");
267 		concatString(result, stringValue(right));
268 		appendString(result, ")");
269 		return createString(result);
270 	}
271 
272 	/* or */
273 	if (mapContains(expr, "or")) {
274 		/*
275 		 * syntax := { "or":
276 		 *             { "left":  <boolean_expression>,
277 		 *               "right": <boolean_expression> }
278 		 *           }
279 		 * semantics: evaluate branches, return true
280 		 * if any is true
281 		 */
282 		struct element *arg;
283 		struct element *left;
284 		struct element *right;
285 		struct string *result;
286 
287 		arg = mapGet(expr, "or");
288 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
289 			debug("can't get or argument");
290 			return NULL;
291 		}
292 		left = mapGet(arg, "left");
293 		if (left == NULL) {
294 			debug("can't get or left branch");
295 			return NULL;
296 		}
297 		right = mapGet(arg, "right");
298 		if (right == NULL) {
299 			debug("can't get or right branch");
300 			return NULL;
301 		}
302 		left = reduce_boolean_expression(left);
303 		if ((left == NULL) || (left->type != ELEMENT_STRING))
304 			return NULL;
305 		right = reduce_boolean_expression(right);
306 		if ((right == NULL) || (right->type != ELEMENT_STRING))
307 			return NULL;
308 		result = makeString(-1, "(");
309 		concatString(result, stringValue(left));
310 		appendString(result, ") or (");
311 		concatString(result, stringValue(right));
312 		appendString(result, ")");
313 		return createString(result);
314 	}
315 
316 	/* not */
317 	if (mapContains(expr, "not")) {
318 		/*
319 		 * syntax := { "not": <boolean_expression> }
320 		 * semantic: evaluate its branch and return its negation
321 		 */
322 		struct element *arg;
323 		struct string *result;
324 
325 		arg = mapGet(expr, "not");
326 		if (arg == NULL) {
327 			debug("can't get not argument");
328 			return NULL;
329 		}
330 		arg = reduce_boolean_expression(arg);
331 		if ((arg == NULL) || (arg->type != ELEMENT_STRING))
332 			return NULL;
333 		result = makeString(-1, "not (");
334 		concatString(result, stringValue(arg));
335 		appendString(result, ")");
336 		return createString(result);
337 	}
338 
339 	/* known */
340 	if (mapContains(expr, "known"))
341 		/*
342 		 * syntax := { "known": null }
343 		 * semantics: client is known, i.e., has a matching
344 		 * host declaration (aka reservation in Kea)
345 		 */
346 		return NULL;
347 
348 	/* static */
349 	if (mapContains(expr, "static"))
350 		/*
351 		 * syntax := { "static": null }
352 		 * semantics: lease is static (doesn't exist in Kea)
353 		 */
354 		return NULL;
355 
356 	return NULL;
357 }
358 
359 /*
360  * data_expression :== SUBSTRING LPAREN data-expression COMMA
361  *                                      numeric-expression COMMA
362  *                                      numeric-expression RPAREN |
363  *                     CONCAT LPAREN data-expression COMMA
364  *                                      data-expression RPAREN
365  *                     SUFFIX LPAREN data_expression COMMA
366  *                                   numeric-expression RPAREN |
367  *                     LCASE LPAREN data_expression RPAREN |
368  *                     UCASE LPAREN data_expression RPAREN |
369  *                     OPTION option_name |
370  *                     HARDWARE |
371  *                     PACKET LPAREN numeric-expression COMMA
372  *                                   numeric-expression RPAREN |
373  *                     V6RELAY LPAREN numeric-expression COMMA
374  *                                    data-expression RPAREN |
375  *                     STRING |
376  *                     colon_separated_hex_list
377  */
378 
379 struct element *
reduce_data_expression(struct element * expr)380 reduce_data_expression(struct element *expr)
381 {
382 	/* trivial case: already done */
383 	if (expr->type == ELEMENT_STRING)
384 		return expr;
385 
386 	/*
387 	 * From is_data_expression
388 	 */
389 
390 	if (expr->type != ELEMENT_MAP)
391 		return NULL;
392 
393 	/* substring */
394 	if (mapContains(expr, "substring")) {
395 		/*
396 		 * syntax := { "substring":
397 		 *             { "expression": <data_expression>,
398 		 *               "offset":     <numeric_expression>,
399 		 *               "length":     <numeric_expression> }
400 		 *           }
401 		 * semantic: evaluate arguments, if the string is
402 		 * shorter than offset return "" else return substring
403 		 */
404 		struct element *arg;
405 		struct element *string;
406 		struct element *offset;
407 		struct element *length;
408 		struct string *result;
409 		int64_t off;
410 		int64_t len;
411 		char buf[80];
412 
413 		arg = mapGet(expr, "substring");
414 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
415 			debug("can't get substring argument");
416 			return NULL;
417 		}
418 		string = mapGet(arg, "expression");
419 		if (string == NULL) {
420 			debug("can't get substring expression");
421 			return NULL;
422 		}
423 		offset = mapGet(arg, "offset");
424 		if (offset  == NULL) {
425 			debug("can't get substring offset");
426 			return NULL;
427 		}
428 		length = mapGet(arg, "length");
429 		if (length  == NULL) {
430 			debug("can't get substring length");
431 			return NULL;
432 		}
433 		/* can't be a literal as it was evaluated before */
434 		string = reduce_data_expression(string);
435 		if ((string == NULL) || (string->type != ELEMENT_STRING))
436 			return NULL;
437 		offset = reduce_numeric_expression(offset);
438 		if ((offset == NULL) || (offset->type != ELEMENT_INTEGER))
439 			return NULL;
440 		off = intValue(offset);
441 		if (off < 0) {
442 			debug("substring with a negative offset (%lld)",
443 			      (long long)off);
444 			return NULL;
445 		}
446 		length = reduce_numeric_expression(length);
447 		if ((length == NULL) || (length->type != ELEMENT_INTEGER))
448 			return NULL;
449 		len = intValue(length);
450 		if (len < 0) {
451 			debug("substring with a negative length (%lld)",
452 			      (long long)len);
453 			return NULL;
454 		}
455 		result = makeString(-1, "substring(");
456 		concatString(result, stringValue(string));
457 		snprintf(buf, sizeof(buf),
458 			 ",%u,%u)", (unsigned)off, (unsigned)len);
459 		appendString(result, buf);
460 		return createString(result);
461 	}
462 
463 	/* suffix */
464 	if (mapContains(expr, "suffix")) {
465 		/*
466 		 * syntax := { "suffix":
467 		 *             { "expression": <data_expression>,
468 		 *               "length":     <numeric_expression> }
469 		 *           }
470 		 * semantic: evaluate arguments, if the string is
471 		 * shorter than length return it else return suffix
472 		 */
473 		struct element *arg;
474 		struct element *string;
475 		struct element *length;
476 		struct string *result;
477 		int64_t len;
478 		char buf[80];
479 
480 		arg = mapGet(expr, "suffix");
481 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
482 			debug("can't get suffix argument");
483 			return NULL;
484 		}
485 		string = mapGet(arg, "expression");
486 		if (string == NULL) {
487 			debug("can't get suffix expression");
488 			return NULL;
489 		}
490 		length = mapGet(arg, "length");
491 		if (length  == NULL) {
492 			debug("can't get suffix length");
493 			return NULL;
494 		}
495 		/* can't be a literal as it was evaluated before */
496 		string = reduce_data_expression(string);
497 		if ((string == NULL) || (string->type != ELEMENT_STRING))
498 			return NULL;
499 		length = reduce_numeric_expression(length);
500 		if ((length == NULL) || (length->type != ELEMENT_INTEGER))
501 			return NULL;
502 		len = intValue(length);
503 		if (len < 0) {
504 			debug("suffix with a negative length (%lld)",
505 			      (long long)len);
506 			return NULL;
507 		}
508 		result = makeString(-1, "substring(");
509 		concatString(result, stringValue(string));
510 		snprintf(buf, sizeof(buf), ",-%u,all)", (unsigned)len);
511 		appendString(result, buf);
512 		return createString(result);
513 	}
514 
515 	/* lowercase */
516 	if (mapContains(expr, "lowercase"))
517 		/*
518 		 * syntax := { "lowercase": <data_expression> }
519 		 * semantic: evaluate its argument and apply tolower to
520 		 * its content
521 		 */
522 		return NULL;
523 
524 	/* uppercase */
525 	if (mapContains(expr, "uppercase"))
526 		/*
527 		 * syntax := { "uppercase": <data_expression> }
528 		 * semantic: evaluate its argument and apply toupper to
529 		 * its content
530 		 */
531 		return NULL;
532 
533 	/* option */
534 	if (mapContains(expr, "option")) {
535 		/*
536 		 * syntax := { "option":
537 		 *             { "universe": <option_space_old>,
538 		 *               "name":  <option_name> }
539 		 *           }
540 		 * semantic: get universe/code option from incoming packet
541 		 */
542 		struct element *arg;
543 		struct element *universe;
544 		struct element *name;
545 		struct option *option;
546 		char result[80];
547 
548 		arg = mapGet(expr, "option");
549 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
550 			debug("can't get option argument");
551 			return NULL;
552 		}
553 		universe = mapGet(arg, "universe");
554 		if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
555 			debug("can't get option universe");
556 			return NULL;
557 		}
558 		name = mapGet(arg, "name");
559 		if ((name == NULL) || (name->type != ELEMENT_STRING)) {
560 			debug("can't get option name");
561 			return NULL;
562 		}
563 		option = option_lookup_name(stringValue(universe)->content,
564 					    stringValue(name)->content);
565 		if ((option == NULL) || (option->code == 0))
566 			return NULL;
567 		if (((local_family == AF_INET) &&
568 		     (strcmp(option->space->name, "dhcp4") != 0)) ||
569 		    ((local_family == AF_INET6) &&
570 		     (strcmp(option->space->name, "dhcp6") != 0)))
571 			return NULL;
572 		snprintf(result, sizeof(result),
573 			 "option[%u].hex", option->code);
574 		return createString(makeString(-1, result));
575 	}
576 
577 	/* hardware */
578 	if (mapContains(expr, "hardware")) {
579 		/*
580 		 * syntax := { "hardware": null }
581 		 * semantic: get mac type and address from incoming packet
582 		 */
583 		struct string *result;
584 
585 		if (local_family != AF_INET) {
586 			debug("get hardware for DHCPv6");
587 			return NULL;
588 		}
589 		result = makeString(-1,
590 			    "concat(substring(pkt4.htype,-1,all),pkt4.mac)");
591 		return createString(result);
592 	}
593 
594 	/* hw-type */
595 	if (mapContains(expr, "hw-type")) {
596 		/*
597 		 * ADDED
598 		 * syntax := { "hw-type": null }
599 		 * semantic: get mac type from incoming packet
600 		 */
601 		struct string *result;
602 
603 		if (local_family != AF_INET) {
604 			debug("get hw-type for DHCPv6");
605 			return NULL;
606 		}
607 		result = makeString(-1, "substring(pkt4.htype,-1,all)");
608 		return createString(result);
609 	}
610 
611 	/* hw-address */
612 	if (mapContains(expr, "hw-address")) {
613 		/*
614 		 * ADDED
615 		 * syntax := { "hw-address": null }
616 		 * semantic: get mac address from incoming packet
617 		 */
618 		struct string *result;
619 
620 		if (local_family != AF_INET) {
621 			debug("get hw-address for DHCPv6");
622 			return NULL;
623 		}
624 		result = makeString(-1, "pkt4.mac");
625 		return createString(result);
626 	}
627 
628 	/* const-data */
629 	if (mapContains(expr, "const-data")) {
630 		/*
631 		 * syntax := { "const-data": <string> }
632 		 * semantic: embedded string value
633 		 */
634 		struct element *arg;
635 
636 		arg = mapGet(expr, "const-data");
637 		if ((arg == NULL) || (arg->type != ELEMENT_STRING)) {
638 			debug("can't get const-data argument");
639 			return NULL;
640 		}
641 		return createString(stringValue(arg));
642 	}
643 
644 	/* packet */
645 	if (mapContains(expr, "packet"))
646 		/*
647 		 * syntax := { "packet":
648 		 *             { "offset": <numeric_expression>,
649 		 *               "length": <numeric_expression> }
650 		 *           }
651 		 * semantic: return the selected substring of the incoming
652 		 * packet content
653 		 */
654 		return NULL;
655 
656 	/* concat */
657 	if (mapContains(expr, "concat")) {
658 		/*
659 		 * syntax := { "concat":
660 		 *             { "left":  <data_expression>,
661 		 *               "right": <data_expression> }
662 		 *           }
663 		 * semantic: evaluate arguments and return the concatenation
664 		 */
665 		struct element *arg;
666 		struct element *left;
667 		struct element *right;
668 		struct string *result;
669 
670 		arg = mapGet(expr, "concat");
671 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
672 			debug("can't get concat argument");
673 			return NULL;
674 		}
675 		left = mapGet(arg, "left");
676 		if (left == NULL) {
677 			debug("can't get concat left branch");
678 			return NULL;
679 		}
680 		right = mapGet(arg, "right");
681 		if (right == NULL) {
682 			debug("can't get concat right branch");
683 			return NULL;
684 		}
685 		/* left is a literal case */
686 		if (left->type == ELEMENT_STRING) {
687 			/* can't be a literal as it was evaluated before */
688 			right = reduce_data_expression(right);
689 			if ((right == NULL) || (right->type != ELEMENT_STRING))
690 				return NULL;
691 			result = makeString(-1, "concat(");
692 			concatString(result, quote(stringValue(left)));
693 			appendString(result, ", ");
694 			concatString(result, stringValue(right));
695 			appendString(result, ")");
696 			return createString(result);
697 		}
698 		left = reduce_data_expression(left);
699 		if ((left == NULL) || (left->type != ELEMENT_STRING))
700 			return NULL;
701 		/* right is a literal case */
702 		if (right->type == ELEMENT_STRING) {
703 			/* literal left was handled before */
704 			result = makeString(-1, "concat(");
705 			concatString(result, stringValue(left));
706 			appendString(result, ", ");
707 			concatString(result, quote(stringValue(right)));
708 			appendString(result, ")");
709 			return createString(result);
710 		}
711 		right = reduce_data_expression(right);
712 		if ((right == NULL) || (right->type != ELEMENT_STRING))
713 			return NULL;
714 		result = makeString(-1, "concat(");
715 		concatString(result, stringValue(left));
716 		appendString(result, ", ");
717 		concatString(result, stringValue(right));
718 		appendString(result, ")");
719 		return createString(result);
720 	}
721 
722 	/* encapsulate */
723 	if (mapContains(expr, "encapsulate"))
724 		/*
725 		 * syntax := { "encapsulate": <encapsulated_space> }
726 		 * semantic: encapsulate options of the given space
727 		 */
728 		return NULL;
729 
730 	/* encode-int8 */
731 	if (mapContains(expr, "encode-int8"))
732 		/*
733 		 * syntax := { "encode-int8": <numeric_expression> }
734 		 * semantic: return a string buffer with the evaluated
735 		 * number as content
736 		 */
737 		return NULL;
738 
739 	/* encode-int16 */
740 	if (mapContains(expr, "encode-int16"))
741 		/*
742 		 * syntax := { "encode-int16": <numeric_expression> }
743 		 * semantic: return a string buffer with the evaluated
744 		 * number as content
745 		 */
746 		return NULL;
747 
748 	/* encode-int32 */
749 	if (mapContains(expr, "encode-int32"))
750 		/*
751 		 * syntax := { "encode-int32": <numeric_expression> }
752 		 * semantic: return a string buffer with the evaluated
753 		 * number as content
754 		 */
755 		return NULL;
756 
757 	/* gethostbyname */
758 	if (mapContains(expr, "gethostbyname"))
759 		/*
760 		 * syntax := { "gethostbyname": <string> }
761 		 * semantic: call gethostbyname and return
762 		 * a binary buffer with addresses
763 		 */
764 		return NULL;
765 
766 	/* binary-to-ascii */
767 	if (mapContains(expr, "binary-to-ascii"))
768 		/*
769 		 * syntax := { "binary-to-ascii":
770 		 *             { "base":      <numeric_expression 2..16>,
771 		 *               "width":     <numeric_expression 8, 16 or 32>,
772 		 *               "separator": <data_expression>,
773 		 *               "buffer":    <data_expression> }
774 		 *           }
775 		 * semantic: split the input buffer into int8/16/32 numbers,
776 		 * output them separated by the given string
777 		 */
778 		return NULL;
779 
780 	/* filename */
781 	if (mapContains(expr, "filename"))
782 		/*
783 		 * syntax := { "filename": null }
784 		 * semantic: get filename field from incoming DHCPv4 packet
785 		 */
786 		return NULL;
787 
788 	/* server-name */
789 	if (mapContains(expr, "server-name"))
790 		/*
791 		 * syntax := { "server-name": null }
792 		 * semantic: get server-name field from incoming DHCPv4 packet
793 		 */
794 		return NULL;
795 
796 	/* reverse */
797 	if (mapContains(expr, "reverse"))
798 		/*
799 		 * syntax := { "reverse":
800 		 *             { "width": <numeric_expression>,
801 		 *               "buffer":    <data_expression> }
802 		 *           }
803 		 * semantic: reverse the input buffer by width chunks of bytes
804 		 */
805 		return NULL;
806 
807 	/* pick-first-value */
808 	if (mapContains(expr, "pick-first-value"))
809 		/*
810 		 * syntax := { "pick-first-value":
811 		 *             [ <data_expression>, ... ]
812 		 *           }
813 		 * semantic: evaluates expressions and return the first
814 		 * not null, return null if all are null
815 		 */
816 		return NULL;
817 
818 	/* host-decl-name */
819 	if (mapContains(expr, "host-decl-name"))
820 		/*
821 		 * syntax := { "host-decl-name": null }
822 		 * semantic: return the name of the matching host
823 		 * declaration (aka revervation in kea) or null
824 		 */
825 		return NULL;
826 
827 	/* leased-address */
828 	if (mapContains(expr, "leased-address"))
829 		/*
830 		 * syntax := { "leased-address": null }
831 		 * semantic: return the address of the assigned lease or
832 		 * log a message
833 		 */
834 		return NULL;
835 
836 	/* config-option */
837 	if (mapContains(expr, "config-option"))
838 		/*
839 		 * syntax := { "config-option":
840 		 *             { "universe": <option_space_old>,
841 		 *               "name":  <option_name> }
842 		 *           }
843 		 * semantic: get universe/code option to send
844 		 */
845 		return NULL;
846 
847 	/* null */
848 	if (mapContains(expr, "null")) {
849 		/*
850 		 * syntax := { "null": null }
851 		 * semantic: return null
852 		 */
853 		debug("unexpected null: this expression was not evaluated");
854 		return NULL;
855 	}
856 
857 	/* gethostname */
858 	if (mapContains(expr, "gethostname")) {
859 		/*
860 		 * syntax := { "gethostname": null }
861 		 * semantic: return gethostname
862 		 */
863 		debug("unexpected gethostname: this expression was not "
864 		      "evaluated");
865 		return NULL;
866 	}
867 
868 	/* v6relay */
869 	if (mapContains(expr, "v6relay")) {
870 		/*
871 		 * syntax := { "v6relay":
872 		 *             { "relay": <numeric_expression>,
873 		 *               "relay-option" <data_expression> }
874 		 *           }
875 		 * semantic: relay is a counter from client, 0 is no-op,
876 		 * 1 is the relay closest to the client, etc, option
877 		 * is a dhcp6 option ans is return when found
878 		 */
879 		struct element *arg;
880 		struct element *relay;
881 		struct element *universe;
882 		struct element *name;
883 		struct option *option;
884 		int64_t r;
885 		char result[100];
886 
887 		if (local_family != AF_INET6) {
888 			debug("get v6relay for DHCPv4");
889 			return NULL;
890 		}
891 		arg = mapGet(expr, "v6relay");
892 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
893 			debug("can't get v6relay argument");
894 			return NULL;
895 		}
896 		relay = mapGet(arg, "relay");
897 		if (relay == NULL) {
898 			debug("can't get v6relay relay");
899 			return NULL;
900 		}
901 		relay = reduce_numeric_expression(relay);
902 		if ((relay == NULL) || (relay->type != ELEMENT_INTEGER))
903 			return NULL;
904 		r = intValue(relay);
905 		if (r < 0) {
906 			debug("v6relay called with illegal relay (%lld)",
907 			      (long long)r);
908 			return NULL;
909 		}
910 		arg = mapGet(arg, "relay-option");
911 		if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
912 			debug("can't get v6relay relay-option");
913 			return NULL;
914 		}
915 		universe = mapGet(arg, "universe");
916 		if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
917 			debug("can't get v6relay option universe");
918 			NULL;
919 		}
920 		name = mapGet(arg, "name");
921 		if ((name == NULL) || (name->type != ELEMENT_STRING)) {
922 			debug("can't get v6relay option name");
923 			return NULL;
924 		}
925 		option = option_lookup_name(stringValue(universe)->content,
926 					    stringValue(name)->content);
927 		if ((option == NULL) || (option->code == 0) ||
928 		    (strcmp(option->space->name, "dhcp6") != 0))
929 			return NULL;
930 		if (r == 0)
931 			snprintf(result, sizeof(result),
932 				 "option[%u].hex", option->code);
933 		else {
934 			/* r > MAX_V6RELAY_HOPS means the relay closest
935 			   to server */
936 			if (r > MAX_V6RELAY_HOPS)
937 				r = 0;
938 			/* Kea counts from the server, use negative nesting
939 			   levels to count from the client */
940 			snprintf(result, sizeof(result),
941 				 "relay6[%d].option[%u].hex",
942 				 (int)-r, option->code);
943 		}
944 		return createString(makeString(-1, result));
945 	}
946 
947 	return NULL;
948 }
949 
950 struct element *
reduce_numeric_expression(struct element * expr)951 reduce_numeric_expression(struct element *expr)
952 {
953 	/* trivial case: already done */
954 	if (expr->type == ELEMENT_INTEGER)
955 		return expr;
956 
957 	if (expr->type != ELEMENT_MAP)
958 		return NULL;
959 
960 	/* Kea has no numeric operators... */
961 	return NULL;
962 }
963 
964 static struct element *
reduce_equal_expression(struct element * left,struct element * right)965 reduce_equal_expression(struct element *left, struct element *right)
966 {
967 	struct string *result;
968 
969 	/*
970 	 * numeric case was handled by evaluation
971 	 */
972 
973 	if (!is_data_expression(left) || !is_data_expression(right))
974 		return NULL;
975 
976 	/* left is a literal case */
977 	if (left->type == ELEMENT_STRING) {
978 		/* can't be a literal as it was evaluated before */
979 		right = reduce_data_expression(right);
980 		if ((right == NULL) || (right->type != ELEMENT_STRING))
981 			return NULL;
982 		result = allocString();
983 		concatString(result, quote(stringValue(left)));
984 		appendString(result, " == ");
985 		concatString(result, stringValue(right));
986 		return createString(result);
987 	}
988 	left = reduce_data_expression(left);
989 	if ((left == NULL) || (left->type != ELEMENT_STRING))
990 		return NULL;
991 
992 	/* right is a literal case */
993 	if (right->type == ELEMENT_STRING) {
994 		/* literal left was handled before */
995 		result = allocString();
996 		concatString(result, stringValue(left));
997 		appendString(result, " == ");
998 		concatString(result, quote(stringValue(right)));
999 		return createString(result);
1000 	}
1001 	right = reduce_data_expression(right);
1002 	if ((right == NULL) || (right->type != ELEMENT_STRING))
1003 		return NULL;
1004 
1005 	result = allocString();
1006 	concatString(result, stringValue(left));
1007 	appendString(result, " == ");
1008 	concatString(result, stringValue(right));
1009 	return createString(result);
1010 }
1011 
1012 static void
debug(const char * fmt,...)1013 debug(const char* fmt, ...)
1014 {
1015 	va_list list;
1016 
1017 	va_start(list, fmt);
1018 	vfprintf(stderr, fmt, list);
1019 	fprintf(stderr, "\n");
1020 	va_end(list);
1021 }
1022