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 1400
vrg_range_fini(struct vdp_ctx *vdc, void **priv)
52
{
53
        struct vrg_priv *vrg_priv;
54
55 1400
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
56 1400
        TAKE_OBJ_NOTNULL(vrg_priv, priv, VRG_PRIV_MAGIC);
57 1400
        if (vrg_priv->req->resp_len >= 0 &&
58 1040
            vrg_priv->range_off < vrg_priv->range_high) {
59 80
                Req_Fail(vrg_priv->req, SC_RANGE_SHORT);
60 80
                vrg_priv->req->vdc->retval = -1;
61 80
        }
62
        /* struct on ws, no need to free */
63 1400
        return (0);
64
}
65
66
static int v_matchproto_(vdp_bytes_f)
67 2607
vrg_range_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
68
    const void *ptr, ssize_t len)
69
{
70 2607
        int retval = 0;
71 2607
        ssize_t l = 0;
72 2607
        const char *p = ptr;
73
        struct vrg_priv *vrg_priv;
74
75 2607
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
76 2607
        AN(priv);
77 2607
        CAST_OBJ_NOTNULL(vrg_priv, *priv, VRG_PRIV_MAGIC);
78
79 2607
        if (ptr != NULL) {
80 2045
                l = vrg_priv->range_low - vrg_priv->range_off;
81 2045
                if (l > 0) {
82 1027
                        if (l > len)
83 107
                                l = len;
84 1027
                        vrg_priv->range_off += l;
85 1027
                        p += l;
86 1027
                        len -= l;
87 1027
                }
88 2045
                l = vmin(vrg_priv->range_high - vrg_priv->range_off, len);
89 2045
                vrg_priv->range_off += len;
90 2045
                if (vrg_priv->range_off >= vrg_priv->range_high)
91 1200
                        act = VDP_END;
92 2045
        }
93
94 2607
        if (l > 0)
95 1918
                retval = VDP_bytes(vdc, act, p, l);
96 689
        else if (l == 0 && act > VDP_NULL)
97 689
                retval = VDP_bytes(vdc, act, p, 0);
98 2607
        return (retval || act == VDP_END ? 1 : 0);
99
}
100
101
/*--------------------------------------------------------------------*/
102
103
static const char *
104 1880
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 1880
        err = http_GetRange(req->http, &low, &high, req->resp_len);
111 1880
        if (err != NULL)
112 360
                return (err);
113
114 1520
        if (low < 0 || high < 0)
115 120
                return (NULL);          // Allow 200 response
116
117 1400
        if (req->resp_len >= 0) {
118 2080
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
119 1040
                    (intmax_t)low, (intmax_t)high, (intmax_t)req->resp_len);
120 1040
                req->resp_len = (intmax_t)(1 + high - low);
121 1040
        } else
122 720
                http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/*",
123 360
                    (intmax_t)low, (intmax_t)high);
124
125 1400
        vrg_priv = WS_Alloc(req->ws, sizeof *vrg_priv);
126 1400
        if (vrg_priv == NULL)
127 0
                return ("WS too small");
128
129 1400
        INIT_OBJ(vrg_priv, VRG_PRIV_MAGIC);
130 1400
        vrg_priv->req = req;
131 1400
        vrg_priv->range_off = 0;
132 1400
        vrg_priv->range_low = low;
133 1400
        vrg_priv->range_high = high + 1;
134 1400
        *priv = vrg_priv;
135 1400
        http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
136 1400
        return (NULL);
137 1880
}
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 2160
vrg_ifrange(struct req *req)
145
{
146
        const char *p, *e;
147
        vtim_real ims, lm, d;
148
149 2160
        if (!http_GetHdr(req->http, H_If_Range, &p))    // rfc7233,l,455,456
150 1800
                return (1);
151
152
        /* strong validation needed */
153 360
        if (p[0] == 'W' && p[1] == '/')                 // rfc7233,l,500,501
154 0
                return (0);
155
156
        /* ETag */
157 360
        if (p[0] == '"') {                              // rfc7233,l,512,514
158 160
                if (!http_GetHdr(req->resp, H_ETag, &e))
159 80
                        return (0);
160 80
                if ((e[0] == 'W' && e[1] == '/'))       // rfc7232,l,547,548
161 0
                        return (0);
162
                /* XXX: should we also have http_etag_cmp() ? */
163 80
                return (strcmp(p, e) == 0);             // rfc7232,l,548,548
164
        }
165
166
        /* assume date, strong check [RFC7232 2.2.2 p7] */
167 200
        ims = VTIM_parse(p);
168 200
        if (!ims)                                       // rfc7233,l,502,512
169 0
                return (0);
170
171
        /* the response needs a Date */
172
        // rfc7232,l,439,440
