// Copyright (c) 2019 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::jingle_dtls_srtp::Fingerprint; use std::net::IpAddr; generate_element!( /// Wrapper element for an ICE-UDP transport. Transport, "transport", JINGLE_ICE_UDP, attributes: [ /// A Password as defined in ICE-CORE. pwd: Option = "pwd", /// A User Fragment as defined in ICE-CORE. ufrag: Option = "ufrag", ], children: [ /// List of candidates for this ICE-UDP session. candidates: Vec = ("candidate", JINGLE_ICE_UDP) => Candidate, /// Fingerprint of the key used for the DTLS handshake. fingerprint: Option = ("fingerprint", JINGLE_DTLS) => Fingerprint ] ); impl Transport { /// Create a new ICE-UDP transport. pub fn new() -> Transport { Transport { pwd: None, ufrag: None, candidates: Vec::new(), fingerprint: None, } } /// Add a candidate to this transport. pub fn add_candidate(mut self, candidate: Candidate) -> Self { self.candidates.push(candidate); self } /// Set the DTLS-SRTP fingerprint of this transport. pub fn with_fingerprint(mut self, fingerprint: Fingerprint) -> Self { self.fingerprint = Some(fingerprint); self } } generate_attribute!( /// A Candidate Type as defined in ICE-CORE. Type, "type", { /// Host candidate. Host => "host", /// Peer reflexive candidate. Prflx => "prflx", /// Relayed candidate. Relay => "relay", /// Server reflexive candidate. Srflx => "srflx", } ); generate_element!( /// A candidate for an ICE-UDP session. Candidate, "candidate", JINGLE_ICE_UDP, attributes: [ /// A Component ID as defined in ICE-CORE. component: Required = "component", /// A Foundation as defined in ICE-CORE. foundation: Required = "foundation", /// An index, starting at 0, that enables the parties to keep track of updates to the /// candidate throughout the life of the session. generation: Required = "generation", /// A unique identifier for the candidate. id: Required = "id", /// The Internet Protocol (IP) address for the candidate transport mechanism; this can be /// either an IPv4 address or an IPv6 address. ip: Required = "ip", /// The port at the candidate IP address. port: Required = "port", /// A Priority as defined in ICE-CORE. priority: Required = "priority", /// The protocol to be used. The only value defined by this specification is "udp". protocol: Required = "protocol", /// A related address as defined in ICE-CORE. rel_addr: Option = "rel-addr", /// A related port as defined in ICE-CORE. rel_port: Option = "rel-port", /// An index, starting at 0, referencing which network this candidate is on for a given /// peer. network: Option = "network", /// A Candidate Type as defined in ICE-CORE. type_: Required = "type", ] ); #[cfg(test)] mod tests { use super::*; use crate::hashes::Algo; use crate::jingle_dtls_srtp::Setup; use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { assert_size!(Transport, 68); assert_size!(Type, 1); assert_size!(Candidate, 92); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Transport, 136); assert_size!(Type, 1); assert_size!(Candidate, 128); } #[test] fn test_gajim() { let elem: Element = " " .parse() .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.pwd.unwrap(), "wakMJ8Ydd5rqnPaFerws5o"); assert_eq!(transport.ufrag.unwrap(), "aeXX"); } #[test] fn test_jitsi_meet() { let elem: Element = " 97:F2:B5:BE:DB:A6:00:B1:3E:40:B2:41:3C:0D:FC:E0:BD:B2:A0:E8 " .parse() .unwrap(); let transport = Transport::try_from(elem).unwrap(); assert_eq!(transport.pwd.unwrap(), "7lk9uul39gckit6t02oavv2r9j"); assert_eq!(transport.ufrag.unwrap(), "2acq51d4p07v2m"); let fingerprint = transport.fingerprint.unwrap(); assert_eq!(fingerprint.hash, Algo::Sha_1); assert_eq!(fingerprint.setup, Setup::Actpass); assert_eq!( fingerprint.value, [ 151, 242, 181, 190, 219, 166, 0, 177, 62, 64, 178, 65, 60, 13, 252, 224, 189, 178, 160, 232 ] ); } #[test] fn test_serialize_transport() { let reference: Element = "02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2" .parse() .unwrap(); let elem: Element = "02:1A:CC:54:27:AB:EB:9C:53:3F:3E:4B:65:2E:7D:46:3F:54:42:CD:54:F1:7A:03:A2:7D:F9:B0:7F:46:19:B2" .parse() .unwrap(); let fingerprint = Fingerprint::try_from(elem).unwrap(); let transport = Transport { pwd: None, ufrag: None, candidates: vec![], fingerprint: Some(fingerprint), }; let serialized: Element = transport.into(); assert_eq!(serialized, reference); } }