use crate::candidate::TransportType;
use crate::{AddressFamily, Feature, IntegrityAlgorithm, const_override};
pub use crate::stream::Credentials as TurnCredentials;
#[derive(Debug)]
pub struct TurnConfig {
ffi: *mut crate::ffi::RiceTurnConfig,
}
unsafe impl Send for TurnConfig {}
impl TurnConfig {
pub fn new(
client_transport: TransportType,
turn_server: crate::Address,
credentials: TurnCredentials,
) -> Self {
unsafe {
let ffi = crate::ffi::rice_turn_config_new(
client_transport.into(),
const_override(turn_server.as_c()),
credentials.into_c_none(),
);
Self { ffi }
}
}
pub fn tls_config(&self) -> Option<TurnTlsConfig> {
unsafe {
let ret = crate::ffi::rice_turn_config_get_tls_config(self.ffi);
if ret.is_null() {
None
} else {
match crate::ffi::rice_tls_config_variant(ret) {
#[cfg(feature = "openssl")]
crate::ffi::RICE_TLS_VARIANT_OPENSSL => Some(TurnTlsConfig::Openssl(ret)),
#[cfg(feature = "rustls")]
crate::ffi::RICE_TLS_VARIANT_RUSTLS => Some(TurnTlsConfig::Rustls(ret)),
_ => None,
}
}
}
}
pub fn set_tls_config(&mut self, tls_config: TurnTlsConfig) {
unsafe {
crate::ffi::rice_turn_config_set_tls_config(self.ffi, tls_config.as_c());
}
}
pub fn addr(&self) -> crate::Address {
unsafe { crate::Address::from_c_full(crate::ffi::rice_turn_config_get_addr(self.ffi)) }
}
pub fn client_transport(&self) -> TransportType {
unsafe { crate::ffi::rice_turn_config_get_client_transport(self.ffi).into() }
}
pub fn set_allocation_transport(&mut self, allocation_transport: TransportType) {
unsafe {
crate::ffi::rice_turn_config_set_allocation_transport(
self.ffi,
allocation_transport.into(),
);
}
}
pub fn allocation_transport(&self) -> TransportType {
unsafe { crate::ffi::rice_turn_config_get_allocation_transport(self.ffi).into() }
}
pub fn add_address_family(&mut self, family: AddressFamily) {
unsafe {
crate::ffi::rice_turn_config_add_address_family(self.ffi, family.into());
}
}
pub fn set_address_family(&mut self, family: AddressFamily) {
unsafe {
crate::ffi::rice_turn_config_set_address_family(self.ffi, family.into());
}
}
pub fn address_families(&self) -> Vec<AddressFamily> {
unsafe {
let mut len = 0;
crate::ffi::rice_turn_config_get_address_families(
self.ffi,
&mut len,
core::ptr::null_mut(),
);
let mut ret = vec![AddressFamily::IPV4; len];
crate::ffi::rice_turn_config_get_address_families(
self.ffi,
&mut len,
ret.as_mut_ptr() as _,
);
ret.resize(len, AddressFamily::IPV4);
ret
}
}
pub fn credentials(&self) -> TurnCredentials {
unsafe {
TurnCredentials::from_c_full(crate::ffi::rice_turn_config_get_credentials(self.ffi))
}
}
pub fn add_supported_integrity(&mut self, integrity: IntegrityAlgorithm) {
unsafe {
crate::ffi::rice_turn_config_add_supported_integrity(self.ffi, integrity.into());
}
}
pub fn set_supported_integrity(&mut self, integrity: IntegrityAlgorithm) {
unsafe {
crate::ffi::rice_turn_config_set_supported_integrity(self.ffi, integrity.into());
}
}
pub fn supported_integrity(&self) -> Vec<IntegrityAlgorithm> {
unsafe {
let mut len = 0;
crate::ffi::rice_turn_config_get_supported_integrity(
self.ffi,
&mut len,
core::ptr::null_mut(),
);
let mut ret = vec![IntegrityAlgorithm::Sha1; len];
crate::ffi::rice_turn_config_get_supported_integrity(
self.ffi,
&mut len,
ret.as_mut_ptr() as _,
);
ret.resize(len, IntegrityAlgorithm::Sha1);
ret
}
}
pub fn set_anonymous_username(&mut self, anon: Feature) {
unsafe {
crate::ffi::rice_turn_config_set_anonymous_username(self.ffi, anon as _);
}
}
pub fn anonymous_username(&self) -> Feature {
unsafe { crate::ffi::rice_turn_config_get_anonymous_username(self.ffi).into() }
}
pub(crate) fn into_c_full(self) -> *mut crate::ffi::RiceTurnConfig {
let ret = self.ffi;
core::mem::forget(self);
ret
}
}
impl Clone for TurnConfig {
fn clone(&self) -> Self {
unsafe {
Self {
ffi: crate::ffi::rice_turn_config_copy(self.ffi),
}
}
}
}
impl Drop for TurnConfig {
fn drop(&mut self) {
unsafe {
crate::ffi::rice_turn_config_free(self.ffi);
}
}
}
#[derive(Debug)]
pub enum TurnTlsConfig {
#[cfg(feature = "rustls")]
Rustls(*mut crate::ffi::RiceTlsConfig),
#[cfg(feature = "openssl")]
Openssl(*mut crate::ffi::RiceTlsConfig),
#[cfg(feature = "dimpl")]
Dimpl(*mut crate::ffi::RiceTlsConfig),
}
impl Clone for TurnTlsConfig {
fn clone(&self) -> Self {
match self {
#[cfg(feature = "dimpl")]
Self::Dimpl(cfg) => unsafe { Self::Rustls(crate::ffi::rice_tls_config_ref(*cfg)) },
#[cfg(feature = "rustls")]
Self::Rustls(cfg) => unsafe { Self::Rustls(crate::ffi::rice_tls_config_ref(*cfg)) },
#[cfg(feature = "openssl")]
Self::Openssl(cfg) => unsafe { Self::Openssl(crate::ffi::rice_tls_config_ref(*cfg)) },
}
}
}
impl Drop for TurnTlsConfig {
fn drop(&mut self) {
match self {
#[cfg(feature = "dimpl")]
Self::Dimpl(cfg) => unsafe { crate::ffi::rice_tls_config_unref(*cfg) },
#[cfg(feature = "rustls")]
Self::Rustls(cfg) => unsafe { crate::ffi::rice_tls_config_unref(*cfg) },
#[cfg(feature = "openssl")]
Self::Openssl(cfg) => unsafe { crate::ffi::rice_tls_config_unref(*cfg) },
}
}
}
impl TurnTlsConfig {
#[cfg(feature = "rustls")]
pub fn new_rustls_with_dns(server_name: &str) -> Self {
let server_str = std::ffi::CString::new(server_name).unwrap();
unsafe {
Self::Rustls(crate::ffi::rice_tls_config_new_rustls_with_dns(
server_str.as_ptr(),
))
}
}
#[cfg(feature = "rustls")]
pub fn new_rustls_with_ip(addr: &crate::Address) -> Self {
unsafe { Self::Rustls(crate::ffi::rice_tls_config_new_rustls_with_ip(addr.as_c())) }
}
#[cfg(feature = "openssl")]
pub fn new_openssl(transport: TransportType) -> Self {
unsafe { Self::Openssl(crate::ffi::rice_tls_config_new_openssl(transport.into())) }
}
#[cfg(feature = "dimpl")]
pub fn new_dimpl() -> Self {
unsafe { Self::Dimpl(crate::ffi::rice_tls_config_new_dimpl()) }
}
pub(crate) fn as_c(&self) -> *mut crate::ffi::RiceTlsConfig {
#[allow(unreachable_patterns)]
let ret = match self {
#[cfg(feature = "dimpl")]
Self::Dimpl(cfg) => *cfg,
#[cfg(feature = "rustls")]
Self::Rustls(cfg) => *cfg,
#[cfg(feature = "openssl")]
Self::Openssl(cfg) => *cfg,
_ => core::ptr::null_mut(),
};
ret
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::net::SocketAddr;
fn turn_server_address() -> crate::Address {
"127.0.0.1:3478".parse::<SocketAddr>().unwrap().into()
}
fn turn_credentials() -> TurnCredentials {
TurnCredentials::new("tuser", "tpass")
}
#[test]
fn test_config_getter() {
let mut cfg = TurnConfig::new(
TransportType::Udp,
turn_server_address(),
turn_credentials(),
);
assert_eq!(cfg.addr(), turn_server_address());
assert_eq!(cfg.client_transport(), TransportType::Udp);
assert_eq!(&cfg.address_families(), &[AddressFamily::IPV4]);
assert_eq!(cfg.allocation_transport(), TransportType::Udp);
assert_eq!(&cfg.supported_integrity(), &[IntegrityAlgorithm::Sha1]);
assert_eq!(cfg.anonymous_username(), Feature::Auto);
assert!(cfg.tls_config().is_none());
for transport in [TransportType::Udp, TransportType::Tcp] {
cfg.set_allocation_transport(transport);
assert_eq!(cfg.allocation_transport(), transport);
}
cfg.add_address_family(AddressFamily::IPV6);
assert_eq!(
&cfg.address_families(),
&[AddressFamily::IPV4, AddressFamily::IPV6]
);
cfg.set_address_family(AddressFamily::IPV6);
assert_eq!(&cfg.address_families(), &[AddressFamily::IPV6]);
cfg.add_supported_integrity(IntegrityAlgorithm::Sha256);
assert_eq!(
&cfg.supported_integrity(),
&[IntegrityAlgorithm::Sha1, IntegrityAlgorithm::Sha256]
);
cfg.set_supported_integrity(IntegrityAlgorithm::Sha256);
assert_eq!(&cfg.supported_integrity(), &[IntegrityAlgorithm::Sha256]);
for feat in [Feature::Disabled, Feature::Auto, Feature::Required] {
cfg.set_anonymous_username(feat);
assert_eq!(cfg.anonymous_username(), feat);
}
}
#[cfg(feature = "rustls")]
mod rustls {
use super::*;
#[test]
fn test_rustls_roundtrip() {
let dns = "turn.example.com";
let cfg = TurnTlsConfig::new_rustls_with_dns(dns);
drop(cfg);
let addr = "127.0.0.1:3478".parse::<SocketAddr>().unwrap();
let _cfg = TurnTlsConfig::new_rustls_with_ip(&addr.into());
}
#[test]
fn test_rustls_getter() {
let dns = "turn.example.com";
let tls = TurnTlsConfig::new_rustls_with_dns(dns);
let mut cfg = TurnConfig::new(
TransportType::Tcp,
turn_server_address(),
turn_credentials(),
);
cfg.set_tls_config(tls.clone());
let retrieved = cfg.tls_config().unwrap();
assert!(matches!(retrieved, TurnTlsConfig::Rustls(_)));
}
}
#[cfg(feature = "openssl")]
mod openssl {
use super::*;
#[test]
fn test_openssl_roundtrip() {
let _cfg = TurnTlsConfig::new_openssl(TransportType::Udp);
}
#[test]
fn test_openssl_getter() {
let tls = TurnTlsConfig::new_openssl(TransportType::Udp);
let mut cfg = TurnConfig::new(
TransportType::Udp,
turn_server_address(),
turn_credentials(),
);
cfg.set_tls_config(tls.clone());
let retrieved = cfg.tls_config().unwrap();
assert!(matches!(retrieved, TurnTlsConfig::Openssl(_)));
}
}
}