173 200
        if (!http_GetHdr(req->resp, H_Date, &p))
174 0
                return (0);
175 200
        d = VTIM_parse(p);
176 200
        if (!d)
177 0
                return (0);
178
179
180
        /* grab the Last Modified value */
181 200
        if (!http_GetHdr(req->resp, H_Last_Modified, &p))
182 80
                return (0);
183
184 120
        lm = VTIM_parse(p);
185 120
        if (!lm)
186 0
                return (0);
187
188
        /* Last Modified must be 60 seconds older than Date */
189 120
        if (lm > d + 60)                                // rfc7232,l,442,443
190 0
                return (0);
191
192 120
        if (lm != ims)                                  // rfc7233,l,455,456
193 80
                return (0);
194 40
        return (1);
195 2160
}
196
197
static int v_matchproto_(vdp_init_f)
198 2160
vrg_range_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
199
{
200
        const char *err;
201
202 2160
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
203 2160
        CHECK_OBJ_ORNULL(ctx->req, REQ_MAGIC);
204 2160
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
205 2160
        AN(priv);
206
207 2160
        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 2160
        if (!vrg_ifrange(ctx->req))             // rfc7233,l,455,456
216 280
                return (1);
217 1880
        err = vrg_dorange(ctx->req, priv);
218 1880
        if (err == NULL)
219 1520
                return (*priv == NULL ? 1 : 0);
220
221 360
        VSLb(vdc->vsl, SLT_Debug, "RANGE_FAIL %s", err);
222 360
        if (ctx->req->resp_len >= 0)
223 720
                http_PrintfHeader(ctx->req->resp,
224
                    "Content-Range: bytes */%jd",
225 360
                    (intmax_t)ctx->req->resp_len);
226 360
        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 360
        ctx->req->resp_len = 0;
232 360
        return (1);
233 2160
}
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 78156
VRG_CheckBo(struct busyobj *bo)
246
{
247
        ssize_t rlo, rhi, crlo, crhi, crlen, clen;
248
        const char *err;
249
250 78156
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
251
252 78156
        if (!cache_param->http_range_support)
253 160
                return (0);
254
255 77996
        err = http_GetRange(bo->bereq0, &rlo, &rhi, -1);
256 77996
        clen = http_GetContentLength(bo->beresp);
257 77996
        crlen = http_GetContentRange(bo->beresp, &crlo, &crhi);
258
259 77996
        if (err != NULL) {
260 40
                VSLb(bo->vsl, SLT_Error, "Invalid range header (%s)", err);
261 40
                return (-1);
262
        }
263
264 77956
        if (crlen < -1) {
265 80
                VSLb(bo->vsl, SLT_Error, "Invalid content-range header");
266 80
                return (-1);
267
        }
268
269 77876
        if (clen < -1) {
270 0
                VSLb(bo->vsl, SLT_Error, "Invalid content-length header");
271 0
                return (-1);
272
        }
273
274 77876
        if (crlo < 0 && crhi < 0 && crlen < 0) {
275 77636
                AZ(http_GetHdr(bo->beresp, H_Content_Range, NULL));
276 77636
                return (0);
277
        }
278
279 240
        if (rlo < 0 && rhi < 0) {
280 80
                VSLb(bo->vsl, SLT_Error, "Unexpected content-range header");
281 80
                return (-1);
282
        }
283
284 160
        if (crlo < 0) {         // Content-Range: bytes */123
285 40
                assert(crhi < 0);
286 40
                assert(crlen > 0);
287 40
                if (http_GetStatus(bo->beresp) == 416)
288 40
                        return (0);
289 0
                crlo = 0;
290 0
                crhi = crlen - 1;
291 0
        }
292
293
#define RANGE_CHECK(val, op, crval, what)                       \
294
        do {                                                    \
295
                if (val >= 0 && !(val op crval)) {              \
296
                        VSLb(bo->vsl, SLT_Error,                \
297
                            "Expected " what " %zd, got %zd",   \
298
                            crval, val);                        \
299
                        return (-1);                            \
300
                }                                               \
301
        } while (0)
302
303 120
        crlen = (crhi - crlo) + 1;
304 120
        RANGE_CHECK(clen, ==, crlen, "content length");
305
306
        /* NB: if the client didn't specify a low range the high range
307
         * was adjusted based on the resource length, and a high range
308
         * is allowed to be out of bounds so at this point there is
309
         * nothing left to check.
310
         */
311 40
        if (rlo < 0)
312 0
                return (0);
313
314 40
        RANGE_CHECK(rlo, ==, crlo, "low range");
315 40
        RANGE_CHECK(rhi, >=, crhi, "minimum high range");
316
#undef RANGE_CHECK
317
318 40
        return (0);
319 78156
}