Commit 7006bbcc authored by Geoff Simmons's avatar Geoff Simmons

initial commit, implements and tests all methods except hosts.explain()

parents
Pipeline #104 skipped
((nil . ((indent-tabs-mode . t)))
(c-mode . ((c-file-style . "BSD"))))
Makefile
Makefile.in
.deps/
.libs/
*.o
*.lo
*.la
*~
*.[1-9]
/aclocal.m4
/autom4te.cache/
/compile
/config.guess
/config.h
/config.h.in
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
/m4/libtool.m4
/m4/lt*.m4
/src/vcc_if.c
/src/vcc_if.h
/src/vmod_*rst
CONTRIBUTING
============
To contribute code or documentation, submit a pull request at the
`source repository website
<https://code.uplex.de/uplex-varnish/libvmod-hoailona>`_.
If you have a problem or discover a bug, you can post an `issue
<https://code.uplex.de/uplex-varnish/libvmod-hoailona/issues>`_ at
the website. You can also write to <varnish-support@uplex.de>.
For developers
--------------
The build specifies C99 conformance, all compiler warnings turned on,
and all warnings considered errors (compiler options ``-std=c99
-Werror -Wall -Wextra``).
By default, ``CFLAGS`` is set to ``-g -O2``, so that symbols are
included in the shared library, and optimization is at level
``O2``. To change or disable these options, set ``CFLAGS`` explicitly
before calling ``make`` (it may be set to the empty string).
For development/debugging cycles, the ``configure`` option
``--enable-debugging`` is recommended (off by default). This will turn
off optimizations and function inlining, so that a debugger will step
through the code as expected.
To run the VTC test cases in ``src/test`` (as when ``make check`` is
invoked), you must have the VMOD ``blobcode`` installed.
* source repository: https://code.uplex.de/uplex-varnish/libvmod-hoailona
* VMOD blobcode: https://code.uplex.de/uplex-varnish/libvmod-blobcode
See LICENSE for details.
INSTALLATION
============
The VMOD is built against a Varnish installation, and the autotools
use ``pkg-config(1)`` to locate the necessary header files and other
resources. This sequence will install the VMOD::
> ./autogen.sh # for builds from the git repo
> ./configure
> make
> make check # to run unit tests in src/tests/*.vtc
> sudo make install
If you have installed Varnish in a non-standard directory, call
``autogen.sh`` and ``configure`` with the ``PKG_CONFIG_PATH``
environment variable pointing to the appropriate path. For example,
when varnishd configure was called with ``--prefix=$PREFIX``, use::
> PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
> export PKG_CONFIG_PATH
By default, the vmod ``configure`` script installs the vmod in
the same directory as Varnish, determined via ``pkg-config(1)``. The
vmod installation directory can be overridden by passing the
``VMOD_DIR`` variable to ``configure``.
Other files such as the man-page are installed in the locations
determined by ``configure``, which inherits its default ``--prefix``
setting from Varnish.
Copyright (c) 2017 UPLEX Nils Goroll Systemoptimierung
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
ACLOCAL_AMFLAGS = -I m4 -I ${LIBVARNISHAPI_DATAROOTDIR}/aclocal
SUBDIRS = src
DISTCHECK_CONFIGURE_FLAGS = \
VMOD_DIR='$${libdir}/varnish/vmods'
EXTRA_DIST = README.rst LICENSE COPYING
doc_DATA = README.rst LICENSE COPYING
dist_man_MANS = vmod_hoailona.3
MAINTAINERCLEANFILES = $(dist_man_MANS)
vmod_hoailona.3: README.rst
README.rst: src/vmod_hoailona.man.rst
cp src/vmod_hoailona.man.rst README.rst
src/vmod_hoailona.man.rst:
make -C src vmod_hoailona.man.rst
%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9:
if HAVE_RST2MAN
${RST2MAN} $< $@
else
@echo "========================================"
@echo "You need rst2man installed to make dist"
@echo "========================================"
@false
endif
..
.. NB: This file is machine generated, DO NOT EDIT!
..
.. Edit vmod.vcc and run make instead
..
.. role:: ref(emphasis)
.. _vmod_hoailona(3):
=============
vmod_hoailona
=============
----------------------------------------
Akamai SecureHD Token Authorization VMOD
----------------------------------------
:Manual section: 3
SYNOPSIS
========
import hoailona [from "path"] ;
::
new OBJECT = ...
DESCRIPTION
===========
This Varnish Module (VMOD) ...
CONTENTS
========
* policy(PRIV_TASK, ENUM {OPEN,DENY,TOKEN}, DURATION, STRING, BLOB, INT)
* hosts()
* STRING version()
.. _obj_policy:
policy
------
::
new OBJ = policy(PRIV_TASK, ENUM {OPEN,DENY,TOKEN} type, DURATION ttl, STRING description=0, BLOB secret=0, INT start_offset=0)
.. _obj_hosts:
hosts
-----
::
new OBJ = hosts()
.. _func_hosts.add:
hosts.add
---------
::
VOID hosts.add(PRIV_TASK, STRING host, STRING policy, STRING path=0, STRING description=0)
# $Method adds are done
.. _func_hosts.policy:
hosts.policy
------------
::
INT hosts.policy(PRIV_TASK, STRING host, STRING path)
.. _func_hosts.token:
hosts.token
-----------
::
STRING hosts.token(PRIV_TASK, STRING acl=0, DURATION ttl=0, STRING data=0)
.. _func_hosts.secret:
hosts.secret
------------
::
BLOB hosts.secret(PRIV_TASK)
.. _func_hosts.explain:
hosts.explain
-------------
::
STRING hosts.explain(PRIV_TASK)
.. _func_version:
version
-------
::
STRING version()
Returns the version string for this VMOD.
Example::
std.log("Using VMOD hoailona version " + hoailona.version());
ERRORS
======
...
REQUIREMENTS
============
This VMOD requires Varnish ...
LIMITATIONS
===========
...
INSTALLATION
============
See ``INSTALL``.
SEE ALSO
========
* varnishd(1)
* vcl(7)
COPYRIGHT
=========
::
This document is licensed under the same conditions
as the libvmod-hoailona project. See LICENSE for details.
Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#!/bin/sh
warn() {
echo "WARNING: $@" 1>&2
}
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
LIBTOOLIZE=libtoolize
;;
Linux)
LIBTOOLIZE=libtoolize
;;
SunOS)
LIBTOOLIZE=libtoolize
;;
*)
warn "unrecognized platform:" `uname -s`
LIBTOOLIZE=libtoolize
esac
automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'`
if [ -z "$automake_version" ] ; then
warn "unable to determine automake version"
else
case $automake_version in
0.*|1.[0-8]|1.[0-8][.-]*)
warn "automake ($automake_version) detected; 1.9 or newer recommended"
;;
*)
;;
esac
fi
# check for varnishapi.m4 in custom paths
dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null)
if [ -z "$dataroot" ] ; then
cat >&2 <<'EOF'
Package varnishapi was not found in the pkg-config search path.
Perhaps you should add the directory containing `varnishapi.pc'
to the PKG_CONFIG_PATH environment variable
EOF
exit 1
fi
set -ex
aclocal -I m4 -I ${dataroot}/aclocal
$LIBTOOLIZE --copy --force
autoheader
automake --add-missing --copy --foreign
autoconf
AC_PREREQ(2.59)
AC_COPYRIGHT([Copyright (c) 2017 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([libvmod-hoailona], [trunk])
AC_CONFIG_MACRO_DIR([m4])
m4_ifndef([VARNISH_VMOD_INCLUDES], AC_MSG_ERROR([Need varnish.m4 -- see README.rst]))
AC_CONFIG_SRCDIR(src/vmod_hoailona.vcc)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([foreign])
AC_GNU_SOURCE
AC_PROG_CC
AC_PROG_CC_STDC
AC_PROG_CC_C99
if test "x$ac_cv_prog_cc_c99" = xno; then
AC_MSG_ERROR([Could not find a C99 compatible compiler])
fi
AC_PROG_CPP
AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
LT_INIT([disable-static])
AC_PROG_INSTALL
AC_PROG_LIBTOOL
AC_PROG_MAKE_SET
# Check for rst utilities
AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no")
if test "x$RST2MAN" = "xno"; then
AC_MSG_WARN([rst2man not found - not building man pages])
fi
AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"])
# Checks for header files.
AC_HEADER_STDC
# Checks for C sources
# Checks for libtool
# backwards compat with older pkg-config
# - pull in AC_DEFUN from pkg.m4
m4_ifndef([PKG_CHECK_VAR], [
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# -------------------------------------------
# Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])# PKG_CHECK_VAR
])
PKG_CHECK_MODULES([libvarnishapi], [varnishapi])
PKG_CHECK_VAR([LIBVARNISHAPI_PREFIX], [varnishapi], [prefix])
PKG_CHECK_VAR([LIBVARNISHAPI_DATAROOTDIR], [varnishapi], [datarootdir])
PKG_CHECK_VAR([LIBVARNISHAPI_BINDIR], [varnishapi], [bindir])
PKG_CHECK_VAR([LIBVARNISHAPI_SBINDIR], [varnishapi], [sbindir])
AC_SUBST([LIBVARNISHAPI_DATAROOTDIR])
# Varnish include files tree
VARNISH_VMOD_INCLUDES
VARNISH_VMOD_DIR
VARNISH_VMODTOOL
# inherit the prefix from Varnish.
# acessing ac_ variable because AC_PREFIX_DEFAULT acts too early
ac_default_prefix=$LIBVARNISHAPI_PREFIX
AC_PATH_PROG([VARNISHTEST], [varnishtest], [],
[$LIBVARNISHAPI_BINDIR:$LIBVARNISHAPI_SBINDIR:$PATH])
AC_PATH_PROG([VARNISHD], [varnishd], [],
[$LIBVARNISHAPI_SBINDIR:$LIBVARNISHAPI_BINDIR:$PATH])
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is YES)]),
[],
[enable_stack_protector=yes])
if test "x$enable_stack_protector" != "xno"; then
AX_CHECK_COMPILE_FLAG([-fstack-protector],
AX_CHECK_LINK_FLAG([-fstack-protector],
[CFLAGS="${CFLAGS} -fstack-protector"], [], []),
[], [])
fi
# --enable-debugging
AC_ARG_ENABLE(debugging,
AS_HELP_STRING([--enable-debugging],[enable debugging (default is NO)]),
[],
[enable_debugging=no])
# AC_PROG_CC sets CFLAGS to '-g -O2' unless it is already set, so
# there's no need to add -g. Disable or change by explicitly setting
# CFLAGS. If this option is enabled, then -Og or -O0 becomes the last
# optimization option, and hence takes precedence.
if test "x$enable_debugging" != "xno"; then
CFLAGS="${CFLAGS} -fno-inline"
AX_CHECK_COMPILE_FLAG([-Og],
[CFLAGS="${CFLAGS} -Og"],
[CFLAGS="${CFLAGS} -O0"],
[])
fi
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
AM_CPPFLAGS = @VMOD_INCLUDES@ -Wall -Werror -Wextra -std=c99
vmoddir = @VMOD_DIR@
vmod_LTLIBRARIES = libvmod_hoailona.la
libvmod_hoailona_la_LDFLAGS = -module -export-dynamic -avoid-version -shared
libvmod_hoailona_la_SOURCES = \
vmod_hoailona.c \
vmod_hoailona.h \
vtree.h \
pattern.c
nodist_libvmod_hoailona_la_SOURCES = \
vcc_if.c \
vcc_if.h
vmod_hoailona.lo: vcc_if.c vcc_if.h
vcc_if.c: vcc_if.h
vcc_if.h vmod_hoailona.man.rst: @VMODTOOL@ $(top_srcdir)/src/vmod_hoailona.vcc
@VMODTOOL@ $(top_srcdir)/src/vmod_hoailona.vcc
VMOD_TESTS = $(top_srcdir)/src/tests/*.vtc
.PHONY: $(VMOD_TESTS)
$(top_srcdir)/src/tests/*.vtc: libvmod_hoailona.la
@VARNISHTEST@ -Dvarnishd=@VARNISHD@ -Dvmod_topbuild=$(abs_top_builddir) $@
check: $(VMOD_TESTS)
EXTRA_DIST = \
vmod_hoailona.vcc \
$(VMOD_TESTS)
CLEANFILES = \
$(builddir)/vcc_if.c \
$(builddir)/vcc_if.h \
$(builddir)/vmod_hoailona.rst \
$(builddir)/vmod_hoailona.man.rst
/*-
* Copyright 2016 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "vmod_hoailona.h"
#include "cache/cache.h"
static vre_t *chars = NULL, *dots = NULL, *stars = NULL, *meta = NULL;
/*
* This comparator implements the "specificness" relation between paths
* assigned to a host according the Akamai SecureHD world. It is used as
* the comparator for red-black trees assigned to hosts.
*
* ass_a < ass_b, and hence path_cmp() returns <0, iff the path assigned
* in ass_a is less specific than the path assigned in ass_b.
*
* Path A is less specific than path B if:
* A has fewer slashes than B,
* else if A has '...' and B does not
* else if A has more *'s than B
* else if A is shorter than B
* else if strcmp(A, B) < 0.
*
* VRB_FIND and VRB_INSERT are only called during vcl_init, so the
* comparator is not on the critical performance path.
*/
int
path_cmp(const struct assignment * restrict const ass_a,
const struct assignment * restrict const ass_b)
{
char *a = ass_a->pattern->path, *b = ass_b->pattern->path, *p;
size_t aslashes = 0, bslashes = 0, astars = 0, bstars = 0, alen = 0,
blen = 0, cmp = 0;
int adots = 0, bdots = 0;
p = a;
while (*p) {
if (*p == '/')
aslashes++;
else if (*p == '*')
astars++;
else if (!adots && *p == '.'
&& (adots = *(p + 1) == '.' && *(p + 2) == '.')) {
p += 3;
alen += 3;
continue;
}
alen++;
p++;
}
p = b;
while (*p) {
if (cmp == 0 && blen < alen)
cmp = a[blen] - b[blen];
if (*p == '/')
bslashes++;
else if (*p == '*')
bstars++;
else if (!bdots && *p == '.'
&& (bdots = *(p + 1) == '.' && *(p + 2) == '.')) {
p += 3;
blen += 3;
continue;
}
blen++;
p++;
}
if (aslashes != bslashes)
return bslashes - aslashes;
if (adots != bdots)
return adots - bdots;
if (astars != bstars)
return astars - bstars;
if (alen != blen)
return blen - alen;
return cmp;
}
VRB_GENERATE(assign_tree, assignment, entry, path_cmp)
void
validation_init(void)
{
VRT_re_init((void **)&chars,
"([^A-Za-z0-9 _\\-~.%:/\\[\\]@!$&()*+,;=]+)");
AN(chars);
VRT_re_init((void**)&dots, "([^/]\\.\\.\\.[^/]*|[^/]*\\.\\.\\.[^/])");
AN(dots);
VRT_re_init((void **)&stars, "(.?\\*{2,}.?)");
AN(stars);
VRT_re_init((void **)&meta, "([[\\]$()+])");
AN(meta);
}
const char *
valid(VRT_CTX, const char * restrict const path)
{
char *errmsg = NULL;
int r, ovector[6];
size_t len;
AN(path);
len = strlen(path);
memset(ovector, 0, sizeof(ovector));
r = VRE_exec(chars, path, len, 0, 0, ovector, 6, NULL);
if (r >= 0) {
if ((errmsg = WS_Printf(ctx->ws,
"invalid character(s) in pattern: %.*s",
ovector[3] - ovector[2],
path + ovector[2]))
== NULL)
return "invalid character(s) in pattern";
return errmsg;
}
memset(ovector, 0, sizeof(ovector));
r = VRE_exec(dots, path, len, 0, 0, ovector, 6, NULL);
if (r >= 0) {
if ((errmsg = WS_Printf(ctx->ws,
"... must only be used before and "
"after slashes: %.*s",
ovector[3] - ovector[2],
path + ovector[2]))
== NULL)
return "... must only be used before and after slashes";
return errmsg;
}
memset(ovector, 0, sizeof(ovector));
r = VRE_exec(stars, path, len, 0, 0, ovector, 6, NULL);
if (r >= 0) {
if ((errmsg = WS_Printf(ctx->ws,
"more than one *: %.*s",
ovector[3] - ovector[2],
path + ovector[2]))
== NULL)
return "more than one *";
return errmsg;
}
return NULL;
}
vre_t *
pattern2re(VRT_CTX, const char * restrict const path)
{
struct vsb *regex;
const char *esc, *p, *end, *regex_errstr;
char *snap;
int end_anchor = 1, regex_erroffset;
vre_t *re;
AN(path);
snap = WS_Snapshot(ctx->ws);
esc = VRT_regsub(ctx, 1, path, meta, "\\\1");
if (WS_Overflowed(ctx->ws)) {
WS_Reset(ctx->ws, snap);
return NULL;
}
regex = VSB_new_auto();
CHECK_OBJ_NOTNULL(regex, VSB_MAGIC);
p = esc;
end = esc + strlen(esc);
if (end - esc > 3 && esc[0] == '.' && esc[1] == '.' && esc[2] == '.') {
VSB_putc(regex, '.');
p = esc + 3;
}
else if (end - esc > 1)
VSB_putc(regex, '^');
while (*p) {
if (*p == '*') {
VSB_cat(regex, "[^/]+");
p++;
continue;
}
if (*p == '.') {
if (end - p >= 3 && p[1] == '.' && p[2] == '.') {
if (p[3] == '\0') {
end_anchor = 0;
break;
}
else {
VSB_cat(regex, ".+");
p += 3;
continue;
}
}
else {
VSB_cat(regex, "\\.");
p++;
continue;
}
}
else {
VSB_putc(regex, *p);
p++;
}
}
if (end_anchor)
VSB_putc(regex, '$');
VSB_finish(regex);
re = VRE_compile(VSB_data(regex), 0, &regex_errstr, &regex_erroffset);
if (ctx->vsl)
VSLb(ctx->vsl, SLT_VCL_Log, "Compiled %s for %s",
VSB_data(regex), path);
else
VSL(SLT_VCL_Log, 0, "Compiled %s for %s", VSB_data(regex),
path);
assert(re != NULL && regex_errstr == NULL);
VSB_destroy(&regex);
WS_Reset(ctx->ws, snap);
return re;
}
# looks like -*- vcl -*-
varnishtest "vcl.use and .discard, and version string"
server s1 {} -start
varnish v1 -vcl+backend {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
} -start
varnish v1 -vcl+backend {}
varnish v1 -cli "vcl.list"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.show vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.discard vcl1"
varnish v1 -cli "vcl.list"
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.x-version = hoailona.version();
if (!resp.http.x-version) {
set resp.status = 500;
}
return(deliver);
}
}
client c1 {
txreq -url "/"
rxresp
expect resp.status == 200
expect resp.http.x-version ~ "^.+$"
} -run
# looks like -*- vcl -*-
varnishtest "hosts.add()"
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new r = hoailona.policy(DENY, 3h);
new h = hoailona.hosts();
h.add("example.com", "q", "/*/...");
h.add(host="example.org", policy="p");
h.add("example.com", "r", ".../x/y");
h.add("varnish-cache.org", "r", "/x/y/./");
}
} -start
varnish v1 -errvcl {vmod hoailona error: host is empty in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("", "p", "/foo/bar");
}
}
varnish v1 -errvcl {vmod hoailona error: policy is empty in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "", "/foo/bar");
}
}
varnish v1 -errvcl {vmod hoailona error: path is set but empty in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", "");
}
}
varnish v1 -errvcl {vmod hoailona error: path is set but empty in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", "");
}
}
varnish v1 -errvcl {vmod hoailona error: path ""<>?\^`| in h.add(): invalid character(s) in pattern: ""<>?\^`|} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", {"""<>?\^`|"});
}
}
varnish v1 -errvcl {vmod hoailona error: path /x... in h.add(): ... must only be used before and after slashes: x...} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", "/x...");
}
}
varnish v1 -errvcl {vmod hoailona error: path /.../...x in h.add(): ... must only be used before and after slashes: ...x} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", "/.../...x");
}
}
varnish v1 -errvcl {vmod hoailona error: path /x/**/y in h.add(): more than one *: /**/} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p", "/x/**/y");
}
}
varnish v1 -errvcl {vmod hoailona error: No policy objects created before calling h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new h = hoailona.hosts();
h.add("example.com", "p", "/x/y");
}
}
varnish v1 -errvcl {vmod hoailona error: Policy object q not found in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "q", "/x/y");
}
}
varnish v1 -errvcl {vmod hoailona error: Policy p already set globally for host example.com in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p");
h.add("example.com", "q");
}
}
varnish v1 -errvcl {vmod hoailona error: Path-specific policies already set for host example.com in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q");
}
}
varnish v1 -errvcl {vmod hoailona error: Policy p already assigned for host example.com and path /foo/bar in h.add()} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/bar");
}
}
# The following assignments are legal since the paths don't compare as
# equal.
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/bar/foo");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/bar/baz");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/b/r");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/...");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", ".../bar");
h.add("example.com", "q", "/foo/bar");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/*");
}
}
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/barz");
}
}
# Test valid hostnames
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("*example.com", "p");
h.add("EXAMPLE-EXAMPLE.EXAMPLE.COM", "p");
h.add("abcdefg-hijklmnop.qrstuv-wxyz.ABCDEFG-HIJKLMNOP.QRSTUV-WXYZ.0123-456.789", "p");
}
}
varnish v1 -errvcl {invalid hostname -example.com: may not begin with - or .} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("-example.com", "p");
}
}
varnish v1 -errvcl {invalid hostname .example.com: may not begin with - or .} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add(".example.com", "p");
}
}
varnish v1 -errvcl {invalid hostname *.*.example.com: illegal characters} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("*.*.example.com", "p");
}
}
varnish v1 -errvcl {invalid hostname example-%.com: illegal characters} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example-%.com", "p");
}
}
varnish v1 -errvcl {invalid hostname example-ä.com: illegal characters} {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example-ä.com", "p");
}
}
# looks like -*- vcl -*-
varnishtest "hosts.policy()"
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new r = hoailona.policy(DENY, 3h);
new h = hoailona.hosts();
h.add("example.com", "q", "/foo/bar");
h.add(host="example.org", policy="p");
h.add("example.com", "r", "/bar/foo");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.org", "/quux/4711");
set resp.http.p3 = h.policy("example.com", "/bar/foo");
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.p2 == "1"
expect resp.http.p3 == "0"
} -run
# Examples from Akamai docs
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/z/...");
h.add("example.com", "q", "/i/*/master.m3u8");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com",
"/z/foo/file1.mp4/manifest.f4m");
set resp.http.p2 = h.policy("example.com",
"/z/foo/foo2/file2.mp4/manifest.f4m");
set resp.http.p3 = h.policy("example.com",
"/i/file1_,1,2,3,.mp4.csmil/master.m3u8");
set resp.http.p4 = h.policy("example.com",
"/i/file1.mp4/master.m3u8");
set resp.http.p5 = h.policy("example.com",
"/i/show1/file1_,1,2,3,.mp4.csmil/master.m3u8");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "1"
expect resp.http.p2 == "1"
expect resp.http.p3 == "2"
expect resp.http.p4 == "2"
expect resp.http.p5 == "-1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p");
h.add("*.example.org", "q");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("foo.example.org", "/quux/4711");
set resp.http.p3 = h.policy("example.org", "/bar/foo");
set resp.http.p4 = h.policy("bar.example.org", "/bar/foo");
set resp.http.p5 = h.policy("bar.example.com", "/bar/foo");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "1"
expect resp.http.p2 == "2"
expect resp.http.p3 == "-1"
expect resp.http.p4 == "2"
expect resp.http.p5 == "-1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar");
h.add("example.com", "q", "/foo/bar/baz");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/foo/bar/baz");
set resp.http.p3 = h.policy("example.com", "/foo/bar/baz/quux");
set resp.http.p4 = h.policy("example.com", "/foo/bar/quux");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "1"
expect resp.http.p2 == "2"
expect resp.http.p3 == "-1"
expect resp.http.p4 == "-1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/bar/...");
h.add("example.com", "q", "/foo/bar/baz/...");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/foo/bar/baz");
set resp.http.p3 = h.policy("example.com", "/foo/bar/baz/quux");
set resp.http.p4 = h.policy("example.com", "/foo/bar/quux");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "1"
expect resp.http.p3 == "2"
expect resp.http.p4 == "1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/*/bar");
h.add("example.com", "q", "/foo/*/baz");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/foo/baz");
set resp.http.p3 = h.policy("example.com", "/foo//bar");
set resp.http.p4 = h.policy("example.com", "/foo//baz");
set resp.http.p5 = h.policy("example.com", "/foo/bar/baz");
set resp.http.p6 = h.policy("example.com", "/foo/baz/bar");
set resp.http.p7 = h.policy("example.com", "/foo/quux/baz/bar");
set resp.http.p8 = h.policy("example.com", "/foo/quux/bar/baz");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "-1"
expect resp.http.p3 == "-1"
expect resp.http.p4 == "-1"
expect resp.http.p5 == "2"
expect resp.http.p6 == "1"
expect resp.http.p7 == "-1"
expect resp.http.p8 == "-1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/*/bar/...");
h.add("example.com", "q", "/foo/*/baz/...");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/foo/baz");
set resp.http.p3 = h.policy("example.com", "/foo/bar/baz");
set resp.http.p4 = h.policy("example.com", "/foo/baz/bar");
set resp.http.p5 = h.policy("example.com", "/foo/quux/baz/bar");
set resp.http.p6 = h.policy("example.com", "/foo/quux/bar/baz");
set resp.http.p7 = h.policy("example.com",
"/foo/bar/baz/quux/4711");
set resp.http.p8 = h.policy("example.com",
"/foo/baz/bar/quux/4711");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "-1"
expect resp.http.p3 == "-1"
expect resp.http.p4 == "-1"
expect resp.http.p5 == "2"
expect resp.http.p6 == "1"
expect resp.http.p7 == "2"
expect resp.http.p8 == "1"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", ".../foo/bar");
h.add("example.com", "q", ".../baz/foo/bar");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/baz/foo/bar");
set resp.http.p3 = h.policy("example.com", "/quux/foo/bar");
set resp.http.p4 = h.policy("example.com", "/quux/baz/foo/bar");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "1"
expect resp.http.p3 == "1"
expect resp.http.p4 == "2"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/.../bar");
h.add("example.com", "q", "/foo/.../baz/bar");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.p2 = h.policy("example.com", "/foo/baz/bar");
set resp.http.p3 = h.policy("example.com", "/foo/quux/bar");
set resp.http.p4 = h.policy("example.com", "/foo/quux/baz/bar");
set resp.http.p5 = h.policy("example.com",
"/foo/quux/quux/bar");
set resp.http.p6 = h.policy("example.com",
"/foo/quux/quux/baz/bar");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "1"
expect resp.http.p3 == "1"
expect resp.http.p4 == "2"
expect resp.http.p5 == "1"
expect resp.http.p6 == "2"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new q = hoailona.policy(TOKEN, 2h);
new h = hoailona.hosts();
h.add("example.com", "p", "/foo/./bar");
h.add("example.com", "q", "/foo/../bar");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/baz/bar");
set resp.http.p2 = h.policy("example.com", "/foo/ba/bar");
set resp.http.p3 = h.policy("example.com", "/foo/b/bar");
set resp.http.p4 = h.policy("example.com", "/foo/./bar");
set resp.http.p5 = h.policy("example.com", "/foo/../bar");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-1"
expect resp.http.p2 == "-1"
expect resp.http.p3 == "-1"
expect resp.http.p4 == "1"
expect resp.http.p5 == "2"
} -run
# Usage tests
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy(req.http.Unset, "/foo");
set resp.http.p2 = h.policy("", "/foo");
set resp.http.p3 = h.policy("example.com", req.http.Unset);
set resp.http.p4 = h.policy("example.com", "");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "-2"
expect resp.http.p2 == "-2"
expect resp.http.p3 == "-2"
expect resp.http.p4 == "-2"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod hoailona error: host is empty in h.policy..$"
expect * = VCL_Error "^vmod hoailona error: host is empty in h.policy..$"
expect * = VCL_Error "^vmod hoailona error: path is empty in h.policy..$"
expect * = VCL_Error "^vmod hoailona error: path is empty in h.policy..$"
expect * = End
} -run
# looks like -*- vcl -*-
varnishtest "policy object constructor"
# VMOD blobcode must be installed
# Doesn't test much, just make sure nothing crashes
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import blobcode;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p1 = hoailona.policy(OPEN, 1h);
new p2 = hoailona.policy(TOKEN, 2h);
new p3 = hoailona.policy(DENY, 3h);
new p4 = hoailona.policy(TOKEN, 1h, description="policy p4");
new p5 = hoailona.policy(OPEN, 1h, start_offset= 0-10);
new p6 = hoailona.policy(DENY, 1h,
secret=blobcode.decode(encoded="foo"));
new p7 = hoailona.policy(TOKEN, 1h, "p7",
blobcode.decode(encoded="bar"), 0-30);
}
} -start
varnish v1 -vcl { backend b { .host = "${bad_ip}"; } }
# Runs fini
varnish v1 -cli "vcl.discard vcl1"
# looks like -*- vcl -*-
varnishtest "hosts.secret()"
# VMOD blobcode must be installed
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import blobcode;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 2h,
secret=blobcode.decode(encoded="foo"));
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.s1 = blobcode.encode(blob=h.secret());
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.s1 == "foo"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import blobcode;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new h = hoailona.hosts();
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.s1 = blobcode.encode(blob=h.secret());
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.s1 == ""
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod hoailona error: h.secret.. called before h.policy..$"
expect * = End
} -run
# looks like -*- vcl -*-
varnishtest "hosts.token()"
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import std;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.t1 = h.token();
set resp.http.st = regsub(resp.http.t1, "^st=(\d+).+$", "\1");
set resp.http.exp = regsub(resp.http.t1, "^.+exp=(\d+)$",
"\1");
set resp.http.diff = std.integer(resp.http.exp, 0)
- std.integer(resp.http.st, 0);
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.t1 ~ "^st=\\d+~exp=\\d+$"
expect resp.http.st ~ "^\\d+$"
expect resp.http.exp ~ "^\\d+$"
expect resp.http.diff == "3600"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.t1 = h.token();
set resp.http.t2 = h.token("/*");
set resp.http.t3 = h.token(data="foo=bar");
set resp.http.t4 = h.token(data="foo=bar", acl="/*");
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.t1 ~ "^st=\\d+~exp=\\d+$"
expect resp.http.t2 ~ "^st=\\d+~exp=\\d+~acl=/\\*$"
expect resp.http.t3 ~ "^st=\\d+~exp=\\d+~data=foo=bar$"
expect resp.http.t4 ~ "^st=\\d+~exp=\\d+~acl=/\\*~data=foo=bar$"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import std;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.t1 = h.token(ttl=2h);
set resp.http.st = regsub(resp.http.t1, "^st=(\d+).+$", "\1");
set resp.http.exp = regsub(resp.http.t1, "^.+exp=(\d+)$",
"\1");
set resp.http.diff = std.integer(resp.http.exp, 0)
- std.integer(resp.http.st, 0);
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.t1 ~ "^st=\\d+~exp=\\d+$"
expect resp.http.st ~ "^\\d+$"
expect resp.http.exp ~ "^\\d+$"
expect resp.http.diff == "7200"
} -run
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import std;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 1h, start_offset=0-10);
new h = hoailona.hosts();
h.add("example.com", "p");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.p1 = h.policy("example.com", "/foo/bar");
set resp.http.t1 = h.token();
set resp.http.st = regsub(resp.http.t1, "^st=(\d+).+$", "\1");
set resp.http.exp = regsub(resp.http.t1, "^.+exp=(\d+)$",
"\1");
set resp.http.diff1 = std.integer(resp.http.exp, 0)
- std.integer(resp.http.st, 0);
set resp.http.t2 = h.token(ttl=2h);
set resp.http.st = regsub(resp.http.t2, "^st=(\d+).+$", "\1");
set resp.http.exp = regsub(resp.http.t2, "^.+exp=(\d+)$",
"\1");
set resp.http.diff2 = std.integer(resp.http.exp, 0)
- std.integer(resp.http.st, 0);
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.p1 == "2"
expect resp.http.t1 ~ "^st=\\d+~exp=\\d+$"
expect resp.http.diff1 == "3610"
expect resp.http.t2 ~ "^st=\\d+~exp=\\d+$"
expect resp.http.diff2 == "7210"
} -run
# Usage
varnish v1 -vcl {
import hoailona from "${vmod_topbuild}/src/.libs/libvmod_hoailona.so";
import std;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = hoailona.policy(TOKEN, 1h);
new q = hoailona.policy(OPEN, 1h);
new h = hoailona.hosts();
h.add("example.com", "p");
h.add("example.org", "q");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.t0 = h.token();
# VCC does not accept a negative value for the DURATION,
# so we can't test the validation in the C code.
#
# set resp.http.p1 = h.policy("example.com", "/foo/bar");
# set resp.http.t1 = h.token(ttl=-1s);
set resp.http.p2 = h.policy("example.org", "/foo/bar");
set resp.http.t2 = h.token();
}
}
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.t0 == ""
# expect resp.http.p1 == "2"
# expect resp.http.t1 == ""
expect resp.http.p2 == "1"
expect resp.http.t2 == ""
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod hoailona error: h.token.. called before h.policy..$"
expect * = VCL_Error "^vmod hoailona error: in h.token..: policy q does not specify a token$"
expect * = End
} -run
/*-
* Copyright 2017 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include <stdlib.h>
#include <ctype.h>
#include "vcl.h"
#include "vas.h"
#include "vdef.h"
#include "cache/cache.h"
#include "vsb.h"
#include "vmod_hoailona.h"
#include "vcc_if.h"
#define ERR(ctx, msg) \
errmsg((ctx), "vmod hoailona error: " msg)
#define VERR(ctx, fmt, ...) \
errmsg((ctx), "vmod hoailona error: " fmt, __VA_ARGS__)
#define VERRNOMEM(ctx, fmt, ...) \
VERR((ctx), fmt ", out of space", __VA_ARGS__)
#define ERRNOMEM(ctx, msg) \
ERR((ctx), msg ", out of space")
#define INIT_FINI(ctx) (((ctx)->method & (VCL_MET_INIT | VCL_MET_FINI)) != 0)
#define ILLEGAL(ctx, m) \
ERR((ctx), m " is illegal in vcl_init() and vcl_fini()")
struct host {
unsigned magic;
#define VMOD_HOAILONA_HOST_MAGIC 0x731af58f
struct assign_tree assignments;
VSLIST_ENTRY(host) list;
char *name;
char *description;
struct vmod_hoailona_policy *policy;
size_t len;
};
typedef VSLIST_HEAD(hosthead, host) hosthead_t;
struct vmod_hoailona_hosts {
unsigned magic;
#define VMOD_HOAILONA_HOSTS_MAGIC 0xa3ef1ea9
hosthead_t hosthead;
char *vcl_name;
};
struct policyitem {
VSLIST_ENTRY(policyitem) list;
struct vmod_hoailona_policy *policy;
};
typedef VSLIST_HEAD(policyhead, policyitem) policyhead_t;
struct policy_task {
unsigned magic;
#define VMOD_HOAILONA_POLICY_TASK_MAGIC 0x5fc90249
struct host *host;
struct assignment *assignment;
struct vmod_hoailona_policy *policy;
};
static inline void
WS_Contains(struct ws * const restrict ws, const void * const restrict ptr,
const size_t len)
{
assert((char *)ptr >= ws->s && (char *)(ptr + len) <= ws->e);
}
void
errmsg(VRT_CTX, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (ctx->method == VCL_MET_INIT) {
AN(ctx->msg);
VSB_vprintf(ctx->msg, fmt, args);
VRT_handling(ctx, VCL_RET_FAIL);
}
else if (ctx->vsl)
VSLbv(ctx->vsl, SLT_VCL_Error, fmt, args);
else
/* Should this ever happen in vcl_fini() ... */
VSL(SLT_VCL_Error, 0, fmt, args);
va_end(args);
}
/* Event function */
int __match_proto__(vmod_event_f)
event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
{
(void) ctx;
(void) priv;
if (e == VCL_EVENT_LOAD)
validation_init();
return 0;
}
/* Object policy */
VCL_VOID
vmod_policy__init(VRT_CTX, struct vmod_hoailona_policy **policyp,
const char *vcl_name, struct vmod_priv *init_task,
VCL_ENUM policys, VCL_DURATION ttl, VCL_STRING description,
VCL_BLOB secret, VCL_INT start_offset)
{
struct vmod_hoailona_policy *policy;
policyhead_t *policyhead;
struct policyitem *item;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
AN(policyp);
AZ(*policyp);
AN(vcl_name);
AN(policys);
ALLOC_OBJ(policy, VMOD_HOAILONA_POLICY_MAGIC);
AN(policy);
*policyp = policy;
if (init_task->priv == NULL) {
if ((policyhead = WS_Alloc(ctx->ws, sizeof(policyhead_t)))
== NULL) {
VERRNOMEM(ctx,
"initializing policy list in %s constructor",
vcl_name);
return;
}
VSLIST_INIT(policyhead);
init_task->priv = policyhead;
}
else {
WS_Contains(ctx->ws, init_task->priv, sizeof(policyhead_t));
policyhead = init_task->priv;
}
if ((item = WS_Alloc(ctx->ws, sizeof(struct policyitem))) == NULL) {
VERRNOMEM(ctx,
"allocating policy list item in %s constructor",
vcl_name);
return;
}
item->policy = policy;
VSLIST_INSERT_HEAD(policyhead, item, list);
if (strcmp(policys, "DENY") == 0)
policy->type = DENY;
else if (strcmp(policys, "OPEN") == 0)
policy->type = OPEN;
else if (strcmp(policys, "TOKEN") == 0)
policy->type = TOKEN;
else
WRONG("illegal policy enum");
policy->vcl_name = strdup(vcl_name);
AN(policy->vcl_name);
if (description != NULL)
policy->description = strdup(description);
else
AZ(policy->description);
if (secret != NULL) {
policy->secret = malloc(sizeof(*secret));
AN(policy->secret);
policy->secret->len = secret->len;
policy->secret->priv = malloc(secret->len);
AN(policy->secret->priv);
memcpy(policy->secret->priv, secret->priv, secret->len);
policy->secret->free = NULL;
}
else
AZ(policy->secret);
policy->ttl = ttl;
policy->start_offset = start_offset;
}
VCL_VOID
vmod_policy__fini(struct vmod_hoailona_policy **policyp)
{
struct vmod_hoailona_policy *policy;
policy = *policyp;
*policyp = NULL;
CHECK_OBJ_NOTNULL(policy, VMOD_HOAILONA_POLICY_MAGIC);
if (policy->vcl_name != NULL)
free(policy->vcl_name);
if (policy->description != NULL)
free(policy->description);
if (policy->secret != NULL) {
AN(policy->secret->priv);
free(policy->secret->priv);
free(policy->secret);
}
FREE_OBJ(policy);
}
/* Object hosts */
VCL_VOID
vmod_hosts__init(VRT_CTX, struct vmod_hoailona_hosts **hostsp,
const char *vcl_name)
{
struct vmod_hoailona_hosts *hosts;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(hostsp);
AZ(*hostsp);
AN(vcl_name);
ALLOC_OBJ(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
AN(hosts);
*hostsp = hosts;
hosts->vcl_name = strdup(vcl_name);
AN(hosts->vcl_name);
VSLIST_INIT(&hosts->hosthead);
}
/*
* The hosts finalizer does not clean up policy objects that the hosts
* object might point to internally, varnishd will take care of that by
* running the policy finalizer.
*/
VCL_VOID
vmod_hosts__fini(struct vmod_hoailona_hosts **hostsp)
{
struct vmod_hoailona_hosts *hosts;
struct host *host = NULL;
hosts = *hostsp;
*hostsp = NULL;
CHECK_OBJ_NOTNULL(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
if (hosts->vcl_name != NULL)
free(hosts->vcl_name);
host = VSLIST_FIRST(&hosts->hosthead);
while (host != NULL) {
struct assignment *a;
struct host *next_host = NULL;
CHECK_OBJ_NOTNULL(host, VMOD_HOAILONA_HOST_MAGIC);
AN(host->name);
free(host->name);
if (host->description != NULL)
free(host->description);
a = VRB_MIN(assign_tree, &host->assignments);
while (a != NULL) {
struct assignment *next_ass;
CHECK_OBJ_NOTNULL(a, VMOD_HOAILONA_ASSIGNMENT_MAGIC);
if (a->description != NULL)
free(a->description);
if (a->pattern != NULL) {
CHECK_OBJ(a->pattern,
VMOD_HOAILONA_PATTERN_MAGIC);
if (a->pattern->path != NULL)
free(a->pattern->path);
if (a->pattern->re != NULL)
VRT_re_fini(a->pattern->re);
}
next_ass = VRB_NEXT(assign_tree, a, a);
FREE_OBJ(a);
a = next_ass;
}
next_host = VSLIST_NEXT(host, list);
FREE_OBJ(host);
host = next_host;
}
FREE_OBJ(hosts);
}
static inline int
valid_hostname(VRT_CTX, const char * restrict const hostname)
{
const char *p = hostname;
if (*p == '-' || *p == '.') {
VERR(ctx, "invalid hostname %s: may not begin with - or .",
hostname);
return 0;
}
else if (*p == '*')
p++;
while (*p) {
if (!isalnum(*p) && *p != '-' && *p != '.') {
VERR(ctx, "invalid hostname %s: illegal characters",
hostname);
return 0;
}
p++;
}
return 1;
}
/* XXX only legal in init */
VCL_VOID
vmod_hosts_add(VRT_CTX, struct vmod_hoailona_hosts *hosts,
struct vmod_priv *init_task, VCL_STRING hostname,
VCL_STRING policyname, VCL_STRING path, VCL_STRING description)
{
policyhead_t *policyhead;
struct policyitem *item;
struct vmod_hoailona_policy *policy = NULL;
struct host *host = NULL;
struct pattern *pattern;
struct assignment *assign;
vre_t *re = NULL;
const char *err = NULL;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
CHECK_OBJ_NOTNULL(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
AN(init_task);
if (hostname == NULL || hostname[0] == '\0') {
VERR(ctx, "host is empty in %s.add()", hosts->vcl_name);
return;
}
if (!valid_hostname(ctx, hostname))
return;
if (policyname == NULL || policyname[0] == '\0') {
VERR(ctx, "policy is empty in %s.add()", hosts->vcl_name);
return;
}
if (path != NULL) {
if (path[0] == '\0') {
VERR(ctx, "path is set but empty in %s.add()",
hosts->vcl_name);
return;
}
if ((err = valid(ctx, path)) != NULL) {
VERR(ctx, "path %s in %s.add(): %s", path,
hosts->vcl_name, err);
return;
}
if ((re = pattern2re(ctx, path)) == NULL) {
VERRNOMEM(ctx, "converting path %s to regex in %s.add()",
path, hosts->vcl_name);
return;
}
}
if (init_task->priv == NULL) {
VERR(ctx, "No policy objects created before calling %s.add()",
hosts->vcl_name);
return;
}
WS_Contains(ctx->ws, init_task->priv, sizeof(policyhead_t));
policyhead = init_task->priv;
VSLIST_FOREACH(item, policyhead, list) {
CHECK_OBJ_NOTNULL(item->policy, VMOD_HOAILONA_POLICY_MAGIC);
if (strcmp(policyname, item->policy->vcl_name) == 0) {
policy = item->policy;
break;
}
}
if (policy == NULL) {
VERR(ctx, "Policy object %s not found in %s.add()", policyname,
hosts->vcl_name);
return;
}
VSLIST_FOREACH(host, &hosts->hosthead, list) {
CHECK_OBJ(host, VMOD_HOAILONA_HOST_MAGIC);
if (strcmp(hostname, host->name) == 0)
break;
}
if (host != NULL) {
if (path == NULL && host->policy != NULL) {
CHECK_OBJ(host->policy, VMOD_HOAILONA_POLICY_MAGIC);
AZ(re);
VERR(ctx, "Policy %s already set globally for host %s "
"in %s.add()", host->policy->vcl_name, hostname,
hosts->vcl_name);
return;
}
else if (path == NULL && !VRB_EMPTY(&host->assignments)) {
VERR(ctx, "Path-specific policies already set for "
"host %s in %s.add()", hostname, hosts->vcl_name);
AZ(re);
return;
}
else if (path != NULL && !VRB_EMPTY(&host->assignments)) {
struct pattern tmp_pattern;
struct assignment tmp_assign, *result;
tmp_pattern.path = (void *)path;
tmp_assign.pattern = &tmp_pattern;
if ((result = VRB_FIND(assign_tree, &host->assignments,
&tmp_assign))
!= NULL) {
VERR(ctx, "Policy %s already assigned for host "
"%s and path %s in %s.add()",
result->policy->vcl_name, hostname, path,
hosts->vcl_name);
if (re != NULL)
VRT_re_fini(re);
return;
}
}
}
else {
ALLOC_OBJ(host, VMOD_HOAILONA_HOST_MAGIC);
AN(host);
host->name = strdup(hostname);
host->len = strlen(hostname);
VRB_INIT(&host->assignments);
AZ(host->description);
AZ(host->policy);
VSLIST_INSERT_HEAD(&hosts->hosthead, host, list);
}
if (path == NULL) {
host->policy = policy;
if (description != NULL)
host->description = strdup(description);
return;
}
ALLOC_OBJ(pattern, VMOD_HOAILONA_PATTERN_MAGIC);
AN(pattern);
AN(re);
pattern->re = re;
pattern->path = strdup(path);
ALLOC_OBJ(assign, VMOD_HOAILONA_ASSIGNMENT_MAGIC);
AN(assign);
assign->policy = policy;
assign->pattern = pattern;
if (description != NULL)
assign->description = strdup(description);
AZ(VRB_INSERT(assign_tree, &host->assignments, assign));
}
/* XXX the remaining methods are all illegal in init & fini (?) */
VCL_INT
vmod_hosts_policy(VRT_CTX, struct vmod_hoailona_hosts *hosts,
struct vmod_priv *priv_task, VCL_STRING hostname,
VCL_STRING pathname)
{
struct policy_task *task;
struct host *h, *host = NULL;
struct vmod_hoailona_policy *policy;
size_t hostlen;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
CHECK_OBJ_NOTNULL(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
AN(priv_task);
if (hostname == NULL || hostname[0] == '\0') {
VERR(ctx, "host is empty in %s.policy()", hosts->vcl_name);
return -2;
}
if (pathname == NULL || pathname[0] == '\0') {
VERR(ctx, "path is empty in %s.policy()", hosts->vcl_name);
return -2;
}
if (priv_task->priv == NULL) {
if ((task = WS_Alloc(ctx->ws, sizeof(struct policy_task)))
== NULL) {
VERRNOMEM(ctx, "allocating task data in %s.policy()",
hosts->vcl_name);
return -2;
}
task->magic = VMOD_HOAILONA_POLICY_TASK_MAGIC;
priv_task->priv = task;
priv_task->free = NULL;
}
else {
WS_Contains(ctx->ws, priv_task->priv,
sizeof(struct policy_task));
CAST_OBJ(task, priv_task->priv,
VMOD_HOAILONA_POLICY_TASK_MAGIC);
}
task->host = NULL;
task->assignment = NULL;
task->policy = NULL;
/* XXX optimize */
hostlen = strlen(hostname);
VSLIST_FOREACH(h, &hosts->hosthead, list) {
const char *q, *hs;
CHECK_OBJ(h, VMOD_HOAILONA_HOST_MAGIC);
AN(h->name);
if (*h->name == '*') {
if (h->len - 1 > hostlen)
continue;
hs = h->name + 1;
q = hostname + (hostlen - h->len + 1);
}
else {
if (h->len != hostlen)
continue;
hs = h->name;
q = hostname;
}
if (strcmp(q, hs) == 0) {
host = h;
break;
}
}
if (host == NULL)
return -1;
/* There is a policy XOR there is a list of assignments */
assert((host->policy != NULL && VRB_EMPTY(&host->assignments))
|| (host->policy == NULL && !VRB_EMPTY(&host->assignments)));
if (host->policy != NULL) {
task->host = host;
task->policy = host->policy;
policy = host->policy;
AZ(task->assignment);
}
else {
struct assignment *a, *assignment = NULL;
int pathlen = strlen(pathname);
/* XXX lazily compute and save VRB_MIN for the host
assignments, and re-use it to init the for loop */
VRB_FOREACH(a, assign_tree, &host->assignments) {
int match;
CHECK_OBJ(a, VMOD_HOAILONA_ASSIGNMENT_MAGIC);
CHECK_OBJ_NOTNULL(a->pattern,
VMOD_HOAILONA_PATTERN_MAGIC);
AN(a->pattern->re);
match = VRE_exec(a->pattern->re, pathname, pathlen,
0, 0, NULL, 0, NULL);
if (match >= 0) {
assignment = a;
break;
}
assert(match == VRE_ERROR_NOMATCH);
}
if (assignment == NULL)
return -1;
CHECK_OBJ_NOTNULL(assignment->policy,
VMOD_HOAILONA_POLICY_MAGIC);
task->host = host;
task->assignment = assignment;
policy = assignment->policy;
AZ(task->policy);
}
CHECK_OBJ_NOTNULL(policy, VMOD_HOAILONA_POLICY_MAGIC);
return policy->type;
}
static struct vmod_hoailona_policy *
get_policy(VRT_CTX, const struct vmod_priv * restrict const priv_task,
const char * restrict const vcl_name,
const char * restrict const method)
{
struct policy_task *task;
AN(priv_task);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
if (priv_task->priv == NULL) {
VERR(ctx, "%s.%s() called before %s.policy()", vcl_name, method,
vcl_name);
return NULL;
}
WS_Contains(ctx->ws, priv_task->priv, sizeof(struct policy_task));
CAST_OBJ(task, priv_task->priv, VMOD_HOAILONA_POLICY_TASK_MAGIC);
if (task->policy != NULL) {
CHECK_OBJ(task->policy, VMOD_HOAILONA_POLICY_MAGIC);
return task->policy;
}
CHECK_OBJ_NOTNULL(task->assignment, VMOD_HOAILONA_ASSIGNMENT_MAGIC);
CHECK_OBJ_NOTNULL(task->assignment->policy, VMOD_HOAILONA_POLICY_MAGIC);
return task->assignment->policy;
}
VCL_STRING
vmod_hosts_token(VRT_CTX, struct vmod_hoailona_hosts *hosts,
struct vmod_priv *priv_task, VCL_STRING acl, VCL_DURATION ttl,
VCL_STRING data)
{
struct vmod_hoailona_policy *policy;
VCL_DURATION t;
int st, exp;
VCL_STRING token;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
CHECK_OBJ_NOTNULL(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
if (ttl < 0) {
VERR(ctx, "ttl must not be < 0 in %s.token(): %f",
hosts->vcl_name, ttl);
return NULL;
}
policy = get_policy(ctx, priv_task, hosts->vcl_name, "token");
if (policy == NULL)
return NULL;
if (policy->type != TOKEN) {
VERR(ctx, "in %s.token(): policy %s does not specify a token",
hosts->vcl_name, policy->vcl_name);
return NULL;
}
t = policy->ttl;
if (ttl > 0)
t = ttl;
st = (int) (ctx->now + policy->start_offset);
exp = (int) (ctx->now + t);
if (acl != NULL) {
if (data != NULL)
token = WS_Printf(ctx->ws,
"st=%d~exp=%d~acl=%s~data=%s",
st, exp, acl, data);
else
token = WS_Printf(ctx->ws, "st=%d~exp=%d~acl=%s",
st, exp, acl);
}
else if (data != NULL) {
token = WS_Printf(ctx->ws, "st=%d~exp=%d~data=%s",
st, exp, data);
}
else
token = WS_Printf(ctx->ws, "st=%d~exp=%d", st, exp);
if (token == NULL) {
VERRNOMEM(ctx, "in %s.token()", hosts->vcl_name);
return NULL;
}
return token;
}
VCL_BLOB
vmod_hosts_secret(VRT_CTX, struct vmod_hoailona_hosts *hosts,
struct vmod_priv *priv_task)
{
struct vmod_hoailona_policy *policy;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(hosts, VMOD_HOAILONA_HOSTS_MAGIC);
policy = get_policy(ctx, priv_task, hosts->vcl_name, "secret");
if (policy == NULL)
return NULL;
return policy->secret;
}
VCL_STRING
vmod_hosts_explain(VRT_CTX, struct vmod_hoailona_hosts *hosts,
struct vmod_priv *task)
{
(void) ctx;
(void) hosts;
(void) task;
return NULL;
}
/* Functions */
VCL_STRING
vmod_version(VRT_CTX __attribute__((unused)))
{
return VERSION;
}
/*-
* Copyright 2017 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "vtree.h"
#include "vrt.h"
#include "vre.h"
enum policy_type {
DENY = 0,
OPEN = 1,
TOKEN = 2
};
struct pattern {
unsigned magic;
#define VMOD_HOAILONA_PATTERN_MAGIC 0x1876e01f
char *path;
vre_t *re;
};
struct vmod_hoailona_policy {
unsigned magic;
#define VMOD_HOAILONA_POLICY_MAGIC 0xf729cbfa
char *vcl_name;
char *description;
struct vmod_priv *secret;
VCL_DURATION ttl;
enum policy_type type;
VCL_INT start_offset;
};
struct assignment {
unsigned magic;
#define VMOD_HOAILONA_ASSIGNMENT_MAGIC 0x7523d6e8
VRB_ENTRY(assignment) entry;
struct pattern *pattern;
struct vmod_hoailona_policy *policy;
char *description;
};
VRB_HEAD(assign_tree, assignment);
int path_cmp(const struct assignment * restrict const ass_a,
const struct assignment * restrict const ass_b);
VRB_PROTOTYPE(assign_tree, assignment, entry, path_cmp)
void validation_init(void);
const char * valid(VRT_CTX, const char * restrict const path);
vre_t * pattern2re(VRT_CTX, const char * restrict const path);
#-
# This document is licensed under the same conditions
# as the libvmod-hoailona project. See LICENSE for details.
#
# Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#
$Module hoailona 3 Akamai SecureHD Token Authorization VMOD
::
new OBJECT = ...
DESCRIPTION
===========
This Varnish Module (VMOD) ...
$Object policy(PRIV_TASK, ENUM {OPEN, DENY, TOKEN} type, DURATION ttl,
STRING description=0, BLOB secret=0, INT start_offset=0)
$Object hosts()
$Method VOID .add(PRIV_TASK, STRING host, STRING policy, STRING path=0,
STRING description=0)
# $Method adds are done
$Method INT .policy(PRIV_TASK, STRING host, STRING path)
$Method STRING .token(PRIV_TASK, STRING acl=0, DURATION ttl=0, STRING data=0)
$Method BLOB .secret(PRIV_TASK)
$Method STRING .explain(PRIV_TASK)
$Function STRING version()
Returns the version string for this VMOD.
Example::
std.log("Using VMOD hoailona version " + hoailona.version());
ERRORS
======
...
REQUIREMENTS
============
This VMOD requires Varnish ...
LIMITATIONS
===========
...
INSTALLATION
============
See ``INSTALL``.
SEE ALSO
========
* varnishd(1)
* vcl(7)
$Event event
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/* $FreeBSD: release/9.0.0/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
/*-
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _VTREE_H_
#define _VTREE_H_
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define VSPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define VSPLAY_INITIALIZER(root) \
{ NULL }
#define VSPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define VSPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define VSPLAY_LEFT(elm, field) (elm)->field.spe_left
#define VSPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define VSPLAY_ROOT(head) (head)->sph_root
#define VSPLAY_EMPTY(head) (VSPLAY_ROOT(head) == NULL)
/* VSPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold VSPLAY_{RIGHT,LEFT} */
#define VSPLAY_ROTATE_RIGHT(head, tmp, field) do { \
VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(tmp, field);\
VSPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define VSPLAY_ROTATE_LEFT(head, tmp, field) do { \
VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(tmp, field);\
VSPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define VSPLAY_LINKLEFT(head, tmp, field) do { \
VSPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define VSPLAY_LINKRIGHT(head, tmp, field) do { \
VSPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define VSPLAY_ASSEMBLE(head, node, left, right, field) do { \
VSPLAY_RIGHT(left, field) = VSPLAY_LEFT((head)->sph_root, field);\
VSPLAY_LEFT(right, field) = VSPLAY_RIGHT((head)->sph_root, field);\
VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(node, field);\
VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(node, field);\
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define VSPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_VSPLAY(struct name *, struct type *); \
void name##_VSPLAY_MINMAX(struct name *, int); \
struct type *name##_VSPLAY_INSERT(struct name *, struct type *); \
struct type *name##_VSPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_VSPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (VSPLAY_EMPTY(head)) \
return(NULL); \
name##_VSPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_VSPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_VSPLAY(head, elm); \
if (VSPLAY_RIGHT(elm, field) != NULL) { \
elm = VSPLAY_RIGHT(elm, field); \
while (VSPLAY_LEFT(elm, field) != NULL) { \
elm = VSPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_VSPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_VSPLAY_MINMAX(head, val); \
return (VSPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define VSPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_VSPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (VSPLAY_EMPTY(head)) { \
VSPLAY_LEFT(elm, field) = VSPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_VSPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
VSPLAY_LEFT(elm, field) = VSPLAY_LEFT((head)->sph_root, field);\
VSPLAY_RIGHT(elm, field) = (head)->sph_root; \
VSPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
VSPLAY_RIGHT(elm, field) = VSPLAY_RIGHT((head)->sph_root, field);\
VSPLAY_LEFT(elm, field) = (head)->sph_root; \
VSPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_VSPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (VSPLAY_EMPTY(head)) \
return (NULL); \
name##_VSPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (VSPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = VSPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);\
name##_VSPLAY(head, elm); \
VSPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_VSPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = VSPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
VSPLAY_ROTATE_RIGHT(head, __tmp, field);\
if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
VSPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = VSPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
VSPLAY_ROTATE_LEFT(head, __tmp, field); \
if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
VSPLAY_LINKRIGHT(head, __left, field); \
} \
} \
VSPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_VSPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = VSPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
VSPLAY_ROTATE_RIGHT(head, __tmp, field);\
if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
VSPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = VSPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
VSPLAY_ROTATE_LEFT(head, __tmp, field); \
if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
VSPLAY_LINKRIGHT(head, __left, field); \
} \
} \
VSPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define VSPLAY_NEGINF -1
#define VSPLAY_INF 1
#define VSPLAY_INSERT(name, x, y) name##_VSPLAY_INSERT(x, y)
#define VSPLAY_REMOVE(name, x, y) name##_VSPLAY_REMOVE(x, y)
#define VSPLAY_FIND(name, x, y) name##_VSPLAY_FIND(x, y)
#define VSPLAY_NEXT(name, x, y) name##_VSPLAY_NEXT(x, y)
#define VSPLAY_MIN(name, x) (VSPLAY_EMPTY(x) ? NULL \
: name##_VSPLAY_MIN_MAX(x, VSPLAY_NEGINF))
#define VSPLAY_MAX(name, x) (VSPLAY_EMPTY(x) ? NULL \
: name##_VSPLAY_MIN_MAX(x, VSPLAY_INF))
#define VSPLAY_FOREACH(x, name, head) \
for ((x) = VSPLAY_MIN(name, head); \
(x) != NULL; \
(x) = VSPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define VRB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define VRB_INITIALIZER(root) \
{ NULL }
#define VRB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define VRB_BLACK 0
#define VRB_RED 1
#define VRB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#define VRB_LEFT(elm, field) (elm)->field.rbe_left
#define VRB_RIGHT(elm, field) (elm)->field.rbe_right
#define VRB_PARENT(elm, field) (elm)->field.rbe_parent
#define VRB_COLOR(elm, field) (elm)->field.rbe_color
#define VRB_ROOT(head) (head)->rbh_root
#define VRB_EMPTY(head) (VRB_ROOT(head) == NULL)
#define VRB_SET(elm, parent, field) do { \
VRB_PARENT(elm, field) = parent; \
VRB_LEFT(elm, field) = VRB_RIGHT(elm, field) = NULL; \
VRB_COLOR(elm, field) = VRB_RED; \
} while (/*CONSTCOND*/ 0)
#define VRB_SET_BLACKRED(black, red, field) do { \
VRB_COLOR(black, field) = VRB_BLACK; \
VRB_COLOR(red, field) = VRB_RED; \
} while (/*CONSTCOND*/ 0)
#ifndef VRB_AUGMENT
#define VRB_AUGMENT(x) do {} while (0)
#endif
#define VRB_ROTATE_LEFT(head, elm, tmp, field) do { \
(tmp) = VRB_RIGHT(elm, field); \
if ((VRB_RIGHT(elm, field) = VRB_LEFT(tmp, field)) != NULL) { \
VRB_PARENT(VRB_LEFT(tmp, field), field) = (elm); \
} \
VRB_AUGMENT(elm); \
if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {\
if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field)) \
VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);\
else \
VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);\
} else \
(head)->rbh_root = (tmp); \
VRB_LEFT(tmp, field) = (elm); \
VRB_PARENT(elm, field) = (tmp); \
VRB_AUGMENT(tmp); \
if ((VRB_PARENT(tmp, field))) \
VRB_AUGMENT(VRB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
#define VRB_ROTATE_RIGHT(head, elm, tmp, field) do { \
(tmp) = VRB_LEFT(elm, field); \
if ((VRB_LEFT(elm, field) = VRB_RIGHT(tmp, field)) != NULL) { \
VRB_PARENT(VRB_RIGHT(tmp, field), field) = (elm); \
} \
VRB_AUGMENT(elm); \
if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {\
if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field)) \
VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);\
else \
VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);\
} else \
(head)->rbh_root = (tmp); \
VRB_RIGHT(tmp, field) = (elm); \
VRB_PARENT(elm, field) = (tmp); \
VRB_AUGMENT(tmp); \
if ((VRB_PARENT(tmp, field))) \
VRB_AUGMENT(VRB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define VRB_PROTOTYPE(name, type, field, cmp) \
VRB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
#define VRB_PROTOTYPE_STATIC(name, type, field, cmp) \
VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
#define VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
/*lint -esym(528, name##_VRB_*) */ \
attr void name##_VRB_INSERT_COLOR(struct name *, struct type *); \
attr void name##_VRB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
attr struct type *name##_VRB_REMOVE(struct name *, struct type *); \
attr struct type *name##_VRB_INSERT(struct name *, struct type *); \
attr struct type *name##_VRB_FIND(const struct name *, const struct type *); \
attr struct type *name##_VRB_NFIND(const struct name *, const struct type *); \
attr struct type *name##_VRB_NEXT(struct type *); \
attr struct type *name##_VRB_PREV(struct type *); \
attr struct type *name##_VRB_MINMAX(const struct name *, int); \
\
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define VRB_GENERATE(name, type, field, cmp) \
VRB_GENERATE_INTERNAL(name, type, field, cmp,)
#define VRB_GENERATE_STATIC(name, type, field, cmp) \
VRB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
#define VRB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
attr void \
name##_VRB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = VRB_PARENT(elm, field)) != NULL && \
VRB_COLOR(parent, field) == VRB_RED) { \
gparent = VRB_PARENT(parent, field); \
if (parent == VRB_LEFT(gparent, field)) { \
tmp = VRB_RIGHT(gparent, field); \
if (tmp && VRB_COLOR(tmp, field) == VRB_RED) { \
VRB_COLOR(tmp, field) = VRB_BLACK; \
VRB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (VRB_RIGHT(parent, field) == elm) { \
VRB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
VRB_SET_BLACKRED(parent, gparent, field); \
VRB_ROTATE_RIGHT(head, gparent, tmp, field); \
} else { \
tmp = VRB_LEFT(gparent, field); \
if (tmp && VRB_COLOR(tmp, field) == VRB_RED) { \
VRB_COLOR(tmp, field) = VRB_BLACK; \
VRB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (VRB_LEFT(parent, field) == elm) { \
VRB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
VRB_SET_BLACKRED(parent, gparent, field); \
VRB_ROTATE_LEFT(head, gparent, tmp, field); \
} \
} \
VRB_COLOR(head->rbh_root, field) = VRB_BLACK; \
} \
\
attr void \
name##_VRB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || VRB_COLOR(elm, field) == VRB_BLACK) && \
elm != VRB_ROOT(head)) { \
AN(parent); \
if (VRB_LEFT(parent, field) == elm) { \
tmp = VRB_RIGHT(parent, field); \
if (VRB_COLOR(tmp, field) == VRB_RED) { \
VRB_SET_BLACKRED(tmp, parent, field); \
VRB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = VRB_RIGHT(parent, field); \
} \
if ((VRB_LEFT(tmp, field) == NULL || \
VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
(VRB_RIGHT(tmp, field) == NULL || \
VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
VRB_COLOR(tmp, field) = VRB_RED; \
elm = parent; \
parent = VRB_PARENT(elm, field); \
} else { \
if (VRB_RIGHT(tmp, field) == NULL || \
VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK) {\
struct type *oleft; \
if ((oleft = VRB_LEFT(tmp, field)) \
!= NULL) \
VRB_COLOR(oleft, field) = VRB_BLACK;\
VRB_COLOR(tmp, field) = VRB_RED;\
VRB_ROTATE_RIGHT(head, tmp, oleft, field);\
tmp = VRB_RIGHT(parent, field); \
} \
VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
VRB_COLOR(parent, field) = VRB_BLACK; \
if (VRB_RIGHT(tmp, field)) \
VRB_COLOR(VRB_RIGHT(tmp, field), field) = VRB_BLACK;\
VRB_ROTATE_LEFT(head, parent, tmp, field);\
elm = VRB_ROOT(head); \
break; \
} \
} else { \
tmp = VRB_LEFT(parent, field); \
if (VRB_COLOR(tmp, field) == VRB_RED) { \
VRB_SET_BLACKRED(tmp, parent, field); \
VRB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = VRB_LEFT(parent, field); \
} \
if ((VRB_LEFT(tmp, field) == NULL || \
VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
(VRB_RIGHT(tmp, field) == NULL || \
VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
VRB_COLOR(tmp, field) = VRB_RED; \
elm = parent; \
parent = VRB_PARENT(elm, field); \
} else { \
if (VRB_LEFT(tmp, field) == NULL || \
VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) {\
struct type *oright; \
if ((oright = VRB_RIGHT(tmp, field)) \
!= NULL) \
VRB_COLOR(oright, field) = VRB_BLACK;\
VRB_COLOR(tmp, field) = VRB_RED;\
VRB_ROTATE_LEFT(head, tmp, oright, field);\
tmp = VRB_LEFT(parent, field); \
} \
VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
VRB_COLOR(parent, field) = VRB_BLACK; \
if (VRB_LEFT(tmp, field)) \
VRB_COLOR(VRB_LEFT(tmp, field), field) = VRB_BLACK;\
VRB_ROTATE_RIGHT(head, parent, tmp, field);\
elm = VRB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
VRB_COLOR(elm, field) = VRB_BLACK; \
} \
\
attr struct type * \
name##_VRB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (VRB_LEFT(elm, field) == NULL) \
child = VRB_RIGHT(elm, field); \
else if (VRB_RIGHT(elm, field) == NULL) \
child = VRB_LEFT(elm, field); \
else { \
struct type *left; \
elm = VRB_RIGHT(elm, field); \
while ((left = VRB_LEFT(elm, field)) != NULL) \
elm = left; \
child = VRB_RIGHT(elm, field); \
parent = VRB_PARENT(elm, field); \
color = VRB_COLOR(elm, field); \
if (child) \
VRB_PARENT(child, field) = parent; \
if (parent) { \
if (VRB_LEFT(parent, field) == elm) \
VRB_LEFT(parent, field) = child; \
else \
VRB_RIGHT(parent, field) = child; \
VRB_AUGMENT(parent); \
} else \
VRB_ROOT(head) = child; \
if (VRB_PARENT(elm, field) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (VRB_PARENT(old, field)) { \
if (VRB_LEFT(VRB_PARENT(old, field), field) == old)\
VRB_LEFT(VRB_PARENT(old, field), field) = elm;\
else \
VRB_RIGHT(VRB_PARENT(old, field), field) = elm;\
VRB_AUGMENT(VRB_PARENT(old, field)); \
} else \
VRB_ROOT(head) = elm; \
VRB_PARENT(VRB_LEFT(old, field), field) = elm; \
if (VRB_RIGHT(old, field)) \
VRB_PARENT(VRB_RIGHT(old, field), field) = elm; \
if (parent) { \
left = parent; \
do { \
VRB_AUGMENT(left); \
} while ((left = VRB_PARENT(left, field)) != NULL); \
} \
goto color; \
} \
parent = VRB_PARENT(elm, field); \
color = VRB_COLOR(elm, field); \
if (child) \
VRB_PARENT(child, field) = parent; \
if (parent) { \
if (VRB_LEFT(parent, field) == elm) \
VRB_LEFT(parent, field) = child; \
else \
VRB_RIGHT(parent, field) = child; \
VRB_AUGMENT(parent); \
} else \
VRB_ROOT(head) = child; \
color: \
if (color == VRB_BLACK) { \
name##_VRB_REMOVE_COLOR(head, parent, child); \
} \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
attr struct type * \
name##_VRB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = VRB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = VRB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = VRB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
VRB_SET(elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
VRB_LEFT(parent, field) = elm; \
else \
VRB_RIGHT(parent, field) = elm; \
VRB_AUGMENT(parent); \
} else \
VRB_ROOT(head) = elm; \
name##_VRB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
attr struct type * \
name##_VRB_FIND(const struct name *head, const struct type *elm) \
{ \
struct type *tmp = VRB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = VRB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = VRB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
/* Finds the first node greater than or equal to the search key */ \
attr struct type * \
name##_VRB_NFIND(const struct name *head, const struct type *elm) \
{ \
struct type *tmp = VRB_ROOT(head); \
struct type *res = NULL; \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) { \
res = tmp; \
tmp = VRB_LEFT(tmp, field); \
} \
else if (comp > 0) \
tmp = VRB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (res); \
} \
\
/* ARGSUSED */ \
attr struct type * \
name##_VRB_NEXT(struct type *elm) \
{ \
if (VRB_RIGHT(elm, field)) { \
elm = VRB_RIGHT(elm, field); \
while (VRB_LEFT(elm, field)) \
elm = VRB_LEFT(elm, field); \
} else { \
if (VRB_PARENT(elm, field) && \
(elm == VRB_LEFT(VRB_PARENT(elm, field), field))) \
elm = VRB_PARENT(elm, field); \
else { \
while (VRB_PARENT(elm, field) && \
(elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))\
elm = VRB_PARENT(elm, field); \
elm = VRB_PARENT(elm, field); \
} \
} \
return (elm); \
} \
\
/* ARGSUSED */ \
attr struct type * \
name##_VRB_PREV(struct type *elm) \
{ \
if (VRB_LEFT(elm, field)) { \
elm = VRB_LEFT(elm, field); \
while (VRB_RIGHT(elm, field)) \
elm = VRB_RIGHT(elm, field); \
} else { \
if (VRB_PARENT(elm, field) && \
(elm == VRB_RIGHT(VRB_PARENT(elm, field), field))) \
elm = VRB_PARENT(elm, field); \
else { \
while (VRB_PARENT(elm, field) && \
(elm == VRB_LEFT(VRB_PARENT(elm, field), field)))\
elm = VRB_PARENT(elm, field); \
elm = VRB_PARENT(elm, field); \
} \
} \
return (elm); \
} \
\
attr struct type * \
name##_VRB_MINMAX(const struct name *head, int val) \
{ \
struct type *tmp = VRB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = VRB_LEFT(tmp, field); \
else \
tmp = VRB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define VRB_NEGINF -1
#define VRB_INF 1
#define VRB_INSERT(name, x, y) name##_VRB_INSERT(x, y)
#define VRB_REMOVE(name, x, y) name##_VRB_REMOVE(x, y)
#define VRB_FIND(name, x, y) name##_VRB_FIND(x, y)
#define VRB_NFIND(name, x, y) name##_VRB_NFIND(x, y)
#define VRB_NEXT(name, x, y) name##_VRB_NEXT(y)
#define VRB_PREV(name, x, y) name##_VRB_PREV(y)
#define VRB_MIN(name, x) name##_VRB_MINMAX(x, VRB_NEGINF)
#define VRB_MAX(name, x) name##_VRB_MINMAX(x, VRB_INF)
#define VRB_FOREACH(x, name, head) \
for ((x) = VRB_MIN(name, head); \
(x) != NULL; \
(x) = name##_VRB_NEXT(x))
#define VRB_FOREACH_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL); \
(x) = (y))
#define VRB_FOREACH_SAFE(x, name, head, y) \
for ((x) = VRB_MIN(name, head); \
((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL); \
(x) = (y))
#define VRB_FOREACH_REVERSE(x, name, head) \
for ((x) = VRB_MAX(name, head); \
(x) != NULL; \
(x) = name##_VRB_PREV(x))
#define VRB_FOREACH_REVERSE_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL); \
(x) = (y))
#define VRB_FOREACH_REVERSE_SAFE(x, name, head, y) \
for ((x) = VRB_MAX(name, head); \
((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL); \
(x) = (y))
#endif /* _VTREE_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment