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 492
h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
42
{
43
        const char *p;
44
45 492
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
46 492
        AN(b);
47 492
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
48 492
        assert(namelen <= len);
49
50 492
        if (namelen == 2) {
51 0
                VSLb(hp->vsl, SLT_BogoHeader, "Empty name");
52 0
                return (H2SE_PROTOCOL_ERROR);
53
        }
54
55 6778
        for (p = b; p < b + len; p++) {
56 6290
                if (p < b + (namelen - 2)) {
57
                        /* Check valid name characters */
58 3356
                        if (p == b && *p == ':')
59 426
                                continue; /* pseudo-header */
60 2930
                        if (isupper(*p)) {
61 2
                                VSLb(hp->vsl, SLT_BogoHeader,
62
                                    "Illegal header name (upper-case): %.*s",
63
                                    (int)(len > 20 ? 20 : len), b);
64 2
                                return (H2SE_PROTOCOL_ERROR);
65
                        }
66 2928
                        if (vct_istchar(*p)) {
67
                                /* XXX: vct should have a proper class for
68
                                   this avoiding two checks */
69 2926
                                continue;
70
                        }
71 2
                        VSLb(hp->vsl, SLT_BogoHeader,
72
                            "Illegal header name: %.*s",
73
                            (int)(len > 20 ? 20 : len), b);
74 2
                        return (H2SE_PROTOCOL_ERROR);
75 2934
                } else if (p < b + namelen) {
76
                        /* ': ' added by us */
77 976
                        assert(*p == ':' || *p == ' ');
78
                } else {
79
                        /* Check valid value characters */
80 1958
                        if (!vct_isctl(*p) || vct_issp(*p))
81 1958
                                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 488
        return (0);
90
}
91
92
static h2_error
93 488
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 488
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
99 488
        AN(b);
100 488
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
101 488
        assert(namelen <= len);
102
103 488
        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 488
        if (b[0] == ':') {
109
                /* Match H/2 pseudo headers */
110
                /* XXX: Should probably have some include tbl for
111
                   pseudo-headers */
112 426
                if (!strncmp(b, ":method: ", namelen)) {
113 134
                        b += namelen;
114 134
                        len -= namelen;
115 134
                        n = HTTP_HDR_METHOD;
116 292
                } else if (!strncmp(b, ":path: ", namelen)) {
117 138
                        b += namelen;
118 138
                        len -= namelen;
119 138
                        n = HTTP_HDR_URL;
120 154
                } 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 136
                        b++;
125 136
                        len-=1;
126 136
                        n = hp->nhd;
127 18
                } else if (!strncmp(b, ":authority: ", namelen)) {
128 16
                        b+=6;
129 16
                        len-=6;
130 16
                        memcpy(b, "host", 4);
131 16
                        n = hp->nhd;
132
                } else {
133
                        /* Unknown pseudo-header */
134 2
                        VSLb(hp->vsl, SLT_BogoHeader,
135
                            "Unknown pseudo-header: %.*s",
136
                            (int)(len > 20 ? 20 : len), b);
137 2
                        return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,2990,2992
138
                }
139
        } else
140 62
                n = hp->nhd;
141
142 486
        if (n < HTTP_HDR_FIRST) {
143
                /* Check for duplicate pseudo-header */
144 272
                if (n < HTTP_HDR_FIRST && hp->hd[n].b != NULL) {
145 2
                        VSLb(hp->vsl, SLT_BogoHeader,
146
                            "Duplicate pseudo-header: %.*s",
147
                            (int)(len > 20 ? 20 : len), b);
148 2
                        return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,3158,3162
149
                }
150
        } else {
151
                /* Check for space in struct http */
152 214
                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 214
                hp->nhd++;
158
        }
159
160 484
        hp->hd[n].b = b;
161 484
        hp->hd[n].e = b + len;
162
163 484
        return (0);
164
}
165
166
void
167 154
h2h_decode_init(const struct h2_sess *h2)
168
{
169
        struct h2h_decode *d;
170
171 154
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
172 154
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
173 154
        CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
174 154
        AN(h2->decode);
175 154
        d = h2->decode;
176 154
        INIT_OBJ(d, H2H_DECODE_MAGIC);
177 154
        VHD_Init(d->vhd);
178 154
        d->out_l = WS_Reserve(h2->new_req->http->ws, 0);
179 154
        assert(d->out_l > 0);   /* Can't do any work without any buffer
180
                                   space. Require non-zero size. */
181 154
        d->out = h2->new_req->http->ws->f;
182 154
        d->reset = d->out;
183 154
}
184
185
/* Possible error returns:
186
 *
187
 * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
188
 * block. This is a connection level error.
189
 *
190
 * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
191
 * is a stream level error.
192
 */
