varnish-cache/bin/varnishd/cache/cache_range.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include "config.h"
32
33
#include "cache_varnishd.h"
34
#include "cache_filter.h"
35
36
#include "vct.h"
37
#include <vtim.h>
38
39
/*--------------------------------------------------------------------*/
40
41
struct vrg_priv {
42
        unsigned                magic;
43
#define VRG_PRIV_MAGIC          0xb886e711
44
        struct req              *req;
45
        ssize_t                 range_low;
46
        ssize_t                 range_high;
47
        ssize_t                 range_off;
48
};
49
50
static int v_matchproto_(vdp_fini_f)
51 105
vrg_range_fini(struct vdp_ctx *vdc, void **priv)
52
{
53
        struct vrg_priv *vrg_priv;
54
55 105
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
56 105
        TAKE_OBJ_NOTNULL(vrg_priv, priv, VRG_PRIV_MAGIC);
57 105
        if (vrg_priv->req->resp_len >= 0 &&
58 78
            vrg_priv->range_off < vrg_priv->range_high) {
59 6
                Req_Fail(vrg_priv->req, SC_RANGE_SHORT);
60 6
                vrg_priv->req->vdc->retval = -1;
61 6
        }
62
        /* struct on ws, no need to free */
63 105
        return (0);
64
}
65
66
static int v_matchproto_(vdp_bytes_f)
67 196
vrg_range_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
68
    const void *ptr, ssize_t len)
69
{
70 196
        int retval = 0;
71 196
        ssize_t l = 0;
72 196
        const char *p = ptr;
73
        struct vrg_priv *vrg_priv;
74
75 196
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
76 196
        AN(priv);
77 196
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
78
79 196
        if (ptr != NULL) {
80 154
                l = vrg_priv->range_low - vrg_priv->range_off;
81 154
                if (l > 0) {
82 78
                        if (l > len)
83 9
                                l = len;
84 78
                        vrg_priv->range_off += l;
85 78
                        p += l;
86 78
                        len -= l;
87 78
                }
88 154
                l = vmin(vrg_priv->range_high - vrg_priv->range_off, len);
89 154
                vrg_priv->range_off += len;
90 154
                if (vrg_priv->range_off >= vrg_priv->range_high)
91 90
                        act = VDP_END;
92 154
        }
93
94 196
        if (l > 0)
95 143
                retval = VDP_bytes(vdc, act, p, l);
96 53
        else if (l == 0 && act > VDP_NULL)
97 53
                retval = VDP_bytes(vdc, act, p, 0);
98 196
        return (retval || act == VDP_END ? 1 : 0);
99
}
100
101
/*--------------------------------------------------------------------*/
102
103
static const char *
104 141
vrg_dorange(struct req *req, void **priv)
105
{
106
        ssize_t low, high;
107
        struct vrg_priv *vrg_priv;
108
        const char *err;
109
110 141
        err = http_GetRange(req->http, &low, &high, req->resp_len);
111 141
        if (err != NULL)
112 27
                return (err);
113
114 114
        if (low < 0 || high < 0)
115 9
                return (NULL);          // Allow 200 response
116
117 105
        if (req->resp_len >= 0) {
118 156
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
119 78
                    (intmax_t)low, (intmax_t)high, (intmax_t)req->resp_len);
120 78
                req->resp_len = (intmax_t)(1 + high - low);
121 78
        } else
122 54
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
123 27
                    (intmax_t)low, (intmax_t)high);
124
125 105
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
126 105
        if (vrg_priv == NULL)
127 0
                return ("WS too small");
128
129 105
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
130 105
        vrg_priv->req = req;
131 105
        vrg_priv->range_off = 0;
132 105
        vrg_priv->range_low = low;
133 105
        vrg_priv->range_high = high + 1;
134 105
        *priv = vrg_priv;
135 105
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
136 105
        return (NULL);
137 141
}
138
139
/*
140
 * return 1 if range should be observed, based on if-range value
141
 * if-range can either be a date or an ETag [RFC7233 3.2 p8]
142
 */
