| | vinyl-cache/vmod/vmod_blob.c |
| 0 |
|
/*- |
| 1 |
|
* Copyright 2015-2017,2026 UPLEX - Nils Goroll Systemoptimierung |
| 2 |
|
* All rights reserved. |
| 3 |
|
* |
| 4 |
2696 |
* Authors: Nils Goroll <nils.goroll@uplex.de> |
| 5 |
7044 |
* Geoffrey Simmons <geoffrey.simmons@uplex.de> |
| 6 |
6032 |
* |
| 7 |
3800 |
* SPDX-License-Identifier: BSD-2-Clause |
| 8 |
3520 |
* |
| 9 |
3512 |
* Redistribution and use in source and binary forms, with or without |
| 10 |
2368 |
* modification, are permitted provided that the following conditions are met: |
| 11 |
1008 |
* 1. Redistributions of source code must retain the above copyright notice, |
| 12 |
|
* this list of conditions and the following disclaimer. |
| 13 |
|
* 2. Redistributions in binary form must reproduce the above copyright notice, |
| 14 |
|
* this list of conditions and the following disclaimer in the documentation |
| 15 |
|
* and/or other materials provided with the distribution. |
| 16 |
|
* |
| 17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY |
| 18 |
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 |
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 |
|
* DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY |
| 21 |
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 22 |
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 |
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 24 |
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 26 |
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 |
|
* |
| 28 |
|
*/ |
| 29 |
|
|
| 30 |
|
#include "config.h" |
| 31 |
|
#include <string.h> |
| 32 |
|
|
| 33 |
|
#include "cache/cache.h" |
| 34 |
|
|
| 35 |
|
#include "vcc_blob_if.h" |
| 36 |
|
#include "vmod_blob.h" |
| 37 |
|
|
| 38 |
|
#define VMOD_BLOB_TYPE 0xfade4faa |
| 39 |
|
|
| 40 |
|
struct vmod_blob_blob { |
| 41 |
|
unsigned magic; |
| 42 |
|
#define VMOD_BLOB_MAGIC 0xfade4fa9 |
| 43 |
|
struct vrt_blob blob; |
| 44 |
|
void *freeptr; |
| 45 |
|
char *encoding[__MAX_ENCODING][2]; |
| 46 |
|
pthread_mutex_t lock; |
| 47 |
|
}; |
| 48 |
|
|
| 49 |
|
#define B64_FUNCS \ |
| 50 |
|
.decode_l = base64_decode_l, \ |
| 51 |
|
.decode = base64_decode, \ |
| 52 |
|
.encode = base64_encode |
| 53 |
|
|
| 54 |
|
static const struct vmod_blob_fptr { |
| 55 |
|
len_f *const decode_l; |
| 56 |
|
decode_f *const decode; |
| 57 |
|
len_f *const encode_l; |
| 58 |
|
encode_f *const encode; |
| 59 |
|
} func[__MAX_ENCODING] = { |
| 60 |
|
[IDENTITY] = { |
| 61 |
|
.decode_l = id_decode_l, |
| 62 |
|
.decode = id_decode, |
| 63 |
|
.encode_l = id_encode_l, |
| 64 |
|
.encode = id_encode |
| 65 |
|
}, |
| 66 |
|
[BASE64] = { |
| 67 |
|
B64_FUNCS, |
| 68 |
|
.encode_l = base64_encode_l |
| 69 |
|
}, |
| 70 |
|
[BASE64URL] = { |
| 71 |
|
B64_FUNCS, |
| 72 |
|
.encode_l = base64_encode_l |
| 73 |
|
}, |
| 74 |
|
[BASE64URLNOPAD] = { |
| 75 |
|
B64_FUNCS, |
| 76 |
|
.encode_l = base64nopad_encode_l |
| 77 |
|
}, |
| 78 |
|
[BASE64CF] = { |
| 79 |
|
B64_FUNCS, |
| 80 |
|
.encode_l = base64_encode_l |
| 81 |
|
}, |
| 82 |
|
[HEX] = { |
| 83 |
|
.decode_l = hex_decode_l, |
| 84 |
|
.decode = hex_decode, |
| 85 |
|
.encode_l = hex_encode_l, |
| 86 |
|
.encode = hex_encode |
| 87 |
|
}, |
| 88 |
|
[URL] = { |
| 89 |
|
.decode_l = url_decode_l, |
| 90 |
|
.decode = url_decode, |
| 91 |
|
.encode_l = url_encode_l, |
| 92 |
|
.encode = url_encode |
| 93 |
|
}, |
| 94 |
|
}; |
| 95 |
|
|
| 96 |
|
#undef B64_FUNCS |
| 97 |
|
|
| 98 |
|
#define ERR(ctx, msg) \ |
| 99 |
|
VRT_fail((ctx), "vmod blob error: " msg) |
| 100 |
|
|
| 101 |
|
#define VERR(ctx, fmt, ...) \ |
| 102 |
|
VRT_fail((ctx), "vmod blob error: " fmt, __VA_ARGS__) |
| 103 |
|
|
| 104 |
|
#define ERRINVAL(ctx, enc) \ |
| 105 |
|
VERR((ctx), "cannot decode, illegal encoding beginning with \"%s\"", \ |
| 106 |
|
(enc)) |
| 107 |
|
|
| 108 |
|
#define VERRNOMEM(ctx, fmt, ...) \ |
| 109 |
|
VERR((ctx), fmt ", out of space", __VA_ARGS__) |
| 110 |
|
|
| 111 |
|
#define ERRNOMEM(ctx, msg) \ |
| 112 |
|
ERR((ctx), msg ", out of space") |
| 113 |
|
|
| 114 |
|
static char empty[1] = { '\0' }; |
| 115 |
|
|
| 116 |
|
static enum encoding |
| 117 |
4692 |
parse_encoding(VCL_ENUM e) |
| 118 |
|
{ |
| 119 |
|
#define VMODENUM(n) \ |
| 120 |
|
do { \ |
| 121 |
|
if (e == VENUM(n)) return (n); \ |
| 122 |
|
} while (0); |
| 123 |
|
#include "vmod_blob_tbl_encodings.h" |
| 124 |
0 |
WRONG("illegal encoding enum"); |
| 125 |
|
} |
| 126 |
|
|
| 127 |
|
static enum case_e |
| 128 |
2696 |
parse_case(VCL_ENUM e) |
| 129 |
|
{ |
| 130 |
|
#define VMODENUM(n) \ |
| 131 |
|
do { \ |
| 132 |
|
if (e == VENUM(n)) return (n); \ |
| 133 |
|
} while (0); |
| 134 |
|
#include "vmod_blob_tbl_case.h" |
| 135 |
0 |
WRONG("illegal case enum"); |
| 136 |
|
} |
| 137 |
|
|
| 138 |
|
|
| 139 |
|
static inline size_t |
| 140 |
944 |
decode_l(enum encoding dec, VCL_STRANDS s) |
| 141 |
|
{ |
| 142 |
944 |
size_t len = 0; |
| 143 |
|
|
| 144 |
944 |
AENC(dec); |
| 145 |
|
|
| 146 |
944 |
CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC); |
| 147 |
2164 |
for (int i = 0; i < s->n; i++) |
| 148 |
2320 |
if (s->p[i] != NULL && *s->p[i] != '\0') |
| 149 |
1100 |
len += vstrlen(s->p[i]); |
| 150 |
|
|
| 151 |
944 |
return (func[dec].decode_l(len)); |
| 152 |
|
} |
| 153 |
|
|
| 154 |
|
static void |
| 155 |
152 |
err_decode(VRT_CTX, const char *enc) |
| 156 |
|
{ |
| 157 |
152 |
switch (errno) { |
| 158 |
|
case EINVAL: |
| 159 |
136 |
ERRINVAL(ctx, enc); |
| 160 |
136 |
break; |
| 161 |
|
case ENOMEM: |
| 162 |
16 |
ERRNOMEM(ctx, "cannot decode"); |
| 163 |
16 |
break; |
| 164 |
|
default: |
| 165 |
0 |
WRONG("invalid errno"); |
| 166 |
0 |
} |
| 167 |
152 |
} |
| 168 |
|
|
| 169 |
|
static inline int |
| 170 |
2816 |
encodes_hex(enum encoding enc) |
| 171 |
|
{ |
| 172 |
2816 |
return (enc == HEX || enc == URL); |
| 173 |
|
} |
| 174 |
|
|
| 175 |
|
/* Require case DEFAULT for all encodings besides HEX and URL. */ |
| 176 |
|
|
| 177 |
|
static inline int |
| 178 |
2696 |
check_enc_case(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, enum encoding enc, |
| 179 |
|
enum case_e kase) |
| 180 |
|
{ |
| 181 |
2696 |
if (!encodes_hex(enc) && kase != DEFAULT) { |
| 182 |
64 |
VERR(ctx, "case %s is illegal with encoding %s", case_s, encs); |
| 183 |
64 |
return (0); |
| 184 |
|
} |
| 185 |
2632 |
return (1); |
| 186 |
2696 |
} |
| 187 |
|
|
| 188 |
|
/* Objects */ |
| 189 |
|
|
| 190 |
|
VCL_VOID v_matchproto_(td_blob_blob__init) |
| 191 |
200 |
vmod_blob__init(VRT_CTX, struct vmod_blob_blob **blobp, const char *vcl_name, |
| 192 |
|
VCL_ENUM decs, VCL_STRANDS strings) |
| 193 |
|
{ |
| 194 |
|
struct vmod_blob_blob *b; |
| 195 |
200 |
enum encoding dec = parse_encoding(decs); |
| 196 |
|
void *buf; |
| 197 |
|
ssize_t len; |
| 198 |
|
|
| 199 |
200 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 200 |
200 |
AN(blobp); |
| 201 |
200 |
AZ(*blobp); |
| 202 |
200 |
AN(vcl_name); |
| 203 |
200 |
AENC(dec); |
| 204 |
200 |
CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC); |
| 205 |
|
|
| 206 |
200 |
ALLOC_OBJ(b, VMOD_BLOB_MAGIC); |
| 207 |
200 |
AN(b); |
| 208 |
200 |
*blobp = b; |
| 209 |
200 |
AZ(pthread_mutex_init(&b->lock, NULL)); |
| 210 |
|
|
| 211 |
200 |
b->blob.magic = VRT_BLOB_MAGIC; |
| 212 |
200 |
b->blob.type = VMOD_BLOB_TYPE; |
| 213 |
|
|
| 214 |
200 |
len = decode_l(dec, strings); |
| 215 |
200 |
if (len == 0) |
| 216 |
32 |
return; |
| 217 |
|
|
| 218 |
168 |
assert(len > 0); |
| 219 |
|
|
| 220 |
168 |
buf = malloc(len); |
| 221 |
168 |
if (buf == NULL) { |
| 222 |
0 |
VERRNOMEM(ctx, "cannot create blob %s", vcl_name); |
| 223 |
0 |
return; |
| 224 |
|
} |
| 225 |
|
|
| 226 |
168 |
errno = 0; |
| 227 |
168 |
len = func[dec].decode(dec, buf, len, -1, strings, NULL); |
| 228 |
|
|
| 229 |
168 |
if (len == -1) { |
| 230 |
32 |
assert(errno == EINVAL); |
| 231 |
32 |
free(buf); |
| 232 |
32 |
VERR(ctx, "cannot create blob %s, illegal encoding beginning " |
| 233 |
|
"with \"%s\"", vcl_name, strings->p[0]); |
| 234 |
32 |
return; |
| 235 |
|
} |
| 236 |
136 |
if (len == 0) { |
| 237 |
0 |
free(buf); |
| 238 |
0 |
memcpy(&b->blob, vrt_null_blob, sizeof b->blob); |
| 239 |
0 |
return; |
| 240 |
|
} |
| 241 |
136 |
b->blob.len = len; |
| 242 |
136 |
b->blob.blob = b->freeptr = buf; |
| 243 |
200 |
} |
| 244 |
|
|
| 245 |
|
VCL_BLOB v_matchproto_(td_blob_blob_get) |
| 246 |
428 |
vmod_blob_get(VRT_CTX, struct vmod_blob_blob *b) |
| 247 |
|
{ |
| 248 |
428 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 249 |
428 |
CHECK_OBJ_NOTNULL(b, VMOD_BLOB_MAGIC); |
| 250 |
428 |
CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC); |
| 251 |
428 |
return (&b->blob); |
| 252 |
|
} |
| 253 |
|
|
| 254 |
|
VCL_STRING v_matchproto_(td_blob_blob_encode) |
| 255 |
708 |
vmod_blob_encode(VRT_CTX, struct vmod_blob_blob *b, VCL_ENUM encs, |
| 256 |
|
VCL_ENUM case_s) |
| 257 |
|
{ |
| 258 |
708 |
enum encoding enc = parse_encoding(encs); |
| 259 |
708 |
AENC(enc); |
| 260 |
708 |
enum case_e kase = parse_case(case_s); |
| 261 |
|
|
| 262 |
708 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 263 |
708 |
CHECK_OBJ_NOTNULL(b, VMOD_BLOB_MAGIC); |
| 264 |
708 |
CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC); |
| 265 |
|
|
| 266 |
708 |
if (!check_enc_case(ctx, encs, case_s, enc, kase)) |
| 267 |
32 |
return (NULL); |
| 268 |
676 |
if (b->blob.len == 0) |
| 269 |
80 |
return (""); |
| 270 |
596 |
if (kase == DEFAULT) |
| 271 |
364 |
kase = LOWER; |
| 272 |
|
|
| 273 |
596 |
if (b->encoding[enc][kase] == NULL) { |
| 274 |
244 |
PTOK(pthread_mutex_lock(&b->lock)); |
| 275 |
244 |
if (b->encoding[enc][kase] == NULL) { |
| 276 |
244 |
ssize_t len = func[enc].encode_l(b->blob.len); |
| 277 |
|
|
| 278 |
244 |
assert(len >= 0); |
| 279 |
244 |
if (len == 0) |
| 280 |
0 |
b->encoding[enc][kase] = empty; |
| 281 |
|
else { |
| 282 |
244 |
b->encoding[enc][kase] = malloc(len); |
| 283 |
244 |
if (b->encoding[enc][kase] == NULL) |
| 284 |
0 |
ERRNOMEM(ctx, "cannot encode"); |
| 285 |
|
else { |
| 286 |
244 |
char *s = b->encoding[enc][kase]; |
| 287 |
244 |
len = |
| 288 |
488 |
func[enc].encode( |
| 289 |
244 |
enc, kase, s, len, |
| 290 |
244 |
b->blob.blob, |
| 291 |
244 |
b->blob.len); |
| 292 |
244 |
assert(len >= 0); |
| 293 |
244 |
if (len == 0) { |
| 294 |
0 |
free(s); |
| 295 |
0 |
b->encoding[enc][kase] = empty; |
| 296 |
0 |
} |
| 297 |
|
else |
| 298 |
244 |
s[len] = '\0'; |
| 299 |
|
} |
| 300 |
|
} |
| 301 |
244 |
} |
| 302 |
244 |
PTOK(pthread_mutex_unlock(&b->lock)); |
| 303 |
244 |
} |
| 304 |
596 |
return (b->encoding[enc][kase]); |
| 305 |
708 |
} |
| 306 |
|
|
| 307 |
|
VCL_VOID v_matchproto_(td_blob_blob__fini) |
| 308 |
100 |
vmod_blob__fini(struct vmod_blob_blob **blobp) |
| 309 |
|
{ |
| 310 |
|
struct vmod_blob_blob *b; |
| 311 |
|
char *s; |
| 312 |
|
int i, j; |
| 313 |
|
|
| 314 |
100 |
TAKE_OBJ_NOTNULL(b, blobp, VMOD_BLOB_MAGIC); |
| 315 |
100 |
CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC); |
| 316 |
|
|
| 317 |
100 |
if (b->freeptr != NULL) { |
| 318 |
56 |
free(b->freeptr); |
| 319 |
56 |
b->blob.blob = NULL; |
| 320 |
56 |
} |
| 321 |
|
|
| 322 |
900 |
for (i = 0; i < __MAX_ENCODING; i++) |
| 323 |
2400 |
for (j = 0; j < 2; j++) { |
| 324 |
1600 |
s = b->encoding[i][j]; |
| 325 |
1600 |
if (s != NULL && s != empty) { |
| 326 |
80 |
free(s); |
| 327 |
80 |
b->encoding[i][j] = NULL; |
| 328 |
80 |
} |
| 329 |
2400 |
} |
| 330 |
|
|
| 331 |
100 |
PTOK(pthread_mutex_destroy(&b->lock)); |
| 332 |
100 |
FREE_OBJ(b); |
| 333 |
100 |
} |
| 334 |
|
|
| 335 |
|
/* Functions */ |
| 336 |
|
|
| 337 |
|
VCL_BLOB v_matchproto_(td_blob_decode) |
| 338 |
1052 |
vmod_decode(VRT_CTX, VCL_ENUM decs, VCL_INT length, VCL_STRANDS strings) |
| 339 |
|
{ |
| 340 |
1052 |
enum encoding dec = parse_encoding(decs); |
| 341 |
|
char *buf; |
| 342 |
|
ssize_t len; |
| 343 |
|
unsigned space; |
| 344 |
|
|
| 345 |
1052 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 346 |
1052 |
AENC(dec); |
| 347 |
1052 |
CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC); |
| 348 |
1052 |
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); |
| 349 |
|
|
| 350 |
1052 |
space = WS_ReserveAll(ctx->ws); |
| 351 |
1052 |
buf = WS_Reservation(ctx->ws); |
| 352 |
|
|
| 353 |
1052 |
if (length <= 0) |
| 354 |
728 |
length = -1; |
| 355 |
1052 |
errno = 0; |
| 356 |
1052 |
len = func[dec].decode(dec, buf, space, length, strings, NULL); |
| 357 |
|
|
| 358 |
1052 |
if (len == -1) { |
| 359 |
72 |
err_decode(ctx, strings->p[0]); |
| 360 |
72 |
WS_Release(ctx->ws, 0); |
| 361 |
72 |
return (NULL); |
| 362 |
|
} |
| 363 |
980 |
if (len == 0) { |
| 364 |
168 |
WS_Release(ctx->ws, 0); |
| 365 |
168 |
return (vrt_null_blob); |
| 366 |
|
} |
| 367 |
812 |
WS_Release(ctx->ws, len); |
| 368 |
|
|
| 369 |
812 |
assert(len > 0); |
| 370 |
|
|
| 371 |
812 |
return (VRT_blob(ctx, "blob.decode", buf, len, VMOD_BLOB_TYPE)); |
| 372 |
1052 |
} |
| 373 |
|
|
| 374 |
|
static VCL_STRING |
| 375 |
1212 |
encode(VRT_CTX, enum encoding enc, enum case_e kase, VCL_BLOB b) |
| 376 |
|
{ |
| 377 |
|
ssize_t len; |
| 378 |
|
char *buf; |
| 379 |
|
unsigned space; |
| 380 |
|
|
| 381 |
1212 |
AENC(enc); |
| 382 |
|
|
| 383 |
1212 |
if (b == NULL) |
| 384 |
108 |
return (NULL); |
| 385 |
|
|
| 386 |
1104 |
CHECK_OBJ(b, VRT_BLOB_MAGIC); |
| 387 |
1104 |
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); |
| 388 |
1104 |
space = WS_ReserveAll(ctx->ws); |
| 389 |
1104 |
buf = WS_Reservation(ctx->ws); |
| 390 |
|
|
| 391 |
1104 |
len = func[enc].encode(enc, kase, buf, space, b->blob, b->len); |
| 392 |
|
|
| 393 |
1104 |
if (len == -1) { |
| 394 |
20 |
ERRNOMEM(ctx, "cannot encode"); |
| 395 |
20 |
WS_Release(ctx->ws, 0); |
| 396 |
20 |
return (NULL); |
| 397 |
|
} |
| 398 |
1084 |
if (len == 0) { |
| 399 |
144 |
WS_Release(ctx->ws, 0); |
| 400 |
144 |
return (""); |
| 401 |
|
} |
| 402 |
940 |
buf[len] = '\0'; |
| 403 |
940 |
WS_Release(ctx->ws, len + 1); |
| 404 |
940 |
return (buf); |
| 405 |
1212 |
} |
| 406 |
|
|
| 407 |
|
VCL_STRING v_matchproto_(td_blob_encode) |
| 408 |
1244 |
vmod_encode(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, VCL_BLOB b) |
| 409 |
|
{ |
| 410 |
1244 |
enum encoding enc = parse_encoding(encs); |
| 411 |
1244 |
enum case_e kase = parse_case(case_s); |
| 412 |
|
|
| 413 |
1244 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 414 |
1244 |
if (!check_enc_case(ctx, encs, case_s, enc, kase)) |
| 415 |
32 |
return (NULL); |
| 416 |
1212 |
return (encode(ctx, enc, kase, b)); |
| 417 |
1244 |
} |
| 418 |
|
|
| 419 |
|
/* |
| 420 |
|
* Remove the first len bytes from strands |
| 421 |
|
* |
| 422 |
|
* to VRT? |
| 423 |
|
*/ |
| 424 |
|
static void |
| 425 |
676 |
strands_consume(struct strands *strings, size_t consume) |
| 426 |
|
{ |
| 427 |
|
const char *s; |
| 428 |
|
size_t len; |
| 429 |
|
int i; |
| 430 |
|
|
| 431 |
676 |
CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC); |
| 432 |
|
|
| 433 |
1200 |
for (i = 0; i < strings->n; i++) { |
| 434 |
844 |
s = strings->p[i]; |
| 435 |
844 |
if (s == NULL || *s == '\0') |
| 436 |
72 |
continue; |
| 437 |
772 |
len = vstrlen(s); |
| 438 |
772 |
if (consume >= len) { |
| 439 |
452 |
strings->p[i] = NULL; |
| 440 |
452 |
consume -= len; |
| 441 |
452 |
continue; |
| 442 |
|
} |
| 443 |
320 |
strings->p[i] += consume; |
| 444 |
320 |
break; |
| 445 |
|
} |
| 446 |
|
|
| 447 |
|
// i is at first string still needed |
| 448 |
676 |
assert(i >= 0); |
| 449 |
676 |
assert(i <= strings->n); |
| 450 |
676 |
if (i == strings->n) { |
| 451 |
356 |
memset(strings->p, 0, strings->n * sizeof *strings->p); |
| 452 |
356 |
strings->n = 0; |
| 453 |
356 |
return; |
| 454 |
|
} |
| 455 |
320 |
len = (unsigned)strings->n - i; |
| 456 |
320 |
vmemmove(strings->p, strings->p + i, len * sizeof *strings->p); |
| 457 |
320 |
strings->n -= i; |
| 458 |
676 |
} |
| 459 |
|
|
| 460 |
|
VCL_STRING v_matchproto_(td_blob_transcode) |
| 461 |
744 |
vmod_transcode(VRT_CTX, VCL_ENUM decs, VCL_ENUM encs, VCL_ENUM case_s, |
| 462 |
|
VCL_INT length, VCL_STRANDS stringsa) |
| 463 |
|
{ |
| 464 |
744 |
enum encoding dec = parse_encoding(decs); |
| 465 |
744 |
enum encoding enc = parse_encoding(encs); |
| 466 |
744 |
enum case_e kase = parse_case(case_s); |
| 467 |
|
VCL_STRING r; |
| 468 |
|
// 32K are safe because PCRE. Needs to be a multiple of three for base64 |
| 469 |
|
// For bughunt, set to 3 |
| 470 |
744 |
const size_t bufmax = (32 * 1024) - 2; |
| 471 |
|
size_t buflen; |
| 472 |
|
ssize_t len; |
| 473 |
|
char *out; |
| 474 |
|
unsigned space; |
| 475 |
|
size_t consumed; |
| 476 |
|
|
| 477 |
744 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 478 |
744 |
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC); |
| 479 |
744 |
CHECK_OBJ_NOTNULL(stringsa, STRANDS_MAGIC); |
| 480 |
|
|
| 481 |
|
// copy stringsa into strings on stack |
| 482 |
744 |
const char *p[stringsa->n]; |
| 483 |
744 |
vmemcpy(p, stringsa->p, stringsa->n * sizeof *p); |
| 484 |
744 |
struct strands *strings = &(struct strands){.magic=STRANDS_MAGIC, .n=stringsa->n, .p=p}; |
| 485 |
|
|
| 486 |
744 |
assert(bufmax % 3 == 0); //lint !e778 constant |
| 487 |
|
|
| 488 |
744 |
AENC(dec); |
| 489 |
744 |
AENC(enc); |
| 490 |
|
|
| 491 |
744 |
if (!check_enc_case(ctx, encs, case_s, enc, kase)) |
| 492 |
0 |
return (NULL); |
| 493 |
|
|
| 494 |
|
/* |
| 495 |
|
* Allocate space for the decoded blob on the stack |
| 496 |
|
* ignoring the limitation imposed by n |
| 497 |
|
*/ |
| 498 |
744 |
buflen = decode_l(dec, strings); |
| 499 |
744 |
if (buflen == 0) |
| 500 |
0 |
return (""); |
| 501 |
|
|
| 502 |
744 |
if (buflen > bufmax) |
| 503 |
4 |
buflen = bufmax; |
| 504 |
|
|
| 505 |
744 |
char buf[buflen]; |
| 506 |
|
|
| 507 |
744 |
if (length <= 0) |
| 508 |
404 |
length = -1; |
| 509 |
|
|
| 510 |
744 |
if (length == -1 && enc == dec && !encodes_hex(enc)) { |
| 511 |
|
/* test if decodes OK, and if so, return input |
| 512 |
|
* basically the same loop as below, but without the encode step |
| 513 |
|
*/ |
| 514 |
52 |
while (strings->n) { |
| 515 |
32 |
errno = 0; |
| 516 |
32 |
len = func[dec].decode(dec, buf, buflen, length, strings, &consumed); |
| 517 |
|
|
| 518 |
32 |
if (len < 0 && errno != ENOMEM) { |
| 519 |
12 |
err_decode(ctx, strings->p[0]); |
| 520 |
12 |
return (NULL); |
| 521 |
|
} |
| 522 |
|
|
| 523 |
20 |
strands_consume(strings, consumed); |
| 524 |
|
} |
| 525 |
20 |
return (VRT_STRANDS_string(ctx, stringsa)); |
| 526 |
|
} |
| 527 |
|
|
| 528 |
712 |
space = WS_ReserveAll(ctx->ws); |
| 529 |
712 |
r = out = WS_Reservation(ctx->ws); |
| 530 |
|
|
| 531 |
1356 |
while (strings->n && length != 0) { |
| 532 |
724 |
errno = 0; |
| 533 |
|
|
| 534 |
724 |
len = func[dec].decode(dec, buf, buflen, length, strings, &consumed); |
| 535 |
724 |
if (len < 0 && errno != ENOMEM) { |
| 536 |
68 |
err_decode(ctx, strings->p[0]); |
| 537 |
68 |
WS_Release(ctx->ws, 0); |
| 538 |
68 |
return (NULL); |
| 539 |
|
} |
| 540 |
656 |
if (len < 0) |
| 541 |
16 |
len = buflen; |
| 542 |
|
|
| 543 |
656 |
strands_consume(strings, consumed); |
| 544 |
|
|
| 545 |
656 |
if (length > 0) { |
| 546 |
304 |
assert(consumed <= (typeof(consumed))length); |
| 547 |
304 |
length -= consumed; |
| 548 |
304 |
} |
| 549 |
|
|
| 550 |
656 |
if (len == 0) |
| 551 |
0 |
continue; |
| 552 |
656 |
AN(consumed); |
| 553 |
|
|
| 554 |
656 |
errno = 0; |
| 555 |
656 |
len = func[enc].encode(enc, kase, out, space, buf, len); |
| 556 |
|
|
| 557 |
656 |
assert(len <= space); |
| 558 |
656 |
if (len == -1) { |
| 559 |
12 |
ERRNOMEM(ctx, "cannot encode"); |
| 560 |
12 |
WS_Release(ctx->ws, 0); |
| 561 |
12 |
return (NULL); |
| 562 |
|
} |
| 563 |
|
|
| 564 |
644 |
out += len; |
| 565 |
644 |
space -= len; |
| 566 |
|
} |
| 567 |
|
|
| 568 |
|
// _encode_l ensures |
| 569 |
632 |
assert(space > 0); |
| 570 |
632 |
*out++ = '\0'; |
| 571 |
632 |
WS_ReleaseP(ctx->ws, out); |
| 572 |
632 |
return (r); |
| 573 |
744 |
} |
| 574 |
|
|
| 575 |
|
VCL_BOOL v_matchproto_(td_blob_same) |
| 576 |
72 |
vmod_same(VRT_CTX, VCL_BLOB b1, VCL_BLOB b2) |
| 577 |
|
{ |
| 578 |
|
|
| 579 |
72 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 580 |
72 |
if (b1 == b2) |
| 581 |
24 |
return (1); |
| 582 |
48 |
if (b1 == NULL || b2 == NULL) |
| 583 |
12 |
return (0); |
| 584 |
36 |
CHECK_OBJ(b1, VRT_BLOB_MAGIC); |
| 585 |
36 |
CHECK_OBJ(b2, VRT_BLOB_MAGIC); |
| 586 |
36 |
return (b1->len == b2->len && b1->blob == b2->blob); |
| 587 |
72 |
} |
| 588 |
|
|
| 589 |
|
VCL_BOOL v_matchproto_(td_blob_equal) |
| 590 |
48 |
vmod_equal(VRT_CTX, VCL_BLOB b1, VCL_BLOB b2) |
| 591 |
|
{ |
| 592 |
|
|
| 593 |
48 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 594 |
48 |
if (b1 == b2) |
| 595 |
12 |
return (1); |
| 596 |
36 |
if (b1 == NULL || b2 == NULL) |
| 597 |
0 |
return (0); |
| 598 |
36 |
CHECK_OBJ(b1, VRT_BLOB_MAGIC); |
| 599 |
36 |
CHECK_OBJ(b2, VRT_BLOB_MAGIC); |
| 600 |
36 |
if (b1->len != b2->len) |
| 601 |
12 |
return (0); |
| 602 |
24 |
if (b1->blob == b2->blob) |
| 603 |
8 |
return (1); |
| 604 |
16 |
if (b1->blob == NULL || b2->blob == NULL) |
| 605 |
0 |
return (0); |
| 606 |
16 |
return (memcmp(b1->blob, b2->blob, b1->len) == 0); |
| 607 |
48 |
} |
| 608 |
|
|
| 609 |
|
VCL_INT v_matchproto_(td_blob_length) |
| 610 |
28 |
vmod_length(VRT_CTX, VCL_BLOB b) |
| 611 |
|
{ |
| 612 |
|
|
| 613 |
28 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 614 |
28 |
if (b == NULL) |
| 615 |
0 |
return (0); |
| 616 |
28 |
CHECK_OBJ(b, VRT_BLOB_MAGIC); |
| 617 |
28 |
return (b->len); |
| 618 |
28 |
} |
| 619 |
|
|
| 620 |
|
VCL_BLOB v_matchproto_(td_blob_sub) |
| 621 |
72 |
vmod_sub(VRT_CTX, VCL_BLOB b, VCL_BYTES n, VCL_BYTES off) |
| 622 |
|
{ |
| 623 |
|
|
| 624 |
72 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
| 625 |
|
|
| 626 |
72 |
CHECK_OBJ_ORNULL(b, VRT_BLOB_MAGIC); |
| 627 |
72 |
if (b == NULL || b->len == 0 || b->blob == NULL) { |
| 628 |
12 |
ERR(ctx, "blob is empty in blob.sub()"); |
| 629 |
12 |
return (NULL); |
| 630 |
|
} |
| 631 |
|
|
| 632 |
60 |
assert(b->len > 0); |
| 633 |
|
|
| 634 |
|
// XXX check for > SIZE_MAX ? |
| 635 |
60 |
if (off < 0 || n < 0) { |
| 636 |
4 |
ERR(ctx, "size or offset negative in blob.sub()"); |
| 637 |
4 |
return (NULL); |
| 638 |
|
} |
| 639 |
|
|
| 640 |
56 |
if ((size_t)off > b->len || (size_t)n > b->len || |
| 641 |
48 |
(size_t)off + (size_t)n > b->len) { |
| 642 |
16 |
VERR(ctx, "size %jd from offset %jd requires more bytes than " |
| 643 |
|
"blob length %zd in blob.sub()", |
| 644 |
|
(intmax_t)n, (intmax_t)off, b->len); |
| 645 |
16 |
return (NULL); |
| 646 |
|
} |
| 647 |
|
|
| 648 |
80 |
return (VRT_blob(ctx, "blob.sub", |
| 649 |
40 |
(const char *)b->blob + off, n, b->type)); |
| 650 |
72 |
} |