vtest "Test ACLs"

server s1 {
	rxreq
	expect req.url == "/"
	txresp -body "1111\n"
	rxreq
	expect req.url == "/foo"
	txresp -body "2222\n"
} -start

vinyl v1 -arg "-p feature=+trace"

vinyl v1 -errvcl {Name acl1 must have type 'acl'.} {
	sub vcl_recv {
		if (client.ip ~ acl1) {
			set req.url = "/";
		}
	}

	backend acl1 {
		.host = "${localhost}";
	}
}

vinyl v1 -vcl+backend {
	sub vcl_recv {
		if (client.ip ~ acl1) {
			set req.url = "/";
		}
	}

	acl acl1 {
		"${localhost}";
	}

	sub vcl_deliver {
		set resp.http.acl = acl1;
	}
} -start

client c1 {
	txreq -url "/foo"
	rxresp
	expect resp.status == 200
	expect resp.http.acl == acl1
} -run

vinyl v1 -vcl+backend {
	acl acl1 {
		! "${localhost}";
		"0.0.0.0" / 0;
		"::" / 0;
	}

	sub vcl_recv {
		if (client.ip ~ acl1) {
			set req.url = "/";
		}
	}
	sub vcl_deliver {
		set resp.http.acl = acl1;
	}
}

client c1 -run

vinyl v1 -cliok "param.set vsl_mask -VCL_trace"

vinyl v1 -vcl {
	import std;

	backend dummy None;

	acl acl1 +log -fold -pedantic {
		# bad notation (confusing)
		"1.2.3.4"/24;
		"1.2.3.66"/26;

		# more specific wins
		"1.4.4.0"/22;
		"1.3.4.0"/23;
		"1.3.5.0"/26;
		"1.3.6.0"/25;
		"1.3.6.128"/25;
		"1.3.0.0"/21;
		"1.4.7";
		"1.4.6.0"/24;

		# bad notation (confusing)
		"affe::affe:0304"/120;
		"affe::affe:0342"/122;

		# more specific wins
		"bad:cafe::"/32;
		"bad:cafe::"/31;
	}

	sub vcl_recv {
		return (synth(200));
	}
	sub t {
		if (std.ip(req.http.ip) ~ acl1) { }
	}
	sub vcl_synth {
		# variables would be nice, but not in core (yet?)
		set req.http.ip = "1.2.3.0";	call t;
		set req.http.ip = "1.2.3.63";	call t;
		set req.http.ip = "1.2.3.64";	call t;

		set req.http.ip = "1.3.4.255";	call t;
		set req.http.ip = "1.3.5.0";	call t;
		set req.http.ip = "1.3.5.255";	call t;
		set req.http.ip = "1.3.6.0";	call t;
		set req.http.ip = "1.3.6.140";	call t;
		set req.http.ip = "1.3.7.255";	call t;

		set req.http.ip = "1.4.5.255";	call t;
		set req.http.ip = "1.4.6.64";	call t;
		set req.http.ip = "1.4.7.64";	call t;

		set req.http.ip = "affe::affe:0300";	call t;
		set req.http.ip = "affe::affe:033f";	call t;
		set req.http.ip = "affe::affe:0340";	call t;

		set req.http.ip = "0bad:cafe:1234::1";	call t;
	}
}

