vinyl-cache/vmod/vmod_blob.c
0
/*-
1
 * Copyright 2015-2017,2026 UPLEX - Nils Goroll Systemoptimierung
2
 * All rights reserved.
3
 *
4 14175
 * Authors: Nils Goroll <nils.goroll@uplex.de>
5 37169
 *          Geoffrey Simmons <geoffrey.simmons@uplex.de>
6 31688
 *
7 19949
 * SPDX-License-Identifier: BSD-2-Clause
8 18479
 *
9 18437
 * Redistribution and use in source and binary forms, with or without
10 12432
 * modification, are permitted provided that the following conditions are met:
11 5292
 * 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 24800
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 14175
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 4977
decode_l(enum encoding dec, VCL_STRANDS s)
141
{
142 4977
        size_t len = 0;
143
144 4977
        AENC(dec);
145
146 4977
        CHECK_OBJ_NOTNULL(s, STRANDS_MAGIC);
147 11403
        for (int i = 0; i < s->n; i++)
148 12222
                if (s->p[i] != NULL && *s->p[i] != '\0')
149 5796
                        len += vstrlen(s->p[i]);
150
151 4977
        return (func[dec].decode_l(len));
152
}
153
154
static void
155 882
err_decode(VRT_CTX, const char *enc)
156
{
157 882
        switch (errno) {
158
        case EINVAL:
159 798
                ERRINVAL(ctx, enc);
160 798
                break;
161
        case ENOMEM:
162 84
                ERRNOMEM(ctx, "cannot decode");
163 84
                break;
164
        default:
165 0
                WRONG("invalid errno");
166 0
        }
167 882
}
168
169
static inline int
170 14826
encodes_hex(enum encoding enc)
171
{
172 14826
        return (enc == HEX || enc == URL);
173
}
174
175
/* Require case DEFAULT for all encodings besides HEX and URL. */
176
177
static inline int
178 14175
check_enc_case(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, enum encoding enc,
179
    enum case_e kase)
180
{
181 14175
        if (!encodes_hex(enc) && kase != DEFAULT) {
182 336
                VERR(ctx, "case %s is illegal with encoding %s", case_s, encs);
183 336
                return (0);
184
        }
185 13839
        return (1);
186 14175
}
187
188
/* Objects */
189
190
VCL_VOID v_matchproto_(td_blob_blob__init)
191 1050
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 1050
        enum encoding dec = parse_encoding(decs);
196
        void *buf;
197
        ssize_t len;
198
199 1050
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
200 1050
        AN(blobp);
201 1050
        AZ(*blobp);
202 1050
        AN(vcl_name);
203 1050
        AENC(dec);
204 1050
        CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC);
205
206 1050
        ALLOC_OBJ(b, VMOD_BLOB_MAGIC);
207 1050
        AN(b);
208 1050
        *blobp = b;
209 1050
        AZ(pthread_mutex_init(&b->lock, NULL));
210
211 1050
        b->blob.magic = VRT_BLOB_MAGIC;
212 1050
        b->blob.type = VMOD_BLOB_TYPE;
213
214 1050
        len = decode_l(dec, strings);
215 1050
        if (len == 0)
216 168
                return;
217
218 882
        assert(len > 0);
219
220 882
        buf = malloc(len);
221 882
        if (buf == NULL) {
222 0
                VERRNOMEM(ctx, "cannot create blob %s", vcl_name);
223 0
                return;
224
        }
225
226 882
        errno = 0;
227 882
        len = func[dec].decode(dec, buf, len, -1, strings, NULL);
228
229 882
        if (len == -1) {
230 168
                assert(errno == EINVAL);
231 168
                free(buf);
232 168
                VERR(ctx, "cannot create blob %s, illegal encoding beginning "
233
                    "with \"%s\"", vcl_name, strings->p[0]);
234 168
                return;
235
        }
236 714
        if (len == 0) {
237 0
                free(buf);
238 0
                memcpy(&b->blob, vrt_null_blob, sizeof b->blob);
239 0
                return;
240
        }
241 714
        b->blob.len = len;
242 714
        b->blob.blob = b->freeptr = buf;
243 1050
}
244
245
VCL_BLOB v_matchproto_(td_blob_blob_get)
246 2247
vmod_blob_get(VRT_CTX, struct vmod_blob_blob *b)
247
{
248 2247
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
249 2247
        CHECK_OBJ_NOTNULL(b, VMOD_BLOB_MAGIC);
250 2247
        CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC);
251 2247
        return (&b->blob);
252
}
253
254
VCL_STRING v_matchproto_(td_blob_blob_encode)
255 3717
vmod_blob_encode(VRT_CTX, struct vmod_blob_blob *b, VCL_ENUM encs,
256
    VCL_ENUM case_s)
