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