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
41static struct element *reduce_equal_expression(struct element *left,
42					       struct element *right);
43static 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
56struct element *
57reduce_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
379struct element *
380reduce_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
950struct element *
951reduce_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
964static struct element *
965reduce_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
1012static void
1013debug(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