Commit 7ab410e6 authored by Nils Goroll's avatar Nils Goroll

Support both Varnish 3 and Varnish 4. Allocate memory from workspace instead of stack for Varnish 4

* Pull in varnish version autoconf logic from libvmod-querystring
  Thanks to Dridi Boukelmoune <dridi.boukelmoune@gmail.com>

* Move allocation of memory for dcs_match to caller

* generate dcs_match_mem.h defining DCS_MATCH_MEM_SZ which is the memory size
  required by dcs_match

* move gen* perl and c source to the gen directory to avoid issues with
  make check and recursive subdirs

* first changes to make splint happier
parent bc7bd4dc
...@@ -55,9 +55,13 @@ src/dcs ...@@ -55,9 +55,13 @@ src/dcs
src/dcs_test src/dcs_test
src/gen/dcs_classifier.c src/gen/dcs_classifier.c
src/gen/dcs_classifier.h src/gen/dcs_classifier.h
src/gen/dcs_match_mem.h
src/gen/dcs_type.c src/gen/dcs_type.c
src/gen/dcs.db* src/gen/dcs.db*
src/gen/fixup.* src/gen/fixup.*
src/gen/gen_dcs_match_mem
src/vcc_if.c src/vcc_if.c
src/vcc_if.h src/vcc_if.h
src/gen/dcs_classifier.checksum src/gen/dcs_classifier.checksum
src/*.rst
test-driver
...@@ -8,8 +8,8 @@ Varnish Device Classification Service Module ...@@ -8,8 +8,8 @@ Varnish Device Classification Service Module
:Manual section: 3 :Manual section: 3
:Authors: Nils Goroll :Authors: Nils Goroll
:Date: 2014-08-14 :Date: 2014-08-31
:Version: 0.3 :Version: 0.4
SYNOPSIS SYNOPSIS
======== ========
...@@ -21,8 +21,8 @@ Command line ...@@ -21,8 +21,8 @@ Command line
<prefix>/bin/dcs <prefix>/bin/dcs
Varnish VMOD Varnish VMOD (Varnish 3 and Varnish 4)
------------ --------------------------------------
VCL: VCL:
...@@ -134,6 +134,7 @@ INTERFACES ...@@ -134,6 +134,7 @@ INTERFACES
The following use cases are supported The following use cases are supported
* Use as a Varnish 4 module
* Use as a Varnish 3 module * Use as a Varnish 3 module
* Use with Varnish 2 as inline-C * Use with Varnish 2 as inline-C
* Command line tools * Command line tools
...@@ -181,6 +182,7 @@ To build the varnish module (VMOD) ...@@ -181,6 +182,7 @@ To build the varnish module (VMOD)
* the source tree of the Varnish version the VMOD is to be used * the source tree of the Varnish version the VMOD is to be used
with. Varnish must have been configured and compiled in this source with. Varnish must have been configured and compiled in this source
tree. tree.
* python2 or higher * python2 or higher
BUILDING BUILDING
...@@ -517,6 +519,9 @@ HISTORY ...@@ -517,6 +519,9 @@ HISTORY
* Version 0.3: Class assignments can now be defined in `src/classes.conf` * Version 0.3: Class assignments can now be defined in `src/classes.conf`
* Version 0.4: Also support Varnish 4. Take memory from Varnish workspace
instead of stack for Varnish 4.
BUGS BUGS
==== ====
......
AC_PREREQ(2.69) AC_PREREQ(2.69)
AC_COPYRIGHT([Copyright 2014 UPLEX - Nils Goroll Systemoptimierung]) AC_COPYRIGHT([Copyright 2014 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([dcs_classifier], [0.3]) AC_INIT([dcs_classifier], [0.4])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/gen_dcs_classifier.pl) AC_CONFIG_SRCDIR(src/gen/gen_dcs_classifier.pl)
AM_CONFIG_HEADER(dcs_config.h) AM_CONFIG_HEADER(dcs_config.h)
AC_CANONICAL_SYSTEM AC_CANONICAL_SYSTEM
...@@ -118,25 +118,52 @@ else ...@@ -118,25 +118,52 @@ else
AC_CHECK_PROGS(PYTHON, [python3 python3.1 python3.2 python2.7 python2.6 python2.5 python2 python], [AC_MSG_ERROR([Python is needed to build this vmod, please install python.])]) AC_CHECK_PROGS(PYTHON, [python3 python3.1 python3.2 python2.7 python2.6 python2.5 python2 python], [AC_MSG_ERROR([Python is needed to build this vmod, please install python.])])
VARNISHSRC=`cd $VARNISHSRC && pwd` VARNISHSRC=`cd $VARNISHSRC && pwd`
AC_CHECK_FILE([$VARNISHSRC/include/varnishapi.h],
[], Varnish_Version_MAJOR=
[AC_MSG_FAILURE(["$VARNISHSRC" is not a Varnish source directory])]
) # varnish3
AC_CHECK_FILE([$VARNISHSRC/lib/libvmod_std/vmod.py], [
AC_SUBST([VMODTOOL], ['$(VARNISHSRC)/lib/libvmod_std/vmod.py'])
AC_SUBST([VCCFILE], [vmod_dcs3.vcc])
AC_DEFINE([VARNISH_MAJOR], [3], [Define the Varnish major version we compile against])
AC_SUBST([TESTDIR], [tests])
])
# varnish4
AC_CHECK_FILE([$VARNISHSRC/lib/libvcc/vmodtool.py], [
AC_SUBST([VMODTOOL], ['$(VARNISHSRC)/lib/libvcc/vmodtool.py'])
AC_SUBST([VCCFILE], [vmod_dcs4.vcc])
AC_DEFINE([VARNISH_MAJOR], [4], [Define the Varnish major version we compile against])
AC_SUBST([TESTDIR], [tests4])
])
# Check that varnishtest is built in the varnish source directory # Check that varnishtest is built in the varnish source directory
AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest], AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest],
[], [],
[AC_MSG_FAILURE([Can't find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])] [AC_MSG_FAILURE([Could not find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])]
)
# Check vanish ABI version
AC_CHECK_FILE([$VARNISHSRC/include/vmod_abi.h],
[
AC_MSG_CHECKING([varnish ABI version])
[VMOD_ABI_Version=`cut -d ' ' -f 4 "$VARNISHSRC/include/vmod_abi.h"`]
AC_MSG_RESULT([$VMOD_ABI_Version])
],
[
[VMOD_ABI_Version=3.0.0]
AC_MSG_NOTICE([Could not find "$VARNISHSRC/include/vmod_abi.h". Assuming varnish 3.0.0 ABI version])
]
) )
# vmod installation dir # vmod installation dir
AC_ARG_VAR([VMODDIR], [vmod installation directory @<:@LIBDIR/varnish/vmods@:>@]) AC_ARG_VAR([VMODDIR], [vmod installation directory @<:@LIBDIR/varnish/vmods@:>@])
if test "x$VMODDIR" = x; then if test "x$VMODDIR" = x; then
VMODDIR=`pkg-config --variable=vmoddir varnishapi` VMODDIR=`pkg-config --variable=vmoddir varnishapi`
if test "x$VMODDIR" = x; then if test "x$VMODDIR" = x; then
AC_MSG_FAILURE([Can't determine vmod installation directory]) AC_MSG_FAILURE([Could not determine vmod installation directory])
fi fi
fi fi
fi fi
############################################################ ############################################################
......
...@@ -9,6 +9,7 @@ SUBDIRS = gen ...@@ -9,6 +9,7 @@ SUBDIRS = gen
include_HEADERS = \ include_HEADERS = \
dcs_match.h \ dcs_match.h \
gen/dcs_match_mem.h \
dcs_type.h dcs_type.h
CHECKSUM = `cat gen/dcs_classifier.checksum` CHECKSUM = `cat gen/dcs_classifier.checksum`
...@@ -42,7 +43,6 @@ dcs_test_SOURCES = \ ...@@ -42,7 +43,6 @@ dcs_test_SOURCES = \
# - # -
EXTRA_DIST = \ EXTRA_DIST = \
gen_dcs_classifier.pl \
classes.conf classes.conf
# included c source # included c source
...@@ -57,6 +57,7 @@ dist_data_DATA = \ ...@@ -57,6 +57,7 @@ dist_data_DATA = \
dcs_varnish2.c \ dcs_varnish2.c \
dcs_varnish.c \ dcs_varnish.c \
dcs_match.h \ dcs_match.h \
gen/dcs_match_mem.h \
dcs_match.c \ dcs_match.c \
dcs_type.h dcs_type.h
...@@ -77,7 +78,7 @@ if BUILD_VMOD ...@@ -77,7 +78,7 @@ if BUILD_VMOD
vmoddir = $(VMODDIR) vmoddir = $(VMODDIR)
vmod_LTLIBRARIES = libvmod_dcs.la vmod_LTLIBRARIES = libvmod_dcs.la
libvmod_dcs_la_CPPFLAGS = -Igen -I$(VARNISHSRC)/include -I$(VARNISHSRC) libvmod_dcs_la_CPPFLAGS = -Igen -I$(VARNISHSRC)/include -I$(VARNISHSRC)/bin/varnishd -I$(VARNISHSRC)
libvmod_dcs_la_LDFLAGS = -module -export-dynamic -avoid-version libvmod_dcs_la_LDFLAGS = -module -export-dynamic -avoid-version
...@@ -92,8 +93,8 @@ vcc_if_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC) ...@@ -92,8 +93,8 @@ vcc_if_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC)
vcc_dcs_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC) vcc_dcs_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC)
vcc_if.c vcc_if.h: $(VARNISHSRC)/lib/libvmod_std/vmod.py vmod_dcs.vcc vcc_if.c vcc_if.h: @VMODTOOL@ @VCCFILE@
@PYTHON@ $(VARNISHSRC)/lib/libvmod_std/vmod.py vmod_dcs.vcc @PYTHON@ @VMODTOOL@ @VCCFILE@
BUILT_SOURCES = \ BUILT_SOURCES = \
vcc_if.c \ vcc_if.c \
...@@ -103,11 +104,11 @@ CLEANFILES = \ ...@@ -103,11 +104,11 @@ CLEANFILES = \
vcc_if.c \ vcc_if.c \
vcc_if.h vcc_if.h
VMOD_TESTS = tests/*.vtc VMOD_TESTS = @TESTDIR@/*.vtc
.PHONY: $(VMOD_TESTS) .PHONY: $(VMOD_TESTS)
tests/*.vtc: libvmod_dcs.la @TESTDIR@/*.vtc: libvmod_dcs.la
$(VARNISHSRC)/bin/varnishtest/varnishtest -Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd -Dvmod_topbuild=$(abs_top_builddir) $@ $(VARNISHSRC)/bin/varnishtest/varnishtest -Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd -Dvmod_topbuild=$(abs_top_builddir) $@
check: $(VMOD_TESTS) check: $(VMOD_TESTS)
......
...@@ -39,13 +39,14 @@ main (void) { ...@@ -39,13 +39,14 @@ main (void) {
char *line = NULL; char *line = NULL;
int e, t; int e, t;
size_t linesz; size_t linesz;
char mem[DCS_MATCH_MEM_SZ];
dcs_match_init(); dcs_match_init();
while (getline(&line, &linesz, stdin) != -1) { while (getline(&line, &linesz, stdin) != -1) {
char *p = line; char *p = line;
for ( ; *p; ++p) *p = tolower(*p); for ( ; *p; ++p) *p = tolower(*p);
e = dcs_match(line); e = dcs_match(line, mem, DCS_MATCH_MEM_SZ);
t = dcs_match_type_id(e); t = dcs_match_type_id(e);
printf("--\n%sentry id %d type %d - %s - %s\n", line, printf("--\n%sentry id %d type %d - %s - %s\n", line,
dcs_match_id(e), t, dcs_type_class(t), dcs_type_name(t)); dcs_match_id(e), t, dcs_type_class(t), dcs_type_name(t));
......
...@@ -45,17 +45,17 @@ ...@@ -45,17 +45,17 @@
#ifdef DEBUG_K_MATCH #ifdef DEBUG_K_MATCH
#define p_entry(state, fmt, name) \ #define p_entry(state, fmt, name) \
do { \ do { \
if (state.name ## _match_entry) { \ if (state->name ## _match_entry) { \
printf(fmt, #name, \ printf(fmt, #name, \
state.name ## _match_entry, \ state->name ## _match_entry, \
dcs_type[dcs_entry[state.name ## _match_entry].type], \ dcs_type[dcs_entry[state->name ## _match_entry].type], \
dcs_entry[state.name ## _match_entry].key); \ dcs_entry[state->name ## _match_entry].key); \
} \ } \
} while(0) } while(0)
#define p_entry_if_changed(state, prev_match,fmt) \ #define p_entry_if_changed(state, prev_match,fmt) \
do if (state.last_match_entry != prev_match) { \ do if (state->last_match_entry != prev_match) { \
p_entry(state, fmt, last); \ p_entry(state, fmt, last); \
prev_match = state.last_match_entry; \ prev_match = state->last_match_entry; \
} while(0) } while(0)
#else #else
#define p_entry(...) do { } while(0) #define p_entry(...) do { } while(0)
...@@ -79,32 +79,37 @@ dcs_match_init(void) { ...@@ -79,32 +79,37 @@ dcs_match_init(void) {
/* /*
* we deliberately return int instead of dcs_entry_id_t here in order to not * we deliberately return int instead of dcs_entry_id_t here in order to not
* require dcs_classifier.h for callers * require dcs_classifier.h for callers
*
* mem needs to be an allocation of at least DCS_MATCH_MEM_SZ bytes size
* sz is the size of the mem allocation
*/ */
int /* dcs_entry_id_t */ int /* dcs_entry_id_t */
dcs_match(const char *p) { dcs_match(const char *p, void *mem, size_t sz) {
struct dcs_matchstate state; struct dcs_matchstate *state = mem;
#ifdef DEBUG_K_MATCH #ifdef DEBUG_K_MATCH
dcs_entry_id_t prev_match = 0; dcs_entry_id_t prev_match = 0;
printf("\n--\n%s",p); printf("\n--\n%s",p);
#endif #endif
assert(p); assert(p != NULL);
assert(state != NULL);
assert(sz >= DCS_MATCH_MEM_SZ);
dcs_init_matchstate(&state); dcs_init_matchstate(state);
while (*p) { while (*p != '\0') {
dcs_parse_subkey(&state, p); dcs_parse_subkey(state, p);
p_entry_if_changed(state, prev_match, " %s match %d: %s - %s\n"); p_entry_if_changed(state, prev_match, " %s match %d: %s - %s\n");
p++; p++;
} }
dcs_state_eval_candidates(&state); dcs_state_eval_candidates(state);
p_entry_if_changed(state, prev_match, " new %s from candidates %d: %s - %s\n"); p_entry_if_changed(state, prev_match, " new %s from candidates %d: %s - %s\n");
p_entry(state, " %s match %d: %s - %s\n", min); p_entry(state, " %s match %d: %s - %s\n", min);
p_entry(state, " %s match %d: %s - %s\n", first); p_entry(state, " %s match %d: %s - %s\n", first);
p_entry(state, " %s match %d: %s - %s\n", last); p_entry(state, " %s match %d: %s - %s\n", last);
return state.min_match_entry; return (int)state->min_match_entry;
} }
#define check_entry_index(entry_index, ret) \ #define check_entry_index(entry_index, ret) \
...@@ -118,7 +123,7 @@ dcs_match(const char *p) { ...@@ -118,7 +123,7 @@ dcs_match(const char *p) {
int /* enum dcs_type */ int /* enum dcs_type */
dcs_match_id(int entry_index /* dcs_entry_id_t */) { dcs_match_id(int entry_index /* dcs_entry_id_t */) {
check_entry_index(entry_index, -1); check_entry_index(entry_index, -1);
return dcs_entry[entry_index].id; return (int)dcs_entry[entry_index].id;
} }
int /* enum dcs_type */ int /* enum dcs_type */
......
...@@ -28,10 +28,12 @@ ...@@ -28,10 +28,12 @@
* *
*/ */
#include "dcs_match_mem.h"
#ifndef DCS_MATCH_H #ifndef DCS_MATCH_H
#define DCS_MATCH_H 1 #define DCS_MATCH_H 1
int dcs_match_init(void); int dcs_match_init(void);
int dcs_match(const char *p); int dcs_match(const char *p, void *mem, size_t sz);
int dcs_match_id(int entry_id /* dcs_entry_id_t */); int dcs_match_id(int entry_id /* dcs_entry_id_t */);
int dcs_match_type_id(int entry_id /* dcs_entry_id_t */); int dcs_match_type_id(int entry_id /* dcs_entry_id_t */);
const char const *dcs_match_key(int entry_id /* dcs_entry_id_t */); const char const *dcs_match_key(int entry_id /* dcs_entry_id_t */);
......
...@@ -96,6 +96,7 @@ fixup_out (FILE *f_remove, FILE *f_reorder, const int i, const int r) { ...@@ -96,6 +96,7 @@ fixup_out (FILE *f_remove, FILE *f_reorder, const int i, const int r) {
int int
test (const char *fixup_remove_name, const char *fixup_reorder_name) { test (const char *fixup_remove_name, const char *fixup_reorder_name) {
char mem[DCS_MATCH_MEM_SZ];
int i, r, errcount = 0; int i, r, errcount = 0;
FILE *f_remove = NULL, *f_reorder = NULL; FILE *f_remove = NULL, *f_reorder = NULL;
...@@ -118,7 +119,7 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) { ...@@ -118,7 +119,7 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) {
for (i = NB_E_SPECIAL_LIMIT; i < DCS_ENTRY_COUNT; i++) { for (i = NB_E_SPECIAL_LIMIT; i < DCS_ENTRY_COUNT; i++) {
if (dcs_matchstate_init.matchmask[i] == 0) { if (dcs_matchstate_init.matchmask[i] == 0) {
r = dcs_match(dcs_entry[i].key); r = dcs_match(dcs_entry[i].key, mem, DCS_MATCH_MEM_SZ);
if (i != r) { if (i != r) {
test_p_diff("miss\t", i, dcs_entry[i].key, r); test_p_diff("miss\t", i, dcs_entry[i].key, r);
fixup_out(f_remove, f_reorder, i, r); fixup_out(f_remove, f_reorder, i, r);
...@@ -173,14 +174,14 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) { ...@@ -173,14 +174,14 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) {
} }
} }
r = dcs_match(poskey); r = dcs_match(poskey, mem, DCS_MATCH_MEM_SZ);
if (i != r) { if (i != r) {
test_p_diff("miss n-p", i, poskey, r); test_p_diff("miss n-p", i, poskey, r);
fixup_out(f_remove, f_reorder, i, r); fixup_out(f_remove, f_reorder, i, r);
errcount++; errcount++;
} }
r = dcs_match(negkey); r = dcs_match(negkey, mem, DCS_MATCH_MEM_SZ);
if (r == i) { if (r == i) {
test_p_diff("miss n-n", i, negkey, r); test_p_diff("miss n-n", i, negkey, r);
fixup_out(f_remove, f_reorder, i, r); fixup_out(f_remove, f_reorder, i, r);
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* *
* common code for varnish 2 (inline-C) and varnish 3 (vmod) * common code for varnish 2 (inline-C) and varnish 3/4 (vmod)
* *
* this gets _INCLUDED_ * this gets _INCLUDED_
*/ */
...@@ -40,13 +40,57 @@ ...@@ -40,13 +40,57 @@
#include "dcs_type.c" #include "dcs_type.c"
#define UA_LIMIT 1024 #define UA_LIMIT 1024
#define DCS_VARNISH2_NHDRS 4 #define DCS_VARNISH2_NHDRS 4
const char * const hdrs[DCS_VARNISH2_NHDRS] = {
[0] = "\025X-OperaMini-Phone-UA:", #if VARNISH_MAJOR == 4
[1] = "\024X-Device-User-Agent:",
[2] = "\026X-Original-User-Agent:", typedef const struct vrt_ctx dcs_ctx;
[3] = "\016X-Goog-Source:"
static inline
const char *DCS_GetHdr(dcs_ctx *ctx, const struct gethdr_s *hdr) {
return VRT_GetHdr(ctx, hdr);
}
/*
* in varnish 4, the default worker thread stack size has been drastically
* reduced to 48k, so we take our allocation from the workspace
*/
#define DCS_USE_WS
#else /* VARNISH_MAJOR == 3 */
struct gethdr_s {
enum gethdr_e where;
const char *what;
};
typedef struct sess dcs_ctx;
typedef int VCL_INT;
typedef const char * VCL_STRING;
static inline
const char *DCS_GetHdr(dcs_ctx *ctx, const struct gethdr_s *hdr) {
return VRT_GetHdr(ctx, hdr->where, hdr->what);
}
#endif
#ifdef DCS_USE_WS
/* XXX better way? */
#undef assert
#include "cache/cache.h"
#define DCS_USE_WS_MINIMUM PRNDUP(64 * 1024 + DCS_MATCH_MEM_SZ + UA_LIMIT)
#endif
const struct gethdr_s hdr_ua = {HDR_REQ, "\013User-Agent:"};
const struct gethdr_s hdr_wap = {HDR_REQ, "\016x-wap-profile:"};
const struct gethdr_s hdrs[DCS_VARNISH2_NHDRS] = {
[0] = { HDR_REQ, "\025X-OperaMini-Phone-UA:" },
[1] = { HDR_REQ, "\024X-Device-User-Agent:" },
[2] = { HDR_REQ, "\026X-Original-User-Agent:" },
[3] = { HDR_REQ, "\016X-Goog-Source:" }
}; };
const char * const ua_prepend[DCS_VARNISH2_NHDRS] = { const char * const ua_prepend[DCS_VARNISH2_NHDRS] = {
...@@ -68,22 +112,42 @@ const char * const ua_prepend[DCS_VARNISH2_NHDRS] = { ...@@ -68,22 +112,42 @@ const char * const ua_prepend[DCS_VARNISH2_NHDRS] = {
w += l; w += l;
static int static int
dcs_varnish_classify(const struct sess *sp) { dcs_varnish_classify(dcs_ctx *ctx) {
const char *ua = VRT_GetHdr(sp, HDR_REQ, "\013User-Agent:"); const char *ua = DCS_GetHdr(ctx, &hdr_ua);
const char *r; const char *r;
char *w = NULL; char *w = NULL;
int i; int i, ret;
size_t l; size_t l;
#ifdef DCS_USE_WS
#define WS_SIZE (DCS_MATCH_MEM_SZ + UA_LIMIT)
unsigned space = WS_Reserve(ctx->ws, WS_SIZE);
void *mem = ctx->ws->f;
char *uabuf = ctx->ws->f + DCS_MATCH_MEM_SZ;
assert(space != 0);
assert(space >= WS_SIZE);
space -= DCS_MATCH_MEM_SZ;
if (space > UA_LIMIT)
space = UA_LIMIT;
#else
size_t space = UA_LIMIT; size_t space = UA_LIMIT;
char mem[DCS_MATCH_MEM_SZ];
char uabuf[space]; char uabuf[space];
#endif
if ((! ua) || (! *ua)) if ((! ua) || (! *ua)) {
return NB_E_SPECIAL_UNIDENTIFIED; ret = NB_E_SPECIAL_UNIDENTIFIED;
goto out;
}
if (VRT_GetHdr(sp, HDR_REQ, "\016x-wap-profile:")) if (DCS_GetHdr(ctx, &hdr_wap)) {
return NB_E_SPECIAL_GENERIC_WAP; ret = NB_E_SPECIAL_GENERIC_WAP;
goto out;
}
/* we need to copy to downcase the string for matching */ /* we need to copy to downcase the string for matching */
w = uabuf; w = uabuf;
...@@ -93,7 +157,7 @@ dcs_varnish_classify(const struct sess *sp) { ...@@ -93,7 +157,7 @@ dcs_varnish_classify(const struct sess *sp) {
ua = uabuf; ua = uabuf;
for (i = 0; i < DCS_VARNISH2_NHDRS; i++) { for (i = 0; i < DCS_VARNISH2_NHDRS; i++) {
r = VRT_GetHdr(sp, HDR_REQ, hdrs[i]); r = DCS_GetHdr(ctx, &hdrs[i]);
if (r && *r) { if (r && *r) {
if (ua_prepend[i]) if (ua_prepend[i])
appnd(w, space, ua_prepend[i], l); appnd(w, space, ua_prepend[i], l);
...@@ -101,13 +165,24 @@ dcs_varnish_classify(const struct sess *sp) { ...@@ -101,13 +165,24 @@ dcs_varnish_classify(const struct sess *sp) {
} }
} }
uabuf[UA_LIMIT] = '\0'; /* ensure the string is always terminated */
if (w == (uabuf + UA_LIMIT))
uabuf[UA_LIMIT] = '\0';
else
w[-1] = '\0';
assert(ua == uabuf); assert(ua == uabuf);
assert(w > uabuf); assert(w > uabuf);
assert(w <= (uabuf + UA_LIMIT)); assert(w <= (uabuf + UA_LIMIT));
for (w = uabuf; *w; ++w) *w = tolower(*w); for (w = uabuf; *w; ++w) *w = tolower(*w);
return dcs_match(ua); ret = dcs_match(ua, mem, DCS_MATCH_MEM_SZ);
out:
#ifdef DCS_USE_WS
/* used workspace only as scratch - releasing all */
WS_Release(ctx->ws, 0);
#endif
return (ret);
} }
...@@ -3,16 +3,34 @@ ...@@ -3,16 +3,34 @@
BUILT_SOURCES = \ BUILT_SOURCES = \
dcs_classifier.c \ dcs_classifier.c \
dcs_classifier.h \ dcs_classifier.h \
dcs_type.c dcs_type.c \
dcs_match_mem.h
EXTRA_DIST = \
gen_dcs_classifier.pl \
gen_dcs_match_mem.c
dcs_classifier.c dcs_classifier.h dcs_type.c: ../gen_dcs_classifier.pl $(DCS_DBFILE) ../classes.conf # -- the classifier
dcs_classifier.c dcs_classifier.h dcs_type.c: gen_dcs_classifier.pl $(DCS_DBFILE) ../classes.conf
if [[ -f fixup.remove ]] && [[ -f fixup.reorder ]] ; then \ if [[ -f fixup.remove ]] && [[ -f fixup.reorder ]] ; then \
../gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf fixup.remove fixup.reorder ; \ ./gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf fixup.remove fixup.reorder ; \
else \ else \
../gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf ; \ ./gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf ; \
fi fi
# fixup db # -- gen_dcs_match_mem
noinst_PROGRAMS = gen_dcs_match_mem
gen_dcs_match_mem_SOURCES = \
gen_dcs_match_mem.c \
dcs_classifier.h
dcs_match_mem.h: gen_dcs_match_mem
./gen_dcs_match_mem >dcs_match_mem.h
# -- fixup db
fixup.remove fixup.reorder fixup.out: fixup.remove fixup.reorder fixup.out:
- rm fixup.remove fixup.reorder fixup.out - rm fixup.remove fixup.reorder fixup.out
- rm dcs_classifier.c dcs_classifier.h dcs_type.c - rm dcs_classifier.c dcs_classifier.h dcs_type.c
...@@ -26,6 +44,8 @@ fixup.remove fixup.reorder fixup.out: ...@@ -26,6 +44,8 @@ fixup.remove fixup.reorder fixup.out:
fixup-local: fixup.remove fixup.reorder fixup.out fixup-local: fixup.remove fixup.reorder fixup.out
# -- load db
load-local: dcs.db.refresh dcs.db load-local: dcs.db.refresh dcs.db
dcs.db.tstamp: dcs.db.tstamp:
...@@ -59,12 +79,13 @@ dcs.db: dcs.db.tstamp ...@@ -59,12 +79,13 @@ dcs.db: dcs.db.tstamp
mv -f dcs.db.tmp dcs.db ; \ mv -f dcs.db.tmp dcs.db ; \
fi fi
# --
CLEANFILES = \ CLEANFILES = \
dcs_classifier.c \ $(BUILT_SOURCES) \
dcs_classifier.h \
dcs_type.c \
fixup.remove \ fixup.remove \
fixup.reorder \ fixup.reorder \
fixup.out \ fixup.out \
dcs.db \ dcs.db \
dcs.db.tstamp dcs.db.tstamp
...@@ -1021,6 +1021,8 @@ struct dcs_subkey { ...@@ -1021,6 +1021,8 @@ struct dcs_subkey {
struct dcs_matchstate { struct dcs_matchstate {
#define DCS_MATCHSTATE_MAGIC 0x32bc524d #define DCS_MATCHSTATE_MAGIC 0x32bc524d
uint32_t magic; uint32_t magic;
uint32_t chk_entry_count;
uint32_t chk_subkey_count;
dcs_entry_id_t min_match_entry; dcs_entry_id_t min_match_entry;
dcs_entry_id_t first_match_entry; dcs_entry_id_t first_match_entry;
...@@ -1038,7 +1040,7 @@ const struct dcs_entry dcs_entry[DCS_ENTRY_COUNT]; ...@@ -1038,7 +1040,7 @@ const struct dcs_entry dcs_entry[DCS_ENTRY_COUNT];
const struct dcs_subkey dcs_subkey[DCS_SUBKEY_COUNT]; const struct dcs_subkey dcs_subkey[DCS_SUBKEY_COUNT];
const struct dcs_matchstate dcs_matchstate_init; const struct dcs_matchstate dcs_matchstate_init;
void dcs_init_matchstate (struct dcs_matchstate *state); void dcs_init_matchstate (/*\@out\@*/ struct dcs_matchstate *state);
void dcs_parse_subkey(struct dcs_matchstate *state, const char const *m); void dcs_parse_subkey(struct dcs_matchstate *state, const char const *m);
void dcs_state_eval_candidates(struct dcs_matchstate *state); void dcs_state_eval_candidates(struct dcs_matchstate *state);
...@@ -1119,6 +1121,9 @@ _VCL (F_CLASSIFIER_C, VCL_TOP, <<'EOF'); ...@@ -1119,6 +1121,9 @@ _VCL (F_CLASSIFIER_C, VCL_TOP, <<'EOF');
const struct dcs_matchstate dcs_matchstate_init = { const struct dcs_matchstate dcs_matchstate_init = {
.magic = DCS_MATCHSTATE_MAGIC, .magic = DCS_MATCHSTATE_MAGIC,
.chk_entry_count = DCS_ENTRY_COUNT,
.chk_subkey_count = DCS_SUBKEY_COUNT,
.min_match_entry = 0, .min_match_entry = 0,
.first_match_entry = 0, .first_match_entry = 0,
.last_match_entry = 0, .last_match_entry = 0,
...@@ -1167,7 +1172,7 @@ _VCL(F_CLASSIFIER_C, undef, <<'EOF'); ...@@ -1167,7 +1172,7 @@ _VCL(F_CLASSIFIER_C, undef, <<'EOF');
#undef PARSE_REGISTER #undef PARSE_REGISTER
void void
dcs_init_matchstate (struct dcs_matchstate *state) { dcs_init_matchstate (/*\@out\@*/ struct dcs_matchstate *state) {
/* if this fails, fix the init code */ /* if this fails, fix the init code */
assert(NB_T_UNIDENTIFIED == 0); assert(NB_T_UNIDENTIFIED == 0);
...@@ -1175,7 +1180,16 @@ dcs_init_matchstate (struct dcs_matchstate *state) { ...@@ -1175,7 +1180,16 @@ dcs_init_matchstate (struct dcs_matchstate *state) {
assert(DCS_ENTRY_COUNT <= DCS_ENTRY_INDEX_MAX); assert(DCS_ENTRY_COUNT <= DCS_ENTRY_INDEX_MAX);
assert(DCS_SUBKEY_COUNT <= DCS_SUBKEY_ID_MAX); assert(DCS_SUBKEY_COUNT <= DCS_SUBKEY_ID_MAX);
/*
* something must be terribly wrong if the const matchstate_init and
* this code don't agree on these counts
*/
assert(DCS_ENTRY_COUNT == dcs_matchstate_init.chk_entry_count);
assert(DCS_SUBKEY_COUNT == dcs_matchstate_init.chk_subkey_count);
assert(sizeof(*state) == sizeof(dcs_matchstate_init));
memcpy(state, &dcs_matchstate_init, sizeof(*state)); memcpy(state, &dcs_matchstate_init, sizeof(*state));
assert(state->magic == DCS_MATCHSTATE_MAGIC); assert(state->magic == DCS_MATCHSTATE_MAGIC);
assert(state->min_match_entry == 0); assert(state->min_match_entry == 0);
} }
......
/*
* Copyright (c) 2014 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.
*
*/
#include "dcs_config.h"
#include <stdio.h>
#include "dcs_classifier.h"
/*
* generate the dcs_match_mem.h file telling callers of dcs_match the size of
* the required private data structure (struct dcs_matchstate)
*/
static const char *pre =
"/*\n"
" * THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT\n"
" */\n\n"
"#ifndef DCS_MATCH_MEM_H\n"
"#define DCS_MATCH_MEM_H 1\n\n";
static const char *post =
"\n"
"#endif\n";
int
main(void) {
printf(pre);
printf("#define DCS_MATCH_MEM_SZ %zu /* sizeof(struct dcs_matchstate) */\n",
sizeof(struct dcs_matchstate));
printf(post);
}
# $Id$
varnishtest "Test dcs vmod"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import dcs from "${vmod_topbuild}/src/.libs/libvmod_dcs.so";
sub vcl_recv {
# this code will classify
#
# for best performance in production, only call classify
# once or use inline-C to save the return value of
# classify (int)
# exotic use cases
set req.http.xx-entry-key = dcs.entry_key(dcs.classify());
set req.http.xx-type-id = dcs.type_id(dcs.classify());
# common use cases
set req.http.x-nb-classified = dcs.type_name(dcs.classify());
set req.http.X-DeviceClass = dcs.type_class(dcs.classify());
return (synth(200));
}
sub vcl_synth {
set resp.http.xx-entry-key = req.http.xx-entry-key;
set resp.http.xx-type-id = req.http.xx-type-id;
set resp.http.x-nb-classified = req.http.x-nb-classified;
set resp.http.X-DeviceClass = req.http.X-DeviceClass;
synthetic ({"classified ad here
"});
return (deliver);
}
} -start
client c1 {
# no User-Agent
txreq -url "/"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "unidentified"
expect resp.http.xx-type-id == "0"
expect resp.http.x-nb-classified == "unidentified"
expect resp.http.X-DeviceClass == "desktop"
} -run
client c1 {
txreq -hdr "user-agent: willgetignored" -hdr "x-wap-profile: anything"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "generic wap"
# cant test - can change easily
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Mobile Phone"
expect resp.http.X-DeviceClass == "smartphone"
} -run
client c1 {
txreq -hdr "User-Agent: Opera/9.80 (Android; Opera Mini/19.0.1340/34.1309; U; de) Presto/2.8.119 Version/11.10" \
-hdr "X-OperaMini-Phone-UA: Mozilla/5.0 (Linux; U; Android 4.3; de-de; ME302C Build/JSS15Q) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "android*me302c"
# cant test - can change easily
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Tablet"
expect resp.http.X-DeviceClass == "tablet"
} -run
# $Id$
varnishtest "Test dcs vmod excessively long ua"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import dcs from "${vmod_topbuild}/src/.libs/libvmod_dcs.so";
sub vcl_recv {
# this code will classify
#
# for best performance in production, only call classify
# once or use inline-C to save the return value of
# classify (int)
# exotic use cases
set req.http.xx-entry-key = dcs.entry_key(dcs.classify());
set req.http.xx-type-id = dcs.type_id(dcs.classify());
# common use cases
set req.http.x-nb-classified = dcs.type_name(dcs.classify());
set req.http.X-DeviceClass = dcs.type_class(dcs.classify());
return (synth(200));
}
sub vcl_synth {
set resp.http.xx-entry-key = req.http.xx-entry-key;
set resp.http.xx-type-id = req.http.xx-type-id;
set resp.http.x-nb-classified = req.http.x-nb-classified;
set resp.http.X-DeviceClass = req.http.X-DeviceClass;
synthetic ({"classified ad here
"});
return (deliver);
}
} -start
client c1 {
txreq -hdr "User-Agent: motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits" \
-hdr "X-Goog-Source: even longer"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits"
# cant test
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Mobile Phone"
expect resp.http.X-DeviceClass == "smartphone"
} -run
...@@ -40,38 +40,48 @@ vmod_init(struct vmod_priv *priv, const struct VCL_conf *cfg) ...@@ -40,38 +40,48 @@ vmod_init(struct vmod_priv *priv, const struct VCL_conf *cfg)
{ {
(void)priv; (void)priv;
(void)cfg; (void)cfg;
#ifdef DCS_USE_WS
if (cache_param->workspace_client < DCS_USE_WS_MINIMUM) {
/* XXX better function for emiting this warning */
fprintf(stderr,
"notice: workspace_client is set too low for vmod_dcs, adjusting from %u to %lu bytes\n",
cache_param->workspace_client, DCS_USE_WS_MINIMUM);
cache_param->workspace_client = DCS_USE_WS_MINIMUM;
}
#endif
return dcs_match_init(); return dcs_match_init();
} }
int VCL_INT
vmod_classify(struct sess *sp) { vmod_classify(dcs_ctx *ctx) {
return dcs_varnish_classify(sp); return dcs_varnish_classify(ctx);
} }
const char * VCL_STRING
vmod_entry_key(struct sess *sp, int e) { vmod_entry_key(dcs_ctx *ctx, VCL_INT e) {
(void) sp; (void) ctx;
return dcs_match_key(e); return dcs_match_key(e);
} }
int VCL_INT
vmod_type_id(struct sess *sp, int e) { vmod_type_id(dcs_ctx *ctx, VCL_INT e) {
(void) sp; (void) ctx;
return dcs_match_type_id(e); return dcs_match_type_id(e);
} }
const char * VCL_STRING
vmod_type_name(struct sess *sp, int e) { vmod_type_name(dcs_ctx *ctx, VCL_INT e) {
const int t = dcs_match_type_id(e); const VCL_INT t = dcs_match_type_id(e);
(void) sp; (void) ctx;
return dcs_type_name(t > 0 ? t : 0); return dcs_type_name(t > 0 ? t : 0);
} }
const char * VCL_STRING
vmod_type_class(struct sess *sp, int e) { vmod_type_class(dcs_ctx *ctx, VCL_INT e) {
const int t = dcs_match_type_id(e); const VCL_INT t = dcs_match_type_id(e);
(void) sp; (void) ctx;
return dcs_type_class(t > 0 ? t : 0); return dcs_type_class(t > 0 ? t : 0);
} }
$Module dcs 3 Varnish Device Classification Service Module
$Init vmod_init
$Function INT classify()
$Function STRING entry_key(INT)
$Function INT type_id(INT)
$Function STRING type_name(INT)
$Function STRING type_class(INT)
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