Home | History | Annotate | Line # | Download | only in keama
      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 (at) 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 *
     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 *
    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 *
    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 *
    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
   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