1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
25392f7a3SLiteSpeed Tech#include <assert.h>
35392f7a3SLiteSpeed Tech#include <inttypes.h>
45392f7a3SLiteSpeed Tech#include <stddef.h>
55392f7a3SLiteSpeed Tech#include <stdint.h>
65392f7a3SLiteSpeed Tech#include <string.h>
75392f7a3SLiteSpeed Tech#include <sys/queue.h>
85392f7a3SLiteSpeed Tech
95392f7a3SLiteSpeed Tech#include "lsquic.h"
105392f7a3SLiteSpeed Tech#include "lsquic_int_types.h"
115392f7a3SLiteSpeed Tech#include "lsquic_varint.h"
125392f7a3SLiteSpeed Tech#include "lsquic_hq.h"
135392f7a3SLiteSpeed Tech#include "lsquic_hash.h"
145392f7a3SLiteSpeed Tech#include "lsquic_conn.h"
155392f7a3SLiteSpeed Tech#include "lsquic_hcsi_reader.h"
165392f7a3SLiteSpeed Tech
175392f7a3SLiteSpeed Tech#define LSQUIC_LOGGER_MODULE LSQLM_HCSI_READER
185392f7a3SLiteSpeed Tech#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(reader->hr_conn)
195392f7a3SLiteSpeed Tech#include "lsquic_logger.h"
205392f7a3SLiteSpeed Tech
215392f7a3SLiteSpeed Tech#define MIN(a, b) ((a) < (b) ? (a) : (b))
225392f7a3SLiteSpeed Tech
235392f7a3SLiteSpeed Tech
245392f7a3SLiteSpeed Techvoid
255392f7a3SLiteSpeed Techlsquic_hcsi_reader_init (struct hcsi_reader *reader,
265392f7a3SLiteSpeed Tech        struct lsquic_conn *conn, const struct hcsi_callbacks *callbacks,
275392f7a3SLiteSpeed Tech        void *ctx)
285392f7a3SLiteSpeed Tech{
295392f7a3SLiteSpeed Tech    memset(reader, 0, sizeof(*reader));
305392f7a3SLiteSpeed Tech    reader->hr_state = HR_READ_FRAME_BEGIN;
315392f7a3SLiteSpeed Tech    reader->hr_conn = conn;
325392f7a3SLiteSpeed Tech    reader->hr_cb = callbacks;
335392f7a3SLiteSpeed Tech    reader->hr_ctx = ctx;
345392f7a3SLiteSpeed Tech    LSQ_DEBUG("initialized");
355392f7a3SLiteSpeed Tech}
365392f7a3SLiteSpeed Tech
375392f7a3SLiteSpeed Tech
385392f7a3SLiteSpeed Techint
395392f7a3SLiteSpeed Techlsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
405392f7a3SLiteSpeed Tech                                                                size_t bufsz)
415392f7a3SLiteSpeed Tech{
425392f7a3SLiteSpeed Tech    const unsigned char *p = buf;
435392f7a3SLiteSpeed Tech    const unsigned char *const end = p + bufsz;
445392f7a3SLiteSpeed Tech
455392f7a3SLiteSpeed Tech    const unsigned char *orig_p;
465392f7a3SLiteSpeed Tech    uint64_t len;
475392f7a3SLiteSpeed Tech    int s;
485392f7a3SLiteSpeed Tech
49fbc6cc04SDmitri Tikhonov  continue_reading:
505392f7a3SLiteSpeed Tech    while (p < end)
515392f7a3SLiteSpeed Tech    {
525392f7a3SLiteSpeed Tech        switch (reader->hr_state)
535392f7a3SLiteSpeed Tech        {
545392f7a3SLiteSpeed Tech        case HR_READ_FRAME_BEGIN:
555392f7a3SLiteSpeed Tech            reader->hr_u.vint2_state.vr2s_state = 0;
565392f7a3SLiteSpeed Tech            reader->hr_state = HR_READ_FRAME_CONTINUE;
575392f7a3SLiteSpeed Tech            /* fall-through */
585392f7a3SLiteSpeed Tech        case HR_READ_FRAME_CONTINUE:
595392f7a3SLiteSpeed Tech            s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
605392f7a3SLiteSpeed Tech            if (s < 0)
615392f7a3SLiteSpeed Tech                break;
625392f7a3SLiteSpeed Tech            reader->hr_frame_type = reader->hr_u.vint2_state.vr2s_one;
635392f7a3SLiteSpeed Tech            reader->hr_frame_length = reader->hr_u.vint2_state.vr2s_two;
6455d69529SGeorge Wang
6555d69529SGeorge Wang            if (!(reader->hr_flag & HR_FLAG_RCVD_SETTING)
6655d69529SGeorge Wang                && reader->hr_frame_type != HQFT_SETTINGS)
6755d69529SGeorge Wang            {
6855d69529SGeorge Wang                reader->hr_cb->on_frame_error(reader->hr_ctx,
6955d69529SGeorge Wang                            HEC_MISSING_SETTINGS, reader->hr_frame_type);
7055d69529SGeorge Wang                return -1;
7155d69529SGeorge Wang            }
7255d69529SGeorge Wang
735392f7a3SLiteSpeed Tech            switch (reader->hr_frame_type)
745392f7a3SLiteSpeed Tech            {
755392f7a3SLiteSpeed Tech            case HQFT_SETTINGS:
7655d69529SGeorge Wang                reader->hr_flag |= HR_FLAG_RCVD_SETTING;
775392f7a3SLiteSpeed Tech                if (reader->hr_frame_length)
785392f7a3SLiteSpeed Tech                {
795392f7a3SLiteSpeed Tech                    reader->hr_state = HR_READ_SETTING_BEGIN;
805392f7a3SLiteSpeed Tech                    reader->hr_nread = 0;
815392f7a3SLiteSpeed Tech                }
825392f7a3SLiteSpeed Tech                else
835392f7a3SLiteSpeed Tech                {
845392f7a3SLiteSpeed Tech                    reader->hr_cb->on_settings_frame(reader->hr_ctx);
855392f7a3SLiteSpeed Tech                    reader->hr_state = HR_READ_FRAME_BEGIN;
865392f7a3SLiteSpeed Tech                }
875392f7a3SLiteSpeed Tech                break;
885392f7a3SLiteSpeed Tech            case HQFT_GOAWAY:
895392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_VARINT;
905392f7a3SLiteSpeed Tech                break;
915392f7a3SLiteSpeed Tech            case HQFT_CANCEL_PUSH:
925392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_VARINT;
935392f7a3SLiteSpeed Tech                break;
945392f7a3SLiteSpeed Tech            case HQFT_MAX_PUSH_ID:
955392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_VARINT;
965392f7a3SLiteSpeed Tech                break;
97fbc6cc04SDmitri Tikhonov            case HQFT_PRIORITY_UPDATE_PUSH:
98fbc6cc04SDmitri Tikhonov            case HQFT_PRIORITY_UPDATE_STREAM:
99fbc6cc04SDmitri Tikhonov                reader->hr_state = HR_READ_VARINT;
100fbc6cc04SDmitri Tikhonov                break;
1015392f7a3SLiteSpeed Tech            case HQFT_DATA:
1025392f7a3SLiteSpeed Tech            case HQFT_HEADERS:
1035392f7a3SLiteSpeed Tech            case HQFT_PUSH_PROMISE:
10455d69529SGeorge Wang                reader->hr_cb->on_frame_error(reader->hr_ctx,
10555d69529SGeorge Wang                    HEC_FRAME_UNEXPECTED, reader->hr_frame_type);
1065392f7a3SLiteSpeed Tech                return -1;
1075392f7a3SLiteSpeed Tech            default:
108758aff32SDmitri Tikhonov            {
109758aff32SDmitri Tikhonov            /* From [draft-ietf-quic-http-31] Section 7.2.8:
110758aff32SDmitri Tikhonov             " Frame types of the format "0x1f * N + 0x21" for non-negative
111758aff32SDmitri Tikhonov             " integer values of N are reserved to exercise the requirement
112758aff32SDmitri Tikhonov             " that unknown types be ignored
113758aff32SDmitri Tikhonov             */
114758aff32SDmitri Tikhonov                enum lsq_log_level L;
115758aff32SDmitri Tikhonov                if (!(reader->hr_frame_type >= 0x21 &&
116758aff32SDmitri Tikhonov                        (reader->hr_frame_type - 0x21) % 0x1F == 0))
117758aff32SDmitri Tikhonov                    /* Non-grease: log with higher level: */
118758aff32SDmitri Tikhonov                    L = LSQ_LOG_INFO;
119758aff32SDmitri Tikhonov                else
120758aff32SDmitri Tikhonov                    L = LSQ_LOG_DEBUG;
121758aff32SDmitri Tikhonov                LSQ_LOG(L, "unknown frame type 0x%"PRIX64": will skip "
122758aff32SDmitri Tikhonov                    "%"PRIu64" bytes", reader->hr_frame_type,
123758aff32SDmitri Tikhonov                    reader->hr_frame_length);
1245392f7a3SLiteSpeed Tech                reader->hr_state = HR_SKIPPING;
1255392f7a3SLiteSpeed Tech                break;
1265392f7a3SLiteSpeed Tech            }
127758aff32SDmitri Tikhonov            }
1285392f7a3SLiteSpeed Tech            break;
1295392f7a3SLiteSpeed Tech        case HR_READ_VARINT:
1305392f7a3SLiteSpeed Tech            reader->hr_u.vint_state.pos = 0;
1315392f7a3SLiteSpeed Tech            reader->hr_state = HR_READ_VARINT_CONTINUE;
1325392f7a3SLiteSpeed Tech            reader->hr_nread = 0;
1335392f7a3SLiteSpeed Tech            /* fall-through */
1345392f7a3SLiteSpeed Tech        case HR_READ_VARINT_CONTINUE:
1355392f7a3SLiteSpeed Tech            orig_p = p;
1365392f7a3SLiteSpeed Tech            s = lsquic_varint_read_nb(&p, end, &reader->hr_u.vint_state);
1375392f7a3SLiteSpeed Tech            reader->hr_nread += p - orig_p;
1385392f7a3SLiteSpeed Tech            if (0 == s)
1395392f7a3SLiteSpeed Tech            {
140fbc6cc04SDmitri Tikhonov                switch (reader->hr_frame_type)
1415392f7a3SLiteSpeed Tech                {
142fbc6cc04SDmitri Tikhonov                case HQFT_GOAWAY:
143fbc6cc04SDmitri Tikhonov                case HQFT_CANCEL_PUSH:
144fbc6cc04SDmitri Tikhonov                case HQFT_MAX_PUSH_ID:
145fbc6cc04SDmitri Tikhonov                    if (reader->hr_nread != reader->hr_frame_length)
146fbc6cc04SDmitri Tikhonov                    {
147fbc6cc04SDmitri Tikhonov                        reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
148fbc6cc04SDmitri Tikhonov                            HEC_FRAME_ERROR,
149fbc6cc04SDmitri Tikhonov                            "Frame length does not match actual payload length");
150fbc6cc04SDmitri Tikhonov                        reader->hr_state = HR_ERROR;
151fbc6cc04SDmitri Tikhonov                        return -1;
152fbc6cc04SDmitri Tikhonov                    }
153fbc6cc04SDmitri Tikhonov                    break;
1545392f7a3SLiteSpeed Tech                }
1555392f7a3SLiteSpeed Tech                switch (reader->hr_frame_type)
1565392f7a3SLiteSpeed Tech                {
1575392f7a3SLiteSpeed Tech                case HQFT_GOAWAY:
1585392f7a3SLiteSpeed Tech                    reader->hr_cb->on_goaway(reader->hr_ctx,
1595392f7a3SLiteSpeed Tech                                                reader->hr_u.vint_state.val);
1605392f7a3SLiteSpeed Tech                    break;
1615392f7a3SLiteSpeed Tech                case HQFT_CANCEL_PUSH:
1625392f7a3SLiteSpeed Tech                    reader->hr_cb->on_cancel_push(reader->hr_ctx,
1635392f7a3SLiteSpeed Tech                                                reader->hr_u.vint_state.val);
1645392f7a3SLiteSpeed Tech                    break;
1655392f7a3SLiteSpeed Tech                case HQFT_MAX_PUSH_ID:
1665392f7a3SLiteSpeed Tech                    reader->hr_cb->on_max_push_id(reader->hr_ctx,
1675392f7a3SLiteSpeed Tech                                                reader->hr_u.vint_state.val);
1685392f7a3SLiteSpeed Tech                    break;
169fbc6cc04SDmitri Tikhonov                case HQFT_PRIORITY_UPDATE_PUSH:
170fbc6cc04SDmitri Tikhonov                case HQFT_PRIORITY_UPDATE_STREAM:
171fbc6cc04SDmitri Tikhonov                    len = reader->hr_frame_length - reader->hr_nread;
172fbc6cc04SDmitri Tikhonov                    if (len <= (uintptr_t) (end - p))
173fbc6cc04SDmitri Tikhonov                    {
174fbc6cc04SDmitri Tikhonov                        reader->hr_cb->on_priority_update(reader->hr_ctx,
175fbc6cc04SDmitri Tikhonov                            reader->hr_frame_type, reader->hr_u.vint_state.val,
176fbc6cc04SDmitri Tikhonov                            (char *) p, len);
177fbc6cc04SDmitri Tikhonov                        p += len;
178fbc6cc04SDmitri Tikhonov                    }
179fbc6cc04SDmitri Tikhonov                    else if (len <= sizeof(reader->hr_u.prio_state.buf))
180fbc6cc04SDmitri Tikhonov                    {
181fbc6cc04SDmitri Tikhonov                        reader->hr_frame_length = len;
182fbc6cc04SDmitri Tikhonov                        reader->hr_nread = 0;
183fbc6cc04SDmitri Tikhonov                        reader->hr_state = HR_READ_PRIORITY_UPDATE;
184fbc6cc04SDmitri Tikhonov                        goto continue_reading;
185fbc6cc04SDmitri Tikhonov                    }
186fbc6cc04SDmitri Tikhonov                    else
187fbc6cc04SDmitri Tikhonov                    {
188fbc6cc04SDmitri Tikhonov                        p += len;
189fbc6cc04SDmitri Tikhonov                        /* 16 bytes is more than enough for a PRIORITY_UPDATE
190fbc6cc04SDmitri Tikhonov                         * frame, anything larger than that is unreasonable.
191fbc6cc04SDmitri Tikhonov                         */
192fbc6cc04SDmitri Tikhonov                        if (reader->hr_frame_length
193fbc6cc04SDmitri Tikhonov                                        > sizeof(reader->hr_u.prio_state.buf))
194fbc6cc04SDmitri Tikhonov                            LSQ_INFO("skip PRIORITY_UPDATE frame that's too "
195fbc6cc04SDmitri Tikhonov                                    "long (%"PRIu64" bytes)", len);
196fbc6cc04SDmitri Tikhonov                    }
197fbc6cc04SDmitri Tikhonov                    break;
1985392f7a3SLiteSpeed Tech                default:
1995392f7a3SLiteSpeed Tech                    assert(0);
2005392f7a3SLiteSpeed Tech                }
2015392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_FRAME_BEGIN;
2025392f7a3SLiteSpeed Tech                break;
2035392f7a3SLiteSpeed Tech            }
2045392f7a3SLiteSpeed Tech            else
2055392f7a3SLiteSpeed Tech            {
2065392f7a3SLiteSpeed Tech                assert(p == end);
2075392f7a3SLiteSpeed Tech                return 0;
2085392f7a3SLiteSpeed Tech            }
2095392f7a3SLiteSpeed Tech        case HR_SKIPPING:
2105392f7a3SLiteSpeed Tech            len = MIN((uintptr_t) (end - p), reader->hr_frame_length);
2115392f7a3SLiteSpeed Tech            p += len;
2125392f7a3SLiteSpeed Tech            reader->hr_frame_length -= len;
2135392f7a3SLiteSpeed Tech            if (0 == reader->hr_frame_length)
2145392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_FRAME_BEGIN;
2155392f7a3SLiteSpeed Tech            break;
2165392f7a3SLiteSpeed Tech        case HR_READ_SETTING_BEGIN:
2175392f7a3SLiteSpeed Tech            reader->hr_u.vint2_state.vr2s_state = 0;
2185392f7a3SLiteSpeed Tech            reader->hr_state = HR_READ_SETTING_CONTINUE;
2195392f7a3SLiteSpeed Tech            /* fall-through */
2205392f7a3SLiteSpeed Tech        case HR_READ_SETTING_CONTINUE:
2215392f7a3SLiteSpeed Tech            orig_p = p;
2225392f7a3SLiteSpeed Tech            s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
2235392f7a3SLiteSpeed Tech            reader->hr_nread += p - orig_p;
2245392f7a3SLiteSpeed Tech            if (reader->hr_nread > reader->hr_frame_length)
2255392f7a3SLiteSpeed Tech            {
2265392f7a3SLiteSpeed Tech                reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
22792f6e17bSDmitri Tikhonov                    HEC_FRAME_ERROR, "SETTING frame contents too long");
2285392f7a3SLiteSpeed Tech                reader->hr_state = HR_ERROR;
2295392f7a3SLiteSpeed Tech                return -1;
2305392f7a3SLiteSpeed Tech            }
2315392f7a3SLiteSpeed Tech            if (s < 0)
2325392f7a3SLiteSpeed Tech                break;
2335392f7a3SLiteSpeed Tech            reader->hr_cb->on_setting(reader->hr_ctx,
2345392f7a3SLiteSpeed Tech                    reader->hr_u.vint2_state.vr2s_one,
2355392f7a3SLiteSpeed Tech                    reader->hr_u.vint2_state.vr2s_two);
2365392f7a3SLiteSpeed Tech            if (reader->hr_nread >= reader->hr_frame_length)
2375392f7a3SLiteSpeed Tech            {
2385392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_FRAME_BEGIN;
2395392f7a3SLiteSpeed Tech                reader->hr_cb->on_settings_frame(reader->hr_ctx);
2405392f7a3SLiteSpeed Tech            }
2415392f7a3SLiteSpeed Tech            else
2425392f7a3SLiteSpeed Tech                reader->hr_state = HR_READ_SETTING_BEGIN;
2435392f7a3SLiteSpeed Tech            break;
244fbc6cc04SDmitri Tikhonov        case HR_READ_PRIORITY_UPDATE:
245fbc6cc04SDmitri Tikhonov            len = MIN((uintptr_t) (end - p),
246fbc6cc04SDmitri Tikhonov                            reader->hr_frame_length - reader->hr_nread);
247fbc6cc04SDmitri Tikhonov            memcpy(reader->hr_u.prio_state.buf + reader->hr_nread, p, len);
248fbc6cc04SDmitri Tikhonov            reader->hr_nread += len;
249fbc6cc04SDmitri Tikhonov            p += len;
250fbc6cc04SDmitri Tikhonov            if (reader->hr_frame_length == reader->hr_nread)
251fbc6cc04SDmitri Tikhonov            {
252fbc6cc04SDmitri Tikhonov                reader->hr_cb->on_priority_update(reader->hr_ctx,
253fbc6cc04SDmitri Tikhonov                        reader->hr_frame_type, reader->hr_u.vint_state.val,
254fbc6cc04SDmitri Tikhonov                        reader->hr_u.prio_state.buf, reader->hr_frame_length);
255fbc6cc04SDmitri Tikhonov                reader->hr_state = HR_READ_FRAME_BEGIN;
256fbc6cc04SDmitri Tikhonov            }
257fbc6cc04SDmitri Tikhonov            break;
2585392f7a3SLiteSpeed Tech        default:
2595392f7a3SLiteSpeed Tech            assert(0);
2605392f7a3SLiteSpeed Tech            /* fall-through */
2615392f7a3SLiteSpeed Tech        case HR_ERROR:
2625392f7a3SLiteSpeed Tech            return -1;
2635392f7a3SLiteSpeed Tech        }
2645392f7a3SLiteSpeed Tech    }
2655392f7a3SLiteSpeed Tech
2665392f7a3SLiteSpeed Tech    return 0;
2675392f7a3SLiteSpeed Tech}
268