From: Alan T. DeKok Date: Thu, 26 May 2022 17:50:35 +0000 (-0400) Subject: cleanups, fixes, and tests to handle many condition tests X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9da42fc7d9b05d124eeccdb26198c70b1bd75b6f;p=thirdparty%2Ffreeradius-server.git cleanups, fixes, and tests to handle many condition tests Some of the results are different due to minor implementation differences. Others are omitted because we're no longer doing that. Others are marked up with a "@todo", because they're arguably wrong. --- diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index 2c985cb7ddb..da8a6e39b62 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -56,9 +56,6 @@ RCSID("$Id$") * the time, but not all of the time. There are currently hacks in the "upcast" code here to fix this, * but it's a hack. * - * @todo - tmpl_aprint doesn't print casts! Changing that would likely mean changing many, many, tests. - * So we'll leave that later. - * * @todo - add instantiation routines for regex and assignment operations. This lets us do things * like: * if ((&foo += 4) > 6) ... @@ -414,6 +411,11 @@ static int xlat_logical_instantiate(xlat_inst_ctx_t const *xctx) { xlat_logical_inst_t *inst = talloc_get_type_abort(xctx->inst, xlat_logical_inst_t); + /* + * @todo - set the 'can purify' flag + * + * @todo - have a special "purify" callback, or a *partial* evaluation function? + */ inst->argc = xlat_flatten_compiled_argv(inst, &inst->argv, xctx->ex->call.args); inst->sense = (xctx->ex->call.func->token == T_LOR); @@ -1105,10 +1107,7 @@ static ssize_t tokenize_field(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_ node->flags.needs_resolving = tmpl_needs_resolving(node->vpt); /* - * @todo - this doesn't quite work yet. We really want - * to start out this node as "pure", and merge in the - * child flags. But if we do that, then the xlat_eval() - * code dies, because we don't (yet) handle TMPL_XLAT or TMPL_EXEC + * Don't keep an intermediate tmpl. */ if (tmpl_contains_xlat(node->vpt)) { xlat_exp_head_t *xlat = tmpl_xlat(node->vpt); @@ -1123,7 +1122,6 @@ static ssize_t tokenize_field(xlat_exp_head_t *head, xlat_exp_t **out, fr_sbuff_ node->flags = xlat->flags; } - /* don't merge flags. That will happen when the node is added to the head */ /* @@ -1418,7 +1416,6 @@ static const fr_sbuff_term_t operator_terms = FR_SBUFF_TERMS( L("-"), L("/"), L("*"), - L(":"), L("="), L("%"), L("!"), diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 07bb030d063..e99a6bd73d2 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -1200,6 +1200,12 @@ ssize_t xlat_print_node(fr_sbuff_t *out, xlat_exp_head_t const *head, xlat_exp_t goto done; case XLAT_TMPL: + if (node->vpt->rules.cast != FR_TYPE_NULL) { + FR_SBUFF_IN_CHAR_RETURN(out, '('); + FR_SBUFF_IN_STRCPY_RETURN(out, fr_type_to_str(node->vpt->rules.cast)); + FR_SBUFF_IN_CHAR_RETURN(out, ')'); + } + if (tmpl_is_data(node->vpt)) { FR_SBUFF_RETURN(fr_value_box_print_quoted, out, tmpl_value(node->vpt), node->vpt->quote); goto done; diff --git a/src/tests/unit/all.mk b/src/tests/unit/all.mk index 594a400c610..f2f95fd8fc4 100644 --- a/src/tests/unit/all.mk +++ b/src/tests/unit/all.mk @@ -67,7 +67,7 @@ test.unit.condition: $(addprefix $(OUTPUT)/,$(filter condition/%.txt,$(FILES))) # # Add special command-line flag for purify tests. # -$(BUILD_DIR)/tests/unit/xlat/purify.txt: PURIFY=-p +$(BUILD_DIR)/tests/unit/xlat/purify.txt $(BUILD_DIR)/tests/unit/xlat/cond_base.txt: PURIFY=-p # # And the actual script to run each test. diff --git a/src/tests/unit/xlat/cond_base.txt b/src/tests/unit/xlat/cond_base.txt new file mode 100644 index 00000000000..8775b910d2d --- /dev/null +++ b/src/tests/unit/xlat/cond_base.txt @@ -0,0 +1,697 @@ +# +# Tests for parsing conditions in the new xlat_expr framework. +# +# $Id$ +# + +proto-dictionary radius +#tmpl-rules allow_unresolved=yes allow_unknown=yes + +# All IP address literals should be parsed as prefixes +xlat_purify ("foo\ +match ERROR offset 2: Unterminated string + +xlat_purify ("foo +match ERROR offset 2: Unterminated string + +xlat_purify () +match ERROR offset 2: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +xlat_purify (!) +match ERROR offset 3: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +xlat_purify (|| b) +match ERROR offset 2: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +xlat_purify ((ok || handled) foo) +match ERROR offset 17: Invalid operator + +# escapes in names are illegal +xlat_purify (ok\ foo || handled) +match ERROR offset 4: Unexpected text after enum value. Expected operator + +# +# @todo - this is an error somehow? +# +xlat_purify (Service-Type == 000-111) +match ERROR offset 1: No IPv6 component separator: Failed resolving attribute in expansion: Service-Type +#match (&Service-Type == (0 - 111)) + +xlat_purify (ok FOO handled) +match ERROR offset 4: Invalid operator + +xlat_purify (ok !x handled) +match ERROR offset 4: Invalid operator + +xlat_purify (ok =x handled) +match ERROR offset 4: Invalid operator + +# +# Re-enable when we have proper bareword xlat tokenization +# +#xlat_purify (ok == handled"foo") +#match ERROR offset 14 Unexpected text after condition + +# And now we have a bunch of VALID conditions we want to parse. + +# sillyness is OK, but cleaned up. +# +# We should really allow parsing of bare words, too? +# +#xlat_purify ((((((ok)))))) +#match ok + +# +# Extra braces get squashed +# +#xlat_purify (&User-Name == &User-Password) +#match (&User-Name == &User-Password) + +#xlat_purify (!ok) +#match !ok + +#xlat_purify !(ok) +#match !ok + +xlat_purify !!true +match ERROR offset 1: Double operator is invalid + +#xlat_purify !(!ok) +#match ok + +# +# These next two are identical after normalization +# +xlat_purify (&User-Name == &User-Password || &Filter-Id == &Reply-Message) +match ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) + +xlat_purify ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) +match ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) + +xlat_purify (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) +match (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) + +# different from the previous ones. +xlat_purify (!((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))) +match !((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) + +xlat_purify (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) +match (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)) + +# +# @todo - add a flag which says to parse the FULL thing, or only parse part of it? +# +xlat_purify ((&User-Name == &Filter-Id) || (&Reply-Message == &User-Password))) +match Passed in 67 characters, but only parsed 66 characters: Unknown data type + +# +# @todo - the first argument is truthy, so we should replace the +# "&&" node with just the RHS. +# +xlat_purify ('handled' && (&Packet-Type == Access-Challenge)) +match ('handled' && (&Packet-Type == Access-Challenge)) + +# This is OK, without the braces +xlat_purify 'handled' && &Packet-Type == Access-Challenge +match ('handled' && (&Packet-Type == Access-Challenge)) + +# and this, though it's not a good idea. +xlat_purify 'handled' &&&Packet-Type == Access-Challenge +match ('handled' && (&Packet-Type == Access-Challenge)) + +# +# @todo - fix list comparisons +# +#xlat_purify &reply == &request +#match ERROR offset 0: Cannot use list references in condition + +#xlat_purify &reply == "hello" +#match ERROR offset 0: Cannot use list references in condition + +#xlat_purify "hello" == &reply +#match ERROR offset 11: Cannot use list references in condition + + +# +# Convert != to !(COND) for normal checks +# +# @todo - xlat doesn't do this, maybe that needs fixing? +# +xlat_purify &User-Name == &User-Password +match (&User-Name == &User-Password) + +xlat_purify &User-Name != &User-Password +match (&User-Name != &User-Password) +#match !&User-Name == &User-Password + +xlat_purify !&User-Name != &User-Password +match (!&User-Name != &User-Password) +#match &User-Name == &User-Password + +# +# We allow a cast for the existence check? +# +xlat_purify ::1 +match ::1 + +# new casts are allowed, too. +xlat_purify (ipv6addr)::1 +match ::1 + +xlat_purify (ipv6addr)"xxx" +match ERROR offset 12: Failed to parse IPv6 address string "xxx" + +# +# Various casts +# +xlat_purify &Filter-Id == &Framed-IP-Address +match ((ipaddr)&Filter-Id == &Framed-IP-Address) + +# +# We can automatically promote things as needed. But if the +# user forces incompatible types, then that's an error. +# +# @todo - perhaps better errors for casts? +# +xlat_purify &Filter-Id == &Framed-IP-Address +match ERROR offset 22: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +xlat_purify &Filter-Id == "foo" +match ERROR offset 1: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +# +# Don't normalize things +# +xlat_purify 127.0.0.1 < &Framed-IP-Address +match (127.0.0.1 < &Framed-IP-Address) + +# redundant casts get squashed +xlat_purify &Framed-IP-Address == 127.0.0.1 +match (&Framed-IP-Address == 127.0.0.1) + +xlat_purify &Framed-IP-Address <= 192.168.0.0/16 +match ((ipv4prefix)&Framed-IP-Address <= 192.168.0.0/16) + +# All IP address literals should be parsed as prefixes +xlat_purify &Framed-IP-Address <= 192.168.0.0/16 +match (&Framed-IP-Address <= 192.168.0.0/16) + +# string attributes must be string +xlat_purify &User-Name == "bob" +match (&User-Name == "bob") + +xlat_purify &User-Name == `bob` +match (&User-Name == `bob`) + +xlat_purify &User-Name == 'bob' +match (&User-Name == 'bob') + +xlat_purify &User-Name == bob +match (&User-Name == 'bob') + +# Integer (etc.) types must be "bare" +xlat_purify &Session-Timeout == 10 +match (&Session-Timeout == 10) + +# Automatic type inference means this is fine +xlat_purify &Session-Timeout == '10' +match (&Session-Timeout == 10) + +# Except for dates, which can be humanly readable! +# This one is be an expansion, so it's left as-is. +# +# @todo - yuck. Suppress full path? +# +xlat_purify &Event-Timestamp == "January 1, 2012 %{User-Name}" +match (&Event-Timestamp == "January 1, 2012 %{Request[0].User-Name}") + +# This one is NOT an expansion, so it's parsed into normal form +# +# @todo - can't parse years? +# +xlat_purify &Event-Timestamp == 'January 1, 2012' +match ERROR offset 1: Invalid year string +#match (&Event-Timestamp == 'Jan 1 2012 00:00:00 EST') + +# literals are parsed when the conditions are parsed +xlat_purify X == 1 +match ERROR offset 10: Failed parsing string as type 'uint32' + +# +# @todo - resolution is delayed, so we don't know where in the input +# string the RHS is. +# +xlat_purify &NAS-Port == X +match ERROR offset 1: Failed parsing string as type 'uint32' +#match ERROR offset 13: Failed parsing string as type 'uint32' + +# +# The RHS is a static string, so this gets mashed to a literal, +# and then statically evaluated. +# +xlat_purify 127.0.0.1 == "127.0.0.1" +match yes + +# @todo - why isn't the RHS being purified? +xlat_purify 127.0.0.1 == "%{md4: 127.0.0.1}" +match (127.0.0.1 == "%{md4: 127.0.0.1}") + +# +# Bare %{...} is allowed. +# + +# @todo - why isn't the RHS being purified? +xlat_purify 127.0.0.1 == %{md4:127.0.0.1} +match (127.0.0.1 == %{md4:127.0.0.1}) + +# @todo - yuck, don't print full path? +xlat_purify 127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{User-Name}'} +match (127.0.0.1 == %{md4: SELECT user FROM table WHERE user='%{Request[0].User-Name}'}) + +xlat_purify 00:11:22:33:44:55 == "00:11:22:33:44:55" +match yes + +# @todo - why isn't the RHS being purified? +xlat_purify 00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}" +match (00:11:22:33:44:55 == "%{md4:00:11:22:33:44:55}") + +xlat_purify 00:XX:22:33:44:55 == 00:11:22:33:44:55 +match ERROR offset 12: Missing separator, expected ':' + +# +# Tests for boolean data types. +# +xlat_purify true +match yes + +# @todo - for conditions, this should evaluate to "yes" +xlat_purify 1 +match 1 + +xlat_purify false +match no + +xlat_purify 0 +match 0 + +# +# @todo - purify logical operators. The instantiation function should update the "can_purify" flags. +# +xlat_purify true && (&User-Name == "bob") +match (yes && (&User-Name == "bob")) +#match &User-Name == "bob" + +xlat_purify false && (&User-Name == "bob") +match (no && (&User-Name == "bob")) + +xlat_purify false || (&User-Name == "bob") +match (no || (&User-Name == "bob")) + +xlat_purify true || (&User-Name == "bob") +match (yes || (&User-Name == "bob")) + +# +# Both sides static data with a cast: evaluate at parse time. +# +xlat_purify 20 < 100 +match yes + +# +# Both sides literal: evaluate at parse time +# +xlat_purify ('foo' == 'bar') +match no + +xlat_purify ('foo' < 'bar') +match no + +xlat_purify ('foo' > 'bar') +match yes + +xlat_purify ('foo' == 'foo') +match yes + +# +# @todo - why isn't the RHS being purified? +# +# @todo - why isn't the RHS being purified? +xlat_purify ("foo" == "%{md4: foo}") +match ("foo" == "%{md4: foo}") + +# +# @todo - why isn't the RHS being purified? +# +# @todo - why isn't the RHS being purified? +xlat_purify ("foo bar" == "%{md4: foo}") +match ("foo bar" == "%{md4: foo}") + +xlat_purify ("foo" == "bar") +match no + +xlat_purify ("foo" == 'bar') +match no + +# +# The RHS gets parsed as a VPT_TYPE_DATA, which is +# a double-quoted string. Except that there's no '%' +# in it, so it reverts back to a literal. +# +xlat_purify (&User-Name == "bob") +match (&User-Name == "bob") + +xlat_purify (&User-Name == "%{md4: blah}") +match (&User-Name == "0x544924d05ec4481925ba3749a096a0a7") + +xlat_purify 127.0.0.1 == 2130706433 +match yes + +# /32 suffix should be trimmed for this type +xlat_purify 127.0.0.1/32 == 127.0.0.1 +match yes + +xlat_purify 127.0.0.1/327 == 127.0.0.1 +match ERROR offset 9: Invalid IPv4 mask length "/327". Should be between 0-32 + +xlat_purify 127.0.0.1/32 == 127.0.0.1 +match yes + +xlat_purify (/foo/) +match ERROR offset 1: Unexpected regular expression + +# +# Tests for (FOO). +# +xlat_purify (1) +match 1 + +xlat_purify (0) +match 0 + +xlat_purify (true) +match yes + +xlat_purify (false) +match no + +xlat_purify ('') +match '' + +xlat_purify ("") +match "" + +# +# Integers are true, as are non-zero strings +# +xlat_purify (4) +match 4 + +xlat_purify ('a') +match 'a' + +# +# @todo - modules && return codes. +# + +#xlat_purify (a) +#match ERROR offset 1: Expected a module return code + +# +# Module return codes are OK +# +#xlat_purify (ok) +#match ok + +#xlat_purify (handled) +#match handled + +#xlat_purify (fail) +#match fail + +xlat_purify ("a") +match "a" + +xlat_purify (`a`) +match `a` + +xlat_purify (&User-Name) +match &User-Name + +# +# Forbidden data types in cast +# +# @todo - this should arguably be allowed? +xlat_purify ("foo" == &User-Name) +match ERROR offset 2: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value + +# +# If the LHS is a cast to a type, and the RHS is an attribute +# of the same type, then re-write it so that the attribute +# is on the LHS of the condition. +# +xlat_purify "foo" == &User-Name +match ("foo" == &User-Name) + +# This used to be expr, but expr isn't a builtin, so it failed... + +xlat_purify "%{md4: 1 + 1}" < &NAS-Port +match ("0x002ade8665c69219ca16bd108d92c8d5" < &NAS-Port) + +# +# The string gets parsed as an IP address. +# +# @todo - cast strings to the other data type? Or just rely on the comparisons to do the right thing? +xlat_purify &Filter-Id == &Framed-IP-Address +match (&Filter-Id == &Framed-IP-Address) +#match (ipv4addr)&Filter-Id == &Framed-IP-Address + +xlat_purify 127.0.0.1 == &Filter-Id +match (127.0.0.1 == &Filter-Id) + +xlat_purify &Tmp-uint64-0 == &request.Tmp-String-0 +match (&Tmp-uint64-0 == &request.Tmp-String-0) + +xlat_purify &Tmp-uint64-0 == &reply.Tmp-String-0 +match (&Tmp-uint64-0 == &reply.Tmp-String-0) + +# +# Casting attributes of different size +# +xlat_purify &Tmp-uint64-0 == &Framed-IP-Address +match ERROR offset 8: Cannot cast type 'uint64' to 'ipaddr' + +# +# LHS is a prefix, which _might_ be castable to an address +# if the prefix is /32. We don't know enough at compile time, +# so this may be a run-time failure. +# +xlat_purify &PMIP6-Home-IPv4-HoA == &Framed-IP-Address +match ((ipaddr)&PMIP6-Home-IPv4-HoA == &Framed-IP-Address) + +# but these are allowed +xlat_purify &Tmp-uint64-0 == "%{module: foo}" +match ((ether)&Tmp-uint64-0 == "%{module: foo}") + +xlat_purify &Filter-Id == &Framed-IP-Address +match ((ipaddr)&Filter-Id == &Framed-IP-Address) + +xlat_purify &Class == &Framed-IP-Address +match ((ipaddr)&Class == &Framed-IP-Address) + +# +# zero offset into arrays get parsed and ignored +# +xlat_purify &User-Name[0] == "bob" +match (&User-Name[0] == "bob") + +xlat_purify &User-Name[1] == "bob" +match (&User-Name[1] == "bob") + +xlat_purify &User-Name[n] == "bob" +match (&User-Name[n] == "bob") + +# +# This is allowed for pass2-fixups. Foo-Bar MAY be an attribute. +# If so allow it so that pass2 can fix it up. Until then, +# it's an unknown attribute +# +#xlat_purify &Foo-Bar +#match &Foo-Bar + +# Same types are optimized +# +# @todo- what does this mean? Check for later... +# +# FIXME: the tests don't currently run the "pass2" checks. +# This test should really be: +# +# xlat_purify &Acct-Input-Octets > &Session-Timeout +# +xlat_purify &Acct-Input-Octets > "%{Session-Timeout}" +match (&Acct-Input-Octets > "%{Request[0].Session-Timeout}") + +xlat_purify &Acct-Input-Octets > &Session-Timeout +match (&Acct-Input-Octets > &Session-Timeout) + +# Separate types aren't optimized +xlat_purify &Tmp-uint64-0 > &Session-Timeout +match (&Tmp-uint64-0 > &Session-Timeout) + +# +# Parse OIDs into known attributes, where possible. +# +# @todo - whoops, resolve it +xlat_purify &26.24757.84.9.5.4 == 0x1a99 +match (&26.24757.84.9.5.4 == 0x1a99) +#match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Port == 6809 + +# +# This OID is known, but the data is malformed. +# Allow it so that we can look for malformed attributes +# in packets. +# +xlat_purify &raw.26.24757.84.9.5.7 == 0x1a99 +match (&raw.26.24757.84.9.5.7 == 0x1a99) +#match &raw.Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.Assigned == 0x1a99 + +# This one is really unknown +xlat_purify &26.24757.84.9.5.15 == 0x1a99 +match ERROR offset 17: Unknown attributes not allowed here +#match &Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.15 == 0x1a99 + +# +# Invalid array references. +# +xlat_purify &User-Name[a] == 'bob' +match ERROR offset 11: Invalid array index + +# @todo - really 25? +xlat_purify &User-Name == &Filter-Id[a] +match ERROR offset 24: Invalid array index + +# +# Bounds checks... +# +xlat_purify &User-Name[1001] == 'bob' +match ERROR offset 11: Invalid array index '1001' (should be between 0-1000) + +xlat_purify &User-Name[-1] == 'bob' +match ERROR offset 11: Invalid array index '-1' (should be between 0-1000) + +# +# The attribute/xlat_purify parser does not fall back to bare words +# +xlat_purify request.Foo == 'request.Foo' +match ERROR offset 1: Invalid attribute reference, missing '&' prefix: Failed resolving attribute in expansion: request.Foo + +xlat_purify ¬-a-list.User-Name == ¬-a-list.User-Name +match ERROR offset 1: Attribute 'not' not found. Searched in: RADIUS, internal: Unresolved attributes are not allowed here + +# . is a valid dictionarxy name attribute, so we can't error out in pass1 +xlat_purify ¬-a-packet.User-Name == ¬-a-packet.User-Name +match ERROR offset 1: Attribute 'not' not found. Searched in: RADIUS, internal: Unresolved attributes are not allowed here + +# +# The LHS is a string with ASCII 5C 30 30 30 inside of it vs the RHS which should contain ASCII 0. +# +xlat_purify ('i have scary embedded things\000 inside me' == "i have scary embedded things\000 inside me") +match no + +# +# 'Unknown' attributes which are defined in the main dictionary +# should be resolved to their real names. +# @todo - resolve! +xlat_purify &1 == 0x616263 +match (&1 == 0x616263) +#match (&User-Name == 'abc') + +# @todo - resolve! +#xlat_purify &26.11344.1 == 0x7f000001 +#match &Vendor-Specific.FreeRADIUS.Proxied-To == 127.0.0.1 + +# +# Escape the backslashes correctly +# And print them correctly +# +xlat_purify &User-Name == '\\' +match (&User-Name == '\\') + +xlat_purify &User-Name == "@|\\" +match (&User-Name == "@|\\") + +xlat_purify &User-Name != "foo\nbar" +match (&User-Name != "foo\nbar") + +# +# We infer that the LHS is a prefix and the RHS is +# and ipaddr without requiring an explicit cast. +# +# @todo - fix up cast! +xlat_purify 192.168.0.0/16 > 192.168.1.2 +match (192.168.0.0/16 > 192.168.1.2) +#match true + +# @todo - fix up cast! +#xlat_purify 192.168.0.0/16 > 192.168.1.2 +#match true + +xlat_purify &NAS-IP-Address == 192.168.0.0/24 +match ((ipv4prefix)&NAS-IP-Address == 192.168.0.0/24) + +# +# Don't rewrite so that the attribute is on the LHS +# and, move the cast to the attribute, as the RHS +# is parsed as ipv4prefix +# +xlat_purify 192.168.0.0/24 > &NAS-IP-Address +match (192.168.0.0/24 > &NAS-IP-Address) + +# +# This is allowed and means "the list is not empty" +# +# @todo - we need a "parse as condition" flag! +xlat_purify (&reply) +match &reply + +# +# Expansions of environment variables +# and empty strings +# +xlat_purify ("$ENV{SOMETHING_OR_OTHER}" == '') +match no + +# +# Attributes with a protocol namespace +# +# @todo - if the explicit namespace is the same as the implicit one, we can omit +# the explicit one? But this is largely due to the printing fixes, where we just +# print the tmpl name as-is. +xlat_purify &radius.User-Name == 'bob' +match (&radius.User-Name == 'bob') +#match &User-Name == 'bob' + +xlat_purify !(!(0)) +match no + +xlat_purify (true) && (false) +match no + +# +# More short-circuit evaluations +# +xlat_purify (&User-Name == "bob") && (false) +match ((&User-Name == "bob") && no) + +xlat_purify (&User-Name == "bob") || (true) +match ((&User-Name == "bob") || yes) + +# +# A && (B || C) is not the same as (A && B) || C, for 0/1/1 +# +# 0 && (1 || 1) = 0 && 1 == 0 +# (0 && 1) || 1 = 0 || 1 == 1 +# +xlat_purify (&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message) +match ((&User-Name == "bob") && ((&User-Password == "bob") || &EAP-Message)) + +count +match 261