>From f4f3161a2cf9fc1a66520e364cd34649016eed8b Mon Sep 17 00:00:00 2001
From: Nils Goroll <nils.goroll@uplex.de>
Date: Fri, 28 Jan 2011 17:24:40 +0100
Subject: [PATCH] - Add vmod_digest:
   VCL support for various digests/hashes (crc32, md5, sha256, rs, plain)
   and encoders (base64, hex)
   - re-add phk's Varnish 2.0 crc32 implementation as digest_crc32 (to avoid
     name clashes with crc32 from libvgz)
   - add md5 code from FreeBSD/libmd as vmd5
 - lib/libvarnishapi/base64.c:
   - add base64_encode and base64_encode_string functions
   - pre-generate base64 conversion lookup tables to avoid the need for base64_init
   - fix a bug in base64_decode which would lead to more (null) bytes being
     written to the destination buffer than contained in the encoded string
   - change base64_decode to also return the length of the decoded data
   - add more test code
 - lib/libvarnish/vsha256.c: add test code

---
 bin/varnishncsa/varnishncsa.c        |    1 -
 bin/varnishtest/tests/m00005.vtc     |  114 ++++++
 bin/varnishtest/tests/m00006.vtc     |   49 +++
 configure.ac                         |    1 +
 doc/sphinx/reference/index.rst       |    1 +
 doc/sphinx/reference/vmod_digest.rst |  159 ++++++++
 include/varnishapi.h                 |    8 +-
 lib/Makefile.am                      |    2 +
 lib/libvarnish/vsha256.c             |   26 ++
 lib/libvarnishapi/Makefile.am        |   12 +
 lib/libvarnishapi/base64.c           |  473 +++++++++++++++++++++++--
 lib/libvarnishapi/base64_gen.py      |   45 +++
 lib/libvmod_digest/Makefile.am       |   31 ++
 lib/libvmod_digest/digest_crc32.c    |  129 +++++++
 lib/libvmod_digest/vmd5.c            |  387 ++++++++++++++++++++
 lib/libvmod_digest/vmd5.h            |   51 +++
 lib/libvmod_digest/vmod.py           |  236 ++++++++++++
 lib/libvmod_digest/vmod_digest.c     |  656 ++++++++++++++++++++++++++++++++++
 lib/libvmod_digest/vmod_digest.py    |  212 +++++++++++
 19 files changed, 2558 insertions(+), 35 deletions(-)
 create mode 100644 bin/varnishtest/tests/m00005.vtc
 create mode 100644 bin/varnishtest/tests/m00006.vtc
 create mode 100644 doc/sphinx/reference/vmod_digest.rst
 create mode 100644 lib/libvarnishapi/base64_gen.py
 create mode 100644 lib/libvmod_digest/Makefile.am
 create mode 100644 lib/libvmod_digest/digest_crc32.c
 create mode 100644 lib/libvmod_digest/vmd5.c
 create mode 100644 lib/libvmod_digest/vmd5.h
 create mode 100755 lib/libvmod_digest/vmod.py
 create mode 100644 lib/libvmod_digest/vmod_digest.c
 create mode 100644 lib/libvmod_digest/vmod_digest.py

