// Copyright (C) 2019, Cloudflare, Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::ffi; use std::ptr; use std::slice; use std::str; use libc::c_char; use libc::c_int; use libc::c_void; use libc::size_t; use libc::ssize_t; use crate::*; use crate::h3::NameValue; #[no_mangle] pub extern fn quiche_h3_config_new() -> *mut h3::Config { match h3::Config::new() { Ok(c) => Box::into_raw(Box::new(c)), Err(_) => ptr::null_mut(), } } #[no_mangle] pub extern fn quiche_h3_config_set_max_header_list_size( config: &mut h3::Config, v: u64, ) { config.set_max_header_list_size(v); } #[no_mangle] pub extern fn quiche_h3_config_set_qpack_max_table_capacity( config: &mut h3::Config, v: u64, ) { config.set_qpack_max_table_capacity(v); } #[no_mangle] pub extern fn quiche_h3_config_set_qpack_blocked_streams( config: &mut h3::Config, v: u64, ) { config.set_qpack_blocked_streams(v); } #[no_mangle] pub extern fn quiche_h3_config_free(config: *mut h3::Config) { unsafe { Box::from_raw(config) }; } #[no_mangle] pub extern fn quiche_h3_conn_new_with_transport( quic_conn: &mut Connection, config: &mut h3::Config, ) -> *mut h3::Connection { match h3::Connection::with_transport(quic_conn, config) { Ok(c) => Box::into_raw(Box::new(c)), Err(_) => ptr::null_mut(), } } #[no_mangle] pub extern fn quiche_h3_conn_poll( conn: &mut h3::Connection, quic_conn: &mut Connection, ev: *mut *const h3::Event, ) -> i64 { match conn.poll(quic_conn) { Ok((id, v)) => { unsafe { *ev = Box::into_raw(Box::new(v)); } id as i64 }, Err(e) => e.to_c() as i64, } } #[no_mangle] pub extern fn quiche_h3_event_type(ev: &h3::Event) -> u32 { match ev { h3::Event::Headers { .. } => 0, h3::Event::Data { .. } => 1, h3::Event::Finished { .. } => 2, h3::Event::Datagram { .. } => 3, h3::Event::GoAway { .. } => 4, } } #[no_mangle] pub extern fn quiche_h3_event_for_each_header( ev: &h3::Event, cb: extern fn( name: *const u8, name_len: size_t, value: *const u8, value_len: size_t, argp: *mut c_void, ) -> c_int, argp: *mut c_void, ) -> c_int { match ev { h3::Event::Headers { list, .. } => for h in list { let rc = cb( h.name().as_ptr(), h.name().len(), h.value().as_ptr(), h.value().len(), argp, ); if rc != 0 { return rc; } }, _ => unreachable!(), } 0 } #[no_mangle] pub extern fn quiche_h3_event_headers_has_body(ev: &h3::Event) -> bool { match ev { h3::Event::Headers { has_body, .. } => *has_body, _ => unreachable!(), } } #[no_mangle] pub extern fn quiche_h3_event_free(ev: *mut h3::Event) { unsafe { Box::from_raw(ev) }; } #[repr(C)] pub struct Header { name: *mut u8, name_len: usize, value: *mut u8, value_len: usize, } #[no_mangle] pub extern fn quiche_h3_send_request( conn: &mut h3::Connection, quic_conn: &mut Connection, headers: *const Header, headers_len: size_t, fin: bool, ) -> i64 { let req_headers = headers_from_ptr(headers, headers_len); match conn.send_request(quic_conn, &req_headers, fin) { Ok(v) => v as i64, Err(e) => e.to_c() as i64, } } #[no_mangle] pub extern fn quiche_h3_send_response( conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64, headers: *const Header, headers_len: size_t, fin: bool, ) -> c_int { let resp_headers = headers_from_ptr(headers, headers_len); match conn.send_response(quic_conn, stream_id, &resp_headers, fin) { Ok(_) => 0, Err(e) => e.to_c() as c_int, } } #[no_mangle] pub extern fn quiche_h3_send_response_with_priority( conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64, headers: *const Header, headers_len: size_t, priority: *const c_char, fin: bool, ) -> c_int { let resp_headers = headers_from_ptr(headers, headers_len); let priority = unsafe { ffi::CStr::from_ptr(priority).to_str().unwrap() }; match conn.send_response_with_priority( quic_conn, stream_id, &resp_headers, &priority, fin, ) { Ok(_) => 0, Err(e) => e.to_c() as c_int, } } #[no_mangle] pub extern fn quiche_h3_send_body( conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64, body: *const u8, body_len: size_t, fin: bool, ) -> ssize_t { if body_len > ::max_value() as usize { panic!("The provided buffer is too large"); } let body = unsafe { slice::from_raw_parts(body, body_len) }; match conn.send_body(quic_conn, stream_id, body, fin) { Ok(v) => v as ssize_t, Err(e) => e.to_c(), } } #[no_mangle] pub extern fn quiche_h3_recv_body( conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64, out: *mut u8, out_len: size_t, ) -> ssize_t { if out_len > ::max_value() as usize { panic!("The provided buffer is too large"); } let out = unsafe { slice::from_raw_parts_mut(out, out_len) }; match conn.recv_body(quic_conn, stream_id, out) { Ok(v) => v as ssize_t, Err(e) => e.to_c(), } } #[no_mangle] pub extern fn quiche_h3_send_dgram( conn: &mut h3::Connection, quic_conn: &mut Connection, flow_id: u64, data: *const u8, data_len: size_t, ) -> c_int { if data_len > ::max_value() as usize { panic!("The provided buffer is too large"); } let data = unsafe { slice::from_raw_parts(data, data_len) }; match conn.send_dgram(quic_conn, flow_id, data) { Ok(_) => 0, Err(e) => e.to_c() as c_int, } } #[no_mangle] pub extern fn quiche_h3_recv_dgram( conn: &mut h3::Connection, quic_conn: &mut Connection, flow_id: *mut u64, flow_id_len: *mut usize, out: *mut u8, out_len: size_t, ) -> ssize_t { if out_len > ::max_value() as usize { panic!("The provided buffer is too large"); } let out = unsafe { slice::from_raw_parts_mut(out, out_len) }; match conn.recv_dgram(quic_conn, out) { Ok((len, id, id_len)) => { unsafe { *flow_id = id }; unsafe { *flow_id_len = id_len }; len as ssize_t }, Err(e) => e.to_c(), } } #[no_mangle] pub extern fn quiche_h3_conn_free(conn: *mut h3::Connection) { unsafe { Box::from_raw(conn) }; } fn headers_from_ptr<'a>( ptr: *const Header, len: size_t, ) -> Vec> { let headers = unsafe { slice::from_raw_parts(ptr, len) }; let mut out = Vec::new(); for h in headers { out.push({ let name = unsafe { let slice = slice::from_raw_parts(h.name, h.name_len); str::from_utf8_unchecked(slice) }; let value = unsafe { let slice = slice::from_raw_parts(h.value, h.value_len); str::from_utf8_unchecked(slice) }; h3::HeaderRef::new(name, value) }); } out }