logexpect l1 -v v1 -g raw {
	expect * 1007	ReqHeader	{^\Qip: 1.2.3.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.4"/24 fixed: 1.2.3.0/24\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.2.3.63\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.4"/24 fixed: 1.2.3.0/24\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.2.3.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.66"/26 fixed: 1.2.3.64/26\E$}

	expect 1 =	ReqHeader	{^\Qip: 1.3.4.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.4.0"/23\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.5.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.5.0"/26\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.5.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.4.0"/23\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.6.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.6.0"/25\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.6.140\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.6.128"/25\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.7.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}

	expect 1 =	ReqHeader	{^\Qip: 1.4.5.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.4.0"/22\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.4.6.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.6.0"/24\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.4.7.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.7"\E$}

	expect 1 =	ReqHeader	{^\Qip: affe::affe:0300\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "affe::affe:0304"/120 fixed: affe::affe:300/120\E$}
	expect 1 =	ReqHeader	{^\Qip: affe::affe:033f\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "affe::affe:0304"/120 fixed: affe::affe:300/120\E$}
	expect 1 =	ReqHeader	{^\Qip: affe::affe:0340\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "affe::affe:0342"/122 fixed: affe::affe:340/122\E$}
	expect 1 =	ReqHeader	{^\Qip: 0bad:cafe:1234::1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "bad:cafe::"/32\E$}
} -start

client c1 {
	txreq
	rxresp
} -run

logexpect l1 -wait

vinyl v1 -errvcl {Non-zero bits in masked part} {
	import std;

	backend dummy None;

	acl acl1 +pedantic {
		"1.2.3.4"/24;
	}

	sub vcl_recv {
		if (client.ip ~ acl1) {}
	}
}

vinyl v1 -errvcl {Non-zero bits in masked part} {
	import std;

	backend dummy None;

	acl acl1 +pedantic {
		"affe::affe:0304"/120;
	}

	sub vcl_recv {
		if (client.ip ~ acl1) {}
	}
}

# this is both an OK test for pedantic and fold
vinyl v1 -vcl {
	import std;

	backend dummy None;

	// +fold and +pedantic are default
	acl acl1 +log {
		# bad notation (confusing)
		"1.2.3.0"/24;
		"1.2.3.64"/26;

		# all contained in 1.3.0.0/21 and 1.4.4.0/22
		"1.4.4.0"/22;
		"1.3.4.0"/23;
		"1.3.5.0"/26;
		"1.3.6.0"/25;
		"1.3.6.128"/25;
		"1.3.0.0"/21;
		"1.4.7";
		"1.4.6.0"/24;

		# right,left adjacent
		"2.3.2.0"/23;
		"2.3.0.0"/23;
		# left,right adjacent
		"2.3.4.0"/23;
		"2.3.6.0"/23;

		# 12/14 folded, not 10
		"2.10.0.0"/15;
		"2.12.0.0"/15;
		"2.14.0.0"/15;

		# 226/227 folded, not 225
		"2.225.0.0"/16;
		"2.226.0.0"/16;
		"2.227.0.0"/16;

		# phks test case
		"10.0.0.0"/23;
		"10.0.2.0"/23;

		"10.1.0.0"/24;
		"10.1.1.0"/24;

		"10.2.0.0"/25;
		"10.2.0.128"/25;

		# contained
		"bad:cafe::"/32;
		"bad:cafe::"/31;
	}

	sub vcl_recv {
		return (synth(200));
	}
	sub t {
		if (std.ip(req.http.ip) ~ acl1) { }
	}
	sub vcl_synth {
		# variables would be nice, but not in core (yet?)
		set req.http.ip = "1.2.3.0";	call t;
		set req.http.ip = "1.2.3.63";	call t;
		set req.http.ip = "1.2.3.64";	call t;

		set req.http.ip = "1.3.4.255";	call t;
		set req.http.ip = "1.3.5.0";	call t;
		set req.http.ip = "1.3.5.255";	call t;
		set req.http.ip = "1.3.6.0";	call t;
		set req.http.ip = "1.3.6.140";	call t;
		set req.http.ip = "1.3.7.255";	call t;

		set req.http.ip = "1.4.5.255";	call t;
		set req.http.ip = "1.4.6.64";	call t;
		set req.http.ip = "1.4.7.64";	call t;

		set req.http.ip = "2.3.0.0";	call t;
		set req.http.ip = "2.3.5.255";	call t;

		set req.http.ip = "2.2.255.255";call t;
		set req.http.ip = "2.3.8.0";	call t;

		set req.http.ip = "2.9.1.1";	call t;
		set req.http.ip = "2.10.1.1";	call t;
		set req.http.ip = "2.12.0.0";	call t;
		set req.http.ip = "2.15.255.255";call t;
		set req.http.ip = "2.16.1.1";	call t;

		set req.http.ip = "2.224.1.1";	call t;
		set req.http.ip = "2.225.1.1";	call t;
		set req.http.ip = "2.226.1.1";	call t;
		set req.http.ip = "2.227.1.1";	call t;

		set req.http.ip = "10.0.3.255";	call t;
		set req.http.ip = "10.1.1.255";	call t;
		set req.http.ip = "10.2.0.255";	call t;

		set req.http.ip = "0bad:cafe:1234::1";	call t;
	}
}

logexpect l1 -v v1 -g raw {
	expect * 1009	ReqHeader	{^\Qip: 1.2.3.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.0"/24\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.2.3.63\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.0"/24\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.2.3.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.2.3.0"/24\E$}

	expect 1 =	ReqHeader	{^\Qip: 1.3.4.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.5.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.5.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.6.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.6.140\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.3.7.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.3.0.0"/21\E$}

	expect 1 =	ReqHeader	{^\Qip: 1.4.5.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.4.0"/22\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.4.6.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.4.0"/22\E$}
	expect 1 =	ReqHeader	{^\Qip: 1.4.7.64\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "1.4.4.0"/22\E$}

	expect 1 =	ReqHeader	{^\Qip: 2.3.0.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.3.0.0"/21 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 2.3.5.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.3.0.0"/21 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 2.2.255.255\E$$}
	expect 0 =	VCL_acl	{^\QNO_MATCH acl1\E$}
	expect 1 =	ReqHeader	{^\Qip: 2.3.8.0\E$}
	expect 0 =	VCL_acl	{^\QNO_MATCH acl1\E$}

	expect 1 =	ReqHeader	{^\Qip: 2.9.1.1\E$}
	expect 0 =	VCL_acl	{^\QNO_MATCH acl1\E$}
	expect 1 =	ReqHeader	{^\Qip: 2.10.1.1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.10.0.0"/15\E$}
	expect 1 =	ReqHeader	{^\Qip: 2.12.0.0\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.12.0.0"/14 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 2.15.255.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.12.0.0"/14 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 2.16.1.1\E$}
	expect 0 =	VCL_acl	{^\QNO_MATCH acl1\E}

	expect 1 =	ReqHeader	{^\Qip: 2.224.1.1\E$}
	expect 0 =	VCL_acl	{^\QNO_MATCH acl1\E$}
	expect 1 =	ReqHeader	{^\Qip: 2.225.1.1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.225.0.0"/16\E$}
	expect 1 =	ReqHeader	{^\Qip: 2.226.1.1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.226.0.0"/15 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 2.227.1.1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "2.226.0.0"/15 fixed: folded\E}

	expect 1 =	ReqHeader	{^\Qip: 10.0.3.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "10.0.0.0"/22 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 10.1.1.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "10.1.0.0"/23 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 10.2.0.255\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "10.2.0.0"/24 fixed: folded\E}
	expect 1 =	ReqHeader	{^\Qip: 0bad:cafe:1234::1\E$}
	expect 0 =	VCL_acl	{^\QMATCH acl1 "bad:cafe::"/31}
} -start

client c1 {
	txreq
	rxresp
} -run

logexpect l1 -wait

# test +fold(-report)
vinyl v1 -cliexpect ! "fold" {vcl.inline silent << EOF
	vcl 4.1;

	backend dummy None;

	acl acl1 +log +pedantic +fold(-report) {
		"1.2.0.0"/23;
		"1.2.2.0"/24;
		"1.2.3.0"/24;
	}

	sub vcl_recv {
		if (client.ip ~ acl1) {
			return (synth(403));
		}
	}
	EOF
}

vinyl v1 -errvcl "-fold(...) is invalid, use -fold" {
	backend dummy None;

	acl acl1 +log +pedantic -fold(+foo) {
		"1.2.0.0"/23;
	}
}

vinyl v1 -errvcl "The only ACL fold sub-flag is `report`" {
	backend dummy None;

	acl acl1 +log +pedantic +fold(+foo) {
		"1.2.0.0"/23;
	}
}
