Skip to main content

quiche/h3/
ffi.rs

1// Copyright (C) 2019, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#[cfg(feature = "sfv")]
28use std::convert::TryFrom;
29
30use std::ptr;
31use std::slice;
32
33use libc::c_int;
34use libc::c_void;
35use libc::size_t;
36use libc::ssize_t;
37
38use crate::*;
39
40use crate::h3::NameValue;
41use crate::h3::Priority;
42
43#[no_mangle]
44pub extern "C" fn quiche_h3_config_new() -> *mut h3::Config {
45    match h3::Config::new() {
46        Ok(c) => Box::into_raw(Box::new(c)),
47
48        Err(_) => ptr::null_mut(),
49    }
50}
51
52#[no_mangle]
53pub extern "C" fn quiche_h3_config_set_max_field_section_size(
54    config: &mut h3::Config, v: u64,
55) {
56    config.set_max_field_section_size(v);
57}
58
59#[no_mangle]
60pub extern "C" fn quiche_h3_config_set_qpack_max_table_capacity(
61    config: &mut h3::Config, v: u64,
62) {
63    config.set_qpack_max_table_capacity(v);
64}
65
66#[no_mangle]
67pub extern "C" fn quiche_h3_config_set_qpack_blocked_streams(
68    config: &mut h3::Config, v: u64,
69) {
70    config.set_qpack_blocked_streams(v);
71}
72
73#[no_mangle]
74pub extern "C" fn quiche_h3_config_enable_extended_connect(
75    config: &mut h3::Config, enabled: bool,
76) {
77    config.enable_extended_connect(enabled);
78}
79
80#[no_mangle]
81pub extern "C" fn quiche_h3_config_set_max_priority_update_size(
82    config: &mut h3::Config, v: u64,
83) {
84    config.set_max_priority_update_size(v);
85}
86
87#[no_mangle]
88pub extern "C" fn quiche_h3_config_free(config: *mut h3::Config) {
89    if !config.is_null() {
90        drop(unsafe { Box::from_raw(config) });
91    }
92}
93
94#[no_mangle]
95pub extern "C" fn quiche_h3_conn_new_with_transport(
96    quic_conn: &mut Connection, config: &mut h3::Config,
97) -> *mut h3::Connection {
98    match h3::Connection::with_transport(quic_conn, config) {
99        Ok(c) => Box::into_raw(Box::new(c)),
100
101        Err(_) => ptr::null_mut(),
102    }
103}
104
105#[no_mangle]
106pub extern "C" fn quiche_h3_for_each_setting(
107    conn: &h3::Connection,
108    cb: extern "C" fn(identifier: u64, value: u64, argp: *mut c_void) -> c_int,
109    argp: *mut c_void,
110) -> c_int {
111    match conn.peer_settings_raw() {
112        Some(raw) => {
113            for setting in raw {
114                let rc = cb(setting.0, setting.1, argp);
115
116                if rc != 0 {
117                    return rc;
118                }
119            }
120
121            0
122        },
123
124        None => -1,
125    }
126}
127
128#[no_mangle]
129pub extern "C" fn quiche_h3_conn_poll(
130    conn: &mut h3::Connection, quic_conn: &mut Connection,
131    ev: *mut *const h3::Event,
132) -> i64 {
133    match conn.poll(quic_conn) {
134        Ok((id, v)) => {
135            unsafe {
136                *ev = Box::into_raw(Box::new(v));
137            }
138
139            id as i64
140        },
141
142        Err(e) => e.to_c() as i64,
143    }
144}
145
146#[no_mangle]
147pub extern "C" fn quiche_h3_event_type(ev: &h3::Event) -> u32 {
148    match ev {
149        h3::Event::Headers { .. } => 0,
150
151        h3::Event::Data => 1,
152
153        h3::Event::Finished => 2,
154
155        h3::Event::GoAway => 3,
156
157        h3::Event::Reset { .. } => 4,
158
159        h3::Event::PriorityUpdate => 5,
160    }
161}
162
163#[no_mangle]
164pub extern "C" fn quiche_h3_event_for_each_header(
165    ev: &h3::Event,
166    cb: extern "C" fn(
167        name: *const u8,
168        name_len: size_t,
169
170        value: *const u8,
171        value_len: size_t,
172
173        argp: *mut c_void,
174    ) -> c_int,
175    argp: *mut c_void,
176) -> c_int {
177    match ev {
178        h3::Event::Headers { list, .. } =>
179            for h in list {
180                let rc = cb(
181                    h.name().as_ptr(),
182                    h.name().len(),
183                    h.value().as_ptr(),
184                    h.value().len(),
185                    argp,
186                );
187
188                if rc != 0 {
189                    return rc;
190                }
191            },
192
193        _ => unreachable!(),
194    }
195
196    0
197}
198
199#[no_mangle]
200pub extern "C" fn quiche_h3_event_headers_has_more_frames(
201    ev: &h3::Event,
202) -> bool {
203    match ev {
204        h3::Event::Headers { more_frames, .. } => *more_frames,
205
206        _ => unreachable!(),
207    }
208}
209
210#[no_mangle]
211pub extern "C" fn quiche_h3_extended_connect_enabled_by_peer(
212    conn: &h3::Connection,
213) -> bool {
214    conn.extended_connect_enabled_by_peer()
215}
216
217#[no_mangle]
218pub extern "C" fn quiche_h3_event_free(ev: *mut h3::Event) {
219    if !ev.is_null() {
220        drop(unsafe { Box::from_raw(ev) });
221    }
222}
223
224#[repr(C)]
225pub struct Header {
226    name: *mut u8,
227    name_len: usize,
228
229    value: *mut u8,
230    value_len: usize,
231}
232
233#[no_mangle]
234pub extern "C" fn quiche_h3_send_request(
235    conn: &mut h3::Connection, quic_conn: &mut Connection,
236    headers: *const Header, headers_len: size_t, fin: bool,
237) -> i64 {
238    let req_headers = headers_from_ptr(headers, headers_len);
239
240    match conn.send_request(quic_conn, &req_headers, fin) {
241        Ok(v) => v as i64,
242
243        Err(e) => e.to_c() as i64,
244    }
245}
246
247#[no_mangle]
248pub extern "C" fn quiche_h3_send_response(
249    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
250    headers: *const Header, headers_len: size_t, fin: bool,
251) -> c_int {
252    let resp_headers = headers_from_ptr(headers, headers_len);
253
254    match conn.send_response(quic_conn, stream_id, &resp_headers, fin) {
255        Ok(_) => 0,
256
257        Err(e) => e.to_c() as c_int,
258    }
259}
260
261#[no_mangle]
262pub extern "C" fn quiche_h3_send_response_with_priority(
263    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
264    headers: *const Header, headers_len: size_t, priority: &Priority, fin: bool,
265) -> c_int {
266    let resp_headers = headers_from_ptr(headers, headers_len);
267
268    match conn.send_response_with_priority(
269        quic_conn,
270        stream_id,
271        &resp_headers,
272        priority,
273        fin,
274    ) {
275        Ok(_) => 0,
276
277        Err(e) => e.to_c() as c_int,
278    }
279}
280
281#[no_mangle]
282pub extern "C" fn quiche_h3_send_additional_headers(
283    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
284    headers: *const Header, headers_len: size_t, is_trailer_section: bool,
285    fin: bool,
286) -> c_int {
287    let headers = headers_from_ptr(headers, headers_len);
288
289    match conn.send_additional_headers(
290        quic_conn,
291        stream_id,
292        &headers,
293        is_trailer_section,
294        fin,
295    ) {
296        Ok(_) => 0,
297
298        Err(e) => e.to_c() as c_int,
299    }
300}
301
302#[no_mangle]
303pub extern "C" fn quiche_h3_send_body(
304    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
305    body: *const u8, body_len: size_t, fin: bool,
306) -> ssize_t {
307    if body_len > <ssize_t>::MAX as usize {
308        panic!("The provided buffer is too large");
309    }
310
311    let body = unsafe { slice::from_raw_parts(body, body_len) };
312
313    match conn.send_body(quic_conn, stream_id, body, fin) {
314        Ok(v) => v as ssize_t,
315
316        Err(e) => e.to_c(),
317    }
318}
319
320#[no_mangle]
321pub extern "C" fn quiche_h3_recv_body(
322    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
323    out: *mut u8, out_len: size_t,
324) -> ssize_t {
325    if out_len > <ssize_t>::MAX as usize {
326        panic!("The provided buffer is too large");
327    }
328
329    let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
330
331    match conn.recv_body(quic_conn, stream_id, out) {
332        Ok(v) => v as ssize_t,
333
334        Err(e) => e.to_c(),
335    }
336}
337
338#[no_mangle]
339pub extern "C" fn quiche_h3_send_goaway(
340    conn: &mut h3::Connection, quic_conn: &mut Connection, id: u64,
341) -> c_int {
342    match conn.send_goaway(quic_conn, id) {
343        Ok(()) => 0,
344
345        Err(e) => e.to_c() as c_int,
346    }
347}
348
349#[no_mangle]
350#[cfg(feature = "sfv")]
351pub extern "C" fn quiche_h3_parse_extensible_priority(
352    priority: *const u8, priority_len: size_t, parsed: &mut Priority,
353) -> c_int {
354    let priority = unsafe { slice::from_raw_parts(priority, priority_len) };
355
356    match Priority::try_from(priority) {
357        Ok(v) => {
358            parsed.urgency = v.urgency;
359            parsed.incremental = v.incremental;
360            0
361        },
362
363        Err(e) => e.to_c() as c_int,
364    }
365}
366
367#[no_mangle]
368pub extern "C" fn quiche_h3_send_priority_update_for_request(
369    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
370    priority: &Priority,
371) -> c_int {
372    match conn.send_priority_update_for_request(quic_conn, stream_id, priority) {
373        Ok(()) => 0,
374
375        Err(e) => e.to_c() as c_int,
376    }
377}
378
379#[no_mangle]
380pub extern "C" fn quiche_h3_take_last_priority_update(
381    conn: &mut h3::Connection, prioritized_element_id: u64,
382    cb: extern "C" fn(
383        priority_field_value: *const u8,
384        priority_field_value_len: size_t,
385        argp: *mut c_void,
386    ) -> c_int,
387    argp: *mut c_void,
388) -> c_int {
389    match conn.take_last_priority_update(prioritized_element_id) {
390        Ok(priority) => {
391            let rc = cb(priority.as_ptr(), priority.len(), argp);
392
393            if rc != 0 {
394                return rc;
395            }
396
397            0
398        },
399
400        Err(e) => e.to_c() as c_int,
401    }
402}
403
404#[no_mangle]
405pub extern "C" fn quiche_h3_dgram_enabled_by_peer(
406    conn: &h3::Connection, quic_conn: &Connection,
407) -> bool {
408    conn.dgram_enabled_by_peer(quic_conn)
409}
410
411#[no_mangle]
412pub extern "C" fn quiche_h3_conn_free(conn: *mut h3::Connection) {
413    if !conn.is_null() {
414        drop(unsafe { Box::from_raw(conn) });
415    }
416}
417
418fn headers_from_ptr<'a>(
419    ptr: *const Header, len: size_t,
420) -> Vec<h3::HeaderRef<'a>> {
421    let headers = unsafe { slice::from_raw_parts(ptr, len) };
422
423    let mut out = Vec::new();
424
425    for h in headers {
426        out.push({
427            let name = unsafe { slice::from_raw_parts(h.name, h.name_len) };
428            let value = unsafe { slice::from_raw_parts(h.value, h.value_len) };
429
430            h3::HeaderRef::new(name, value)
431        });
432    }
433
434    out
435}
436
437#[repr(C)]
438pub struct Stats {
439    qpack_encoder_stream_recv_bytes: u64,
440    qpack_decoder_stream_recv_bytes: u64,
441}
442
443#[no_mangle]
444pub extern "C" fn quiche_h3_conn_stats(conn: &h3::Connection, out: &mut Stats) {
445    let stats = conn.stats();
446
447    out.qpack_encoder_stream_recv_bytes = stats.qpack_encoder_stream_recv_bytes;
448    out.qpack_decoder_stream_recv_bytes = stats.qpack_decoder_stream_recv_bytes;
449}