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