1 /*****************************************************************************
2 * h2frame.c: HTTP/2 frame formatting
3 *****************************************************************************
4 * Copyright (C) 2015 Rémi Denis-Courmont
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <assert.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #include <vlc_common.h>
33
34 #include "conn.h"
35 #include "hpack.h"
36 #include "h2frame.h"
37
38 static struct vlc_h2_frame *
vlc_h2_frame_alloc(uint_fast8_t type,uint_fast8_t flags,uint_fast32_t stream_id,size_t length)39 vlc_h2_frame_alloc(uint_fast8_t type, uint_fast8_t flags,
40 uint_fast32_t stream_id, size_t length)
41 {
42 assert((stream_id >> 31) == 0);
43
44 if (unlikely(length >= (1u << 24)))
45 {
46 errno = EINVAL;
47 return NULL;
48 }
49
50 struct vlc_h2_frame *f = malloc(sizeof (*f) + 9 + length);
51 if (unlikely(f == NULL))
52 return NULL;
53
54 f->next = NULL;
55 f->data[0] = length >> 16;
56 f->data[1] = length >> 8;
57 f->data[2] = length;
58 f->data[3] = type;
59 f->data[4] = flags;
60 SetDWBE(f->data + 5, stream_id);
61 return f;
62 }
63
64 #define vlc_h2_frame_payload(f) ((f)->data + 9)
65
vlc_h2_frame_length(const struct vlc_h2_frame * f)66 static uint_fast32_t vlc_h2_frame_length(const struct vlc_h2_frame *f)
67 {
68 const uint8_t *buf = f->data;
69 return (buf[0] << 16) | (buf[1] << 8) | buf[2];
70 }
71
vlc_h2_frame_size(const struct vlc_h2_frame * f)72 size_t vlc_h2_frame_size(const struct vlc_h2_frame *f)
73 {
74 return 9 + vlc_h2_frame_length(f);
75 }
76
vlc_h2_frame_type(const struct vlc_h2_frame * f)77 static uint_fast8_t vlc_h2_frame_type(const struct vlc_h2_frame *f)
78 {
79 return f->data[3];
80 }
81
vlc_h2_frame_flags(const struct vlc_h2_frame * f)82 static uint_fast8_t vlc_h2_frame_flags(const struct vlc_h2_frame *f)
83 {
84 return f->data[4];
85 }
86
vlc_h2_frame_id(const struct vlc_h2_frame * f)87 static uint_fast32_t vlc_h2_frame_id(const struct vlc_h2_frame *f)
88 {
89 return GetDWBE(f->data + 5) & 0x7FFFFFFF;
90 }
91
92 enum {
93 VLC_H2_FRAME_DATA,
94 VLC_H2_FRAME_HEADERS,
95 VLC_H2_FRAME_PRIORITY,
96 VLC_H2_FRAME_RST_STREAM,
97 VLC_H2_FRAME_SETTINGS,
98 VLC_H2_FRAME_PUSH_PROMISE,
99 VLC_H2_FRAME_PING,
100 VLC_H2_FRAME_GOAWAY,
101 VLC_H2_FRAME_WINDOW_UPDATE,
102 VLC_H2_FRAME_CONTINUATION,
103 };
104
vlc_h2_type_name(uint_fast8_t type)105 static const char *vlc_h2_type_name(uint_fast8_t type)
106 {
107 static const char names[][14] = {
108 [VLC_H2_FRAME_DATA] = "DATA",
109 [VLC_H2_FRAME_HEADERS] = "HEADERS",
110 [VLC_H2_FRAME_PRIORITY] = "PRIORITY",
111 [VLC_H2_FRAME_RST_STREAM] = "RST_STREAM",
112 [VLC_H2_FRAME_SETTINGS] = "SETTINGS",
113 [VLC_H2_FRAME_PUSH_PROMISE] = "PUSH_PROMISE",
114 [VLC_H2_FRAME_PING] = "PING",
115 [VLC_H2_FRAME_GOAWAY] = "GOAWAY",
116 [VLC_H2_FRAME_WINDOW_UPDATE] = "WINDOW_UPDATE",
117 [VLC_H2_FRAME_CONTINUATION] = "CONTINUATION",
118 };
119
120 if (type >= (sizeof (names) / sizeof (names[0])) || names[type][0] == '\0')
121 return "<unknown>";
122 return names[type];
123 }
124
125 enum {
126 VLC_H2_DATA_END_STREAM = 0x01,
127 VLC_H2_DATA_PADDED = 0x08,
128 };
129
130 enum {
131 VLC_H2_HEADERS_END_STREAM = 0x01,
132 VLC_H2_HEADERS_END_HEADERS = 0x04,
133 VLC_H2_HEADERS_PADDED = 0x08,
134 VLC_H2_HEADERS_PRIORITY = 0x20,
135 };
136
137 enum {
138 VLC_H2_SETTINGS_ACK = 0x01,
139 };
140
141 enum {
142 VLC_H2_PUSH_PROMISE_END_HEADERS = 0x04,
143 VLC_H2_PUSH_PROMISE_PADDED = 0x08,
144 };
145
146 enum {
147 VLC_H2_PING_ACK = 0x01,
148 };
149
150 enum {
151 VLC_H2_CONTINUATION_END_HEADERS = 0x04,
152 };
153
154 struct vlc_h2_frame *
vlc_h2_frame_headers(uint_fast32_t stream_id,uint_fast32_t mtu,bool eos,unsigned count,const char * const headers[][2])155 vlc_h2_frame_headers(uint_fast32_t stream_id, uint_fast32_t mtu, bool eos,
156 unsigned count, const char *const headers[][2])
157 {
158 struct vlc_h2_frame *f;
159 uint8_t flags = eos ? VLC_H2_HEADERS_END_STREAM : 0;
160
161 size_t len = hpack_encode(NULL, 0, headers, count);
162
163 if (likely(len <= mtu))
164 { /* Most common case: single frame - with zero copy */
165 flags |= VLC_H2_HEADERS_END_HEADERS;
166
167 f = vlc_h2_frame_alloc(VLC_H2_FRAME_HEADERS, flags, stream_id, len);
168 if (unlikely(f == NULL))
169 return NULL;
170
171 hpack_encode(vlc_h2_frame_payload(f), len, headers, count);
172 return f;
173 }
174
175 /* Edge case: HEADERS frame then CONTINUATION frame(s) */
176 uint8_t *payload = malloc(len);
177 if (unlikely(payload == NULL))
178 return NULL;
179
180 hpack_encode(payload, len, headers, count);
181
182 struct vlc_h2_frame **pp = &f, *n;
183 const uint8_t *offset = payload;
184 uint_fast8_t type = VLC_H2_FRAME_HEADERS;
185
186 f = NULL;
187
188 while (len > mtu)
189 {
190 n = vlc_h2_frame_alloc(type, flags, stream_id, mtu);
191 if (unlikely(n == NULL))
192 goto error;
193
194 memcpy(vlc_h2_frame_payload(n), offset, mtu);
195 *pp = n;
196 pp = &n->next;
197
198 type = VLC_H2_FRAME_CONTINUATION;
199 flags = 0;
200 offset += mtu;
201 len -= mtu;
202 }
203
204 flags |= VLC_H2_CONTINUATION_END_HEADERS;
205
206 n = vlc_h2_frame_alloc(type, flags, stream_id, len);
207 if (unlikely(n == NULL))
208 goto error;
209
210 memcpy(vlc_h2_frame_payload(n), offset, len);
211 *pp = n;
212
213 free(payload);
214 return f;
215
216 error:
217 while (f != NULL)
218 {
219 n = f->next;
220 free(f);
221 f = n;
222 }
223 free(payload);
224 return NULL;
225 }
226
227 struct vlc_h2_frame *
vlc_h2_frame_data(uint_fast32_t stream_id,const void * buf,size_t len,bool eos)228 vlc_h2_frame_data(uint_fast32_t stream_id, const void *buf, size_t len,
229 bool eos)
230 {
231 struct vlc_h2_frame *f;
232 uint8_t flags = eos ? VLC_H2_DATA_END_STREAM : 0;
233
234 f = vlc_h2_frame_alloc(VLC_H2_FRAME_DATA, flags, stream_id, len);
235 if (likely(f != NULL))
236 memcpy(vlc_h2_frame_payload(f), buf, len);
237 return f;
238 }
239
240 struct vlc_h2_frame *
vlc_h2_frame_rst_stream(uint_fast32_t stream_id,uint_fast32_t error_code)241 vlc_h2_frame_rst_stream(uint_fast32_t stream_id, uint_fast32_t error_code)
242 {
243 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_RST_STREAM, 0,
244 stream_id, 4);
245 if (likely(f != NULL))
246 SetDWBE(vlc_h2_frame_payload(f), error_code);
247 return f;
248 }
249
vlc_h2_frame_settings(void)250 struct vlc_h2_frame *vlc_h2_frame_settings(void)
251 {
252 unsigned n = (VLC_H2_MAX_HEADER_TABLE != VLC_H2_DEFAULT_MAX_HEADER_TABLE)
253 + 1 /* ENABLE_PUSH */
254 #if defined(VLC_H2_MAX_STREAMS)
255 + 1
256 #endif
257 + (VLC_H2_INIT_WINDOW != VLC_H2_DEFAULT_INIT_WINDOW)
258 + (VLC_H2_MAX_FRAME != VLC_H2_DEFAULT_MAX_FRAME)
259 #if defined(VLC_H2_MAX_HEADER_LIST)
260 + 1
261 #endif
262 ;
263 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_SETTINGS, 0, 0,
264 n * 6);
265 if (unlikely(f == NULL))
266 return NULL;
267
268 uint8_t *p = vlc_h2_frame_payload(f);
269
270 #if (VLC_H2_MAX_HEADER_TABLE != VLC_H2_DEFAULT_MAX_HEADER_TABLE)
271 SetWBE(p, VLC_H2_SETTING_HEADER_TABLE_SIZE);
272 SetDWBE(p + 2, VLC_H2_MAX_HEADER_TABLE);
273 p += 6;
274 #endif
275
276 SetWBE(p, VLC_H2_SETTING_ENABLE_PUSH);
277 SetDWBE(p + 2, 0);
278 p += 6;
279
280 #if defined(VLC_H2_MAX_STREAMS)
281 SetWBE(p, VLC_H2_SETTING_MAX_CONCURRENT_STREAMS);
282 SetDWBE(p + 2, VLC_H2_MAX_STREAMS);
283 p += 6;
284 #endif
285
286 #if (VLC_H2_INIT_WINDOW != VLC_H2_DEFAULT_INIT_WINDOW)
287 # if (VLC_H2_INIT_WINDOW > 2147483647)
288 # error Illegal initial window value
289 # endif
290 SetWBE(p, VLC_H2_SETTING_INITIAL_WINDOW_SIZE);
291 SetDWBE(p + 2, VLC_H2_INIT_WINDOW);
292 p += 6;
293 #endif
294
295 #if (VLC_H2_MAX_FRAME != VLC_H2_DEFAULT_MAX_FRAME)
296 # if (VLC_H2_MAX_FRAME < 16384 || VLC_H2_MAX_FRAME > 16777215)
297 # error Illegal maximum frame size
298 # endif
299 SetWBE(p, VLC_H2_SETTING_MAX_FRAME_SIZE);
300 SetDWBE(p + 2, VLC_H2_MAX_FRAME);
301 p += 6;
302 #endif
303
304 #if defined(VLC_H2_MAX_HEADER_LIST)
305 SetWBE(p, VLC_H2_SETTING_MAX_HEADER_LIST_SIZE);
306 SetDWBE(p + 2, VLC_H2_MAX_HEADER_LIST);
307 p += 6;
308 #endif
309
310 return f;
311 }
312
vlc_h2_frame_settings_ack(void)313 struct vlc_h2_frame *vlc_h2_frame_settings_ack(void)
314 {
315 return vlc_h2_frame_alloc(VLC_H2_FRAME_SETTINGS, VLC_H2_SETTINGS_ACK, 0,
316 0);
317 }
318
vlc_h2_setting_name(uint_fast16_t id)319 const char *vlc_h2_setting_name(uint_fast16_t id)
320 {
321 static const char names[][20] = {
322 [0] = "Unknown setting",
323 [VLC_H2_SETTING_HEADER_TABLE_SIZE] = "Header table size",
324 [VLC_H2_SETTING_ENABLE_PUSH] = "Enable push",
325 [VLC_H2_SETTING_MAX_CONCURRENT_STREAMS] = "Concurrent streams",
326 [VLC_H2_SETTING_INITIAL_WINDOW_SIZE] = "Initial window size",
327 [VLC_H2_SETTING_MAX_FRAME_SIZE] = "Frame size",
328 [VLC_H2_SETTING_MAX_HEADER_LIST_SIZE] = "Header list size",
329 };
330
331 if (id >= sizeof (names) / sizeof (names[0]) || names[id][0] == '\0')
332 id = 0;
333 return names[id];
334 }
335
vlc_h2_frame_ping(uint64_t opaque)336 struct vlc_h2_frame *vlc_h2_frame_ping(uint64_t opaque)
337 {
338 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_PING, 0, 0, 8);
339 if (likely(f != NULL))
340 memcpy(vlc_h2_frame_payload(f), &opaque, 8);
341 return f;
342 }
343
vlc_h2_frame_pong(uint64_t opaque)344 struct vlc_h2_frame *vlc_h2_frame_pong(uint64_t opaque)
345 {
346 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_PING,
347 VLC_H2_PING_ACK, 0, 8);
348 if (likely(f != NULL))
349 memcpy(vlc_h2_frame_payload(f), &opaque, 8);
350 return f;
351 }
352
353 struct vlc_h2_frame *
vlc_h2_frame_goaway(uint_fast32_t last_stream_id,uint_fast32_t error_code)354 vlc_h2_frame_goaway(uint_fast32_t last_stream_id, uint_fast32_t error_code)
355 {
356 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_GOAWAY, 0, 0, 8);
357 if (likely(f != NULL))
358 {
359 uint8_t *p = vlc_h2_frame_payload(f);
360
361 SetDWBE(p, last_stream_id);
362 SetDWBE(p + 4, error_code);
363 }
364 return f;
365 }
366
367 struct vlc_h2_frame *
vlc_h2_frame_window_update(uint_fast32_t stream_id,uint_fast32_t credit)368 vlc_h2_frame_window_update(uint_fast32_t stream_id, uint_fast32_t credit)
369 {
370 assert((stream_id >> 31) == 0);
371
372 struct vlc_h2_frame *f = vlc_h2_frame_alloc(VLC_H2_FRAME_WINDOW_UPDATE,
373 0, stream_id, 4);
374 if (likely(f != NULL))
375 {
376 uint8_t *p = vlc_h2_frame_payload(f);
377
378 SetDWBE(p, credit);
379 }
380 return f;
381 }
382
vlc_h2_strerror(uint_fast32_t code)383 const char *vlc_h2_strerror(uint_fast32_t code)
384 {
385 static const char names[][20] = {
386 [VLC_H2_NO_ERROR] = "No error",
387 [VLC_H2_PROTOCOL_ERROR] = "Protocol error",
388 [VLC_H2_INTERNAL_ERROR] = "Internal error",
389 [VLC_H2_FLOW_CONTROL_ERROR] = "Flow control error",
390 [VLC_H2_SETTINGS_TIMEOUT] = "Settings time-out",
391 [VLC_H2_STREAM_CLOSED] = "Stream closed",
392 [VLC_H2_FRAME_SIZE_ERROR] = "Frame size error",
393 [VLC_H2_REFUSED_STREAM] = "Refused stream",
394 [VLC_H2_CANCEL] = "Cancellation",
395 [VLC_H2_COMPRESSION_ERROR] = "Compression error",
396 [VLC_H2_CONNECT_ERROR] = "CONNECT error",
397 [VLC_H2_ENHANCE_YOUR_CALM] = "Excessive load",
398 [VLC_H2_INADEQUATE_SECURITY] = "Inadequate security",
399 [VLC_H2_HTTP_1_1_REQUIRED] = "Required HTTP/1.1",
400 };
401
402 if (code >= sizeof (names) / sizeof (names[0]) || names[code][0] == '\0')
403 return "Unknown error";
404 return names[code];
405 }
406
vlc_h2_frame_dump(void * opaque,const struct vlc_h2_frame * f,const char * msg)407 void vlc_h2_frame_dump(void *opaque, const struct vlc_h2_frame *f,
408 const char *msg)
409 {
410 size_t len = vlc_h2_frame_length(f);
411 uint_fast8_t type = vlc_h2_frame_type(f);
412 uint_fast8_t flags = vlc_h2_frame_flags(f);
413 uint_fast32_t sid = vlc_h2_frame_id(f);
414
415 if (sid != 0)
416 vlc_http_dbg(opaque, "%s %s (0x%02"PRIxFAST8") frame of %zu bytes, "
417 "flags 0x%02"PRIxFAST8", stream %"PRIuFAST32, msg,
418 vlc_h2_type_name(type), type, len, flags, sid);
419 else
420 vlc_http_dbg(opaque, "%s %s (0x%02"PRIxFAST8") frame of %zu bytes, "
421 "flags 0x%02"PRIxFAST8", global", msg,
422 vlc_h2_type_name(type), type, len, flags);
423 }
424
425 const uint8_t *(vlc_h2_frame_data_get)(const struct vlc_h2_frame *f,
426 size_t *restrict lenp)
427 {
428 assert(vlc_h2_frame_type(f) == VLC_H2_FRAME_DATA);
429
430 size_t len = vlc_h2_frame_length(f);
431 uint_fast8_t flags = vlc_h2_frame_flags(f);
432 const uint8_t *ptr = vlc_h2_frame_payload(f);
433
434 /* At this point, the frame has already been validated by the parser. */
435 if (flags & VLC_H2_DATA_PADDED)
436 {
437 assert(len >= 1u && len >= 1u + ptr[0]);
438 len -= 1u + *(ptr++);
439 }
440
441 *lenp = len;
442 return ptr;
443 }
444
445 typedef int (*vlc_h2_parser)(struct vlc_h2_parser *, struct vlc_h2_frame *,
446 size_t, uint_fast32_t);
447
448 /** HTTP/2 incoming frames parser */
449 struct vlc_h2_parser
450 {
451 void *opaque;
452 const struct vlc_h2_parser_cbs *cbs;
453
454 vlc_h2_parser parser; /*< Parser state / callback for next frame */
455
456 struct
457 {
458 uint32_t sid; /*< Ongoing stream identifier */
459 bool eos; /*< End of stream after headers block */
460 size_t len; /*< Compressed headers buffer length */
461 uint8_t *buf; /*< Compressed headers buffer base address */
462 struct hpack_decoder *decoder; /*< HPACK decompressor state */
463 } headers; /*< Compressed headers reception state */
464
465 uint32_t rcwd_size; /*< Receive congestion window (bytes) */
466 };
467
468 static int vlc_h2_parse_generic(struct vlc_h2_parser *, struct vlc_h2_frame *,
469 size_t, uint_fast32_t);
470 static int vlc_h2_parse_headers_block(struct vlc_h2_parser *,
471 struct vlc_h2_frame *, size_t,
472 uint_fast32_t);
473
vlc_h2_parse_error(struct vlc_h2_parser * p,uint_fast32_t code)474 static int vlc_h2_parse_error(struct vlc_h2_parser *p, uint_fast32_t code)
475 {
476 p->cbs->error(p->opaque, code);
477 return -1;
478 }
479
vlc_h2_stream_error(struct vlc_h2_parser * p,uint_fast32_t id,uint_fast32_t code)480 static int vlc_h2_stream_error(struct vlc_h2_parser *p, uint_fast32_t id,
481 uint_fast32_t code)
482 {
483 return p->cbs->stream_error(p->opaque, id, code);
484 }
485
vlc_h2_stream_lookup(struct vlc_h2_parser * p,uint_fast32_t id)486 static void *vlc_h2_stream_lookup(struct vlc_h2_parser *p, uint_fast32_t id)
487 {
488 return p->cbs->stream_lookup(p->opaque, id);
489 }
490
vlc_h2_parse_headers_start(struct vlc_h2_parser * p,uint_fast32_t sid,bool eos)491 static void vlc_h2_parse_headers_start(struct vlc_h2_parser *p,
492 uint_fast32_t sid, bool eos)
493 {
494 assert(sid != 0);
495 assert(p->headers.sid == 0);
496
497 p->parser = vlc_h2_parse_headers_block;
498 p->headers.sid = sid;
499 p->headers.eos = eos;
500 p->headers.len = 0;
501 }
502
vlc_h2_parse_headers_append(struct vlc_h2_parser * p,const uint8_t * data,size_t len)503 static int vlc_h2_parse_headers_append(struct vlc_h2_parser *p,
504 const uint8_t *data, size_t len)
505 {
506 assert(p->headers.sid != 0);
507
508 if (p->headers.len + len > 65536)
509 return vlc_h2_parse_error(p, VLC_H2_INTERNAL_ERROR);
510
511 uint8_t *buf = realloc(p->headers.buf, p->headers.len + len);
512 if (unlikely(buf == NULL))
513 return vlc_h2_parse_error(p, VLC_H2_INTERNAL_ERROR);
514
515 p->headers.buf = buf;
516 memcpy(p->headers.buf + p->headers.len, data, len);
517 p->headers.len += len;
518 return 0;
519 }
520
vlc_h2_parse_headers_end(struct vlc_h2_parser * p)521 static int vlc_h2_parse_headers_end(struct vlc_h2_parser *p)
522 {
523 char *headers[VLC_H2_MAX_HEADERS][2];
524
525 /* TODO: limit total decompressed size of the headers list */
526 int n = hpack_decode(p->headers.decoder, p->headers.buf, p->headers.len,
527 headers, VLC_H2_MAX_HEADERS);
528 if (n > VLC_H2_MAX_HEADERS)
529 {
530 for (unsigned i = 0; i < VLC_H2_MAX_HEADERS; i++)
531 {
532 free(headers[i][0]);
533 free(headers[i][1]);
534 }
535 n = -1;
536 }
537 if (n < 0)
538 return vlc_h2_parse_error(p, VLC_H2_COMPRESSION_ERROR);
539
540 void *s = vlc_h2_stream_lookup(p, p->headers.sid);
541 int val = 0;
542
543 if (s != NULL)
544 {
545 const char *ch[n ? n : 1][2];
546
547 for (int i = 0; i < n; i++)
548 ch[i][0] = headers[i][0], ch[i][1] = headers[i][1];
549
550 p->cbs->stream_headers(s, n, ch);
551
552 if (p->headers.eos)
553 p->cbs->stream_end(s);
554 }
555 else
556 /* NOTE: The specification implies that the error should be sent for
557 * the first header frame. But we actually want to receive the whole
558 * fragmented headers block, to preserve the HPACK decoder state.
559 * So we send the error at the last header frame instead. */
560 val = vlc_h2_stream_error(p, p->headers.sid, VLC_H2_REFUSED_STREAM);
561
562 for (int i = 0; i < n; i++)
563 {
564 free(headers[i][0]);
565 free(headers[i][1]);
566 }
567
568 p->parser = vlc_h2_parse_generic;
569 p->headers.sid = 0;
570 return val;
571 }
572
573 /** Parses an HTTP/2 DATA frame */
vlc_h2_parse_frame_data(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)574 static int vlc_h2_parse_frame_data(struct vlc_h2_parser *p,
575 struct vlc_h2_frame *f, size_t len,
576 uint_fast32_t id)
577 {
578 uint_fast8_t flags = vlc_h2_frame_flags(f);
579 const uint8_t *ptr = vlc_h2_frame_payload(f);
580
581 if (id == 0)
582 {
583 free(f);
584 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
585 }
586
587 if (len > VLC_H2_MAX_FRAME)
588 {
589 free(f);
590 return vlc_h2_stream_error(p, id, VLC_H2_FRAME_SIZE_ERROR);
591 }
592
593 if (flags & VLC_H2_DATA_PADDED)
594 {
595 if (len < 1 || len < (1u + ptr[0]))
596 {
597 free(f);
598 return vlc_h2_stream_error(p, id, VLC_H2_FRAME_SIZE_ERROR);
599 }
600 len -= 1 + ptr[0];
601 }
602
603 if (len > p->rcwd_size)
604 {
605 free(f);
606 return vlc_h2_parse_error(p, VLC_H2_FLOW_CONTROL_ERROR);
607 }
608
609 p->rcwd_size -= len;
610 p->cbs->window_status(p->opaque, &p->rcwd_size);
611
612 void *s = vlc_h2_stream_lookup(p, id);
613 if (s == NULL)
614 {
615 free(f);
616 return vlc_h2_stream_error(p, id, VLC_H2_STREAM_CLOSED);
617 }
618
619 int ret = p->cbs->stream_data(s, f);
620 /* Frame gets consumed here ^^ */
621
622 if (flags & VLC_H2_DATA_END_STREAM)
623 p->cbs->stream_end(s);
624 return ret;
625 }
626
627 /** Parses an HTTP/2 HEADERS frame */
vlc_h2_parse_frame_headers(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)628 static int vlc_h2_parse_frame_headers(struct vlc_h2_parser *p,
629 struct vlc_h2_frame *f, size_t len,
630 uint_fast32_t id)
631 {
632 uint_fast8_t flags = vlc_h2_frame_flags(f);
633 const uint8_t *ptr = vlc_h2_frame_payload(f);
634
635 if (id == 0)
636 {
637 free(f);
638 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
639 }
640
641 if (len > VLC_H2_MAX_FRAME)
642 {
643 free(f);
644 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
645 }
646
647 if (flags & VLC_H2_HEADERS_PADDED)
648 {
649 if (len < 1 || len < (1u + ptr[0]))
650 {
651 free(f);
652 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
653 }
654 len -= 1 + ptr[0];
655 ptr++;
656 }
657
658 if (flags & VLC_H2_HEADERS_PRIORITY)
659 { /* Ignore priorities for now as we do not upload anything. */
660 if (len < 5)
661 {
662 free(f);
663 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
664 }
665 ptr += 5;
666 len -= 5;
667 }
668
669 vlc_h2_parse_headers_start(p, id, flags & VLC_H2_HEADERS_END_STREAM);
670
671 int ret = vlc_h2_parse_headers_append(p, ptr, len);
672
673 if (ret == 0 && (flags & VLC_H2_HEADERS_END_HEADERS))
674 ret = vlc_h2_parse_headers_end(p);
675
676 free(f);
677 return ret;
678 }
679
680 /** Parses an HTTP/2 PRIORITY frame */
vlc_h2_parse_frame_priority(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)681 static int vlc_h2_parse_frame_priority(struct vlc_h2_parser *p,
682 struct vlc_h2_frame *f, size_t len,
683 uint_fast32_t id)
684 {
685 free(f);
686
687 if (id == 0)
688 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
689
690 if (len != 5)
691 return vlc_h2_stream_error(p, id, VLC_H2_FRAME_SIZE_ERROR);
692
693 /* Ignore priorities for now as we do not upload much. */
694 return 0;
695 }
696
697 /** Parses an HTTP/2 RST_STREAM frame */
vlc_h2_parse_frame_rst_stream(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)698 static int vlc_h2_parse_frame_rst_stream(struct vlc_h2_parser *p,
699 struct vlc_h2_frame *f, size_t len,
700 uint_fast32_t id)
701 {
702 if (id == 0)
703 {
704 free(f);
705 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
706 }
707
708 if (len != 4)
709 {
710 free(f);
711 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
712 }
713
714 void *s = vlc_h2_stream_lookup(p, id);
715 uint_fast32_t code = GetDWBE(vlc_h2_frame_payload(f));
716
717 free(f);
718
719 if (s == NULL)
720 return 0;
721 return p->cbs->stream_reset(s, code);
722 }
723
724 /** Parses an HTTP/2 SETTINGS frame */
vlc_h2_parse_frame_settings(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)725 static int vlc_h2_parse_frame_settings(struct vlc_h2_parser *p,
726 struct vlc_h2_frame *f, size_t len,
727 uint_fast32_t id)
728 {
729 const uint8_t *ptr = vlc_h2_frame_payload(f);
730
731 if (id != 0)
732 {
733 free(f);
734 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
735 }
736
737 if (len % 6 || len > VLC_H2_MAX_FRAME)
738 {
739 free(f);
740 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
741 }
742
743 if (vlc_h2_frame_flags(f) & VLC_H2_SETTINGS_ACK)
744 {
745 free(f);
746 if (len != 0)
747 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
748 /* Ignore ACKs for now as we never change settings. */
749 return 0;
750 }
751
752 for (const uint8_t *end = ptr + len; ptr < end; ptr += 6)
753 p->cbs->setting(p->opaque, GetWBE(ptr), GetDWBE(ptr + 2));
754
755 free(f);
756 return p->cbs->settings_done(p->opaque);
757 }
758
759 /** Parses an HTTP/2 PUSH_PROMISE frame */
vlc_h2_parse_frame_push_promise(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)760 static int vlc_h2_parse_frame_push_promise(struct vlc_h2_parser *p,
761 struct vlc_h2_frame *f, size_t len,
762 uint_fast32_t id)
763 {
764 uint8_t flags = vlc_h2_frame_flags(f);
765 const uint8_t *ptr = vlc_h2_frame_payload(f);
766
767 if (id == 0)
768 {
769 free(f);
770 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
771 }
772
773 if (len > VLC_H2_MAX_FRAME)
774 {
775 free(f);
776 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
777 }
778
779 if (flags & VLC_H2_PUSH_PROMISE_PADDED)
780 {
781 if (len < 1 || len < (1u + ptr[0]))
782 {
783 free(f);
784 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
785 }
786 len -= 1 + ptr[0];
787 ptr++;
788 }
789
790 /* Not permitted by our settings. */
791 free(f);
792 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
793 }
794
795 /** Parses an HTTP/2 PING frame */
vlc_h2_parse_frame_ping(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)796 static int vlc_h2_parse_frame_ping(struct vlc_h2_parser *p,
797 struct vlc_h2_frame *f, size_t len,
798 uint_fast32_t id)
799 {
800 uint64_t opaque;
801
802 if (id != 0)
803 {
804 free(f);
805 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
806 }
807
808 if (len != 8)
809 {
810 free(f);
811 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
812 }
813
814 if (vlc_h2_frame_flags(f) & VLC_H2_PING_ACK)
815 {
816 free(f);
817 return 0;
818 }
819
820 memcpy(&opaque, vlc_h2_frame_payload(f), 8);
821 free(f);
822
823 return p->cbs->ping(p->opaque, opaque);
824 }
825
826 /** Parses an HTTP/2 GOAWAY frame */
vlc_h2_parse_frame_goaway(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)827 static int vlc_h2_parse_frame_goaway(struct vlc_h2_parser *p,
828 struct vlc_h2_frame *f, size_t len,
829 uint_fast32_t id)
830 {
831 const uint8_t *ptr = vlc_h2_frame_payload(f);
832
833 if (id != 0)
834 {
835 free(f);
836 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
837 }
838
839 if (len < 8 || len > VLC_H2_MAX_FRAME)
840 {
841 free(f);
842 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
843 }
844
845 uint_fast32_t last_id = GetDWBE(ptr) & 0x7FFFFFFF;
846 uint_fast32_t code = GetDWBE(ptr + 4);
847
848 free(f);
849 return p->cbs->reset(p->opaque, last_id, code);
850 }
851
852 /** Parses an HTTP/2 WINDOW_UPDATE frame */
vlc_h2_parse_frame_window_update(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)853 static int vlc_h2_parse_frame_window_update(struct vlc_h2_parser *p,
854 struct vlc_h2_frame *f, size_t len,
855 uint_fast32_t id)
856 {
857 free(f);
858
859 if (len != 4)
860 {
861 if (id == 0)
862 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
863 return vlc_h2_stream_error(p, id, VLC_H2_FRAME_SIZE_ERROR);
864 }
865
866 /* Nothing to do as we do not send data for the time being. */
867 return 0;
868 }
869
870 /** Parses an HTTP/2 CONTINUATION frame */
vlc_h2_parse_frame_continuation(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)871 static int vlc_h2_parse_frame_continuation(struct vlc_h2_parser *p,
872 struct vlc_h2_frame *f, size_t len,
873 uint_fast32_t id)
874 {
875 const uint8_t *ptr = vlc_h2_frame_payload(f);
876
877 /* Stream ID must match with the previous frame. */
878 if (id == 0 || id != p->headers.sid)
879 {
880 free(f);
881 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
882 }
883
884 if (len > VLC_H2_MAX_FRAME)
885 {
886 free(f);
887 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
888 }
889
890 int ret = vlc_h2_parse_headers_append(p, ptr, len);
891
892 if (ret == 0 && (vlc_h2_frame_flags(f) & VLC_H2_CONTINUATION_END_HEADERS))
893 ret = vlc_h2_parse_headers_end(p);
894
895 free(f);
896 return 0;
897 }
898
899 /** Parses an HTTP/2 frame of unknown type */
vlc_h2_parse_frame_unknown(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)900 static int vlc_h2_parse_frame_unknown(struct vlc_h2_parser *p,
901 struct vlc_h2_frame *f, size_t len,
902 uint_fast32_t id)
903 {
904 free(f);
905
906 if (len > VLC_H2_MAX_FRAME)
907 {
908 if (id == 0)
909 return vlc_h2_parse_error(p, VLC_H2_FRAME_SIZE_ERROR);
910 return vlc_h2_stream_error(p, id, VLC_H2_FRAME_SIZE_ERROR);
911 }
912
913 /* Ignore frames of unknown type as specified. */
914 return 0;
915 }
916
917 static const vlc_h2_parser vlc_h2_parsers[] = {
918 [VLC_H2_FRAME_DATA] = vlc_h2_parse_frame_data,
919 [VLC_H2_FRAME_HEADERS] = vlc_h2_parse_frame_headers,
920 [VLC_H2_FRAME_PRIORITY] = vlc_h2_parse_frame_priority,
921 [VLC_H2_FRAME_RST_STREAM] = vlc_h2_parse_frame_rst_stream,
922 [VLC_H2_FRAME_SETTINGS] = vlc_h2_parse_frame_settings,
923 [VLC_H2_FRAME_PUSH_PROMISE] = vlc_h2_parse_frame_push_promise,
924 [VLC_H2_FRAME_PING] = vlc_h2_parse_frame_ping,
925 [VLC_H2_FRAME_GOAWAY] = vlc_h2_parse_frame_goaway,
926 [VLC_H2_FRAME_WINDOW_UPDATE] = vlc_h2_parse_frame_window_update,
927 [VLC_H2_FRAME_CONTINUATION] = vlc_h2_parse_frame_continuation,
928 };
929
930 /** Parses the HTTP/2 connection preface. */
vlc_h2_parse_preface(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)931 static int vlc_h2_parse_preface(struct vlc_h2_parser *p,
932 struct vlc_h2_frame *f, size_t len,
933 uint_fast32_t id)
934 {
935 /* The length must be within the specification default limits. */
936 if (len > VLC_H2_DEFAULT_MAX_FRAME
937 /* The type must SETTINGS. */
938 || vlc_h2_frame_type(f) != VLC_H2_FRAME_SETTINGS
939 /* The SETTINGS ACK flag must be clear. */
940 || (vlc_h2_frame_flags(f) & VLC_H2_SETTINGS_ACK))
941 {
942 free(f);
943 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
944 }
945
946 p->parser = vlc_h2_parse_generic;
947
948 return vlc_h2_parse_frame_settings(p, f, len, id);
949 }
950
951 /** Parses any HTTP/2 frame. */
vlc_h2_parse_generic(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)952 static int vlc_h2_parse_generic(struct vlc_h2_parser *p,
953 struct vlc_h2_frame *f, size_t len,
954 uint_fast32_t id)
955 {
956 uint_fast8_t type = vlc_h2_frame_type(f);
957 vlc_h2_parser func = vlc_h2_parse_frame_unknown;
958
959 assert(p->headers.sid == 0);
960
961 if (type < sizeof (vlc_h2_parsers) / sizeof (vlc_h2_parsers[0])
962 && vlc_h2_parsers[type] != NULL)
963 func = vlc_h2_parsers[type];
964
965 return func(p, f, len, id);
966 }
967
vlc_h2_parse_headers_block(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)968 static int vlc_h2_parse_headers_block(struct vlc_h2_parser *p,
969 struct vlc_h2_frame *f, size_t len,
970 uint_fast32_t id)
971 {
972 assert(p->headers.sid != 0);
973
974 /* After a HEADER, PUSH_PROMISE of CONTINUATION frame without the
975 * END_HEADERS flag, must come a CONTINUATION frame. */
976 if (vlc_h2_frame_type(f) != VLC_H2_FRAME_CONTINUATION)
977 {
978 free(f);
979 return vlc_h2_parse_error(p, VLC_H2_PROTOCOL_ERROR);
980 }
981
982 return vlc_h2_parse_frame_continuation(p, f, len, id);
983 }
984
vlc_h2_parse_failed(struct vlc_h2_parser * p,struct vlc_h2_frame * f,size_t len,uint_fast32_t id)985 static int vlc_h2_parse_failed(struct vlc_h2_parser *p, struct vlc_h2_frame *f,
986 size_t len, uint_fast32_t id)
987 {
988 free(f);
989 (void) p; (void) len; (void) id;
990 return -1;
991 }
992
vlc_h2_parse(struct vlc_h2_parser * p,struct vlc_h2_frame * f)993 int vlc_h2_parse(struct vlc_h2_parser *p, struct vlc_h2_frame *f)
994 {
995 int ret = 0;
996
997 while (f != NULL)
998 {
999 struct vlc_h2_frame *next = f->next;
1000 size_t len = vlc_h2_frame_length(f);
1001 uint_fast32_t id = vlc_h2_frame_id(f);
1002
1003 f->next = NULL;
1004 ret = p->parser(p, f, len, id);
1005 if (ret)
1006 p->parser = vlc_h2_parse_failed;
1007 f = next;
1008 }
1009
1010 return ret;
1011 }
1012
vlc_h2_parse_init(void * ctx,const struct vlc_h2_parser_cbs * cbs)1013 struct vlc_h2_parser *vlc_h2_parse_init(void *ctx,
1014 const struct vlc_h2_parser_cbs *cbs)
1015 {
1016 struct vlc_h2_parser *p = malloc(sizeof (*p));
1017 if (unlikely(p == NULL))
1018 return NULL;
1019
1020 p->opaque = ctx;
1021 p->cbs = cbs;
1022 p->parser = vlc_h2_parse_preface;
1023 p->headers.sid = 0;
1024 p->headers.buf = NULL;
1025 p->headers.len = 0;
1026 p->headers.decoder = hpack_decode_init(VLC_H2_MAX_HEADER_TABLE);
1027 if (unlikely(p->headers.decoder == NULL))
1028 {
1029 free(p);
1030 return NULL;
1031 }
1032 p->rcwd_size = 65535; /* initial per-connection value */
1033 return p;
1034 }
1035
vlc_h2_parse_destroy(struct vlc_h2_parser * p)1036 void vlc_h2_parse_destroy(struct vlc_h2_parser *p)
1037 {
1038 hpack_decode_destroy(p->headers.decoder);
1039 free(p->headers.buf);
1040 free(p);
1041 }
1042