Coverage Report

Created: 2026-08-31 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/ssl/quic/quic_demux.c
Line
Count
Source
1
/*
2
 * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include "internal/quic_demux.h"
11
#include "internal/quic_wire_pkt.h"
12
#include "internal/dgram_demux.h"
13
#include "internal/common.h"
14
#include <openssl/lhash.h>
15
#include <openssl/err.h>
16
17
/*
18
 * QUIC Demuxer Implementation
19
 * ===========================
20
 *
21
 * The QUIC demuxer wraps the generic DGRAM_DEMUX to add QUIC-specific
22
 * functionality:
23
 *   - Extraction of DCID from the first packet in each datagram
24
 *   - QUIC-specific callback signature that includes the DCID
25
 *
26
 * QUIC_URXE is a typedef to DGRAM_URXE, so all URXE management is delegated
27
 * to the underlying DGRAM_DEMUX.
28
 */
29
struct quic_demux_st {
30
    /* The underlying generic datagram demuxer. */
31
    DGRAM_DEMUX *dgram_demux;
32
33
    /*
34
     * QUIC short packets do not contain the length of the connection ID field,
35
     * therefore it must be known contextually. The demuxer requires connection
36
     * IDs of the same length to be used for all incoming packets.
37
     */
38
    size_t short_conn_id_len;
39
40
    /* The QUIC-specific packet handler callback (includes DCID). */
41
    ossl_quic_demux_cb_fn *default_cb;
42
    void *default_cb_arg;
43
};
44
45
/*
46
 * Internal callback that wraps the QUIC callback. This is called by
47
 * DGRAM_DEMUX for each received datagram. We extract the DCID and forward
48
 * to the QUIC-specific callback.
49
 */
50
static void quic_demux_dgram_cb(DGRAM_URXE *e, void *arg)
51
0
{
52
0
    QUIC_DEMUX *demux = arg;
53
54
0
    if (demux->default_cb != NULL) {
55
0
        QUIC_CONN_ID dst_conn_id;
56
0
        int dst_conn_id_ok;
57
58
        /* Extract DCID from the first packet in the datagram. */
59
0
        dst_conn_id_ok = ossl_quic_wire_get_pkt_hdr_dst_conn_id(
60
0
            ossl_quic_urxe_data(e),
61
0
            e->data_len,
62
0
            demux->short_conn_id_len,
63
0
            &dst_conn_id);
64
65
0
        demux->default_cb(e, demux->default_cb_arg,
66
0
            dst_conn_id_ok ? &dst_conn_id : NULL);
67
0
    } else {
68
        /* No handler set, release the URXE back to the demuxer. */
69
0
        ossl_dgram_demux_release_urxe(demux->dgram_demux, e);
70
0
    }
71
0
}
72
73
QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
74
    size_t short_conn_id_len,
75
    OSSL_TIME (*now)(void *arg),
76
    void *now_arg)
77
0
{
78
0
    QUIC_DEMUX *demux;
79
80
0
    demux = OPENSSL_zalloc(sizeof(QUIC_DEMUX));
81
0
    if (demux == NULL)
82
0
        return NULL;
83
84
0
    demux->short_conn_id_len = short_conn_id_len;
85
86
    /* Create the underlying generic demuxer (no internal locking for QUIC). */
87
0
    demux->dgram_demux = ossl_dgram_demux_new(net_bio, 0, now, now_arg);
88
0
    if (demux->dgram_demux == NULL) {
89
0
        OPENSSL_free(demux);
90
0
        return NULL;
91
0
    }
92
93
    /*
94
     * Set our internal wrapper callback on the DGRAM_DEMUX. This will be
95
     * called for every received datagram, and we'll extract the DCID and
96
     * forward to the QUIC-specific callback.
97
     */
98
0
    ossl_dgram_demux_set_default_handler(demux->dgram_demux,
99
0
        quic_demux_dgram_cb, demux);
100
101
0
    return demux;
102
0
}
103
104
void ossl_quic_demux_free(QUIC_DEMUX *demux)
105
0
{
106
0
    if (demux == NULL)
107
0
        return;
108
109
0
    ossl_dgram_demux_free(demux->dgram_demux);
110
0
    OPENSSL_free(demux);
111
0
}
112
113
void ossl_quic_demux_set_bio(QUIC_DEMUX *demux, BIO *net_bio)
114
0
{
115
0
    ossl_dgram_demux_set_bio(demux->dgram_demux, net_bio);
116
0
}
117
118
int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu)
119
0
{
120
0
    return ossl_dgram_demux_set_mtu(demux->dgram_demux, mtu);
121
0
}
122
123
void ossl_quic_demux_set_default_handler(QUIC_DEMUX *demux,
124
    ossl_quic_demux_cb_fn *cb,
125
    void *cb_arg)
126
0
{
127
0
    demux->default_cb = cb;
128
0
    demux->default_cb_arg = cb_arg;
129
0
}
130
131
int ossl_quic_demux_pump(QUIC_DEMUX *demux)
132
0
{
133
0
    int ret;
134
135
0
    ret = ossl_dgram_demux_pump(demux->dgram_demux);
136
137
    /*
138
     * Map DGRAM_DEMUX_PUMP_RES_* to QUIC_DEMUX_PUMP_RES_*. The values are
139
     * identical, but this provides documentation and future-proofing.
140
     */
141
0
    switch (ret) {
142
0
    case DGRAM_DEMUX_PUMP_RES_OK:
143
0
        return QUIC_DEMUX_PUMP_RES_OK;
144
0
    case DGRAM_DEMUX_PUMP_RES_TRANSIENT_FAIL:
145
0
        return QUIC_DEMUX_PUMP_RES_TRANSIENT_FAIL;
146
0
    case DGRAM_DEMUX_PUMP_RES_PERMANENT_FAIL:
147
0
    default:
148
0
        return QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL;
149
0
    }
150
0
}
151
152
/* Artificially inject a packet into the demuxer for testing purposes. */
153
int ossl_quic_demux_inject(QUIC_DEMUX *demux,
154
    const unsigned char *buf,
155
    size_t buf_len,
156
    const BIO_ADDR *peer,
157
    const BIO_ADDR *local)
158
0
{
159
0
    return ossl_dgram_demux_inject(demux->dgram_demux, buf, buf_len,
160
0
        peer, local);
161
0
}
162
163
/* Called by our user to return a URXE to the free list. */
164
void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
165
0
{
166
0
    ossl_dgram_demux_release_urxe(demux->dgram_demux, e);
167
0
}
168
169
void ossl_quic_demux_reinject_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
170
0
{
171
0
    ossl_dgram_demux_reinject_urxe(demux->dgram_demux, e);
172
0
}
173
174
int ossl_quic_demux_has_pending(const QUIC_DEMUX *demux)
175
0
{
176
0
    return ossl_dgram_demux_has_pending(demux->dgram_demux);
177
0
}