257
{
258 3717
        enum encoding enc = parse_encoding(encs);
259 3717
        AENC(enc);
260 3717
        enum case_e kase = parse_case(case_s);
261
262 3717
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
263 3717
        CHECK_OBJ_NOTNULL(b, VMOD_BLOB_MAGIC);
264 3717
        CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC);
265
266 3717
        if (!check_enc_case(ctx, encs, case_s, enc, kase))
267 168
                return (NULL);
268 3549
        if (b->blob.len == 0)
269 420
                return ("");
270 3129
        if (kase == DEFAULT)
271 1911
                kase = LOWER;
272
273 3129
        if (b->encoding[enc][kase] == NULL) {
274 1281
                PTOK(pthread_mutex_lock(&b->lock));
275 1281
                if (b->encoding[enc][kase] == NULL) {
276 1281
                        ssize_t len = func[enc].encode_l(b->blob.len);
277
278 1281
                        assert(len >= 0);
279 1281
                        if (len == 0)
280 0
                                b->encoding[enc][kase] = empty;
281
                        else {
282 1281
                                b->encoding[enc][kase] = malloc(len);
283 1281
                                if (b->encoding[enc][kase] == NULL)
284 0
                                        ERRNOMEM(ctx, "cannot encode");
285
                                else {
286 1281
                                        char *s = b->encoding[enc][kase];
287 1281
                                        len =
288 2562
                                                func[enc].encode(
289 1281
                                                        enc, kase, s, len,
290 1281
                                                        b->blob.blob,
291 1281
                                                        b->blob.len);
292 1281
                                        assert(len >= 0);
293 1281
                                        if (len == 0) {
294 0
                                                free(s);
295 0
                                                b->encoding[enc][kase] = empty;
296 0
                                        }
297
                                        else
298 1281
                                                s[len] = '\0';
299
                                }
300
                        }
301 1281
                }
302 1281
                PTOK(pthread_mutex_unlock(&b->lock));
303 1281
        }
304 3129
        return (b->encoding[enc][kase]);
305 3717
}
306
307
VCL_VOID v_matchproto_(td_blob_blob__fini)
308 525
vmod_blob__fini(struct vmod_blob_blob **blobp)
309
{
310
        struct vmod_blob_blob *b;
311
        char *s;
312
        int i, j;
313
314 525
        TAKE_OBJ_NOTNULL(b, blobp, VMOD_BLOB_MAGIC);
315 525
        CHECK_OBJ(&b->blob, VRT_BLOB_MAGIC);
316
317 525
        if (b->freeptr != NULL) {
318 294
                free(b->freeptr);
319 294
                b->blob.blob = NULL;
320 294
        }
321
322 4725
        for (i = 0; i < __MAX_ENCODING; i++)
323 12600
                for (j = 0; j < 2; j++) {
324 8400
                        s = b->encoding[i][j];
325 8400
                        if (s != NULL && s != empty) {
326 420
                                free(s);
327 420
                                b->encoding[i][j] = NULL;
328 420
                        }
329 12600
                }
330
331 525
        PTOK(pthread_mutex_destroy(&b->lock));
332 525
        FREE_OBJ(b);
333 525
}
334
335
/* Functions */
336
337
VCL_BLOB v_matchproto_(td_blob_decode)
338 5649
vmod_decode(VRT_CTX, VCL_ENUM decs, VCL_INT length, VCL_STRANDS strings)
339
{
340 5649
        enum encoding dec = parse_encoding(decs);
341
        char *buf;
342
        ssize_t len;
343
        unsigned space;
344
345 5649
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
346 5649
        AENC(dec);
347 5649
        CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC);
348 5649
        CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
349
350 5649
        space = WS_ReserveAll(ctx->ws);
351 5649
        buf = WS_Reservation(ctx->ws);
352
353 5649
        if (length <= 0)
354 3948
                length = -1;
355 5649
        errno = 0;
356 5649
        len = func[dec].decode(dec, buf, space, length, strings, NULL);
357
358 5649
        if (len == -1) {
359 441
                err_decode(ctx, strings->p[0]);
360 441
                WS_Release(ctx->ws, 0);
361 441
                return (NULL);
362
        }
363 5208
        if (len == 0) {
364 903
                WS_Release(ctx->ws, 0);
365 903
                return (vrt_null_blob);
366
        }
367 4305
        WS_Release(ctx->ws, len);
368
369 4305
        assert(len > 0);
370
371 4305
        return (VRT_blob(ctx, "blob.decode", buf, len, VMOD_BLOB_TYPE));
372 5649
}
373
374
static VCL_STRING
375 6362
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 6362
        AENC(enc);
382
383 6362
        if (b == NULL)
384 567
                return (NULL);
