lsquic_hcsi_reader.c revision 5392f7a3
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <assert.h>
3#include <inttypes.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <string.h>
7#include <sys/queue.h>
8
9#include "lsquic.h"
10#include "lsquic_int_types.h"
11#include "lsquic_varint.h"
12#include "lsquic_hq.h"
13#include "lsquic_hash.h"
14#include "lsquic_conn.h"
15#include "lsquic_hcsi_reader.h"
16
17#define LSQUIC_LOGGER_MODULE LSQLM_HCSI_READER
18#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(reader->hr_conn)
19#include "lsquic_logger.h"
20
21#define MIN(a, b) ((a) < (b) ? (a) : (b))
22
23
24void
25lsquic_hcsi_reader_init (struct hcsi_reader *reader,
26        struct lsquic_conn *conn, const struct hcsi_callbacks *callbacks,
27        void *ctx)
28{
29    memset(reader, 0, sizeof(*reader));
30    reader->hr_state = HR_READ_FRAME_BEGIN;
31    reader->hr_conn = conn;
32    reader->hr_cb = callbacks;
33    reader->hr_ctx = ctx;
34    LSQ_DEBUG("initialized");
35}
36
37
38int
39lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
40                                                                size_t bufsz)
41{
42    const unsigned char *p = buf;
43    const unsigned char *const end = p + bufsz;
44
45    const unsigned char *orig_p;
46    enum h3_prio_frame_read_status prio_status;
47    uint64_t len;
48    int s;
49
50    while (p < end)
51    {
52        switch (reader->hr_state)
53        {
54        case HR_READ_FRAME_BEGIN:
55            reader->hr_u.vint2_state.vr2s_state = 0;
56            reader->hr_state = HR_READ_FRAME_CONTINUE;
57            /* fall-through */
58        case HR_READ_FRAME_CONTINUE:
59            s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
60            if (s < 0)
61                break;
62            reader->hr_frame_type = reader->hr_u.vint2_state.vr2s_one;
63            reader->hr_frame_length = reader->hr_u.vint2_state.vr2s_two;
64            switch (reader->hr_frame_type)
65            {
66            case HQFT_SETTINGS:
67                if (reader->hr_frame_length)
68                {
69                    reader->hr_state = HR_READ_SETTING_BEGIN;
70                    reader->hr_nread = 0;
71                }
72                else
73                {
74                    reader->hr_cb->on_settings_frame(reader->hr_ctx);
75                    reader->hr_state = HR_READ_FRAME_BEGIN;
76                }
77                break;
78            case HQFT_PRIORITY:
79                reader->hr_state = HR_READ_PRIORITY_BEGIN;
80                break;
81            case HQFT_GOAWAY:
82                reader->hr_state = HR_READ_VARINT;
83                break;
84            case HQFT_CANCEL_PUSH:
85                reader->hr_state = HR_READ_VARINT;
86                break;
87            case HQFT_MAX_PUSH_ID:
88                reader->hr_state = HR_READ_VARINT;
89                break;
90            case HQFT_DATA:
91            case HQFT_HEADERS:
92            case HQFT_PUSH_PROMISE:
93                reader->hr_cb->on_unexpected_frame(reader->hr_ctx,
94                                                        reader->hr_frame_type);
95                return -1;
96            default:
97                if (!(reader->hr_frame_type >= 0xB &&
98                        (reader->hr_frame_type - 0xB) % 0x1F == 0))
99                    LSQ_INFO("unknown frame type 0x%"PRIX64" -- skipping",
100                                                        reader->hr_frame_type);
101                reader->hr_state = HR_SKIPPING;
102                LSQ_DEBUG("unknown frame 0x%"PRIX64": will skip %"PRIu64" bytes",
103                            reader->hr_frame_type, reader->hr_frame_length);
104                break;
105            }
106            break;
107        case HR_READ_VARINT:
108            reader->hr_u.vint_state.pos = 0;
109            reader->hr_state = HR_READ_VARINT_CONTINUE;
110            reader->hr_nread = 0;
111            /* fall-through */
112        case HR_READ_VARINT_CONTINUE:
113            orig_p = p;
114            s = lsquic_varint_read_nb(&p, end, &reader->hr_u.vint_state);
115            reader->hr_nread += p - orig_p;
116            if (0 == s)
117            {
118                if (reader->hr_nread != reader->hr_frame_length)
119                {
120                    reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
121                        HEC_MALFORMED_FRAME + reader->hr_frame_type,
122                        "Frame length does not match actual payload length");
123                    reader->hr_state = HR_ERROR;
124                    return -1;
125                }
126                switch (reader->hr_frame_type)
127                {
128                case HQFT_GOAWAY:
129                    reader->hr_cb->on_goaway(reader->hr_ctx,
130                                                reader->hr_u.vint_state.val);
131                    break;
132                case HQFT_CANCEL_PUSH:
133                    reader->hr_cb->on_cancel_push(reader->hr_ctx,
134                                                reader->hr_u.vint_state.val);
135                    break;
136                case HQFT_MAX_PUSH_ID:
137                    reader->hr_cb->on_max_push_id(reader->hr_ctx,
138                                                reader->hr_u.vint_state.val);
139                    break;
140                default:
141                    assert(0);
142                }
143                reader->hr_state = HR_READ_FRAME_BEGIN;
144                break;
145            }
146            else
147            {
148                assert(p == end);
149                return 0;
150            }
151        case HR_SKIPPING:
152            len = MIN((uintptr_t) (end - p), reader->hr_frame_length);
153            p += len;
154            reader->hr_frame_length -= len;
155            if (0 == reader->hr_frame_length)
156                reader->hr_state = HR_READ_FRAME_BEGIN;
157            break;
158        case HR_READ_SETTING_BEGIN:
159            reader->hr_u.vint2_state.vr2s_state = 0;
160            reader->hr_state = HR_READ_SETTING_CONTINUE;
161            /* fall-through */
162        case HR_READ_SETTING_CONTINUE:
163            orig_p = p;
164            s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
165            reader->hr_nread += p - orig_p;
166            if (reader->hr_nread > reader->hr_frame_length)
167            {
168                reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
169                    HEC_MALFORMED_FRAME + HQFT_SETTINGS,
170                    "SETTING frame contents too long");
171                reader->hr_state = HR_ERROR;
172                return -1;
173            }
174            if (s < 0)
175                break;
176            reader->hr_cb->on_setting(reader->hr_ctx,
177                    reader->hr_u.vint2_state.vr2s_one,
178                    reader->hr_u.vint2_state.vr2s_two);
179            if (reader->hr_nread >= reader->hr_frame_length)
180            {
181                reader->hr_state = HR_READ_FRAME_BEGIN;
182                reader->hr_cb->on_settings_frame(reader->hr_ctx);
183            }
184            else
185                reader->hr_state = HR_READ_SETTING_BEGIN;
186            break;
187        case HR_READ_PRIORITY_BEGIN:
188            reader->hr_u.prio.h3pfrs_state = 0;
189            reader->hr_nread = 0;
190            reader->hr_state = HR_READ_PRIORITY_CONTINUE;
191            /* fall-through */
192        case HR_READ_PRIORITY_CONTINUE:
193            orig_p = p;
194            prio_status = lsquic_h3_prio_frame_read(&p, end - p,
195                                                        &reader->hr_u.prio);
196            reader->hr_nread += p - orig_p;
197            if (prio_status == H3PFR_STATUS_DONE)
198            {
199                if (reader->hr_nread != reader->hr_frame_length)
200                {
201                    reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
202                        HEC_MALFORMED_FRAME + HQFT_PRIORITY, "PRIORITY frame "
203                        "contents size does not match frame length");
204                    reader->hr_state = HR_ERROR;
205                    return -1;
206                }
207                reader->hr_state = HR_READ_FRAME_BEGIN;
208                reader->hr_cb->on_priority(reader->hr_ctx,
209                                                    &reader->hr_u.prio.h3pfrs_prio);
210            }
211            break;
212        default:
213            assert(0);
214            /* fall-through */
215        case HR_ERROR:
216            return -1;
217        }
218    }
219
220    return 0;
221}
222