Skip to main content

rtc_shared/
util.rs

1//! Shared helpers: packet demultiplexing and random strings.
2//!
3//! WebRTC multiplexes STUN, DTLS and SRTP onto one port, so the first byte of a datagram decides
4//! which layer receives it ([RFC 7983]). The `match_*` predicates implement those ranges, and
5//! [`is_rtcp`](crate::util::is_rtcp) separates RTCP from RTP once a packet is known to be one of the two.
6//!
7//! [RFC 7983]: https://datatracker.ietf.org/doc/html/rfc7983
8use crate::error::{Error, Result};
9use rand::{RngExt, rng};
10use std::net::{SocketAddr, ToSocketAddrs};
11
12// match_range is a MatchFunc that accepts packets with the first byte in [lower..upper]
13fn match_range(lower: u8, upper: u8) -> impl Fn(&[u8]) -> bool {
14    move |buf: &[u8]| -> bool {
15        if buf.is_empty() {
16            return false;
17        }
18        let b = buf[0];
19        b >= lower && b <= upper
20    }
21}
22
23/// MatchFuncs as described in RFC7983
24/// <https://tools.ietf.org/html/rfc7983>
25///              +----------------+
26///              |        [0..3] -+--> forward to STUN
27///              |                |
28///              |      [16..19] -+--> forward to ZRTP
29///              |                |
30///  packet -->  |      [20..63] -+--> forward to DTLS
31///              |                |
32///              |      [64..79] -+--> forward to TURN Channel
33///              |                |
34///              |    [128..191] -+--> forward to RTP/RTCP
35///              +----------------+
36/// match_dtls is a MatchFunc that accepts packets with the first byte in [20..63]
37/// as defied in RFC7983
38pub fn match_dtls(b: &[u8]) -> bool {
39    match_range(20, 63)(b)
40}
41
42/// Returns `true` if `b` looks like SRTP or SRTCP: its first byte is in `[128, 191]`.
43///
44/// One of the demultiplexing predicates from [RFC 7983], which is how a single port can carry
45/// STUN, DTLS and SRTP at once.
46///
47/// [RFC 7983]: https://datatracker.ietf.org/doc/html/rfc7983
48pub fn match_srtp_or_srtcp(b: &[u8]) -> bool {
49    match_range(128, 191)(b)
50}
51
52/// Returns `true` if `buf` is RTCP rather than RTP.
53///
54/// Distinguished by the payload-type byte: RTCP packet types occupy `[192, 223]`, which RTP
55/// cannot use. Returns `false` for buffers too short to tell.
56pub fn is_rtcp(buf: &[u8]) -> bool {
57    // Not long enough to determine RTP/RTCP
58    if buf.len() < 4 {
59        return false;
60    }
61
62    let rtcp_packet_type = buf[1];
63    (192..=223).contains(&rtcp_packet_type)
64}
65
66/// match_srtp is a MatchFunc that only matches SRTP and not SRTCP
67pub fn match_srtp(buf: &[u8]) -> bool {
68    match_srtp_or_srtcp(buf) && !is_rtcp(buf)
69}
70
71/// match_srtcp is a MatchFunc that only matches SRTCP and not SRTP
72pub fn match_srtcp(buf: &[u8]) -> bool {
73    match_srtp_or_srtcp(buf) && is_rtcp(buf)
74}
75
76/// lookup host to SocketAddr
77pub fn lookup_host<T>(use_ipv4: bool, host: T) -> Result<SocketAddr>
78where
79    T: ToSocketAddrs,
80{
81    for remote_addr in host.to_socket_addrs()? {
82        if (use_ipv4 && remote_addr.is_ipv4()) || (!use_ipv4 && remote_addr.is_ipv6()) {
83            return Ok(remote_addr);
84        }
85    }
86
87    Err(Error::ErrAddressParseFailed)
88}
89
90const RUNES_ALPHA: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
91const RUNES_ALPHA_NUMBER: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
92
93/// math_rand_alpha generates a mathematical random alphabet sequence of the requested length.
94pub fn math_rand_alpha(n: usize) -> String {
95    generate_crypto_random_string(n, RUNES_ALPHA)
96}
97
98/// math_rand_alpha generates a mathematical random alphabet and number sequence of the requested length.
99pub fn math_rand_alpha_number(n: usize) -> String {
100    generate_crypto_random_string(n, RUNES_ALPHA_NUMBER)
101}
102
103//TODO: generates a random string for cryptographic usage.
104/// Generates a random `n`-character string drawn from `runes`.
105///
106/// Used for values that must be unguessable, such as ICE credentials and SDP identifiers.
107pub fn generate_crypto_random_string(n: usize, runes: &[u8]) -> String {
108    let mut rng = rng();
109
110    let rand_string: String = (0..n)
111        .map(|_| {
112            let idx = rng.random_range(0..runes.len());
113            runes[idx] as char
114        })
115        .collect();
116
117    rand_string
118}