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 216
h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
42
{
43
        const char *p;
44
45 216
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
46 216
        AN(b);
47 216
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
48 216
        assert(namelen <= len);
49
50 216
        if (namelen == 2) {
51 0
                VSLb(hp->vsl, SLT_BogoHeader, "Empty name");
52 0
                return (H2SE_PROTOCOL_ERROR);
53
        }
54
55 3035
        for (p = b; p < b + len; p++) {
56 2821
                if (p < b + (namelen - 2)) {
57
                        /* Check valid name characters */
58 1488
                        if (p == b && *p == ':')
59 183
                                continue; /* pseudo-header */
60 1305
                        if (isupper(*p)) {
61 1
                                VSLb(hp->vsl, SLT_BogoHeader,
62
                                    "Illegal header name (upper-case): %.*s",
63
                                    (int)(len > 20 ? 20 : len), b);
64 1
                                return (H2SE_PROTOCOL_ERROR);
65
                        }
66 1304
                        if (vct_istchar(*p)) {
67
                                /* XXX: vct should have a proper class for
68
                                   this avoiding two checks */
69 1303
                                continue;
70
                        }
71 1
                        VSLb(hp->vsl, SLT_BogoHeader,
72
                            "Illegal header name: %.*s",
73
                            (int)(len > 20 ? 20 : len), b);
74 1
                        return (H2SE_PROTOCOL_ERROR);
75 1333
                } else if (p < b + namelen) {
76
                        /* ': ' added by us */
77 428
                        assert(*p == ':' || *p == ' ');
78
                } else {
79
                        /* Check valid value characters */
80 905
                        if (!vct_isctl(*p) || vct_issp(*p))
81 905
                                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 214
        return (0);
90
}
91
92
static h2_error
93 214
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 214
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
99 214
        AN(b);
100 214
        assert(namelen >= 2);   /* 2 chars from the ': ' that we added */
101 214
        assert(namelen <= len);
102
103 214
        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 214
        if (b[0] == ':') {
109
                /* Match H/2 pseudo headers */
110
                /* XXX: Should probably have some include tbl for
111
                   pseudo-headers */
112 183
                if (!strncmp(b, ":method: ", namelen)) {
113 57
                        b += namelen;
114 57
                        len -= namelen;
115 57
                        n = HTTP_HDR_METHOD;
116 126
                } else if (!strncmp(b, ":path: ", namelen)) {
117 59
                        b += namelen;
118 59
                        len -= namelen;
119 59
                        n = HTTP_HDR_URL;
120 67
                } 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 58
                        b++;
125 58
                        len-=1;
126 58
                        n = hp->nhd;
127 9
                } else if (!strncmp(b, ":authority: ", namelen)) {
128 8
                        b+=6;
129 8
                        len-=6;
130 8
                        memcpy(b, "host", 4);
131 8
                        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 31
                n = hp->nhd;
141
142 213
        if (n < HTTP_HDR_FIRST) {
143
                /* Check for duplicate pseudo-header */
144 116
                if (n < HTTP_HDR_FIRST && hp->hd[n].b != NULL) {
145 1
                        VSLb(hp->vsl, SLT_BogoHeader,
146
                            "Duplicate pseudo-header: %.*s",
147
                            (int)(len > 20 ? 20 : len), b);
148 1
                        return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,3158,3162
149
                }
150
        } else {
151
                /* Check for space in struct http */
152 97
                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 97
                hp->nhd++;
158
        }
159
160 212
        hp->hd[n].b = b;
161 212
        hp->hd[n].e = b + len;
162
163 212
        return (0);
164
}
165
166
void
167 67
h2h_decode_init(const struct h2_sess *h2)
168
{
169
        struct h2h_decode *d;
170
171 67
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
172 67
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
173 67
        CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
174 67
        AN(h2->decode);
175 67
        d = h2->decode;
176 67
        INIT_OBJ(d, H2H_DECODE_MAGIC);
177 67
        VHD_Init(d->vhd);
178 67
        d->out_l = WS_Reserve(h2->new_req->http->ws, 0);
179 67
        assert(d->out_l > 0);   /* Can't do any work without any buffer
180
                                   space. Require non-zero size. */
181 67
        d->out = h2->new_req->http->ws->f;
182 67
        d->reset = d->out;
183 67
}
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 67
h2h_decode_fini(const struct h2_sess *h2)
195
{
196
        h2_error ret;
197
        struct h2h_decode *d;
198
199 67
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
200 67
        d = h2->decode;
201 67
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
202 67
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
203 67
        WS_ReleaseP(h2->new_req->http->ws, d->out);
204 67
        if (d->vhd_ret != VHD_OK) {
205
                /* HPACK header block didn't finish at an instruction
206
                   boundary */
207 7
                VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
208
                    "HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret));
209 7
                ret = H2CE_COMPRESSION_ERROR;
210
        } else
211 60
                ret = d->error;
212 67
        d->magic = 0;
213 67
        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 69
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 69
        size_t in_u = 0;
229
230 69
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
231 69
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
232 69
        hp = h2->new_req->http;
233 69
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
234 69
        CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC);
235 69
        AN(hp->ws->r);
236 69
        d = h2->decode;
237 69
        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 69
        assert(d->error == 0 || d->error == H2SE_ENHANCE_YOUR_CALM);
243
244
        while (1) {
245 927
                AN(d->out);
246 498
                assert(d->out_u <= d->out_l);
247 498
                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 498
                if (d->vhd_ret < 0) {
251 2
                        VSLb(hp->vsl, SLT_BogoHeader,
252
                            "HPACK compression error (%s)",
253
                            VHD_Error(d->vhd_ret));
254 2
                        d->error = H2CE_COMPRESSION_ERROR;
255 2
                        break;
256 496
                } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
257 63
                        assert(in_u == in_l);
258 63
                        break;
259
                }
260
261 433
                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 433
                switch (d->vhd_ret) {
268
                case VHD_NAME_SEC:
269
                        /* XXX: header flag for never-indexed header */
270
                case VHD_NAME:
271 217
                        assert(d->namelen == 0);
272 217
                        if (d->out_l - d->out_u < 2) {
273 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
274 0
                                break;
275
                        }
276 217
                        d->out[d->out_u++] = ':';
277 217
                        d->out[d->out_u++] = ' ';
278 217
                        d->namelen = d->out_u;
279 217
                        break;
280
281
                case VHD_VALUE_SEC:
282
                        /* XXX: header flag for never-indexed header */
283
                case VHD_VALUE:
284 216
                        assert(d->namelen > 0);
285 216
                        if (d->out_l - d->out_u < 1) {
286 0
                                d->error = H2SE_ENHANCE_YOUR_CALM;
287 0
                                break;
288
                        }
289 216
                        d->error = h2h_checkhdr(hp, d->out, d->namelen,
290
                            d->out_u);
291 216
                        if (d->error)
292 2
                                break;
293 214
                        d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
294 214
                        if (d->error)
295 2
                                break;
296 212
                        d->out[d->out_u++] = '\0'; /* Zero guard */
297 212
                        d->out += d->out_u;
298 212
                        d->out_l -= d->out_u;
299 212
                        d->out_u = 0;
300 212
                        d->namelen = 0;
301 212
                        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 433
                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 433
                } else if (d->error)
318 4
                        break;
319
        }
320
321 69
        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 69
        return (d->error);
326
}