Commit efa1e7d6 authored by Geoff Simmons's avatar Geoff Simmons

Merge branch 'body' into 'master'

Add hashing of req.body, bereq.body and resp.body to .update()

See merge request uplex/varnish/libvmod-blobdigest!1
parents 4bdc5999 98638e57
......@@ -37,3 +37,4 @@ Makefile.in
/src/tests/*.trs
libvmod-blobdigest-*.tar.gz
test-suite.log
......@@ -25,7 +25,7 @@ SYNOPSIS
new xdigest = blobdigest.digest(ENUM hash, BLOB init, ENUM scope)
BOOL xdigest.update(BLOB)
BOOL xdigest.update([BLOB msg], [ENUM from], BOOL fail)
BLOB xdigest.final()
......@@ -46,7 +46,7 @@ SYNOPSIS
::
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init], ["TASK"|"TOP"])
BOOL <obj>.update(BLOB)
BOOL <obj>.update(BLOB msg)
BLOB <obj>.final()
BLOB blobdigest.hash(ENUM hash, BLOB msg)
......@@ -140,7 +140,7 @@ Here are some examples::
sub vcl_recv {
# Use the fooprefix object to get the hash of "foobar"
if (!fooprefix.update(blob.decode(encoded="bar"))) {
if (!fooprefix.update(blob.decode(encoded="bar"), fail=false)) {
call do_error;
}
set req.http.Foobar-Hash = blob.encode(encoding=BASE64,
......@@ -152,7 +152,7 @@ Here are some examples::
# The uses of the object in client and backend contexts have
# no effect on each other, or on subsequent client or
# backend transactions.
if (!fooprefix.update(blob.decode(encoded="baz"))) {
if (!fooprefix.update(blob.decode(encoded="baz"), fail=false)) {
call do_error;
}
set req.http.Foobaz-Hash = blob.encode(encoding=BASE64,
......@@ -266,10 +266,26 @@ Example::
.. _xdigest.update():
BOOL xdigest.update(BLOB)
-------------------------
BOOL xdigest.update([BLOB msg], [ENUM from], BOOL fail)
-------------------------------------------------------
::
BOOL xdigest.update(
[BLOB msg],
[ENUM {req_body, bereq_body, resp_body} from],
BOOL fail=1
)
If the BLOB ``msg`` is provided, incrementally add it to the digest
context of this object.
If a ``from`` argument is given, incrementally add the named body to
the digest context of this object.
If both arguments are given, they are added in the order documented
herein.
Incrementally add the BLOB to the digest context of this object.
Returns ``true`` if and only if the operation was successful.
As described above: if a digest object is updated in ``vcl_init``,
......@@ -280,11 +296,16 @@ task (client or backend transaction).
This method MAY NOT be called after ``.final()`` has been called
for the same object, either in ``vcl_init`` or in the current task.
The method fails and returns ``false`` if the BLOB is NULL, or if it
is called after ``.final()``. If it fails in ``vcl_init``, the VCL
load will fail with an error message. If it fails in any other VCL
subroutine, an error message is emitted to the Varnish log with the
``VCL_Error`` tag, and the message digest context is unchanged.
The method returns ``false`` if ``msg`` is NULL, if the body as per
the ``from`` argument is not available from the current context, or if
it is called after ``.final()``. If the ``fail`` argument is ``true``
(the default), this also triggers a VCL Error. A ``fail=false``
argument can be used for explicit error handling.
If it fails in ``vcl_init``, the VCL load will fail with an error
message. If it fails in any other VCL subroutine, an error message is
emitted to the Varnish log with the ``VCL_Error`` tag, and the message
digest context is unchanged.
Example::
......@@ -310,13 +331,13 @@ Example::
sub vcl_recv {
# Update the SHA3_256 digest in the current client transaction
if (!sha3_256.update(blob.blob(IDENTITY, "bar"))) {
if (!sha3_256.update(blob.blob(IDENTITY, "bar")), fail=false) {
call do_client_error;
}
sub vcl_backend_fetch {
# Update the SHA3_256 digest in the current backend transaction
if (!sha3_256.update(blob.blob(IDENTITY, "baz"))) {
if (!sha3_256.update(blob.blob(IDENTITY, "baz")), fail=false) {
call do_backend_error;
}
......
# looks like -*- vcl -*-
varnishtest "from = ...body with .update()"
varnish v1 -vcl {
import blobdigest;
import blob;
import std;
backend none none;
sub vcl_init {
new a = blob.blob(IDENTITY, "a");
new b = blob.blob(IDENTITY, "b");
new d1 = blobdigest.digest(SHA256);
new d2 = blobdigest.digest(SHA256, a.get());
}
sub vcl_backend_error {
set beresp.status = 200;
set beresp.http.d1 = blob.encode(HEX, LOWER, d1.final());
set beresp.http.d2 = blob.encode(HEX, LOWER, d2.final());
set beresp.http.d1r = bereq.http.d1r;
set beresp.http.d2r = bereq.http.d2r;
set beresp.body = bereq.url;
return (deliver);
}
sub vcl_backend_fetch {
if (bereq.http.unset ~ "b") {
unset bereq.body;
}
set bereq.http.d1r = d1.update(from = bereq_body);
set bereq.http.d2r = d2.update(b.get(), from = bereq_body,
fail = false);
}
sub vcl_recv {
if (req.url ~ "^/cache") {
std.cache_req_body(1m);
}
if (req.url ~ "bereq_body$") {
return(pass);
}
if (req.url ~ "req_body$") {
set req.http.d1r = d1.update(from = req_body);
set req.http.d2r = d2.update(b.get(),
from = req_body, fail = false);
return(synth(200));
}
return(synth(400));
}
sub vcl_synth {
if (resp.status != 200) {
return(deliver);
}
set resp.http.d1 = blob.encode(HEX, LOWER, d1.final());
set resp.http.d2 = blob.encode(HEX, LOWER, d2.final());
set resp.http.d1r = req.http.d1r;
set resp.http.d2r = req.http.d2r;
}
sub vcl_deliver {
# this is a different d1 than on the backend side
# because PRIV_TASK
set resp.http.d1dr = d1.update(from = resp_body);
set resp.http.d1d = blob.encode(HEX, LOWER, d1.final());
}
} -start
client c1 {
txreq -url "/cache_req_body"
rxresp
expect resp.status == 200
# empty
expect resp.http.d1 == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
# "ab"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
txreq -url "/cache_req_body" -body "foo"
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "1530db9d21811ed52817f99bf207bbc0aeb433c6f0f1b37e571d28890d33078d"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
txreq -url "/cache_req_body" -nolen -hdr "Transfer-encoding: chunked"
chunked f
chunked o
chunked o
chunkedlen 0
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "1530db9d21811ed52817f99bf207bbc0aeb433c6f0f1b37e571d28890d33078d"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
} -start
# without a cached req body, d2 is always just the hash of "ab"
client c2 {
txreq -url "/req_body"
rxresp
expect resp.status == 200
# empty
expect resp.http.d1 == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
# "ab"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
txreq -url "/req_body" -body "foo"
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "false"
txreq -url "/req_body" -nolen -hdr "Transfer-encoding: chunked"
chunked f
chunked o
chunked o
chunkedlen 0
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "false"
} -start
client c3 {
txreq -url "/cache_bereq_body"
rxresp
expect resp.status == 200
# empty
expect resp.http.d1 == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
# "ab"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "307b3532e01bc16bd0ebdb42255d22b01b7fe244227a4dfe13ef5203a24b0c9c"
txreq -url "/cache_bereq_body" -body "foo"
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "1530db9d21811ed52817f99bf207bbc0aeb433c6f0f1b37e571d28890d33078d"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "307b3532e01bc16bd0ebdb42255d22b01b7fe244227a4dfe13ef5203a24b0c9c"
txreq -url "/cache_bereq_body" -nolen -hdr "Transfer-encoding: chunked"
chunked f
chunked o
chunked o
chunkedlen 0
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "1530db9d21811ed52817f99bf207bbc0aeb433c6f0f1b37e571d28890d33078d"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "307b3532e01bc16bd0ebdb42255d22b01b7fe244227a4dfe13ef5203a24b0c9c"
# was broken before #3914 merge / ace59ccfe8ad99aaa96dc5d9fdcda447086a1225
txreq -url "/cache_bereq_body" -hdr "unset: b" -body "foo"
rxresp
expect resp.status == 200
expect resp.http.d1 == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "307b3532e01bc16bd0ebdb42255d22b01b7fe244227a4dfe13ef5203a24b0c9c"
} -start
# without a cached req body, d2 is always just the hash of "ab"
client c4 {
txreq -url "/bereq_body"
rxresp
expect resp.status == 200
# empty
expect resp.http.d1 == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
# "ab"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "true"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "966b82e2e767ff8fdfa80ef1e6e1ca2467d7a39e783b46ffe5dcec7f7d2b174a"
txreq -url "/bereq_body" -body "foo"
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "false"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "966b82e2e767ff8fdfa80ef1e6e1ca2467d7a39e783b46ffe5dcec7f7d2b174a"
txreq -url "/bereq_body" -nolen -hdr "Transfer-encoding: chunked"
chunked f
chunked o
chunked o
chunkedlen 0
rxresp
expect resp.status == 200
expect resp.http.d1 == "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
expect resp.http.d2 == "fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603"
expect resp.http.d1r == "true"
expect resp.http.d2r == "false"
expect resp.http.d1dr == "true"
expect resp.http.d1d == "966b82e2e767ff8fdfa80ef1e6e1ca2467d7a39e783b46ffe5dcec7f7d2b174a"
} -start
client c1 -wait
client c2 -wait
client c3 -wait
client c4 -wait
......@@ -87,6 +87,13 @@ varnish v1 -vcl+backend {
}
sub vcl_backend_fetch {
if (bereq.url == "/handle") {
d2.final();
if (! d2.update(blob.decode(encoded="illegal"),
fail=false)) {
return (error(400));
}
}
if (bereq.url == "/fail") {
dtop.update(blob.decode(encoded="illegal"));
}
......@@ -129,6 +136,10 @@ client c1 {
expect resp.http.d2_c1 == "0CC175B9C0F1B6A831C399E269772661"
expect resp.http.d2_c2 == "0CC175B9C0F1B6A831C399E269772661"
txreq -url "/handle"
rxresp
expect resp.status == 400
txreq -url "/fail"
rxresp
expect resp.status == 503
......
......@@ -41,17 +41,39 @@
#include "byte_order.h"
#define ERR(ctx, msg) \
VRT_fail((ctx), "vmod blobdigest error: " msg)
#define ERR(fail, ctx, msg) do { \
if (fail) \
VRT_fail((ctx), "vmod blobdigest error: " msg); \
else \
VSLbs((ctx)->vsl, SLT_Error, TOSTRAND(msg)); \
} while (0)
#define VERR(ctx, fmt, ...) \
VRT_fail((ctx), "vmod blobdigest error: " fmt, __VA_ARGS__)
#define VERR(fail, ctx, fmt, ...) do { \
if (fail) \
VRT_fail((ctx), "vmod blobdigest error: " fmt, \
__VA_ARGS__); \
else \
VSLb((ctx)->vsl, SLT_Error, \
"vmod blobdigest error: " fmt, __VA_ARGS__); \
} while (0)
#define VERRNOMEM(ctx, fmt, ...) \
VERR((ctx), fmt ", out of space", __VA_ARGS__)
#define VERRNOMEM(fail, ctx, fmt, ...) \
VERR(fail, (ctx), fmt ", out of space", __VA_ARGS__)
#define ERRNOMEM(ctx, msg) \
ERR((ctx), msg ", out of space")
#define ERRNOMEM(fail, ctx, msg) \
ERR(fail, (ctx), msg ", out of space")
#define FAIL(ctx, msg) \
ERR(1, ctx, msg)
#define VFAIL(ctx, fmt, ...) \
VERR(1, ctx, fmt, __VA_ARGS__)
#define VFAILNOMEM(ctx, fmt, ...) \
VERRNOMEM(1, ctx, fmt, __VA_ARGS__)
#define FAILNOMEM(ctx, msg) \
ERRNOMEM(1, ctx, msg)
#define INIT_FINI(ctx) (((ctx)->method & (VCL_MET_INIT | VCL_MET_FINI)) != 0)
......@@ -207,6 +229,87 @@ update(const enum algorithm hash, hash_ctx *restrict const hctx,
}
}
struct blobdigest_iter_priv {
unsigned magic;
#define BLOBDIGEST_ITER_PRIV_MAGIC 0x7b7dbbaf
enum algorithm hash;
hash_ctx *restrict const hctx;
};
static int v_matchproto_(objiterate_f)
blobdigest_iter_f(void *priv, unsigned flush, const void *ptr, ssize_t len)
{
struct blobdigest_iter_priv *bi;
CAST_OBJ_NOTNULL(bi, priv, BLOBDIGEST_ITER_PRIV_MAGIC);
(void) flush;
update(bi->hash, bi->hctx, ptr, len);
return (0);
}
static const char *
blobdigest_vrb_iter(struct worker *wrk, struct vsl_log *vsl, struct req *req,
struct blobdigest_iter_priv *bi)
{
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
if (req->req_body_status == BS_NONE)
return (NULL);
if (req->req_body_status == BS_ERROR)
return ("previous error on body (BS_ERROR)");
if (req->req_body_status == BS_TAKEN)
return ("body already taken (BS_TAKEN)");
(void) VRB_Iterate(wrk, vsl, req, blobdigest_iter_f, bi);
return (NULL);
}
static const char *
update_body(VRT_CTX, VCL_ENUM from,
const enum algorithm hash, hash_ctx *restrict const hctx)
{
struct blobdigest_iter_priv bi[1] = {{
.magic = BLOBDIGEST_ITER_PRIV_MAGIC,
.hash = hash,
.hctx = hctx
}};
struct req *req = ctx->req;
if (from == VENUM(req_body)) {
if (req == NULL)
return ("from = req_body, "
"but no request body found");
return (blobdigest_vrb_iter(req->wrk, ctx->vsl, req, bi));
}
else if (from == VENUM(bereq_body) &&
ctx->bo != NULL && ctx->bo->bereq_body != NULL) {
(void) ObjIterate(ctx->bo->wrk, ctx->bo->bereq_body,
bi, blobdigest_iter_f, 0);
}
else if (from == VENUM(bereq_body)) {
if (ctx->bo == NULL)
return ("from = bereq_body, "
"but no backend request body found");
req = ctx->bo->req;
/* no req == no body */
if (req == NULL)
return (NULL);
return (blobdigest_vrb_iter(req->wrk, ctx->vsl, req, bi));
}
else if (from == VENUM(resp_body)) {
if (req == NULL || req->objcore == NULL)
return ("from = resp_body, "
"but no response body found");
(void) ObjIterate(req->wrk, req->objcore,
bi, blobdigest_iter_f, 0);
}
else
WRONG("from VENUM");
return (NULL);
}
static void
final(const enum algorithm hash, hash_ctx *restrict const hctx,
uint8_t *restrict result)
......@@ -268,7 +371,7 @@ ws_alloc_digest(VRT_CTX, const size_t digestsz, void **digestp,
spc = WS_Alloc(ctx->ws, PRNDUP(digestsz) + sizeof *b);
if (spc == NULL) {
VERRNOMEM(ctx, "WS_Alloc in %s.%s()", context, caller);
VFAILNOMEM(ctx, "WS_Alloc in %s.%s()", context, caller);
return (NULL);
}
......@@ -292,7 +395,7 @@ heap_alloc_digest(VRT_CTX, const size_t digestsz, void **digestp,
spc = malloc(PRNDUP(digestsz) + sizeof *b);
if (spc == NULL) {
VERRNOMEM(ctx, "malloc in %s.%s()", context, caller);
VFAILNOMEM(ctx, "malloc in %s.%s()", context, caller);
return (NULL);
}
......@@ -329,7 +432,7 @@ get_scope(const struct vrt_ctx * const restrict ctx,
break;
case TOP: {
if (ctx->req == NULL) {
VERR(ctx, "%s.%s(): object has TOP scope - only "
VFAIL(ctx, "%s.%s(): object has TOP scope - only "
"accessible in client VCL context",
h->vcl_name, method);
return (NULL);
......@@ -347,7 +450,7 @@ get_scope(const struct vrt_ctx * const restrict ctx,
}
if (priv == NULL) {
ERR(ctx, "no priv - out of workspace?");
FAIL(ctx, "no priv - out of workspace?");
return (NULL);
}
......@@ -361,7 +464,7 @@ get_scope(const struct vrt_ctx * const restrict ctx,
else {
task = WS_Alloc(ws, sizeof(struct digest_task));
if (task == NULL) {
VERRNOMEM(ctx, "allocating task data in %s.%s()",
VFAILNOMEM(ctx, "allocating task data in %s.%s()",
h->vcl_name, method);
return (NULL);
}
......@@ -422,15 +525,20 @@ vmod_digest__fini(struct vmod_blobdigest_digest **digestp)
}
VCL_BOOL
vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h,
struct VARGS(digest_update) *a)
{
struct digest_task *task;
hash_ctx *hctx;
const char *err = NULL;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(a);
CHECK_OBJ_NOTNULL(h, VMOD_BLOBDIGEST_DIGEST_MAGIC);
if (h->result != NULL) {
VERR(ctx, "already finalized in %s.update()", h->vcl_name);
VERR(a->fail, ctx, "already finalized in %s.update()",
h->vcl_name);
return (0);
}
......@@ -438,23 +546,27 @@ vmod_digest_update(VRT_CTX, struct vmod_blobdigest_digest *h, VCL_BLOB b)
if (task == NULL)
return (0);
if (b == NULL) {
VERR(ctx, "null BLOB passed to %s.update()", h->vcl_name);
if (a->valid_msg && a->msg == NULL) {
VERR(a->fail, ctx, "null BLOB passed to %s.update()",
h->vcl_name);
return (0);
}
if (INIT_FINI(ctx)) {
if (b->len > 0 && b->blob != NULL)
update(h->hash, &h->ctx, b->blob, b->len);
return (1);
}
hctx = INIT_FINI(ctx) ? &h->ctx : &task->ctx;
if (task->result != NULL) {
VERR(ctx, "already finalized in %s.update()", h->vcl_name);
VERR(a->fail, ctx, "already finalized in %s.update()",
h->vcl_name);
return (0);
}
if (a->valid_msg && a->msg->len > 0 && a->msg->blob != NULL)
update(h->hash, hctx, a->msg->blob, a->msg->len);
if (a->valid_from)
err = update_body(ctx, a->from, h->hash, hctx);
if (err != NULL) {
VERR(a->fail, ctx, "%s in %s.update()", err, h->vcl_name);
return (0);
}
if (b->len > 0 && b->blob != NULL)
update(h->hash, &task->ctx, b->blob, b->len);
return (1);
}
......@@ -566,7 +678,7 @@ vmod_hmac__init(VRT_CTX, struct vmod_blobdigest_hmac **hmacp,
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(vcl_name);
if (key == NULL || key->blob == NULL) {
VERR(ctx, "key is NULL in %s constructor", vcl_name);
VFAIL(ctx, "key is NULL in %s constructor", vcl_name);
return;
}
AN(hmacp);
......@@ -588,7 +700,7 @@ vmod_hmac_hmac(VRT_CTX, struct vmod_blobdigest_hmac *h, VCL_BLOB msg)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(h, VMOD_BLOBDIGEST_HMAC_MAGIC);
if (msg == NULL || msg->blob == NULL) {
VERR(ctx, "msg is NULL in %s.hmac()", h->vcl_name);
VFAIL(ctx, "msg is NULL in %s.hmac()", h->vcl_name);
return (NULL);
}
......@@ -607,7 +719,7 @@ vmod_hmac_hmac_bench(VRT_CTX, struct vmod_blobdigest_hmac *h, VCL_INT n,
uintptr_t snap;
if (n <= 0) {
ERR(ctx, "number of rounds must be greater than zero");
FAIL(ctx, "number of rounds must be greater than zero");
return (-1);
}
snap = WS_Snapshot(ctx->ws);
......@@ -668,11 +780,11 @@ vmod_hmacf(VRT_CTX, VCL_ENUM hashs, VCL_BLOB key, VCL_BLOB msg)
hash_ctx inner_ctx, outer_ctx;
if (key == NULL || key->blob == NULL) {
ERR(ctx, "key is NULL in blobdigest.hmacf()");
FAIL(ctx, "key is NULL in blobdigest.hmacf()");
return (NULL);
}
if (msg == NULL || msg->blob == NULL) {
ERR(ctx, "msg is NULL in blobdigest.hmacf()");
FAIL(ctx, "msg is NULL in blobdigest.hmacf()");
return (NULL);
}
hmac_init(hash, key, &inner_ctx, &outer_ctx);
......
......@@ -14,7 +14,7 @@ $ABI vrt
::
new OBJECT = blobdigest.digest(ENUM hash [, BLOB init], ["TASK"|"TOP"])
BOOL <obj>.update(BLOB)
BOOL <obj>.update(BLOB msg)
BLOB <obj>.final()
BLOB blobdigest.hash(ENUM hash, BLOB msg)
......@@ -108,7 +108,7 @@ Here are some examples::
sub vcl_recv {
# Use the fooprefix object to get the hash of "foobar"
if (!fooprefix.update(blob.decode(encoded="bar"))) {
if (!fooprefix.update(blob.decode(encoded="bar"), fail=false)) {
call do_error;
}
set req.http.Foobar-Hash = blob.encode(encoding=BASE64,
......@@ -120,7 +120,7 @@ Here are some examples::
# The uses of the object in client and backend contexts have
# no effect on each other, or on subsequent client or
# backend transactions.
if (!fooprefix.update(blob.decode(encoded="baz"))) {
if (!fooprefix.update(blob.decode(encoded="baz"), fail=false)) {
call do_error;
}
set req.http.Foobaz-Hash = blob.encode(encoding=BASE64,
......@@ -223,9 +223,18 @@ Example::
new sha512 = blobdigest.digest(SHA512, foo.get());
}
$Method BOOL .update(BLOB)
$Method BOOL .update([BLOB msg], [ENUM {req_body, bereq_body, resp_body } from],
BOOL fail=1)
If the BLOB ``msg`` is provided, incrementally add it to the digest
context of this object.
If a ``from`` argument is given, incrementally add the named body to
the digest context of this object.
If both arguments are given, they are added in the order documented
herein.
Incrementally add the BLOB to the digest context of this object.
Returns ``true`` if and only if the operation was successful.
As described above: if a digest object is updated in ``vcl_init``,
......@@ -236,11 +245,16 @@ task (client or backend transaction).
This method MAY NOT be called after ``.final()`` has been called
for the same object, either in ``vcl_init`` or in the current task.
The method fails and returns ``false`` if the BLOB is NULL, or if it
is called after ``.final()``. If it fails in ``vcl_init``, the VCL
load will fail with an error message. If it fails in any other VCL
subroutine, an error message is emitted to the Varnish log with the
``VCL_Error`` tag, and the message digest context is unchanged.
The method returns ``false`` if ``msg`` is NULL, if the body as per
the ``from`` argument is not available from the current context, or if
it is called after ``.final()``. If the ``fail`` argument is ``true``
(the default), this also triggers a VCL Error. A ``fail=false``
argument can be used for explicit error handling.
If it fails in ``vcl_init``, the VCL load will fail with an error
message. If it fails in any other VCL subroutine, an error message is
emitted to the Varnish log with the ``VCL_Error`` tag, and the message
digest context is unchanged.
Example::
......@@ -266,13 +280,13 @@ Example::
sub vcl_recv {
# Update the SHA3_256 digest in the current client transaction
if (!sha3_256.update(blob.blob(IDENTITY, "bar"))) {
if (!sha3_256.update(blob.blob(IDENTITY, "bar")), fail=false) {
call do_client_error;
}
sub vcl_backend_fetch {
# Update the SHA3_256 digest in the current backend transaction
if (!sha3_256.update(blob.blob(IDENTITY, "baz"))) {
if (!sha3_256.update(blob.blob(IDENTITY, "baz")), fail=false) {
call do_backend_error;
}
......
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