diff --git a/bin/varnishncsa/varnishncsa.c b/bin/varnishncsa/varnishncsa.c
index 396111c..ad0d07b 100644
--- a/bin/varnishncsa/varnishncsa.c
+++ b/bin/varnishncsa/varnishncsa.c
@@ -621,7 +621,6 @@ h_ncsa(void *priv, enum vsl_tag tag, unsigned fd,
 				char *rubuf;
 				size_t rulen;
 
-				base64_init();
 				rulen = ((strlen(lp->df_u) + 3) * 4) / 3;
 				rubuf = malloc(rulen);
 				assert(rubuf != NULL);
diff --git a/bin/varnishtest/tests/m00005.vtc b/bin/varnishtest/tests/m00005.vtc
new file mode 100644
index 0000000..362973d
--- /dev/null
+++ b/bin/varnishtest/tests/m00005.vtc
@@ -0,0 +1,114 @@
+# $Id$
+
+test "Test digest vmod"
+
+server s1 {
+	rxreq 
+	txresp
+	rxreq 
+	txresp
+	rxreq 
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import digest from "${topbuild}/lib/libvmod_digest/.libs/libvmod_digest.so.1" ;
+
+	sub vcl_deliver {
+	    if (req.url == "/digests") {
+		set resp.http.default-base64 = digest.default_base64("123" + "456");
+		set resp.http.default-hex = digest.default_hex("123" + "456");
+		set resp.http.default-hexlc = digest.default_hexlc("123" + "456");
+		set resp.http.crc32-base64 = digest.crc32_base64("123" + "456");
+		set resp.http.crc32-hex = digest.crc32_hex("123" + "456");
+		set resp.http.crc32-hexlc = digest.crc32_hexlc("123" + "456");
+		set resp.http.sha256-base64 = digest.sha256_base64("123" + "456");
+		set resp.http.sha256-hex = digest.sha256_hex("123" + "456");
+		set resp.http.sha256-hexlc = digest.sha256_hexlc("123" + "456");
+		set resp.http.plain-base64 = digest.plain_base64("123" + "456");
+		set resp.http.plain-hex = digest.plain_hex("123" + "456");
+		set resp.http.plain-hexlc = digest.plain_hexlc("123" + "456");
+		set resp.http.rs-base64 = digest.rs_base64("123" + "456");
+		set resp.http.rs-hex = digest.rs_hex("123" + "456");
+		set resp.http.rs-hexlc = digest.rs_hexlc("123" + "456");
+		set resp.http.md5-base64 = digest.md5_base64("123" + "456");
+		set resp.http.md5-hex = digest.md5_hex("123" + "456");
+		set resp.http.md5-hexlc = digest.md5_hexlc("123" + "456");
+	    } else if (req.url == "/encoders") {
+#		encodings (no digest)
+
+		set resp.http.1base64 = digest.encode_base64("123" + "456" + "" + "Z");
+		set resp.http.1hex = digest.encode_hex("123" + "456" + "" + "Z");
+		set resp.http.1hexlc = digest.encode_hexlc("123" + "456" + "" + "Z");
+
+		set resp.http.base64 = digest.encode_base64("123" + "456");
+		set resp.http.hex = digest.encode_hex("123" + "456");
+		set resp.http.hexlc = digest.encode_hexlc("123" + "456");
+	    } else if (req.url == "/hmacs") {
+
+		set resp.http.hmac_sha256-base64 = digest.hmac_sha256_base64("key", "123" + "456");
+		set resp.http.hmac_sha256-hex = digest.hmac_sha256_hex("key", "123" + "456");
+		set resp.http.hmac_sha256-hexlc = digest.hmac_sha256_hexlc("key", "123" + "456");
+		set resp.http.hmac_md5-base64 = digest.hmac_md5_base64("key", "123" + "456");
+		set resp.http.hmac_md5-hex = digest.hmac_md5_hex("key", "123" + "456");
+		set resp.http.hmac_md5-hexlc = digest.hmac_md5_hexlc("key", "123" + "456");
+
+		set resp.http.hmac_sha256-hexlc-l = digest.hmac_sha256_hexlc("This is a very very long key which really needs to be longer than the 512 bits or 64 bytes which the digest function will devour at once. Have I typed enough?", "123" + "456");
+		set resp.http.hmac_md5-hexlc-l = digest.hmac_md5_hexlc("This is a very very long key which really needs to be longer than the 512 bits or 64 bytes which the digest function will devour at once. Have I typed enough?", "123" + "456");
+
+
+	    }
+	}
+} -start
+
+client c1 {
+	txreq -url "/digests"
+	rxresp
+	expect resp.status == 200
+
+	expect resp.http.default-base64 == "CXLTYQ=="
+	expect resp.http.default-hex == "0972D361"
+	expect resp.http.default-hexlc == "0972d361"
+	expect resp.http.crc32-base64 == "CXLTYQ=="
+	expect resp.http.crc32-hex == "0972D361"
+	expect resp.http.crc32-hexlc == "0972d361"
+	expect resp.http.sha256-base64 == "jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI="
+	expect resp.http.sha256-hex == "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92"
+	expect resp.http.sha256-hexlc == "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"
+	expect resp.http.plain-base64 == "AAABNQ=="
+	expect resp.http.plain-hex == "00000135"
+	expect resp.http.plain-hexlc == "00000135"
+	expect resp.http.rs-base64 == "OPSd2Q=="
+	expect resp.http.rs-hex == "38F49DD9"
+	expect resp.http.rs-hexlc == "38f49dd9"
+	expect resp.http.md5-base64 == "4QrcOUm6Wau+VuBX8g+IPg=="
+	expect resp.http.md5-hex == "E10ADC3949BA59ABBE56E057F20F883E"
+	expect resp.http.md5-hexlc == "e10adc3949ba59abbe56e057f20f883e"
+
+	txreq -url "/encoders"
+	rxresp
+	expect resp.status == 200
+
+	expect resp.http.1base64 == "MTIzNDU2Wg=="
+	expect resp.http.1hex == "3132333435365A"
+	expect resp.http.1hexlc == "3132333435365a"
+
+	expect resp.http.base64 == "MTIzNDU2"
+	expect resp.http.hex == "313233343536"
+	expect resp.http.hexlc == "313233343536"
+
+	txreq -url "/hmacs"
+	rxresp
+
+	expect resp.http.hmac_sha256-base64 == "TfgfVdcIrhcg1fZe9C80ddwWj6I/3kJKxZRPh8MJsF8="
+	expect resp.http.hmac_sha256-hex == "4DF81F55D708AE1720D5F65EF42F3475DC168FA23FDE424AC5944F87C309B05F"
+	expect resp.http.hmac_sha256-hexlc == "4df81f55d708ae1720d5f65ef42f3475dc168fa23fde424ac5944f87c309b05f"
+	expect resp.http.hmac_md5-base64 == "Cr9rrNI8VfpqsU60Sn9XIA=="
+	expect resp.http.hmac_md5-hex == "0ABF6BACD23C55FA6AB14EB44A7F5720"
+	expect resp.http.hmac_md5-hexlc == "0abf6bacd23c55fa6ab14eb44a7f5720"
+	expect resp.http.hmac_sha256-hexlc-l == "af359adeff51bfaf635c581dbcb353682665fcc8c7129796f1e8b71689608fca"
+	expect resp.http.hmac_md5-hexlc-l == "252b93628dac628d8a496b38b89ffb39"
+
+	expect resp.status == 200
+
+} -run
diff --git a/bin/varnishtest/tests/m00006.vtc b/bin/varnishtest/tests/m00006.vtc
new file mode 100644
index 0000000..832eb08
--- /dev/null
+++ b/bin/varnishtest/tests/m00006.vtc
@@ -0,0 +1,49 @@
+# $Id$
+
+test "Test digest vmod rst examples"
+
+server s1 {
+	rxreq 
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import digest from "${topbuild}/lib/libvmod_digest/.libs/libvmod_digest.so.1" ;
+
+	sub vcl_recv {
+		# Simplistic check for basic HTTP authentication in Varnish
+
+		if (req.http.Authorization != ("Basic " +
+		    digest.encode_base64("user:password"))) {
+			error (401, "don't know the magic credentials?");
+		}					
+
+		# Check a digest of something else
+		if (req.http.X-Identity !=
+			digest.md5_hexlc("secret" + client.ip)) {
+			error (403, "You are not on my guest list");
+		}
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 401
+} -run 
+
+client c2 {
+	txreq \
+	      -hdr "Authorization: Basic dXNlcjpwYXNzd29yZA=="
+	rxresp
+	expect resp.status == 403
+} -run 
+
+client c3 {
+	# identity is md5("secret127.0.0.1")
+	txreq \
+	      -hdr "Authorization: Basic dXNlcjpwYXNzd29yZA==" \
+	      -hdr "X-Identity: 44fad189cc8fb13f321ba835aeb8d579"
+	rxresp
+	expect resp.status == 200
+} -run
diff --git a/configure.ac b/configure.ac
index 7b17ba6..a9ba290 100644
--- a/configure.ac
+++ b/configure.ac
@@ -509,6 +509,7 @@ AC_CONFIG_FILES([
     lib/libvcl/Makefile
     lib/libvgz/Makefile
     lib/libvmod_std/Makefile
+    lib/libvmod_digest/Makefile
     lib/libjemalloc/Makefile
     man/Makefile
     redhat/Makefile
diff --git a/doc/sphinx/reference/index.rst b/doc/sphinx/reference/index.rst
index 2dd8c1b..4e7a0ae 100644
--- a/doc/sphinx/reference/index.rst
+++ b/doc/sphinx/reference/index.rst
@@ -19,6 +19,7 @@ The Varnish Reference Manual
 	varnishtop.rst
 	shmem.rst
 	vmod.rst
+	vmod_digest.rst
 
 .. todo::
         The programs:
diff --git a/doc/sphinx/reference/vmod_digest.rst b/doc/sphinx/reference/vmod_digest.rst
new file mode 100644
index 0000000..8161af2
--- /dev/null
+++ b/doc/sphinx/reference/vmod_digest.rst
@@ -0,0 +1,159 @@
+
+.. _reference-vmod_digest:
+
+===========
+vmod_digest
+===========
+
+---------------------------------------------
+Message digests (hashes) and encoders for VCL
+---------------------------------------------
+
+:Author:		Nils Goroll
+:Date:			2010-10-28
+:Version:		0.1
+:Manual section:	7
+
+SYNOPSIS
+========
+
+	import digest;
+
+	digest.DIGEST_ENCODER(STRING)
+
+	digest.encode_ENCODER(STRING)
+
+Common use cases:
+
+	digest.sha256_hexlc(STRING)
+
+	digest.md5_hexlc(STRING)
+
+	digest.encode_base64(STRING)
+
+DESCRIPTION
+===========
+
+The digest module makes message digest (hashing) functions available
+for use from VCLs. As a side-product, it also provides various string
+encoders.
+
+The provided functions are named according to the scheme given in the
+synopsis.
+
+DIGESTS
+=======
+
+DIGEST can be one of
+
+* ``default``: Some efficient default digest, subject to change
+  (currently ``crc32``)
+
+* ``crc32``: "vanilla" 32bit Cyclic redundancy checksum (CRC32) with
+  polynomial 0x04C11DB7 according to
+  http://regregex.bbcmicro.net/crc-catalogue.htm in big endian
+  representation (network byte order)
+
+* ``sha256``: 256 bit Secure Hash Algorithm 2 (SHA-2) cryptographic
+  hash function as used internally by Varnish
+
+* ``md5``: 128 bit Message-Digest algorithm 5 (MD5) cryptographic hash
+  function
+
+* ``plain``: 32 bit plain check sum (sum of all bytes modulo 2^32) in
+  network byte order
+
+* ``rs``: 32 bit rshash: Simple, efficient hash function from Robert
+  Sedgwicks *Algorithms in C* book in network byte order
+
+ENCODERS
+========
+
+ENCODER can be one of
+
+* ``base64``: Base64 encoder according to
+  http://www.faqs.org/rfcs/rfc1113.html section 4.3.2.4, but without
+  line split
+
+* ``hex``: Hexadecimal representation with uppercase characters
+
+* ``hexlc``: Hexadecimal representation with lowercase characters
+
+RETURN VALUES
+=============
+
+All functions will return the string representation of the encoded
+input or encoded digest of the imput, or the NULL (empty) string upon
+error.
+
+EXAMPLES
+========
+
+The following example shows how to generate a Base64 encoding of a 
+clear-text username/passwort to check Basic HTTP authentication and
+how to check an md5 hash of a shared secret and the IP Address of the
+client in order to validate a request.
+
+While the pratical use of the former example is probably limited, a
+more elaborate version of the latter example is actually used by a
+well known Content Delivery Network.
+
+::
+
+	import digest;
+	
+	sub vcl_recv {
+		# Simplistic check for basic HTTP authentication in Varnish
+		
+		if (req.http.Authorization != ("Basic " +
+		    digest.encode_base64("user:password"))) {
+			error (401, "don't know the magic credentials?");
+		}
+		
+		# Check a digest of something else
+		if (req.http.X-Identity !=
+			digest.md5_hexlc("secret" + client.ip)) {
+			error (403, "You are not on my guest list");
+		}
+	}
+
+SEE ALSO
+========
+
+* VCL(7)
+* vmod(7)
+
+LIMITATIONS
+===========
+
+Due to the fact that a data type to store the respective state does
+not currently exist in VCL, updating a hash is not possible from
+VCL. All components to be hashed need to be passed at once.
+
+Because VCL does not provide a data type for binary data, digests can
+not be accessed in their binary form.
+
+Currently, only strings can be hashed or encoded from VCL. Directly
+hashing or encoding other data types is not currently supported by the
+VMOD framework.
+
+Inline C might help to avoid these limitations.
+
+TODO
+====
+
+* more hashes
+* decoding functions
+
+HISTORY
+=======
+
+The digest vmod and this manual were written by Nils Goroll.
+
+COPYRIGHT
+=========
+
+This document is licensed under the same licence as Varnish
+itself. See LICENCE for details.
+
+* Copyright (c) 2010 UPLEX Nils Goroll
diff --git a/include/varnishapi.h b/include/varnishapi.h
index fd1e3b5..0560951 100644
--- a/include/varnishapi.h
+++ b/include/varnishapi.h
@@ -33,6 +33,7 @@
 #define VARNISHAPI_H_INCLUDED
 
 #include <stdint.h>
+#include <stdarg.h>
 
 #include "vsl.h"
 
@@ -261,7 +262,12 @@ extern const char *VSL_tags[256];
 
 
 /* base64.c */
-void base64_init(void);
 int base64_decode(char *d, unsigned dlen, const char *s);
+int base64_encode(char *e, unsigned elen, const char *s, unsigned n);
+int base64_encode_string(char *e, unsigned elen, const void *term, const char *s, va_list ap);
+
+/* safe estimate of length of de/encoded string including terminal NULL */
+#define base64_decode_l(l) ((l * 3 / 4) + 1)
+#define base64_encode_l(l) (((((l * 4 / 3) + 3) / 4) * 4) + 1)
 
 #endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 0ff096b..c5f1a1c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -7,6 +7,7 @@ SUBDIRS = \
 	libvcl \
 	libvgz \
 	libvmod_std \
+	libvmod_digest \
 	@JEMALLOC_SUBDIR@
 
 DIST_SUBDIRS = 	\
@@ -16,4 +17,5 @@ DIST_SUBDIRS = 	\
 	libvcl \
 	libvgz \
 	libvmod_std \
+	libvmod_digest \
 	libjemalloc
diff --git a/lib/libvarnish/vsha256.c b/lib/libvarnish/vsha256.c
index d074bdb..4cb439b 100644
--- a/lib/libvarnish/vsha256.c
+++ b/lib/libvarnish/vsha256.c
@@ -334,3 +334,29 @@ SHA256_Test(void)
 	}
 }
 
+/*
+gcc -I ../../include -I../.. -L .libs -L ../libvarnishcompat/.libs -R .libs -R ../libvarnishcompat/.libs -lvarnish -lvarnishcompat  vsha256.c 
+*/
+
+void main(int argc, char *argv[]) {
+	int i, j, k;
+	int mods[] = {18, 36};
+	struct SHA256Context c;
+	unsigned char o[32];
+	uint32_t o32;
+	
+
+	for (i = 1; i < argc; i++) {
+		SHA256_Init(&c);
+		SHA256_Update(&c, argv[i], strlen(argv[i]));
+		SHA256_Final(o, &c);
+
+		o32 = 0;
+		for (j = 0; j < 32; j+=4)
+			o32 += *((uint32_t *)&o[j]);
+
+		for (j = 0; j < (sizeof(mods) / sizeof(*mods)); j++)
+			printf("%s mod %d : %d\n", argv[i],
+			    mods[j], o32 % mods[j]);
+	}
+}
diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index 9aa49ce..e7f4907 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -17,6 +17,7 @@ libvarnishapi_la_SOURCES = \
 	../libvarnish/vin.c \
 	../libvarnish/vmb.c \
 	../libvarnish/vre.c \
+	base64_tables.h \
 	base64.c \
 	vsm.c \
 	vsl_arg.c \
@@ -27,3 +28,14 @@ libvarnishapi_la_CFLAGS = \
 	-DVARNISH_STATE_DIR='"${VARNISH_STATE_DIR}"'
 
 libvarnishapi_la_LIBADD = @PCRE_LIBS@
+
+lib_srcdir = $(top_srcdir)/lib/libvarnishapi
+
+base64.c: base64_tables.h
+
+base64_tables.h: $(lib_srcdir)/base64_gen.py
+	@PYTHON@ $(lib_srcdir)/base64_gen.py
+
+EXTRA_DIST = base64_gen.py
+
+CLEANFILES = $(builddir)/base64_tables.h
\ No newline at end of file
diff --git a/lib/libvarnishapi/base64.c b/lib/libvarnishapi/base64.c
index feb10f1..f199cce 100644
--- a/lib/libvarnishapi/base64.c
+++ b/lib/libvarnishapi/base64.c
@@ -1,5 +1,16 @@
 /*
+ * Utility functions for base64 encoding / decoding according to
+ * http://www.faqs.org/rfcs/rfc1113.html section 4.3.2.4
+ *
+ * Limits:
+ *
+ * - '*' (not encrypted) is unimplemented
+ * - Encoding line width limitation not implemented, the base64 encoded string
+ *   will not be split up into multiple lines
+ * - Decoding of multi line input is not implemented
+ *
  * Written by Poul-Henning Kamp <phk@phk.freebsd.dk>
+ *        and Nils Goroll <slink@uplex.de>
  *
  * This file is in the public domain.
  */
@@ -10,45 +21,56 @@
 SVNID("$Id$")
 
 #include <sys/types.h>
+#include <stdarg.h>
 #include "varnishapi.h"
+#include "vas.h"
 
-static const char b64[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static char i64[256];
-
-void
-base64_init(void)
-{
-	int i;
-	const char *p;
+#include "base64_tables.h"
 
-	for (i = 0; i < 256; i++)
-		i64[i] = -1;
-	for (p = b64, i = 0; *p; p++, i++)
-		i64[(int)*p] = (char)i;
-	i64['='] = 0;
-}
+/*
+ * Args: destination buffer, buffer length
+ *       source data
+ *
+ * 	 source data must be a null-terminated string
+ *
+ * return length of decoded data in d or -1 upon error
+ *
+ * to facilitate decoding of strings, a final null byte will be appended, but
+ * not returned as the length
+ *
+ * d must be large enough to store the null byte
+ *
+ * implementation note: When encountering the N/A char '=', we finish decoding
+ * the block, but limit the number of bytes being output
+ */
 
 int
 base64_decode(char *d, unsigned dlen, const char *s)
 {
-	unsigned u, v, l;
+	unsigned u, v, w, l;
 	int i;
 
 	u = 0;
 	l = 0;
+
 	while (*s) {
+		w = 3;
 		for (v = 0; v < 4; v++) {
 			if (!*s)
 				break;
-			i = i64[(int)*s++];
+			i = i64[(int)*s];
 			if (i < 0)
 				return (-1);
 			u <<= 6;
 			u |= i;
+			if ((*s++ == '=') && (w == 3)) {
+				if (v == 3)
+					w = 2;
+				else if (v == 2)
+					w = 1;
+			}
 		}
-		for (v = 0; v < 3; v++) {
+		for (v = 0; v < w; v++) {
 			if (l >= dlen - 1)
 				return (-1);
 			*d = (u >> 16) & 0xff;
@@ -58,33 +80,418 @@ base64_decode(char *d, unsigned dlen, const char *s)
 		}
 	}
 	*d = '\0';
-	return (0);
+	return (l);
+}
+
+/*
+ * "left" and "right" part of the respective input bytes, thinking big endian,
+ * shifted to the right base64 output position
+ */
+
+#define b1_l(s)	((s[0] & 0xfc) >> 2)
+#define b1_r(s) ((s[0] & 0x03) << 4)
+
+#define b2_l(s) ((s[1] & 0xf0) >> 4)
+#define b2_r(s) ((s[1] & 0x0f) << 2)
+
+#define b3_l(s) ((s[2] & 0xc0) >> 6)
+#define b3_r(s) ((s[2] & 0x3f))
+
+/*
+ * write one 4-byte block into e (must be large enough),
+ * encoding b = (1..3) bytes from s
+ *
+ * performance note: Should be compiler-optimizable when being fed a constant b
+ */
+
+static inline void
+_b64block(char *e, const char *s, char b)
+{
+	switch (b) {
+	case 3:
+		e[3] = b64[ b3_r(s) ];
+		e[2] = b64[ b3_l(s) | b2_r(s) ];
+	case 2:
+		e[1] = b64[ b2_l(s) | b1_r(s) ];
+	case 1:
+		e[0] = b64[ b1_l(s) ];
+	}
+	switch (b) {
+	case 1:
+		e[1] = b64[ b1_r(s) ];
+		e[2] = '=';
+		e[3] = '=';
+		break;
+	case 2:
+		e[2] = b64[ b2_r(s) ];
+		e[3] = '=';
+	}
+}
+
+/*
+ * Args: destination buffer, buffer length
+ *       source data, source length
+ *
+ * return length of encoded data in d or -1 upon error
+ */
+
+int
+base64_encode(char *e, unsigned elen, const char *s, unsigned n)
+{
+	char b;
+	int  l;
+
+	l = 0;
+
+	while (n) {
+		if (elen < (l + 5))
+			return (-1);
+
+		b = (n < 3) ? n : 3;
+
+		_b64block(e, s, b);
+
+		l += 4;
+		e += 4;
+		s += 3;
+		n -= b;
+	}
+	*e='\0';
+	return (++l);
+}
+
+/*
+ * same as base64_encode, but encode strings passed as varargs as if they were
+ * one concatenated string.
+ *
+ * term is the terminator for the string list, this will usually be
+ * vrt_magic_string_end when called from VRT, VMOD etc. When a non-NULL
+ * terminator is passed, null arguments will be skipped over.
+ *
+ * if NULL is passed as a terminator, varargs processing will stop on a NULL
+ * pointer
+ */
+
+#if 0
+#define b64assert(x)	assert(x)
+#else
+#define b64assert(x)	((void)(x))
+#endif
+
+int
+base64_encode_string(char *e, unsigned elen, const void *term, const char *s, va_list ap)
+{
+	const char	*p;
+	char		b;
+	int		l;
+	/* temp space to carry over three bytes from one vararg to the next */
+	char		carry[2];
+	int		carry_l;
+
+	l = carry_l = 0;
+
+	if (elen == 0)
+		return -1;
+
+	p = s;
+	while (p != term) {
+		if (p == NULL)
+			goto nextp;
+
+		/*
+		 * fill up the array carrying the last bytes from the previous
+		 * string until this string ends or carry is full
+		 */
+		if (carry_l) {
+			while (*p && (carry_l < 3))
+				carry[carry_l++] = *p++;
+
+			if (carry_l < 3)
+				goto nextp;
+
+			b64assert(carry_l == 3);
+
+			if (elen < (l + 5))
+				return -1;
+
+			_b64block(e, carry, 3);
+			carry_l = 0;
+			l += 4;
+			e += 4;
+		}
+
+		b64assert(carry_l == 0);
+
+		while (*p) {
+			if (elen < (l + 5))
+				return -1;
+			
+			if (*(p+1))
+				if (*(p+2))
+					b = 3;
+				else
+					b = 2;
+			else
+				b = 1;
+
+			if (b == 3) {
+				_b64block(e, p, 3);
+
+				l += 4;
+				e += 4;
+				p += 3;
+			} else {
+				b64assert (b > 0);
+
+				if (carry_l == 2) {
+					carry[carry_l] = *p;
+					p++;
+					b--;
+					_b64block(e, carry, 3);
+					l += 4;
+					e += 4;
+					carry_l = 0;
+				}
+
+				b64assert((carry_l + b) <= 3);
+
+				switch (b) {
+				case 2:
+					carry[carry_l + 1] = *(p+1);
+				case 1:
+					carry[carry_l] = *p;
+				}
+				carry_l += b;
+				p += b;
+			}
+		}
+	  nextp:
+		p = va_arg(ap, const char *);
+	}
+
+	/* Anything left ? */
+
+	if (carry_l) {
+		if (elen < (l + 5))
+			return -1;
+
+		_b64block(e, carry, carry_l);
+		l += 4;
+		e += 4;
+	}
+
+	*e='\0';
+	return (++l);
 }
 
 #ifdef TEST_DRIVER
+
+/* gcc -DTEST_DRIVER -I ../.. -I ../../include ../libvarnish/assert.c base64.c */
+
 #include <stdio.h>
+#include <string.h>
+
+#define MAX_PRINTABLE 5
+#define MAX_TEST 11
 
-const char *test1 =
-"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
-"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
-"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
-"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
-"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
+const void * const magic_string_end = &magic_string_end;
+
+const char *test[MAX_TEST+1] = {
+	"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
+	"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
+	"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
+	"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
+	"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", /* 0 */
+	"", /* 1 */
+	/* all following tests encoded using GNU coreutils base64 */
+	"YQ==", /* 2: "a" */
+	"YWI=", /* 3: "ab" */
+	"YWJj", /* 4: "abc" */
+	"UGhrIGFuZCBWYXJuaXNoIHNob3VsZCByZWFsbHkgYmUgYXdhcmRlZCBhIHByaXplIGZvciBwcm90"
+	"ZWN0aW5nIG91ciBlbnZpcm9ubWVudCBieQpzaWduaWZpY2FudGx5IGxvd2VyaW5nIHRoZSBDTzIg"
+	"Zm9vdHByaW50IC8gZW5lcmd5IGNvbnN1bXB0aW9uIGJ5IHRvZGF5J3MgaW50ZXJuZXQKaW5kdXN0"
+	"cnkuIFRoZSBvbmx5IHF1ZXN0aW9uIGlzOiBEb2VzIHN1Y2ggYSBwcml6ZSBleGlzdD8K", /* 5 */
+	/* 
+	 * 6: just some random data from:
+	 * export LC_CTYPE=C
+	 * dd if=/dev/random bs=256 count=1 | tr '\000' '0' | base64
+	 */
+	"qZzhtC90adeYmJSbUUIXti2e6ob7Ru+QeVl9UiiqM9xtsRFh6gnWIFGDCksSmhZ74FkIPj34UawD"
+	"U7Db/5cWWMclJf+B55jmttozMvDW9IumypB/XjMr1W9e9gyiOOoRc1snjIGhyEdiRwvsOl2V0mX0"
+	"7e7StefOtImNz80gIWtGxq+e5KKW5GiJmVamla2zGNj2Sdvo1LV1CGOh3PhwGwJwmHOn9R1/zZ5x"
+	"XWosmz90Kp5RLDbsdDmc/jmJvGzZkE66p0554Xl4rFdu7jSWEqoQKGYboCa8YDAGzKhB0C+xth/J"
+	"F9T42OINv9VXkIjB0Vs+0geY3GifRqbjFPmk",
+	/*
+	 * 7: random data with nulls
+	 */
+	"KN3KvtjkNP70IBlGqYzRMbUCAJdpux0VXTVGNGVP3iGbTr4fYJ/S78iKQNPVf1TWWhJPLT0ENZVI"
+	"OP4UYWP9Laja4hsPn3d9iunTzG8fDovDXwRlmyTL5xzI1OC34MNwA0nVxoSsHCDHvIBT1uz3ZEaM"
+	"k4N6A8Uwifk5IlLsoeWOU83ao1+jqNXkhrLcuZOaccIr5GfI7KU+igswmH22+ncaIIuAhc8fIzSv"
+	"n01grCHeB5H3O/+Yt8ycQIoUmf+HkbpIm4v//AWIZVivgzeI2dMoJ0a8xWV+MsgKg+2NwnknIVLB"
+	"dfukgA32FjiuJ0lWZAUVsakmk0QChtTLQpkDqg==",
+	/* various lengths of zero buffers */
+	"AA==",		/*  8 */
+	"AAA=",		/*  9 */
+	"AAAA",		/* 10 */
+	"AAAAAA=="	/* 11 */
+};
+
+int
+base64_encode_s(char *e, unsigned elen, const char *s, ...)
+{
+	va_list ap;
+	int     r;
+
+	va_start(ap, s);
+	r = base64_encode_string(e, elen, magic_string_end, s, ap);
+	va_end(ap);
+	return (r);
+}
+
+void
+print_varargs(const char *s, ...)
+{
+	va_list ap;
+	const char *p;
+
+	va_start(ap, s);
+	p = s;
+	while (p != magic_string_end) {
+		if (p)
+			printf(">%s< ", p);
+		else
+			printf("(null) ");
+		p = va_arg(ap, const char *);
+	}
+	printf("\n");
+	va_end(ap);
+}
 
 int
 main(int argc, char **argv)
 {
-	int i;
-	char buf[BUFSIZ];
-	unsigned l;
+	int i, j, k, l;
+	char buf1[BUFSIZ];
+	char buf2[BUFSIZ];
+
+	int l_dec, l_dec_est, l_dec_str;
+	int l_enc, l_enc_est, l_enc_str;
+
+	/* split for varargish encode */
+#define CHUNK_LEN 9
+#define NCHUNK 8
+	char chunk[NCHUNK][CHUNK_LEN];
+	const char *cp;
+
 
 	(void)argc;
 	(void)argv;
 
-	base64_init();
-	l = sizeof buf;
-	base64_decode(buf, &l, test1);
-	printf("%s\n", buf);
+	for (i = 0; i <= MAX_TEST; i++) {
+		printf("\nTest %d:\n-------\n", i);
+
+		/* decode */
+
+		if ((l_dec = base64_decode(buf1, sizeof(buf1), test[i])) < 0) {
+			fprintf(stderr, "decode(test[%d]) failed\n", i);
+			return (1);
+		}
+		l_dec_est = base64_decode_l(strlen(test[i]));
+			
+		if (i <= MAX_PRINTABLE) {
+			printf("%s\n", buf1);
+			l_dec_str = strlen(buf1) + 1;
+			printf("decode length estimated: %d, actual %d, strlen+1 %d\n",
+			    l_dec_est, l_dec, l_dec_str);
+		} else {
+			printf("decode length estimated: %d, actual %d\n",
+			    l_dec_est, l_dec);
+		}
+
+		if (l_dec_est < l_dec) {
+			fprintf(stderr, "decode length estimate to small for test %d\n", i);
+		}
+
+
+		/* encode */
+
+		if ((l_enc = base64_encode(buf2, sizeof(buf2), buf1, l_dec)) < 0) {
+			fprintf(stderr, "encode(test[%d]) failed\n", i);
+			return (1);
+		}
+
+		l_enc_est = base64_encode_l(l_dec);
+		if (i <= MAX_PRINTABLE) {
+			l_enc_str = strlen(buf2) + 1;
+			printf("encode length estimated: %d, actual %d, strlen+1 %d\n",
+			    l_enc_est, l_enc, l_enc_str);
+		} else {
+			printf("encode length estimated: %d, actual %d\n",
+			    l_enc_est, l_enc);
+		}
+
+		if (l_enc_est < l_enc) {
+			fprintf(stderr, "encode length estimate to small for test %d\n", i);
+		}
+			
+
+		printf("%s\n", test[i]);
+		printf("%s\n", buf2);
+
+		if (strcmp(test[i], buf2) != 0) {
+			fprintf(stderr, "encode(decode(test[%d])) != test[%d]\n", i, i);
+			return (1);
+		}
+
+		if ((i > MAX_PRINTABLE) || (l_dec < (NCHUNK * CHUNK_LEN / 2)))
+			continue;
+		
+		/* varargisch encode: split input into chunks */
+		for (j = 0; j < CHUNK_LEN; j++) {
+			cp = buf1;
+
+			/* permutate lengths of chunks */
+			for (k = 0; k < NCHUNK && *cp; k++) {
+				l = strnlen(cp, (j + k + 1) % CHUNK_LEN);
+				strncpy(chunk[k], cp, l);
+				cp += l;
+				chunk[k][l] = '\0';
+			}
+
+			l_enc = base64_encode_s(buf2, sizeof(buf2),
+			    chunk[0], NULL, "",
+			    chunk[1], chunk[2], chunk[3],
+			    "", "", "", "", NULL, "",
+			    chunk[4], chunk[5], chunk[6],
+			    chunk[7], cp, magic_string_end);
+			if (l_enc < 0) {
+				fprintf(stderr, "encode(test[%d]) varargs %d"
+				    "failed\n", i, j);
+				return (1);
+			}
+
+			if (strcmp(test[i], buf2) != 0) {
+				fprintf(stderr, "encode(decode(test[%d])) "
+				    "varargs %d != test[%d]:\n", i, j, i);
+
+				print_varargs(
+				    chunk[0], NULL, "",
+				    chunk[1], chunk[2], chunk[3],
+				    "", "", "", "", NULL, "",
+				    chunk[4], chunk[5], chunk[6],
+				    chunk[7], cp, magic_string_end);
+				printf("%s\n", buf2);
+
+				return (1);
+			}
+		}
+	}
+
 	return (0);
 }
+
+#undef MAX_PRINTABLE
+#undef MAX_TEST
+#undef CHUNK_LEN
+#undef NCHUNK
 #endif
diff --git a/lib/libvarnishapi/base64_gen.py b/lib/libvarnishapi/base64_gen.py
new file mode 100644
index 0000000..a5ff4b6
--- /dev/null
+++ b/lib/libvarnishapi/base64_gen.py
@@ -0,0 +1,45 @@
+#!/usr/local/bin/python
+#
+# generate conversion tables for base64 encoding/decoding
+#
+# Written by Nils Goroll <slink@uplex.de>
+# based upon code by Poul-Henning Kamp <phk@phk.freebsd.dk>
+#
+# This file is in the public domain.
+
+
+from struct import *
+
+import sys
+srcroot = "../../.."
+buildroot = "../../.."
+if len(sys.argv) == 3:
+    srcroot = sys.argv[1]
+    buildroot = sys.argv[2]
+
+b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+b64_l = unpack('s' * len(b64), b64)
+
+i64 = [-1] * 255
+
+i = 0
+for p in b64_l:
+    i64[ord(p)] = i
+    i += 1
+
+i64[ord('=')] = 0;
+
+h = open("base64_tables.h", "w")
+
+h.write("/* THIS FILE IS AUTO-GENERATED. don't edit */\n\n")
+
+h.write("static const char b64[] =\n\t\"" + b64 + "\";\n")
+
+h.write("static const char i64[256] = {\n")
+for i in range(0, 255):
+    h.write("\t[" + str(i) + "] = " + str(i64[i]) + "," +
+            ("\t/* "+chr(i)+" */" if (i64[i] != -1) else "") + "\n")
+h.write("};\n")
+
+h.close
diff --git a/lib/libvmod_digest/Makefile.am b/lib/libvmod_digest/Makefile.am
new file mode 100644
index 0000000..8d15f93
--- /dev/null
+++ b/lib/libvmod_digest/Makefile.am
@@ -0,0 +1,31 @@
+# $Id$
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include
+
+vmoddir = $(pkglibdir)/vmods
+vmod_LTLIBRARIES = libvmod_digest.la
+
+libvmod_digest_la_LDFLAGS = -version-info 1:0:0
+
+libvmod_digest_la_LIBADD = $(top_builddir)/lib/libvarnishapi/libvarnishapi.la
+
+libvmod_digest_la_SOURCES = \
+	digest_crc32.c \
+	vmd5.h \
+	vmd5.c \
+	vcc_if.c \
+	vmod_digest.h \
+	vmod_digest.c \
+	vmod_digest_if.c
+
+vmod_srcdir = $(top_srcdir)/lib/libvmod_digest
+
+vcc_if.c vcc_if.h: $(vmod_srcdir)/vmod.py $(builddir)/vmod_digest.h $(builddir)/vmod.vcc
+	@PYTHON@ $(vmod_srcdir)/vmod.py $(builddir)/vmod.vcc
+
+vmod_digest.h vmod_digest_if.c vmod.vcc: $(vmod_srcdir)/vmod_digest.py
+	@PYTHON@ $(vmod_srcdir)/vmod_digest.py
+
+EXTRA_DIST = vmod.py vmod_digest.py
+
+CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h $(builddir)/vmod_digest.h $(builddir)/vmod_digest_if.c $(builddir)/vmod.vcc $(builddir)/vtc_snippet
diff --git a/lib/libvmod_digest/digest_crc32.c b/lib/libvmod_digest/digest_crc32.c
new file mode 100644
index 0000000..eb8f7a3
--- /dev/null
+++ b/lib/libvmod_digest/digest_crc32.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2008 Linpro AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
+ *
+ * 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.
+ *
+ * $Id$
+ *
+ * This CRC32 implementation is in the public domain.
+ *
+ * This implementation seems to be a "vanilla" CRC32 with poly 04C11DB7
+ * according to http://regregex.bbcmicro.net/crc-catalogue.htm
+ *
+ * Note that the result needs to be converted to network byte order for cross
+ * platform use!
+ */
+
+#include "config.h"
+
+#include "libvarnish.h"
+
+
+/*--------------------------------------------------------------------*/
+
+static const uint32_t digest_crc32bits[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+digest_crc32(uint32_t crc, const void *p1, unsigned l)
+{
+	const unsigned char *p;
+
+	for (p = (const unsigned char*)p1; l-- > 0; p++)
+		crc = (crc >> 8) ^ digest_crc32bits[(crc ^ *p) & 0xff];
+	return (crc);
+}
+
+uint32_t
+digest_crc32_l(const void *p1, unsigned l)
+{
+	uint32_t crc;
+
+	crc = digest_crc32(~0U, p1, l);
+	return (crc ^ ~0U);
+}
+
+#ifdef TEST_DRIVER
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+	int i, j;
+	uint32_t crc;
+	int mods[] = {18, 36};
+
+	for (i = 1; i < argc; i++)
+		for (j = 0; j < (sizeof(mods) / sizeof(*mods)); j++)
+			printf("%s mod %d : %d\n", argv[i],
+			    mods[j], digest_crc32_l(argv[i], strlen(argv[i])) % mods[j]);
+
+	return 0;
+}
+
+#endif
diff --git a/lib/libvmod_digest/vmd5.c b/lib/libvmod_digest/vmd5.c
new file mode 100644
index 0000000..1c16bbb
--- /dev/null
+++ b/lib/libvmod_digest/vmd5.c
@@ -0,0 +1,387 @@
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * This code is the same as the code published by RSA Inc.  It has been
+ * edited for clarity and style only.
+ *
+ * From: $FreeBSD: head/lib/libmd/md5c.c head/lib/libmd/sha256c.c 154479 2006-01-17 15:35:57Z phk $
+ */
+
+#include "config.h"
+
+#include "svnid.h"
+SVNID("$Id$")
+
+#include <stdint.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#define VBYTE_ORDER    __BYTE_ORDER
+#define VBIG_ENDIAN    __BIG_ENDIAN
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#define VBYTE_ORDER    _BYTE_ORDER
+#define VBIG_ENDIAN    _BIG_ENDIAN
+#endif
+
+#include "libvarnish.h"
+#include "vmd5.h"
+
+static void MD5Transform(uint32_t [4], const unsigned char [64]);
+
+#if !defined(VBYTE_ORDER) || (VBYTE_ORDER == LITTLE_ENDIAN)
+
+/* assume little endian if not defined */
+
+#define Encode memcpy
+#define Decode memcpy
+
+#else
+
+/*
+ * Encodes input (uint32_t) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+
+static void
+Encode (unsigned char *output, uint32_t *input, unsigned int len)
+{
+	unsigned int i;
+	uint32_t *op = (uint32_t *)output;
+
+	for (i = 0; i < len / 4; i++)
+		vle32enc(op[i], input[i]);
+}
+
+/*
+ * Decodes input (unsigned char) into output (uint32_t). Assumes len is
+ * a multiple of 4.
+ */
+
+static void
+Decode (uint32_t *output, const unsigned char *input, unsigned int len)
+{
+	unsigned int i;
+	const uint32_t *ip = (const uint32_t *)input;
+
+	for (i = 0; i < len / 4; i++)
+		output[i] = vle32dec(ip[i]);
+}
+#endif
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+	(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define GG(a, b, c, d, x, s, ac) { \
+	(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define HH(a, b, c, d, x, s, ac) { \
+	(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+#define II(a, b, c, d, x, s, ac) { \
+	(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = ROTATE_LEFT ((a), (s)); \
+	(a) += (b); \
+	}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context. */
+
+void
+MD5Init (context)
+	MD5_CTX *context;
+{
+
+	context->count[0] = context->count[1] = 0;
+
+	/* Load magic initialization constants.  */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xefcdab89;
+	context->state[2] = 0x98badcfe;
+	context->state[3] = 0x10325476;
+}
+
+/* 
+ * MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+
+void
+MD5Update (context, in, inputLen)
+	MD5_CTX *context;
+	const void *in;
+	unsigned int inputLen;
+{
+	unsigned int i, idx, partLen;
+	const unsigned char *input = in;
+
+	/* Compute number of bytes mod 64 */
+	idx = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+	/* Update number of bits */
+	if ((context->count[0] += ((uint32_t)inputLen << 3))
+	    < ((uint32_t)inputLen << 3))
+		context->count[1]++;
+	context->count[1] += ((uint32_t)inputLen >> 29);
+
+	partLen = 64 - idx;
+
+	/* Transform as many times as possible. */
+	if (inputLen >= partLen) {
+		memcpy((void *)&context->buffer[idx], (const void *)input,
+		    partLen);
+		MD5Transform (context->state, context->buffer);
+
+		for (i = partLen; i + 63 < inputLen; i += 64)
+			MD5Transform (context->state, &input[i]);
+
+		idx = 0;
+	}
+	else
+		i = 0;
+
+	/* Buffer remaining input */
+	memcpy ((void *)&context->buffer[idx], (const void *)&input[i],
+	    inputLen-i);
+}
+
+/*
+ * MD5 padding. Adds padding followed by original length.
+ */
+
+void
+MD5Pad (context)
+	MD5_CTX *context;
+{
+	unsigned char bits[8];
+	unsigned int idx, padLen;
+
+	/* Save number of bits */
+	Encode (bits, context->count, 8);
+
+	/* Pad out to 56 mod 64. */
+	idx = (unsigned int)((context->count[0] >> 3) & 0x3f);
+	padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+	MD5Update (context, PADDING, padLen);
+
+	/* Append length (before padding) */
+	MD5Update (context, bits, 8);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+
+void
+MD5Final (digest, context)
+	unsigned char digest[16];
+	MD5_CTX *context;
+{
+	/* Do padding. */
+	MD5Pad (context);
+
+	/* Store state in digest */
+	Encode (digest, context->state, 16);
+
+	/* Zeroize sensitive information. */
+	memset ((void *)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+
+static void
+MD5Transform (state, block)
+	uint32_t state[4];
+	const unsigned char block[64];
+{
+	uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+	Decode (x, block, 64);
+
+	/* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+	FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+	FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+	FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+	FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+	FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+	FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+	FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+	FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+	FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+	FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+	FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+	FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+	FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+	FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+	FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+	FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+	/* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+	GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+	GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+	GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+	GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+	GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+	GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+	GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+	GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+	GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+	GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+	GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+	GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+	GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+	GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+	GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+	GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+	/* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+	HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+	HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+	HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+	HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+	HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+	HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+	HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+	HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+	HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+	HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+	HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+	HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+	HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+	HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+	HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+	HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+	/* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+	II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+	II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+	II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+	II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+	II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+	II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+	II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+	II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+	II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+	II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+	II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+	II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+	II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+	II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+	II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+	II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+
+	/* Zeroize sensitive information. */
+	memset ((void *)x, 0, sizeof (x));
+}
+
+/*
+ * A few test-vectors, just in case - taken from vsha256.c and modified
+ */
+
+static const struct md5test {
+	const char              *input;
+	const unsigned char     output[16];
+} md5test[] = {
+    { "",
+      { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
+	0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
+      }
+    },
+    { "message digest",
+      { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
+	0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0
+      }
+    },
+    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+      { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 
+	0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f
+      }
+    },
+    { NULL }
+};
+
+void
+MD5Test(void)
+{
+	MD5_CTX c;
+	const struct md5test *p;
+	unsigned char o[MD5_DIGEST_LENGTH];
+
+	for (p = md5test; p->input != NULL; p++) {
+		MD5Init(&c);
+		MD5Update(&c, p->input, strlen(p->input));
+		MD5Final(o, &c);
+		assert(!memcmp(o, p->output, MD5_DIGEST_LENGTH));
+	}
+}
diff --git a/lib/libvmod_digest/vmd5.h b/lib/libvmod_digest/vmd5.h
new file mode 100644
index 0000000..4e08fcb
--- /dev/null
+++ b/lib/libvmod_digest/vmd5.h
@@ -0,0 +1,51 @@
+/*-
+ Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+Taken from: $FreeBSD: head/sys/sys/md5.h 156752 2006-03-15 19:47:12Z andre $
+ */
+
+#ifndef _VMD5_H_
+#define _VMD5_H_
+
+#include <stdint.h>
+
+#define MD5_BLOCK_LENGTH		64
+#define MD5_DIGEST_LENGTH		16
+#define MD5_DIGEST_STRING_LENGTH	(MD5_DIGEST_LENGTH * 2 + 1)
+
+/* MD5 context. */
+typedef struct MD5Context {
+  uint32_t state[4];	/* state (ABCD) */
+  uint32_t count[2];	/* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];	/* input buffer */
+} MD5_CTX;
+
+void   MD5Init (MD5_CTX *);
+void   MD5Update (MD5_CTX *, const void *, unsigned int);
+void   MD5Final (unsigned char [16], MD5_CTX *);
+char * MD5End(MD5_CTX *, char *);
+char * MD5File(const char *, char *);
+char * MD5FileChunk(const char *, char *, off_t, off_t);
+char * MD5Data(const void *, unsigned int, char *);
+void   MD5Test(void);
+
+#endif /* !_VMD5_H_ */
diff --git a/lib/libvmod_digest/vmod.py b/lib/libvmod_digest/vmod.py
new file mode 100755
index 0000000..32fb536
--- /dev/null
+++ b/lib/libvmod_digest/vmod.py
@@ -0,0 +1,236 @@
+#!/usr/local/bin/python
+#-
+# Copyright (c) 2010 Linpro AS
+# All rights reserved.
+#
+# Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
+#
+# 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.
+#
+# Read the vmod.spec file and produce the vmod.h and vmod.c files.
+#
+# vmod.h contains the prototypes for the published functions, the module
+# C-code should include this file to ensure type-consistency.
+#
+# vmod.c contains the symbols which VCC and varnishd will use to access
+# the module:  A structure of properly typed function pointers, the
+# size of this structure in bytes, and the definition of the structure
+# as a string, suitable for inclusion in the C-source of the compile VCL
+# program.
+#
+# $Id$
+
+import sys
+
+if len(sys.argv) == 2:
+	specfile = sys.argv[1]
+else:
+	specfile = "vmod.vcc"
+
+type_tab = dict()
+
+ctypes = {
+	'IP':		"struct sockaddr_storage *",
+	'STRING':	"const char *",
+	'STRING_LIST':	"const char *, ...",
+	'BOOL':		"unsigned",
+	'BACKEND':	"struct director *",
+	'TIME':		"double",
+	'REAL':		"double",
+	'DURATION':	"double",
+	'INT':		"int",
+	'HEADER':	"const char *",
+	'PRIV_VCL':	"struct vmod_priv *",
+	'PRIV_CALL':	"struct vmod_priv *",
+	'VOID':		"void",
+}
+
+#######################################################################
+
+initname = ""
+modname = "???"
+pstruct = ""
+pinit = ""
+tdl = ""
+plist = ""
+slist = ""
+
+def do_func(fname, rval, args):
+	global pstruct
+	global pinit
+	global plist
+	global slist
+	global tdl
+	#print(fname, rval, args)
+
+	proto = ctypes[rval] + " vmod_" + fname + "(struct sess *"
+	sproto = ctypes[rval] + " td_" + modname + "_" + fname + "(struct sess *"
+	s=", "
+	for i in args:
+		proto += s + ctypes[i]
+		sproto += s + ctypes[i]
+	proto += ")"
+	sproto += ")"
+
+	plist += proto + ";\n"
+	tdl += "typedef " + sproto + ";\n"
+
+	pstruct += "\ttd_" + modname + "_" + fname + "\t*" + fname + ";\n"
+	pinit += "\tvmod_" + fname + ",\n"
+
+	s = modname + '.' + fname + "\\0"
+	s += "Vmod_Func_" + modname + "." + fname + "\\0"
+	s += rval
+	for i in args:
+		s += '\\0' + i
+	slist += '\t"' + s + '\\0",\n'
+
+#######################################################################
+
+def partition(string, separator):
+	if (hasattr(string,"partition")):
+		return string.partition(separator)
+	i = string.find(separator)
+	if i >= 0:
+		return (string[:i],separator,string[i+len(separator):])
+	return (string, '', '')
+
+f = open(specfile, "r")
+
+for l0 in f:
+	# print("# " + l0)
+	l0=l0.strip()
+	if l0 == "":
+		continue
+	i = l0.find("#")
+	if i == 0:
+		continue
+	if i >= 0:
+		line = l0[:i-1]
+	else:
+		line = l0
+	line = line.expandtabs().strip()
+	l = partition(line, " ")
+
+	if l[0] == "Module":
+		modname = l[2].strip();
+		continue
+
+	if l[0] == "Init":
+		initname = l[2].strip();
+		continue
+
+	if l[0] != "Function":
+		assert False
+
+	l = partition(l[2].strip(), " ")
+	rt_type = l[0]
+
+	l = partition(l[2].strip(), "(")
+	fname = l[0].strip()
+
+	args = list()
+
+	while True:
+		l = partition(l[2].strip(), ",")
+		if len(l[2]) == 0:
+			break
+		args.append(l[0])
+	l = partition(l[0].strip(), ")")
+	args.append(l[0])
+	do_func(fname, rt_type, args)
+
+#######################################################################
+def dumps(s):
+	while True:
+		l = partition(s, "\n")
+		if len(l[0]) == 0:
+			break
+		fc.write('\t"' + l[0] + '\\n"\n')
+		s = l[2]
+
+#######################################################################
+
+if initname != "":
+	plist += "int " + initname + "(struct vmod_priv *, const struct VCL_conf *);\n"
+	pstruct += "\tvmod_init_f\t*_init;\n"
+	pinit += "\t" + initname + ",\n"
+	slist += '\t"INIT\\0Vmod_Func_' + modname + '._init",\n'
+
+#######################################################################
+
+def file_header(fo):
+        fo.write("""/*
+ *
+ * NB:  This file is machine generated, DO NOT EDIT!
+ *
+ * Edit vmod.vcc and run vmod.py instead
+ */
+
+""")
+
+#######################################################################
+
+fc = open("vcc_if.c", "w")
+fh = open("vcc_if.h", "w")
+
+file_header(fc)
+file_header(fh)
+
+fh.write('struct sess;\n')
+fh.write('struct VCL_conf;\n')
+fh.write('struct vmod_priv;\n')
+fh.write("\n");
+
+fh.write(plist)
+
+
+fc.write('#include "vcc_if.h"\n')
+fc.write('#include "vrt.h"\n')
+fc.write("\n");
+
+fc.write("\n");
+
+fc.write(tdl);
+fc.write("\n");
+
+fc.write('const char Vmod_Name[] = "' + modname + '";\n')
+
+fc.write("const struct Vmod_Func_" + modname + " {\n")
+fc.write(pstruct + "} Vmod_Func = {\n" + pinit + "};\n")
+fc.write("\n");
+
+fc.write("const int Vmod_Len = sizeof(Vmod_Func);\n")
+fc.write("\n");
+
+fc.write('const char Vmod_Proto[] =\n')
+dumps(tdl);
+fc.write('\t"\\n"\n')
+dumps("struct Vmod_Func_" + modname + " {\n")
+dumps(pstruct + "} Vmod_Func_" + modname + ";\n")
+fc.write('\t;\n')
+fc.write("\n");
+
+fc.write('const char * const Vmod_Spec[] = {\n' + slist + '\t0\n};\n')
+
+fc.write("\n")
+
diff --git a/lib/libvmod_digest/vmod_digest.c b/lib/libvmod_digest/vmod_digest.c
new file mode 100644
index 0000000..c09a9b3
--- /dev/null
+++ b/lib/libvmod_digest/vmod_digest.c
@@ -0,0 +1,656 @@
+/*-
+ *
+ * VCL module (vmod) providing some hash / message digest functions
+ *
+ * Copyright (c) 2010-2011 UPLEX, Nils Goroll
+ * All rights reserved.
+ *
+ * initially based upon vmod_std.c by Poul-Henning Kamp <phk@phk.freebsd.dk>
+ *
+ * Author: Nils Goroll <slink@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 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.
+ *
+ * NOTE ON CRC32:
+ *
+ * This code was written for the original Varnish crc32 implementation by
+ * phk. The crc32 code from libvgz yields different results.
+ *
+ * XXX: revisit later and implement several flavors, if needed
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+
+#include "vrt.h"
+#include "vsha256.h"
+#include "vmd5.h"
+#include "varnishapi.h"
+#include "vmod_digest.h"
+
+#define DIGEST_WS(sp) (sp)->wrk->ws
+
+/*
+ * common structure to keep results from all digests
+ *
+ * result must get filled up from lowest to highest bits
+ * thinking big-endian (rightmost)
+ * 
+ */
+
+#define DIGEST_RESULT_SIZE 32
+#define DIGEST_RESULT_SIZE_UINT32 (DIGEST_RESULT_SIZE *	\
+	sizeof(unsigned char) / sizeof(uint32_t))
+
+struct digest_data {
+	unsigned		magic;
+#define DIGEST_MAGIC		0x0d12a0b2
+	enum digest_type	type;
+	void 			*state;
+	size_t			state_size;
+
+	union {
+		unsigned char	uchar[DIGEST_RESULT_SIZE];
+		uint32_t	uint32[DIGEST_RESULT_SIZE_UINT32];
+	} result;
+	size_t			result_size;
+
+	/* how to free state and myself */
+	vmod_priv_free_f	*free;
+};
+
+/* set to the largest context of all hashes */
+
+#if 0 /* (sizeof(MD5_CTX) > sizeof(struct SHA256Context)) */
+#define DIGEST_DATA_SIZE				\
+	(sizeof(struct digest_data) + sizeof(MD5_CTX))
+#else
+#define DIGEST_DATA_SIZE						\
+	(sizeof(struct digest_data) + sizeof(struct SHA256Context))
+#endif
+
+/* set to the largest block size of all hashes */
+
+#define DIGEST_BLOCK_SIZE	64
+
+/*
+ * space will be allocated from DIGEST_WS(sp), unless buf is passed,
+ * which must be at least of size DIGEST_DATA_SIZE
+ * 
+ */
+static struct digest_data *
+digest_init(const enum digest_type type, struct sess *sp, void *bufp)
+{
+	struct digest_data	*data;
+	size_t 			sz;
+	char			*buf = bufp;
+
+	if (buf) {
+		data = (void *)buf;
+		buf += sizeof(struct digest_data);
+	} else {
+		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+		data = (void *)WS_Alloc(DIGEST_WS(sp), sizeof(struct digest_data));
+	}
+
+	AN(data);
+	memset(data, 0, sizeof(struct digest_data));
+
+	switch (type) {
+	case DEFAULT:
+	case CRC32:
+	case PLAIN:
+		data->result_size = sizeof(uint32_t);
+#if 0
+		data->result.uint32[0] = 0U;	/* implicit */
+#endif
+		/* no data->state, result _is_ the state */
+		goto done;
+	case RS:
+		sz = sizeof(uint32_t);		
+		data->result_size = sz;
+		goto alloc_state;
+	case SHA256:
+		sz = sizeof(struct SHA256Context);
+		data->result_size = 32;
+		goto alloc_state;
+	case MD5:
+		sz = sizeof(MD5_CTX);
+		data->result_size = MD5_DIGEST_LENGTH;
+		goto alloc_state;
+	default:
+		INCOMPL();
+		return (0);
+	}
+
+  alloc_state:
+	if (buf) {			
+		data->state = (void *)buf;
+		buf += sz;
+	} else {
+		data->state = WS_Alloc(DIGEST_WS(sp), sz);
+	}
+	AN(data->state);
+	memset(data->state, 0, sz);
+	data->state_size = sz;
+	
+  done:
+	data->magic = DIGEST_MAGIC;
+	return (data);
+}
+
+
+static void
+digest_free(struct digest_data *d)
+{
+	AN(d);
+
+	/* zero out the internal state, if any */
+	if (d->state && d->state_size)
+		memset(d->state, 0, d->state_size);
+
+	if (!d->free)
+		return;
+
+	if (d->state)
+		d->free(d->state);
+	
+	d->free(d);
+}
+
+#define VA_VSTRING_FOREACH(p, s, ap)					\
+	for (p = s; p != vrt_magic_string_end; p = ap ? va_arg(ap, const char *) : vrt_magic_string_end)
+
+/*
+ * digest: run varargs trough digest alg and return digest_data
+ *
+ *
+ * data must have been intialized with digest_init
+ *
+ * We store the uint32_t based digest in network byte order, to allow using
+ * these checksums cross-platform (and big endian is "network byte order", so
+ * this seems most natural).
+ *
+ * see also: http://www.boost.org/doc/libs/1_39_0/libs/crc/crc.html:
+ *
+ * Note that the CRC has to be stored with the more-significant bytes first
+ * (big-endian).
+ *
+ * checked with http://www.lammertbies.nl/comm/info/crc-calculation.html
+ *		http://www.pvlteam.com/doc/crc/testonline.aspx
+ */
+
+static struct digest_data *
+digest(const enum digest_type type, struct digest_data *data,
+       const char *s,  va_list ap)
+{
+	const char *p;
+	uint32_t res;
+
+	CHECK_OBJ_NOTNULL(data, DIGEST_MAGIC);
+
+	switch (type) {
+	case DEFAULT:
+	case CRC32:
+		res = ntohl(data->result.uint32[0] ^ ~0U);
+
+		VA_VSTRING_FOREACH(p, s, ap)
+		    res = digest_crc32(res, p, strlen(p));
+
+		data->result.uint32[0] = htonl(res ^ ~0U);
+		break;
+	case PLAIN:
+		res = ntohl(data->result.uint32[0]);
+
+		VA_VSTRING_FOREACH(p, s, ap)
+		    while (*p)
+			    res += *p++;
+
+		data->result.uint32[0] = htonl(res);
+		break;
+	case RS:
+	{
+		/* hash function from Robert Sedgwicks 'Algorithms in C' book */
+		const uint32_t b    = 378551;
+		uint32_t a	    = 63689;
+
+		res = ntohl(data->result.uint32[0]);
+
+		VA_VSTRING_FOREACH(p, s, ap)
+		    while (*p) {
+			    res = res * a + (*p++);
+			    a *= b;
+		    }
+
+		data->result.uint32[0] = htonl(res);
+		break;
+	}
+	case SHA256:
+	{
+		struct SHA256Context *sha256 = data->state;
+
+		SHA256_Init(sha256);
+		VA_VSTRING_FOREACH(p, s, ap)
+		    SHA256_Update(sha256, p, strlen(p));
+		SHA256_Final(data->result.uchar, sha256);
+		break;
+	}
+	case MD5:
+	{
+		MD5_CTX *md5 = data->state;
+
+		MD5Init(md5);
+		VA_VSTRING_FOREACH(p, s, ap)
+		    MD5Update(md5, p, strlen(p));
+		MD5Final(data->result.uchar, md5);
+		break;
+	}
+	default:
+		INCOMPL();
+		return (0);
+	}
+	return (data);
+}
+
+static size_t
+hmac_digest_block_size(const enum digest_type type)
+{
+	switch (type) {
+	case DEFAULT:
+	case CRC32:
+	case PLAIN:
+	case RS:
+		/* invalid for hmac */
+		return (0);
+	case SHA256:
+	case MD5:
+		/* XXX add macros to the digest impl to use here */
+		return (64);
+	default:
+		INCOMPL();
+		return (0);
+	}
+}
+
+
+/* 
+ * Implementation based upon http://en.wikipedia.org/wiki/HMAC
+ * 
+ * (yeah, I've been lazy :) )
+ */
+
+static struct digest_data *
+hmac(const enum digest_type type, struct digest_data *data,
+     const char *key, const char *s, va_list ap)
+{
+	/*
+	 * we need one temporary hash internally, which we're going to
+	 * reuse, to:
+	 * - optionally hash the key if it is longer than the block size
+	 * - calculate the inner hash
+	 */
+
+	char			buf[DIGEST_DATA_SIZE];
+	struct digest_data 	*tmp;
+
+	size_t			key_l;
+	unsigned char		i_key_pad[DIGEST_BLOCK_SIZE+1];
+	unsigned char		o_key_pad[DIGEST_BLOCK_SIZE+1];
+
+	size_t			blocksize = hmac_digest_block_size(type);
+
+	int			i;
+	const char     		*p;
+
+	CHECK_OBJ_NOTNULL(data, DIGEST_MAGIC);
+
+	tmp = NULL;
+
+	if (blocksize == 0) {
+		INCOMPL();
+		return (NULL);
+	}
+
+	key_l = strlen(key);
+	/* 
+	 * XXX: We really don't want to return an HMAC based on a zero key
+	 */
+	if (key_l == 0)
+		return NULL;
+	/*
+	 *  if (length(key) > blocksize) then
+	 *    key = hash(key) // keys longer than blocksize are shortened
+	 */
+	if (key_l > blocksize) {		
+		tmp = digest_init(type, NULL, buf);
+		digest(type, tmp, key, NULL);
+		key = tmp->result.uchar;
+		key_l = tmp->result_size;
+	}
+
+	assert(key_l <= blocksize);
+
+	/* implicit in the following code:
+	 *  if (length(key) < blocksize) then
+	 *    key = key || zeroes(blocksize - length(key))
+	 *
+	 * o_key_pad = [0x5c * blocksize] ⊕ key
+	 * i_key_pad = [0x36 * blocksize] ⊕ key
+	 */
+
+	for (i = 0; i < key_l; i++) {
+		o_key_pad[i] = 0x5c ^ key[i];
+		i_key_pad[i] = 0x36 ^ key[i];
+	}
+
+	/* PAD... probably not worth calling memset */
+	while (i < blocksize) {
+		o_key_pad[i] = 0x5c;
+		i_key_pad[i] = 0x36;
+		i++;
+	}
+
+	/* 
+	 * At this point, tmp is free for reuse as the inner hash
+	 *
+	 * For completeness, call free, but as we've alloc'ed from buf, this
+	 * should be a no-op (except for nulling)
+	 */
+	if (tmp)
+		digest_free(tmp);
+
+	/* 
+	 * return hash(o_key_pad || hash(i_key_pad || message))
+	 *
+	 * generate the inner hash and the final hash
+	 */
+	 
+	tmp = digest_init(type, NULL, buf);
+
+	switch (type) {
+	case SHA256:
+	{
+		struct SHA256Context *inner = tmp->state;
+		struct SHA256Context *outer = data->state;
+
+		/* inner */
+		SHA256_Init(inner);
+		SHA256_Update(inner, i_key_pad, blocksize);
+		VA_VSTRING_FOREACH(p, s, ap)
+		    SHA256_Update(inner, p, strlen(p));
+		SHA256_Final(tmp->result.uchar, inner);
+
+		/* outer */
+		SHA256_Init(outer);
+		SHA256_Update(outer, o_key_pad, blocksize);
+		SHA256_Update(outer, tmp->result.uchar, tmp->result_size);
+		SHA256_Final(data->result.uchar, outer);
+
+		break;
+	}
+	case MD5:
+	{
+		MD5_CTX *inner = tmp->state;
+		MD5_CTX *outer = data->state;
+
+		/* inner */
+		MD5Init(inner);
+		MD5Update(inner, i_key_pad, blocksize);
+		VA_VSTRING_FOREACH(p, s, ap)
+		    MD5Update(inner, p, strlen(p));
+		MD5Final(tmp->result.uchar, inner);
+
+		/* outer */
+		MD5Init(outer);
+		MD5Update(outer, o_key_pad, blocksize);
+		MD5Update(outer, tmp->result.uchar, tmp->result_size);
+		MD5Final(data->result.uchar, outer);
+
+		break;
+	}
+	default:
+		INCOMPL();
+		return (0);
+	}
+
+	return (data);
+}
+
+static inline char
+hexnibble(char c)
+{
+	return ((c > 9) ? ('A' + (c - 0xa)) : ('0' + c));
+}
+
+static inline char
+hexnibblelc(char c)
+{
+	return ((c > 9) ? ('a' + (c - 0xa)) : ('0' + c));
+}
+
+/* get size for specified encoding */
+
+static inline size_t
+encode_l(const enum encoding_type enc, size_t src_size)
+{
+	switch (enc) {
+	case BASE64:
+		return (base64_encode_l(src_size));
+	case HEX:
+	case HEXLC:
+		return (src_size * 2 + 1);
+	default:
+		INCOMPL();
+		return (0);
+	}
+}
+
+/* encode data with the specified encoding */
+
+static char *
+encode(const enum encoding_type enc, char *dst, size_t dst_size,
+	char *src, size_t src_size)
+{
+	char	*p;
+	int	i;
+
+	switch (enc) {
+	case BASE64:
+		base64_encode(dst, dst_size, src, src_size);
+		break;
+	case HEX:
+		for (i = 0, p = dst; i < src_size; i++, p += 2) {
+			/* sprintf(p, "%02X", src[i]); */
+			*p = hexnibble((src[i] & 0xf0) >> 4);
+			*(p+1) = hexnibble(src[i] & 0x0f);
+		}
+		*p = '\0';
+		break;
+	case HEXLC:
+		for (i = 0, p = dst; i < src_size; i++, p += 2) {
+			/* sprintf(p, "%02x", src[i]); */
+			*p = hexnibblelc((src[i] & 0xf0) >> 4);
+			*(p+1) = hexnibblelc(src[i] & 0x0f);
+		}
+		*p = '\0';
+		break;
+	default:
+		INCOMPL();
+		return (NULL);
+	}
+	return (dst);
+}
+
+
+/*
+ * digest_string, encode_string:
+ *
+ * Functions called from auto-generated vmod wrappers (see vmod_digest.py)
+ */
+ 
+
+char *
+digest_string(const enum digest_type type, const enum encoding_type enc,
+	      struct sess *sp, const char *s, va_list ap)
+{
+	/* alloc scratch for digest_data on stack */
+	char			buf[DIGEST_DATA_SIZE];
+	struct digest_data 	*data = digest_init(type, sp, buf);
+	char			*res;
+	size_t			l;
+
+	AN(data);
+	digest(type, data, s, ap);
+	l = encode_l(enc, data->result_size);
+	res = WS_Alloc(DIGEST_WS(sp), l);
+	if (!res)
+		return (NULL);
+	if (!encode(enc, res, l, data->result.uchar, data->result_size))
+		return (NULL);
+
+	/*
+	 * not strictly needed because alloc'd from stack, calling free for
+	 * completeness 
+	 */
+	digest_free(data);
+
+	return (res);
+}
+
+char *
+hmac_string(const enum digest_type type, const enum encoding_type enc,
+	struct sess *sp, const char *key, const char *s, va_list ap)
+{
+	/* alloc scratch for digest_data on stack */
+	char			buf[DIGEST_DATA_SIZE];
+	struct digest_data 	*data = digest_init(type, sp, buf);
+	char			*res;
+	size_t			l;
+
+	AN(data);
+	hmac(type, data, key, s, ap);
+	l = encode_l(enc, data->result_size);
+	res = WS_Alloc(DIGEST_WS(sp), l);
+	if (!res)
+		return (NULL);
+	if (!encode(enc, res, l, data->result.uchar, data->result_size))
+		return (NULL);
+
+	/*
+	 * not strictly needed because alloc'd from stack, calling free for
+	 * completeness 
+	 */
+	digest_free(data);
+
+	return (res);
+}
+
+char *
+encode_string(const enum encoding_type enc,
+	      struct sess *sp, const char *s, va_list ap)
+{
+	const char		*p;
+	size_t			l;
+	unsigned 		u;
+	char			*b, *e;
+
+
+	/* get memory. skeleton taken from vmoc_std.c:vmod_updown() */
+	u = WS_Reserve(DIGEST_WS(sp), 0);
+	e = b = DIGEST_WS(sp)->f;
+	e += u;
+
+	switch (enc) {
+	case BASE64:
+		l = base64_encode_string(b, u, vrt_magic_string_end, s, ap);
+		if (l < 0)
+			return (NULL);
+		b += l;
+		break;
+	case HEX:
+	case HEXLC:
+		p = s;
+		while (p != vrt_magic_string_end && b < e) {
+			if (p != NULL) {
+				for (; (b < e - 1) && *p != '\0'; p++) {
+					*b++ = (enc == HEX) ?
+					    hexnibble((*p & 0xf0) >> 4) :
+					    hexnibblelc((*p & 0xf0) >> 4);
+					*b++ = (enc == HEX) ?
+					    hexnibble(*p & 0x0f) :
+					    hexnibblelc(*p & 0x0f);
+				}
+			}
+			p = va_arg(ap, const char *);
+		}
+		if (b < e)
+			*b = '\0';
+		b++;
+		break;
+	default:
+		INCOMPL();
+		return (NULL);
+	}
+
+	if (b > e) {
+		WS_Release(DIGEST_WS(sp), 0);
+		return (NULL);
+	} else {
+		e = b;
+		b = DIGEST_WS(sp)->f;
+		WS_Release(DIGEST_WS(sp), e - b);
+		return (b);
+	}
+}
+
+#ifdef HAVE_VMOD
+int
+init_function(struct vmod_priv *priv, const struct VCL_conf *cfg)
+#else
+void
+VRT_Digest_Init(void)
+#endif
+{
+	MD5Test();
+	
+	/* for hexnibble */
+	assert(('b' - 'a' == 1) && ('B' - 'A' == 1) && ('2' - '1' == 1));
+
+	/* for digest (result) size */
+	assert((DIGEST_RESULT_SIZE >= SHA256_LEN) &&
+	    (DIGEST_RESULT_SIZE >= MD5_DIGEST_LENGTH) &&
+	    (DIGEST_RESULT_SIZE >= sizeof(uint32_t)));
+	
+	/* for context size allocation */
+	assert((DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(struct SHA256Context))) &&
+	    (DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(struct MD5Context))) &&
+	    (DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(uint32_t))));
+
+#ifdef HAVE_VMOD
+	return (0);
+#endif
+}
diff --git a/lib/libvmod_digest/vmod_digest.py b/lib/libvmod_digest/vmod_digest.py
new file mode 100644
index 0000000..b6188db
--- /dev/null
+++ b/lib/libvmod_digest/vmod_digest.py
@@ -0,0 +1,212 @@
+#!/usr/local/bin/python
+#-
+# Copyright (c) 2010-2011 UPLEX, Nils Goroll
+# All rights reserved.
+#
+# Author: Nils Goroll <slink@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.
+#
+# Read the vmod.spec file and produce the vmod.h and vmod.c files.
+#
+# vmod.h contains the prototypes for the published functions, the module
+# C-code should include this file to ensure type-consistency.
+#
+# vmod.c contains the symbols which VCC and varnishd will use to access
+# the module:  A structure of properly typed function pointers, the
+# size of this structure in bytes, and the definition of the structure
+# as a string, suitable for inclusion in the C-source of the compile VCL
+# program.
+#
+# $Id$
+
+import sys
+srcroot = "../../.."
+buildroot = "../../.."
+if len(sys.argv) == 3:
+        srcroot = sys.argv[1]
+        buildroot = sys.argv[2]
+
+digest_types = [
+	'DEFAULT',
+	'CRC32',
+	'SHA256',
+	'PLAIN',
+	'RS',
+	'MD5'
+]
+
+hmac_types = [
+	'SHA256',
+	'MD5'
+]
+
+encoding_types = [
+	'BASE64',
+	'HEX',
+	'HEXLC'
+]
+
+h = open("vmod_digest.h", "w")
+c = open("vmod_digest_if.c", "w")
+vcc = open("vmod.vcc", "w")
+vtc = open("vtc_snippet","w")
+
+h.write("/* THIS FILE IS AUTO-GENERATED. don't edit */\n")
+h.write("#define HAVE_VMOD\n")
+
+c.write("/* THIS FILE IS AUTO-GENERATED. don't edit */\n")
+c.write("#include \"vmod_digest.h\"\n")
+
+vcc.write("# THIS FILE IS AUTO-GENERATED. don't edit\n")
+vtc.write("# THIS FILE IS AUTO-GENERATED. don't edit\n")
+vtc.write("# use this code snippet for the vtc test\n")
+
+h.write("""
+
+#include <stdarg.h>
+#include "../../bin/varnishd/cache.h"
+
+/* from libvarnish/crc32.c */
+uint32_t crc32(uint32_t crc, const void *p1, unsigned l);
+uint32_t crc32_l(const void *p1, unsigned l);
+""")
+
+h.write("enum digest_type {\n\t" +
+	",\n\t".join(digest_types) +
+	"\n};\n")
+h.write("enum encoding_type {\n\t" +
+	",\n\t".join(encoding_types) +
+	"\n};\n")
+h.write("\n")
+h.write("""
+char *digest_string(const enum digest_type type, const enum encoding_type enc,
+	      struct sess *sp, const char *s, va_list ap);
+char *encode_string(const enum encoding_type enc,
+	      struct sess *sp, const char *s, va_list ap);
+char *hmac_string(const enum digest_type type, const enum encoding_type enc,
+	struct sess *sp, const char *key, const char *s, va_list ap);
+
+""")
+h.write("\n")
+
+vcc.write("""
+Module digest
+Init init_function
+""")
+
+# output wrappers for "digest&encode" functions
+#
+# Function STRING digest_encoder(STRING_LIST)
+# const char *vmod_digest_encoder_(sp ...)
+
+for d in digest_types:
+	for e in encoding_types:
+		vcc.write("Function STRING " + 
+			  d.lower() + "_" + e.lower() + 
+			  "(STRING_LIST)\n")
+		h.write("const char *" + 
+"vmod_" + d.lower() + "_" + e.lower() + "(struct sess *sp, const char *s, ...);\n")
+		c.write("""
+const char *
+vmod_""" + d.lower() + "_" + e.lower() + """(struct sess *sp, const char *s, ...)
+{
+	const char *p;
+	va_list ap;
+
+	va_start(ap, s);
+	p = digest_string(""" + ", ".join([d, e, "sp", "s", "ap"]) + """);
+	va_end(ap);
+        return (p);
+}
+""")
+		vtc.write("\t\tset resp.http." +
+			  d.lower() + "-" + e.lower() +
+			  " = digest." +
+			  d.lower() + "_" + e.lower() + 
+			  """("123" + "456");"""+ "\n")
+
+# output wrappers for "hmac & encode" functions
+#
+# Function STRING hmac_encoder(STRING, STRING_LIST)
+# const char *vmod_hmac_encoder_(sp ...)
+
+for d in hmac_types:
+	for e in encoding_types:
+		vcc.write("Function STRING hmac_" + 
+			  d.lower() + "_" + e.lower() + 
+			  "(STRING, STRING_LIST)\n")
+		h.write("const char *" + 
+"vmod_hmac_" + d.lower() + "_" + e.lower() + "(struct sess *sp, const char *key, const char *s, ...);\n")
+		c.write("""
+const char *
+vmod_hmac_""" + d.lower() + "_" + e.lower() + """(struct sess *sp, const char *key, const char *s, ...)
+{
+	const char *p;
+	va_list ap;
+
+	va_start(ap, s);
+	p = hmac_string(""" + ", ".join([d, e, "sp", "key", "s", "ap"]) + """);
+	va_end(ap);
+        return (p);
+}
+""")
+		vtc.write("\t\tset resp.http.hmac_" +
+			  d.lower() + "-" + e.lower() +
+			  " = digest.hmac_" +
+			  d.lower() + "_" + e.lower() + 
+			  """("key", "123" + "456");"""+ "\n")
+
+# output wrappers for "encode" functions
+#
+# Function STRING digest_encoder(STRING_LIST)
+# const char *vmod_digest_encoder_(sp ...)
+
+vtc.write("\n#\t\tencodings (no digest)\n")
+
+for e in encoding_types:
+	vcc.write("Function STRING encode_" + e.lower() + "(STRING_LIST)\n")
+	h.write("const char *" +
+		"vmod_encode_" + e.lower() + "(struct sess *sp, const char *s, ...);\n")
+	c.write("""
+const char *
+vmod_encode_""" + e.lower() + """(struct sess *sp, const char *s, ...)
+{
+	const char *p;
+	va_list ap;
+
+	va_start(ap, s);
+	p = encode_string(""" + ", ".join([e, "sp", "s", "ap"]) + """);
+	va_end(ap);
+        return (p);
+}
+""")
+	vtc.write("\t\tset resp.http." +
+		  e.lower() +
+		  " = digest.encode_" +
+		  e.lower() + 
+		  """("123" + "456");"""+ "\n")
+
+h.close
+c.close
+vcc.close
+vtc.close
-- 
1.5.6.5

