1641 lines
42 KiB
Rust
1641 lines
42 KiB
Rust
// Copyright (C) 2018-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::sync::atomic;
|
|
|
|
use std::net::Ipv4Addr;
|
|
use std::net::Ipv6Addr;
|
|
use std::net::SocketAddr;
|
|
use std::net::SocketAddrV4;
|
|
use std::net::SocketAddrV6;
|
|
|
|
#[cfg(unix)]
|
|
use std::os::unix::io::FromRawFd;
|
|
|
|
use libc::c_char;
|
|
use libc::c_int;
|
|
use libc::c_void;
|
|
use libc::size_t;
|
|
use libc::sockaddr;
|
|
use libc::ssize_t;
|
|
use libc::timespec;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::AF_INET;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2def::AF_INET;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::AF_INET6;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2def::AF_INET6;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::in_addr;
|
|
#[cfg(windows)]
|
|
use winapi::shared::inaddr::IN_ADDR as in_addr;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::in6_addr;
|
|
#[cfg(windows)]
|
|
use winapi::shared::in6addr::IN6_ADDR as in6_addr;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::sa_family_t;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2def::ADDRESS_FAMILY as sa_family_t;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::sockaddr_in;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2def::SOCKADDR_IN as sockaddr_in;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::sockaddr_in6;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6;
|
|
|
|
#[cfg(not(windows))]
|
|
use libc::sockaddr_storage;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2def::SOCKADDR_STORAGE_LH as sockaddr_storage;
|
|
|
|
#[cfg(windows)]
|
|
use libc::c_int as socklen_t;
|
|
#[cfg(not(windows))]
|
|
use libc::socklen_t;
|
|
|
|
#[cfg(windows)]
|
|
use winapi::shared::in6addr::in6_addr_u;
|
|
#[cfg(windows)]
|
|
use winapi::shared::inaddr::in_addr_S_un;
|
|
#[cfg(windows)]
|
|
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
|
|
|
|
use crate::*;
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_version() -> *const u8 {
|
|
static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
|
|
VERSION.as_ptr()
|
|
}
|
|
|
|
struct Logger {
|
|
cb: extern fn(line: *const u8, argp: *mut c_void),
|
|
argp: std::sync::atomic::AtomicPtr<c_void>,
|
|
}
|
|
|
|
impl log::Log for Logger {
|
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
|
true
|
|
}
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
let line = format!("{}: {}\0", record.target(), record.args());
|
|
(self.cb)(line.as_ptr(), self.argp.load(atomic::Ordering::Relaxed));
|
|
}
|
|
|
|
fn flush(&self) {}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_enable_debug_logging(
|
|
cb: extern fn(line: *const u8, argp: *mut c_void), argp: *mut c_void,
|
|
) -> c_int {
|
|
let argp = atomic::AtomicPtr::new(argp);
|
|
let logger = Box::new(Logger { cb, argp });
|
|
|
|
if log::set_boxed_logger(logger).is_err() {
|
|
return -1;
|
|
}
|
|
|
|
log::set_max_level(log::LevelFilter::Trace);
|
|
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_new(version: u32) -> *mut Config {
|
|
match Config::new(version) {
|
|
Ok(c) => Box::into_raw(Box::new(c)),
|
|
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_load_cert_chain_from_pem_file(
|
|
config: &mut Config, path: *const c_char,
|
|
) -> c_int {
|
|
let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
match config.load_cert_chain_from_pem_file(path) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_load_priv_key_from_pem_file(
|
|
config: &mut Config, path: *const c_char,
|
|
) -> c_int {
|
|
let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
match config.load_priv_key_from_pem_file(path) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_load_verify_locations_from_file(
|
|
config: &mut Config, path: *const c_char,
|
|
) -> c_int {
|
|
let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
match config.load_verify_locations_from_file(path) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_load_verify_locations_from_directory(
|
|
config: &mut Config, path: *const c_char,
|
|
) -> c_int {
|
|
let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
match config.load_verify_locations_from_directory(path) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_verify_peer(config: &mut Config, v: bool) {
|
|
config.verify_peer(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_grease(config: &mut Config, v: bool) {
|
|
config.grease(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_log_keys(config: &mut Config) {
|
|
config.log_keys();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_enable_early_data(config: &mut Config) {
|
|
config.enable_early_data();
|
|
}
|
|
|
|
#[no_mangle]
|
|
/// Corresponds to the `Config::set_application_protos_wire_format` Rust
|
|
/// function.
|
|
pub extern fn quiche_config_set_application_protos(
|
|
config: &mut Config, protos: *const u8, protos_len: size_t,
|
|
) -> c_int {
|
|
let protos = unsafe { slice::from_raw_parts(protos, protos_len) };
|
|
|
|
match config.set_application_protos_wire_format(protos) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_idle_timeout(config: &mut Config, v: u64) {
|
|
config.set_max_idle_timeout(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_recv_udp_payload_size(
|
|
config: &mut Config, v: size_t,
|
|
) {
|
|
config.set_max_recv_udp_payload_size(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_data(config: &mut Config, v: u64) {
|
|
config.set_initial_max_data(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_stream_data_bidi_local(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_initial_max_stream_data_bidi_local(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_stream_data_bidi_remote(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_initial_max_stream_data_bidi_remote(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_stream_data_uni(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_initial_max_stream_data_uni(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_streams_bidi(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_initial_max_streams_bidi(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_max_streams_uni(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_initial_max_streams_uni(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_ack_delay_exponent(config: &mut Config, v: u64) {
|
|
config.set_ack_delay_exponent(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_ack_delay(config: &mut Config, v: u64) {
|
|
config.set_max_ack_delay(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_disable_active_migration(
|
|
config: &mut Config, v: bool,
|
|
) {
|
|
config.set_disable_active_migration(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_cc_algorithm_name(
|
|
config: &mut Config, name: *const c_char,
|
|
) -> c_int {
|
|
let name = unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() };
|
|
match config.set_cc_algorithm_name(name) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_cc_algorithm(
|
|
config: &mut Config, algo: CongestionControlAlgorithm,
|
|
) {
|
|
config.set_cc_algorithm(algo);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_initial_congestion_window_packets(
|
|
config: &mut Config, packets: size_t,
|
|
) {
|
|
config.set_initial_congestion_window_packets(packets);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_enable_hystart(config: &mut Config, v: bool) {
|
|
config.enable_hystart(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_enable_pacing(config: &mut Config, v: bool) {
|
|
config.enable_pacing(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_pacing_rate(config: &mut Config, v: u64) {
|
|
config.set_max_pacing_rate(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_enable_dgram(
|
|
config: &mut Config, enabled: bool, recv_queue_len: size_t,
|
|
send_queue_len: size_t,
|
|
) {
|
|
config.enable_dgram(enabled, recv_queue_len, send_queue_len);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_send_udp_payload_size(
|
|
config: &mut Config, v: size_t,
|
|
) {
|
|
config.set_max_send_udp_payload_size(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_connection_window(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_max_connection_window(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_max_stream_window(config: &mut Config, v: u64) {
|
|
config.set_max_stream_window(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_active_connection_id_limit(
|
|
config: &mut Config, v: u64,
|
|
) {
|
|
config.set_active_connection_id_limit(v);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_set_stateless_reset_token(
|
|
config: &mut Config, v: *const u8,
|
|
) {
|
|
let reset_token = unsafe { slice::from_raw_parts(v, 16) };
|
|
let reset_token = match reset_token.try_into() {
|
|
Ok(rt) => rt,
|
|
Err(_) => unreachable!(),
|
|
};
|
|
let reset_token = u128::from_be_bytes(reset_token);
|
|
config.set_stateless_reset_token(Some(reset_token));
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_config_free(config: *mut Config) {
|
|
drop(unsafe { Box::from_raw(config) });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_header_info(
|
|
buf: *mut u8, buf_len: size_t, dcil: size_t, version: *mut u32, ty: *mut u8,
|
|
scid: *mut u8, scid_len: *mut size_t, dcid: *mut u8, dcid_len: *mut size_t,
|
|
token: *mut u8, token_len: *mut size_t,
|
|
) -> c_int {
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
|
|
let hdr = match Header::from_slice(buf, dcil) {
|
|
Ok(v) => v,
|
|
|
|
Err(e) => return e.to_c() as c_int,
|
|
};
|
|
|
|
unsafe {
|
|
*version = hdr.version;
|
|
|
|
*ty = match hdr.ty {
|
|
Type::Initial => 1,
|
|
Type::Retry => 2,
|
|
Type::Handshake => 3,
|
|
Type::ZeroRTT => 4,
|
|
Type::Short => 5,
|
|
Type::VersionNegotiation => 6,
|
|
};
|
|
|
|
if *scid_len < hdr.scid.len() {
|
|
return -1;
|
|
}
|
|
|
|
let scid = slice::from_raw_parts_mut(scid, *scid_len);
|
|
let scid = &mut scid[..hdr.scid.len()];
|
|
scid.copy_from_slice(&hdr.scid);
|
|
|
|
*scid_len = hdr.scid.len();
|
|
|
|
if *dcid_len < hdr.dcid.len() {
|
|
return -1;
|
|
}
|
|
|
|
let dcid = slice::from_raw_parts_mut(dcid, *dcid_len);
|
|
let dcid = &mut dcid[..hdr.dcid.len()];
|
|
dcid.copy_from_slice(&hdr.dcid);
|
|
|
|
*dcid_len = hdr.dcid.len();
|
|
|
|
match hdr.token {
|
|
Some(tok) => {
|
|
if *token_len < tok.len() {
|
|
return -1;
|
|
}
|
|
|
|
let token = slice::from_raw_parts_mut(token, *token_len);
|
|
let token = &mut token[..tok.len()];
|
|
token.copy_from_slice(&tok);
|
|
|
|
*token_len = tok.len();
|
|
},
|
|
|
|
None => *token_len = 0,
|
|
}
|
|
}
|
|
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_accept(
|
|
scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
|
|
local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
|
|
config: &mut Config,
|
|
) -> *mut Connection {
|
|
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
|
|
let scid = ConnectionId::from_ref(scid);
|
|
|
|
let odcid = if !odcid.is_null() && odcid_len > 0 {
|
|
Some(ConnectionId::from_ref(unsafe {
|
|
slice::from_raw_parts(odcid, odcid_len)
|
|
}))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let local = std_addr_from_c(local, local_len);
|
|
let peer = std_addr_from_c(peer, peer_len);
|
|
|
|
match accept(&scid, odcid.as_ref(), local, peer, config) {
|
|
Ok(c) => Box::into_raw(Box::new(c)),
|
|
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_connect(
|
|
server_name: *const c_char, scid: *const u8, scid_len: size_t,
|
|
local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
|
|
config: &mut Config,
|
|
) -> *mut Connection {
|
|
let server_name = if server_name.is_null() {
|
|
None
|
|
} else {
|
|
Some(unsafe { ffi::CStr::from_ptr(server_name).to_str().unwrap() })
|
|
};
|
|
|
|
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
|
|
let scid = ConnectionId::from_ref(scid);
|
|
|
|
let local = std_addr_from_c(local, local_len);
|
|
let peer = std_addr_from_c(peer, peer_len);
|
|
|
|
match connect(server_name, &scid, local, peer, config) {
|
|
Ok(c) => Box::into_raw(Box::new(c)),
|
|
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_negotiate_version(
|
|
scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
|
|
out: *mut u8, out_len: size_t,
|
|
) -> ssize_t {
|
|
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
|
|
let scid = ConnectionId::from_ref(scid);
|
|
|
|
let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) };
|
|
let dcid = ConnectionId::from_ref(dcid);
|
|
|
|
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
|
|
|
|
match negotiate_version(&scid, &dcid, out) {
|
|
Ok(v) => v as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_version_is_supported(version: u32) -> bool {
|
|
version_is_supported(version)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_retry(
|
|
scid: *const u8, scid_len: size_t, dcid: *const u8, dcid_len: size_t,
|
|
new_scid: *const u8, new_scid_len: size_t, token: *const u8,
|
|
token_len: size_t, version: u32, out: *mut u8, out_len: size_t,
|
|
) -> ssize_t {
|
|
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
|
|
let scid = ConnectionId::from_ref(scid);
|
|
|
|
let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) };
|
|
let dcid = ConnectionId::from_ref(dcid);
|
|
|
|
let new_scid = unsafe { slice::from_raw_parts(new_scid, new_scid_len) };
|
|
let new_scid = ConnectionId::from_ref(new_scid);
|
|
|
|
let token = unsafe { slice::from_raw_parts(token, token_len) };
|
|
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
|
|
|
|
match retry(&scid, &dcid, &new_scid, token, version, out) {
|
|
Ok(v) => v as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_new_with_tls(
|
|
scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
|
|
local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
|
|
config: &Config, ssl: *mut c_void, is_server: bool,
|
|
) -> *mut Connection {
|
|
let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
|
|
let scid = ConnectionId::from_ref(scid);
|
|
|
|
let odcid = if !odcid.is_null() && odcid_len > 0 {
|
|
Some(ConnectionId::from_ref(unsafe {
|
|
slice::from_raw_parts(odcid, odcid_len)
|
|
}))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let local = std_addr_from_c(local, local_len);
|
|
let peer = std_addr_from_c(peer, peer_len);
|
|
|
|
let tls = unsafe { tls::Handshake::from_ptr(ssl) };
|
|
|
|
match Connection::with_tls(
|
|
&scid,
|
|
odcid.as_ref(),
|
|
local,
|
|
peer,
|
|
config,
|
|
tls,
|
|
is_server,
|
|
) {
|
|
Ok(c) => Box::into_raw(Box::new(c)),
|
|
|
|
Err(_) => ptr::null_mut(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_set_keylog_path(
|
|
conn: &mut Connection, path: *const c_char,
|
|
) -> bool {
|
|
let filename = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
let file = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open(filename);
|
|
|
|
let writer = match file {
|
|
Ok(f) => std::io::BufWriter::new(f),
|
|
|
|
Err(_) => return false,
|
|
};
|
|
|
|
conn.set_keylog(Box::new(writer));
|
|
|
|
true
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[cfg(unix)]
|
|
pub extern fn quiche_conn_set_keylog_fd(conn: &mut Connection, fd: c_int) {
|
|
let f = unsafe { std::fs::File::from_raw_fd(fd) };
|
|
let writer = std::io::BufWriter::new(f);
|
|
|
|
conn.set_keylog(Box::new(writer));
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[cfg(feature = "qlog")]
|
|
pub extern fn quiche_conn_set_qlog_path(
|
|
conn: &mut Connection, path: *const c_char, log_title: *const c_char,
|
|
log_desc: *const c_char,
|
|
) -> bool {
|
|
let filename = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
|
|
|
|
let file = std::fs::OpenOptions::new()
|
|
.write(true)
|
|
.create_new(true)
|
|
.open(filename);
|
|
|
|
let writer = match file {
|
|
Ok(f) => std::io::BufWriter::new(f),
|
|
|
|
Err(_) => return false,
|
|
};
|
|
|
|
let title = unsafe { ffi::CStr::from_ptr(log_title).to_str().unwrap() };
|
|
let description = unsafe { ffi::CStr::from_ptr(log_desc).to_str().unwrap() };
|
|
|
|
conn.set_qlog(
|
|
Box::new(writer),
|
|
title.to_string(),
|
|
format!("{} id={}", description, conn.trace_id),
|
|
);
|
|
|
|
true
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[cfg(all(unix, feature = "qlog"))]
|
|
pub extern fn quiche_conn_set_qlog_fd(
|
|
conn: &mut Connection, fd: c_int, log_title: *const c_char,
|
|
log_desc: *const c_char,
|
|
) {
|
|
let f = unsafe { std::fs::File::from_raw_fd(fd) };
|
|
let writer = std::io::BufWriter::new(f);
|
|
|
|
let title = unsafe { ffi::CStr::from_ptr(log_title).to_str().unwrap() };
|
|
let description = unsafe { ffi::CStr::from_ptr(log_desc).to_str().unwrap() };
|
|
|
|
conn.set_qlog(
|
|
Box::new(writer),
|
|
title.to_string(),
|
|
format!("{} id={}", description, conn.trace_id),
|
|
);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_set_session(
|
|
conn: &mut Connection, buf: *const u8, buf_len: size_t,
|
|
) -> c_int {
|
|
let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
|
|
|
|
match conn.set_session(buf) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct RecvInfo<'a> {
|
|
from: &'a sockaddr,
|
|
from_len: socklen_t,
|
|
to: &'a sockaddr,
|
|
to_len: socklen_t,
|
|
}
|
|
|
|
impl<'a> From<&RecvInfo<'a>> for crate::RecvInfo {
|
|
fn from(info: &RecvInfo) -> crate::RecvInfo {
|
|
crate::RecvInfo {
|
|
from: std_addr_from_c(info.from, info.from_len),
|
|
to: std_addr_from_c(info.to, info.to_len),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_recv(
|
|
conn: &mut Connection, buf: *mut u8, buf_len: size_t, info: &RecvInfo,
|
|
) -> ssize_t {
|
|
if buf_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
|
|
|
|
match conn.recv(buf, info.into()) {
|
|
Ok(v) => v as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct SendInfo {
|
|
from: sockaddr_storage,
|
|
from_len: socklen_t,
|
|
to: sockaddr_storage,
|
|
to_len: socklen_t,
|
|
|
|
at: timespec,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_send(
|
|
conn: &mut Connection, out: *mut u8, out_len: size_t, out_info: &mut SendInfo,
|
|
) -> ssize_t {
|
|
if out_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
|
|
|
|
match conn.send(out) {
|
|
Ok((v, info)) => {
|
|
out_info.from_len = std_addr_to_c(&info.from, &mut out_info.from);
|
|
out_info.to_len = std_addr_to_c(&info.to, &mut out_info.to);
|
|
|
|
std_time_to_c(&info.at, &mut out_info.at);
|
|
|
|
v as ssize_t
|
|
},
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_recv(
|
|
conn: &mut Connection, stream_id: u64, out: *mut u8, out_len: size_t,
|
|
fin: &mut bool,
|
|
) -> ssize_t {
|
|
if out_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
|
|
|
|
let (out_len, out_fin) = match conn.stream_recv(stream_id, out) {
|
|
Ok(v) => v,
|
|
|
|
Err(e) => return e.to_c(),
|
|
};
|
|
|
|
*fin = out_fin;
|
|
|
|
out_len as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_send(
|
|
conn: &mut Connection, stream_id: u64, buf: *const u8, buf_len: size_t,
|
|
fin: bool,
|
|
) -> ssize_t {
|
|
if buf_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
|
|
|
|
match conn.stream_send(stream_id, buf, fin) {
|
|
Ok(v) => v as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_priority(
|
|
conn: &mut Connection, stream_id: u64, urgency: u8, incremental: bool,
|
|
) -> c_int {
|
|
match conn.stream_priority(stream_id, urgency, incremental) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_shutdown(
|
|
conn: &mut Connection, stream_id: u64, direction: Shutdown, err: u64,
|
|
) -> c_int {
|
|
match conn.stream_shutdown(stream_id, direction, err) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_capacity(
|
|
conn: &Connection, stream_id: u64,
|
|
) -> ssize_t {
|
|
match conn.stream_capacity(stream_id) {
|
|
Ok(v) => v as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_readable(
|
|
conn: &Connection, stream_id: u64,
|
|
) -> bool {
|
|
conn.stream_readable(stream_id)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_readable_next(conn: &mut Connection) -> i64 {
|
|
conn.stream_readable_next().map(|v| v as i64).unwrap_or(-1)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_writable(
|
|
conn: &mut Connection, stream_id: u64, len: usize,
|
|
) -> c_int {
|
|
match conn.stream_writable(stream_id, len) {
|
|
Ok(true) => 1,
|
|
|
|
Ok(false) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_writable_next(conn: &mut Connection) -> i64 {
|
|
conn.stream_writable_next().map(|v| v as i64).unwrap_or(-1)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stream_finished(
|
|
conn: &Connection, stream_id: u64,
|
|
) -> bool {
|
|
conn.stream_finished(stream_id)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_readable(conn: &Connection) -> *mut StreamIter {
|
|
Box::into_raw(Box::new(conn.readable()))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_writable(conn: &Connection) -> *mut StreamIter {
|
|
Box::into_raw(Box::new(conn.writable()))
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_max_send_udp_payload_size(conn: &Connection) -> usize {
|
|
conn.max_send_udp_payload_size()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_readable(conn: &Connection) -> bool {
|
|
conn.is_readable()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_close(
|
|
conn: &mut Connection, app: bool, err: u64, reason: *const u8,
|
|
reason_len: size_t,
|
|
) -> c_int {
|
|
let reason = unsafe { slice::from_raw_parts(reason, reason_len) };
|
|
|
|
match conn.close(app, err, reason) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => e.to_c() as c_int,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_timeout_as_nanos(conn: &Connection) -> u64 {
|
|
match conn.timeout() {
|
|
Some(timeout) => timeout.as_nanos() as u64,
|
|
|
|
None => u64::MAX,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_timeout_as_millis(conn: &Connection) -> u64 {
|
|
match conn.timeout() {
|
|
Some(timeout) => timeout.as_millis() as u64,
|
|
|
|
None => u64::MAX,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_on_timeout(conn: &mut Connection) {
|
|
conn.on_timeout()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_trace_id(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
let trace_id = conn.trace_id();
|
|
|
|
*out = trace_id.as_ptr();
|
|
*out_len = trace_id.len();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_source_id(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
let conn_id = conn.source_id();
|
|
let id = conn_id.as_ref();
|
|
*out = id.as_ptr();
|
|
*out_len = id.len();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_destination_id(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
let conn_id = conn.destination_id();
|
|
let id = conn_id.as_ref();
|
|
|
|
*out = id.as_ptr();
|
|
*out_len = id.len();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_application_proto(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
let proto = conn.application_proto();
|
|
|
|
*out = proto.as_ptr();
|
|
*out_len = proto.len();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_peer_cert(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
match conn.peer_cert() {
|
|
Some(peer_cert) => {
|
|
*out = peer_cert.as_ptr();
|
|
*out_len = peer_cert.len();
|
|
},
|
|
|
|
None => *out_len = 0,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_session(
|
|
conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
|
|
) {
|
|
match conn.session() {
|
|
Some(session) => {
|
|
*out = session.as_ptr();
|
|
*out_len = session.len();
|
|
},
|
|
|
|
None => *out_len = 0,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_established(conn: &Connection) -> bool {
|
|
conn.is_established()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_in_early_data(conn: &Connection) -> bool {
|
|
conn.is_in_early_data()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_draining(conn: &Connection) -> bool {
|
|
conn.is_draining()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_closed(conn: &Connection) -> bool {
|
|
conn.is_closed()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_timed_out(conn: &Connection) -> bool {
|
|
conn.is_timed_out()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_peer_error(
|
|
conn: &Connection, is_app: *mut bool, error_code: *mut u64,
|
|
reason: &mut *const u8, reason_len: &mut size_t,
|
|
) -> bool {
|
|
match &conn.peer_error {
|
|
Some(conn_err) => unsafe {
|
|
*is_app = conn_err.is_app;
|
|
*error_code = conn_err.error_code;
|
|
*reason = conn_err.reason.as_ptr();
|
|
*reason_len = conn_err.reason.len();
|
|
|
|
true
|
|
},
|
|
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_local_error(
|
|
conn: &Connection, is_app: *mut bool, error_code: *mut u64,
|
|
reason: &mut *const u8, reason_len: &mut size_t,
|
|
) -> bool {
|
|
match &conn.local_error {
|
|
Some(conn_err) => unsafe {
|
|
*is_app = conn_err.is_app;
|
|
*error_code = conn_err.error_code;
|
|
*reason = conn_err.reason.as_ptr();
|
|
*reason_len = conn_err.reason.len();
|
|
|
|
true
|
|
},
|
|
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_stream_iter_next(
|
|
iter: &mut StreamIter, stream_id: *mut u64,
|
|
) -> bool {
|
|
if let Some(v) = iter.next() {
|
|
unsafe { *stream_id = v };
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_stream_iter_free(iter: *mut StreamIter) {
|
|
drop(unsafe { Box::from_raw(iter) });
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Stats {
|
|
recv: usize,
|
|
sent: usize,
|
|
lost: usize,
|
|
retrans: usize,
|
|
sent_bytes: u64,
|
|
recv_bytes: u64,
|
|
lost_bytes: u64,
|
|
stream_retrans_bytes: u64,
|
|
paths_count: usize,
|
|
peer_max_idle_timeout: u64,
|
|
peer_max_udp_payload_size: u64,
|
|
peer_initial_max_data: u64,
|
|
peer_initial_max_stream_data_bidi_local: u64,
|
|
peer_initial_max_stream_data_bidi_remote: u64,
|
|
peer_initial_max_stream_data_uni: u64,
|
|
peer_initial_max_streams_bidi: u64,
|
|
peer_initial_max_streams_uni: u64,
|
|
peer_ack_delay_exponent: u64,
|
|
peer_max_ack_delay: u64,
|
|
peer_disable_active_migration: bool,
|
|
peer_active_conn_id_limit: u64,
|
|
peer_max_datagram_frame_size: ssize_t,
|
|
paths: [PathStats; 8],
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_stats(conn: &Connection, out: &mut Stats) {
|
|
let stats = conn.stats();
|
|
|
|
out.recv = stats.recv;
|
|
out.sent = stats.sent;
|
|
out.lost = stats.lost;
|
|
out.retrans = stats.retrans;
|
|
out.sent_bytes = stats.sent_bytes;
|
|
out.recv_bytes = stats.recv_bytes;
|
|
out.lost_bytes = stats.lost_bytes;
|
|
out.stream_retrans_bytes = stats.stream_retrans_bytes;
|
|
out.paths_count = stats.paths_count;
|
|
out.peer_max_idle_timeout = stats.peer_max_idle_timeout;
|
|
out.peer_max_udp_payload_size = stats.peer_max_udp_payload_size;
|
|
out.peer_initial_max_data = stats.peer_initial_max_data;
|
|
out.peer_initial_max_stream_data_bidi_local =
|
|
stats.peer_initial_max_stream_data_bidi_local;
|
|
out.peer_initial_max_stream_data_bidi_remote =
|
|
stats.peer_initial_max_stream_data_bidi_remote;
|
|
out.peer_initial_max_stream_data_uni = stats.peer_initial_max_stream_data_uni;
|
|
out.peer_initial_max_streams_bidi = stats.peer_initial_max_streams_bidi;
|
|
out.peer_initial_max_streams_uni = stats.peer_initial_max_streams_uni;
|
|
out.peer_ack_delay_exponent = stats.peer_ack_delay_exponent;
|
|
out.peer_max_ack_delay = stats.peer_max_ack_delay;
|
|
out.peer_disable_active_migration = stats.peer_disable_active_migration;
|
|
out.peer_active_conn_id_limit = stats.peer_active_conn_id_limit;
|
|
out.peer_max_datagram_frame_size = match stats.peer_max_datagram_frame_size {
|
|
None => Error::Done.to_c(),
|
|
|
|
Some(v) => v as ssize_t,
|
|
};
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct PathStats {
|
|
local_addr: sockaddr_storage,
|
|
local_addr_len: socklen_t,
|
|
peer_addr: sockaddr_storage,
|
|
peer_addr_len: socklen_t,
|
|
validation_state: ssize_t,
|
|
active: bool,
|
|
recv: usize,
|
|
sent: usize,
|
|
lost: usize,
|
|
retrans: usize,
|
|
rtt: u64,
|
|
cwnd: usize,
|
|
sent_bytes: u64,
|
|
recv_bytes: u64,
|
|
lost_bytes: u64,
|
|
stream_retrans_bytes: u64,
|
|
pmtu: usize,
|
|
delivery_rate: u64,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_path_stats(
|
|
conn: &Connection, idx: usize, out: &mut PathStats,
|
|
) -> c_int {
|
|
let stats = match conn.path_stats().nth(idx) {
|
|
Some(p) => p,
|
|
None => return Error::Done.to_c() as c_int,
|
|
};
|
|
|
|
out.local_addr_len = std_addr_to_c(&stats.local_addr, &mut out.local_addr);
|
|
out.peer_addr_len = std_addr_to_c(&stats.peer_addr, &mut out.peer_addr);
|
|
out.validation_state = stats.validation_state.to_c();
|
|
out.active = stats.active;
|
|
out.recv = stats.recv;
|
|
out.sent = stats.sent;
|
|
out.lost = stats.lost;
|
|
out.retrans = stats.retrans;
|
|
out.rtt = stats.rtt.as_nanos() as u64;
|
|
out.cwnd = stats.cwnd;
|
|
out.sent_bytes = stats.sent_bytes;
|
|
out.recv_bytes = stats.recv_bytes;
|
|
out.lost_bytes = stats.lost_bytes;
|
|
out.stream_retrans_bytes = stats.stream_retrans_bytes;
|
|
out.pmtu = stats.pmtu;
|
|
out.delivery_rate = stats.delivery_rate;
|
|
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_is_server(conn: &Connection) -> bool {
|
|
conn.is_server()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_max_writable_len(conn: &Connection) -> ssize_t {
|
|
match conn.dgram_max_writable_len() {
|
|
None => Error::Done.to_c(),
|
|
|
|
Some(v) => v as ssize_t,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_recv_front_len(conn: &Connection) -> ssize_t {
|
|
match conn.dgram_recv_front_len() {
|
|
None => Error::Done.to_c(),
|
|
|
|
Some(v) => v as ssize_t,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_recv_queue_len(conn: &Connection) -> ssize_t {
|
|
conn.dgram_recv_queue_len() as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_recv_queue_byte_size(
|
|
conn: &Connection,
|
|
) -> ssize_t {
|
|
conn.dgram_recv_queue_byte_size() as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_send_queue_len(conn: &Connection) -> ssize_t {
|
|
conn.dgram_send_queue_len() as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_send_queue_byte_size(
|
|
conn: &Connection,
|
|
) -> ssize_t {
|
|
conn.dgram_send_queue_byte_size() as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_send(
|
|
conn: &mut Connection, buf: *const u8, buf_len: size_t,
|
|
) -> ssize_t {
|
|
if buf_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
|
|
|
|
match conn.dgram_send(buf) {
|
|
Ok(_) => buf_len as ssize_t,
|
|
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_recv(
|
|
conn: &mut Connection, out: *mut u8, out_len: size_t,
|
|
) -> ssize_t {
|
|
if out_len > <ssize_t>::max_value() as usize {
|
|
panic!("The provided buffer is too large");
|
|
}
|
|
|
|
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
|
|
|
|
let out_len = match conn.dgram_recv(out) {
|
|
Ok(v) => v,
|
|
|
|
Err(e) => return e.to_c(),
|
|
};
|
|
|
|
out_len as ssize_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_dgram_purge_outgoing(
|
|
conn: &mut Connection, f: extern fn(*const u8, size_t) -> bool,
|
|
) {
|
|
conn.dgram_purge_outgoing(|d: &[u8]| -> bool {
|
|
let ptr: *const u8 = d.as_ptr();
|
|
let len: size_t = d.len();
|
|
|
|
f(ptr, len)
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_send_ack_eliciting(conn: &mut Connection) -> ssize_t {
|
|
match conn.send_ack_eliciting() {
|
|
Ok(()) => 0,
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_send_ack_eliciting_on_path(
|
|
conn: &mut Connection, local: &sockaddr, local_len: socklen_t,
|
|
peer: &sockaddr, peer_len: socklen_t,
|
|
) -> ssize_t {
|
|
let local = std_addr_from_c(local, local_len);
|
|
let peer = std_addr_from_c(peer, peer_len);
|
|
match conn.send_ack_eliciting_on_path(local, peer) {
|
|
Ok(()) => 0,
|
|
Err(e) => e.to_c(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_free(conn: *mut Connection) {
|
|
drop(unsafe { Box::from_raw(conn) });
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_peer_streams_left_bidi(conn: &Connection) -> u64 {
|
|
conn.peer_streams_left_bidi()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_peer_streams_left_uni(conn: &Connection) -> u64 {
|
|
conn.peer_streams_left_uni()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_conn_send_quantum(conn: &Connection) -> size_t {
|
|
conn.send_quantum() as size_t
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_put_varint(
|
|
buf: *mut u8, buf_len: size_t, val: u64,
|
|
) -> c_int {
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
|
|
|
|
let mut b = octets::OctetsMut::with_slice(buf);
|
|
match b.put_varint(val) {
|
|
Ok(_) => 0,
|
|
|
|
Err(e) => {
|
|
let err: Error = e.into();
|
|
err.to_c() as c_int
|
|
},
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern fn quiche_get_varint(
|
|
buf: *const u8, buf_len: size_t, val: *mut u64,
|
|
) -> ssize_t {
|
|
let buf = unsafe { slice::from_raw_parts(buf, buf_len) };
|
|
|
|
let mut b = octets::Octets::with_slice(buf);
|
|
match b.get_varint() {
|
|
Ok(v) => unsafe { *val = v },
|
|
|
|
Err(e) => {
|
|
let err: Error = e.into();
|
|
return err.to_c();
|
|
},
|
|
};
|
|
|
|
b.off() as ssize_t
|
|
}
|
|
|
|
fn std_addr_from_c(addr: &sockaddr, addr_len: socklen_t) -> SocketAddr {
|
|
match addr.sa_family as i32 {
|
|
AF_INET => {
|
|
assert!(addr_len as usize == std::mem::size_of::<sockaddr_in>());
|
|
|
|
let in4 = unsafe { *(addr as *const _ as *const sockaddr_in) };
|
|
|
|
#[cfg(not(windows))]
|
|
let ip_addr = Ipv4Addr::from(u32::from_be(in4.sin_addr.s_addr));
|
|
#[cfg(windows)]
|
|
let ip_addr = {
|
|
let ip_bytes = unsafe { in4.sin_addr.S_un.S_un_b() };
|
|
|
|
Ipv4Addr::from([
|
|
ip_bytes.s_b1,
|
|
ip_bytes.s_b2,
|
|
ip_bytes.s_b3,
|
|
ip_bytes.s_b4,
|
|
])
|
|
};
|
|
|
|
let port = u16::from_be(in4.sin_port);
|
|
|
|
let out = SocketAddrV4::new(ip_addr, port);
|
|
|
|
out.into()
|
|
},
|
|
|
|
AF_INET6 => {
|
|
assert!(addr_len as usize == std::mem::size_of::<sockaddr_in6>());
|
|
|
|
let in6 = unsafe { *(addr as *const _ as *const sockaddr_in6) };
|
|
|
|
let ip_addr = Ipv6Addr::from(
|
|
#[cfg(not(windows))]
|
|
in6.sin6_addr.s6_addr,
|
|
#[cfg(windows)]
|
|
*unsafe { in6.sin6_addr.u.Byte() },
|
|
);
|
|
|
|
let port = u16::from_be(in6.sin6_port);
|
|
|
|
#[cfg(not(windows))]
|
|
let scope_id = in6.sin6_scope_id;
|
|
#[cfg(windows)]
|
|
let scope_id = unsafe { *in6.u.sin6_scope_id() };
|
|
|
|
let out =
|
|
SocketAddrV6::new(ip_addr, port, in6.sin6_flowinfo, scope_id);
|
|
|
|
out.into()
|
|
},
|
|
|
|
_ => unimplemented!("unsupported address type"),
|
|
}
|
|
}
|
|
|
|
fn std_addr_to_c(addr: &SocketAddr, out: &mut sockaddr_storage) -> socklen_t {
|
|
let sin_port = addr.port().to_be();
|
|
|
|
match addr {
|
|
SocketAddr::V4(addr) => unsafe {
|
|
let sa_len = std::mem::size_of::<sockaddr_in>();
|
|
let out_in = out as *mut _ as *mut sockaddr_in;
|
|
|
|
let s_addr = u32::from_ne_bytes(addr.ip().octets());
|
|
|
|
#[cfg(not(windows))]
|
|
let sin_addr = in_addr { s_addr };
|
|
#[cfg(windows)]
|
|
let sin_addr = {
|
|
let mut s_un = std::mem::zeroed::<in_addr_S_un>();
|
|
*s_un.S_addr_mut() = s_addr;
|
|
in_addr { S_un: s_un }
|
|
};
|
|
|
|
*out_in = sockaddr_in {
|
|
sin_family: AF_INET as sa_family_t,
|
|
|
|
sin_addr,
|
|
|
|
#[cfg(any(
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_os = "watchos",
|
|
target_os = "freebsd",
|
|
target_os = "dragonfly",
|
|
target_os = "openbsd",
|
|
target_os = "netbsd"
|
|
))]
|
|
sin_len: sa_len as u8,
|
|
|
|
sin_port,
|
|
|
|
sin_zero: std::mem::zeroed(),
|
|
};
|
|
|
|
sa_len as socklen_t
|
|
},
|
|
|
|
SocketAddr::V6(addr) => unsafe {
|
|
let sa_len = std::mem::size_of::<sockaddr_in6>();
|
|
let out_in6 = out as *mut _ as *mut sockaddr_in6;
|
|
|
|
#[cfg(not(windows))]
|
|
let sin6_addr = in6_addr {
|
|
s6_addr: addr.ip().octets(),
|
|
};
|
|
#[cfg(windows)]
|
|
let sin6_addr = {
|
|
let mut u = std::mem::zeroed::<in6_addr_u>();
|
|
*u.Byte_mut() = addr.ip().octets();
|
|
in6_addr { u }
|
|
};
|
|
|
|
#[cfg(windows)]
|
|
let u = {
|
|
let mut u = std::mem::zeroed::<SOCKADDR_IN6_LH_u>();
|
|
*u.sin6_scope_id_mut() = addr.scope_id();
|
|
u
|
|
};
|
|
|
|
*out_in6 = sockaddr_in6 {
|
|
sin6_family: AF_INET6 as sa_family_t,
|
|
|
|
sin6_addr,
|
|
|
|
#[cfg(any(
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_os = "watchos",
|
|
target_os = "freebsd",
|
|
target_os = "dragonfly",
|
|
target_os = "openbsd",
|
|
target_os = "netbsd"
|
|
))]
|
|
sin6_len: sa_len as u8,
|
|
|
|
sin6_port: sin_port,
|
|
|
|
sin6_flowinfo: addr.flowinfo(),
|
|
|
|
#[cfg(not(windows))]
|
|
sin6_scope_id: addr.scope_id(),
|
|
#[cfg(windows)]
|
|
u,
|
|
};
|
|
|
|
sa_len as socklen_t
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
|
|
fn std_time_to_c(time: &std::time::Instant, out: &mut timespec) {
|
|
const INSTANT_ZERO: std::time::Instant =
|
|
unsafe { std::mem::transmute(std::time::UNIX_EPOCH) };
|
|
|
|
let raw_time = time.duration_since(INSTANT_ZERO);
|
|
|
|
out.tv_sec = raw_time.as_secs() as libc::time_t;
|
|
out.tv_nsec = raw_time.subsec_nanos() as libc::c_long;
|
|
}
|
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "windows"))]
|
|
fn std_time_to_c(_time: &std::time::Instant, out: &mut timespec) {
|
|
// TODO: implement Instant conversion for systems that don't use timespec.
|
|
out.tv_sec = 0;
|
|
out.tv_nsec = 0;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[cfg(windows)]
|
|
use winapi::um::ws2tcpip::inet_ntop;
|
|
|
|
#[test]
|
|
fn addr_v4() {
|
|
let addr = "127.0.0.1:8080".parse().unwrap();
|
|
|
|
let mut out: sockaddr_storage = unsafe { std::mem::zeroed() };
|
|
|
|
assert_eq!(
|
|
std_addr_to_c(&addr, &mut out),
|
|
std::mem::size_of::<sockaddr_in>() as socklen_t
|
|
);
|
|
|
|
let s = std::ffi::CString::new("ddd.ddd.ddd.ddd").unwrap();
|
|
|
|
let s = unsafe {
|
|
let in_addr = &out as *const _ as *const sockaddr_in;
|
|
assert_eq!(u16::from_be((*in_addr).sin_port), addr.port());
|
|
|
|
let dst = s.into_raw();
|
|
|
|
inet_ntop(
|
|
AF_INET,
|
|
&((*in_addr).sin_addr) as *const _ as *const c_void,
|
|
dst,
|
|
16,
|
|
);
|
|
|
|
std::ffi::CString::from_raw(dst).into_string().unwrap()
|
|
};
|
|
|
|
assert_eq!(s, "127.0.0.1");
|
|
|
|
let addr = unsafe {
|
|
std_addr_from_c(
|
|
&*(&out as *const _ as *const sockaddr),
|
|
std::mem::size_of::<sockaddr_in>() as socklen_t,
|
|
)
|
|
};
|
|
|
|
assert_eq!(addr, "127.0.0.1:8080".parse().unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn addr_v6() {
|
|
let addr = "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
|
|
.parse()
|
|
.unwrap();
|
|
|
|
let mut out: sockaddr_storage = unsafe { std::mem::zeroed() };
|
|
|
|
assert_eq!(
|
|
std_addr_to_c(&addr, &mut out),
|
|
std::mem::size_of::<sockaddr_in6>() as socklen_t
|
|
);
|
|
|
|
let s = std::ffi::CString::new("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")
|
|
.unwrap();
|
|
|
|
let s = unsafe {
|
|
let in6_addr = &out as *const _ as *const sockaddr_in6;
|
|
assert_eq!(u16::from_be((*in6_addr).sin6_port), addr.port());
|
|
|
|
let dst = s.into_raw();
|
|
|
|
inet_ntop(
|
|
AF_INET6,
|
|
&((*in6_addr).sin6_addr) as *const _ as *const c_void,
|
|
dst,
|
|
45,
|
|
);
|
|
|
|
std::ffi::CString::from_raw(dst).into_string().unwrap()
|
|
};
|
|
|
|
assert_eq!(s, "2001:db8:85a3::8a2e:370:7334");
|
|
|
|
let addr = unsafe {
|
|
std_addr_from_c(
|
|
&*(&out as *const _ as *const sockaddr),
|
|
std::mem::size_of::<sockaddr_in6>() as socklen_t,
|
|
)
|
|
};
|
|
|
|
assert_eq!(
|
|
addr,
|
|
"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
|
|
.parse()
|
|
.unwrap()
|
|
);
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
extern {
|
|
fn inet_ntop(
|
|
af: c_int, src: *const c_void, dst: *mut c_char, size: socklen_t,
|
|
) -> *mut c_char;
|
|
}
|
|
}
|