193
h2_error
194 154
h2h_decode_fini(const struct h2_sess *h2)
195
{
196
        h2_error ret;
197
        struct h2h_decode *d;
198
199 154
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
200 154
        d = h2->decode;
201 154
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
202 154
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
203 154
        WS_ReleaseP(h2->new_req->http->ws, d->out);
204 154
        if (d->vhd_ret != VHD_OK) {
205
                /* HPACK header block didn't finish at an instruction
206
                   boundary */
207 14
                VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
208
                    "HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret));
209 14
                ret = H2CE_COMPRESSION_ERROR;
210
        } else
211 140
                ret = d->error;
212 154
        d->magic = 0;
213 154
        return (ret);
214
}
215
216
/* Possible error returns:
217
 *
218
 * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header
219
 * block. This is a connection level error.
220
 *
221
 * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header.
222
 */
223
h2_error
224 158
h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
225
{
226
        struct http *hp;
227
        struct h2h_decode *d;
228 158
        size_t in_u = 0;
229
230 158
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
231 158
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
232 158
        hp = h2->new_req->http;
233 158
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
234 158
        CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC);
235 158
        AN(hp->ws->r);
236 158
        d = h2->decode;
237 158
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
238
239
        /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue
240
           processing. Other errors should have been returned and handled
241
           by the caller. */
242 158
        assert(d->error == 0 || d->error == H2SE_ENHANCE_YOUR_CALM);
243
244
        while (1) {
245 2114
                AN(d->out);
246 1136
                assert(d->out_u <= d->out_l);
247 1136
                d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
248
                    d->out, d->out_l, &d->out_u);
249
250 1136
                if (d->vhd_ret < 0) {
251 4
                        VSLb(hp->vsl, SLT_BogoHeader,
252
                            "HPACK compression error (%s)",
253
                            VHD_Error(d->vhd_ret));
254 4
                        d->error = H2CE_COMPRESSION_ERROR;
255 4
                        break;
256 1132
                } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
257 146
                        assert(in_u == in_l);
258 146
                        break;
259
                }
260
261 986
                if (d->error == H2SE_ENHANCE_YOUR_CALM) {
262 0
                        d->out_u = 0;
263 0
                        assert(d->out_u < d->out_l);
264 0
                        continue;
265
                }
266
267 986
                switch (d->vhd_ret) {
268
                case VHD_NAME_SEC:
269
                        /* XXX: header flag for never-indexed header */
270
                case VHD_NAME:
271 494
                        assert(d->namelen == 0);
272 494
                        if (d->out_l - d->out_u < 2) {
273 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
274 0
                                break;
275
                        }
276 494
                        d->out[d->out_u++] = ':';
277 494
                        d->out[d->out_u++] = ' ';
278 494
                        d->namelen = d->out_u;
279 494
                        break;
280
281
                case VHD_VALUE_SEC:
282
                        /* XXX: header flag for never-indexed header */
283
                case VHD_VALUE:
284 492
                        assert(d->namelen > 0);
285 492
                        if (d->out_l - d->out_u < 1) {
286 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
287 0
                                break;
288
                        }
289 492
                        d->error = h2h_checkhdr(hp, d->out, d->namelen,
290
                            d->out_u);
291 492
                        if (d->error)
292 4
                                break;
293 488
                        d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
294 488
                        if (d->error)
295 4
                                break;
296 484
                        d->out[d->out_u++] = '\0'; /* Zero guard */
297 484
                        d->out += d->out_u;
298 484
                        d->out_l -= d->out_u;
299 484
                        d->out_u = 0;
300 484
                        d->namelen = 0;
301 484
                        break;
302
303
                case VHD_BUF:
304 0
                        d->error = H2SE_ENHANCE_YOUR_CALM;
305 0
                        break;
306
307
                default:
308 0
                        WRONG("Unhandled return value");
309
                        break;
310
                }
311
312 986
                if (d->error == H2SE_ENHANCE_YOUR_CALM) {
313 0
                        d->out = d->reset;
314 0
                        d->out_l = hp->ws->r - d->out;
315 0
                        d->out_u = 0;
316 0
                        assert(d->out_u < d->out_l);
317 986
                } else if (d->error)
318 8
                        break;
319
        }
320
321 158
        if (d->error == H2SE_ENHANCE_YOUR_CALM)
322 0
                return (0); /* Stream error, delay reporting until
323
                               h2h_decode_fini so that we can process the
324
                               complete header block */
325 158
        return (d->error);
326
}