/*- * Copyright (c) 2006 Verdens Gang AS * Copyright (c) 2006-2015 Varnish Software AS * All rights reserved. * * Author: Poul-Henning Kamp * * SPDX-License-Identifier: BSD-2-Clause * * 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. * * Runtime support for compiled VCL programs */ #include "config.h" #include #include "cache_varnishd.h" #include "cache_objhead.h" #include "vav.h" #include "vcl.h" #include "vct.h" #include "venc.h" #include "vend.h" #include "vrt_obj.h" #include "vsa.h" #include "vsha256.h" #include "vtcp.h" #include "vtim.h" #include "vcc_interface.h" #include "common/heritage.h" #include "common/vsmw.h" #include "proxy/cache_proxy.h" // NOT using TOSTRANDS() to create a NULL pointer element despite n == 0 const struct strands *const vrt_null_strands = &(struct strands){ .magic = STRANDS_MAGIC, .n = 0, .p = (const char *[1]){NULL} }; const struct vrt_blob *const vrt_null_blob = &(struct vrt_blob){ .magic = VRT_BLOB_MAGIC, .type = VRT_NULL_BLOB_TYPE, .len = 0, .blob = "\0" }; /*--------------------------------------------------------------------*/ VCL_VOID VRT_synth(VRT_CTX, VCL_INT code, VCL_STRING reason) { const char *ret; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); assert(ctx->req != NULL || ctx->bo != NULL); ret = ctx->req == NULL ? "error" : "synth"; if (code < 0) { VRT_fail(ctx, "return(%s()) status code (%jd) is negative", ret, (intmax_t)code); return; } if (code > 65535) { VRT_fail(ctx, "return(%s()) status code (%jd) > 65535", ret, (intmax_t)code); return; } if ((code % 1000) < 100) { VRT_fail(ctx, "illegal return(%s()) status code (%jd) (..0##)", ret, (intmax_t)code); return; } if (reason && !WS_Allocated(ctx->ws, reason, -1)) { reason = WS_Copy(ctx->ws, reason, -1); if (!reason) { VRT_fail(ctx, "Workspace overflow"); return; } } if (ctx->req == NULL) { CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); ctx->bo->err_code = (uint16_t)code; ctx->bo->err_reason = reason ? reason : http_Status2Reason(ctx->bo->err_code % 1000, NULL); return; } ctx->req->err_code = (uint16_t)code; ctx->req->err_reason = reason ? reason : http_Status2Reason(ctx->req->err_code % 1000, NULL); } /*--------------------------------------------------------------------*/ void VPI_acl_log(VRT_CTX, const char *msg) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(msg); if (ctx->vsl != NULL) VSLbs(ctx->vsl, SLT_VCL_acl, TOSTRAND(msg)); else VSL(SLT_VCL_acl, NO_VXID, "%s", msg); } int VRT_acl_match(VRT_CTX, VCL_ACL acl, VCL_IP ip) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (acl == NULL || ip == NULL) { VRT_fail(ctx, "Cannot match a null %s", acl == NULL ? "ACL" : "IP address"); return (0); } CHECK_OBJ(acl, VRT_ACL_MAGIC); assert(VSA_Sane(ip)); return (acl->match(ctx, ip)); } static int acl_tbl_cmp(int fam, const uint8_t *key, const uint8_t *b) { int rv; rv = fam - (int)b[3]; if (rv == 0 && b[1] > 0) rv = memcmp(key, b + 4, b[1]); if (rv == 0 && b[2]) rv = (int)(key[b[1]] & b[2]) - (int)b[4 + b[1]]; return (rv); } static const uint8_t * bbsearch(int fam, const uint8_t *key, const uint8_t *base0, size_t nmemb, size_t size) { const uint8_t *base = base0; size_t lim; int cmp; const uint8_t *p; for (lim = nmemb; lim != 0; lim >>= 1) { p = base + (lim >> 1) * size; cmp = acl_tbl_cmp(fam, key, p); if (cmp == 0) return (p); if (cmp > 0) { /* key > p: move right */ base = p + size; lim--; } /* else move left */ } return (NULL); } int VPI_acl_table(VRT_CTX, VCL_IP p, unsigned n, unsigned m, const uint8_t *tbl, const char * const *str, const char *fin) { int fam; const uint8_t *key; const uint8_t *ptr; size_t sz; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(p); AN(n); assert(m == 20); AN(tbl); AN(fin); fam = VRT_VSA_GetPtr(ctx, p, &key); ptr = bbsearch(fam, key, tbl, n, m); if (ptr != NULL) { sz = ptr - tbl; AZ(sz % m); sz /= m; if (str != NULL) VPI_acl_log(ctx, str[sz]); return (*ptr); } if (str != NULL) VPI_acl_log(ctx, fin); return (0); } /*--------------------------------------------------------------------*/ VCL_VOID VRT_hit_for_pass(VRT_CTX, VCL_DURATION d) { struct objcore *oc; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->bo == NULL) { VSLb(ctx->vsl, SLT_Error, "Note: Ignoring DURATION argument to return(pass);"); return; } CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); oc = ctx->bo->fetch_objcore; oc->ttl = d; oc->grace = 0.0; oc->keep = 0.0; VSLb(ctx->vsl, SLT_TTL, "HFP %.0f %.0f %.0f %.0f uncacheable", oc->ttl, oc->grace, oc->keep, oc->t_origin); } /*--------------------------------------------------------------------*/ VCL_HTTP VRT_selecthttp(VRT_CTX, enum gethdr_e where) { VCL_HTTP hp; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); switch (where) { case HDR_REQ: hp = ctx->http_req; break; case HDR_REQ_TOP: hp = ctx->http_req_top; break; case HDR_BEREQ: hp = ctx->http_bereq; break; case HDR_BERESP: hp = ctx->http_beresp; break; case HDR_RESP: hp = ctx->http_resp; break; case HDR_OBJ_STALE: case HDR_OBJ: hp = NULL; break; default: WRONG("VRT_selecthttp 'where' invalid"); } return (hp); } /*--------------------------------------------------------------------*/ VCL_STRING VRT_GetHdr(VRT_CTX, VCL_HEADER hs) { VCL_HTTP hp; const char *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (hs->where == HDR_OBJ) { CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->objcore, OBJCORE_MAGIC); return (HTTP_GetHdrPack(ctx->req->wrk, ctx->req->objcore, hs->what)); } if (hs->where == HDR_OBJ_STALE) { CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(ctx->bo->stale_oc, OBJCORE_MAGIC); return (HTTP_GetHdrPack(ctx->bo->wrk, ctx->bo->stale_oc, hs->what)); } hp = VRT_selecthttp(ctx, hs->where); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); if (!http_GetHdr(hp, hs->what, &p)) return (NULL); return (p); } /*-------------------------------------------------------------------- * Alloc Strands with space for n elements on workspace * * Error handling is deliberately left to the caller */ struct strands * VRT_AllocStrandsWS(struct ws *ws, int n) { struct strands *s; const char **p; s = WS_Alloc(ws, sizeof *s); p = WS_Alloc(ws, n * sizeof *p); if (s == NULL || p == NULL) return (NULL); s->magic = STRANDS_MAGIC; s->n = n; s->p = p; return (s); } /*-------------------------------------------------------------------- * Compare two STRANDS */ int VRT_CompareStrands(VCL_STRANDS a, VCL_STRANDS b) { const char *pa = NULL, *pb = NULL; int na = 0, nb = 0; CHECK_OBJ_NOTNULL(a, STRANDS_MAGIC); CHECK_OBJ_NOTNULL(b, STRANDS_MAGIC); while (1) { if (pa != NULL && *pa == '\0') pa = NULL; if (pb != NULL && *pb == '\0') pb = NULL; if (pa == NULL && na < a->n) pa = a->p[na++]; else if (pb == NULL && nb < b->n) pb = b->p[nb++]; else if (pa == NULL && pb == NULL) return (0); else if (pa == NULL) return (-1); else if (pb == NULL) return (1); else if (*pa == '\0') pa = NULL; else if (*pb == '\0') pb = NULL; else if (*pa != *pb) return (*pa - *pb); else { pa++; pb++; } } } /*-------------------------------------------------------------------- * STRANDS to BOOL */ VCL_BOOL VRT_Strands2Bool(VCL_STRANDS s) { int i; CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); for (i = 0; i < s->n; i++) if (s->p[i] != NULL) return (1); return (0); } /*-------------------------------------------------------------------- * Hash a STRANDS */ uint32_t VRT_HashStrands32(VCL_STRANDS s) { struct VSHA256Context sha_ctx; unsigned char sha256[VSHA256_LEN]; const char *p; int i; CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); VSHA256_Init(&sha_ctx); for (i = 0; i < s->n; i++) { p = s->p[i]; if (p != NULL && *p != '\0') VSHA256_Update(&sha_ctx, p, strlen(p)); } VSHA256_Final(sha256, &sha_ctx); /* NB: for some reason vmod_director's shard director specifically * relied on little-endian decoding of the last 4 octets. In order * to maintain a stable hash function to share across consumers we * need to stick to that. */ return (vle32dec(sha256 + VSHA256_LEN - 4)); } /*-------------------------------------------------------------------- * Collapse STRANDS into the space provided, or return NULL */ char * VRT_Strands(char *d, size_t dl, VCL_STRANDS s) { char *b; const char *e; unsigned x; AN(d); CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); b = d; e = b + dl; for (int i = 0; i < s->n; i++) if (s->p[i] != NULL && *s->p[i] != '\0') { x = strlen(s->p[i]); if (b + x >= e) return (NULL); memcpy(b, s->p[i], x); b += x; } assert(b < e); *b++ = '\0'; return (b); } /*-------------------------------------------------------------------- * Copy and merge STRANDS into a workspace. */ VCL_STRING VRT_StrandsWS(struct ws *ws, const char *h, VCL_STRANDS s) { const char *q = NULL; struct vsb vsb[1]; int i; WS_Assert(ws); CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); for (i = 0; i < s->n; i++) { if (s->p[i] != NULL && *s->p[i] != '\0') { q = s->p[i]; break; } } if (q == NULL) { if (h == NULL) return (""); if (WS_Allocated(ws, h, -1)) return (h); } else if (h == NULL && WS_Allocated(ws, q, -1)) { for (i++; i < s->n; i++) if (s->p[i] != NULL && *s->p[i] != '\0') break; if (i == s->n) return (q); } WS_VSB_new(vsb, ws); if (h != NULL) VSB_cat(vsb, h); for (i = 0; i < s->n; i++) { if (s->p[i] != NULL && *s->p[i] != '\0') VSB_cat(vsb, s->p[i]); } return (WS_VSB_finish(vsb, ws, NULL)); } /*-------------------------------------------------------------------- * Copy and merge STRANDS on the current workspace */ VCL_STRING VRT_STRANDS_string(VRT_CTX, VCL_STRANDS s) { const char *b; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); b = VRT_StrandsWS(ctx->ws, NULL, s); if (b == NULL) VRT_fail(ctx, "Workspace overflow"); return (b); } /*-------------------------------------------------------------------- * upper/lower-case STRANDS (onto workspace) */ #include VCL_STRING VRT_UpperLowerStrands(VRT_CTX, VCL_STRANDS s, int up) { unsigned u; char *b, *e, *r; const char *p, *q = NULL; int i, copy = 0; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); u = WS_ReserveAll(ctx->ws); r = b = WS_Reservation(ctx->ws); e = b + u; for (i = 0; i < s->n; i++) { if (s->p[i] == NULL || s->p[i][0] == '\0') continue; if (q != NULL) copy = 1; for(p = q = s->p[i]; *p != '\0'; p++) { if ((up && vct_islower(*p)) || (!up && vct_isupper(*p))) { *b++ = *p ^ 0x20; copy = 1; } else if (b < e) { *b++ = *p; } if (copy && b == e) break; } if (copy && b == e) { WS_Release(ctx->ws, 0); VRT_fail(ctx, "Workspace overflow"); return (NULL); } } assert(b <= e); if (!copy) { WS_Release(ctx->ws, 0); return (q); } assert(b < e); *b++ = '\0'; assert(b <= e); WS_ReleaseP(ctx->ws, b); return (r); } // rfc9110,l,1585,1589 // field-content = field-vchar // [ 1*( SP / HTAB / field-vchar ) field-vchar ] // field-vchar = VCHAR / obs-text // obs-text = %x80-FF // // This implementation is less strict, see #4221 static inline VCL_BOOL validhdr(const char *p) { AN(p); for(;*p != '\0'; p++) if (! vct_ishdrval(*p)) return (0); return (1); } /*--------------------------------------------------------------------*/ VCL_BOOL VRT_ValidHdr(VRT_CTX, VCL_STRANDS s) { int i; (void) ctx; CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); for (i = 0; i < s->n; i++) { if (s->p[i] == NULL || s->p[i][0] == '\0') continue; if (! validhdr(s->p[i])) return (0); } return (1); } /*--------------------------------------------------------------------*/ VCL_VOID VRT_UnsetHdr(VRT_CTX, VCL_HEADER hs) { VCL_HTTP hp; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(hs); AN(hs->what); hp = VRT_selecthttp(ctx, hs->where); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); http_Unset(hp, hs->what); } VCL_VOID VRT_SetHdr(VRT_CTX, VCL_HEADER hs, const char *pfx, VCL_STRANDS s) { VCL_HTTP hp; unsigned u, l, pl; char *p, *b; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(hs); AN(hs->what); hp = VRT_selecthttp(ctx, hs->where); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); u = WS_ReserveAll(hp->ws); pl = (pfx == NULL) ? 0 : strlen(pfx); l = hs->what->len + 1 + pl; if (u <= l) { WS_Release(hp->ws, 0); WS_MarkOverflow(hp->ws); VSLbs(ctx->vsl, SLT_LostHeader, TOSTRAND(hs->what->str)); return; } b = WS_Reservation(hp->ws); if (s != NULL) { p = VRT_Strands(b + l, u - l, s); if (p == NULL) { WS_Release(hp->ws, 0); WS_MarkOverflow(hp->ws); VSLbs(ctx->vsl, SLT_LostHeader, TOSTRAND(hs->what->str)); return; } } else { b[l] = '\0'; } p = b; memcpy(p, hs->what->str, hs->what->len); p += hs->what->len; *p++ = ' '; if (pfx != NULL) memcpy(p, pfx, pl); p += pl; if (FEATURE(FEATURE_VALIDATE_HEADERS) && !validhdr(b)) { VRT_fail(ctx, "Bad header %s", b); WS_Release(hp->ws, 0); return; } WS_ReleaseP(hp->ws, strchr(p, '\0') + 1); http_Unset(hp, hs->what); http_SetHeader(hp, b); } /*--------------------------------------------------------------------*/ VCL_VOID VRT_handling(VRT_CTX, unsigned hand) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); assert(hand != VCL_RET_FAIL); AN(ctx->vpi); AZ(ctx->vpi->handling); assert(hand > 0); assert(hand < VCL_RET_MAX); ctx->vpi->handling = hand; } unsigned VRT_handled(VRT_CTX) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(ctx->vpi); return (ctx->vpi->handling); } /* the trace value is cached in the VPI for efficiency */ VCL_VOID VRT_trace(VRT_CTX, VCL_BOOL a) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(ctx->vpi); ctx->vpi->trace = a; } /*--------------------------------------------------------------------*/ VCL_VOID VRT_fail(VRT_CTX, const char *fmt, ...) { va_list ap; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); assert(ctx->vsl != NULL || ctx->msg != NULL); AN(ctx->vpi); if (ctx->vpi->handling == VCL_RET_FAIL) return; AZ(ctx->vpi->handling); AN(fmt); AZ(strchr(fmt, '\n')); va_start(ap, fmt); if (ctx->vsl != NULL) { VSLbv(ctx->vsl, SLT_VCL_Error, fmt, ap); } else { AN(ctx->msg); VSB_vprintf(ctx->msg, fmt, ap); VSB_putc(ctx->msg, '\n'); } va_end(ap); ctx->vpi->handling = VCL_RET_FAIL; } /*-------------------------------------------------------------------- * Feed data into the hash calculation */ VCL_VOID VRT_hashdata(VRT_CTX, VCL_STRANDS s) { int i; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); AN(ctx->specific); CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); for (i = 0; i < s->n; i++) HSH_AddString(ctx->req, ctx->specific, s->p[i]); /* * Add a 'field-separator' to make it more difficult to * manipulate the hash. */ HSH_AddString(ctx->req, ctx->specific, NULL); } /*--------------------------------------------------------------------*/ VCL_TIME VRT_r_now(VRT_CTX) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); return (ctx->now); } /*--------------------------------------------------------------------*/ VCL_STRING v_matchproto_() VRT_IP_string(VRT_CTX, VCL_IP ip) { char buf[VTCP_ADDRBUFSIZE]; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ip == NULL) { VRT_fail(ctx, "%s: Illegal IP", __func__); return (NULL); } VTCP_name(ip, buf, sizeof buf, NULL, 0); return (WS_Copy(ctx->ws, buf, -1)); } int VRT_INT_is_valid(VCL_INT arg) { return (arg >= VRT_INTEGER_MIN && arg <= VRT_INTEGER_MAX); } VCL_STRING v_matchproto_() VRT_INT_string(VRT_CTX, VCL_INT num) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (!VRT_INT_is_valid(num)) VRT_fail(ctx, "INT overflow converting to string (0x%jx)", (intmax_t)num); return (WS_Printf(ctx->ws, "%jd", (intmax_t)num)); } int VRT_REAL_is_valid(VCL_REAL arg) { return (!isnan(arg) && arg >= VRT_DECIMAL_MIN && arg <= VRT_DECIMAL_MAX); } VCL_STRING v_matchproto_() VRT_REAL_string(VRT_CTX, VCL_REAL num) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (!VRT_REAL_is_valid(num)) VRT_fail(ctx, "REAL overflow converting to string (%e)", num); return (WS_Printf(ctx->ws, "%.3f", num)); } VCL_STRING v_matchproto_() VRT_TIME_string(VRT_CTX, VCL_TIME t) { char *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); p = WS_Alloc(ctx->ws, VTIM_FORMAT_SIZE); if (p == NULL) { VRT_fail(ctx, "Workspace overflow"); return (NULL); } VTIM_format(t, p); if (*p == '\0') { VRT_fail(ctx, "Unformattable VCL_TIME"); return (NULL); } return (p); } VCL_STRING v_matchproto_() VRT_BACKEND_string(VCL_BACKEND d) { if (d == NULL) return (NULL); CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC); return (d->vcl_name); } VCL_STRING v_matchproto_() VRT_PROBE_string(VCL_PROBE p) { if (p == NULL) return (NULL); CHECK_OBJ_NOTNULL(p, VRT_BACKEND_PROBE_MAGIC); return (p->vcl_name); } VCL_STRING v_matchproto_() VRT_BOOL_string(VCL_BOOL val) { return (val ? "true" : "false"); } VCL_STRING v_matchproto_() VRT_BLOB_string(VRT_CTX, VCL_BLOB val) { struct vsb vsb[1]; const char *s; if (val == NULL) return (NULL); WS_VSB_new(vsb, ctx->ws); VSB_putc(vsb, ':'); VENC_Encode_Base64(vsb, val->blob, val->len); VSB_putc(vsb, ':'); s = WS_VSB_finish(vsb, ctx->ws, NULL); return (s); } /*--------------------------------------------------------------------*/ VCL_VOID VRT_Rollback(VRT_CTX, VCL_HTTP hp) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); if (ctx->method & VCL_MET_PIPE) { VRT_fail(ctx, "Cannot rollback in vcl_pipe {}"); return; } if (hp == ctx->http_req) { CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); Req_Rollback(ctx); if (ctx->method & VCL_MET_DELIVER) XXXAZ(Resp_Setup_Deliver(ctx->req)); if (ctx->method & VCL_MET_SYNTH) Resp_Setup_Synth(ctx->req); } else if (hp == ctx->http_bereq) { Bereq_Rollback(ctx); } else WRONG("VRT_Rollback 'hp' invalid"); } /*--------------------------------------------------------------------*/ VCL_VOID VRT_synth_strands(VRT_CTX, VCL_STRANDS s) { struct vsb *vsb; int i; CAST_OBJ_NOTNULL(vsb, ctx->specific, VSB_MAGIC); CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); for (i = 0; i < s->n; i++) { if (s->p[i] != NULL) VSB_cat(vsb, s->p[i]); else VSB_cat(vsb, "(null)"); } } VCL_VOID VRT_synth_blob(VRT_CTX, VCL_BLOB b) { struct vsb *vsb; CAST_OBJ_NOTNULL(vsb, ctx->specific, VSB_MAGIC); CHECK_OBJ_NOTNULL(b, VRT_BLOB_MAGIC); if (b->len > 0 && b->blob != NULL) VSB_bcat(vsb, b->blob, b->len); } VCL_VOID VRT_synth_page(VRT_CTX, VCL_STRANDS s) { VRT_synth_strands(ctx, s); } /*--------------------------------------------------------------------*/ static VCL_STRING vrt_ban_error(VRT_CTX, VCL_STRING err) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(ctx->vsl); AN(err); VSLb(ctx->vsl, SLT_VCL_Error, "ban(): %s", err); return (err); } VCL_STRING VRT_ban_string(VRT_CTX, VCL_STRING str) { char *a1, *a2, *a3; char **av; struct ban_proto *bp; const char *err = NULL, *berr = NULL; int i; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (str == NULL) return (vrt_ban_error(ctx, "Null argument")); bp = BAN_Build(); if (bp == NULL) return (vrt_ban_error(ctx, "Out of Memory")); av = VAV_Parse(str, NULL, ARGV_NOESC); AN(av); if (av[0] != NULL) { err = av[0]; VAV_Free(av); BAN_Abandon(bp); return (vrt_ban_error(ctx, err)); } for (i = 0; ;) { a1 = av[++i]; if (a1 == NULL) { err = "No ban conditions found."; break; } a2 = av[++i]; if (a2 == NULL) { err = "Expected comparison operator."; break; } a3 = av[++i]; if (a3 == NULL) { err = "Expected second operand."; break; } berr = BAN_AddTest(bp, a1, a2, a3); if (berr != NULL) break; if (av[++i] == NULL) { berr = BAN_Commit(bp); if (berr == NULL) bp = NULL; break; } if (strcmp(av[i], "&&")) { err = WS_Printf(ctx->ws, "Expected && between " "conditions, found \"%s\"", av[i]); if (err == NULL) err = "Expected && between conditions " "(workspace overflow)"; break; } } if (berr != NULL) { AZ(err); err = WS_Copy(ctx->ws, berr, -1); if (err == NULL) err = "Unknown error (workspace overflow)"; berr = NULL; } AZ(berr); if (bp != NULL) BAN_Abandon(bp); VAV_Free(av); if (err == NULL) return (NULL); return (vrt_ban_error(ctx, err)); } VCL_BYTES VRT_CacheReqBody(VRT_CTX, VCL_BYTES maxsize) { const char * const err = "req.body can only be cached in vcl_recv{}"; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->method != VCL_MET_RECV) { if (ctx->vsl != NULL) { VSLbs(ctx->vsl, SLT_VCL_Error, TOSTRAND(err)); } else { AN(ctx->msg); VSB_printf(ctx->msg, "%s\n", err); }; return (-1); } CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); return (VRB_Cache(ctx->req, maxsize)); } /*-------------------------------------------------------------------- * purges */ VCL_INT VRT_purge(VRT_CTX, VCL_DURATION ttl, VCL_DURATION grace, VCL_DURATION keep) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if ((ctx->method & (VCL_MET_HIT|VCL_MET_MISS)) == 0) { VRT_fail(ctx, "purge can only happen in vcl_hit{} or vcl_miss{}"); return (0); } CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->wrk, WORKER_MAGIC); return (HSH_Purge(ctx->req->wrk, ctx->req->objcore->objhead, ctx->req->t_req, ttl, grace, keep)); } /*-------------------------------------------------------------------- */ struct vsmw_cluster * v_matchproto_() VRT_VSM_Cluster_New(VRT_CTX, size_t sz) { struct vsmw_cluster *vc; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); assert(sz > 0); AN(vsc_lock); AN(vsc_unlock); AN(heritage.proc_vsmw); vsc_lock(); vc = VSMW_NewCluster(heritage.proc_vsmw, sz, "VSC_cluster"); vsc_unlock(); return (vc); } void v_matchproto_() VRT_VSM_Cluster_Destroy(VRT_CTX, struct vsmw_cluster **vsmcp) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(vsmcp); VSMW_DestroyCluster(heritage.proc_vsmw, vsmcp); } /*-------------------------------------------------------------------- * Simple stuff */ int VRT_strcmp(const char *s1, const char *s2) { if (s1 == NULL || s2 == NULL) return (1); return (strcmp(s1, s2)); } void VRT_memmove(void *dst, const void *src, unsigned len) { (void)memmove(dst, src, len); } VCL_BOOL VRT_ipcmp(VRT_CTX, VCL_IP sua1, VCL_IP sua2) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (sua1 == NULL || sua2 == NULL) { VRT_fail(ctx, "%s: Illegal IP", __func__); return (1); } return (VSA_Compare_IP(sua1, sua2)); } /* * the pointer passed as src must have at least VCL_TASK lifetime */ VCL_BLOB VRT_blob(VRT_CTX, const char *err, const void *src, size_t len, unsigned type) { struct vrt_blob *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); if (src == NULL || len == 0) return (vrt_null_blob); p = (void *)WS_Alloc(ctx->ws, sizeof *p); if (p == NULL) { VRT_fail(ctx, "Workspace overflow (%s)", err); return (NULL); } INIT_OBJ(p, VRT_BLOB_MAGIC); p->type = type; p->len = len; p->blob = src; return (p); } int VRT_VSA_GetPtr(VRT_CTX, const struct suckaddr *sua, const unsigned char ** dst) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(dst); if (sua == NULL) { VRT_fail(ctx, "%s: Illegal IP", __func__); *dst = NULL; return (-1); } return (VSA_GetPtr(sua, dst)); } void VRT_Format_Proxy(struct vsb *vsb, VCL_INT version, VCL_IP sac, VCL_IP sas, VCL_STRING auth) { VPX_Format_Proxy(vsb, (int)version, sac, sas, auth); } /* * Clone a struct vrt_endpoint in a single malloc() allocation */ //lint -e{662} Possible of out-of-bounds pointer (___ beyond end of data) //lint -e{826} Suspicious pointer-to-pointer conversion (area to o small struct vrt_endpoint * VRT_Endpoint_Clone(const struct vrt_endpoint * const vep) { size_t sz; struct vrt_endpoint *nvep; struct vrt_blob *blob = NULL; struct suckaddr *sa; size_t uds_len = 0; char *p, *e; CHECK_OBJ_NOTNULL(vep, VRT_ENDPOINT_MAGIC); sz = sizeof *nvep; if (vep->ipv4) sz += vsa_suckaddr_len; if (vep->ipv6) sz += vsa_suckaddr_len; if (vep->uds_path != NULL) { uds_len = strlen(vep->uds_path) + 1; sz += uds_len; } if (vep->preamble != NULL && vep->preamble->len) { sz += sizeof(*blob); sz += vep->preamble->len; } p = calloc(1, sz); AN(p); e = p + sz; nvep = (void*)p; p += sizeof *nvep; INIT_OBJ(nvep, VRT_ENDPOINT_MAGIC); if (vep->ipv4) { sa = (void*)p; memcpy(sa, vep->ipv4, vsa_suckaddr_len); nvep->ipv4 = sa; p += vsa_suckaddr_len; } if (vep->ipv6) { sa = (void*)p; memcpy(sa, vep->ipv6, vsa_suckaddr_len); nvep->ipv6 = sa; p += vsa_suckaddr_len; } if (vep->preamble != NULL && vep->preamble->len) { /* Before uds because we need p to be aligned still */ blob = (void*)p; INIT_OBJ(blob, VRT_BLOB_MAGIC); p += sizeof(*blob); nvep->preamble = blob; memcpy(p, vep->preamble->blob, vep->preamble->len); blob->type = 0x70ea5b1e; blob->len = vep->preamble->len; blob->blob = p; p += vep->preamble->len; } if (uds_len) { memcpy(p, vep->uds_path, uds_len); nvep->uds_path = p; p += uds_len; } assert(p == e); return (nvep); }