varnish-cache/bin/varnishd/http2/cache_http2_hpack.c
1
/*-
2
 * Copyright (c) 2016 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 */
29
30
#include "config.h"
31
32
#include "cache/cache_varnishd.h"
33
34
#include <ctype.h>
35
#include <stdio.h>
36
37
#include "http2/cache_http2.h"
38
#include "vct.h"
39
40
static h2_error
41 154
h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
42
{
43
        const char *p;
44
45 154
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
46 154
        AN(b);
47 154
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
48 154
        assert(namelen <= len);
49
50 154
        if (namelen == 2) {
51 0
                VSLb(hp->vsl, SLT_BogoHeader, "Empty name");
52 0
                return (H2SE_PROTOCOL_ERROR);
53
        }
54
55 2236
        for (p = b; p < b + len; p++) {
56 2082
                if (p < b + (namelen - 2)) {
57
                        /* Check valid name characters */
58 1088
                        if (p == b && *p == ':')
59 126
                                continue; /* pseudo-header */
60 962
                        if (isupper(*p)) {
61 0
                                VSLb(hp->vsl, SLT_BogoHeader,
62
                                    "Illegal header name (upper-case): %.*s",
63
                                    (int)(len > 20 ? 20 : len), b);
64 0
                                return (H2SE_PROTOCOL_ERROR);
65
                        }
66 962
                        if (vct_istchar(*p)) {
67
                                /* XXX: vct should have a proper class for
68
                                   this avoiding two checks */
69 962
                                continue;
70
                        }
71 0
                        VSLb(hp->vsl, SLT_BogoHeader,
72
                            "Illegal header name: %.*s",
73
                            (int)(len > 20 ? 20 : len), b);
74 0
                        return (H2SE_PROTOCOL_ERROR);
75 994
                } else if (p < b + namelen) {
76
                        /* ': ' added by us */
77 308
                        assert(*p == ':' || *p == ' ');
78
                } else {
79
                        /* Check valid value characters */
80 686
                        if (!vct_isctl(*p) || vct_issp(*p))
81 686
                                continue;
82 0
                        VSLb(hp->vsl, SLT_BogoHeader,
83
                            "Illegal header value: %.*s",
84
                            (int)(len > 20 ? 20 : len), b);
85 0
                        return (H2SE_PROTOCOL_ERROR);
86
                }
87
        }
88
89 154
        return (0);
90
}
91
92
static h2_error
93 154
h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
94
{
95
        /* XXX: This might belong in cache/cache_http.c */
96
        unsigned n;
97
98 154
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
99 154
        AN(b);
100 154
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
101 154
        assert(namelen <= len);
102
103 154
        if (len > UINT_MAX) {   /* XXX: cache_param max header size */
104 0
                VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", b);
105 0
                return (H2SE_ENHANCE_YOUR_CALM);
106
        }
107
108 154
        if (b[0] == ':') {
109
                /* Match H/2 pseudo headers */
110
                /* XXX: Should probably have some include tbl for
111
                   pseudo-headers */
112 126
                if (!strncmp(b, ":method: ", namelen)) {
113 40
                        b += namelen;
114 40
                        len -= namelen;
115 40
                        n = HTTP_HDR_METHOD;
116 86
                } else if (!strncmp(b, ":path: ", namelen)) {
117 40
                        b += namelen;
118 40
                        len -= namelen;
119 40
                        n = HTTP_HDR_URL;
120 46
                } else if (!strncmp(b, ":scheme: ", namelen)) {
121
                        /* XXX: What to do about this one? (typically
122
                           "http" or "https"). For now set it as a normal
123
                           header, stripping the first ':'. */
124 40
                        b++;
125 40
                        len-=1;
126 40
                        n = hp->nhd;
127 6
                } else if (!strncmp(b, ":authority: ", namelen)) {
128 5
                        b+=6;
129 5
                        len-=6;
130 5
                        memcpy(b, "host", 4);
131 5
                        n = hp->nhd;
132
                } else {
133
                        /* Unknown pseudo-header */
134 1
                        VSLb(hp->vsl, SLT_BogoHeader,
135
                            "Unknown pseudo-header: %.*s",
136
                            (int)(len > 20 ? 20 : len), b);
137 1
                        return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,2990,2992
138
                }
139
        } else
140 28
                n = hp->nhd;
141
142 153
        if (n < HTTP_HDR_FIRST) {
143
                /* Check for duplicate pseudo-header */
144 80
                if (n < HTTP_HDR_FIRST && hp->hd[n].b != NULL) {
145 0
                        VSLb(hp->vsl, SLT_BogoHeader,
146
                            "Duplicate pseudo-header: %.*s",
147
                            (int)(len > 20 ? 20 : len), b);
148 0
                        return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,3158,3162
149
                }
150
        } else {
151
                /* Check for space in struct http */
152 73
                if (n >= hp->shd) {
153 0
                        VSLb(hp->vsl, SLT_LostHeader, "Too many headers: %.*s",
154
                            (int)(len > 20 ? 20 : len), b);
155 0
                        return (H2SE_ENHANCE_YOUR_CALM);
156
                }
157 73
                hp->nhd++;
158
        }
159
160 153
        hp->hd[n].b = b;
161 153
        hp->hd[n].e = b + len;
162
163 153
        return (0);
164
}
165
166
void
167 45
h2h_decode_init(const struct h2_sess *h2, struct h2h_decode *d)
168
{
169
170 45
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
171 45
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
172 45
        CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
173 45
        AN(d);
174 45
        INIT_OBJ(d, H2H_DECODE_MAGIC);
175 45
        VHD_Init(d->vhd);
176 45
        d->out_l = WS_Reserve(h2->new_req->http->ws, 0);
177 45
        assert(d->out_l > 0);   /* Can't do any work without any buffer
178
                                   space. Require non-zero size. */
179 45
        d->out = h2->new_req->http->ws->f;
180 45
        d->reset = d->out;
181 45
}
182
183
/* Possible error returns:
184
 *
185
 * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
186
 * block. This is a connection level error.
187
 *
188
 * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
189
 * is a stream level error.
190
 */