385
386 5795
        CHECK_OBJ(b, VRT_BLOB_MAGIC);
387 5795
        CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
388 5795
        space = WS_ReserveAll(ctx->ws);
389 5795
        buf = WS_Reservation(ctx->ws);
390
391 5795
        len = func[enc].encode(enc, kase, buf, space, b->blob, b->len);
392
393 5795
        if (len == -1) {
394 105
                ERRNOMEM(ctx, "cannot encode");
395 105
                WS_Release(ctx->ws, 0);
396 105
                return (NULL);
397
        }
398 5690
        if (len == 0) {
399 756
                WS_Release(ctx->ws, 0);
400 756
                return ("");
401
        }
402 4934
        buf[len] = '\0';
403 4934
        WS_Release(ctx->ws, len + 1);
404 4934
        return (buf);
405 6362
}
406
407
VCL_STRING v_matchproto_(td_blob_encode)
408 6531
vmod_encode(VRT_CTX, VCL_ENUM encs, VCL_ENUM case_s, VCL_BLOB b)
409
{
410 6531
        enum encoding enc = parse_encoding(encs);
411 6531
        enum case_e kase = parse_case(case_s);
412
413 6531
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
414 6531
        if (!check_enc_case(ctx, encs, case_s, enc, kase))
415 168
                return (NULL);
416 6363
        return (encode(ctx, enc, kase, b));
417 6531
}
418
419
/*
420
 * Remove the first len bytes from strands
421
 *
422
 * to VRT?
423
 */
424
static void
425 3549
strands_consume(struct strands *strings, size_t consume)
426
{
427
        const char *s;
428
        size_t len;
429
        int i;
430
431 3549
        CHECK_OBJ_NOTNULL(strings, STRANDS_MAGIC);
432
433 6300
        for (i = 0; i < strings->n; i++) {
434 4431
                s = strings->p[i];
435 4431
                if (s == NULL || *s == '\0')
436 378
                        continue;
437 4053
                len = vstrlen(s);
438 4053
                if (consume >= len) {
439 2373
                        strings->p[i] = NULL;
440 2373
                        consume -= len;
441 2373
                        continue;
442
                }
443 1680
                strings->p[i] += consume;
444 1680
                break;
445
        }
446
447
        // i is at first string still needed
448 3549
        assert(i >= 0);
449 3549
        assert(i <= strings->n);
450 3549
        if (i == strings->n) {
451 1869
                memset(strings->p, 0, strings->n * sizeof *strings->p);
452 1869
                strings->n = 0;
453 1869
                return;
454
        }
455 1680
        len = (unsigned)strings->n - i;
456 1680
        vmemmove(strings->p, strings->p + i, len * sizeof *strings->p);
457 1680
        strings->n -= i;
458 3549
}
459
460
VCL_STRING v_matchproto_(td_blob_transcode)
461 3927
vmod_transcode(VRT_CTX, VCL_ENUM decs, VCL_ENUM encs, VCL_ENUM case_s,
462
               VCL_INT length, VCL_STRANDS stringsa)
463
{
464 3927
        enum encoding dec = parse_encoding(decs);
465 3927
        enum encoding enc = parse_encoding(encs);
466 3927
        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 3927
        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 3927
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
478 3927
        CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
479 3927
        CHECK_OBJ_NOTNULL(stringsa, STRANDS_MAGIC);
480
481
        // copy stringsa into strings on stack
482 3927
        const char *p[stringsa->n];
483 3927
        vmemcpy(p, stringsa->p, stringsa->n * sizeof *p);
484 3927
        struct strands *strings = &(struct strands){.magic=STRANDS_MAGIC, .n=stringsa->n, .p=p};
485
486 3927
        assert(bufmax % 3 == 0); //lint !e778 constant
487
488 3927
        AENC(dec);
489 3927
        AENC(enc);
490
491 3927
        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 3927
        buflen = decode_l(dec, strings);
499 3927
        if (buflen == 0)
500 0
                return ("");
501
502 3927
        if (buflen > bufmax)
503 21
                buflen = bufmax;
504
505 3927
        char buf[buflen];
506
507 3927
        if (length <= 0)
508 2142
                length = -1;
509
510 3927
        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 294
                while (strings->n) {
515 189
                        errno = 0;
516 189
                        len = func[dec].decode(dec, buf, buflen, length, strings, &consumed);
517
518 189
                        if (len < 0 && errno != ENOMEM) {
519 84
                                err_decode(ctx, strings->p[0]);
520 84
                                return (NULL);
521
                        }
522
523 105
                        strands_consume(strings, consumed);
524
                }
525 105
                return (VRT_STRANDS_string(ctx, stringsa));
526
        }
527
528 3738
        space = WS_ReserveAll(ctx->ws);
