diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /src/tests/keywords | |
parent | Initial commit. (diff) | |
download | freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.tar.xz freeradius-50b37d4a27d3295a29afca2286f1a5a086142cec.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
150 files changed, 8442 insertions, 0 deletions
diff --git a/src/tests/keywords/3gpp b/src/tests/keywords/3gpp new file mode 100644 index 0000000..05e3fb2 --- /dev/null +++ b/src/tests/keywords/3gpp @@ -0,0 +1,19 @@ +# +# PRE: update +# +update request { + 3GPP-IMSI := "hello" +} + +# +# "request:[0-9]" should be parsed as a list followed +# by an attribute. +# +update control { + Tmp-String-0 := "%{3GPP-IMSI}" + Tmp-String-1 := "%{request:3GPP-IMSI}" +} + +update reply { + Filter-Id := "filter" +}
\ No newline at end of file diff --git a/src/tests/keywords/README.md b/src/tests/keywords/README.md new file mode 100644 index 0000000..68ce136 --- /dev/null +++ b/src/tests/keywords/README.md @@ -0,0 +1,43 @@ +# The Keyword test Framework + +See `update` and `default-input.attrs` for examples. + +In short, the test framework assumes Access-Request with PAP +authentication. The password is hard-coded into the configuration, +and can't be changed. + +The entire test suite consists of two files: + +* foo + + Contains a short piece of "unlang". The shorter the better. The + goal is to do something useful in unlang, and modify the input + packet and/or the reply. + + If the test depends on another one, it should name the other test + at the top of the file. For example, the `if-else` test depends + on the `if` test. This dependency is given by the following lines + at the top of the `if-else` file: + + `# PRE: if` + +* foo.attrs + + Contains the input packet and the filter for the reply. There + always has to be attributes in the input, and filter attributes in the + reply. + + If `foo` doesn't exist, then the `default-input.attrs` file is used. + This allows many tests to be simplified, as all they need is a + little bit of "unlang". + + +## How it works + +The input packet is passed into the unit test framework, through the +unlang snippet in `foo`, and filtered through the reply filter in +`foo.attrs`. If everything matches, then the test case passes. + +To add a test, just put `foo` and (optionally) `foo.attrs` into this +directory. The build framework will pick them up and automatically +run them. diff --git a/src/tests/keywords/all.mk b/src/tests/keywords/all.mk new file mode 100644 index 0000000..739b738 --- /dev/null +++ b/src/tests/keywords/all.mk @@ -0,0 +1,123 @@ +# +# Unit tests for unlang keywords +# + +# +# The test files are files without extensions. +# The list is unordered. The order is added in the next step by looking +# at precursors. +# +KEYWORD_FILES := $(filter-out %.conf %.md %.attrs %.mk %~ %.rej,$(subst $(DIR)/,,$(wildcard $(DIR)/*))) + +ifeq "$(OPENSSL_LIBS)" "" +KEYWORD_FILES := $(filter-out pap-ssha2,$(KEYWORD_FILES)) +endif + +# +# Create the output directory +# +.PHONY: $(BUILD_DIR)/tests/keywords +$(BUILD_DIR)/tests/keywords: + @mkdir -p $@ + +# +# Find which input files are needed by the tests +# strip out the ones which exist +# move the filenames to the build directory. +# +BOOTSTRAP_EXISTS := $(addprefix $(DIR)/,$(addsuffix .attrs,$(KEYWORD_FILES))) +BOOTSTRAP_NEEDS := $(filter-out $(wildcard $(BOOTSTRAP_EXISTS)),$(BOOTSTRAP_EXISTS)) +BOOTSTRAP := $(subst $(DIR),$(BUILD_DIR)/tests/keywords,$(BOOTSTRAP_NEEDS)) + +# +# For each file, look for precursor test. +# Ensure that each test depends on its precursors. +# +-include $(BUILD_DIR)/tests/keywords/depends.mk + +export OPENSSL_LIBS + +$(BUILD_DIR)/tests/keywords/depends.mk: $(addprefix $(DIR)/,$(KEYWORD_FILES)) | $(BUILD_DIR)/tests/keywords + @rm -f $@ + @for x in $^; do \ + y=`grep 'PRE: ' $$x | sed 's/.*://;s/ / /g;s, , $(BUILD_DIR)/tests/keywords/,g'`; \ + if [ "$$y" != "" ]; then \ + z=`echo $$x | sed 's,src/,$(BUILD_DIR)/',`; \ + echo "$$z: $$y" >> $@; \ + echo "" >> $@; \ + fi \ + done + +# +# These ones get copied over from the default input +# +$(BOOTSTRAP): $(DIR)/default-input.attrs | $(BUILD_DIR)/tests/keywords + @cp $< $@ + +# +# These ones get copied over from their original files +# +$(BUILD_DIR)/tests/keywords/%.attrs: $(DIR)/%.attrs | $(BUILD_DIR)/tests/keywords + @cp $< $@ + +# +# Don't auto-remove the files copied by the rule just above. +# It's unnecessary, and it clutters the output with crap. +# +.PRECIOUS: $(BUILD_DIR)/tests/keywords/%.attrs + +KEYWORD_MODULES := $(shell grep -- mods-enabled src/tests/keywords/radiusd.conf | sed 's,.*/,,') +KEYWORD_RADDB := $(addprefix raddb/mods-enabled/,$(KEYWORD_MODULES)) +KEYWORD_LIBS := $(addsuffix .la,$(addprefix rlm_,$(KEYWORD_MODULES))) rlm_example.la rlm_cache.la + +# +# Files in the output dir depend on the unit tests +# +# src/tests/keywords/FOO unlang for the test +# src/tests/keywords/FOO.attrs input RADIUS and output filter +# build/tests/keywords/FOO updated if the test succeeds +# build/tests/keywords/FOO.log debug output for the test +# +# Auto-depend on modules via $(shell grep INCLUDE $(DIR)/radiusd.conf | grep mods-enabled | sed 's/.*}/raddb/')) +# +# If the test fails, then look for ERROR in the input. No error +# means it's unexpected, so we die. +# +# Otherwise, check the log file for a parse error which matches the +# ERROR line in the input. +# +$(BUILD_DIR)/tests/keywords/%: ${DIR}/% $(BUILD_DIR)/tests/keywords/%.attrs $(TESTBINDIR)/unittest | $(BUILD_DIR)/tests/keywords $(KEYWORD_RADDB) $(KEYWORD_LIBS) build.raddb rlm_cache_rbtree.la rlm_test.la rlm_unix.la + @echo UNIT-TEST $(notdir $@) + @if ! KEYWORD=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/keywords/ -i $@.attrs -f $@.attrs -xx > $@.log 2>&1; then \ + if ! grep ERROR $< 2>&1 > /dev/null; then \ + cat $@.log; \ + echo "# $@.log"; \ + echo KEYWORD=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/keywords/ -i $@.attrs -f $@.attrs -xx; \ + exit 1; \ + fi; \ + FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \ + EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \ + if [ "$$EXPECTED" != "$$FOUND" ]; then \ + cat $@.log; \ + echo "# $@.log"; \ + echo KEYWORD=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/keywords/ -i $@.attrs -f $@.attrs -xx; \ + exit 1; \ + fi \ + fi + @touch $@ + +# +# Get all of the unit test output files +# +TESTS.KEYWORDS_FILES := $(addprefix $(BUILD_DIR)/tests/keywords/,$(KEYWORD_FILES)) + +# +# Depend on the output files, and create the directory first. +# +tests.keywords: $(TESTS.KEYWORDS_FILES) + +$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES) $(TESTS.MAP_FILES) + +.PHONY: clean.tests.keywords +clean.tests.keywords: + @rm -rf $(BUILD_DIR)/tests/keywords/ diff --git a/src/tests/keywords/array b/src/tests/keywords/array new file mode 100644 index 0000000..a901a2b --- /dev/null +++ b/src/tests/keywords/array @@ -0,0 +1,53 @@ +# +# PRE: if +# +# Tests for dereferencing the Nth attribute +# +update reply { + Filter-Id := "filter" +} + +update request { + Class := 0x01020304 + Class += 0x05060708 + Class += 0x090a0b0c +} + +if (&Class != 0x01020304) { + update reply { + Filter-Id := "fail 0" + } +} + +# Must be the same as above +if (&Class[0] != 0x01020304) { + update reply { + Filter-Id += "fail 0a" + } +} + +if (&Class[1] != 0x05060708) { + update reply { + Filter-Id += "fail 1" + } +} + +if (&Class[2] != 0x090a0b0c) { + update reply { + Filter-Id += "fail 2" + } +} + +# must not exist +if (&Class[3]) { + update reply { + Filter-Id += "fail 3" + } +} + +# Last element of the array +if (&Class[n] != 0x090a0b0c) { + update reply { + Filter-Id += "fail 4" + } +} diff --git a/src/tests/keywords/base64 b/src/tests/keywords/base64 new file mode 100644 index 0000000..9661252 --- /dev/null +++ b/src/tests/keywords/base64 @@ -0,0 +1,141 @@ +# +# PRE: hex +# +update reply { + Filter-Id := "filter" +} + +update request { + Tmp-String-0 := '9870' + Tmp-Octets-0 := 0x39383731 + Tmp-IP-Address-0 := 57.56.55.50 + Tmp-Date-0 := 959985459 + Tmp-Integer-0 := 959985460 + Tmp-Cast-Abinary := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812' + Tmp-Cast-IfId := '0000:0000:3938:3737' + Tmp-Cast-IPv6Addr := '::3938:3738' + Tmp-Cast-IPv6Prefix := '::3938:3739/128' + Tmp-Cast-Byte := 58 + Tmp-Cast-Short := 14139 + Tmp-Cast-Ethernet := 00:00:39:38:37:3c + Tmp-Cast-Integer64 := 1152921505566832445 + Tmp-Cast-IPv4Prefix := 57.56.55.62/32 +} + +update request { + Tmp-String-0 := "%{base64:&Tmp-String-0}" + Tmp-String-1 := "%{base64:&Tmp-Octets-0}" + Tmp-String-2 := "%{base64:&Tmp-IP-Address-0}" + Tmp-String-3 := "%{base64:&Tmp-Date-0}" + Tmp-String-4 := "%{base64:&Tmp-Integer-0}" + Tmp-String-5 := "%{base64:&Tmp-Cast-Abinary}" + Tmp-String-6 := "%{base64:&Tmp-Cast-Ifid}" + Tmp-String-7 := "%{base64:&Tmp-Cast-IPv6Addr}" + Tmp-String-8 := "%{base64:&Tmp-Cast-IPv6Prefix}" + Tmp-String-9 := "%{base64:&Tmp-Cast-Byte}" +} + +# String - bin 0x39383730 +if (Tmp-String-0 != 'OTg3MA==') { + update reply { + Filter-Id += 'fail 0' + } +} + +# Octets - bin 0x39383731 +if (Tmp-String-1 != 'OTg3MQ==') { + update reply { + Filter-Id += 'fail 1' + } +} + +# IP Address - bin 0x39383732 +if (Tmp-String-2 != 'OTg3Mg==') { + update reply { + Filter-Id += 'fail 2' + } +} + +# Date - bin 0x39383733 +if (Tmp-String-3 != 'OTg3Mw==') { + update reply { + Filter-Id += 'fail 3' + } +} + +# Integer - bin 0x39383734 +if (Tmp-String-4 != 'OTg3NA==') { + update reply { + Filter-Id += 'fail 4' + } +} + +# Abinary - bin 0x0101000039383735000000002000110000000714000200000000000000000000 +if (Tmp-String-5 != 'AQEAADk4NzUAAAAAIAARAAAABxQAAgAAAAAAAAAAAAA=') { + update reply { + Filter-Id += 'fail 5' + } +} + +# ifid - bin 0x0000000039383737 +if (Tmp-String-6 != 'AAAAADk4Nzc=') { + update reply { + Filter-Id += 'fail 6' + } +} + +# ipv6addr - bin 0x00000000000000000000000039383738 +if (Tmp-String-7 != 'AAAAAAAAAAAAAAAAOTg3OA==') { + update reply { + Filter-ID += 'fail 7' + } +} + +# ipv6addrprefix - bin 0x008000000000000000000000000039383739 +if (Tmp-String-8 != 'AIAAAAAAAAAAAAAAAAA5ODc5') { + update reply { + Filter-ID += 'fail 8' + } +} + +# byte - bin 0x3a +if (Tmp-String-9 != 'Og==') { + update reply { + Filter-ID += 'fail 9' + } +} + +update request { + Tmp-String-0 := "%{base64:&Tmp-Cast-Short}" + Tmp-String-1 := "%{base64:&Tmp-Cast-Ethernet}" + Tmp-String-2 := "%{base64:&Tmp-Cast-Integer64}" + Tmp-String-3 := "%{base64:&Tmp-Cast-IPv4Prefix}" +} + +# short - bin 0x373b +if (Tmp-String-0 != 'Nzs=') { + update reply { + Filter-ID += 'fail 9' + } +} + +# ethernet - bin 0x00003938373c +if (Tmp-String-1 != 'AAA5ODc8') { + update reply { + Filter-Id += 'fail 10' + } +} + +# integer64 - bin 0x100000003938373d +if (Tmp-String-2 != 'EAAAADk4Nz0=') { + update reply { + Filter-Id += 'fail 11' + } +} + +# ipv4prefix - bin 0x00203938373e +if (Tmp-String-3 != 'ACA5ODc+') { + update reply { + Filter-Id += 'fail 12' + } +} diff --git a/src/tests/keywords/break-error b/src/tests/keywords/break-error new file mode 100644 index 0000000..fff20f0 --- /dev/null +++ b/src/tests/keywords/break-error @@ -0,0 +1,11 @@ +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +if (User-Name == "bob") { + break # ERROR +} diff --git a/src/tests/keywords/cache b/src/tests/keywords/cache new file mode 100644 index 0000000..58ce207 --- /dev/null +++ b/src/tests/keywords/cache @@ -0,0 +1,229 @@ +# +# PRE: update if +# +update { + &control:Cleartext-Password := 'hello' + &request:Tmp-String-0 := 'testkey' + &reply:Filter-Id := 'filter' +} + + +# +# Basic store and retrieve +# +update control { + &control:Tmp-String-1 := 'cache me' +} + +cache +if (!updated) { + update reply { + Filter-Id := 'fail 0' + } + reject +} + +# Check status-only works correctly (should return ok and consume attribute) +update control { + Cache-Status-Only := 'yes' +} +cache +if (!ok) { + update reply { + Filter-Id := 'fail 2a' + } + reject +} + +if (&control:Cache-Status-Only) { + update reply { + Filter-Id := 'fail 2b' + } + reject +} + +# Retrieve the entry (should be copied to request list) +cache +if (!ok) { + update reply { + Filter-Id := 'fail 3a' + } + reject +} + +if (&request:Tmp-String-1 != &control:Tmp-String-1) { + update reply { + Filter-Id := 'fail 3b' + } +} + +# Retrieving the entry should not expire it +update request { + Tmp-String-1 !* ANY +} + +cache +if (!ok) { + update reply { + Filter-Id := 'fail 4a' + } + reject +} + +if (&request:Tmp-String-1 != &control:Tmp-String-1) { + update reply { + Filter-Id := 'fail 4b' + } + reject +} + +# Force expiry of the entry +update control { + Cache-TTL := 0 +} +cache +if (!ok) { + update reply { + Filter-Id := 'fail 5' + } + reject +} + +# Check status-only works correctly (should return notfound and consume attribute) +update control { + Cache-Status-Only := 'yes' +} +cache +if (!notfound) { + update reply { + Filter-Id := 'fail 6a' + } + reject +} + +if (&control:Cache-Status-Only) { + update reply { + Filter-Id := 'fail 6b' + } + reject +} + +# Check read-only works correctly (should return notfound and consume attribute) +update control { + Cache-Read-Only := 'yes' +} +cache +if (!notfound) { + update reply { + Filter-Id := 'fail 7a' + } + reject +} + +if (&control:Cache-Read-Only) { + update reply { + Filter-Id := 'fail 7b' + } + reject +} + +# ...and check the entry wasn't recreated +update control { + Cache-Status-Only := 'yes' +} +cache +if (!notfound) { + update reply { + Filter-Id := 'fail 7c' + } + reject +} + +# This should still allow the creation of a new entry +update control { + Cache-TTL := -1 +} +cache +if (!updated) { + update reply { + Filter-Id := 'fail 8a' + } + reject +} + +cache +if (!ok) { + update reply { + Filter-Id := 'fail 8b' + } + reject +} + +if (&Cache-TTL) { + update reply { + Filter-Id := 'fail 8c' + } + reject +} + +if (&request:Tmp-String-1 != &control:Tmp-String-1) { + update reply { + Filter-Id := 'fail 8d' + } + reject +} + +update control { + Tmp-String-1 := 'cache me2' +} + +# Updating the Cache-TTL shouldn't make things go boom (we can't really check if it works) +update control { + Cache-TTL := 30 +} +cache +if (!ok) { + update reply { + Filter-Id := 'fail 9a' + } + reject +} + +# Request Tmp-String-1 shouldn't have been updated yet +if (&request:Tmp-String-1 == &control:Tmp-String-1) { + update reply { + Filter-Id := 'fail 9b' + } + reject +} + +# Check that a new entry is created +update control { + Cache-TTL := -1 +} +cache +if (!updated) { + update reply { + Filter-Id := 'fail 10a' + } + reject +} + +# Check Cache-Entry-Hits is updated as we expect +if (&request:Cache-Entry-Hits != 0) { + update reply { + Filter-Id := 'fail 12a' + } + reject +} + +cache + +if (&request:Cache-Entry-Hits != 1) { + update reply { + Filter-Id := 'fail 12b' + } + reject +} + + diff --git a/src/tests/keywords/case-attr-error b/src/tests/keywords/case-attr-error new file mode 100644 index 0000000..e9516cc --- /dev/null +++ b/src/tests/keywords/case-attr-error @@ -0,0 +1,20 @@ +# PRE: case-empty +# +update reply { + Filter-Id := "fail" +} + +switch &reply:Filter-Id { + # deliberately empty + case "filter" { + } + + case &Not-Dynamically-Allocated { # ERROR + update reply { + Filter-Id := "fail" + } + } + + case { + } +} diff --git a/src/tests/keywords/case-empty b/src/tests/keywords/case-empty new file mode 100644 index 0000000..46ea054 --- /dev/null +++ b/src/tests/keywords/case-empty @@ -0,0 +1,23 @@ +# PRE: switch +# +update reply { + Filter-Id := "filter" +} + +switch &reply:Filter-Id { + # deliberately empty + case "filter" { + } + + case "fail" { + update reply { + Filter-Id := "fail" + } + } + + case { + update reply { + Filter-Id := "default" + } + } +} diff --git a/src/tests/keywords/case-empty-string b/src/tests/keywords/case-empty-string new file mode 100644 index 0000000..4b8e7cd --- /dev/null +++ b/src/tests/keywords/case-empty-string @@ -0,0 +1,25 @@ +# PRE: switch +# +update request { + Filter-Id := "" +} + +switch &Filter-Id { + case "" { + update reply { + Filter-Id := "filter" + } + } + + case "doug" { + update reply { + Filter-Id := "doug" + } + } + + case { + update reply { + Filter-Id := "default" + } + } +} diff --git a/src/tests/keywords/case-list b/src/tests/keywords/case-list new file mode 100644 index 0000000..e4a0290 --- /dev/null +++ b/src/tests/keywords/case-list @@ -0,0 +1,19 @@ +switch &User-Name { + case "bob" { + update reply { + Filter-Id := "filter" + } + } + + case &reply: { # ERROR + update reply { + Filter-Id := "doug" + } + } + + case { + update reply { + Filter-Id := "default" + } + } +} diff --git a/src/tests/keywords/cast-byte b/src/tests/keywords/cast-byte new file mode 100644 index 0000000..4663d95 --- /dev/null +++ b/src/tests/keywords/cast-byte @@ -0,0 +1,25 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:Class := 0xad +} + +if (<byte>Class == 173) { + update reply { + Filter-Id := "filter" + } +} + +if (<byte>Class < 173) { + update reply { + Filter-Id += "wrong" + } +} + +if (<byte>Class > 173) { + update reply { + Filter-Id += "wrong" + } +} diff --git a/src/tests/keywords/cast-integer b/src/tests/keywords/cast-integer new file mode 100644 index 0000000..4972ee7 --- /dev/null +++ b/src/tests/keywords/cast-integer @@ -0,0 +1,25 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:Class := 0x00000101 +} + +if (<integer>Class == 257) { + update reply { + Filter-Id := "filter" + } +} + +if (<integer>Class < 256) { + update reply { + Filter-Id += "wrong" + } +} + +if (<integer>Class > 257) { + update reply { + Filter-Id += "wrong" + } +} diff --git a/src/tests/keywords/cast-ipaddr b/src/tests/keywords/cast-ipaddr new file mode 100644 index 0000000..f0dd356 --- /dev/null +++ b/src/tests/keywords/cast-ipaddr @@ -0,0 +1,442 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:NAS-IP-Address := 127.0.0.1 + request:Tmp-Integer-0 := 2130706433 + reply:Filter-Id := "filter" +} + +update request { + Tmp-String-0 := "%{request:NAS-IP-Address}" +} + +if (<ipaddr>Tmp-Integer-0 != NAS-IP-Address) { + update reply { + Filter-Id += 'Fail 0' + } +} + +# +# Update statements do implicit casts, so we can check +# cast results are correct, by using the update to perform +# the cast, and looking at the results. +# +update request { + Tmp-Cast-Ipaddr := 203.0.113.1 + Tmp-Cast-IPv4Prefix := 203.0.113.0/24 + Tmp-Cast-IPv4Prefix += 203.0.113.1/32 + Tmp-Cast-IPv6Addr := 2001:DB8::1 + Tmp-Cast-IPv6Addr += ::ffff:203.0.113.1 + Tmp-Cast-IPv6Prefix := 2001:DB8::/32 + Tmp-Cast-IPv6Prefix += ::ffff:203.0.113.1/128 + Tmp-Cast-IPv6Prefix += ::ffff:203.0.113.1/64 +} + +# +# IPv4 address to IPv6 address +# +update control { + Tmp-Cast-IPv6addr := &Tmp-Cast-IPaddr +} + +if (&control:Tmp-Cast-IPv6addr != ::ffff:203.0.113.1) { + update reply { + Filter-Id += 'Fail 1' + } +} + +# +# IPv6 address to IPv4 address +# +update control { + Tmp-Cast-IPaddr := &control:Tmp-Cast-IPv6addr +} + +if (&control:Tmp-Cast-IPaddr != 203.0.113.1) { + update reply { + Filter-Id += 'Fail 2' + } +} + +# +# IPv4 prefix to IPv6 prefix +# +update control { + Tmp-Cast-IPv6Prefix := &Tmp-Cast-IPv4Prefix +} + +if (&control:Tmp-Cast-IPv6Prefix != ::ffff:203.0.113.0/120) { + update reply { + Filter-Id += 'Fail 31' + } +} + +# +# IPv6 prefix to IPv4 prefix +# +update control { + Tmp-Cast-IPv4Prefix := &control:Tmp-Cast-IPv6Prefix +} + +if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/24) { + update reply { + Filter-Id += 'Fail 4' + } +} + +# +# IPv4 prefix (32) to IPv6 address +# +update control { + Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv4Prefix[1] +} + +if (&control:Tmp-Cast-IPv6Addr != ::ffff:203.0.113.1) { + update reply { + Filter-Id += 'Fail 5' + } +} + +# +# IPv6 prefix (128) to IPv4 address +# +update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix[1] +} + +if (&control:Tmp-Cast-Ipaddr != 203.0.113.1/32) { + update reply { + Filter-Id += 'Fail 6' + } +} + +# +# IPv4 address to IPv6 prefix (128) +# +update control { + Tmp-Cast-IPv6Prefix := &Tmp-Cast-Ipaddr +} + +if (&control:Tmp-Cast-IPv6Prefix != ::ffff:203.0.113.1/128) { + update reply { + Filter-Id += 'Fail 7' + } +} + +# +# IPv6 address to IPv4 prefix (32) +# +update control { + Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Addr[1] +} + +if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/32) { + update reply { + Filter-Id += 'Fail 8' + } +} + +# +# IPv4 address to IPv4 prefix (32) +# +update control { + Tmp-Cast-IPv4Prefix := &Tmp-Cast-Ipaddr +} + +if (&control:Tmp-Cast-IPv4Prefix != 203.0.113.1/32) { + update reply { + Filter-Id += 'Fail 9' + } +} + +# +# IPv6 address to IPv6 prefix (128) +# +update control { + Tmp-Cast-IPv6Prefix := Tmp-Cast-Ipv6addr +} + +if (&control:Tmp-Cast-IPv6Prefix != 2001:DB8::1/128) { + update reply { + Filter-Id += 'Fail 11' + } +} + +# +# IPv4 prefix (32) to IPv4 address +# +update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv4Prefix[1] +} + +if (&control:Tmp-Cast-Ipaddr != 203.0.113.1) { + update reply { + Filter-Id += 'Fail 12' + } +} + +# +# IPv6 prefix (128) to IPv6 address +# +update control { + Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv6Prefix[1] +} + +if (&control:Tmp-Cast-IPv6Addr != ::ffff:203.0.113.1) { + update reply { + Filter-Id += 'Fail 13' + } +} + +# +# And the invalid cases... +# + +# +# IPv6 Prefix < 128 to IPv6 address +# +redundant { + group { + update control { + Tmp-Cast-IPv6Addr := Tmp-Cast-IPv6Prefix + } + update reply { + Filter-Id += 'Fail 14' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv6addr. Only /128 prefixes may be cast to IP address types') { + update reply { + Filter-Id += 'Fail 14.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Prefix < 128 to IPv4 address +# +redundant { + group { + update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix[2] + } + update reply { + Filter-Id += 'Fail 15' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipaddr. Only /128 prefixes may be cast to IP address types') { + update reply { + Filter-Id += 'Fail 15.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Prefix < 96 to IPv4 prefix (causes part of the IPv4/v6 mapping prefix to be masked off) +# +redundant { + group { + update control { + Tmp-Cast-Ipv4Prefix := &Tmp-Cast-IPv6Prefix[2] + } + update reply { + Filter-Id += 'Fail 16' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv4prefix. No IPv4-IPv6 mapping prefix') { + update reply { + Filter-Id += 'Fail 16.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv4 Prefix < 32 to IPv6 address +# +redundant { + group { + update control { + Tmp-Cast-IPv6Addr := &Tmp-Cast-IPv4Prefix + } + update reply { + Filter-Id += 'Fail 17' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv4prefix to ipv6addr. Only /32 prefixes may be cast to IP address types') { + update reply { + Filter-Id += 'Fail 17.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv4 Prefix < 32 to IPv4 address +# +redundant { + group { + update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv4Prefix + } + update reply { + Filter-Id += 'Fail 17' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv4prefix to ipaddr. Only /32 prefixes may be cast to IP address types') { + update reply { + Filter-Id += 'Fail 17.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Prefix outside mapping range to IPv4 address +# +redundant { + group { + update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Prefix + } + update reply { + Filter-Id += 'Fail 18' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipaddr. Only /128 prefixes may be cast to IP address types') { + update reply { + Filter-Id += 'Fail 18.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Prefix outside mapping range to IPv4 prefix +# +redundant { + group { + update control { + Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Prefix + } + update reply { + Filter-Id += 'Fail 19' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6prefix to ipv4prefix. No IPv4-IPv6 mapping prefix') { + update reply { + Filter-Id += 'Fail 19.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Address outside mapping range to IPv4 address +# +redundant { + group { + update control { + Tmp-Cast-Ipaddr := &Tmp-Cast-IPv6Addr + } + update reply { + Filter-Id += 'Fail 20' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6addr to ipaddr. No IPv4-IPv6 mapping prefix') { + update reply { + Filter-Id += 'Fail 20' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv6 Address outside mapping range to IPv4 prefix +# +redundant { + group { + update control { + Tmp-Cast-IPv4Prefix := &Tmp-Cast-IPv6Addr + } + update reply { + Filter-Id += 'Fail 21' + } + } + group { + if ("%{Module-Failure-Message}" != 'Attribute conversion failed: Invalid cast from ipv6addr to ipv4prefix. No IPv4-IPv6 mapping prefix') { + update reply { + Filter-Id += 'Fail 21.5' + } + } + update request { + Module-Failure-Message !* ANY + } + ok + } +} + +# +# IPv4 address to integer +# +update request { + Tmp-Integer-0 := &NAS-IP-Address +} + +if (Tmp-Integer-0 != 0x7f000001) { + update reply { + Filter-Id += 'Fail 22' + } +} + +# +# Check the decimal value just for the heck of it +# +if (Tmp-Integer-0 != 2130706433) { + update reply { + Filter-Id += 'Fail 23' + } +} diff --git a/src/tests/keywords/cast-short b/src/tests/keywords/cast-short new file mode 100644 index 0000000..a17b379 --- /dev/null +++ b/src/tests/keywords/cast-short @@ -0,0 +1,25 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:Class := 0x0101 +} + +if (<short>Class == 257) { + update reply { + Filter-Id := "filter" + } +} + +if (<short>Class < 256) { + update reply { + Filter-Id += "wrong" + } +} + +if (<short>Class > 257) { + update reply { + Filter-Id += "wrong" + } +} diff --git a/src/tests/keywords/cmp b/src/tests/keywords/cmp new file mode 100644 index 0000000..4cd59b8 --- /dev/null +++ b/src/tests/keywords/cmp @@ -0,0 +1,20 @@ +# +# PRE: update +# +update control { + Cleartext-Password := 'hello' +} + +update request { + Called-Station-Id := "This is a test" + Calling-Station-Id := "This is a test" +} + +# +# Check attribute references +# +if (Called-Station-Id == &Calling-Station-Id) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/cmp-ipaddr b/src/tests/keywords/cmp-ipaddr new file mode 100644 index 0000000..bf197af --- /dev/null +++ b/src/tests/keywords/cmp-ipaddr @@ -0,0 +1,20 @@ +# +# PRE: update +# +update control { + Cleartext-Password := 'hello' +} + +update request { + NAS-IP-Address := 127.0.0.1 + Framed-IP-Address := 127.0.0.1 +} + +# +# Check attribute references +# +if (NAS-IP-Address == &Framed-IP-Address) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/comments b/src/tests/keywords/comments new file mode 100644 index 0000000..483bd05 --- /dev/null +++ b/src/tests/keywords/comments @@ -0,0 +1,48 @@ +# +# PRE: update if +# + +# One comment +#{ Two comment +#} Three comment +#'Four' +#"Five comment" +##Six Comment# + #Seven comment (yes i'm meant to be tabbed in) + #Eight comment (yes i'm meant to have spaces before me) + #Nine comment (tabs and spaces, are you crazy?!) + +update { #}'{ Opening block with extra special chars {} '" + control:Cleartext-Password := 'hello' # This should update the password so the test passes + reply:Filter-Id := 'filter'# Eek! Too close +} #{'} Closing block with extra special chars {} '" + +update { request:Tmp-String-0 := 'candy' } # Comment after unicorn block + +update request { + request:Reply-Message += 'I am #literally a comment #' + request:Reply-Message += "I am #literally a comment #" +} + +if (&request:Tmp-String-0 != 'candy') { + update reply { + reply:Filter-Id += 'fail 0' + } +} + +if (&request:Reply-Message[0] != 'I am #literally a comment #') { + update reply { + reply:Filter-Id += 'fail 1' + } +} + +if (&request:Reply-Message[1] != "I am #literally a comment #") { + update reply { + reply:Filter-Id += 'fail 2' + } +} + +ok # I'm a comment after a module call +ok # I'm a comment # after a module {} call + +ok, ok, ok, ok, ok diff --git a/src/tests/keywords/count-error b/src/tests/keywords/count-error new file mode 100644 index 0000000..f0723cb --- /dev/null +++ b/src/tests/keywords/count-error @@ -0,0 +1,11 @@ +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +update request { + Tmp-String-0 := &reply:Filter-Id[#] # ERROR +} diff --git a/src/tests/keywords/crypt b/src/tests/keywords/crypt new file mode 100644 index 0000000..e6d63aa --- /dev/null +++ b/src/tests/keywords/crypt @@ -0,0 +1,151 @@ +# +# PRE: update if +# + +# Skip all these tests if crypt_r was not available +# +if ("%{crypt:&User-Password}") { + noop +} +if ("%{request:Module-Failure-Message[0]}" !~ /^Crypt not available at compile time/) { + + +# Set required attributes +# +update reply { + &Filter-Id := "filter" +} + +update request { + &Tmp-String-0 := 'foo' + &Tmp-String-1 := 'foo:bar' + &Tmp-String-2 := 'f:' + &Tmp-String-3 := &User-Password + &Tmp-String-4 := &control:Cleartext-Password + &Tmp-String-5 := 'fwtLWDtMiSbH8lmXCMIVfrSMJjF' + &Tmp-String-8 := 'aa' + &Tmp-String-9 := '$1$abcdefgh' +} + + +# Check for error on no salt +# +if ("%{crypt:&User-Password}") { + update reply { + &Filter-Id += 'fail 1a' + } +} + +if ("%{request:Module-Failure-Message[0]}" != 'No salt specified in crypt xlat') { + update reply { + &Filter-Id += 'fail 1b' + } +} + + +# Check DES - all crypt_r() implementations should do this. +# +if ("%{crypt:aa:foo}" != "aaKNIEDOaueR6") { + update reply { + &Filter-Id += 'fail 2a' + } +} + +if ("%{crypt:&Tmp-String-8:foo}" != "aaKNIEDOaueR6") { + update reply { + &Filter-Id += 'fail 2b' + } +} + +if ("%{crypt:aa:&User-Password}" != "aaPwJ9XL9Y99E") { + update reply { + &Filter-Id += 'fail 2c' + } +} + + +# Test we can encrypt and then authenticate +# +update { + &request:User-Password := &request:Tmp-String-5 + &control:Crypt-Password := "%{crypt:AZ:&Tmp-String-5}" + &control:Cleartext-Password !* "" +} + +group { + pap.authenticate { + fail = 1 + reject = 1 + } + + if (!ok) { + update reply { + &Filter-Id += 'fail 3' + } + } +} + +update { + &request:User-Password := &Tmp-String-3 + &control:Cleartext-Password := &Tmp-String-4 +} + + +# Clear Module-Failure-Message so below tests work no matter what +# happened above +# +update request { + &Module-Failure-Message !* "" +} + + +# Check colons in password +# +if ("%{crypt:aa:foo:bar}" != "aadzEnaZwH90k") { + update reply { + &Filter-Id += 'fail 4a' + } +} + +if ("%{crypt:aa:&Tmp-String-1}" != "aadzEnaZwH90k") { + update reply { + &Filter-Id += 'fail 4b' + } +} + + +# Check invalid chars in salt +# +# In this case, depending on the library implementation, crypt +# seems to either return an empty string (null) and set an error, +# or it will return an invalid hash beginning with '*'. +# +update request { + &Tmp-String-7 := "%{crypt:&Tmp-String-2:foo}" +} + +if (&Tmp-String-7 !~ /^\*/ && \ + "%{request:Module-Failure-Message[0]}" !~ /Crypt salt has the wrong format/) { + update reply { + &Filter-Id += 'fail 5a' + } +} + + +# Convert the Cleartext-Password to Password-With-Header and auth with that +# +update control { + &Password-With-Header := "{crypt}%{crypt:$1$abcdefgh:&Tmp-String-4}" + &Crypt-Password !* "" + &Cleartext-Password !* "" +} + + +# Crypt not available at compile time? Force the test to pass. +# +} +else { + update reply { + &Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/default-input.attrs b/src/tests/keywords/default-input.attrs new file mode 100644 index 0000000..93566a6 --- /dev/null +++ b/src/tests/keywords/default-input.attrs @@ -0,0 +1,11 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Filter-Id == 'filter' diff --git a/src/tests/keywords/else-error b/src/tests/keywords/else-error new file mode 100644 index 0000000..3816270 --- /dev/null +++ b/src/tests/keywords/else-error @@ -0,0 +1,14 @@ +# +# PRE: update if +# +# "else" has to be preceded by an "if" or "elsif" +# +if (1) { + update reply { + Filter-Id := "filter" + } + + else { # ERROR + ok + } +} diff --git a/src/tests/keywords/escape b/src/tests/keywords/escape new file mode 100644 index 0000000..5d0b3bc --- /dev/null +++ b/src/tests/keywords/escape @@ -0,0 +1,67 @@ +# +# PRE: update if xlat-attr-index +# +update request { + control:Cleartext-Password := 'hello' + reply:Filter-Id := "filter" + + Tmp-String-0 := '@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /' + Tmp-String-1 := '±§#$%^&+={[}];<,>?`|"' + Tmp-String-2 := '™¥¤' + Tmp-String-3 := '=C2=B1=C2=A7=23=24=25=5E=26=2B=3D=7B=5B=7D=5D=3B=3C=2C=3E=3F=60=7C=22' + Tmp-String-4 := '=E2=84=A2=C2=A5=C2=A4' + Tmp-String-5 := '=40=61=62=63=64=65=66=67' + + # Mixture of safe and unsafe chars + Tmp-String-6 := 'ŒČÿ' + Tmp-String-7 := 'Œ=C4=8Cÿ' +} + +if (<string>"%{escape:%{request:Tmp-String-0}}" != &Tmp-String-0) { + update reply { + Filter-Id += 'Fail 1' + } +} + +if (<string>"%{escape:%{request:Tmp-String-1}}" != &Tmp-String-3) { + update reply { + Filter-Id += 'Fail 2' + } +} + +if (<string>"%{escape:%{request:Tmp-String-2}}" != &Tmp-String-4) { + update reply { + Filter-Id += 'Fail 3' + } +} + +if (<string>"%{unescape:%{request:Tmp-String-0}}" != &Tmp-String-0) { + update reply { + Filter-Id += 'Fail 4' + } +} + +if (<string>"%{unescape:%{request:Tmp-String-3}}" != "%{Tmp-String-1}") { + update reply { + Filter-Id += 'Fail 5' + } +} + +if (<string>"%{unescape:%{request:Tmp-String-4}}" != &Tmp-String-2) { + update reply { + Filter-Id += 'Fail 6' + } +} + +if (<string>"%{escape:%{request:Tmp-String-6}}" != &Tmp-String-7) { + update reply { + Filter-Id += 'Fail 7' + } +} + +if (<string>"%{unescape:%{request:Tmp-String-7}}" != &Tmp-String-6) { + update reply { + Filter-Id += 'Fail 8' + } +} + diff --git a/src/tests/keywords/escape-sequences b/src/tests/keywords/escape-sequences new file mode 100644 index 0000000..967656d --- /dev/null +++ b/src/tests/keywords/escape-sequences @@ -0,0 +1,95 @@ +# +# PRE: update if xlat-attr-index +# +update request { + control:Cleartext-Password := 'hello' + Tmp-Octets-0 := 0x69206861766520736361727920656d626564646564207468696e67730020696e73696465206d65 + Tmp-Octets-1 := 0x30783031013078303707307830410A307830440D222230786230b0C2b0 + Tmp-String-0 := "i have scary embedded things\000 inside me" + Tmp-String-0 += "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°" + + # and again with single quoted strings. + # unlike other languages, \r, \t, and \n have meaning inside of 'string' + Tmp-String-1 := 'i have scary embedded things\000 inside me' + Tmp-String-1 += '0x01\0010x07\0070x0A\n0x0D\r""0xb0\260°' + + Tmp-String-2 := 'i have scary embedded things\000 inside me' + Tmp-String-2 += "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°" + + reply:Filter-Id := "filter" +} + + +if ("%{length:&Tmp-String-0}" != 39) { + update reply { + Filter-Id += 'fail l-0' + } +} + +if ("%{length:&Tmp-String-1}" != 42) { + update reply { + Filter-Id += 'fail l-1' + } +} + +if ("%{string:Tmp-Octets-0}" != "i have scary embedded things\000 inside me") { + update reply { + Filter-Id += 'fail 1' + } +} + +if (&Tmp-String-0 != "i have scary embedded things\000 inside me") { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{string:Tmp-Octets-1}" != "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{Tmp-String-0[0]}" != "i have scary embedded things\000 inside me") { + update reply { + Filter-Id += 'fail 4' + } +} + +if ("%{Tmp-String-0[1]}" != "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") { + update reply { + Filter-Id += 'fail 5' + } +} + +# And another slightly different codepath... +if ("%{Tmp-String-0[*]}" != "i have scary embedded things\000 inside me,0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") { + update reply { + Filter-Id += 'fail 6' + } +} + +if (&Tmp-String-0[0] != &Tmp-String-0[0]) { + update reply { + Filter-Id += 'fail 7' + } +} + +# +# This seems weird... double escapes for most things, but single escapes +# for the quotation marks. +# +if ("%{Tmp-String-2[1]}" != "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°") { + update reply { + Filter-Id += 'fail 8' + } +} + +# +# And again as an attribute reference +# +if (&Tmp-String-2[1] != "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°") { + update reply { + Filter-Id += 'fail 9' + } +} diff --git a/src/tests/keywords/expand b/src/tests/keywords/expand new file mode 100644 index 0000000..ada0ee9 --- /dev/null +++ b/src/tests/keywords/expand @@ -0,0 +1,39 @@ +# +# PRE: update switch +# + +# +# This is a virtual attribute. +# It is NOT optimized to +# +# switch &Request-Processing-Stage +# +# because it doesn't really exist. +# The xlat expansion code will take care of +# returning the string value of the "attribute" +# +switch "%{Request-Processing-Stage}" { + case authorize { + update reply { + Filter-Id := "filter" + } + } + + case authenticate { + update reply { + Filter-Id := "authenticate" + } + } + + case bob { + update reply { + Filter-Id := "bob" + } + } + + case { + update reply { + Filter-Id := "default" + } + } +} diff --git a/src/tests/keywords/expr b/src/tests/keywords/expr new file mode 100644 index 0000000..7645931 --- /dev/null +++ b/src/tests/keywords/expr @@ -0,0 +1,108 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +# +# Simple +# +if ("%{expr: 1 + 2 + 3 + 4}" != 10) { + update reply { + Filter-Id := "fail-1" + } +} + +# +# Precedence +# +if ("%{expr: 1 + 2 * 3 + 4}" != 11) { + update reply { + Filter-Id := "fail-2" + } +} + +# +# attribute references +# +update request { + Tmp-Integer-0 = 1 + Tmp-Integer-1 = 3 + Tmp-Integer-2 = 4 + Tmp-Date-0 = "%l" +} + +if ("%{expr: 1 + 2 * &Tmp-Integer-1 + 4}" != 11) { + update reply { + Filter-Id := "fail-3" + } +} + +if ("%{expr: 1 + 2 * (&Tmp-Integer-1 + 4)}" != 15) { + update reply { + Filter-Id := "fail-4" + } +} + +if ("%{expr: 1 + 2 * (&Tmp-Integer-1 + &Tmp-Integer-2)}" != 15) { + update reply { + Filter-Id := "fail-5" + } +} + +if ("%{expr: 1 & ~1}" != 0) { + update reply { + Filter-Id := "fail-6" + } +} + +if ("%{expr: 1 & ~2}" != 1) { + update reply { + Filter-Id := "fail-7" + } +} + +if ("%{expr: -1 * 2}" != -2) { + update reply { + Filter-Id := "fail-8" + } +} + +if ("%{expr: 2 - -1}" != 3) { + update reply { + Filter-Id := "fail-9" + } +} + +if ("%{expr: 1 << 2 | 1}" != 5) { + update reply { + Filter-Id := "fail-10" + } +} + +if ("%{expr: &Tmp-Date-0}" <= 0) { + update reply { + Filter-Id := "fail-11" + } +} + +# +# Unary negation +# +if ("%{expr: 6 + -(1 + 3)}" != 2) { + update reply { + Filter-Id := "fail-12" + } +} + +if ("%{expr: 6 * -&Tmp-Integer-2}" != -24) { + update reply { + Filter-Id := "fail-13" + } +} + diff --git a/src/tests/keywords/foreach b/src/tests/keywords/foreach new file mode 100644 index 0000000..9a4c266 --- /dev/null +++ b/src/tests/keywords/foreach @@ -0,0 +1,5 @@ +foreach Filter-Id { + update reply { + Called-Station-Id += "%{Foreach-Variable-0}" + } +} diff --git a/src/tests/keywords/foreach-break b/src/tests/keywords/foreach-break new file mode 100644 index 0000000..67812fe --- /dev/null +++ b/src/tests/keywords/foreach-break @@ -0,0 +1,73 @@ +# PRE: foreach +# + +# +# We DON'T want to see this one. +# +update request { + Filter-Id += "broken" +} + +foreach Filter-Id { + # + # If we see this one, "break" out of the + # foreach loop. + # + if ("%{Foreach-Variable-0}" == "broken") { + break + } + + update reply { + Called-Station-Id += "%{Foreach-Variable-0}" + } +} + + +# +# Adding attribute during request and immediately breaking +# +update { + request:Filter-Id += "1" + request:Filter-Id += "2" +} + +foreach &request:Reply-Message { + if("%{Foreach-Variable-0}" == "1") { + update { + request:Filter-Id += "3" + } + break + + update reply { + Filter-Id := "fail-break-1" + } + } +} + +update { + request:Filter-Id !* ANY +} + +# +# Adding attribute during request and continuing +# +update { + request:Filter-Id += "1" + request:Filter-Id += "2" +} + +foreach &request:Reply-Message { + if("%{Foreach-Variable-0}" == "1") { + update { + request:Filter-Id += "3" + } + } + + if ("%{Foreach-Variable-0}" == "3") { + break + + update reply { + Filter-Id := "fail-break-2" + } + } +} diff --git a/src/tests/keywords/foreach-break-2 b/src/tests/keywords/foreach-break-2 new file mode 100644 index 0000000..b1f6040 --- /dev/null +++ b/src/tests/keywords/foreach-break-2 @@ -0,0 +1,46 @@ +# +# PRE: foreach foreach-break +# + +update request { + Calling-Station-Id := "ABCDEF_8" +} + +update control { + &Tmp-String-0 := "0" + &Tmp-String-0 += "1" + &Tmp-String-0 += "2" + &Tmp-String-0 += "3" + &Tmp-String-0 += "4" + &Tmp-String-0 += "5" + &Tmp-String-0 += "6" + &Tmp-String-0 += "7" + &Tmp-String-0 += "8" + &Tmp-String-0 += "9" + &Tmp-String-0 += "a" + &Tmp-String-0 += "b" + &Tmp-String-0 += "c" + &Tmp-String-0 += "d" + &Tmp-String-0 += "e" + &Tmp-String-0 += "f" + &Tmp-String-0 += "g" +} + +foreach control:Tmp-String-0 { + if ("%{Calling-Station-Id[*]}" =~ /([A-Z0-9\-]*)_%{Foreach-Variable-0}/) { + update request { + Called-Station-Id := "%{1}" + } + update reply { + Filter-Id := "filter" + } + + break + } + elsif ("%{Foreach-Variable-0}" == '9') { + update reply { + Filter-Id := "fail-9" + } + reject + } +} diff --git a/src/tests/keywords/foreach-break-3 b/src/tests/keywords/foreach-break-3 new file mode 100644 index 0000000..af03da6 --- /dev/null +++ b/src/tests/keywords/foreach-break-3 @@ -0,0 +1,44 @@ +# +# PRE: foreach foreach-break +# + +update request { + Calling-Station-Id := "8" +} + +update control { + &Calling-Station-Id := "0" + &Calling-Station-Id += "1" + &Calling-Station-Id += "2" + &Calling-Station-Id += "3" + &Calling-Station-Id += "4" + &Calling-Station-Id += "5" + &Calling-Station-Id += "6" + &Calling-Station-Id += "7" + &Calling-Station-Id += "8" + &Calling-Station-Id += "9" + &Calling-Station-Id += "a" + &Calling-Station-Id += "b" + &Calling-Station-Id += "c" + &Calling-Station-Id += "d" + &Calling-Station-Id += "e" + &Calling-Station-Id += "f" + &Calling-Station-Id += "g" +} + +foreach &control:Calling-Station-Id { + if (&request:Calling-Station-Id == "%{Foreach-Variable-0}") { + update reply { + Filter-Id := "filter" + } + + break + } + elsif ("%{Foreach-Variable-0}" == '9') { + update reply { + Filter-Id := "fail-9" + } + + reject + } +} diff --git a/src/tests/keywords/foreach-break-4 b/src/tests/keywords/foreach-break-4 new file mode 100644 index 0000000..037af8e --- /dev/null +++ b/src/tests/keywords/foreach-break-4 @@ -0,0 +1,44 @@ +# +# PRE: foreach foreach-break-3 +# + +update request { + Calling-Station-Id := "8" +} + +update control { + &Calling-Station-Id := "0" + &Calling-Station-Id += "1" + &Calling-Station-Id += "2" + &Calling-Station-Id += "3" + &Calling-Station-Id += "4" + &Calling-Station-Id += "5" + &Calling-Station-Id += "6" + &Calling-Station-Id += "7" + &Calling-Station-Id += "8" + &Calling-Station-Id += "9" + &Calling-Station-Id += "a" + &Calling-Station-Id += "b" + &Calling-Station-Id += "c" + &Calling-Station-Id += "d" + &Calling-Station-Id += "e" + &Calling-Station-Id += "f" + &Calling-Station-Id += "g" +} + +foreach &control:Calling-Station-Id { + if (&request:Calling-Station-Id == "%{Foreach-Variable-0}") { + update reply { + Filter-Id := "filter" + } + + break + } + elsif ("%{Foreach-Variable-0}" == '9') { + update reply { + Filter-Id := "fail-9" + } + + reject + } +} diff --git a/src/tests/keywords/foreach-break.attrs b/src/tests/keywords/foreach-break.attrs new file mode 100644 index 0000000..26c2876 --- /dev/null +++ b/src/tests/keywords/foreach-break.attrs @@ -0,0 +1,18 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +Filter-Id = "1" +Filter-Id += "2" +Filter-Id += "3" +Filter-Id += "4" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Called-Station-Id == "1" +Called-Station-Id == "2" +Called-Station-Id == "3" +Called-Station-Id == "4" diff --git a/src/tests/keywords/foreach-error b/src/tests/keywords/foreach-error new file mode 100644 index 0000000..fb4a236 --- /dev/null +++ b/src/tests/keywords/foreach-error @@ -0,0 +1,5 @@ +foreach "%{expr:1 + 2}" { # ERROR + update reply { + Called-Station-Id += "%{Foreach-Variable-0}" + } +} diff --git a/src/tests/keywords/foreach-isolation b/src/tests/keywords/foreach-isolation new file mode 100644 index 0000000..b77806d --- /dev/null +++ b/src/tests/keywords/foreach-isolation @@ -0,0 +1,38 @@ +# +# PRE: foreach if-multivalue +# + +update { + &reply:Filter-Id := 'filter' + &control:Tmp-String-0 := '0' + &control:Tmp-String-0 += '1' + &control:Tmp-String-0 += '2' + &control:Tmp-String-0 += '3' +} + +foreach control:Tmp-String-0 { + update control { + Tmp-String-0 -= "%{expr:%{Foreach-Variable-0} + 1}" + } + update request { + Tmp-String-0 += "%{Foreach-Variable-0}" + } +} + +if (!&Tmp-String-0[0] || !&Tmp-String-0[1] || !&Tmp-String-0[2] || !&Tmp-String-0[3]) { + update reply { + Filter-Id += 'Fail 0' + } +} + +if ((&Tmp-String-0[0] != '0') || (&Tmp-String-0[1] != '1') || (&Tmp-String-0[2] != '2') || (&Tmp-String-0[3] != '3')) { + update reply { + Filter-Id += 'Fail 1' + } +} + +if (!&control:Tmp-String-0[0] || &control:Tmp-String-0[1] || &control:Tmp-String-0[2] || &control:Tmp-String-0[3]) { + update reply { + Filter-Id += 'Fail 2' + } +} diff --git a/src/tests/keywords/foreach-list b/src/tests/keywords/foreach-list new file mode 100644 index 0000000..4780e4f --- /dev/null +++ b/src/tests/keywords/foreach-list @@ -0,0 +1,5 @@ +foreach &request: { + update reply { + Called-Station-Id += "%{Foreach-Variable-0}" + } +} diff --git a/src/tests/keywords/foreach-list.attrs b/src/tests/keywords/foreach-list.attrs new file mode 100644 index 0000000..aedd599 --- /dev/null +++ b/src/tests/keywords/foreach-list.attrs @@ -0,0 +1,21 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +Filter-Id = "1" +Filter-Id += "2" +Filter-Id += "3" +Filter-Id += "4" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Called-Station-Id == 'bob' +Called-Station-Id == 'hello' +Called-Station-Id == '1' +Called-Station-Id == '2' +Called-Station-Id == '3' +Called-Station-Id == '4' + diff --git a/src/tests/keywords/foreach-nested b/src/tests/keywords/foreach-nested new file mode 100644 index 0000000..b6109a3 --- /dev/null +++ b/src/tests/keywords/foreach-nested @@ -0,0 +1,9 @@ +# PRE: foreach +# +foreach Filter-Id { + foreach Calling-Station-Id { + update reply { + Called-Station-Id += "%{Foreach-Variable-0} %{Foreach-Variable-1}" + } + } +} diff --git a/src/tests/keywords/foreach-nested.attrs b/src/tests/keywords/foreach-nested.attrs new file mode 100644 index 0000000..52d1f81 --- /dev/null +++ b/src/tests/keywords/foreach-nested.attrs @@ -0,0 +1,25 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +Filter-Id = "1" +Filter-Id += "2" +Filter-Id += "3" +Filter-Id += "4" +Calling-Station-Id = "foo\n" +Calling-Station-Id += "bar" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Called-Station-Id == '1 foo\n' +Called-Station-Id == '1 bar' +Called-Station-Id == '2 foo\n' +Called-Station-Id == '2 bar' +Called-Station-Id == '3 foo\n' +Called-Station-Id == '3 bar' +Called-Station-Id == '4 foo\n' +Called-Station-Id == '4 bar' + diff --git a/src/tests/keywords/foreach-regex b/src/tests/keywords/foreach-regex new file mode 100644 index 0000000..dab57a3 --- /dev/null +++ b/src/tests/keywords/foreach-regex @@ -0,0 +1,26 @@ +# PRE: foreach if-regex-match + +# This is what most people end up using foreach for, +# so we should probably test it works. +update request { + Tmp-String-0 := "cisco" +} + +# Expanded regex +foreach Cisco-AVPair { + if ("%{Foreach-Variable-0}" =~ /^%{Tmp-String-0}=(.*)$/i) { + update reply { + Called-Station-Id += "%{1}" + } + } +} + +# Compiled regex +foreach Cisco-AVPair { + if ("%{Foreach-Variable-0}" =~ /^stupid=(.*)$/i) { + update reply { + Called-Station-Id += "%{1}" + } + } +} + diff --git a/src/tests/keywords/foreach-regex.attrs b/src/tests/keywords/foreach-regex.attrs new file mode 100644 index 0000000..79996c7 --- /dev/null +++ b/src/tests/keywords/foreach-regex.attrs @@ -0,0 +1,16 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +Cisco-AVPair = "stupid=1" +Cisco-AVPair += "retarded=2" +Cisco-AVPair += "cisco=3" +Cisco-AVPair += "shit=4" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Called-Station-Id == "3" +Called-Station-Id == "1" diff --git a/src/tests/keywords/foreach-return b/src/tests/keywords/foreach-return new file mode 100644 index 0000000..05409c9 --- /dev/null +++ b/src/tests/keywords/foreach-return @@ -0,0 +1,52 @@ +# PRE: foreach foreach-break +# + +update control { + Cleartext-Password := 'hello' +} + +# +# Adding attribute during request and immediately returning should still work +# +update request { + Filter-Id := "1" + Filter-Id += "2" + Filter-Id += "3" + Filter-Id += "4" + Filter-Id += "5" +} + +foreach &Filter-Id { + if ("%{Foreach-Variable-0}" == "3") { + update reply { + Filter-Id := "filter" + } + + # + # We need this because the "return" below + # will prevent the "pap" module from being run + # in the "authorize" section. + # + update control { + Auth-Type := PAP + } + + # + # Stop processing "authorize", and go to the next section. + # + return + + # + # Shouldn't reach this + # + update reply { + Filter-Id := "fail" + } + } + + if ("%{Foreach-Variable-0}" == "4") { + update reply { + Filter-Id := "fail-4" + } + } +} diff --git a/src/tests/keywords/foreach-varied-depth b/src/tests/keywords/foreach-varied-depth new file mode 100644 index 0000000..3c3918d --- /dev/null +++ b/src/tests/keywords/foreach-varied-depth @@ -0,0 +1,43 @@ +update { + control:Tmp-String-0 := "ssid=ABCDEF" + control:Tmp-String-0 += "ssid=GHIJKL" + reply:Filter-Id := "filter" +} + +if (User-Name) { + foreach &control:Tmp-String-0 { + if ("%{Foreach-Variable-0}" =~ /(.*)/) { + update control { + Tmp-String-1 := "%{1}" + } + } + } +} + +if (&control:Tmp-String-1 != 'ssid=GHIJKL') { + update reply { + Filter-Id += 'fail 0' + } +} + +update control { + Tmp-String-1 !* ANY +} + +foreach &control:Tmp-String-0 { + if ("%{Foreach-Variable-0}" =~ /(.*)/) { + update control { + Tmp-String-1 := "%{1}" + } + } +} + +if (&control:Tmp-String-1 != 'ssid=GHIJKL') { + update reply { + Filter-Id += 'fail 1' + } +} + +update control { + Tmp-String-1 !* ANY +} diff --git a/src/tests/keywords/foreach.attrs b/src/tests/keywords/foreach.attrs new file mode 100644 index 0000000..26c2876 --- /dev/null +++ b/src/tests/keywords/foreach.attrs @@ -0,0 +1,18 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +Filter-Id = "1" +Filter-Id += "2" +Filter-Id += "3" +Filter-Id += "4" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Called-Station-Id == "1" +Called-Station-Id == "2" +Called-Station-Id == "3" +Called-Station-Id == "4" diff --git a/src/tests/keywords/hex b/src/tests/keywords/hex new file mode 100644 index 0000000..b55bab2 --- /dev/null +++ b/src/tests/keywords/hex @@ -0,0 +1,141 @@ +# +# PRE: update +# +update reply { + Filter-Id := "filter" +} + +update request { + Tmp-String-0 := '9870' + Tmp-Octets-0 := 0x39383731 + Tmp-IP-Address-0 := 57.56.55.50 + Tmp-Date-0 := 959985459 + Tmp-Integer-0 := 959985460 + Tmp-Cast-Abinary := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812' + Tmp-Cast-IfId := '0000:0000:3938:3737' + Tmp-Cast-IPv6Addr := '::3938:3738' + Tmp-Cast-IPv6Prefix := '::3938:3739/128' + Tmp-Cast-Byte := 58 + Tmp-Cast-Short := 14139 + Tmp-Cast-Ethernet := 00:00:39:38:37:3c + Tmp-Cast-Integer64 := 1152921505566832445 + Tmp-Cast-IPv4Prefix := 57.56.55.62/32 +} + +update request { + Tmp-String-0 := "%{hex:Tmp-String-0}" + Tmp-String-1 := "%{hex:Tmp-Octets-0}" + Tmp-String-2 := "%{hex:Tmp-IP-Address-0}" + Tmp-String-3 := "%{hex:Tmp-Date-0}" + Tmp-String-4 := "%{hex:Tmp-Integer-0}" + Tmp-String-5 := "%{hex:Tmp-Cast-Abinary}" + Tmp-String-6 := "%{hex:Tmp-Cast-Ifid}" + Tmp-String-7 := "%{hex:Tmp-Cast-IPv6Addr}" + Tmp-String-8 := "%{hex:Tmp-Cast-IPv6Prefix}" + Tmp-String-9 := "%{hex:Tmp-Cast-Byte}" +} + +# String +if (Tmp-String-0 != '39383730') { + update reply { + Filter-Id += 'fail 1' + } +} + +# Octets +if (Tmp-String-1 != '39383731') { + update reply { + Filter-Id += 'fail 2' + } +} + +# IP Address +if (Tmp-String-2 != '39383732') { + update reply { + Filter-Id += 'fail 3' + } +} + +# Date +if (Tmp-String-3 != '39383733') { + update reply { + Filter-Id += 'fail 4' + } +} + +# Integer +if (Tmp-String-4 != '39383734') { + update reply { + Filter-Id += 'fail 5' + } +} + +# Abinary +if (Tmp-String-5 != '0101000039383735000000002000110000000714000200000000000000000000') { + update reply { + Filter-Id += 'fail 6' + } +} + +# ifid +if (Tmp-String-6 != '0000000039383737') { + update reply { + Filter-Id += 'fail 7' + } +} + +# ipv6addr +if (Tmp-String-7 != '00000000000000000000000039383738') { + update reply { + Filter-ID += 'fail 8' + } +} + +# ipv6addrprefix +if (Tmp-String-8 != '008000000000000000000000000039383739') { + update reply { + Filter-ID += 'fail 9' + } +} + +# byte +if (Tmp-String-9 != '3a') { + update reply { + Filter-ID += "fail 10 - expected 3a got %{Tmp-String-9}" + } +} + +update request { + Tmp-String-0 := "%{hex:Tmp-Cast-Short}" + Tmp-String-1 := "%{hex:Tmp-Cast-Ethernet}" + Tmp-String-2 := "%{hex:Tmp-Cast-Integer64}" + Tmp-String-3 := "%{hex:Tmp-Cast-IPv4Prefix}" +} + +# short +if (Tmp-String-0 != '373b') { + update reply { + Filter-ID += 'fail 11' + } +} + +# ethernet +if (Tmp-String-1 != '00003938373c') { + update reply { + Filter-Id += 'fail 12' + } +} + +# integer64 +if (Tmp-String-2 != '100000003938373d') { + update reply { + Filter-Id += 'fail 13' + } +} + +# ipv4prefix +if (Tmp-String-3 != '00203938373e') { + update reply { + Filter-Id += 'fail 14 expected 00203938373e got %{Tmp-String-3}' + } +} diff --git a/src/tests/keywords/if b/src/tests/keywords/if new file mode 100644 index 0000000..a146029 --- /dev/null +++ b/src/tests/keywords/if @@ -0,0 +1,10 @@ +# +# PRE: update +# +# Static if condition +# +if (1) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/if-bob b/src/tests/keywords/if-bob new file mode 100644 index 0000000..4e8ae3c --- /dev/null +++ b/src/tests/keywords/if-bob @@ -0,0 +1,15 @@ +# PRE: if +# +# Matching "if" conditions +# +if (User-Name == "bob") { + update reply { + Filter-Id := "filter" + } +} + +if (User-Name != "bob") { + update reply { + Filter-Id := "not bob" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/if-else b/src/tests/keywords/if-else new file mode 100644 index 0000000..788d606 --- /dev/null +++ b/src/tests/keywords/if-else @@ -0,0 +1,15 @@ +# +# PRE: if +# +# Matching "if" conditions +# +if (User-Name != "bob") { + update reply { + Filter-Id := "not bob" + } +} +else { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/if-elsif b/src/tests/keywords/if-elsif new file mode 100644 index 0000000..c0a41ed --- /dev/null +++ b/src/tests/keywords/if-elsif @@ -0,0 +1,19 @@ +# PRE: if if-else +# +# Matching "if" conditions +# +if (User-Name != "bob") { + update reply { + Filter-Id := "not bob" + } +} +elsif (User-Name == "bob") { + update reply { + Filter-Id := "filter" + } +} +else { + update reply { + Filter-Id := "last else" + } +} diff --git a/src/tests/keywords/if-multivalue b/src/tests/keywords/if-multivalue new file mode 100644 index 0000000..f12d6fe --- /dev/null +++ b/src/tests/keywords/if-multivalue @@ -0,0 +1,173 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tmp-String-0 := 'foo' + Tmp-String-0 += 'bar' + Tmp-String-0 += 'baz' + + Tmp-String-1 := 'GROUP ADMINISTRATORS' + Tmp-String-1 += 'GROUP STUDENTS' + Tmp-String-1 += 'GROUP PEONS' + + Tmp-String-2 := 'PEONS' + Tmp-String-2 += 'STUDENTS' + Tmp-String-2 += 'ADMINISTRATORS' + + Tmp-String-3 := 'no' + Tmp-String-3 += 'no' + Tmp-String-3 += 'yes' + + Tmp-Integer-0 := 1 + Tmp-Integer-0 += 2 + Tmp-Integer-0 += 5 +} + +update control { + Tmp-String-0 := 'foo' + Tmp-String-0 += 'bar' + Tmp-String-0 += 'baz' + + Tmp-String-1 := 'boink' + Tmp-String-1 += 'tard' + Tmp-String-1 += 'dink' + Tmp-String-1 += 'slink' + + Tmp-Integer-0 := 01 + Tmp-Integer-0 += 02 + Tmp-Integer-0 += 05 + Tmp-Integer-0 += 04 + + Tmp-Integer-1 := 10 + Tmp-Integer-1 += 20 + Tmp-Integer-1 += 30 +} + +# +# Mmmm O(N^2) +# +if (&request:Tmp-String-0[*] != &control:Tmp-String-0[*]) { + update reply { + Filter-Id += 'fail 0' + } +} + +if (&request:Tmp-String-0[*] == &control:Tmp-String-1[*]) { + update reply { + Filter-Id += 'fail 1' + } +} + +if (&request:Tmp-String-1[*] == &control:Tmp-String-0[*]) { + update reply { + Filter-Id += 'fail 2' + } +} + +# +# Integer comparison and normalisation +# +if (&request:Tmp-Integer-0 != &control:Tmp-Integer-0) { + update reply { + Filter-Id += 'fail 3' + } +} + +# +# if any value of request:Tmp-Integer-0 > any value of +# request:Tmp-Integer-1 then evaluate to true +# +if (&request:Tmp-Integer-0[*] > &control:Tmp-Integer-1[*]) { + update reply { + Filter-Id += 'fail 4' + } +} + +# +# Compiled regex comparisons +# +if (&request:Tmp-String-1[*] !~ /PEONS$/) { + update reply { + Filter-Id += 'fail 5' + } +} + +if (&control:Tmp-String-1 =~ /PEONS$/) { + update reply { + Filter-Id += 'fail 6' + } +} + +if (&control:Tmp-String-1 =~ /DINKS$/) { + update reply { + Filter-Id += 'fail 7' + } +} + +# +# Dynamic regex comparisons +# +if (&request:Tmp-String-1[*] !~ /%{Tmp-String-2[0]}$/) { + update reply { + Filter-Id += 'fail 8' + } +} + +if (&request:Tmp-String-1 =~ /%{Tmp-String-2[1]}$/) { + update reply { + Filter-Id += 'fail 9' + } +} + +if (&request:Tmp-String-1 !~ /%{Tmp-String-2[2]}$/) { + update reply { + Filter-Id += 'fail 10' + } +} + +if (&request:Tmp-String-1 =~ /%{Tmp-String-2[#]}$/) { + update reply { + Filter-Id += 'fail 11' + } +} + +# +# XLAT virtual comparisons +# +if (&control:Tmp-Integer-0[*] != "%{control:Tmp-Integer-0[#]}") { + update reply { + Filter-Id += 'fail 12' + } +} + +# +# Literal comparisons +# +if (&control:Tmp-String-1[*] != 'boink') { + update reply { + Filter-Id += 'fail 13' + } +} + +if (&control:Tmp-String-1[*] == 'foo') { + update reply { + Filter-Id += 'fail 14' + } +} + +if (&request:Tmp-Integer-0[*] > 10) { + update reply { + Filter-Id += 'fail 15' + } +} + +if (!(&request:Tmp-Integer-0[*] < 10)) { + update reply { + Filter-Id += 'fail 16' + } +} diff --git a/src/tests/keywords/if-paircmp b/src/tests/keywords/if-paircmp new file mode 100644 index 0000000..6ed06e3 --- /dev/null +++ b/src/tests/keywords/if-paircmp @@ -0,0 +1,27 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +# +# Paircmp +# + +# +# Passing 'yes' causes the test paircmp to return match +# Passing 'no' causes the test paircmp to return a non-match +# +if (&Test-Paircmp != 'yes') { + update reply { + Filter-Id += 'fail 1' + } +} + +if (&Test-Paircmp == 'no') { + update reply { + Filter-Id += 'fail 2' + } +} diff --git a/src/tests/keywords/if-rcode-error b/src/tests/keywords/if-rcode-error new file mode 100644 index 0000000..fed8a49 --- /dev/null +++ b/src/tests/keywords/if-rcode-error @@ -0,0 +1,11 @@ +# PRE: if +# +# return code in an "if" section. +# +if (User-Name == "bob") { + update reply { + Filter-Id := "filter" + } + + ok = reject # ERROR +} diff --git a/src/tests/keywords/if-regex-bad-attribute b/src/tests/keywords/if-regex-bad-attribute new file mode 100644 index 0000000..f330fde --- /dev/null +++ b/src/tests/keywords/if-regex-bad-attribute @@ -0,0 +1,21 @@ +# +# PRE: if-regex-match if-regex-error +# + +# +# This should parse +# +if (&User-Name =~ /%{User-Name}/) { + update reply { + Filter-Id := "filter" + } +} + +# +# Check regexes which refer to unknown attributes +# +if (&User-Name =~ /%{What-The-Heck-Is-This-Thing}/) { # ERROR + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/if-regex-error b/src/tests/keywords/if-regex-error new file mode 100644 index 0000000..f618e82 --- /dev/null +++ b/src/tests/keywords/if-regex-error @@ -0,0 +1,12 @@ +# +# PRE: if-regex-match +# + +# +# Check that bad regular expressions will fail +# +if (&User-Name =~ /[a-3]/) { # ERROR + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/if-regex-match b/src/tests/keywords/if-regex-match new file mode 100644 index 0000000..458e455 --- /dev/null +++ b/src/tests/keywords/if-regex-match @@ -0,0 +1,183 @@ +# PRE: if +# +update request { + Tmp-Integer-0 := '123456789' +} + +# Non matching on attribute ref +if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) { + update reply { + Filter-Id += 'Fail 0' + } +} + +# Matching on xlat expanded value +if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) { + update reply { + Filter-Id += 'Fail 1' + } +} + +# Matching on attribute ref with capture groups +if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) { + # Test all the capture groups + update { + reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}" + } +} +else { + update reply { + Filter-Id += 'Fail 2' + } +} + +# Checking capture groups are cleared out correctly +if (User-Name =~ /^([0-9])_%{Tmp-String-0}/) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') { + update reply { + Filter-Id += 'Fail 3' + } + } +} +else { + update reply { + Filter-Id += 'Fail 3.5' + } +} + +# Checking capture groups are cleared out correctly when there are no matches +if (User-Name =~ /^.%{Tmp-String-0}/) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') { + update reply { + Filter-Id += 'Fail 4' + } + } +} +else { + update reply { + Filter-Id += 'Fail 4.5' + } +} + +# Checking full capture group range +if ('a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F' =~ /^(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)$/) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}%{8}%{9}%{10}%{11}%{12}%{13}%{14}%{15}%{16}%{17}%{18}%{19}%{20}%{21}%{22}%{23}%{24}%{25}%{26}%{27}%{28}%{29}%{30}%{31}%{32}" != 'a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_FabcdefghijklmnopqrstuvwxyzABCDEF') { + update reply { + Filter-Id += 'Fail 6' + } + } +} +else { + update reply { + Filter-Id += 'Fail 6.5' + } +} + +# Checking full capture group overun +if ('a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F_G' =~ /^(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)$/) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}%{8}%{9}%{10}%{11}%{12}%{13}%{14}%{15}%{16}%{17}%{18}%{19}%{20}%{21}%{22}%{23}%{24}%{25}%{26}%{27}%{28}%{29}%{30}%{31}%{32}" != 'a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F_GabcdefghijklmnopqrstuvwxyzABCDEF') { + update reply { + Filter-Id += 'Fail 7' + } + } +} +else { + update reply { + Filter-Id += 'Fail 7.5' + } +} + +# uncompiled - ref - insensitive +if (Calling-Station-Id !~ /:roamyroam%{Tmp-String-0}$/i) { + update reply { + Filter-Id += 'Fail 8' + } +} + +# uncompiled - expansion - insensitive +if ("%{Calling-Station-Id}" !~ /:roamyroam%{Tmp-String-0}$/i) { + update reply { + Filter-Id += 'Fail 9' + } +} + +# uncompiled - enum - ref - insensitive +if (Service-Type !~ /^framed-user%{Tmp-String-0}$/i) { + update reply { + Filter-Id += 'Fail 10' + } +} + +# uncompiled - enum - expansion - insensitive +if ("%{Service-Type}" !~ /^framed-user%{Tmp-String-0}$/i) { + update reply { + Filter-Id += 'Fail 11' + } +} + +# uncompiled - enum - ref +if (Service-Type =~ /^framed-user%{Tmp-String-0}$/) { + update reply { + Filter-Id += 'Fail 12' + } +} + +# uncompiled - integer - ref +if (Tmp-Integer-0 !~ /%{Tmp-Integer-0}/) { + update reply { + Filter-Id += 'Fail 13' + } +} + +update request { + Tmp-String-0 := "foo\nbar" +} + +# uncompiled - ref - multiline +if (&Tmp-String-0 !~ /^foo$%{Tmp-String-8}/m) { + update reply { + Filter-Id += 'Fail 14' + } +} + +# uncompiled - ref - non-multiline +if (&Tmp-String-0 =~ /^foo$%{Tmp-String-8}/) { + update reply { + Filter-Id += 'Fail 15' + } +} + +# uncompiled - ref - non-multiline +if (&Tmp-String-0 !~ /^foo\nbar%{Tmp-String-8}$/) { + update reply { + Filter-Id += 'Fail 16' + } +} + +# uncompiled - ref - multiline +if (&Tmp-String-0 !~ /^bar%{Tmp-String-8}$/m) { + update reply { + Filter-Id += 'Fail 17' + } +} + +# uncompiled - ref - multiline - sensitive +if (&Tmp-String-0 =~ /^BAR%{Tmp-String-8}$/m) { + update reply { + Filter-Id += 'Fail 18' + } +} + +# uncompiled - ref - multiline - insensitive +if (&Tmp-String-0 !~ /^BAR%{Tmp-String-8}$/mi) { + update reply { + Filter-Id += 'Fail 19' + } +} + +# uncompiled - ref - multiline - insensitive (flag order reversed) +if (&Tmp-String-0 !~ /^BAR%{Tmp-String-8}$/im) { + update reply { + Filter-Id += 'Fail 20' + } +} diff --git a/src/tests/keywords/if-regex-match-comp b/src/tests/keywords/if-regex-match-comp new file mode 100644 index 0000000..c9c2d15 --- /dev/null +++ b/src/tests/keywords/if-regex-match-comp @@ -0,0 +1,149 @@ +# PRE: if +# + +# Non matching on attribute ref +if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) { + update reply { + Filter-Id += 'Fail 0' + } +} + +# Matching on xlat expanded value +if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) { + update reply { + Filter-Id += 'Fail 1' + } +} + +# Matching on attribute ref with capture groups +if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) { + # Test all the capture groups + update { + reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}" + } +} +else { + update reply { + Filter-Id += 'Fail 2' + } +} + +# Checking capture groups are cleared out correctly +if (User-Name =~ /^([0-9])_/) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') { + update reply { + Filter-Id += 'Fail 3' + } + } +} +else { + update reply { + Filter-Id += 'Fail 3.5' + } +} + +# Checking capture groups are cleared out correctly when there are no matches +if (User-Name =~ /^./) { + if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') { + update reply { + Filter-Id += 'Fail 4' + } + } +} +else { + update reply { + Filter-Id += 'Fail 4.5' + } +} + +# compiled - ref - insensitive +if (Calling-Station-Id !~ /:roamyroam$/i) { + update reply { + Filter-Id += 'Fail 5' + } +} + +# compiled - expansion - insensitive +if ("%{Calling-Station-Id}" !~ /:roamyroam$/i) { + update reply { + Filter-Id += 'Fail 6' + } +} + +# compiled - enum - ref - insensitive +if (Service-Type !~ /^framed-user$/i) { + update reply { + Filter-Id += 'Fail 7' + } +} + +# compiled - enum - expansion - insensitive +if ("%{Service-Type}" !~ /^framed-user$/i) { + update reply { + Filter-Id += 'Fail 8' + } +} + +# compiled - enum - ref +if (Service-Type =~ /^framed-user$/) { + update reply { + Filter-Id += 'Fail 9' + } +} + +update request { + Tmp-String-0 := "foo\nbar" +} + +# compiled - ref - multiline +if (&Tmp-String-0 !~ /^foo$/m) { + update reply { + Filter-Id += 'Fail 14' + } +} + +# compiled - ref - non-multiline +if (&Tmp-String-0 =~ /^foo$/) { + update reply { + Filter-Id += 'Fail 15' + } +} + +# compiled - ref - non-multiline + +# Not all POSIX implementations support the \n character classes +# so only run this test if the server was built with libpcre. +if (("${feature.regex-pcre}" == 'yes') && (&Tmp-String-0 !~ /^foo\nbar$/)) { + update reply { + Filter-Id += 'Fail 16' + } +} + +# compiled - ref - multiline +if (&Tmp-String-0 !~ /^bar$/m) { + update reply { + Filter-Id += 'Fail 17' + } +} + +# compiled - ref - multiline - sensitive +if (&Tmp-String-0 =~ /^BAR$/m) { + update reply { + Filter-Id += 'Fail 17' + } +} + +# compiled - ref - multiline - insensitive +if (&Tmp-String-0 !~ /^BAR$/mi) { + update reply { + Filter-Id += 'Fail 17' + } +} + +# compiled - ref - multiline - insensitive (flag order reversed) +if (&Tmp-String-0 !~ /^BAR$/im) { + update reply { + Filter-Id += 'Fail 18' + } +} + diff --git a/src/tests/keywords/if-regex-match-comp.attrs b/src/tests/keywords/if-regex-match-comp.attrs new file mode 100644 index 0000000..ba7188d --- /dev/null +++ b/src/tests/keywords/if-regex-match-comp.attrs @@ -0,0 +1,7 @@ +User-Name = '1_2_3_4_5_6_7' +User-Password = 'hello' +Service-Type := 'Framed-User' +Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM' + +Response-Packet-Type == Access-Accept +User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7' diff --git a/src/tests/keywords/if-regex-match-named b/src/tests/keywords/if-regex-match-named new file mode 100644 index 0000000..2aa665f --- /dev/null +++ b/src/tests/keywords/if-regex-match-named @@ -0,0 +1,117 @@ +# PRE: if +# +if ('${feature.regex-pcre}' == 'yes') { +update request { + Tmp-Integer-0 := '123456789' + Tmp-Integer-1 := 1 +} + +# Check failures when no previous capture +if ("%{regex:}" != "") { + update reply { + Filter-Id += 'Fail 0.1' + } +} + +if ("%{regex:foo}" != "") { + update reply { + Filter-Id += 'Fail 0.2' + } +} + +if ("%{regex:%{Tmp-Integer-1}}" != "") { + update reply { + Filter-Id += 'Fail 0.3' + } +} + +if ("%{regex:1}" != "") { + update reply { + Filter-Id += 'Fail 0.4' + } +} + +# uncompiled - ref - named capture groups +if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])%{Tmp-String-0}/) { + if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') { + update reply { + Filter-Id += 'Fail 1' + } + } +} +else { + update reply { + Filter-Id += 'Fail 1.5' + } +} + +# Checking capture groups are cleared out correctly +if (User-Name =~ /^(?<one>[0-9])_%{Tmp-String-0}/) { + if ("%{0}%{regex:one}%{regex:two}%{regex:three}%{regex:four}%{regex:five}%{regex:six}%{regex:seven}" != '1_1') { + update reply { + Filter-Id += 'Fail 2' + } + } +} +else { + update reply { + Filter-Id += 'Fail 2.5' + } +} + +# Checking capture groups are cleared out correctly when there are no matches +if (User-Name =~ /^.%{Tmp-String-0}/) { + if ("%{0}%{regex:one}%{regex:two}%{regex:three}%{regex:four}%{regex:five}%{regex:six}%{regex:seven}" != '1') { + update reply { + Filter-Id += 'Fail 3' + } + } +} +else { + update reply { + Filter-Id += 'Fail 3.5' + } +} + +# compiled - ref - named capture groups +if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) { + if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') { + update reply { + Filter-Id += 'Fail 4' + } + } +} +else { + update reply { + Filter-Id += 'Fail 4.5' + } +} + +# compiled - xlat - named capture groups +if ('1_2_3_4_5_6_7' =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) { + if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') { + update reply { + Filter-Id += 'Fail 5' + } + } +} +else { + update reply { + Filter-Id += 'Fail 5.5' + } +} + +# compiled - ref - named capture groups (numeric indexes) +if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) { + if ("%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') { + update reply { + Filter-Id += 'Fail 4' + } + } +} +else { + update reply { + Filter-Id += 'Fail 4.5' + } +} +} diff --git a/src/tests/keywords/if-regex-match-named.attrs b/src/tests/keywords/if-regex-match-named.attrs new file mode 100644 index 0000000..867ed23 --- /dev/null +++ b/src/tests/keywords/if-regex-match-named.attrs @@ -0,0 +1,6 @@ +User-Name = '1_2_3_4_5_6_7' +User-Password = 'hello' +Service-Type := 'Framed-User' +Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM' + +Response-Packet-Type == Access-Accept diff --git a/src/tests/keywords/if-regex-match.attrs b/src/tests/keywords/if-regex-match.attrs new file mode 100644 index 0000000..ba7188d --- /dev/null +++ b/src/tests/keywords/if-regex-match.attrs @@ -0,0 +1,7 @@ +User-Name = '1_2_3_4_5_6_7' +User-Password = 'hello' +Service-Type := 'Framed-User' +Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM' + +Response-Packet-Type == Access-Accept +User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7' diff --git a/src/tests/keywords/if-regex-multivalue b/src/tests/keywords/if-regex-multivalue new file mode 100644 index 0000000..7358c93 --- /dev/null +++ b/src/tests/keywords/if-regex-multivalue @@ -0,0 +1,26 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Cisco-AVPair := 'foo=bar' + Cisco-AVPair += 'bar=baz' + Cisco-AVPair += 'baz=foo' +} + +if (&Cisco-AVPair[*] =~ /bar=(.*)/) { + if ("%{1}" != 'baz') { + update reply { + Filter-Id += 'Fail 1' + } + } +} +else { + update reply { + Filter-Id += 'Fail 2' + } +} diff --git a/src/tests/keywords/if-skip b/src/tests/keywords/if-skip new file mode 100644 index 0000000..0e74f22 --- /dev/null +++ b/src/tests/keywords/if-skip @@ -0,0 +1,42 @@ +# PRE: if +# +# Conditions which statically evaluate to "false" +# have their entire contents skipped on load. +# +# Conditions which statically evaluate to "true" +# have the following "else" sections skipped, too. +# +# i.e. we can reference things which don't exist, +# and they'll get ignored. +# +if (0) { + no-such-module +} + +if (0) { + no-such-module +} +else { + ok +} + +if (1) { + ok +} +else { + no-such-module +} + +if (1) { + ok +} +elsif ("%{foo:bar}") { # no pass2 + no-such-module +} +else { + no-such-module +} + +update reply { + Filter-Id := "filter" +} diff --git a/src/tests/keywords/integer b/src/tests/keywords/integer new file mode 100644 index 0000000..7e43270 --- /dev/null +++ b/src/tests/keywords/integer @@ -0,0 +1,209 @@ +# +# PRE: update +# +update reply { + Filter-Id := "filter" +} + +update request { + Tmp-String-0 := '9870' + Tmp-String-1 := '98709870' + Tmp-String-2 := '987098709870' + Tmp-Octets-0 := 0x39383731 + Tmp-Octets-1 := 0x3938373139383731 + Tmp-Octets-2 := 0x393837313938373139383731 + Tmp-IP-Address-0 := 57.56.55.50 + Tmp-Date-0 := 959985459 + Tmp-Integer-0 := 959985460 + Tmp-Cast-Abinary := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812' + Tmp-Cast-IfId := '0000:0000:3938:3737' + Tmp-Cast-IPv6Addr := '::3938:3738' + Tmp-Cast-IPv6Prefix := '::3938:3739/128' + Tmp-Cast-Byte := 58 + Tmp-Cast-Short := 14139 + Tmp-Cast-Ethernet := 00:00:39:38:37:3c + Tmp-Cast-Integer64 := 1152921505566832445 + Tmp-Cast-IPv4Prefix := 57.56.55.62/32 +} + +update request { + Tmp-String-2 := "%{integer:Tmp-IP-Address-0}" + Tmp-String-3 := "%{integer:Tmp-Date-0}" + Tmp-String-4 := "%{integer:Tmp-Integer-0}" + Tmp-String-5 := "%{integer:Tmp-Cast-Abinary}" + Tmp-String-6 := "%{integer:Tmp-Cast-Ifid}" + Tmp-String-7 := "%{integer:Tmp-Cast-IPv6Addr}" + Tmp-String-8 := "%{integer:Tmp-Cast-IPv6Prefix}" +} + +# String - network order representation of a 4 char string +update request { + Tmp-Integer-0 := "%{integer:Tmp-String-0}" +} +if ((Tmp-String-0 != "%{string:Tmp-Integer-0}") || (Tmp-Integer-0 != 959985456)) { + update reply { + Filter-Id += 'fail 1' + } +} + +# String - network order representation of a 8 char string +update request { + Tmp-Integer64-0 := "%{integer:Tmp-String-1}" +} +if ((Tmp-String-1 != "%{string:Tmp-Integer64-0}") || (Tmp-Integer64-0 != 4123106139115632432)) { + update reply { + Filter-Id += 'fail 2' + } +} + +# String - Can't convert 12 byte string to integer (our biggest native size is a 64bit unsigned int) +if ("%{integer:Tmp-String-2}" != '') { + update reply { + Filter-Id += 'fail 3' + } +} + +# Octets - network order representation of a 4 byte octet string +update request { + Tmp-Integer-0 := "%{integer:Tmp-Octets-0}" +} +if (Tmp-Octets-0 != "0x%{hex:Tmp-Integer-0}") { + update reply { + Filter-Id += 'fail 4a' + } +} + +if (Tmp-Integer-0 != 959985457) { + update reply { + Filter-Id += 'fail 4b' + } +} + +# Octets - network order representation of a 8 byte octet string +update request { + Tmp-Integer64-0 := "%{integer:Tmp-Octets-1}" +} +if (Tmp-Octets-1 != "0x%{hex:Tmp-Integer64-0}") { + update reply { + Filter-Id += 'fail 5a' + } +} + +if (Tmp-Integer64-0 != 4123106143410599729) { + update reply { + Filter-Id += 'fail 5b' + } +} + +# String - Can't convert 12 byte octet string to integer (our biggest native size is a 64bit unsigned int) +if ("%{integer:Tmp-Octets-2}" != '') { + update reply { + Filter-Id += 'fail 6' + } +} + +# IP Address +if (Tmp-String-2 != '959985458') { + update reply { + Filter-Id += 'fail 7' + } +} + +if (<ipaddr>Tmp-String-2 != &Tmp-IP-Address-0) { + update reply { + Filter-Id += 'fail 8' + } +} + +# Date +if (Tmp-String-3 != '959985459') { + update reply { + Filter-Id += 'fail 9' + } +} + +# Integer +if (Tmp-String-4 != '959985460') { + update reply { + Filter-Id += 'fail 10' + } +} + +# Abinary - Can't convert ascend binary to an integer +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 11' + } +} + +# ifid - Can't convert interface ID to an integer +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 12' + } +} + +# ipv6addr - Can't convert IPv6 to integer +if (Tmp-String-7 != '959985464') { + update reply { + Filter-ID += 'fail 13' + } +} + +# ipv6addrprefix +if (Tmp-String-8 != '959985465') { + update reply { + Filter-ID += 'fail 14' + } +} +update request { + Tmp-String-0 := "%{integer:Tmp-Cast-Byte}" + Tmp-String-1 := "%{integer:Tmp-Cast-Short}" + Tmp-String-2 := "%{integer:Tmp-Cast-Ethernet}" + Tmp-String-3 := "%{integer:Tmp-Cast-Integer64}" + Tmp-String-4 := "%{integer:Tmp-Cast-IPv4Prefix}" +} + +# byte +if (Tmp-String-0 != '58') { + update reply { + Filter-ID += 'fail 15' + } +} + +# short +if (Tmp-String-1 != '14139') { + update reply { + Filter-ID += 'fail 16' + } +} + +# ethernet +if (Tmp-String-2 != '62913607630848') { + update reply { + Filter-Id += 'fail 17' + } +} +if (<ether>Tmp-String-2 != &Tmp-Cast-Ethernet) { + update reply { + Filter-Id += 'fail 18' + } +} + +# integer64 +if (Tmp-String-3 != '1152921505566832445') { + update reply { + Filter-Id += 'fail 19' + } +} + +# ipv4prefix +if (Tmp-String-4 != '959985470') { + update reply { + Filter-Id += 'fail 20' + } +} + + + + diff --git a/src/tests/keywords/ipaddr b/src/tests/keywords/ipaddr new file mode 100644 index 0000000..3010a23 --- /dev/null +++ b/src/tests/keywords/ipaddr @@ -0,0 +1,51 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' + reply:Filter-Id := "filter" +} + +update request { + # Dotted Quad + Tmp-IP-Address-0 := 127.0.0.1 + + # Dotted Quad with prefix + Tmp-IP-Address-1 := 127.0.0.2/32 + + # Hex (0x) + Tmp-IP-Address-2 := 0x7f000003 + + # Decimal + Tmp-IP-Address-3 := 2130706436 +} + +if (NAS-IP-Address != 127.0.0.1) { + update reply { + Filter-Id += "fail 1" + } +} + +if (Tmp-IP-Address-0 != 127.0.0.1) { + update reply { + Filter-Id += "fail 2" + } +} + +if (Tmp-IP-Address-1 != 127.0.0.2) { + update reply { + Filter-Id += "fail 3" + } +} + +if (Tmp-IP-Address-2 != 127.0.0.3) { + update reply { + Filter-Id += "fail 4" + } +} + +if (Tmp-IP-Address-3 != 127.0.0.4) { + update reply { + Filter-Id += "fail 5" + } +} diff --git a/src/tests/keywords/ipaddr-error b/src/tests/keywords/ipaddr-error new file mode 100644 index 0000000..5483a4f --- /dev/null +++ b/src/tests/keywords/ipaddr-error @@ -0,0 +1,10 @@ +# +# PRE: update ipaddr +# +update control { + Cleartext-Password := 'hello' +} + +update reply { + Framed-IP-Address := 256.1 # ERROR +} diff --git a/src/tests/keywords/ipaddr.attrs b/src/tests/keywords/ipaddr.attrs new file mode 100644 index 0000000..ab9c27e --- /dev/null +++ b/src/tests/keywords/ipaddr.attrs @@ -0,0 +1,12 @@ +# +# Input packet +# +User-Name = "bob" +User-Password = "hello" +NAS-IP-Address = 127.0.0.1 + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Filter-Id == 'filter' diff --git a/src/tests/keywords/ipprefix b/src/tests/keywords/ipprefix new file mode 100644 index 0000000..0ab8dce --- /dev/null +++ b/src/tests/keywords/ipprefix @@ -0,0 +1,52 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +update control { + Tmp-Cast-IPv4Prefix := 198.51.100.255/16 + Tmp-Cast-IPv6Prefix := ::198.51.100.255/112 + Framed-IP-Address := 198.51.0.1 +} + +if ("%{control:Tmp-Cast-IPv6Prefix}" != '::198.51.0.0/112') { + update reply { + Filter-Id += "Fail 0" + } +} + +if ("%{control:Tmp-Cast-IPv4Prefix}" != '198.51.0.0/16') { + update reply { + Filter-Id += "Fail 1" + } +} + +if (control:Tmp-Cast-IPv6Prefix != ::198.51.0.0/112) { + update reply { + Filter-Id += "Fail 2" + } +} + +if (control:Tmp-Cast-IPv4Prefix != 198.51.0.0/16) { + update reply { + Filter-Id += "Fail 3" + } +} + +if (!(&control:Tmp-Cast-IPv4Prefix < 198.0.0.0/8)) { + update reply { + Filter-Id += "Fail 4" + } +} + +if (!(&control:Framed-IP-Address < 198.51.0.0/16)) { + update reply { + Filter-Id += "Fail 5" + } +} diff --git a/src/tests/keywords/length b/src/tests/keywords/length new file mode 100644 index 0000000..ad37fc8 --- /dev/null +++ b/src/tests/keywords/length @@ -0,0 +1,155 @@ +# +# PRE: hex +# +update reply { + Filter-Id := "filter" +} + +update request { + Tmp-String-0 := '\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\ +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' + Tmp-String-2 := '9870' + Tmp-Octets-0 := 0x39383731 + Tmp-IP-Address-0 := 57.56.55.50 + Tmp-Date-0 := 959985459 + Tmp-Integer-0 := 959985460 + Tmp-Cast-Abinary := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812' + Tmp-Cast-IfId := '0000:0000:3938:3737' + Tmp-Cast-IPv6Addr := '::3938:3738' + Tmp-Cast-IPv6Prefix := '::3938:3739/128' + Tmp-Cast-Byte := 58 + Tmp-Cast-Short := 14139 + Tmp-Cast-Ethernet := 00:00:39:38:37:3c + Tmp-Cast-Integer64 := 1152921505566832445 + Tmp-Cast-IPv4Prefix := 57.56.55.62/32 +} + +update request { + Tmp-Integer-0 := "%{length:Tmp-String-0}" +} + +if (Tmp-Integer-0 != 260) { + update reply { + Filter-Id += 'fail 1' + } +} + +update request { + Tmp-Integer-0 := "%{length:Tmp-String-2}" + Tmp-Integer-1 := "%{length:Tmp-Octets-0}" + Tmp-Integer-2 := "%{length:Tmp-IP-Address-0}" + Tmp-Integer-3 := "%{length:Tmp-Date-0}" + Tmp-Integer-4 := "%{length:Tmp-Integer-0}" + Tmp-Integer-5 := "%{length:Tmp-Cast-Abinary}" + Tmp-Integer-6 := "%{length:Tmp-Cast-Ifid}" + Tmp-Integer-7 := "%{length:Tmp-Cast-IPv6Addr}" + Tmp-Integer-8 := "%{length:Tmp-Cast-IPv6Prefix}" + Tmp-Integer-9 := "%{length:Tmp-Cast-Byte}" +} + +# String - bin 0x39383730 +if (Tmp-Integer-0 != 4) { + update reply { + Filter-Id += 'fail 2' + } +} + +# Octets - bin 0x39383731 +if (Tmp-Integer-1 != 4) { + update reply { + Filter-Id += 'fail 3' + } +} + +# IP Address - bin 0x39383732 +if (Tmp-Integer-2 != 4) { + update reply { + Filter-Id += 'fail 4' + } +} + +# Date - bin 0x39383733 +if (Tmp-Integer-3 != 4) { + update reply { + Filter-Id += 'fail 5' + } +} + +# Integer - bin 0x39383734 +if (Tmp-Integer-4 != 4) { + update reply { + Filter-Id += 'fail 6' + } +} + +# Abinary - bin 0x0101000039383735000000002000110000000714000200000000000000000000 +if (Tmp-Integer-5 != 32) { + update reply { + Filter-Id += 'fail 7' + } +} + +# ifid - bin 0x0000000039383737 +if (Tmp-Integer-6 != 8) { + update reply { + Filter-Id += 'fail 8' + } +} + +# ipv6addr - bin 0x00000000000000000000000039383738 +if (Tmp-Integer-7 != 16) { + update reply { + Filter-ID += 'fail 9' + } +} + +# ipv6addrprefix - bin 0x008000000000000000000000000039383739 +if (Tmp-Integer-8 != 18) { + update reply { + Filter-ID += 'fail 10' + } +} + +# byte - bin 0x3a +if (Tmp-Integer-9 != 1) { + update reply { + Filter-ID += 'fail 11' + } +} + +update request { + Tmp-Integer-0 := "%{length:Tmp-Cast-Short}" + Tmp-Integer-1 := "%{length:Tmp-Cast-Ethernet}" + Tmp-Integer-2 := "%{length:Tmp-Cast-Integer64}" + Tmp-Integer-3 := "%{length:Tmp-Cast-IPv4Prefix}" +} + +# short - bin 0x373b +if (Tmp-Integer-0 != 2) { + update reply { + Filter-ID += 'fail 12' + } +} + +# ethernet - bin 0x00003938373c +if (Tmp-Integer-1 != 6) { + update reply { + Filter-Id += 'fail 13' + } +} + +# integer64 - bin 0x100000003938373d +if (Tmp-Integer-2 != 8) { + update reply { + Filter-Id += 'fail 14' + } +} + +# ipv4prefix - bin 0x00203938373e +if (Tmp-Integer-3 != 6) { + update reply { + Filter-Id += 'fail 15' + } +} diff --git a/src/tests/keywords/load-balance b/src/tests/keywords/load-balance new file mode 100644 index 0000000..d07939a --- /dev/null +++ b/src/tests/keywords/load-balance @@ -0,0 +1,97 @@ +# PRE: update if foreach +# +# Load-Balance blocks. +# +# Should distribute load between the modules. +# +update request { + Tmp-Integer-0 := 0 + Tmp-Integer-1 := 0 + + Tmp-Integer-2 += 0 # 0 + Tmp-Integer-2 += 1 + Tmp-Integer-2 += 2 + Tmp-Integer-2 += 3 + Tmp-Integer-2 += 4 + Tmp-Integer-2 += 5 + Tmp-Integer-2 += 6 + Tmp-Integer-2 += 7 + Tmp-Integer-2 += 8 + Tmp-Integer-2 += 9 # 10 + Tmp-Integer-2 += 0 + Tmp-Integer-2 += 1 + Tmp-Integer-2 += 2 + Tmp-Integer-2 += 3 + Tmp-Integer-2 += 4 + Tmp-Integer-2 += 5 + Tmp-Integer-2 += 6 + Tmp-Integer-2 += 7 + Tmp-Integer-2 += 8 + Tmp-Integer-2 += 9 # 20 + Tmp-Integer-2 += 0 + Tmp-Integer-2 += 1 + Tmp-Integer-2 += 2 + Tmp-Integer-2 += 3 + Tmp-Integer-2 += 4 + Tmp-Integer-2 += 5 + Tmp-Integer-2 += 6 + Tmp-Integer-2 += 7 + Tmp-Integer-2 += 8 + Tmp-Integer-2 += 9 # 30 + Tmp-Integer-2 += 0 + Tmp-Integer-2 += 1 + Tmp-Integer-2 += 2 + Tmp-Integer-2 += 3 + Tmp-Integer-2 += 4 + Tmp-Integer-2 += 5 + Tmp-Integer-2 += 6 + Tmp-Integer-2 += 7 + Tmp-Integer-2 += 8 + Tmp-Integer-2 += 9 # 40 + Tmp-Integer-2 += 0 + Tmp-Integer-2 += 1 + Tmp-Integer-2 += 2 + Tmp-Integer-2 += 3 + Tmp-Integer-2 += 4 + Tmp-Integer-2 += 5 + Tmp-Integer-2 += 6 + Tmp-Integer-2 += 7 + Tmp-Integer-2 += 8 + Tmp-Integer-2 += 9 # 49 +} + +# +# Loop 0..9 +# +foreach &Tmp-Integer-2 { + load-balance { + group { + update request { + Tmp-Integer-0 := "%{expr:%{Tmp-Integer-0} + 1}" + Filter-Id += "PICKED GROUP 1 %{Tmp-Integer-0} TIME(S)" + } + ok + } + group { + update request { + Tmp-Integer-1 := "%{expr:%{Tmp-Integer-1} + 1}" + Filter-Id += "PICKED GROUP 2 %{Tmp-Integer-1} TIME(S)" + } + ok + } + } +} + +# The chances of one group being used over another 50 times by random occurrence +# is quite small, and if this happens repeatedly, it's likely there's a bug in +# the load-balance code or random number generator. +if ((&Tmp-Integer-0 == 0) || (&Tmp-Integer-1 == 0)) { + update reply { + Filter-Id += "fail 1 %{Tmp-Integer-0}/%{Tmp-Integer-1})" + } +} +else { + update reply { + Filter-Id := 'filter' + } +} diff --git a/src/tests/keywords/log b/src/tests/keywords/log new file mode 100644 index 0000000..97a2557 --- /dev/null +++ b/src/tests/keywords/log @@ -0,0 +1,7 @@ +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} diff --git a/src/tests/keywords/map-xlat b/src/tests/keywords/map-xlat new file mode 100644 index 0000000..24446a5 --- /dev/null +++ b/src/tests/keywords/map-xlat @@ -0,0 +1,25 @@ +# +# PRE: update +# +# Test the map xlat +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := "filter" +} + +update { + Tmp-String-0 := '&control:Tmp-String-0 := \'testing123\'' +} + +if ("%{map:%{Tmp-String-0}}" != 1) { + update reply { + Filter-Id += 'Fail 0' + } +} + +if (&control:Tmp-String-0 != 'testing123') { + update reply { + Filter-Id += 'Fail 1' + } +} diff --git a/src/tests/keywords/md4 b/src/tests/keywords/md4 new file mode 100644 index 0000000..7e9b1ff --- /dev/null +++ b/src/tests/keywords/md4 @@ -0,0 +1,58 @@ +# +# PRE: update if +# +update reply { + Filter-Id := "filter" +} + +update { + control:Cleartext-Password := 'hello' + request:Tmp-String-0 := "This is a string\n" + request:Tmp-Octets-0 := 0x000504030201 + request:Tmp-String-1 := "what do ya want for nothing?" + request:Tmp-String-2 := "Jefe" +} + +# +# Put "This is a string" into a file and call "md5sum" on it. +# You should get this string. +# +if ("%{md4:This is a string\n}" != '1f60d5cd85e17bfbdda7c923822f060c') { + update reply { + Filter-Id += 'fail 1' + } +} + +if ("%{md4:&Tmp-String-0}" != '1f60d5cd85e17bfbdda7c923822f060c') { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{md4:&request:Tmp-String-0}" != '1f60d5cd85e17bfbdda7c923822f060c') { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{md4:%{request:Tmp-String-0}}" != '1f60d5cd85e17bfbdda7c923822f060c') { + update reply { + Filter-Id += 'fail 4' + } +} + +# +# MD4 should also be able to cope with references to octet attributes +# +if ("%{md4:&request:Tmp-Octets-0}" != 'ac3ed17b3cf19ec38352ec534a932fc6') { + update reply { + Filter-Id += 'fail 5' + } +} + +if ("%{md4:&Tmp-String-1}" != 'f7b44afb9cfdc877aa99d44654fe808b') { + update reply { + Filter-Id += 'fail 6' + } +} + diff --git a/src/tests/keywords/md5 b/src/tests/keywords/md5 new file mode 100644 index 0000000..a973660 --- /dev/null +++ b/src/tests/keywords/md5 @@ -0,0 +1,60 @@ +# +# PRE: update if +# +update reply { + Filter-Id := "filter" +} + +update { + control:Cleartext-Password := 'hello' + request:Tmp-String-0 := "This is a string\n" + request:Tmp-Octets-0 := 0x000504030201 + request:Tmp-String-1 := "what do ya want for nothing?" + request:Tmp-String-2 := "Jefe" +} + +# +# Put "This is a string" into a file and call "md5sum" on it. +# You should get this string. +# +if ("%{md5:This is a string\n}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') { + update reply { + Filter-Id += 'fail 1' + } +} + +if ("%{md5:&Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{md5:&request:Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{md5:%{request:Tmp-String-0}}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') { + update reply { + Filter-Id += 'fail 4' + } +} + +# +# MD5 should also be able to cope with references to octet attributes +# +if ("%{md5:&request:Tmp-Octets-0}" != 'c1e7fa505b2fc1fd0da6cac3db6f6f44') { + update reply { + Filter-Id += 'fail 5' + } +} + +# +# MD5 HMAC with attribute references +# +if ("%{hmacmd5:&Tmp-String-1 &Tmp-String-2}" != '750c783e6ab0b503eaa86e310a5db738') { + update reply { + Filter-Id += 'fail 6' + } +} diff --git a/src/tests/keywords/module-failure-message b/src/tests/keywords/module-failure-message new file mode 100644 index 0000000..51b1ef4 --- /dev/null +++ b/src/tests/keywords/module-failure-message @@ -0,0 +1,40 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +test +if ("%{request:Module-Failure-Message[0]}" != 'test: RERROR error message') { + update reply { + Filter-Id += 'Fail 0' + } +} + +if ("%{request:Module-Failure-Message[1]}" != 'test: RDEBUG error message') { + update reply { + Filter-Id += 'Fail 1' + } +} + +if ("%{request:Module-Failure-Message[2]}" != 'test: RDEBUG2 error message') { + update reply { + Filter-Id += 'Fail 2' + } +} + +if ("%{request:Module-Failure-Message[3]}" != 'test: RDEBUG3 error message') { + update reply { + Filter-Id += 'Fail 3' + } +} + +if ("%{request:Module-Failure-Message[4]}" != 'test: RDEBUG4 error message') { + update reply { + Filter-Id += 'Fail 4' + } +} diff --git a/src/tests/keywords/ok-return b/src/tests/keywords/ok-return new file mode 100644 index 0000000..f79ef02 --- /dev/null +++ b/src/tests/keywords/ok-return @@ -0,0 +1,13 @@ +update { + control:Auth-Type = 'Accept' + reply:Reply-Message = 'pass' +} + +# Section should exit after this statement +ok { + ok = return +} + +update { + reply:Reply-Message := 'fail' +} diff --git a/src/tests/keywords/ok-return.attrs b/src/tests/keywords/ok-return.attrs new file mode 100644 index 0000000..ad30f4d --- /dev/null +++ b/src/tests/keywords/ok-return.attrs @@ -0,0 +1,4 @@ +User-Name = 'test' + +Response-Packet-Type == Access-Accept +Reply-Message == 'pass' diff --git a/src/tests/keywords/pad b/src/tests/keywords/pad new file mode 100644 index 0000000..0a1f9ce --- /dev/null +++ b/src/tests/keywords/pad @@ -0,0 +1,62 @@ +# +# PRE: if update return +# + +update request { + Tmp-String-0 = "test" +} + +# +# rpad tests +# + +if ("%{rpad:&Tmp-String-0 7}" != "test ") { + update reply { + Filter-Id := "fail 1" + } + return +} + +if ("%{rpad:&Tmp-String-0 2}" != "te") { + update reply { + Filter-Id := "fail 2" + } + return +} + +if ("%{rpad:&Tmp-String-0 7 x}" != "testxxx") { + update reply { + Filter-Id := "fail 1" + } + return +} + +# +# lpad tests +# + +if ("%{lpad:&Tmp-String-0 7}" != " test") { + update reply { + Filter-Id := "fail 1" + } + return +} + +if ("%{lpad:&Tmp-String-0 2}" != "te") { + update reply { + Filter-Id := "fail 2" + } + return +} + +if ("%{lpad:&Tmp-String-0 7 x}" != "xxxtest") { + update reply { + Filter-Id := "fail 1" + } + return +} + +update reply { + Filter-Id := "filter" +} + diff --git a/src/tests/keywords/pairs b/src/tests/keywords/pairs new file mode 100644 index 0000000..4230c57 --- /dev/null +++ b/src/tests/keywords/pairs @@ -0,0 +1,42 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:Tmp-String-0 := "This is a string" + request:Tmp-String-0 += "This is another one" + request:Tmp-Octets-0 := 0x000504030201 + request:Tmp-Integer-0 := 7331 + request:Tunnel-Private-Group-Id:5 = 127.0.0.1 + reply:Filter-Id = 'filter' +} + +if ("%{pairs:request:}" != "User-Name = \"bob\", User-Password = \"hello\", Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\", Tmp-Octets-0 = 0x000504030201, Tmp-Integer-0 = 7331, Tunnel-Private-Group-Id:5 = \"127.0.0.1\"") { + update reply { + Filter-Id += 'fail 1' + } +} + +if ("%{pairs:Tmp-String-0}" != "Tmp-String-0 = \"This is a string\"") { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{pairs:Tmp-String-0[*]}" != "Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\"") { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{pairs:control:}" != "Cleartext-Password = \"hello\"") { + update reply { + Filter-Id += 'fail 4' + } +} + +if ("%{pairs:control:User-Name}" != '') { + update reply { + Filter-Id += 'fail 5' + } +} diff --git a/src/tests/keywords/pap b/src/tests/keywords/pap new file mode 100644 index 0000000..a347b7c --- /dev/null +++ b/src/tests/keywords/pap @@ -0,0 +1,146 @@ +# +# PRE: update if +# +update { + reply:Filter-Id := 'filter' + control: !* ANY + request:Tmp-String-0 := "5RNqNl8iYLbkCc7JhR8as4TtDDCX6otuuWtcja8rITUyx9zrnHSe9tTHGmKK" # 60 byte salt +} + +# +# Unencoded Cleartext-Password in password with header +# +update { + control:Password-With-Header := "%{request:User-Password}" +} +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 0' + } +} + +update { + control: !* ANY +} + +# +# Base64 encoded Cleartext-Password in password with header +# +update { + Tmp-String-1 := "{clear}%{User-Password}" +} +update { + control:Password-With-Header := "%{base64:&request:Tmp-String-1}" +} +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 0' + } +} + +update { + control: !* ANY +} + +# +# Hex encoded SSHA password +# +update { + control:Password-With-Header += "{ssha}%{sha1:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 1' + } +} + +update { + control: !* ANY +} + +# +# Base64 encoded SSHA password +# +update { + control:Tmp-String-1 := "%{sha1:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +# To Binary +update { + control:Tmp-Octets-0 := "0x%{control:Tmp-String-1}" +} + +# To Base64 +update { + control:Tmp-String-1 := "%{base64:&control:Tmp-Octets-0}" +} + +update { + control:Password-With-Header += "{ssha}%{control:Tmp-String-1}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 2' + } +} + +update { + control: !* ANY +} + +# +# Base64 of Base64 encoded SSHA password +# +update { + control:Tmp-String-1 := "%{sha1:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +# To Binary +update { + control:Tmp-Octets-0 := "0x%{control:Tmp-String-1}" +} + +# To Base64 +update { + control:Tmp-String-1 := "{ssha}%{base64:&control:Tmp-Octets-0}" +} + +update { + control:Password-With-Header += "%{base64:&control:Tmp-String-1}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 3' + } +} + +update { + control: !* ANY +} + +update control { + Auth-Type := Accept +} diff --git a/src/tests/keywords/pap-ssha2 b/src/tests/keywords/pap-ssha2 new file mode 100644 index 0000000..a8c9c9b --- /dev/null +++ b/src/tests/keywords/pap-ssha2 @@ -0,0 +1,114 @@ +# +# PRE: update if pap +# + +# +# Skip if the server wasn't built with openssl +# +if ('${feature.tls}' != 'yes') { + update control { + Response-Packet-Type := Access-Accept + } + handled +} + +update { + reply:Filter-Id := 'filter' + control: !* ANY + request:Tmp-String-0 := "5RNqNl8iYLbkCc7JhR8as4TtDDCX6otuuWtcja8rITUyx9zrnHSe9tTHGmKK" # 60 byte salt +} + +# +# Hex encoded SSHA2-512 password +# +update { + control:Password-With-Header += "{ssha512}%{sha512:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 1' + } +} + +update { + control: !* ANY +} + +# +# Base64 encoded SSHA2-512 password +# +update { + control:Tmp-String-1 := "%{sha512:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +# To Binary +update { + control:Tmp-Octets-0 := "0x%{control:Tmp-String-1}" +} + +# To Base64 +update { + control:Tmp-String-1 := "%{base64:&control:Tmp-Octets-0}" +} + +update { + control:Password-With-Header += "{ssha512}%{control:Tmp-String-1}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 2' + } +} + +update { + control: !* ANY +} + +# +# Base64 of Base64 encoded SSHA2-512 password +# +update { + control:Tmp-String-1 := "%{sha512:%{request:User-Password}%{&request:Tmp-String-0}}%{hex:&request:Tmp-String-0}" +} + +# To Binary +update { + control:Tmp-Octets-0 := "0x%{control:Tmp-String-1}" +} + +# To Base64 +update { + control:Tmp-String-1 := "{ssha512}%{base64:&control:Tmp-Octets-0}" +} + +update { + control:Password-With-Header += "%{base64:&control:Tmp-String-1}" +} + +pap.authorize +pap.authenticate { + reject = 1 +} +if (reject) { + update reply { + Filter-Id += 'fail 3' + } +} + +update { + control: !* ANY +} + +update control { + Auth-Type := Accept +} diff --git a/src/tests/keywords/radiusd.conf b/src/tests/keywords/radiusd.conf new file mode 100644 index 0000000..58cb356 --- /dev/null +++ b/src/tests/keywords/radiusd.conf @@ -0,0 +1,127 @@ +# +# Minimal radiusd.conf for testing keywords +# + +raddb = raddb +keyword = src/tests/keywords + +modconfdir = ${raddb}/mods-config + +correct_escapes = true + +# Only for testing! +# Setting this on a production system is a BAD IDEA. +security { + allow_vulnerable_openssl = yes +} + +modules { + $INCLUDE ${raddb}/mods-enabled/always + + $INCLUDE ${raddb}/mods-enabled/pap + + $INCLUDE ${raddb}/mods-enabled/expr + + $INCLUDE ${raddb}/mods-enabled/unpack + + test { + + } + + unix { + } + + cache { + driver = "rlm_cache_rbtree" + + key = "%{Tmp-String-0}" + ttl = 2 + + update { + &request:Tmp-String-1 := &control:Tmp-String-1 + &request:Tmp-Integer-0 := &control:Tmp-Integer-0 + &control: += &reply: + } + + add_stats = yes + } +} + +policy { + # + # Outputs the contents of the control list in debugging (-X) mode + # + debug_control { + if("%{debug_attr:control:}" == '') { + noop + } + } + + # + # Outputs the contents of the request list in debugging (-X) mode + # + debug_request { + if("%{debug_attr:request:}" == '') { + noop + } + } + + # + # Outputs the contents of the reply list in debugging (-X) mode + # + debug_reply { + if("%{debug_attr:reply:}" == '') { + noop + } + } + + # + # Outputs the contents of the main lists in debugging (-X) mode + # + debug_all { + debug_control + debug_request + debug_reply + } + + # + # Just check that this can be referred to as "virtual_policy.post-auth" + # + virtual_policy { + ok + } + + with.dots { + ok + } +} + +instantiate { + # + # Just check that this can be referred to as "virtual_instantiate.post-auth" + # + load-balance virtual_instantiate { + ok + ok + } +} + +server default { + authorize { + update control { + Cleartext-Password := 'hello' + } + + # + # Include the test file specified by the + # KEYWORD environment variable. + # + $INCLUDE ${keyword}/$ENV{KEYWORD} + + pap + } + + authenticate { + pap + } +} diff --git a/src/tests/keywords/redundant b/src/tests/keywords/redundant new file mode 100644 index 0000000..cebe7be --- /dev/null +++ b/src/tests/keywords/redundant @@ -0,0 +1,17 @@ +# PRE: update if foreach +# +# Redundant blocks. +# +# The first one fails, so the second one is used +# +redundant { + group { + fail + } + + group { + update reply { + Filter-Id := "filter" + } + } +} diff --git a/src/tests/keywords/redundant-error b/src/tests/keywords/redundant-error new file mode 100644 index 0000000..bec4335 --- /dev/null +++ b/src/tests/keywords/redundant-error @@ -0,0 +1,6 @@ +# +# Check that redundant blocks can't be empty +# +redundant { # ERROR + +} diff --git a/src/tests/keywords/redundant-load-balance b/src/tests/keywords/redundant-load-balance new file mode 100644 index 0000000..27721f1 --- /dev/null +++ b/src/tests/keywords/redundant-load-balance @@ -0,0 +1,65 @@ +# PRE: update if foreach +# +# Redundant blocks. +# +# The first one fails, so the second one is used +# +update request { + Tmp-Integer-0 := 0 + Tmp-Integer-1 += 0 + Tmp-Integer-1 += 1 + Tmp-Integer-1 += 2 + Tmp-Integer-1 += 3 + Tmp-Integer-1 += 4 + Tmp-Integer-1 += 5 + Tmp-Integer-1 += 6 + Tmp-Integer-1 += 7 + Tmp-Integer-1 += 8 + Tmp-Integer-1 += 9 + +} + +# +# Loop 0..9 +# +foreach &Tmp-Integer-1 { + redundant-load-balance { + group { + # fail on even numbered values, succeed on odd numbered ones + if ("%{expr:%{Foreach-Variable-0} %% 2}" == 0) { + fail + } + else { + update request { + Tmp-Integer-0 := "%{expr:%{Tmp-Integer-0} + 1}" + Filter-Id += "SUCCEED ODD %{Foreach-Variable-0} %{Tmp-Integer-0}" + } + ok + } + } + group { + # succeed on even-numbered values, fail on off-numbered ones. + if ("%{expr:%{Foreach-Variable-0} %% 2}" == 1) { + fail + } + else { + update request { + Tmp-Integer-0 := "%{expr:%{Tmp-Integer-0} + 1}" + Filter-Id += "SUCCEED EVEN %{Foreach-Variable-0} %{Tmp-Integer-0}" + } + ok + } + } + } +} + +if (&Tmp-Integer-0 != "%{Tmp-Integer-1[#]}") { + update reply { + Filter-Id += "fail 2 (passed %{Tmp-Integer-0}/%{Tmp-Integer-1[#]})" + } +} +else { + update reply { + Filter-Id := 'filter' + } +} diff --git a/src/tests/keywords/redundant-redundant b/src/tests/keywords/redundant-redundant new file mode 100644 index 0000000..294f53e --- /dev/null +++ b/src/tests/keywords/redundant-redundant @@ -0,0 +1,73 @@ +# PRE: update if foreach redundant redundant-load-balance +# +# Nested redundant blocks. +# +# +update request { + Tmp-Integer-2 := 0 + Tmp-Integer-3 := 0 + Tmp-Integer-4 := 0 + Tmp-Integer-5 := 0 +} + +redundant { + redundant-load-balance { + group { + update request { + Tmp-Integer-2 := "%{expr:&Tmp-Integer-2 + 1}" + } + fail + } + group { + update request { + Tmp-Integer-3 := "%{expr:&Tmp-Integer-3 + 1}" + } + fail + } + group { + update request { + Tmp-Integer-4 := "%{expr:&Tmp-Integer-4 + 1}" + } + fail + } + group { + update request { + Tmp-Integer-5 := "%{expr:&Tmp-Integer-5 + 1}" + } + fail + } + } + ok +} + +if (&Tmp-Integer-2 != 1) { + update reply { + Filter-Id += 'Fail 3a' + } + return +} + +if (&Tmp-Integer-3 != 1) { + update reply { + Filter-Id += 'Fail 3b' + } + return +} + +if (&Tmp-Integer-4 != 1) { + update reply { + Filter-Id += 'Fail 3c' + } + return +} + +if (&Tmp-Integer-5 != 1) { + update reply { + Filter-Id += 'Fail 3d' + } + return +} + +update reply { + Filter-Id := "filter" +}
\ No newline at end of file diff --git a/src/tests/keywords/regex-escape b/src/tests/keywords/regex-escape new file mode 100644 index 0000000..4ab1e5b --- /dev/null +++ b/src/tests/keywords/regex-escape @@ -0,0 +1,29 @@ +# +# PRE: update if +# + +# +# Strings which are expanded in a regex have regex special +# characters escaped. Because the input strings are unsafe. +# +update request { + Tmp-String-0 := "example.com" + Tmp-String-1 := "exampleXcom" +} + +if ("exampleXcom" =~ /%{Tmp-String-0}/) { + update reply { + Filter-Id := "fail 1" + } +} + +elsif (&Tmp-String-1 =~ /%{Tmp-String-0}/) { + update reply { + Filter-Id := "fail 2" + } +} +else { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/regex-lhs b/src/tests/keywords/regex-lhs new file mode 100644 index 0000000..91b0b20 --- /dev/null +++ b/src/tests/keywords/regex-lhs @@ -0,0 +1,27 @@ +# +# PRE: update if regex-escape +# + +# +# Strings which are expanded in a regex have regex special +# characters escaped. Because the input strings are unsafe. +# +update request { + Tmp-String-0 := "example.com" + Tmp-String-1 := "^foo$bar" +} + +if (&Tmp-String-0 !~ /example\.com$/) { + update reply { + Filter-Id := "fail 1" + } +} +elsif (&Tmp-String-1 !~ /\^foo\$bar/) { + update reply { + Filter-Id := "fail 1" + } +} else { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/return b/src/tests/keywords/return new file mode 100644 index 0000000..49779a5 --- /dev/null +++ b/src/tests/keywords/return @@ -0,0 +1,33 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +if (User-Name == "bob") { + update reply { + Filter-Id := "filter" + } + + # + # We need this because the "return" below + # will prevent the "pap" module from being run + # in the "authorize" section. + # + update control { + Auth-Type := PAP + } + + # + # Stop processing "authorize", and go to the next section. + # + return + + # + # Shouldn't reach this + # + update reply { + Filter-Id := "fail" + } +} diff --git a/src/tests/keywords/return-group b/src/tests/keywords/return-group new file mode 100644 index 0000000..92978e4 --- /dev/null +++ b/src/tests/keywords/return-group @@ -0,0 +1,22 @@ +# PRE: return +# +update { + control:Auth-Type = 'Accept' +} + +group { + # Section should exit after this statement + ok { + ok = return + } + + # This entry should never be reached + update { + reply:Reply-Message := 'fail' + } +} + +# We should continue processing after the previous group. +update { + reply:Reply-Message += 'pass' +} diff --git a/src/tests/keywords/return-group.attrs b/src/tests/keywords/return-group.attrs new file mode 100644 index 0000000..ad30f4d --- /dev/null +++ b/src/tests/keywords/return-group.attrs @@ -0,0 +1,4 @@ +User-Name = 'test' + +Response-Packet-Type == Access-Accept +Reply-Message == 'pass' diff --git a/src/tests/keywords/return-section b/src/tests/keywords/return-section new file mode 100644 index 0000000..21ecea1 --- /dev/null +++ b/src/tests/keywords/return-section @@ -0,0 +1,35 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +if (User-Name == "bob") { + update reply { + Filter-Id := "filter" + } + + # + # We need this because the "return" below + # will prevent the "pap" module from being run + # in the "authorize" section. + # + update control { + Auth-Type := PAP + } + + # + # Stop processing "authorize", and go to the next section. + # + return { # ERROR + ok = 1 + } + + # + # Shouldn't reach this + # + update reply { + Filter-Id := "fail" + } +} diff --git a/src/tests/keywords/sha1 b/src/tests/keywords/sha1 new file mode 100644 index 0000000..0d577a9 --- /dev/null +++ b/src/tests/keywords/sha1 @@ -0,0 +1,60 @@ +# +# PRE: update if +# +update { + control:Cleartext-Password := 'hello' + request:Tmp-String-0 := "This is a string\n" + request:Tmp-Octets-0 := 0x000504030201 + request:Tmp-String-1 := "what do ya want for nothing?" + request:Tmp-String-2 := "Jefe" +} + +update reply { + Filter-Id := 'filter' +} + +# +# Put "This is a string" into a file and call "sha1sum" on it. +# You should get this string. +# +if ("%{sha1:This is a string\n}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') { + update reply { + Filter-Id += 'fail 1' + } +} + +if ("%{sha1:&Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{sha1:&request:Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{sha1:%{Tmp-String-0}}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') { + update reply { + Filter-Id += 'fail 4' + } +} + +# +# SHA1 should also be able to cope with references to octet attributes +# +if ("%{sha1:&request:Tmp-Octets-0}" != '365b244645fe7294dff062174996572319d5a82c') { + update reply { + Filter-Id += 'fail 5' + } +} + +# +# SHA1 HMAC with attribute references +# +if ("%{hmacsha1:&Tmp-String-1 &Tmp-String-2}" != 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79') { + update reply { + Filter-Id += 'fail 6' + } +} diff --git a/src/tests/keywords/sha2 b/src/tests/keywords/sha2 new file mode 100644 index 0000000..89c54f4 --- /dev/null +++ b/src/tests/keywords/sha2 @@ -0,0 +1,81 @@ +# +# PRE: update if +# +if ("$ENV{OPENSSL_LIBS}" != "") { + +update { + control:Cleartext-Password := 'hello' + request:Tmp-String-0 := "This is a string\n" + request:Tmp-Octets-0 := 0x000504030201 +} + +update reply { + Filter-Id := 'filter' +} + +# +# Put "This is a string" into a file and call "sha256sum" on it. +# You should get this string. +# +if ("%{sha256:This is a string\n}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') { + update reply { + Filter-Id += 'fail 1' + } +} + +if ("%{sha256:&Tmp-String-0}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') { + update reply { + Filter-Id += 'fail 2' + } +} + +if ("%{sha256:&request:Tmp-String-0}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') { + update reply { + Filter-Id += 'fail 3' + } +} + +if ("%{sha256:%{Tmp-String-0}}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') { + update reply { + Filter-Id += 'fail 4' + } +} + +# +# SHA256 should also be able to cope with references to octet attributes +# +if ("%{sha256:&request:Tmp-Octets-0}" != 'f307e202b881fded70e58017aa0c4d7b29c76ab25d02bf078301a5f6635187eb') { + update reply { + Filter-Id += 'fail 5' + } +} + +# +# SHA512 and SHA256 share common code paths, so the tests don't need to be +# as exhaustive. +# +if ("%{sha512:This is a string\n}" != '56b57df5cce42d4e35c644649798ea23ec16f4f4626e78faf4d2d8f430ea349bcc28cd5532457c82f0aa66bf68988346039fe75b900a92ff94fd53993d45990f') { + update reply { + Filter-Id += 'fail 6' + } +} + +if ("%{sha512:&Tmp-String-0}" != '56b57df5cce42d4e35c644649798ea23ec16f4f4626e78faf4d2d8f430ea349bcc28cd5532457c82f0aa66bf68988346039fe75b900a92ff94fd53993d45990f') { + update reply { + Filter-Id += 'fail 7' + } +} + +if ("%{sha512:&request:Tmp-Octets-0}" != 'de80271eb5e03a1c24dd0cd823a22305a743ee3a54f1de5bf97adbf56984561154bfb6928b1da4ccc3f5dde9f4032ad461937b60b9ace4ad3898cf45c90596d7') { + update reply { + Filter-Id += 'fail 8' + } +} + +} + +else { # no OPENSSL. Force the test to pass + update reply { + Filter-Id := 'filter' + } +} diff --git a/src/tests/keywords/smash b/src/tests/keywords/smash new file mode 100644 index 0000000..fd19d3f --- /dev/null +++ b/src/tests/keywords/smash @@ -0,0 +1,6 @@ +# +# PRE: update +# +# This gives the game away. +# +update { control:Cleartext-Password := 'hello', reply:Filter-Id := "filter" } diff --git a/src/tests/keywords/string b/src/tests/keywords/string new file mode 100644 index 0000000..bcf0fcf --- /dev/null +++ b/src/tests/keywords/string @@ -0,0 +1,19 @@ +# +# PRE: cmp +# +update control { + Cleartext-Password := 'hello' +} + +update request { + Tmp-String-0 := "this\000is\000a\000string" +} + +# +# %{string:...} is explicitly not binary safe +# +if ("%{string:Tmp-String-0}" == "this") { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/substring b/src/tests/keywords/substring new file mode 100644 index 0000000..0ce4e9d --- /dev/null +++ b/src/tests/keywords/substring @@ -0,0 +1,418 @@ +# +# PRE: update +# +# Check substring xlat works correctly +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tmp-String-0 := 'foo bar' + Tmp-Integer-0 := 54786512 + Tmp-IP-Address-0 := 192.168.56.34 + Tmp-Cast-Ethernet := 01:23:45:67:89:ab +} + +update request { + Tmp-String-1 := "%{substring:&Tmp-String-0 2 3}" + Tmp-String-2 := "%{substring:&Tmp-String-0 -3 2}" + Tmp-String-3 := "%{substring:&Tmp-String-0 1 -3}" + Tmp-String-4 := "%{substring:&Tmp-String-0 -4 -1}" + Tmp-String-5 := "%{substring:&Tmp-String-0 8 5}" + Tmp-String-6 := "%{substring:&Tmp-String-0 4 -10}" + Tmp-String-7 := "%{substring:&Tmp-String-0 0 7}" + Tmp-String-8 := "%{substring:&Tmp-String-0 3 0}" + Tmp-String-9 := "%{substring:&Tmp-String-0 4 8}" +} + +if (Tmp-String-1 != 'o b') { + update reply { + Filter-Id += 'fail 0.1' + } +} + +if (Tmp-String-2 != 'ba') { + update reply { + Filter-Id += 'fail 0.2' + } +} + +if (Tmp-String-3 != 'oo ') { + update reply { + Filter-Id += 'fail 0.3' + } +} + +if (Tmp-String-4 != ' ba') { + update reply { + Filter-Id += 'fail 0.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 0.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 0.6' + } +} + +if (Tmp-String-7 != 'foo bar') { + update reply { + Filter-Id += 'fail 0.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 0.8' + } +} + +if (Tmp-String-9 != 'bar') { + update reply { + Filter-Id += 'fail 0.9' + } +} + +update request { + Tmp-String-0 := ' foo bar ' +} + +update request { + Tmp-String-1 := "%{substring: &Tmp-String-0 2 3}" + Tmp-String-2 := "%{substring:&Tmp-String-0 -3 2}" + Tmp-String-3 := "%{substring:&Tmp-String-0 1 -3}" + Tmp-String-4 := "%{substring:&Tmp-String-0 -4 -1}" + Tmp-String-5 := "%{substring:&Tmp-String-0 10 5}" + Tmp-String-6 := "%{substring:&Tmp-String-0 4 -10}" + Tmp-String-7 := "%{substring:&Tmp-String-0 0 9}" + Tmp-String-8 := "%{substring:&Tmp-String-0 3 0}" + Tmp-String-9 := "%{substring:&Tmp-String-0 4 10}" +} + +if (Tmp-String-1 != 'oo ') { + update reply { + Filter-Id += 'fail 1.1' + } +} + +if (Tmp-String-2 != 'ar') { + update reply { + Filter-Id += 'fail 1.2' + } +} + +if (Tmp-String-3 != 'foo b') { + update reply { + Filter-Id += 'fail 1.3' + } +} + +if (Tmp-String-4 != 'bar') { + update reply { + Filter-Id += 'fail 1.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 1.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 1.6' + } +} + +if (Tmp-String-7 != ' foo bar ') { + update reply { + Filter-Id += 'fail 1.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 1.8' + } +} + +if (Tmp-String-9 != ' bar ') { + update reply { + Filter-Id += 'fail 1.9' + } +} + +update request { + Tmp-String-1 := "%{substring:&Tmp-Integer-0 2 3}" + Tmp-String-2 := "%{substring:&Tmp-Integer-0 -3 2}" + Tmp-String-3 := "%{substring:&Tmp-Integer-0 1 -3}" + Tmp-String-4 := "%{substring:&Tmp-Integer-0 -4 -1}" + Tmp-String-5 := "%{substring:&Tmp-Integer-0 8 5}" + Tmp-String-6 := "%{substring:&Tmp-Integer-0 4 -10}" + Tmp-String-7 := "%{substring:&Tmp-Integer-0 0 8}" + Tmp-String-8 := "%{substring:&Tmp-Integer-0 5 0}" + Tmp-String-9 := "%{substring:&Tmp-Integer-0 4 10}" +} + +if (Tmp-String-1 != '786') { + update reply { + Filter-Id += 'fail 2.1' + } +} + +if (Tmp-String-2 != '51') { + update reply { + Filter-Id += 'fail 2.2' + } +} + +if (Tmp-String-3 != '4786') { + update reply { + Filter-Id += 'fail 2.3' + } +} + +if (Tmp-String-4 != '651') { + update reply { + Filter-Id += 'fail 2.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 2.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 2.6' + } +} + +if (Tmp-String-7 != '54786512') { + update reply { + Filter-Id += 'fail 2.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 2.8' + } +} + +if (Tmp-String-9 != '6512') { + update reply { + Filter-Id += 'fail 2.9' + } +} + +update request { + Tmp-String-1 := "%{substring:&Tmp-IP-Address-0 2 3}" + Tmp-String-2 := "%{substring:&Tmp-IP-Address-0 -3 2}" + Tmp-String-3 := "%{substring:&Tmp-IP-Address-0 1 -3}" + Tmp-String-4 := "%{substring:&Tmp-IP-Address-0 -4 -1}" + Tmp-String-5 := "%{substring:&Tmp-IP-Address-0 15 5}" + Tmp-String-6 := "%{substring:&Tmp-IP-Address-0 4 -20}" + Tmp-String-7 := "%{substring:&Tmp-IP-Address-0 0 13}" + Tmp-String-8 := "%{substring:&Tmp-IP-Address-0 6 0}" + Tmp-String-9 := "%{substring:&Tmp-IP-Address-0 8 12}" +} + +if (Tmp-String-1 != '2.1') { + update reply { + Filter-Id += 'fail 3.1' + } +} + +if (Tmp-String-2 != '.3') { + update reply { + Filter-Id += 'fail 3.2' + } +} + +if (Tmp-String-3 != '92.168.56') { + update reply { + Filter-Id += 'fail 3.3' + } +} + +if (Tmp-String-4 != '6.3') { + update reply { + Filter-Id += 'fail 3.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 3.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 3.6' + } +} + +if (Tmp-String-7 != '192.168.56.34') { + update reply { + Filter-Id += 'fail 3.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 3.8' + } +} + +if (Tmp-String-9 != '56.34') { + update reply { + Filter-Id += 'fail 3.9' + } +} + +update request { + Tmp-String-1 := "%{substring:&Tmp-Cast-Ethernet 2 3}" + Tmp-String-2 := "%{substring:&Tmp-Cast-Ethernet -3 2}" + Tmp-String-3 := "%{substring:&Tmp-Cast-Ethernet 1 -3}" + Tmp-String-4 := "%{substring:&Tmp-Cast-Ethernet -4 -1}" + Tmp-String-5 := "%{substring:&Tmp-Cast-Ethernet 20 5}" + Tmp-String-6 := "%{substring:&Tmp-Cast-Ethernet 4 -20}" + Tmp-String-7 := "%{substring:&Tmp-Cast-Ethernet 0 17}" + Tmp-String-8 := "%{substring:&Tmp-Cast-Ethernet 8 0}" + Tmp-String-9 := "%{substring:&Tmp-Cast-Ethernet 9 12}" +} + +if (Tmp-String-1 != ':23') { + update reply { + Filter-Id += 'fail 4.1' + } +} + +if (Tmp-String-2 != ':a') { + update reply { + Filter-Id += 'fail 4.2' + } +} + +if (Tmp-String-3 != '1:23:45:67:89') { + update reply { + Filter-Id += 'fail 4.3' + } +} + +if (Tmp-String-4 != '9:a') { + update reply { + Filter-Id += 'fail 4.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 4.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 4.6' + } +} + +if (Tmp-String-7 != '01:23:45:67:89:ab') { + update reply { + Filter-Id += 'fail 4.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 4.8' + } +} + +if (Tmp-String-9 != '67:89:ab') { + update reply { + Filter-Id += 'fail 4.9' + } +} + +update request { + Tmp-String-1 := "%{substring:foo bar 2 3}" + Tmp-String-2 := "%{substring:foo bar -3 2}" + Tmp-String-3 := "%{substring:foo bar 1 -3}" + Tmp-String-4 := "%{substring:foo bar -4 -1}" + Tmp-String-5 := "%{substring:foo bar 8 5}" + Tmp-String-6 := "%{substring:foo bar 4 -10}" + Tmp-String-7 := "%{substring: foo bar 0 9}" + Tmp-String-8 := "%{substring: foo bar 5 0}" + Tmp-String-9 := "%{substring: foo bar 4 10}" +} + +debug_request + +if (Tmp-String-1 != 'o b') { + update reply { + Filter-Id += 'fail 5.1' + } +} + +if (Tmp-String-2 != 'ba') { + update reply { + Filter-Id += 'fail 5.2' + } +} + +if (Tmp-String-3 != 'oo ') { + update reply { + Filter-Id += 'fail 5.3' + } +} + +if (Tmp-String-4 != ' ba') { + update reply { + Filter-Id += 'fail 5.4' + } +} + +if (Tmp-String-5 != '') { + update reply { + Filter-Id += 'fail 5.5' + } +} + +if (Tmp-String-6 != '') { + update reply { + Filter-Id += 'fail 5.6' + } +} + +if (Tmp-String-7 != ' foo bar ') { + update reply { + Filter-Id += 'fail 5.7' + } +} + +if (Tmp-String-8 != '') { + update reply { + Filter-Id += 'fail 5.8' + } +} + +if (Tmp-String-9 != ' bar ') { + update reply { + Filter-Id += 'fail 5.9' + } +} diff --git a/src/tests/keywords/switch b/src/tests/keywords/switch new file mode 100644 index 0000000..f64aeaf --- /dev/null +++ b/src/tests/keywords/switch @@ -0,0 +1,19 @@ +switch &User-Name { + case "bob" { + update reply { + Filter-Id := "filter" + } + } + + case "doug" { + update reply { + Filter-Id := "doug" + } + } + + case { + update reply { + Filter-Id := "default" + } + } +} diff --git a/src/tests/keywords/switch-attr-cast b/src/tests/keywords/switch-attr-cast new file mode 100644 index 0000000..e271a18 --- /dev/null +++ b/src/tests/keywords/switch-attr-cast @@ -0,0 +1,34 @@ +# +# PRE: switch switch-attr-cmp +# + +update request { + Service-Type := Login-User + Filter-Id := "Login-User" +} + +switch &Service-Type { + case "%{expr: 1 + 2}" { + update reply { + Filter-Id := "3" + } + } + + # + # The Filter-Id will get printed to a string, + # have the string parsed as a Service-Type attr, + # and then that compared to the input Service-Type + # + case &Filter-Id { + update reply { + Filter-Id := "filter" + } + } + + case { + update reply { + Filter-Id := "default" + } + } + +} diff --git a/src/tests/keywords/switch-attr-cmp b/src/tests/keywords/switch-attr-cmp new file mode 100644 index 0000000..e28ded8 --- /dev/null +++ b/src/tests/keywords/switch-attr-cmp @@ -0,0 +1,36 @@ +# +# PRE: switch +# +update request { + Tmp-String-0 := &User-Name +} + +# +# A switch statement where we compare two attributes +# +switch &User-Name { + case &Tmp-String-0 { + update reply { + Filter-Id := "filter" + } + } + + case "bob" { + update reply { + Filter-Id := "failed 0" + } + } + + case "doug" { + update reply { + Filter-Id := "failed 1" + } + } + + case { + update reply { + Filter-Id := "failed 2" + } + } + +} diff --git a/src/tests/keywords/switch-default b/src/tests/keywords/switch-default new file mode 100644 index 0000000..6115ed9 --- /dev/null +++ b/src/tests/keywords/switch-default @@ -0,0 +1,22 @@ +# PRE: switch +# +switch User-Name { + case "harry" { + update reply { + Filter-Id := "harry" + } + } + + case "doug" { + update reply { + Filter-Id := "doug" + } + } + + case { + update reply { + Filter-Id := "filter" + } + } + +}
\ No newline at end of file diff --git a/src/tests/keywords/switch-escape b/src/tests/keywords/switch-escape new file mode 100644 index 0000000..50d9fdf --- /dev/null +++ b/src/tests/keywords/switch-escape @@ -0,0 +1,43 @@ +update request { + &Tmp-String-0 := 'foo' +} + +switch "%{tolower:%{request:Tmp-String-0}}" { + case 'foo' { + update reply { + Filter-Id := "filter" + } + } + + case '' { + update reply { + Filter-Id += "fail-empty-1" + } + } + + case { + update reply { + Filter-Id += "fail-default-1" + } + } +} + +switch "%{request:Tmp-String-0}" { + case 'foo' { + update reply { + Filter-Id := "filter" + } + } + + case '' { + update reply { + Filter-Id += "fail-empty-2" + } + } + + case { + update reply { + Filter-Id += "fail-default-2" + } + } +} diff --git a/src/tests/keywords/switch-nodefault b/src/tests/keywords/switch-nodefault new file mode 100644 index 0000000..5fb9469 --- /dev/null +++ b/src/tests/keywords/switch-nodefault @@ -0,0 +1,22 @@ +# +# User-Name is "bob", and a switch statement +# with no "default" should not crash the server. +# +switch &User-Name { + case "doug" { + update reply { + Filter-Id := "doug" + } + } +} + +if (&reply:Filter-Id) { + update reply { + Filter-Id := "fail 1" + } +} +else { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/switch-value-error b/src/tests/keywords/switch-value-error new file mode 100644 index 0000000..18db9e1 --- /dev/null +++ b/src/tests/keywords/switch-value-error @@ -0,0 +1,29 @@ +# +# PRE: switch +# +switch &Service-Type { + case "%{expr: 1 + 2}" { + update reply { + Filter-Id := "3" + } + } + + case Login-User { + update reply { + Filter-Id := "Login-User" + } + } + + case No-Such-Value { # ERROR + update reply { + Filter-Id := "FAILED" + } + } + + case { + update reply { + Filter-Id := "default" + } + } + +} diff --git a/src/tests/keywords/switch-value-error2 b/src/tests/keywords/switch-value-error2 new file mode 100644 index 0000000..a291e7b --- /dev/null +++ b/src/tests/keywords/switch-value-error2 @@ -0,0 +1,27 @@ +# +# PRE: switch-value-error +# +# The same as "switch-value-error", but the attribute +# is hidden inside of an xlat expansion. We now turn +# simple attribute xlats into templates. +# +switch "%{Service-Type}" { # ERROR + case "%{expr: 1 + 2}" { + update reply { + Filter-Id := "3" + } + } + + case Login-User { + update reply { + Filter-Id := "Login-User" + } + } + + case { + update reply { + Filter-Id := "default" + } + } + +} diff --git a/src/tests/keywords/switch-virtual b/src/tests/keywords/switch-virtual new file mode 100644 index 0000000..659604d --- /dev/null +++ b/src/tests/keywords/switch-virtual @@ -0,0 +1,23 @@ +# +# PRE: update switch +# +update control { + Cleartext-Password := 'hello' +} + +# +# Virtual attribute references get mashed to xlats +# +switch &Packet-Type { + case Access-Request { + update reply { + Filter-Id := "filter" + } + } + + case { + update reply { + Filter-Id := "fail" + } + } +}
\ No newline at end of file diff --git a/src/tests/keywords/switch-xlat-error b/src/tests/keywords/switch-xlat-error new file mode 100644 index 0000000..d03f6d3 --- /dev/null +++ b/src/tests/keywords/switch-xlat-error @@ -0,0 +1,17 @@ +# +# PRE: switch +# +switch &User-Name { + case "%{no-such-module:bob}" { # ERROR + update reply { + Filter-Id := "fail" + } + } + + case { + update reply { + Filter-Id := "default" + } + } + +} diff --git a/src/tests/keywords/truncation b/src/tests/keywords/truncation new file mode 100644 index 0000000..8217e36 --- /dev/null +++ b/src/tests/keywords/truncation @@ -0,0 +1,109 @@ +# cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c <n> + +update reply { + Filter-Id := "filter" +} + +# 8192 - 0x (2) - '' (2) there are unlikely to be any static buffers this big outside of the conffile parser +update request { + Tmp-Octets-0 := '0x\ +d8abccb7834711af1de1812be2579febe946f5d7beef6fa5c7074c0cb917e9b91e23e14b016f27610097c16c0e0fad88176e077b24198c770746159\ +05b8810d1c8b774d98889fa5c6027cde5e9c56dd4f7c48298c7713aeca5ba5dcfd506032ad05b1396f50e825b633d5a6af0dce6181b640287e03a65\ +8734df46a86341556f28455b3f377313a5a2ac8c8267b8a5de559b95f9b493a68b9e0278485f9e3914d702b2b7b90ee85ff393461f197386d09b836\ +5a8ae85ea025aea095f5d834c2ddf21e9a16b945da03c97f8a52687f19c605af9a245878b4bc5ed15b7937371ad629081159cf7de613d02c43f5cab\ +3529abbe61a6da1e55c685c2310c8eccb452f9758bf63fddfa58cdffb5cbf912f90e628310978dd1b3b7c2d3a08dd7ca6ca51081a114b013cc9d4c1\ +9f5ce0b1af81166c9402c105019a0fa9d15996c4f3d5fa35226bf88166598ff4f619322866df276d8b92fad06d120470c29217d29abb96ff861751e\ +acebbe839e28287c8cd485769d9a09e5bab524bde5776e15037d19503a2d032658e21aec7090032707ba0d662fcfa99e5e5ec183f8c63c6022bf281\ +22cf090fb80263f70c523aa0e00dd9f610c5b669754092253edd6f83b57bdd7a251514cdd99ab86cfa98b11469d1d1118aba0a149db6f0ccceb2f17\ +06bf33f657c7b8547dc284dfc10fdc1678c184901aa17f756d18df43409e1c4ead239cb78c46a9c412ccb097bba2244bb680bb249c15004d796731f\ +3890c1f94d8e0eda81487da9a65f817b5dbac4e639fce063f61a7db87d8b9d4b855120f56c3a0d57cb61ced57092506da881337280a18b2729717f5\ +133978362ca7941f9dbe9462ae5a46886c4d3c51b633278d6ad58a331c95e481cbbae866f046cb1a59ae2545fa32b12d4772244fd24bb91f7611d0c\ +beffbc5dee309c671fb5a171eacf45b20641e1cb4db306965eb43e98cb900ee805065c7adf4db3cdb97bbf8116dbb29f5ac07c7041a5f02d1a4fd1f\ +a7be6da388146d6b80ae8e152ac873cbf1f88c98cdd2e1de02df76461a1c50139f558f43ac1a91824fc48ea35976fab29d412f1ad990e528893ad4c\ +b6045b63d2a7af0657f1c348302e674ef0b39bd689b3b33dd3664c3e2386d0ea3241e70d2a2990b6243638d68033cc6040758e11f82ef783db2ae94\ +d2a8dbccd25fe2dce554837a2ca424199e4bd5442215623a77bffd603b575c847f88763fff1d6d0ff314851b1af06f99fb44d0a3115ad4389462452\ +c2331be24d486f6b5f2224cdfe44909e0a3bec47cdf6fe49fa60518e37950f986677e7f45617adcb20bc02990d3fe60de54ad2d1950bdd423e752c4\ +60f4eeaf2eeece07f3dea26db63cc0bc222a6f3228131cdb3360b9ec7e8f861c3e0bc822f9da00fef17f44ab4de7c166e5cb07d5c671f0364d4bccb\ +dc7f24868e066998bc8b3ab9052171b967162d536082b0a11c24f263c0e5f656b8349f970124af712cf098e8b08910aefcf7d42dd8a74e84ae6f9c4\ +006aa09483715b514833291c9d2e2bcdda567a16a493ac4fa17d5dff7a04b9f7f967febe7a8bdf911b1b668716be73fbf6f2416609224494dca1a18\ +1c1c7bb298671d39bdc1f664c833c9d2d4c08a65866db885929611fcd0c33139bc2959a865cbb686694896b539daa1db265ba78298cb3a09a331ef0\ +1423a7fd04b38ccdfbf1182a493c7a53c720a2dcf486249ff2b674341e77c2c0d5eb78efc07f295d562a77667457df58ea419c488680b9ce51aac01\ +4cd78984f70f29aec2a9b77fd45d38e0b07257b71f2146ccb11cd07681cbfff787b39b7ce9c42ce60513eccec2b490c68378e86bd441735b30410be\ +8710e71b2a326bd8b929abde80f25a0adb312f8ed5bb748866ddd8fb5fa81855d194afbdb1d511be453e21ab3f6481e47fa86b0697342706f85b8e8\ +84c6bf427390ef4c62532797de25004bb9d293e196ed29950e14682c68a8b6ab7c03256bc493b61eb3e44dc16726fcedb6c6776e2e9e0b3450fe6c6\ +24eef42e4806ef7c8abc367b7f1c83b7955ff6579d03362b428ca7711d228cf88402b60fd162ef4819216b9db66b4a1b0a5e0651a7901290eafa3d5\ +c698e40f73b3d66345d9e328d50ef2c767d370879890980d0d989de122ab22d7ef9979832b022e676eba4630d49434ec70025b44cb7146f9688d102\ +ceb55e6ca8ee9b9a66327da80f46367f452de7e2a84bf702b155539c7fa488d86c83faf3f150fda6d75eb476e2c31d73cea88148d9f02d178b0c098\ +5b4061830231ca405fbf41547ed9c036f53fc706b0fd1bb084e84a9e6dec81b4b4eea46c5ea26e99993ee930ccfeff4a185ffbce8a937243fa4734b\ +c24abbf465b0cd1f87686d44762140b5dfe57ae904cd8ed121f055d8a1bbf9c8a977133215f135e563b935451a7f797e72ef8f96390bc2477ad7aac\ +d2679557bac3b9a030926ff0801cc692dd641cf4f8a7bd9bdee21cdab3f7fbcf7d1d0de51101c5d816b19db49109958a8e75662ac700653112cd86c\ +10cbf45d3ea16f861de2d46714dfba8a34c1ad7ed4d5484802270187c3c7f70f7a51858479d8504e3b6267194404011b416329401ec55940f890f33\ +97d4bb3eab46990a4a03cdae9951ccd0bd42584ec88262f7d80193f09843c1fca7627b9cf708c646e84883b19fe3a8987912d04f1c0d1c779b740a7\ +fea0270374416a3987dd91d706e22e8c9698ef870d7f80d1c3042914a34736c185e542976d0145531c8eb20c3a6c5a5f747e97228ff55425fd93222\ +2d802ca36ac61555f69be4d645532579ddadb24a821f0bab1fb1f3b724ed4759b5303fe0015626ad00879e1a363d5e8b3aa0b7f3e572df3dc5f7352\ +7eb30b78e70dbe6ca81052315925d3892485168dd6f900289d1a0f9285e52988480bfda6a2c8b28cbd175b4dec735240c8f79afd5c93ed058da5551\ +8f20fb09edf4b4112c352604876f4083b68e534874d0b9ce2f977be2bcea0f9c123e119a70d269bc26ece608c39c98dbb069118148b01e403c16076\ +330b5d7929a7a7d724a7030d514ff0e38bee78e0044ffa508fc9793d7fc59075fd825e68d34e58568bffd8b20791862a8d0686763e49f1e3ed8a728\ +e33bb32a57c6374d153f93ba3cd283ce0af9a0a3b0ced4f83e3d604ff66f6584a35b51938ff4a0282c51f6f8561bd77b842ed0fd39fc43be825652f\ +f2c021f2b4d2b8c8ed2cf8eeb16f86863e59b374eda5a17e1bfc0b5a54169dd3e62b84a81bc1afd5cc3a1b193484428bcc3afb5344da990697f6787\ +5f72bc1ab04f52f75bc671b8d1239fc811d44032822abc95f8ea3465cdabdce5f83d5895d50fcbd97ad88e6b193172a4cf5f51814d9348498b1736e\ +de25f8b5afc8556d3b9573cb2ae8f51e8f0bca2f048d883592c9ab317e87864d48ca901e2aa2774360c77ba3f0a76c21f10155e32e91a2239d00e6b\ +0279bbc8977fadbea04b7b67575437fd5f6611a8889ecbde5fe45fcab8db9b62e831aadcd0ade54a94d30b6963d727979c116ef9f4a9a16a0e7a76e\ +d4f1177ad0ac7022beb12581629a7fd36c8ca5eeaa40d1a0d88cb0f701b288baae6c00a74c0b0dc3ffa6e9710bf3264684c1b9ff220710b2348cc93\ +d658d9eddae36232a0d0e06439d5dafdc7271eafbb3d16a17e36de7470c7a5ed034d735e2c216073ba6b4be7b319718d5afbd634684f5468d26afd0\ +63edfbc7fe6c8e98914eff397fcfdf44a10b84ba90b30d1a5da6823aab1792d666b32bdd0516ce3590c5dc2b49a6eabdb28cdc72dbc24e60ba0883f\ +953f0a99d29c1089b06063588b8bd965ba42e27f618d2b046fb3db93161981bbcd67fc965b0084ac25993445732b52d18cd2868e6b545214a2d501a\ +0002d2043d9a9790905dde296f16517e295dd295a97991c205d50dba331e86f06b0195e52fdd21f8e65e6b00fe7165e1a951805e5947aaee125f180\ +a2127375d8898aba1af3a3db34470e78bed99917a6bb87725788c1e2c8f35d6022d51bafdc3e69673cbdd80dc9cfde7dc1bd06e3a6437c720cb7b6c\ +2b3bc808111593d5f2a88c6e5b499c51155f4ff0267606abcbfa03dc5e4c108e7f3d33989952df2f4f1476df8fe53eaa39d2338a87b6ccc91587a67\ +bc0da2f5667248ce969b91f1521c5bfda97c8f062b62d768a9d302f4a4bd0e67646c6666a9fea93749a65d06fdcd0168fe81edac542fc287503a592\ +61591a686b418ac2602e2464f6679ef7ecf5387af45e5fdd72224695e454d6676687b03a5858afe71dda7180a59ef2e1d8c81c4a9c8375e62c43711\ +cbd0e7d437a5913bc4d7e8fbb71f68e4805ad41bd624c06e84761dbda3014c1576a866c073ca32b37007884730ac9f831b95db8ce10f0db45e009d0\ +6e1ad8c96c340c98debee0dd0ca9e6694fe7a69306c15e6a7054cfd612e070f538c6ff0c1a34b257df10087b650da852a5b663a5795a335193dfe64\ +1950ea614ae05b0c202cedabd1ebe6da561e167fe81dd9fc190740a20db38f5356e61f8bed7b3db783a6b53e48f408a8b08f9f79738438a3ed0c4bc\ +f444d5922c76ff03c943a3c72c513047aa75caf51381b5c306fdbaedcd4001a2df456c3afef7559241f553dffac2dfd9ac94570e50c571329203bf0\ +38cbdac9bc76f896635356c402f2f5c105eb4e334f7792716e8e8883352870e949623f29bb8fac428bc858ec3297166afe358e1947b0c2337309315\ +062a040129f978b036657e9323550765cc4c14aaa4c24572eb17a0aa940ba336fd8fd385fb46b5b0c067c59ee42922d2702feb7b43d2ffef11cbdf0\ +1f11c92ac18197a387f0633fd19509b19c0b8a2f63983f6c0a6286593c84d5866ae8d91139b141e8bdf3b7ae7df7d92754186e545f4b600fbe69494\ +3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49fa69c9a5feac5376e07f52e64be9c873afca3ebf776fe9\ +fc813bd2e58c6d0dfd951570dbff4b5e73ce547cd100ab320b6944e887d611b3425bfcdf3bfa852ff5804119a1f69e33d624eda6f9e5d1fb2811f9f\ +9f5b3224d009e09621a8fcb524f89c6fcf38f7c933aa027de2b48aa9ddbfa55f56b383f06b8feef09e4709bc4fdcbab659bab4a277a2fbebb98970e\ +44bd68a61b264c695d8ae27a676e123b5fb60c7c4cbc88f24b6554dcfebfde607500f50ec85f589eaa4213ccaa237598660a66009bc56d55455487a\ +28fa24d62e50df57feab0d1c8ec77b1085002d922c52d4a3092f8693c3b9ba9725a4d225637350812b534e93209b414b4516642eca67490199a9217\ +614322972fa2fcc5c7fc2695e9b5762d442e2c7dd8403d14228aa90df58e2ffe1a3c9922f6c5d62649664b63c017fbf3859723e7e97ea6710097683\ +6408a97de3ad7d902c7be0296fb3d476de2460602f65eb7edacc2e4f49b1652fe8948f7afb1cc9f83f3283e6013304160cd2ac7c311c492133252a6\ +5ccf45c5af9c05a47905ac8bfe55f9b912c3fca856abe7863f87392b6f6b0069e3d8412a056b1034aaac506bf2ca51760aa180c5f43a751beb06e88\ +fc6afab4c5dd4aae4a2e6f0293c15278d557c2925acba90c73eeea09ebb95f0d469aa77ae983a0e69dacd55bd8a7e78a41df5227de35af05127fa3b\ +a02f4a3ab98d75992d68a15d393387fe9ef01041569570ad6fe884764e55567311bcacfcffae76554dcfebfde607500f50ec85f589eaade607500f5\ +3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49fab' +} + +# Actual length of octet string is 4084 bytes +update request { + Tmp-Integer-0 := "%{length:Tmp-Octets-0}" +} + +if (Tmp-Integer-0 != 4084) { + update reply { + Filter-Id += 'Fail 0' + } +} + +# Octets are expanded to 8168 hexits +if ("%{Tmp-Octets-0}" !~ /^0x([0-9a-f]+)$/) { + update reply { + Filter-Id += 'Fail 1' + } +} + +update request { + Tmp-String-0 := "%{1}" +} + +if ("%{length:Tmp-String-0}" != 8168) { + update reply { + Filter-Id += 'Fail 2' + } +} + +# We can't do any more until all the xlat code uses dynamically allocated buffers diff --git a/src/tests/keywords/unknown b/src/tests/keywords/unknown new file mode 100644 index 0000000..eb96591 --- /dev/null +++ b/src/tests/keywords/unknown @@ -0,0 +1,84 @@ +# +# PRE: wimax +# +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +update request { + FreeRADIUS-Proxied-To := 127.0.0.2 +} + +# +# Check that a known but malformed attr is malformed +# +update request { + Attr-26.24757.84.9.5.7 = 0xab +} + +if (&Attr-26.24757.84.9.5.7 != 0xab) { + update reply { + Filter-Id += 'Fail 1' + } +} + +# +# Check that an unknown attr is OK +# +update request { + Attr-26.24757.84.9.5.15 = 0xabcdef +} + +if (&Attr-26.24757.84.9.5.15 != 0xabcdef) { + update reply { + Filter-Id += 'Fail 2' + } +} + +# +# Check that unknown attributes which are defined +# get automatically resolved to the real attribute. +# +if (&Vendor-11344-Attr-1 == 127.0.0.1) { + update reply { + Filter-Id += 'Fail 3' + } +} + +if (&Vendor-11344-Attr-1 != 127.0.0.2) { + update reply { + Filter-Id += 'Fail 4' + } +} + +update request { + &Vendor-11344-Attr-1 := 127.0.0.1 +} + +if (&FreeRADIUS-Proxied-To == 127.0.0.2) { + update reply { + Filter-Id += 'Fail 5' + } +} + +if (&FreeRADIUS-Proxied-To != 127.0.0.1) { + update reply { + Filter-Id += 'Fail 6' + } +} + +if (&Vendor-11344-Attr-1 == 127.0.0.2) { + update reply { + Filter-Id += 'Fail 7' + } +} + +if (&Vendor-11344-Attr-1 != 127.0.0.1) { + update reply { + Filter-Id += 'Fail 8' + } +} diff --git a/src/tests/keywords/unknown-if b/src/tests/keywords/unknown-if new file mode 100644 index 0000000..3dccbff --- /dev/null +++ b/src/tests/keywords/unknown-if @@ -0,0 +1,8 @@ +# +# PRE: unknown-update +# +if (&This-Does-Not-Exist == 1) { # ERROR + update reply { + Filter-Id := "fail" + } +} diff --git a/src/tests/keywords/unknown-name b/src/tests/keywords/unknown-name new file mode 100644 index 0000000..5a99ac9 --- /dev/null +++ b/src/tests/keywords/unknown-name @@ -0,0 +1,15 @@ +# +# PRE: update unknown +# +update control { + Cleartext-Password := 'hello' +} + +# +# This delayed binding should result in a parse error +# +if (&User-Name == &Foo-Bar-Baz) { # ERROR + update reply { + Filter-Id += "fail-2" + } +} diff --git a/src/tests/keywords/unknown-update b/src/tests/keywords/unknown-update new file mode 100644 index 0000000..1403879 --- /dev/null +++ b/src/tests/keywords/unknown-update @@ -0,0 +1,6 @@ +# +# PRE: update unknown +# +update control { + This-Does-Not-Exist = 1 # ERROR +} diff --git a/src/tests/keywords/update b/src/tests/keywords/update new file mode 100644 index 0000000..97a2557 --- /dev/null +++ b/src/tests/keywords/update @@ -0,0 +1,7 @@ +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} diff --git a/src/tests/keywords/update-add-ref-index b/src/tests/keywords/update-add-ref-index new file mode 100644 index 0000000..7f5e74a --- /dev/null +++ b/src/tests/keywords/update-add-ref-index @@ -0,0 +1,118 @@ +# +# PRE: update array +# + +update request { + reply:Filter-Id := "filter" + Class := 0x01020304 + Class += 0x05060708 + Class += 0x090a0b0c +} + +# +# Copy all the class attributes to Proxy-State +# +update request { + Proxy-State += &Class[*] +} + +if (&Proxy-State[0] != 0x01020304) { + update reply { + Filter-Id := "fail 0a" + } +} + +# Must be the same as above +if (&Proxy-State[1] != 0x05060708) { + update reply { + Filter-Id += "fail 0b" + } +} + +if (&Proxy-State[2] != 0x090a0b0c) { + update reply { + Filter-Id += "fail 0c" + } +} + +# must not exist +if (&Proxy-State[3]) { + update reply { + Filter-Id += "fail 1" + } +} + +# Remove all the Proxy-State attributes +update request { + Proxy-State !* ANY +} + +# +# Copy the first instance (implicitly) of class +# +update request { + Proxy-State += &Class +} + +if (&Proxy-State[0] != 0x01020304) { + update reply { + Filter-Id := "fail 2a" + } +} + +# Must be the same as above +if (&Proxy-State[1]) { + update reply { + Filter-Id += "fail 2b" + } +} + +# Remove all the Proxy-State attributes +update request { + Proxy-State !* ANY +} + +# +# Copy the first instance (explicitly) of class +# +update request { + Proxy-State += &Class[0] +} + +if (&Proxy-State[0] != 0x01020304) { + update reply { + Filter-Id := "fail 3a" + } +} + +# Must be the same as above +if (&Proxy-State[1]) { + update reply { + Filter-Id += "fail 3b" + } +} + +# Remove all the Proxy-State attributes +update request { + Proxy-State !* ANY +} + +# +# Copy the second instance of class +# +update request { + Proxy-State += &Class[1] +} + +if (&Proxy-State[0] != 0x05060708) { + update reply { + Filter-Id := "fail 4a" + } +} + +# Must be the same as above +if (&Proxy-State[1]) { + update reply { + Filter-Id += "fail 4b" + } +} diff --git a/src/tests/keywords/update-add-ref-tag b/src/tests/keywords/update-add-ref-tag new file mode 100644 index 0000000..39800b8 --- /dev/null +++ b/src/tests/keywords/update-add-ref-tag @@ -0,0 +1,118 @@ +# +# PRE: update array +# + +update request { + reply:Filter-Id := "filter" + request:Tunnel-Server-Endpoint += '192.0.1.1' + request:Tunnel-Server-Endpoint += '192.0.1.2' + request:Tunnel-Server-Endpoint:1 += '192.0.1.1' + request:Tunnel-Server-Endpoint:2 += '192.0.2.1' + request:Tunnel-Server-Endpoint:2 += '192.0.2.2' + request:Tunnel-Server-Endpoint:3 += '192.0.3.1' +} + +# +# Copy all Tunnel-Server-Endpoint attributes +# +update request { + control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint[*] +} + +if ((&control:Tunnel-Server-Endpoint[0] != '192.0.1.1') || \ + (&control:Tunnel-Server-Endpoint[1] != '192.0.1.2') || \ + (&control:Tunnel-Server-Endpoint[2] != '192.0.1.1') || \ + (&control:Tunnel-Server-Endpoint[3] != '192.0.2.1') || \ + (&control:Tunnel-Server-Endpoint[4] != '192.0.2.2') || \ + (&control:Tunnel-Server-Endpoint[5] != '192.0.3.1') || \ + &control:Tunnel-Server-Endpoint[6]) { + update reply { + Filter-Id := "fail 0" + } +} + +# +# Clear out control attributes... +# +update control { + Tunnel-Server-Endpoint !* ANY +} + +# +# Copy all Tunnel-Server-Endpoint attributes with tag 2 +# +update request { + control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:2[*] +} + +if ((&control:Tunnel-Server-Endpoint[0] != '192.0.2.1') || \ + (&control:Tunnel-Server-Endpoint[1] != '192.0.2.2') || \ + &control:Tunnel-Server-Endpoint[2]) { + update reply { + Filter-Id := "fail 1" + } +} + +# +# Clear out control attributes... +# +update control { + Tunnel-Server-Endpoint !* ANY +} + +# +# Copy all Tunnel-Server-Endpoint attributes with no tag +# +update request { + control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:0[*] +} + +if ((&control:Tunnel-Server-Endpoint[0] != '192.0.1.1') || \ + (&control:Tunnel-Server-Endpoint[1] != '192.0.1.2') || \ + &control:Tunnel-Server-Endpoint[2]) { + update reply { + Filter-Id := "fail 2" + } +} + +# +# Clear out control attributes... +# +update control { + Tunnel-Server-Endpoint !* ANY +} + +# +# Copy the first attribute with tag 2 (implicit) +# +update request { + control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:2 +} + +if ((&control:Tunnel-Server-Endpoint[0] != '192.0.2.1') || \ + &control:Tunnel-Server-Endpoint[1]) { + update reply { + Filter-Id := "fail 3" + } +} + +# +# Clear out control attributes... +# +update control { + Tunnel-Server-Endpoint !* ANY +} + +# +# Copy the first attribute with tag 2 (explicit) +# +update request { + control:Tunnel-Server-Endpoint += &Tunnel-Server-Endpoint:2[0] +} + +if ((&control:Tunnel-Server-Endpoint[0] != '192.0.2.1') || \ + &control:Tunnel-Server-Endpoint[1]) { + update reply { + Filter-Id := "fail 4" + } +} diff --git a/src/tests/keywords/update-all b/src/tests/keywords/update-all new file mode 100644 index 0000000..549a122 --- /dev/null +++ b/src/tests/keywords/update-all @@ -0,0 +1,9 @@ +# +# PRE: update +# +# A more generic "update" mechanism +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := "filter" +} diff --git a/src/tests/keywords/update-array b/src/tests/keywords/update-array new file mode 100644 index 0000000..c872f4e --- /dev/null +++ b/src/tests/keywords/update-array @@ -0,0 +1,63 @@ +# +# PRE: update array +# + +update request { + Class := 0x01020304 + Class += 0x05060708 + Class += 0x090a0b0c +} + + +# +# Use array references in the RHS +# of the update section +# + +update request { + Proxy-State += &Class[0] + Proxy-State += &Class[1] + Proxy-State += &Class[2] +} + +if (&Proxy-State != 0x01020304) { + update reply { + Filter-Id := "fail 0" + } +} + +# Must be the same as above +if (&Proxy-State[0] != 0x01020304) { + update reply { + Filter-Id += "fail 0a" + } +} + +if (&Proxy-State[1] != 0x05060708) { + update reply { + Filter-Id += "fail 1" + } +} + +if (&Proxy-State[2] != 0x090a0b0c) { + update reply { + Filter-Id += "fail 2" + } +} + +# must not exist +if (&Proxy-State[3]) { + update reply { + Filter-Id += "fail 3" + } +} + +# +# The test passes only if no test above +# added a Filter-Id +# +if (!reply:Filter-Id) { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/update-delete b/src/tests/keywords/update-delete new file mode 100644 index 0000000..a5c2d5a --- /dev/null +++ b/src/tests/keywords/update-delete @@ -0,0 +1,40 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tmp-String-0 := 'foobarbaz' + Tmp-Integer-0 := 123456789 + Tmp-IP-Address-0 := 192.0.2.1 +} + +if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789) || (Tmp-IP-Address-0 != 192.0.2.1)) { + update { + reply:Filter-Id := 'fail' + } +} + +# Remove all attributes in the control list +update { + request: !* ANY +} + +# All attributes should now of been removed +if ((Tmp-String-0 && (Tmp-String-0 == 'foobarbaz')) || \ + (Tmp-Integer-0 && (Tmp-Integer-0 == 123456789)) || \ + (Tmp-IP-Address-0 && (Tmp-IP-Address-0 == 192.0.2.1))) { + update { + reply:Filter-Id := 'fail' + } +} + +# This will of been removed too +update request { + User-Password := 'hello' +} diff --git a/src/tests/keywords/update-error b/src/tests/keywords/update-error new file mode 100644 index 0000000..92e0ed2 --- /dev/null +++ b/src/tests/keywords/update-error @@ -0,0 +1,9 @@ +# +# PRE: update +# +# It's an error to update lists that don't exist. +# +update no-such-list { # ERROR + control:Cleartext-Password := 'hello' + reply:Filter-Id := "filter" +} diff --git a/src/tests/keywords/update-error-2 b/src/tests/keywords/update-error-2 new file mode 100644 index 0000000..1fe98f0 --- /dev/null +++ b/src/tests/keywords/update-error-2 @@ -0,0 +1,9 @@ +# +# PRE: update-error +# +# It's an error to update lists that don't exist. +# +update { + no-such-list:Cleartext-Password := 'hello' # ERROR + reply:Filter-Id := "filter" +} diff --git a/src/tests/keywords/update-error-3 b/src/tests/keywords/update-error-3 new file mode 100644 index 0000000..ffab73a --- /dev/null +++ b/src/tests/keywords/update-error-3 @@ -0,0 +1,10 @@ +# +# PRE: update-error +# +# It's an error to assign literal values which are not +# part of the set of enumerated values for an attribute +# +update { + Service-Type := 'hello' # ERROR + reply:Filter-Id := "filter" +} diff --git a/src/tests/keywords/update-exec b/src/tests/keywords/update-exec new file mode 100644 index 0000000..b9a0f73 --- /dev/null +++ b/src/tests/keywords/update-exec @@ -0,0 +1,94 @@ +# +# PRE: update if redundant +# +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} + +# +# Exec with script output to attribute +# +update request { + Tmp-String-0 = `/bin/sh -c "echo 'foo bar baz'"` +} + +if (Tmp-String-0 != "foo bar baz") { + update reply { + Filter-Id += "fail 1" + } +} + +# +# Exec with output to list (single attribute) +# +update { + request: = `/bin/sh -c "echo Tmp-String-0 := foo"` +} + +if (Tmp-String-0 != 'foo') { + update reply { + Filter-Id += "fail 2" + } +} + +# +# Exec with output to list (multiple attributes) +# +update { + request: = `/bin/sh -c 'echo Tmp-String-0 := foo, Tmp-String-1 := bar'` +} + +if ((Tmp-String-0 != 'foo') || (Tmp-String-1 != 'bar')) { + update reply { + Filter-Id += "fail 3" + } +} + +# +# Failed exec (malformed attributes) - check no attributes are added +# +update request { + Tmp-String-0 !* ANY + Tmp-String-1 !* ANY +} + +redundant { + group { + update { + request: = `/bin/sh -c 'echo Tmp-String-0 := foo, Tmp-String-1 ?= bar'` + } + } + ok +} +if (Tmp-String-0 || Tmp-String-1) { + update reply { + Filter-Id += "fail 4" + } +} + +# +# Exec with output to list - error code +# +update request { + Tmp-String-0 !* ANY + Tmp-String-1 !* ANY +} + +redundant { + group { + update { + request: = `/bin/sh -c 'echo Tmp-String-0 := foo; exit 64'` + } + } + ok +} +if (Tmp-String-0) { + update reply { + Filter-Id += "fail 5" + } +} + diff --git a/src/tests/keywords/update-filter b/src/tests/keywords/update-filter new file mode 100644 index 0000000..30f96be --- /dev/null +++ b/src/tests/keywords/update-filter @@ -0,0 +1,75 @@ +# +# PRE: update +# +update control { + Tmp-Integer-0 := 5 + Tmp-Integer-0 += 10 + Tmp-Integer-0 += 15 + Tmp-Integer-0 += 20 + Tmp-String-0 := 'foo' + Tmp-String-0 += 'baz' + Tmp-String-0 += 'boink' +} + +# +# Reset the request list +# +update { + &request: !* ANY + &request: += &control:[*] +} + +debug_request + +# +# Only matching attributes of the specified type should remain +# +update request { + &Tmp-Integer-0 == 10 +} + +if (&Tmp-Integer-0[0] != 10) { + update reply { + Filter-Id += "fail 1" + } +} + +if ("%{Tmp-Integer-0[#]}" != 1) { + update reply { + Filter-Id += "fail 2" + } +} + +if ("%{Tmp-String-0[#]}" != 3) { + update reply { + Filter-Id += "fail 3" + } +} + +debug_request + +# +# Only matching attributes of the specified type should remain +# +update request { + &Tmp-String-0 == 'baz' +} + +if (&Tmp-String-0[0] != 'baz') { + update reply { + Filter-Id += "fail 4" + } +} + +if ("%{Tmp-String-0[#]}" != 1) { + update reply { + Filter-Id += "fail 5" + } +} + +update { + control:Auth-Type := Accept + reply:Filter-Id := "filter" +} + +debug_request diff --git a/src/tests/keywords/update-index b/src/tests/keywords/update-index new file mode 100644 index 0000000..390aca7 --- /dev/null +++ b/src/tests/keywords/update-index @@ -0,0 +1,52 @@ +# +# PRE: update update-remove-index +# +# A more generic "update" mechanism +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := "filter" +} + +update { + control:Reply-Message += 'a' + control:Reply-Message += 'b' + control:Reply-Message += 'c' +} + +if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'b') || (&control:Reply-Message[2] != 'c')) { + update { + reply:Filter-Id := 'Fail 0' + } +} + +# Overwrite a specific index, and check the value here is replaced +update { + &control:Reply-Message[1] := 'd' +} + +if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'd') || (&control:Reply-Message[2] != 'c')) { + update { + reply:Filter-Id := 'Fail 1' + } +} + +# Check isolation... +update { + &control:Reply-Message[0] := &control:Reply-Message[0] +} + +if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'd') || (&control:Reply-Message[2] != 'c')) { + update { + reply:Filter-Id := 'Fail 2' + } +} + +# Verify we haven't acquired any extra.. + +if ("%{control:Reply-Message[#]}" != 3) { + update { + reply:Filter-Id := 'Fail 3' + } +} + diff --git a/src/tests/keywords/update-list-error b/src/tests/keywords/update-list-error new file mode 100644 index 0000000..2613055 --- /dev/null +++ b/src/tests/keywords/update-list-error @@ -0,0 +1,19 @@ +# +# PRE: update +# +# It's an error to update lists that don't exist. +# +update { + request := reply + config += request + reply !* ANY +} + +update { + reply += `/path/to/foo bar baz` +} + + +update { + request := nope # ERROR +} diff --git a/src/tests/keywords/update-operator b/src/tests/keywords/update-operator new file mode 100644 index 0000000..ccbcb16 --- /dev/null +++ b/src/tests/keywords/update-operator @@ -0,0 +1,85 @@ +# +# PRE: update +# + +# +# Set it. +# +update request { + NAS-Port := 1000 +} + +# +# Enforce it. +# +update request { + NAS-Port == 1000 +} + +if (NAS-Port != 1000) { + update reply { + Filter-Id += "fail 1" + } +} + +# +# Enforce to new lower value. +# +update request { + NAS-Port <= 500 +} + +if (NAS-Port != 500) { + update reply { + Filter-Id += "fail 2 - expected 500, got %{NAS-Port}" + } +} + +# +# Enforce to new higher value +# +update request { + NAS-Port >= 2000 +} + +if (NAS-Port != 2000) { + update reply { + Filter-Id += "fail 3 - expected 2000, got %{NAS-Port}" + } +} + +# +# Enforce value which previously didn't exist. +# +update request { + Idle-Timeout >= 14400 +} + +if (&request:Idle-Timeout != 14400) { + update reply { + Filter-Id += "fail Idle-Timeout >= 14400" + } +} + +# non-existent attribute +update request { + Class -= 0xabcdef +} + +update request { + Class -= &Class +} + +update request { + NAS-Port -= &NAS-Port +} + +if (!reply:Filter-Id) { + update control { + Cleartext-Password := 'hello' + } + + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/update-prepend b/src/tests/keywords/update-prepend new file mode 100644 index 0000000..4ba9335 --- /dev/null +++ b/src/tests/keywords/update-prepend @@ -0,0 +1,65 @@ +# +# PRE: update +# +update control { + &Tmp-String-0 := 'foo' + &Tmp-String-0 += 'baz' +} + +# Reset the request list +update { + &request !* ANY + &request += &control +} + +debug_request + +# Prepend a single value +update request { + &Tmp-String-0 ^= 'boink' +} + +# The prepended value should be first followd by the other two +if (("%{Tmp-String-0[0]}" != 'boink') || ("%{Tmp-String-0[1]}" != 'foo') || ("%{Tmp-String-0[2]}" != 'baz')) { + update reply { + Filter-Id += "fail 1" + } +} + +if ("%{Tmp-String-0[#]}" != 3) { + update reply { + Filter-Id += "fail 1a" + } +} + +# Add an extra element to the start of control +update control { + &Tmp-String-0 ^= 'wibble' +} + +# Prepend control to request +update { + &request ^= &control +} + +debug_request + +# The attributes should now be "wibble", "foo", "baz", "boink", "foo", "baz" +if (("%{Tmp-String-0[0]}" != 'wibble') || ("%{Tmp-String-0[1]}" != 'foo') || ("%{Tmp-String-0[2]}" != 'baz') || ("%{Tmp-String-0[3]}" != 'boink') || ("%{Tmp-String-0[4]}" != 'foo') || ("%{Tmp-String-0[5]}" != 'baz')) { + update reply { + Filter-Id += "fail 2" + } +} + +if ("%{Tmp-String-0[#]}" != 6) { + update reply { + Filter-Id += "fail 2a" + } +} + +if (!reply:Filter-Id) { + update { + &request:User-Password := 'hello' + &reply:Filter-Id := 'filter' + } +} diff --git a/src/tests/keywords/update-remove-any b/src/tests/keywords/update-remove-any new file mode 100644 index 0000000..e0ef600 --- /dev/null +++ b/src/tests/keywords/update-remove-any @@ -0,0 +1,50 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update { + request:Tmp-String-0 := 'foobarbaz' + request:Tmp-Integer-0 := 123456789 + request:Tmp-IP-Address-0 := 192.0.2.1 + request:Tmp-IP-Address-0 += 192.0.2.2 + control:Tmp-IP-Address-0 := 192.0.2.1 + control:Tmp-IP-Address-0 += 192.0.2.3 +} + +if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2)) { + update { + reply:Filter-Id := 'fail 1' + } +} + +# Remove all attributes in the control list +update { + request:Tmp-IP-Address-0 !* ANY +} + +# Non Tmp-IP-Address-0 address attributes should still be in the request list +if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) { + update reply { + reply:Filter-Id += 'fail 2' + } +} + +# There should be no Tmp-IP-Address attributes in the request list +if (Tmp-IP-Address-0 || ("%{Tmp-IP-Address-0[1]}" != '')) { + update { + reply:Filter-Id += 'fail 3' + } +} + +# But there should still be some in the control list +if ((control:Tmp-IP-Address-0 != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) { + update { + reply:Filter-Id += 'fail 4' + } +} diff --git a/src/tests/keywords/update-remove-index b/src/tests/keywords/update-remove-index new file mode 100644 index 0000000..58df9a5 --- /dev/null +++ b/src/tests/keywords/update-remove-index @@ -0,0 +1,100 @@ +# +# PRE: update update-remove-value +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update { + request:Tmp-String-0 := 'foobarbaz' + request:Tmp-Integer-0 := 123456789 + request:Tmp-IP-Address-0 := 192.0.2.1 + request:Tmp-IP-Address-0 += 192.0.2.2 + request:Tmp-IP-Address-0 += 192.0.2.3 + request:Tmp-IP-Address-0 += 192.0.2.2 + request:Tmp-IP-Address-0 += 192.0.2.4 +} + + +update request { + Tmp-IP-Address-0[3] -= 192.0.2.2 +} + +# Only the 1st, 2nd, 3rd and 5th Tmp-IP-Address attributes should still be in the list +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.1') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[2]}" != '192.0.2.3') || \ + ("%{Tmp-IP-Address-0[3]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[4]}" != '')) { + update reply { + Filter-Id += 'fail 1' + } +} + +# There's still a 192.0.2.2 but it's not at index 3 +update request { + Tmp-IP-Address-0[3] -= 192.0.2.2 +} + +# Should be the same as the previous result +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.1') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[2]}" != '192.0.2.3') || \ + ("%{Tmp-IP-Address-0[3]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[4]}" != '')) { + update reply { + Filter-Id += 'fail 2' + } +} + +# Remove whatever's at index 0 +update request { + Tmp-IP-Address-0[0] !* ANY +} + +# IP address at index 0 should be removed +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \ + ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[3]}" != '')) { + update reply { + Filter-Id += 'fail 3' + } +} + +# Remove whatever's at index 3 (should be nothing) +update request { + Tmp-IP-Address-0[3] !* ANY +} + +# Should be the same as the previous result +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \ + ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[3]}" != '')) { + update reply { + Filter-Id += 'fail 4' + } +} + +# Remove all instances of Tmp-IP-Address +update request { + Tmp-IP-Address-0 !* ANY +} + +# No more IP address attributes! +if ("%{Tmp-IP-Address-0[0]}" != '') { + update reply { + Filter-Id += 'fail 5' + } +} + +# Non Tmp-IP-Address-0 address attributes should still be in the request list +if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) { + update reply { + Filter-Id += 'fail 6' + } +} diff --git a/src/tests/keywords/update-remove-list b/src/tests/keywords/update-remove-list new file mode 100644 index 0000000..22ae577 --- /dev/null +++ b/src/tests/keywords/update-remove-list @@ -0,0 +1,40 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tmp-String-0 := 'foobarbaz' + Tmp-Integer-0 := 123456789 + Tmp-IP-Address-0 := 192.0.2.1 +} + +if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789) || (Tmp-IP-Address-0 != 192.0.2.1)) { + update reply { + Filter-Id += 'fail 0' + } +} + +# Remove all attributes in the control list +update { + request: !* ANY +} + +# All attributes should now of been removed +if ((Tmp-String-0 && (Tmp-String-0 == 'foobarbaz')) || \ + (Tmp-Integer-0 && (Tmp-Integer-0 == 123456789)) || \ + (Tmp-IP-Address-0 && (Tmp-IP-Address-0 == 192.0.2.1))) { + update reply { + Filter-Id := 'fail 1' + } +} + +# This will of been removed too +update request { + User-Password := 'hello' +} diff --git a/src/tests/keywords/update-remove-tag b/src/tests/keywords/update-remove-tag new file mode 100644 index 0000000..3328789 --- /dev/null +++ b/src/tests/keywords/update-remove-tag @@ -0,0 +1,275 @@ +# +# PRE: update update-remove-value update-remove-index update-tag +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update { + request:Tunnel-Server-Endpoint += '192.0.1.1' + request:Tunnel-Server-Endpoint += '192.0.1.2' + request:Tunnel-Server-Endpoint:1 += '192.0.1.1' + request:Tunnel-Server-Endpoint:2 += '192.0.2.1' + request:Tunnel-Server-Endpoint:2 += '192.0.2.2' + request:Tunnel-Server-Endpoint:3 += '192.0.3.1' + request:Tunnel-Server-Endpoint:3 += '192.0.3.2' + request:Tunnel-Server-Endpoint:3 += '192.0.3.3' + control: += request: +} + +# Check [#] is working correctly (should probably be another set of tests) +if (("%{request:Tunnel-Server-Endpoint[#]}" != 8) || \ + ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \ + ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \ + ("%{request:Tunnel-Server-Endpoint:2[#]}" != 2) || \ + ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) { + update reply { + Filter-Id += 'fail 0' + } +} + +update { + Tunnel-Server-Endpoint !* ANY +} + +# List should now be empty +if ("%{request:Tunnel-Server-Endpoint[#]}" != 0) { + update reply { + Filter-Id += 'fail 1' + } +} + +# Reset the list +update { + request: += control: +} + +# Now remove all Tunnel-Server-Endpoint attributes with :2 +update { + Tunnel-Server-Endpoint:2 !* ANY +} + +if (("%{request:Tunnel-Server-Endpoint[#]}" != 6) || \ + ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \ + ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \ + ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) { + update reply { + Filter-Id += 'fail 2' + } +} + +# Now remove all Tunnel-Server-Endpoint attributes with :0 (no tags) +update { + Tunnel-Server-Endpoint:0 !* ANY +} + +if (("%{request:Tunnel-Server-Endpoint[#]}" != 4) || \ + ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \ + ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) { + update reply { + Filter-Id += 'fail 3' + } +} + +# Now remove all Tunnel-Server-Endpoint attributes with :3 +update { + Tunnel-Server-Endpoint:3 !* ANY +} + +if (("%{request:Tunnel-Server-Endpoint[#]}" != 1) || \ + ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \ + ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) { + update reply { + Filter-Id += 'fail 4' + } +} + +# Now remove all Tunnel-Server-Endpoint attributes with :1 +update { + Tunnel-Server-Endpoint:1 !* ANY +} + +if (("%{request:Tunnel-Server-Endpoint[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:1[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \ + ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) { + update reply { + Filter-Id += 'fail 5' + } +} + +# Reset the list +update { + request: += control: +} + +# Remove all Tunnel-Server-Endpoint attributes at :3[0] (none) +update { + Tunnel-Server-Endpoint:1[3] !* ANY +} + +if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') { + update reply { + Filter-Id += 'fail 6' + } +} + +if (Tunnel-Server-Endpoint:3[1] != '192.0.3.2') { + update reply { + Filter-Id += 'fail 7' + } +} + +if (Tunnel-Server-Endpoint:3[2] != '192.0.3.3') { + update reply { + Filter-Id += 'fail 8' + } +} + +# Remove all Tunnel-Server-Endpoint attributes at :3[1] +update { + Tunnel-Server-Endpoint:3[1] !* ANY +} + +if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') { + update reply { + Filter-Id += 'fail 9' + } +} + +if (Tunnel-Server-Endpoint:3[1] != '192.0.3.3') { + update reply { + Filter-Id += 'fail 10' + } +} + +# Remove any Tunnel-Server-Endpoint with a value of '192.0.1.1' (should remove both tagged and untagged versions) +update { + Tunnel-Server-Endpoint -= '192.0.1.1' +} + +# Also checks whether presence checks for tagged attributes work correctly +if (request:Tunnel-Server-Endpoint:1) { + update reply { + Filter-Id += 'fail 11' + } +} + +if (request:Tunnel-Server-Endpoint:0[0] != '192.0.1.2') { + update reply { + Filter-Id += 'fail 12' + } +} + +# Remove any Tunnel-Server-Endpoint with a value of '192.0.3.1' +update { + Tunnel-Server-Endpoint:3 -= '192.0.3.2' +} + +if (request:Tunnel-Server-Endpoint:3[0] != '192.0.3.1') { + update reply { + Filter-Id += 'fail 13' + } +} + +if (request:Tunnel-Server-Endpoint:3[1] != '192.0.3.3') { + update reply { + Filter-Id += 'fail 14' + } +} + +# Reset the list +update { + request: !* ANY +} +update { + request: += control: +} + +# Remove only the tagged version of '192.0.1.1' +update { + request:Tunnel-Server-Endpoint:1 -= '192.0.1.1' +} + +if (request:Tunnel-Server-Endpoint:0[0] != '192.0.1.1') { + update reply { + Filter-Id += 'fail 15' + } +} + +# Reset the list +update { + request: !* ANY +} +update { + request: += control: +} + +# Remove only the untagged version of '192.0.1.1' +update { + request:Tunnel-Server-Endpoint:0 -= '192.0.1.1' +} + +if (request:Tunnel-Server-Endpoint:1[0] != '192.0.1.1') { + update reply { + Filter-Id += 'fail 16' + } +} + +# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.3' (which it does) +update { + Tunnel-Server-Endpoint:3[1] -= '192.0.3.2' +} + +if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') { + update reply { + Filter-Id += 'fail 17' + } +} + +if (Tunnel-Server-Endpoint:3[1] != '192.0.3.3') { + update reply { + Filter-Id += 'fail 18' + } +} + +# Reset the list +update { + request: !* ANY +} +update { + request: += control: +} + +# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.4' (which it doesn't) +update { + Tunnel-Server-Endpoint:3[1] -= '192.0.3.4' +} + +if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') { + update reply { + Filter-Id += 'fail 19' + } +} + +if (Tunnel-Server-Endpoint:3[1] != '192.0.3.2') { + update reply { + Filter-Id += 'fail 20' + } +} + +if (Tunnel-Server-Endpoint:3[2] != '192.0.3.3') { + update reply { + Filter-Id += 'fail 21' + } +} + diff --git a/src/tests/keywords/update-remove-value b/src/tests/keywords/update-remove-value new file mode 100644 index 0000000..3fd1f94 --- /dev/null +++ b/src/tests/keywords/update-remove-value @@ -0,0 +1,116 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update { + request:Tmp-String-0 := 'foobarbaz' + request:Tmp-Integer-0 := 123456789 + request:Tmp-IP-Address-0 := 192.0.2.1 + request:Tmp-IP-Address-0 += 192.0.2.2 + request:Tmp-IP-Address-0 += 192.0.2.3 + request:Tmp-IP-Address-0 += 192.0.2.4 + control:Tmp-IP-Address-0 := 192.0.2.1 + control:Tmp-IP-Address-0 += 192.0.2.3 +} + +if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || \ + ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2) || \ + ("%{Tmp-IP-Address-0[2]}" != 192.0.2.3) || \ + ("%{Tmp-IP-Address-0[3]}" != 192.0.2.4)) { + update reply { + Filter-Id += 'fail 0' + } +} + +# Remove Tmp-IP-Address-0 with a specific value +update { + request:Tmp-IP-Address-0 -= 192.0.2.1 +} + +# Only the 2nd, 3rd and 4th Tmp-IP-Address attributes should still be in the list +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \ + ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[3]}" != '')) { + update reply { + Filter-Id += 'fail 1' + } +} + +# Remove Tmp-IP-Address-0 with a specific value (somewhere in the middle) +update { + request:Tmp-IP-Address-0 -= 192.0.2.3 +} + +# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[2]}" != '')) { + update reply { + Filter-Id += 'fail 2' + } +} + +# Remove Tmp-IP-Address-0 with a specific value (which doesn't exist) +update { + request:Tmp-IP-Address-0 -= 192.0.2.3 +} + +# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '192.0.2.4') || \ + ("%{Tmp-IP-Address-0[2]}" != '')) { + update reply { + Filter-Id += 'fail 3' + } +} + +# Remove Tmp-IP-Address-4 (which doesn't exist - more to check for SEGV/assert) +update { + request:Tmp-IP-Address-4 -= 192.0.2.3 +} + +# Remove Tmp-IP-Address-0 with a specific value +update { + request:Tmp-IP-Address-0 -= 192.0.2.4 +} + +# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list +if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \ + ("%{Tmp-IP-Address-0[1]}" != '')) { + update reply { + Filter-Id += 'fail 4' + } +} + +# Remove Tmp-IP-Address-0 with a specific value +update { + request:Tmp-IP-Address-0 -= 192.0.2.2 +} + +# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list +if ("%{Tmp-IP-Address-0[0]}" != '') { + update reply { + Filter-Id += 'fail 5' + } +} + +# Non Tmp-IP-Address-0 address attributes should still be in the request list +if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) { + update reply { + Filter-Id += 'fail 6' + } +} + +# But there should still be some in the control list +if (("%{control:Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) { + update { + Filter-Id += 'fail 7' + } +} diff --git a/src/tests/keywords/update-tag b/src/tests/keywords/update-tag new file mode 100644 index 0000000..15afd59 --- /dev/null +++ b/src/tests/keywords/update-tag @@ -0,0 +1,176 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tunnel-Server-Endpoint:0 := '192.0.1.1' # Should not be tagged + Tunnel-Server-Endpoint:0 += '192.0.1.2' # Should not be tagged + Tunnel-Server-Endpoint:1 := '192.0.2.1' + Tunnel-Server-Endpoint:1 += '192.0.2.2' + Tunnel-Server-Endpoint:2 := '192.0.3.1' + Tunnel-Server-Endpoint:2 += '192.0.3.2' +} + +update request { + Tmp-Integer-0 := "%{debug_attr:request:}" +} + + +# +# Selecting on attributes which have no tag (0) +# +if (Tunnel-Server-Endpoint:0[0] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 1' + } +} + +if (Tunnel-Server-Endpoint:0[1] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 2' + } +} + +# +# Selecting on attributes with no tag specified (should match all of that type) +# +if (Tunnel-Server-Endpoint[0] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 3' + } +} + +if (Tunnel-Server-Endpoint[1] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 4' + } +} + +if (Tunnel-Server-Endpoint[2] != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 5' + } +} + +# +# Now the none xlat version +# +# Check that access attributes by tag works first +if (Tunnel-Server-Endpoint:2 != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 6' + } +} + +if (Tunnel-Server-Endpoint:2 == '192.0.3.2') { + update { + reply:Filter-Id += 'fail 7' + } +} + +if (Tunnel-Server-Endpoint:1 != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 8' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tunnel-Server-Endpoint:2[0] != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 9' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tunnel-Server-Endpoint:2[1] != '192.0.3.2') { + update { + reply:Filter-Id += 'fail 10' + } +} + +# +# Assignment (bare) +# +update request { + Tmp-String-1 += &Tunnel-Server-Endpoint:2 # 0 + Tmp-String-1 += &Tunnel-Server-Endpoint:2 # 1 + Tmp-String-1 += &Tunnel-Server-Endpoint:1 # 2 + Tmp-String-1 += &Tunnel-Server-Endpoint:2[0] # 3 + Tmp-String-1 += &Tunnel-Server-Endpoint:2[1] # 4 + Tmp-String-1 += &Tunnel-Server-Endpoint:0[0] # 5 + Tmp-String-1 += &Tunnel-Server-Endpoint:0[1] # 6 + Tmp-String-1 += &Tunnel-Server-Endpoint:0[2] # 7 (No attribute should be added here) + Tmp-String-1 += &Tunnel-Server-Endpoint[0] # 8 + Tmp-String-1 += &Tunnel-Server-Endpoint[1] # 9 + Tmp-String-1 += &Tunnel-Server-Endpoint[2] # 10 +} + +# Check that access attributes by tag works first +if (Tmp-String-1[0] != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 11' + } +} + +if (Tmp-String-1[1] == '192.0.3.2') { + update { + reply:Filter-Id += 'fail 12' + } +} + +if (Tmp-String-1[2] != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 13' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tmp-String-1[3] != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 14' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tmp-String-1[4] != '192.0.3.2') { + update { + reply:Filter-Id += 'fail 15' + } +} + +# Now check the assignment +if (Tmp-String-1[5] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 16' + } +} + +if (Tmp-String-1[6] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 17' + } +} + +if (Tmp-String-1[7] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 19' + } +} + +if (Tmp-String-1[8] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 20' + } +} + +if (Tmp-String-1[9] != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 21' + } +} diff --git a/src/tests/keywords/update-xlat b/src/tests/keywords/update-xlat new file mode 100644 index 0000000..59230dc --- /dev/null +++ b/src/tests/keywords/update-xlat @@ -0,0 +1,61 @@ +# +# PRE: update +# +# Form attribute references with xlats +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + + +update request { + Tmp-String-0 := 'Tmp-String-1' +} + +# +# Shouldn't update Tmp-String-0, should instead update Tmp-String-1 +# ... maybe this is what Alan meant when he was talking about people +# doing stupid things with this feature. +# +update request { + "%{Tmp-String-0}" := 'hello' +} + +if (&Tmp-String-1 != 'hello') { + update reply { + Filter-Id += 'Fail 0' + } +} + +if (&Tmp-String-0 == 'hello') { + update reply { + Filter-Id += 'Fail 1' + } +} + +# +# Try updating an attribute specified by an OID string +# +update { + Tmp-Integer-0 := 11344 +} +update { + "Vendor-%{Tmp-Integer-0}-Attr-1" := 127.0.0.1 +} + +if (&FreeRADIUS-Proxied-To != 127.0.0.1) { + update reply { + Filter-Id += 'Fail 2' + } +} + +update { + "Attr-%{attr_num:Tmp-String-1}" := 'hello2' +} + +if (&Tmp-String-1 != 'hello2') { + update reply { + Filter-Id += 'Fail 3' + } +} diff --git a/src/tests/keywords/urlquote b/src/tests/keywords/urlquote new file mode 100644 index 0000000..35057d8 --- /dev/null +++ b/src/tests/keywords/urlquote @@ -0,0 +1,50 @@ +# +# PRE: update if +# +update { + # Some encoders replace ~ with %7E RFC3986 Section 2.4 says this should not be done. + request:Tmp-String-0 := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~' + request:Tmp-String-1 := '±§!@#$%^&*()+={[}]:;"\'|\<,>?/`' + request:Tmp-String-2 := '™œ¥¤' + request:Tmp-String-3 := '%C2%B1%C2%A7%21%40%23%24%25%5E%26%2A%28%29%2B%3D%7B%5B%7D%5D%3A%3B%22%27%7C%5C%3C%2C%3E%3F%2F%60' + + request:Tmp-String-4 := '%E2%84%A2%C5%93%C2%A5%C2%A4' + reply:Filter-ID := 'filter' +} + + +if (<string>"%{urlquote:%{request:Tmp-String-0}}" != &Tmp-String-0) { + update reply { + Filter-Id += 'Fail 1' + } +} + +if (<string>"%{urlquote:%{request:Tmp-String-1}}" != &Tmp-String-3) { + update reply { + Filter-Id += 'Fail 2' + } +} + +if (<string>"%{urlquote:%{request:Tmp-String-2}}" != &Tmp-String-4) { + update reply { + Filter-Id += 'Fail 3' + } +} + +if (<string>"%{urlunquote:%{request:Tmp-String-0}}" != &Tmp-String-0) { + update reply { + Filter-Id += 'Fail 4' + } +} + +if (<string>"%{urlunquote:%{request:Tmp-String-3}}" != &Tmp-String-1) { + update reply { + Filter-Id += 'Fail 5' + } +} + +if (<string>"%{urlunquote:%{request:Tmp-String-4}}" != &Tmp-String-2) { + update reply { + Filter-Id += 'Fail 6' + } +} diff --git a/src/tests/keywords/virtual b/src/tests/keywords/virtual new file mode 100644 index 0000000..d6dbe32 --- /dev/null +++ b/src/tests/keywords/virtual @@ -0,0 +1,12 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +if (request:Packet-Type == Access-Request) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/virtual-exists b/src/tests/keywords/virtual-exists new file mode 100644 index 0000000..7a8e8f3 --- /dev/null +++ b/src/tests/keywords/virtual-exists @@ -0,0 +1,12 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +if (&Client-Shortname) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/virtual-load-balance b/src/tests/keywords/virtual-load-balance new file mode 100644 index 0000000..256c2ff --- /dev/null +++ b/src/tests/keywords/virtual-load-balance @@ -0,0 +1,14 @@ +# PRE: update if foreach +# +# Virtual Load-Balance blocks. +# + +# +# Both of these should parse. +# +virtual_instantiate +virtual_instantiate.post-auth + +update reply { + Filter-Id := 'filter' +} diff --git a/src/tests/keywords/virtual-rhs b/src/tests/keywords/virtual-rhs new file mode 100644 index 0000000..0d21e7f --- /dev/null +++ b/src/tests/keywords/virtual-rhs @@ -0,0 +1,16 @@ +# +# PRE: update if +# +update control { + Cleartext-Password := 'hello' +} + +update request { + Tmp-String-0 := "<UNKNOWN-CLIENT>" +} + +if (&Tmp-String-0 == &Client-Shortname) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/keywords/virtual_policy b/src/tests/keywords/virtual_policy new file mode 100644 index 0000000..4ab00e2 --- /dev/null +++ b/src/tests/keywords/virtual_policy @@ -0,0 +1,15 @@ +# PRE: update if foreach +# +# Virtual policies +# + + +# +# Both of these should parse. +# +virtual_policy +virtual_policy.post-auth + +update reply { + Filter-Id := 'filter' +} diff --git a/src/tests/keywords/wimax b/src/tests/keywords/wimax new file mode 100644 index 0000000..f149da4 --- /dev/null +++ b/src/tests/keywords/wimax @@ -0,0 +1,31 @@ +update control { + Cleartext-Password := 'hello' +} + +update request { + WiMAX-PFDv2-Src-Port = 6809 +} + +if (WiMAX-PFDv2-Src-Port != 6809) { + update reply { + Filter-Id := "fail-1" + } +} + +# +# This is known, and should be renamed +update request { + Attr-26.24757.84.9.5.7 = 0x01 +} + +if (WiMAX-PFDv2-Src-Assigned != 1) { + update reply { + Filter-Id := "fail-2" + } +} + +if (!reply:Filter-Id) { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/wimax-comboip b/src/tests/keywords/wimax-comboip new file mode 100644 index 0000000..c4c8a3f --- /dev/null +++ b/src/tests/keywords/wimax-comboip @@ -0,0 +1,19 @@ +update control { + Cleartext-Password := 'hello' +} + +update request { + WiMAX-DHCPv4-Server := 192.0.2.23 +} + +if (WiMAX-DHCPv4-Server != 192.0.2.23) { + update reply { + Filter-Id := "fail-1" + } +} + +if (!reply:Filter-Id) { + update reply { + Filter-Id := "filter" + } +}
\ No newline at end of file diff --git a/src/tests/keywords/with_dots b/src/tests/keywords/with_dots new file mode 100644 index 0000000..4fc6b06 --- /dev/null +++ b/src/tests/keywords/with_dots @@ -0,0 +1,19 @@ +# +# PRE: update +# + +# +# Ensure that policies can have dots. +# +# The main problem is that conf section references +# also have dots in them... +# +with.dots + +update control { + Cleartext-Password := 'hello' +} + +update reply { + Filter-Id := "filter" +} diff --git a/src/tests/keywords/xlat-attr b/src/tests/keywords/xlat-attr new file mode 100644 index 0000000..d19495a --- /dev/null +++ b/src/tests/keywords/xlat-attr @@ -0,0 +1,62 @@ +# +# PRE: update +# +# Check attribute info xlats work correctly +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Reply-Message := 'foo' + FreeRADIUS-Proxied-To := 127.0.0.1 +} + +if ("%{attr:&FreeRADIUS-Proxied-To}" != 'FreeRADIUS-Proxied-To') { + update reply { + Filter-Id += 'Fail 0' + } +} + +if ("%{attr_num:&FreeRADIUS-Proxied-To}" != 1) { + update reply { + Filter-Id += 'Fail 2' + } +} + +if ("%{vendor:&FreeRADIUS-Proxied-To}" != 'FreeRADIUS') { + update reply { + Filter-Id += 'Fail 3' + } +} + +if ("%{vendor_num:&FreeRADIUS-Proxied-To}" != 11344) { + update reply { + Filter-Id += 'Fail 4' + } +} + +if ("%{attr:&Reply-Message}" != 'Reply-Message') { + update reply { + Filter-Id += 'Fail 5' + } +} + +if ("%{attr_num:&Reply-Message}" != 18) { + update reply { + Filter-Id += 'Fail 6' + } +} + +if ("%{vendor:&Reply-Message}" != '') { + update reply { + Filter-Id += 'Fail 7' + } +} + +if ("%{vendor_num:&Reply-Message}" != 0) { + update reply { + Filter-Id += 'Fail 8' + } +} diff --git a/src/tests/keywords/xlat-attr-index b/src/tests/keywords/xlat-attr-index new file mode 100644 index 0000000..c967dd6 --- /dev/null +++ b/src/tests/keywords/xlat-attr-index @@ -0,0 +1,53 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tmp-IP-Address-0 := 192.0.2.1 + Tmp-IP-Address-0 += 192.0.2.2 +} + +if ("%{Tmp-IP-Address-0[#]}" != 2) { + update { + reply:Filter-Id += 'fail 0' + } +} + +if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2)) { + update { + reply:Filter-Id += 'fail 1' + } +} + +if ("%{Tmp-IP-Address-0[*]}" != '192.0.2.1,192.0.2.2') { + update { + reply:Filter-Id += 'fail 2' + } +} + +# Try calling these xlats in mapping too, they may get optimised to VPTs which is a +# different code path. +update request { + Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[1]}" + Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[0]}" + Tmp-String-0 = "%{Tmp-IP-Address-0[*]}" + Tmp-Integer-0 = "%{Tmp-IP-Address-0[#]}" +} + +if (Tmp-String-0 != '192.0.2.1,192.0.2.2') { + update { + reply:Filter-Id += 'fail 3' + } +} + +if (Tmp-Integer-0 != 2) { + update { + reply:Filter-Id += 'fail 4' + } +} diff --git a/src/tests/keywords/xlat-attr-tag b/src/tests/keywords/xlat-attr-tag new file mode 100644 index 0000000..c0bd8b6 --- /dev/null +++ b/src/tests/keywords/xlat-attr-tag @@ -0,0 +1,225 @@ +# +# PRE: update +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +update request { + Tunnel-Server-Endpoint := '192.0.1.1' # Should not be tagged + Tunnel-Server-Endpoint:0 += '192.0.1.2' # Should not be tagged + Tunnel-Server-Endpoint:1 := '192.0.2.1' + Tunnel-Server-Endpoint:1 += '192.0.2.2' + Tunnel-Server-Endpoint:2 := '192.0.3.1' + Tunnel-Server-Endpoint:2 += '192.0.3.2' +} + +update request { + Tmp-Integer-0 := "%{debug_attr:request:}" +} + +# Check the tag printing xlat works correctly +if ("%{tag:Tunnel-Server-Endpoint[0]}" != '') { + update { + reply:Filter-Id += 'fail 0a' + } +} + +if ("%{tag:Tunnel-Server-Endpoint[1]}" != '') { + update { + reply:Filter-Id += 'fail 0b' + } +} + + +if ("%{tag:Tunnel-Server-Endpoint[2]}" != '1') { + update { + reply:Filter-Id += 'fail 0c' + } +} + +if ("%{tag:Tunnel-Server-Endpoint[5]}" != '2') { + update { + reply:Filter-Id += 'fail 0d' + } +} + +if ("%{tag:Tunnel-Server-Endpoint[6]}" != '') { + update { + reply:Filter-Id += 'fail 0e' + } +} + +if ("%{tag:control:Cleartext-Password}" != '') { + update { + reply:Filter-Id += 'fail 0f' + } +} + +# Check that access attributes by tag works first +if ("%{Tunnel-Server-Endpoint:2}" != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 1' + } +} + +if ("%{Tunnel-Server-Endpoint:2}" == '192.0.3.2') { + update { + reply:Filter-Id += 'fail 2' + } +} + +if ("%{Tunnel-Server-Endpoint:1}" != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 3' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if ("%{Tunnel-Server-Endpoint:2[0]}" != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 4' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if ("%{Tunnel-Server-Endpoint:2[1]}" != '192.0.3.2') { + update { + reply:Filter-Id += 'fail 5' + } +} + +if ("%{Tunnel-Server-Endpoint:0[2]}" != '') { + update { + reply:Filter-Id += 'fail 6' + } +} + +if ("%{Tunnel-Server-Endpoint:0[0]}" != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 7' + } +} + +if ("%{Tunnel-Server-Endpoint:0[1]}" != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 8' + } +} + +if ("%{Tunnel-Server-Endpoint:0[2]}" != '') { + update { + reply:Filter-Id += 'fail 9' + } +} + +# +# Selecting on attributes with no tag specified (should match all of that type) +# +if ("%{Tunnel-Server-Endpoint[0]}" != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 10' + } +} + +if ("%{Tunnel-Server-Endpoint[1]}" != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 11' + } +} + +if ("%{Tunnel-Server-Endpoint[2]}" != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 12' + } +} + +# +# Assignment (xlat) +# +update request { + Tmp-String-0 += "%{Tunnel-Server-Endpoint:2}" #0 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:2}" #1 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:1}" #2 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[0]}" #3 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[1]}" #4 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[0]}" #5 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[1]}" #6 + Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[2]}" #7 + Tmp-String-0 += "%{Tunnel-Server-Endpoint[0]}" #8 + Tmp-String-0 += "%{Tunnel-Server-Endpoint[1]}" #9 + Tmp-String-0 += "%{Tunnel-Server-Endpoint[2]}" #10 +} + +# Check that access attributes by tag works first +if (Tmp-String-0[0] != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 13' + } +} + +if (Tmp-String-0[1] == '192.0.3.2') { + update { + reply:Filter-Id += 'fail 14' + } +} + +if (Tmp-String-0[2] != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 15' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tmp-String-0[3] != '192.0.3.1') { + update { + reply:Filter-Id += 'fail 16' + } +} + +# Get the first instance of Tunnel-Server-Endpoint:2 +if (Tmp-String-0[4] != '192.0.3.2') { + update { + reply:Filter-Id += 'fail 17' + } +} + +# Now check the assignment +if (Tmp-String-0[5] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 18' + } +} + +if (Tmp-String-0[6] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 19' + } +} + +if (Tmp-String-0[7] != '') { + update { + reply:Filter-Id += 'fail 20' + } +} + +if (Tmp-String-0[8] != '192.0.1.1') { + update { + reply:Filter-Id += 'fail 21' + } +} + +if (Tmp-String-0[9] != '192.0.1.2') { + update { + reply:Filter-Id += 'fail 22' + } +} + +if (Tmp-String-0[10] != '192.0.2.1') { + update { + reply:Filter-Id += 'fail 23' + } +} diff --git a/src/tests/keywords/xlat-concat b/src/tests/keywords/xlat-concat new file mode 100644 index 0000000..e0c55a9 --- /dev/null +++ b/src/tests/keywords/xlat-concat @@ -0,0 +1,40 @@ +# +# PRE: xlat-list +# +# concat xlat +# + +update control { + control !* ANY +} + +update control { + Tmp-IP-Address-0 := 192.0.2.1 + Tmp-IP-Address-0 += 192.0.2.2 +} + +update request { + Tmp-String-0 := "%{concat:control:[*] ;}" +} + +if (Tmp-String-0 != '192.0.2.1;192.0.2.2') { + update { + reply:Filter-Id += 'fail 1' + } +} + +update request { + Tmp-String-0 := "%{concat:control:[*] X}" +} + +if (Tmp-String-0 != '192.0.2.1X192.0.2.2') { + update { + reply:Filter-Id += 'fail 2' + } +} + +# Boilerplate junk +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} diff --git a/src/tests/keywords/xlat-error b/src/tests/keywords/xlat-error new file mode 100644 index 0000000..b6a2587 --- /dev/null +++ b/src/tests/keywords/xlat-error @@ -0,0 +1,12 @@ +# +# PRE: if xlat-attr-tag +# + +# +# missing a closing } for the expansion in the string. +# +if ("%{expr:1 + 2" == 3) { # ERROR + update reply { + Filter-Id := "fail" + } +} diff --git a/src/tests/keywords/xlat-explode b/src/tests/keywords/xlat-explode new file mode 100644 index 0000000..ea727d9 --- /dev/null +++ b/src/tests/keywords/xlat-explode @@ -0,0 +1,91 @@ +# +# PRE: update +# +# Check explode works correctly +# +update { + request:Class := '1=1|my_attr=2|my_attr=hello|' + request:Calling-Station-ID += '|' + control:User-Name += '|hello|goodbye' + control:User-Name += '|morning|night|1|' + control:Reply-Message := 'Can\'t touch this' + reply:Filter-Id = 'filter' +} + +if ("%{explode:&Class |}" != 3) { + update reply { + Filter-Id += 'Fail 0' + } +} + +if ("%{Class[#]}" != 3) { + update reply { + Filter-Id += 'Fail 1' + } +} + +if ((&Class[0] != '1=1') || (&Class[1] != 'my_attr=2') || (&Class[2] != 'my_attr=hello')) { + update reply { + Filter-Id += 'Fail 2' + } +} + +if (&Class[3]) { + update reply { + Filter-Id += 'Fail 3' + } +} + +if ("%{explode:&control:Calling-Station-Id |}" != 0) { + update reply { + filter-Id += 'Fail 4' + } +} + +if ("%{explode:&control:User-Name[*] |}" != 5) { + update reply { + Filter-Id += 'Fail 5' + } +} + +if ("%{control:User-Name[#]}" != 5) { + update reply { + Filter-Id += 'Fail 6' + } +} + +if ((&control:User-Name[0] != 'hello') || \ + (&control:User-Name[1] != 'goodbye') || \ + (&control:User-Name[2] != 'morning') || \ + (&control:User-Name[3] != 'night') || \ + (&control:User-Name[4] != '1')) { + update reply { + Filter-Id += 'Fail 7' + } +} + +if (&control:User-Name[5]) { + update reply { + Filter-Id += 'Fail 8' + } +} + +if ("%{explode:&control:Reply-Message |}" != 0) { + update reply { + Filter-Id += 'Fail 9' + } +} + +if ("%{control:Reply-Message[#]}" != 1) { + update reply { + Filter-Id += 'Fail 10' + } +} + +if (&control:Reply-Message != 'Can\'t touch this') { + update reply { + Filter-Id += 'Fail 11' + } +} + +debug_all diff --git a/src/tests/keywords/xlat-list b/src/tests/keywords/xlat-list new file mode 100644 index 0000000..fcd9e84 --- /dev/null +++ b/src/tests/keywords/xlat-list @@ -0,0 +1,64 @@ +# +# PRE: update +# +update control { + control !* ANY +} + +update control { + Tmp-IP-Address-0 := 192.0.2.1 + Tmp-IP-Address-0 += 192.0.2.2 +} + +if ("%{control:[#]}" != 2) { + update { + reply:Filter-Id += 'fail 0' + } +} + +debug_control + +if (("%{control:[0]}" != 192.0.2.1) || ("%{control:[1]}" != 192.0.2.2)) { + update { + reply:Filter-Id += 'fail 1' + } +} + +if (("%{control:[n]}" != 192.0.2.2)) { + update { + reply:Filter-Id += 'fail 1a' + } +} + +if ("%{control:[*]}" != '192.0.2.1,192.0.2.2') { + update { + reply:Filter-Id += 'fail 2' + } +} + +# Try calling these xlats in mapping too, they may get optimised to VPTs which is a +# different code path. +update request { + Tmp-IP-Address-1 += "%{control:[1]}" + Tmp-IP-Address-1 += "%{control:[0]}" + Tmp-String-0 = "%{control:[*]}" + Tmp-Integer-0 = "%{control:[#]}" +} + +if (Tmp-String-0 != '192.0.2.1,192.0.2.2') { + update { + reply:Filter-Id += 'fail 3' + } +} + +if (Tmp-Integer-0 != 2) { + update { + reply:Filter-Id += 'fail 4' + } +} + +# Boilerplate junk +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} diff --git a/src/tests/keywords/xlat-octets b/src/tests/keywords/xlat-octets new file mode 100644 index 0000000..ea9543c --- /dev/null +++ b/src/tests/keywords/xlat-octets @@ -0,0 +1,36 @@ +# +# PRE: update if +# +# Remove all attributes in a list +# +update { + control:Cleartext-Password := 'hello' + reply:Filter-Id := 'filter' +} + +# +# Regression test for 0x prefix. xlat expanded +# octet strings must NOT have a 0x prefix added +# +update request { + Tmp-Octets-0 := 0x0001020304050607 + Tmp-Octets-0 += 0x0706050403020100 +} + +if ("%{Tmp-Octets-0}" != '0x0001020304050607') { + update { + reply:Filter-Id := 'fail 1' + } +} + +if ("%{Tmp-Octets-0[0]}" != '0x0001020304050607') { + update { + reply:Filter-Id += 'fail 2' + } +} + +if ("%{Tmp-Octets-0[*]}" != '0x0001020304050607,0x0706050403020100') { + update { + reply:Filter-Id += 'fail 3' + } +} diff --git a/src/tests/keywords/xlat-virtual-attr b/src/tests/keywords/xlat-virtual-attr new file mode 100644 index 0000000..e476993 --- /dev/null +++ b/src/tests/keywords/xlat-virtual-attr @@ -0,0 +1,131 @@ +# +# PRE: if +# + +update reply { + Filter-Id := "filter" +} + +if ("%{Client-Shortname}" != '<UNKNOWN-CLIENT>') { + update reply { + Filter-Id += "fail 0" + } +} + +if ("%{Request-Processing-Stage}" != 'authorize') { + update reply { + Filter-Id += "fail 1" + } +} + +if ("%{Virtual-Server}" != 'default') { + update reply { + Filter-Id += "fail 2" + } +} + +if ("%{Module-Return-Code}" != '') { + update reply { + Filter-Id += "fail 3a" + } +} + +ok +if ("%{Module-Return-Code}" != 'ok') { + update reply { + Filter-Id += "fail 3b" + } +} + +if ("%{Packet-Type}" != 'Access-Request') { + update reply { + Filter-Id += "fail 4" + } +} + +# Response hasn't been set yet +if ("%{Response-Packet-Type}" != '') { + update reply { + Filter-Id += "fail 5" + } +} + +if ("%{Packet-Authentication-Vector}" != '0x00000000000000000000000000000000') { + update reply { + Filter-Id += "fail 6" + } +} + +if ("%{Client-IP-Address}" != 127.0.0.1) { + update reply { + Filter-Id += "fail 7a" + } +} + +if ("%{Packet-Src-IP-Address}" != 127.0.0.1) { + update reply { + Filter-Id += "fail 7b" + } +} + +if ("%{Packet-Dst-IP-Address}" != 127.0.0.1) { + update reply { + Filter-Id += "fail 8" + } +} + +# Can't have both... +if ("%{Packet-Src-IPv6-Address}" != '') { + update reply { + Filter-Id += "fail 9" + } +} + +if ("%{Packet-Dst-IPv6-Address}" != '') { + update reply { + Filter-Id += "fail 10" + } +} + +if ("%{Packet-Src-Port}" != '18120') { + update reply { + Filter-Id += "fail 11" + } +} + +if ("%{Packet-Dst-Port}" != '1812') { + update reply { + Filter-Id += "fail 12" + } +} + + +# We should allow the user to overload virtual attributes +update request { + Client-Shortname := 'my_test_client' +} + +if ("%{Client-Shortname}" != 'my_test_client') { + update reply { + Filter-Id += "fail 13" + } +} + +# Operations on virtual attributes should be the same as on real ones +if ("%{Virtual-Server[0]}" != 'default') { + update reply { + Filter-Id += "fail 14" + } +} + +if ("%{Virtual-Server[*]}" != 'default') { + update reply { + Filter-Id += "fail 15" + } +} + +if ("%{Virtual-Server[#]}" != 1) { + update reply { + Filter-Id += "fail 16" + } +} |