191
h2_error
192 45
h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d)
193
{
194
        h2_error ret;
195
196 45
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
197 45
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
198 45
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
199 45
        WS_ReleaseP(h2->new_req->http->ws, d->out);
200 45
        if (d->vhd_ret != VHD_OK) {
201
                /* HPACK header block didn't finish at an instruction
202
                   boundary */
203 4
                VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
204
                    "HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret));
205 4
                ret = H2CE_COMPRESSION_ERROR;
206
        } else
207 41
                ret = d->error;
208 45
        d->magic = 0;
209 45
        return (ret);
210
}
211
212
/* Possible error returns:
213
 *
214
 * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header
215
 * block. This is a connection level error.
216
 *
217
 * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header.
218
 */
219
h2_error
220 47
h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d,
221
    const uint8_t *in, size_t in_l)
222
{
223
        struct http *hp;
224 47
        size_t in_u = 0;
225
226 47
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
227 47
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
228 47
        hp = h2->new_req->http;
229 47
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
230 47
        CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC);
231 47
        AN(hp->ws->r);
232 47
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
233
234
        /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue
235
           processing. Other errors should have been returned and handled
236
           by the caller. */
237 47
        assert(d->error == 0 || d->error == H2SE_ENHANCE_YOUR_CALM);
238
239
        while (1) {
240 355
                AN(d->out);
241 355
                assert(d->out_u <= d->out_l);
242 355
                d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
243
                    d->out, d->out_l, &d->out_u);
244
245 355
                if (d->vhd_ret < 0) {
246 2
                        VSLb(hp->vsl, SLT_BogoHeader,
247
                            "HPACK compression error (%s)",
248
                            VHD_Error(d->vhd_ret));
249 2
                        d->error = H2CE_COMPRESSION_ERROR;
250 2
                        break;
251 353
                } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
252 44
                        assert(in_u == in_l);
253 44
                        break;
254
                }
255
256 309
                if (d->error == H2SE_ENHANCE_YOUR_CALM) {
257 0
                        d->out_u = 0;
258 0
                        assert(d->out_u < d->out_l);
259 0
                        continue;
260
                }
261
262 309
                switch (d->vhd_ret) {
263
                case VHD_NAME_SEC:
264
                        /* XXX: header flag for never-indexed header */
265
                case VHD_NAME:
266 155
                        assert(d->namelen == 0);
267 155
                        if (d->out_l - d->out_u < 2) {
268 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
269 0
                                break;
270
                        }
271 155
                        d->out[d->out_u++] = ':';
272 155
                        d->out[d->out_u++] = ' ';
273 155
                        d->namelen = d->out_u;
274 155
                        break;
275
276
                case VHD_VALUE_SEC:
277
                        /* XXX: header flag for never-indexed header */
278
                case VHD_VALUE:
279 154
                        assert(d->namelen > 0);
280 154
                        if (d->out_l - d->out_u < 1) {
281 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
282 0
                                break;
283
                        }
284 154
                        d->error = h2h_checkhdr(hp, d->out, d->namelen,
285
                            d->out_u);
286 154
                        if (d->error)
287 0
                                break;
288 154
                        d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
289 154
                        if (d->error)
290 1
                                break;
291 153
                        d->out[d->out_u++] = '\0'; /* Zero guard */
292 153
                        d->out += d->out_u;
293 153
                        d->out_l -= d->out_u;
294 153
                        d->out_u = 0;
295 153
                        d->namelen = 0;
296 153
                        break;
297
298
                case VHD_BUF:
299 0
                        d->error = H2SE_ENHANCE_YOUR_CALM;
300 0
                        break;
301
302
                default:
303 0
                        WRONG("Unhandled return value");
304
                        break;
305
                }
306
307 309
                if (d->error == H2SE_ENHANCE_YOUR_CALM) {
308 0
                        d->out = d->reset;
309 0
                        d->out_l = hp->ws->r - d->out;
310 0
                        d->out_u = 0;
311 0
                        assert(d->out_u < d->out_l);
312 309
                } else if (d->error)
313 1
                        break;
314 308
        }
315
316 47
        if (d->error == H2SE_ENHANCE_YOUR_CALM)
317 0
                return (0); /* Stream error, delay reporting until
318
                               h2h_decode_fini so that we can process the
319
                               complete header block */
320 47
        return (d->error);
321
}