529 3738
        r = out = WS_Reservation(ctx->ws);
530
531 7119
        while (strings->n && length != 0) {
532 3801
                errno = 0;
533
534 3801
                len = func[dec].decode(dec, buf, buflen, length, strings, &consumed);
535 3801
                if (len < 0 && errno != ENOMEM) {
536 357
                        err_decode(ctx, strings->p[0]);
537 357
                        WS_Release(ctx->ws, 0);
538 357
                        return (NULL);
539
                }
540 3444
                if (len < 0)
541 84
                        len = buflen;
542
543 3444
                strands_consume(strings, consumed);
544
545 3444
                if (length > 0) {
546 1596
                        assert(consumed <= (typeof(consumed))length);
547 1596
                        length -= consumed;
548 1596
                }
549
550 3444
                if (len == 0)
551 0
                        continue;
552 3444
                AN(consumed);
553
554 3444
                errno = 0;
555 3444
                len = func[enc].encode(enc, kase, out, space, buf, len);
556
557 3444
                if (len == -1) {
558 63
                        ERRNOMEM(ctx, "cannot encode");
559 63
                        WS_Release(ctx->ws, 0);
560 63
                        return (NULL);
561
                }
562 3381
                assert(len >= 0);
563 3381
                assert((size_t)len <= space);
564
565 3381
                out += len;
566 3381
                space -= len;
567
        }
568
569
        // _encode_l ensures
570 3318
        assert(space > 0);
571 3318
        *out++ = '\0';
572 3318
        WS_ReleaseP(ctx->ws, out);
573 3318
        return (r);
574 3927
}
575
576
VCL_BOOL v_matchproto_(td_blob_same)
577 378
vmod_same(VRT_CTX, VCL_BLOB b1, VCL_BLOB b2)
578
{
579
580 378
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
581 378
        if (b1 == b2)
582 126
                return (1);
583 252
        if (b1 == NULL || b2 == NULL)
584 63
                return (0);
585 189
        CHECK_OBJ(b1, VRT_BLOB_MAGIC);
586 189
        CHECK_OBJ(b2, VRT_BLOB_MAGIC);
587 189
        return (b1->len == b2->len && b1->blob == b2->blob);
588 378
}
589
590
VCL_BOOL v_matchproto_(td_blob_equal)
591 252
vmod_equal(VRT_CTX, VCL_BLOB b1, VCL_BLOB b2)
592
{
593
594 252
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
595 252
        if (b1 == b2)
596 63
                return (1);
597 189
        if (b1 == NULL || b2 == NULL)
598 0
                return (0);
599 189
        CHECK_OBJ(b1, VRT_BLOB_MAGIC);
600 189
        CHECK_OBJ(b2, VRT_BLOB_MAGIC);
601 189
        if (b1->len != b2->len)
602 63
                return (0);
603 126
        if (b1->blob == b2->blob)
604 42
                return (1);
605 84
        if (b1->blob == NULL || b2->blob == NULL)
606 0
                return (0);
607 84
        return (memcmp(b1->blob, b2->blob, b1->len) == 0);
608 252
}
609
610
VCL_INT v_matchproto_(td_blob_length)
611 147
vmod_length(VRT_CTX, VCL_BLOB b)
612
{
613
614 147
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
615 147
        if (b == NULL)
616 0
                return (0);
617 147
        CHECK_OBJ(b, VRT_BLOB_MAGIC);
618 147
        return (b->len);
619 147
}
620
621
VCL_BLOB v_matchproto_(td_blob_sub)
622 378
vmod_sub(VRT_CTX, VCL_BLOB b, VCL_BYTES n, VCL_BYTES off)
623
{
624
625 378
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
626
627 378
        CHECK_OBJ_ORNULL(b, VRT_BLOB_MAGIC);
628 378
        if (b == NULL || b->len == 0 || b->blob == NULL) {
629 63
                ERR(ctx, "blob is empty in blob.sub()");
630 63
                return (NULL);
631
        }
632
633 315
        assert(b->len > 0);
634
635
        // XXX check for > SIZE_MAX ?
636 315
        if (off < 0 || n < 0) {
637 21
                ERR(ctx, "size or offset negative in blob.sub()");
638 21
                return (NULL);
639
        }
640
641 294
        if ((size_t)off > b->len || (size_t)n > b->len ||
642 252
            (size_t)off + (size_t)n > b->len) {
643 84
                VERR(ctx, "size %jd from offset %jd requires more bytes than "
644
                    "blob length %zd in blob.sub()",
645
                    (intmax_t)n, (intmax_t)off, b->len);
646 84
                return (NULL);
647
        }
648
649 420
        return (VRT_blob(ctx, "blob.sub",
650 210
            (const char *)b->blob + off, n, b->type));
651 378
}