turn-types 0.7.1

TURN parsing and writing
Documentation
// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

//! # turn-types
//!
//! `turn-types` provides an implementation for two main things related to TURN clients and servers:
//! 1. TURN specific STUN attributes using the [stun-types] crate.
//! 2. Parsing to and from the actual data sent and received on the wire between a TURN client and
//!    a TURN server.
//!
//! This crate implements the STUN attributes and methods presented in the following standards:
//! - [RFC5766]: Traversal Using Relays around NAT (TURN).
//! - [RFC6062]: Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations
//! - [RFC6156]: Traversal Using Relays around NAT (TURN) Extension for IPv6
//! - [RFC8656]: Traversal Using Relays around NAT (TURN): Relay Extensions to Session
//!   Traversal Utilities for NAT (STUN)
//!
//! [stun-types]: https://docs.rs/stun-types/latest/stun_types
//! [RFC5766]: https://tools.ietf.org/html/rfc5766
//! [RFC6062]: https://tools.ietf.org/html/rfc6062
//! [RFC6156]: https://tools.ietf.org/html/rfc6156
//! [RFC8656]: https://tools.ietf.org/html/rfc8656

#![no_std]

extern crate alloc;

#[cfg(any(feature = "std", test))]
extern crate std;

pub use stun_types as stun;
pub mod attribute;
pub mod channel;
pub mod message;
pub mod tcp;
pub mod transmit;

pub use stun_proto::Instant;

/// Public prelude.
pub mod prelude {
    pub use crate::transmit::DelayedTransmitBuild;
}

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use stun_types::message::{LongTermCredentials, LongTermKeyCredentials};
pub use stun_types::{AddressFamily, TransportType};

/// Initialize some debugging functionality of the library.
///
/// It is not required to call this function, however doing so allows debug functionality of
/// stun-types to print much more human readable descriptions of attributes and messages.
pub fn debug_init() {
    attribute::attributes_init();
    message::debug_init();
}

/// Credentials used for a TURN user.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TurnCredentials {
    username: String,
    password: String,
}

impl From<TurnCredentials> for LongTermCredentials {
    fn from(value: TurnCredentials) -> Self {
        LongTermCredentials::new(value.username, value.password)
    }
}

impl TurnCredentials {
    /// Transform these credentials into some `LongTermCredentials` for use in a STUN context.
    pub fn into_long_term_credentials(self, realm: &str) -> LongTermKeyCredentials {
        LongTermKeyCredentials::new(self.username, self.password, realm.to_string())
    }

    /// Construct a new set of [`TurnCredentials`]
    pub fn new(username: &str, password: &str) -> Self {
        Self {
            username: username.to_owned(),
            password: password.to_owned(),
        }
    }

    /// The username of the credentials.
    pub fn username(&self) -> &str {
        &self.username
    }

    /// The password of the credentials.
    pub fn password(&self) -> &str {
        &self.password
    }
}

#[cfg(test)]
mod tests {
    use tracing::subscriber::DefaultGuard;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::Layer;

    pub fn test_init_log() -> DefaultGuard {
        crate::debug_init();
        let level_filter = std::env::var("TURN_LOG")
            .or(std::env::var("RUST_LOG"))
            .ok()
            .and_then(|var| var.parse::<tracing_subscriber::filter::Targets>().ok())
            .unwrap_or(
                tracing_subscriber::filter::Targets::new().with_default(tracing::Level::TRACE),
            );
        let registry = tracing_subscriber::registry().with(
            tracing_subscriber::fmt::layer()
                .with_file(true)
                .with_line_number(true)
                .with_level(true)
                .with_target(false)
                .with_test_writer()
                .with_filter(level_filter),
        );
        tracing::subscriber::set_default(registry)
    }
}