143
static int
144 162
vrg_ifrange(struct req *req)
145
{
146
        const char *p, *e;
147
        vtim_real ims, lm, d;
148
149 162
        if (!http_GetHdr(req->http, H_If_Range, &p))    // rfc7233,l,455,456
150 135
                return (1);
151
152
        /* strong validation needed */
153 27
        if (p[0] == 'W' && p[1] == '/')                 // rfc7233,l,500,501
154 0
                return (0);
155
156
        /* ETag */
157 27
        if (p[0] == '"') {                              // rfc7233,l,512,514
158 12
                if (!http_GetHdr(req->resp, H_ETag, &e))
159 6
                        return (0);
160 6
                if ((e[0] == 'W' && e[1] == '/'))       // rfc7232,l,547,548
161 0
                        return (0);
162
                /* XXX: should we also have http_etag_cmp() ? */
163 6
                return (strcmp(p, e) == 0);             // rfc7232,l,548,548
164
        }
165
166
        /* assume date, strong check [RFC7232 2.2.2 p7] */
167 15
        ims = VTIM_parse(p);
168 15
        if (!ims)                                       // rfc7233,l,502,512
169 0
                return (0);
170
171
        /* the response needs a Date */
172
        // rfc7232,l,439,440
173 15
        if (!http_GetHdr(req->resp, H_Date, &p))
174 0
                return (0);
175 15
        d = VTIM_parse(p);
176 15
        if (!d)
177 0
                return (0);
178
179
180
        /* grab the Last Modified value */
181 15
        if (!http_GetHdr(req->resp, H_Last_Modified, &p))
182 6
                return (0);
183
184 9
        lm = VTIM_parse(p);
185 9
        if (!lm)
186 0
                return (0);
187
188
        /* Last Modified must be 60 seconds older than Date */
189 9
        if (lm > d + 60)                                // rfc7232,l,442,443
190 0
                return (0);
191
192 9
        if (lm != ims)                                  // rfc7233,l,455,456
193 6
                return (0);
194 3
        return (1);
195 162
}
196
197
static int v_matchproto_(vdp_init_f)
198 162
vrg_range_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
199
{
200
        const char *err;
201
202 162
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
203 162
        CHECK_OBJ_ORNULL(ctx->req, REQ_MAGIC);
204 162
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
205 162
        AN(priv);
206
207 162
        if (ctx->req == NULL) {
208 0
                VSLb(vdc->vsl, SLT_Error,
209
                     "range can only be used on the client side");
210 0
                return (1);
211
        }
212
213
        // not using vdc->{hd,cl}, because range needs req anyway for Req_Fail()
214
215 162
        if (!vrg_ifrange(ctx->req))             // rfc7233,l,455,456
216 21
                return (1);
217 141
        err = vrg_dorange(ctx->req, priv);
218 141
        if (err == NULL)
219 114
                return (*priv == NULL ? 1 : 0);
220
221 27
        VSLb(vdc->vsl, SLT_Debug, "RANGE_FAIL %s", err);
222 27
        if (ctx->req->resp_len >= 0)
223 54
                http_PrintfHeader(ctx->req->resp,
224
                    "Content-Range: bytes */%jd",
225 27
                    (intmax_t)ctx->req->resp_len);
226 27
        http_PutResponse(ctx->req->resp, "HTTP/1.1", 416, NULL);
227
        /*
228
         * XXX: We ought to produce a body explaining things.
229
         * XXX: That really calls for us to hit vcl_synth{}
230
         */
231 27
        ctx->req->resp_len = 0;
232 27
        return (1);
233 162
}
234
235
const struct vdp VDP_range = {
236
        .name =         "range",
237
        .init =         vrg_range_init,
238
        .bytes =        vrg_range_bytes,
239
        .fini =         vrg_range_fini,
240
};
241
242
/*--------------------------------------------------------------------*/
243
244
int
245 5952
VRG_CheckBo(struct busyobj *bo)
246
{
247
        ssize_t rlo, rhi, crlo, crhi, crlen, clen;
248
        const char *err;
249
250 5952
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
251
252 5952
        if (!cache_param->http_range_support)
253 12
                return (0);
254
255 5940
        err = http_GetRange(bo->bereq0, &rlo, &rhi, -1);
256 5940
        clen = http_GetContentLength(bo->beresp);
257 5940
        crlen = http_GetContentRange(bo->beresp, &crlo, &crhi);
258
259 5940
        if (err != NULL) {
260 3
                VSLb(bo->vsl, SLT_Error, "Invalid range header (%s)", err);
261 3
                return (-1);
262
        }
263
264 5937
        if (crlen < -1) {
265 6
                VSLb(bo->vsl, SLT_Error, "Invalid content-range header");
266 6
                return (-1);
267
        }
268
269 5931
        if (clen < -1) {
270 0
                VSLb(bo->vsl, SLT_Error, "Invalid content-length header");
271 0
                return (-1);
272
        }
273
274 5931
        if (crlo < 0 && crhi < 0 && crlen < 0) {
275 5913
                VSLb(bo->vsl, SLT_Debug,
276
                    "Missing content-range header or unknown range unit");
277 5913
                return (0);
278
        }
279
280 18
        if (rlo < 0 && rhi < 0) {
281 6
                VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
282 6
                return (-1);
283
        }
284
285 12
        if (crlo < 0) {         // Content-Range: bytes */123
286 3
                assert(crhi < 0);
287 3
                assert(crlen > 0);
288 3
                if (http_GetStatus(bo->beresp) == 416)
289 3
                        return (0);
290 0
                crlo = 0;
291 0
                crhi = crlen - 1;
292 0
        }
293
294
#define RANGE_CHECK(val, op, crval, what)                       \
295
        do {                                                    \
296
                if (val >= 0 && !(val op crval)) {              \
297
                        VSLb(bo->vsl, SLT_Error,                \
298
                            "Expected " what " %zd, got %zd",   \
299
                            crval, val);                        \
300
                        return (-1);                            \
301
                }                                               \
302
        } while (0)
303
304 9
        crlen = (crhi - crlo) + 1;
305 9
        RANGE_CHECK(clen, ==, crlen, "content length");
306
307
        /* NB: if the client didn't specify a low range the high range
308
         * was adjusted based on the resource length, and a high range
309
         * is allowed to be out of bounds so at this point there is
310
         * nothing left to check.
311
         */
312 3
        if (rlo < 0)
313 0
                return (0);
314
315 3
        RANGE_CHECK(rlo, ==, crlo, "low range");
316 3
        RANGE_CHECK(rhi, >=, crhi, "minimum high range");
317
#undef RANGE_CHECK
318
319 3
        return (0);
320 5952
}