// 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::SocketAddr; #[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::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(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; use crate::*; #[no_mangle] pub extern fn quiche_version() -> *const u8 { //static VERSION: &str = concat!("0.9.0", "\0"); // ANDROID's build system doesn't support environment variables // so we hardcode the package version here. static VERSION: &str = concat!("0.6.0", "\0"); VERSION.as_ptr() } struct Logger { cb: extern fn(line: *const u8, argp: *mut c_void), argp: std::sync::atomic::AtomicPtr, } 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_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] 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(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_enable_hystart(config: &mut Config, v: bool) { config.enable_hystart(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_free(config: *mut Config) { 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, from: &sockaddr, from_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 from = std_addr_from_c(from, from_len); match accept(&scid, odcid.as_ref(), from, config) { Ok(c) => Box::into_raw(Pin::into_inner(c)), Err(_) => ptr::null_mut(), } } #[no_mangle] pub extern fn quiche_connect( server_name: *const c_char, scid: *const u8, scid_len: size_t, to: &sockaddr, to_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 to = std_addr_from_c(to, to_len); match connect(server_name, &scid, to, config) { Ok(c) => Box::into_raw(Pin::into_inner(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, peer: &sockaddr, peer_len: socklen_t, config: &mut 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 peer = std_addr_from_c(peer, peer_len); let tls = unsafe { tls::Handshake::from_ptr(ssl) }; match Connection::with_tls( &scid, odcid.as_ref(), peer, config, tls, is_server, ) { Ok(c) => Box::into_raw(Pin::into_inner(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, } 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), } } } #[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 > ::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 { 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 > ::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.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 > ::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 > ::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: &mut 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: &mut Connection, stream_id: u64, ) -> bool { conn.stream_readable(stream_id) } #[no_mangle] pub extern fn quiche_conn_stream_finished( conn: &mut 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() } struct AppData(*mut c_void); unsafe impl Send for AppData {} unsafe impl Sync for AppData {} #[no_mangle] pub extern fn quiche_conn_stream_init_application_data( conn: &mut Connection, stream_id: u64, data: *mut c_void, ) -> c_int { match conn.stream_init_application_data(stream_id, AppData(data)) { Ok(_) => 0, Err(e) => e.to_c() as c_int, } } #[no_mangle] pub extern fn quiche_conn_stream_application_data( conn: &mut Connection, stream_id: u64, ) -> *mut c_void { match conn.stream_application_data(stream_id) { Some(v) => v.downcast_mut::().unwrap().0, None => ptr::null_mut(), } } #[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: &mut Connection) -> u64 { match conn.timeout() { Some(timeout) => timeout.as_nanos() as u64, None => std::u64::MAX, } } #[no_mangle] pub extern fn quiche_conn_timeout_as_millis(conn: &mut Connection) -> u64 { match conn.timeout() { Some(timeout) => timeout.as_millis() as u64, None => std::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: &mut 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: &mut 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: &mut 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: &mut 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_session( conn: &mut 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: &mut Connection) -> bool { conn.is_established() } #[no_mangle] pub extern fn quiche_conn_is_in_early_data(conn: &mut Connection) -> bool { conn.is_in_early_data() } #[no_mangle] pub extern fn quiche_conn_is_draining(conn: &mut Connection) -> bool { conn.is_draining() } #[no_mangle] pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool { conn.is_closed() } #[no_mangle] pub extern fn quiche_conn_peer_error( conn: &mut 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_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) { unsafe { Box::from_raw(iter) }; } #[repr(C)] pub struct Stats { recv: usize, sent: usize, lost: usize, rtt: u64, cwnd: usize, delivery_rate: u64, } #[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.rtt = stats.rtt.as_nanos() as u64; out.cwnd = stats.cwnd; out.delivery_rate = stats.delivery_rate; } #[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 > ::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 > ::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_free(conn: *mut Connection) { unsafe { Box::from_raw(conn) }; } #[no_mangle] pub extern fn quiche_conn_peer_streams_left_bidi(conn: &mut Connection) -> u64 { conn.peer_streams_left_bidi() } #[no_mangle] pub extern fn quiche_conn_peer_streams_left_uni(conn: &mut Connection) -> u64 { conn.peer_streams_left_uni() } fn std_addr_from_c(addr: &sockaddr, addr_len: socklen_t) -> SocketAddr { unsafe { match addr.sa_family as i32 { AF_INET => { assert!(addr_len as usize == std::mem::size_of::()); SocketAddr::V4( *(addr as *const _ as *const sockaddr_in as *const _), ) }, AF_INET6 => { assert!(addr_len as usize == std::mem::size_of::()); SocketAddr::V6( *(addr as *const _ as *const sockaddr_in6 as *const _), ) }, _ => unimplemented!("unsupported address type"), } } } fn std_addr_to_c(addr: &SocketAddr, out: &mut sockaddr_storage) -> socklen_t { unsafe { match addr { SocketAddr::V4(addr) => { let sa_len = std::mem::size_of::(); let src = addr as *const _ as *const u8; let dst = out as *mut _ as *mut u8; std::ptr::copy_nonoverlapping(src, dst, sa_len); sa_len as socklen_t }, SocketAddr::V6(addr) => { let sa_len = std::mem::size_of::(); let src = addr as *const _ as *const u8; let dst = out as *mut _ as *mut u8; std::ptr::copy_nonoverlapping(src, dst, sa_len); 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) { unsafe { ptr::copy_nonoverlapping(time as *const _ as *const timespec, out, 1) } } #[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; }