diff --git a/README.md b/README.md index eb55a2bb..8e479829 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ let mut rtc = Rtc::new(); // Add some ICE candidate such as a locally bound UDP port. let addr = "1.2.3.4:5000".parse().unwrap(); -let candidate = Candidate::host(addr).unwrap(); +let candidate = Candidate::host(addr, "udp").unwrap(); rtc.add_local_candidate(candidate); // Accept an incoming offer from the remote peer @@ -83,7 +83,7 @@ let mut rtc = Rtc::new(); // Add some ICE candidate such as a locally bound UDP port. let addr = "1.2.3.4:5000".parse().unwrap(); -let candidate = Candidate::host(addr).unwrap(); +let candidate = Candidate::host(addr, "udp").unwrap(); rtc.add_local_candidate(candidate); // Create a `SdpApi`. The change lets us make multiple changes @@ -176,6 +176,7 @@ loop { Input::Receive( Instant::now(), Receive { + proto: Protocol::Udp, source, destination: socket.local_addr().unwrap(), contents: buf.as_slice().try_into().unwrap(), @@ -220,9 +221,9 @@ let writer = rtc.writer(mid).unwrap(); let pt = writer.payload_params()[0].pt(); // Write the data -let wallclock = todo!(); // Absolute time of the data -let media_time = todo!(); // Media time, in RTP time -let data = todo!(); // Actual data +let wallclock = todo!(); // Absolute time of the data +let media_time = todo!(); // Media time, in RTP time +let data: &[u8] = todo!(); // Actual data writer.write(pt, wallclock, media_time, data).unwrap(); ``` @@ -486,4 +487,4 @@ when catching a panic. Zulip logo -License: MIT/Apache-2.0 +License: MIT OR Apache-2.0 diff --git a/examples/chat.rs b/examples/chat.rs index 5feeb1b3..20584588 100644 --- a/examples/chat.rs +++ b/examples/chat.rs @@ -17,8 +17,8 @@ use str0m::change::{SdpAnswer, SdpOffer, SdpPendingOffer}; use str0m::channel::{ChannelData, ChannelId}; use str0m::media::MediaKind; use str0m::media::{Direction, KeyframeRequest, MediaData, Mid, Rid}; -use str0m::Event; -use str0m::{net::Receive, Candidate, IceConnectionState, Input, Output, Rtc, RtcError}; +use str0m::net::Protocol; +use str0m::{net::Receive, Candidate, Event, IceConnectionState, Input, Output, Rtc, RtcError}; mod util; @@ -87,7 +87,7 @@ fn web_request(request: &Request, addr: SocketAddr, tx: SyncSender) -> Resp .build(); // Add the shared UDP socket as a host candidate - let candidate = Candidate::host(addr).expect("a host candidate"); + let candidate = Candidate::host(addr, "udp").expect("a host candidate"); rtc.add_local_candidate(candidate); // Create an SDP Answer. @@ -240,6 +240,7 @@ fn read_socket_input<'a>(socket: &UdpSocket, buf: &'a mut Vec) -> Option Response { // Spin up a UDP socket for the RTC let socket = UdpSocket::bind(format!("{addr}:0")).expect("binding a random UDP port"); let addr = socket.local_addr().expect("a local socket adddress"); - let candidate = Candidate::host(addr).expect("a host candidate"); + let candidate = Candidate::host(addr, "udp").expect("a host candidate"); rtc.add_local_candidate(candidate); // Create an SDP Answer. @@ -124,6 +124,7 @@ fn run(mut rtc: Rtc, socket: UdpSocket) -> Result<(), RtcError> { Input::Receive( Instant::now(), Receive { + proto: Protocol::Udp, source, destination: socket.local_addr().unwrap(), contents: buf.as_slice().try_into()?, diff --git a/src/ice/agent.rs b/src/ice/agent.rs index b402e70b..16efad3b 100644 --- a/src/ice/agent.rs +++ b/src/ice/agent.rs @@ -4,6 +4,7 @@ use std::time::{Duration, Instant}; use rand::random; +use crate::io::Protocol; use crate::io::{DatagramRecv, Receive, Transmit, DATAGRAM_MTU}; use crate::io::{Id, DATAGRAM_MTU_WARN}; use crate::io::{StunMessage, TransId, STUN_TIMEOUT}; @@ -75,7 +76,7 @@ pub struct IceAgent { /// Remote addresses we have seen traffic appear from. This is used /// to dedupe [`IceAgentEvent::DiscoveredRecv`]. - discovered_recv: HashSet, + discovered_recv: HashSet<(Protocol, SocketAddr)>, /// Currently nominated pair for sending. This is used to evaluate /// if we get a better candidate for [`IceAgentEvent::NominatedSend`]. @@ -88,6 +89,7 @@ pub struct IceAgent { #[derive(Debug)] struct StunRequest { now: Instant, + proto: Protocol, source: SocketAddr, destination: SocketAddr, trans_id: TransId, @@ -194,6 +196,8 @@ pub enum IceAgentEvent { /// /// For each ICE restart, the app will only receive unique addresses once. DiscoveredRecv { + // The protocol to use for the socket. + proto: Protocol, /// The remote socket to look out for. source: SocketAddr, }, @@ -204,6 +208,8 @@ pub enum IceAgentEvent { /// requiring an ICE restart. The application should always use the values /// of the last emitted event to send data. NominatedSend { + // The protocol to use for the socket. + proto: Protocol, /// The local socket address to send datagrams from. /// /// This will correspond to some local address added to @@ -443,11 +449,10 @@ impl IceAgent { // // NB this must be done _after_ set_local_preference(), since the prio() used in the // elimination is calculated from that preference. - if let Some((idx, other)) = self - .local_candidates - .iter_mut() - .enumerate() - .find(|(_, v)| v.addr() == c.addr() && v.base() == c.base()) + if let Some((idx, other)) = + self.local_candidates.iter_mut().enumerate().find(|(_, v)| { + v.addr() == c.addr() && v.base() == c.base() && v.proto() == c.proto() + }) { if c.prio() < other.prio() { // The new candidate is not better than what we already got. @@ -574,6 +579,11 @@ impl IceAgent { let local = &self.local_candidates[*local_idx]; let remote = &self.remote_candidates[*remote_idx]; + // Candidates in a pair must share the same protocol + if local.proto() != remote.proto() { + continue 'outer; + } + let prio = CandidatePair::calculate_prio(self.controlling, remote.prio(), local.prio()); let mut pair = CandidatePair::new(*local_idx, *remote_idx, prio); @@ -590,8 +600,10 @@ impl IceAgent { let check_local = check.local_candidate(&self.local_candidates); let check_remote = check.remote_candidate(&self.remote_candidates); - let redundant = - local.base() == check_local.base() && remote.addr() == check_remote.addr(); + let redundant = local.base() == check_local.base() + && remote.addr() == check_remote.addr() + && local.proto() == check_local.proto() + && remote.proto() == check_remote.proto(); if redundant { if check.prio() >= pair.prio() { @@ -795,12 +807,15 @@ impl IceAgent { } if message.is_binding_request() { - self.stun_server_handle_message(now, r.source, r.destination, message); + self.stun_server_handle_message(now, r.proto, r.source, r.destination, message); } else if message.is_successful_binding_response() { self.stun_client_handle_response(now, message); } - self.emit_event(IceAgentEvent::DiscoveredRecv { source: r.source }); + self.emit_event(IceAgentEvent::DiscoveredRecv { + proto: r.proto, + source: r.source, + }); // TODO handle unsuccessful responses. } @@ -960,8 +975,8 @@ impl IceAgent { } fn emit_event(&mut self, event: IceAgentEvent) { - if let IceAgentEvent::DiscoveredRecv { source } = event { - if !self.discovered_recv.insert(source) { + if let IceAgentEvent::DiscoveredRecv { proto, source } = event { + if !self.discovered_recv.insert((proto, source)) { // we already dispatched this discovered return; } @@ -985,6 +1000,7 @@ impl IceAgent { fn stun_server_handle_message( &mut self, now: Instant, + proto: Protocol, source: SocketAddr, destination: SocketAddr, message: StunMessage, @@ -1008,6 +1024,7 @@ impl IceAgent { // credentials, we extract all relevant bits of information so it can be owned. let req = StunRequest { now, + proto, source, destination, trans_id, @@ -1075,7 +1092,7 @@ impl IceAgent { .remote_candidates .iter() .enumerate() - .find(|(_, c)| !c.discarded() && c.addr() == req.source); + .find(|(_, c)| !c.discarded() && c.proto() == req.proto && c.addr() == req.source); let remote_idx = if let Some((idx, _)) = found_in_remote { trace!("Remote candidate for STUN request found"); @@ -1089,6 +1106,7 @@ impl IceAgent { // candidate exchanges contain this peer-reflexive candidate, it will // signal the actual foundation for the candidate. let c = Candidate::peer_reflexive( + req.proto, req.source, req.source, req.prio, @@ -1124,7 +1142,7 @@ impl IceAgent { // received through a relay). The local candidate can never be a // server-reflexive candidate. matches!(v.kind(), CandidateKind::Host | CandidateKind::Relayed) - && v.addr() == req.destination + && v.addr() == req.destination && v.proto() == req.proto }) // Receiving traffic for an IP address that neither is a HOST nor RELAY is a configuration // fault. We need to be aware of the interfaces that the ice agent is connected to. @@ -1172,6 +1190,7 @@ impl IceAgent { .unwrap(); let local = pair.local_candidate(&self.local_candidates); + let proto = local.proto(); let local_addr = local.base(); let remote = pair.remote_candidate(&self.remote_candidates); let remote_addr = remote.addr(); @@ -1208,6 +1227,7 @@ impl IceAgent { buf.truncate(n); let trans = Transmit { + proto, source: local_addr, destination: remote_addr, contents: buf.into(), @@ -1254,6 +1274,7 @@ impl IceAgent { buf.truncate(n); let trans = Transmit { + proto: local.proto(), source: local.base(), destination: remote.addr(), contents: buf.into(), @@ -1329,6 +1350,7 @@ impl IceAgent { // o The type is peer reflexive. let candidate = Candidate::peer_reflexive( + local_sent_from.proto(), mapped_address, base, prio, @@ -1416,6 +1438,7 @@ impl IceAgent { self.nominated_send = Some(best); self.emit_event(IceAgentEvent::NominatedSend { + proto: local.proto(), source: local.base(), destination: remote.addr(), }) @@ -1534,10 +1557,10 @@ mod test { fn local_preference_host() { let mut agent = IceAgent::new(); - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); - agent.add_local_candidate(Candidate::host(ipv6_1()).unwrap()); - agent.add_local_candidate(Candidate::host(ipv6_2()).unwrap()); - agent.add_local_candidate(Candidate::host(ipv4_2()).unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); + agent.add_local_candidate(Candidate::host(ipv6_1(), "udp").unwrap()); + agent.add_local_candidate(Candidate::host(ipv6_2(), "udp").unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_2(), "udp").unwrap()); let v: Vec<_> = agent .local_candidates @@ -1555,14 +1578,32 @@ mod test { // Frequently, a server-reflexive candidate and a host candidate will be // redundant when the agent is not behind a NAT. - let x2 = agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); + let x2 = agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); assert!(x2); // this is redundant given we have the direct host candidate above. - let x1 = agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_1(), ipv4_1())); + let x1 = agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_1(), ipv4_1(), "udp")); assert!(!x1); } + #[test] + fn discard_adding_redundant_by_address_and_protocol() { + let mut agent = IceAgent::new(); + + // Candidates with the same SocketAddr but different protocols are considered distinct. + assert!(agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap())); + assert!(agent.add_local_candidate(Candidate::host(ipv4_1(), "tcp").unwrap())); + assert!(agent.add_local_candidate(Candidate::host(ipv4_1(), "ssltcp").unwrap())); + + // Verify these are rejected, since these tuples of address and protocol have been added. + assert!(!agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap())); + assert!(!agent.add_local_candidate(Candidate::host(ipv4_1(), "ssltcp").unwrap())); + + // Verify these are allowed, since these have different addresses. + assert!(agent.add_local_candidate(Candidate::host(ipv4_2(), "udp").unwrap())); + assert!(agent.add_local_candidate(Candidate::host(ipv4_2(), "ssltcp").unwrap())); + } + #[test] fn discard_already_added_redundant() { let mut agent = IceAgent::new(); @@ -1571,10 +1612,10 @@ mod test { // redundant when the agent is not behind a NAT. // this is contrived, but it is redundant when we add the host candidate below. - let x1 = agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_1(), ipv4_1())); + let x1 = agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_1(), ipv4_1(), "udp")); assert!(x1); - let x2 = agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); + let x2 = agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); assert!(x2); let v: Vec<_> = agent @@ -1591,51 +1632,66 @@ mod test { let mut agent = IceAgent::new(); // local 0 - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); - // local 1 - agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_4(), ipv4_2())); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); + // local 1 "udp" + agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_4(), ipv4_2(), "udp")); + // local 2 "tcp" + agent.add_local_candidate(Candidate::host(ipv4_1(), "tcp").unwrap()); // remote 0 - agent.add_remote_candidate(Candidate::test_peer_rflx(ipv4_4(), ipv4_3())); - // remote 1 - agent.add_remote_candidate(Candidate::host(ipv4_3()).unwrap()); + agent.add_remote_candidate(Candidate::test_peer_rflx(ipv4_4(), ipv4_3(), "udp")); + // remote 1 "udp" + agent.add_remote_candidate(Candidate::host(ipv4_3(), "udp").unwrap()); + // remote 2 "tcp" + agent.add_remote_candidate(Candidate::host(ipv4_3(), "tcp").unwrap()); // we expect: - // (host host) - (0, 1) - // (host rflx) - (0, 1) - // (rflx host) - (1, 1) - // (rflx rflx) - (1, 0) - - assert_eq!(agent.pair_indexes(), [(0, 1), (0, 0), (1, 1), (1, 0)]); + // (host/udp host/udp) - (0, 1) + // (host/udp rflx/udp) - (0, 1) + // (rflx/udp host/udp) - (1, 1) + // (rflx/udp rflx/udp) - (1, 0) + // (host/tcp host/tcp) - (2, 2) + + assert_eq!( + agent.pair_indexes(), + [(0, 1), (0, 0), (1, 1), (1, 0), (2, 2)] + ); } #[test] fn form_pairs_skip_redundant() { let mut agent = IceAgent::new(); - agent.add_remote_candidate(Candidate::host(ipv4_3()).unwrap()); - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); + agent.add_remote_candidate(Candidate::host(ipv4_3(), "udp").unwrap()); + agent.add_remote_candidate(Candidate::host(ipv4_3(), "tcp").unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); + // the UDP candidates should be pair up. assert_eq!(agent.pair_indexes(), [(0, 0)]); - // this local candidate is redundant an won't form a new pair. - agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_2(), ipv4_1())); + // this local UDP candidate is redundant an won't form a new pair. + agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_2(), ipv4_1(), "udp")); assert_eq!(agent.pair_indexes(), [(0, 0)]); + + // this local TCP candidate will be paired up (This is the 3rd local candidate) + agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_2(), ipv4_1(), "tcp")); + + assert_eq!(agent.pair_indexes(), [(0, 0), (2, 1)]); } #[test] fn form_pairs_replace_redundant() { let mut agent = IceAgent::new(); - agent.add_remote_candidate(Candidate::host(ipv4_3()).unwrap()); - agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_2(), ipv4_1())); + agent.add_remote_candidate(Candidate::host(ipv4_3(), "udp").unwrap()); + agent.add_local_candidate(Candidate::test_peer_rflx(ipv4_2(), ipv4_1(), "udp")); assert_eq!(agent.pair_indexes(), [(0, 0)]); // this local candidate is redundant, but has higher priority than then existing pair. // it replaces the existing pair. - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); assert_eq!(agent.pair_indexes(), [(1, 0)]); } @@ -1663,6 +1719,7 @@ mod test { // This is just prepping the test, this would have been discovered in a STUN packet. let c = Candidate::peer_reflexive( + "udp", ipv4_3(), ipv4_3(), 123, @@ -1671,7 +1728,7 @@ mod test { ); agent.add_remote_candidate(c); - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); assert_eq!(agent.pair_indexes(), [(0, 0)]); @@ -1680,7 +1737,7 @@ mod test { agent.candidate_pairs[0].increase_remote_binding_requests(now); // this remote should replace the "discovered" peer reflexive added above. - agent.add_remote_candidate(Candidate::host(ipv4_3()).unwrap()); + agent.add_remote_candidate(Candidate::host(ipv4_3(), "udp").unwrap()); // The index should not have changed, since we replaced the peer reflexive remote candidate. assert_eq!(agent.pair_indexes(), [(0, 0)]); @@ -1694,8 +1751,8 @@ mod test { #[test] fn poll_time_must_timing_advance() { let mut agent = IceAgent::new(); - agent.add_local_candidate(Candidate::host(ipv4_1()).unwrap()); - agent.add_remote_candidate(Candidate::host(ipv4_3()).unwrap()); + agent.add_local_candidate(Candidate::host(ipv4_1(), "udp").unwrap()); + agent.add_remote_candidate(Candidate::host(ipv4_3(), "udp").unwrap()); let now1 = Instant::now(); agent.handle_timeout(now1); diff --git a/src/ice/candidate.rs b/src/ice/candidate.rs index 62935cc7..fc194aea 100644 --- a/src/ice/candidate.rs +++ b/src/ice/candidate.rs @@ -3,6 +3,8 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::net::{IpAddr, SocketAddr}; +use crate::io::Protocol; + use super::IceError; /// ICE candidates are network addresses used to connect to a peer. @@ -32,7 +34,7 @@ pub struct Candidate { component_id: u16, // 1 for RTP, 2 for RTCP /// Protocol for the candidate. - proto: String, // "udp" or "tcp" + proto: Protocol, /// Priority. /// @@ -80,7 +82,7 @@ pub struct Candidate { impl fmt::Debug for Candidate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Candidate({}={}", self.kind, self.addr)?; + write!(f, "Candidate({}={}/{}", self.kind, self.addr, self.proto)?; if let Some(base) = self.base { if base != self.addr { write!(f, " base={base}")?; @@ -102,7 +104,7 @@ impl Candidate { fn new( foundation: Option, component_id: u16, - proto: String, + proto: Protocol, prio: Option, addr: SocketAddr, base: Option, @@ -130,7 +132,7 @@ impl Candidate { pub fn parsed( foundation: String, component_id: u16, - proto: String, + proto: Protocol, prio: u32, addr: SocketAddr, kind: CandidateKind, @@ -153,7 +155,7 @@ impl Candidate { /// Creates a host ICE candidate. /// /// Host candidates are local sockets directly on the host. - pub fn host(addr: SocketAddr) -> Result { + pub fn host(addr: SocketAddr, proto: impl TryInto) -> Result { if !is_valid_ip(addr.ip()) { return Err(IceError::BadCandidate(format!("invalid ip {}", addr.ip()))); } @@ -161,7 +163,7 @@ impl Candidate { Ok(Candidate::new( None, 1, // only RTP - "udp".into(), + parse_proto(proto)?, None, addr, Some(addr), @@ -175,7 +177,10 @@ impl Candidate { /// /// Server reflexive candidates are local sockets mapped to external ip discovered /// via a STUN binding request. - pub fn server_reflexive(addr: SocketAddr) -> Result { + pub fn server_reflexive( + addr: SocketAddr, + proto: impl TryInto, + ) -> Result { if !is_valid_ip(addr.ip()) { return Err(IceError::BadCandidate(format!("invalid ip {}", addr.ip()))); } @@ -183,7 +188,7 @@ impl Candidate { Ok(Candidate::new( None, 1, // only RTP - "udp".into(), + parse_proto(proto)?, None, addr, Some(addr), @@ -197,7 +202,7 @@ impl Candidate { /// /// Relayed candidates are server sockets relaying traffic to a local socket. /// Allocate a TURN addr to use as a local candidate. - pub fn relayed(addr: SocketAddr, network_type: &str) -> Result { + pub fn relayed(addr: SocketAddr, proto: impl TryInto) -> Result { if !is_valid_ip(addr.ip()) { return Err(IceError::BadCandidate(format!("invalid ip {}", addr.ip()))); } @@ -205,7 +210,7 @@ impl Candidate { Ok(Candidate::new( None, 1, // only RTP - network_type.into(), + parse_proto(proto)?, None, addr, Some(addr), @@ -221,6 +226,7 @@ impl Candidate { /// binding responses. `addr` is the discovered address. `base` is the local /// (host) address inside the NAT we used to get this response. pub(crate) fn peer_reflexive( + proto: impl TryInto, addr: SocketAddr, base: SocketAddr, prio: u32, @@ -230,7 +236,7 @@ impl Candidate { Candidate::new( found, 1, // only RTP - "udp".into(), + parse_proto(proto).expect("internal call to have correct protocol"), Some(prio), addr, Some(base), @@ -241,11 +247,15 @@ impl Candidate { } #[cfg(test)] - pub(crate) fn test_peer_rflx(addr: SocketAddr, base: SocketAddr) -> Self { + pub(crate) fn test_peer_rflx( + addr: SocketAddr, + base: SocketAddr, + proto: impl TryInto, + ) -> Self { Candidate::new( None, 1, // only RTP - "udp".into(), + parse_proto(proto).expect("internal test to have correct protocol"), None, addr, Some(base), @@ -307,18 +317,26 @@ impl Candidate { return *prio; } - // The RECOMMENDED values for type preferences are 126 for host - // candidates, 110 for peer-reflexive candidates, 100 for server- - // reflexive candidates, and 0 for relayed candidates. - let type_preference = if as_prflx { - 110 + let kind = if as_prflx { + CandidateKind::PeerReflexive } else { - match self.kind { - CandidateKind::Host => 126, - CandidateKind::PeerReflexive => 110, - CandidateKind::ServerReflexive => 100, - CandidateKind::Relayed => 0, - } + self.kind + }; + + // Per RFC5245 Sec. 4.1.2.1, the RECOMMENDED values for type preferences are + // 126 for host candidates, 110 for peer-reflexive candidates, 100 for + // server-reflexive candidates, and 0 for relayed candidates. The variations + // for non-UDP protocols are taken from libwebrtc: + // + let type_preference = match (kind, self.proto) { + (CandidateKind::Host, Protocol::Udp) => 126, + (CandidateKind::PeerReflexive, Protocol::Udp) => 110, + (CandidateKind::ServerReflexive, _) => 100, + (CandidateKind::Host, _) => 90, + (CandidateKind::PeerReflexive, _) => 80, + (CandidateKind::Relayed, Protocol::Udp) => 2, + (CandidateKind::Relayed, Protocol::Tcp) => 1, + (CandidateKind::Relayed, _) => 0, }; // The recommended formula combines a preference for the candidate type @@ -355,8 +373,8 @@ impl Candidate { /// Returns a reference to the String containing the transport protocol of /// the ICE candidate. For example tcp/udp/.. - pub fn proto(&self) -> &String { - &self.proto + pub fn proto(&self) -> Protocol { + self.proto } pub(crate) fn base(&self) -> SocketAddr { @@ -397,6 +415,12 @@ impl Candidate { } } +fn parse_proto(proto: impl TryInto) -> Result { + proto + .try_into() + .map_err(|_| IceError::BadCandidate("invalid protocol".into())) +} + /// Type of candidate. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CandidateKind { diff --git a/src/ice/mod.rs b/src/ice/mod.rs index 56b82632..98fbb5c9 100644 --- a/src/ice/mod.rs +++ b/src/ice/mod.rs @@ -30,10 +30,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("1.1.1.1:9999"); // 9999 is just dropped by propagate + let c1 = host("1.1.1.1:9999", "udp"); // 9999 is just dropped by propagate a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); a1.set_controlling(true); @@ -74,6 +74,7 @@ mod test { use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; + use crate::io::Protocol; use crate::io::Receive; use tracing::Span; @@ -82,8 +83,8 @@ mod test { s.parse().unwrap() } - pub fn host(s: impl Into) -> Candidate { - Candidate::host(sock(s)).unwrap() + pub fn host(s: impl Into, proto: impl TryInto) -> Candidate { + Candidate::host(sock(s), proto).unwrap() } /// Transform the socket to rig different test scenarios. @@ -139,10 +140,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("1.1.1.1:1000"); + let c1 = host("1.1.1.1:1000", "udp"); a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); @@ -210,10 +211,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("1.1.1.1:1000"); + let c1 = host("1.1.1.1:1000", "udp"); a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); a1.set_controlling(true); @@ -257,10 +258,10 @@ mod test { // a1 acts as "server" a1.agent.set_ice_lite(true); - let c1 = host("1.1.1.1:9999"); // 9999 is just dropped by propagate + let c1 = host("1.1.1.1:9999", "udp"); // 9999 is just dropped by propagate a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); a1.set_controlling(true); @@ -305,10 +306,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("3.3.3.3:1000"); // will be rewritten to 4.4.4.4 + let c1 = host("3.3.3.3:1000", "udp"); // will be rewritten to 4.4.4.4 a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); @@ -366,10 +367,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("1.1.1.1:1000"); + let c1 = host("1.1.1.1:1000", "udp"); a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1.clone()); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2.clone()); @@ -417,10 +418,10 @@ mod test { let mut a1 = TestAgent::new(info_span!("L")); let mut a2 = TestAgent::new(info_span!("R")); - let c1 = host("3.3.3.3:9999"); // no traffic possible + let c1 = host("3.3.3.3:9999", "udp"); // no traffic possible a1.add_local_candidate(c1.clone()); a2.add_remote_candidate(c1); - let c2 = host("2.2.2.2:1000"); + let c2 = host("2.2.2.2:1000", "udp"); a2.add_local_candidate(c2.clone()); a1.add_remote_candidate(c2); @@ -433,7 +434,7 @@ mod test { } // "trickle" a possible candidate - a1.add_local_candidate(host("1.1.1.1:1000")); // possible + a1.add_local_candidate(host("1.1.1.1:1000", "udp")); // possible // loop until we're connected. loop { diff --git a/src/io/mod.rs b/src/io/mod.rs index 202024f0..47e7d4cb 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -47,8 +47,26 @@ pub enum NetError { Io(#[from] io::Error), } +/// Type of protocol used in [`Transmit`] and [`Receive`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Protocol { + /// UDP + Udp, + /// TCP (See RFC 4571 for framing) + Tcp, + /// TCP with fixed SSL Hello Exchange + /// See AsyncSSLServerSocket implementation for exchange details: + /// + SslTcp, + /// TLS (only used via relay) + Tls, +} + /// An outgoing packet pub struct Transmit { + /// This protocol the socket is using. + pub proto: Protocol, + /// The source socket this packet should be sent from. /// /// For ICE it's important to match up outgoing packets with source network interface. @@ -80,6 +98,9 @@ impl From for Vec { #[derive(Debug)] /// Received incoming data. pub struct Receive<'a> { + /// The protocol the socket this received data originated from is using. + pub proto: Protocol, + /// The socket this received data originated from. pub source: SocketAddr, @@ -93,12 +114,14 @@ pub struct Receive<'a> { impl<'a> Receive<'a> { /// Creates a new instance by trying to parse the contents of `buf`. pub fn new( + proto: Protocol, source: SocketAddr, destination: SocketAddr, buf: &'a [u8], ) -> Result { let contents = DatagramRecv::try_from(buf)?; Ok(Receive { + proto, source, destination, contents, @@ -183,6 +206,7 @@ impl<'a> TryFrom<&'a Transmit> for Receive<'a> { fn try_from(t: &'a Transmit) -> Result { Ok(Receive { + proto: t.proto, source: t.source, destination: t.destination, contents: DatagramRecv::try_from(&t.contents[..])?, @@ -219,3 +243,36 @@ impl fmt::Debug for DatagramRecv<'_> { } // } + +impl TryFrom<&str> for Protocol { + type Error = (); + + fn try_from(proto: &str) -> Result { + let proto = proto.to_lowercase(); + match proto.as_str() { + "udp" => Ok(Protocol::Udp), + "tcp" => Ok(Protocol::Tcp), + "ssltcp" => Ok(Protocol::SslTcp), + "tls" => Ok(Protocol::Tls), + _ => Err(()), + } + } +} + +impl From for &str { + fn from(proto: Protocol) -> Self { + match proto { + Protocol::Udp => "udp", + Protocol::Tcp => "tcp", + Protocol::SslTcp => "ssltcp", + Protocol::Tls => "tls", + } + } +} + +impl fmt::Display for Protocol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let x: &str = (*self).into(); + write!(f, "{}", x) + } +} diff --git a/src/lib.rs b/src/lib.rs index 1672f334..cc8be125 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ //! //! // Add some ICE candidate such as a locally bound UDP port. //! let addr = "1.2.3.4:5000".parse().unwrap(); -//! let candidate = Candidate::host(addr).unwrap(); +//! let candidate = Candidate::host(addr, "udp").unwrap(); //! rtc.add_local_candidate(candidate); //! //! // Accept an incoming offer from the remote peer @@ -84,7 +84,7 @@ //! //! // Add some ICE candidate such as a locally bound UDP port. //! let addr = "1.2.3.4:5000".parse().unwrap(); -//! let candidate = Candidate::host(addr).unwrap(); +//! let candidate = Candidate::host(addr, "udp").unwrap(); //! rtc.add_local_candidate(candidate); //! //! // Create a `SdpApi`. The change lets us make multiple changes @@ -114,7 +114,7 @@ //! //! ```no_run //! # use str0m::{Rtc, Output, IceConnectionState, Event, Input}; -//! # use str0m::net::Receive; +//! # use str0m::net::{Receive, Protocol}; //! # use std::io::ErrorKind; //! # use std::net::UdpSocket; //! # use std::time::Instant; @@ -183,6 +183,7 @@ //! Input::Receive( //! Instant::now(), //! Receive { +//! proto: Protocol::Udp, //! source, //! destination: socket.local_addr().unwrap(), //! contents: buf.as_slice().try_into().unwrap(), @@ -230,9 +231,9 @@ //! let pt = writer.payload_params()[0].pt(); //! //! // Write the data -//! let wallclock = todo!(); // Absolute time of the data -//! let media_time = todo!(); // Media time, in RTP time -//! let data: Vec = todo!(); // Actual data +//! let wallclock = todo!(); // Absolute time of the data +//! let media_time = todo!(); // Media time, in RTP time +//! let data: &[u8] = todo!(); // Actual data //! writer.write(pt, wallclock, media_time, data).unwrap(); //! ``` //! @@ -444,7 +445,7 @@ //! //! ## Panics, Errors and unwraps //! -//! Rust adheres to [fail-last][ff]. That means rather than brushing state +//! Rust adheres to [fail-fast][ff]. That means rather than brushing state //! bugs under the carpet, it panics. We make a distinction between errors and //! bugs. //! @@ -603,7 +604,7 @@ mod streams; /// Network related types to get socket data in/out of [`Rtc`]. pub mod net { - pub use crate::io::{DatagramRecv, DatagramSend, Receive, Transmit}; + pub use crate::io::{DatagramRecv, DatagramSend, Protocol, Receive, Transmit}; } /// Various error types. @@ -757,6 +758,7 @@ pub struct Rtc { } struct SendAddr { + proto: net::Protocol, source: SocketAddr, destination: SocketAddr, } @@ -1001,7 +1003,7 @@ impl Rtc { /// let mut rtc = Rtc::new(); /// /// let a = "127.0.0.1:5000".parse().unwrap(); - /// let c = Candidate::host(a).unwrap(); + /// let c = Candidate::host(a, "udp").unwrap(); /// /// rtc.add_local_candidate(c); /// ``` @@ -1024,7 +1026,7 @@ impl Rtc { /// let mut rtc = Rtc::new(); /// /// let a = "1.2.3.4:5000".parse().unwrap(); - /// let c = Candidate::host(a).unwrap(); + /// let c = Candidate::host(a, "udp").unwrap(); /// /// rtc.add_remote_candidate(c); /// ``` @@ -1198,22 +1200,24 @@ impl Rtc { IceAgentEvent::IceConnectionStateChange(v) => { return Ok(Output::Event(Event::IceConnectionStateChange(v))) } - IceAgentEvent::DiscoveredRecv { source } => { - info!("ICE remote address: {:?}", source); + IceAgentEvent::DiscoveredRecv { proto, source } => { + info!("ICE remote address: {:?}/{:?}", source, proto); self.remote_addrs.push(source); while self.remote_addrs.len() > 20 { self.remote_addrs.remove(0); } } IceAgentEvent::NominatedSend { + proto, source, destination, } => { info!( - "ICE nominated send from: {:?} to: {:?}", - source, destination + "ICE nominated send from: {:?} to: {:?} with protocol {:?}", + source, destination, proto, ); self.send_addr = Some(SendAddr { + proto, source, destination, }); @@ -1332,6 +1336,7 @@ impl Rtc { if let Some(contents) = datagram { let t = net::Transmit { + proto: send.proto, source: send.source, destination: send.destination, contents, diff --git a/src/sdp/parser.rs b/src/sdp/parser.rs index e6507926..02ef13e4 100644 --- a/src/sdp/parser.rs +++ b/src/sdp/parser.rs @@ -242,7 +242,11 @@ where .map_err(StreamErrorFor::::message_format) }), token(' '), - not_sp(), + not_sp().and_then(|s| { + s.as_str().try_into().map_err(|_| { + StreamErrorFor::::message_format(format!("invalid protocol: {}", s)) + }) + }), token(' '), not_sp().and_then(|s| { s.parse::() diff --git a/tests/bidirectional.rs b/tests/bidirectional.rs index ad120d5d..b2723b02 100644 --- a/tests/bidirectional.rs +++ b/tests/bidirectional.rs @@ -16,8 +16,8 @@ pub fn bidirectional_same_m_line() -> Result<(), RtcError> { let mut l = TestRtc::new(info_span!("L")); let mut r = TestRtc::new(info_span!("R")); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/common.rs b/tests/common.rs index 73c1815b..487cb46c 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -10,6 +10,7 @@ use rand::Rng; use str0m::change::SdpApi; use str0m::format::Codec; use str0m::format::PayloadParams; +use str0m::net::Protocol; use str0m::net::Receive; use str0m::rtp::ExtensionMap; use str0m::rtp::RtpHeader; @@ -81,6 +82,7 @@ pub fn progress(l: &mut TestRtc, r: &mut TestRtc) -> Result<(), RtcError> { let input = Input::Receive( f.last, Receive { + proto: v.proto, source: v.source, destination: v.destination, contents: (&*data).try_into()?, @@ -121,6 +123,7 @@ pub fn progress_with_loss(l: &mut TestRtc, r: &mut TestRtc, loss: f32) -> Result let input = Input::Receive( f.last, Receive { + proto: v.proto, source: v.source, destination: v.destination, contents: (&*data).try_into()?, @@ -217,8 +220,8 @@ pub fn connect_l_r() -> (TestRtc, TestRtc) { let mut l = TestRtc::new_with_rtc(info_span!("L"), rtc1); let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc2); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into()).unwrap(); - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into()).unwrap(); + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp").unwrap(); + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp").unwrap(); l.add_local_candidate(host1.clone()); l.add_remote_candidate(host2.clone()); r.add_local_candidate(host2); diff --git a/tests/contiguous.rs b/tests/contiguous.rs index 4cf7311a..83947f56 100644 --- a/tests/contiguous.rs +++ b/tests/contiguous.rs @@ -18,8 +18,8 @@ pub fn contiguous_all_the_way() -> Result<(), RtcError> { let mut l = TestRtc::new(info_span!("L")); let mut r = TestRtc::new(info_span!("R")); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); @@ -114,8 +114,8 @@ pub fn not_contiguous() -> Result<(), RtcError> { let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc_r); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/data-channel-direct.rs b/tests/data-channel-direct.rs index f0307d4c..c2d6852f 100644 --- a/tests/data-channel-direct.rs +++ b/tests/data-channel-direct.rs @@ -17,8 +17,8 @@ pub fn data_channel_direct() -> Result<(), RtcError> { let rtc_r = RtcConfig::new().set_ice_lite(true).build(); let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc_r); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1.clone()); l.add_remote_candidate(host2.clone()); r.add_local_candidate(host2); diff --git a/tests/data-channel.rs b/tests/data-channel.rs index 0de4f9f4..49d32226 100644 --- a/tests/data-channel.rs +++ b/tests/data-channel.rs @@ -14,8 +14,8 @@ pub fn data_channel() -> Result<(), RtcError> { let mut l = TestRtc::new(info_span!("L")); let mut r = TestRtc::new(info_span!("R")); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/flappy-ice-state.rs b/tests/flappy-ice-state.rs index 21832823..f6f39c1a 100644 --- a/tests/flappy-ice-state.rs +++ b/tests/flappy-ice-state.rs @@ -17,8 +17,8 @@ pub fn flappy_ice_lite_state() -> Result<(), RtcError> { let rtc = RtcConfig::new().set_ice_lite(true).build(); let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/ice-restart.rs b/tests/ice-restart.rs index 915a6a06..f885979e 100644 --- a/tests/ice-restart.rs +++ b/tests/ice-restart.rs @@ -17,8 +17,8 @@ pub fn ice_restart() -> Result<(), RtcError> { let rtc = RtcConfig::new().set_ice_lite(true).build(); let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/stats.rs b/tests/stats.rs index 0937b2f8..1b74fbbc 100644 --- a/tests/stats.rs +++ b/tests/stats.rs @@ -20,8 +20,8 @@ pub fn stats() -> Result<(), RtcError> { let mut l = TestRtc::new_with_rtc(info_span!("L"), l_config.build()); let mut r = TestRtc::new_with_rtc(info_span!("R"), r_config.build()); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/twcc.rs b/tests/twcc.rs index d52fd91f..37dba6e4 100644 --- a/tests/twcc.rs +++ b/tests/twcc.rs @@ -19,8 +19,8 @@ pub fn twcc() -> Result<(), RtcError> { let mut l = TestRtc::new_with_rtc(info_span!("L"), l_rtc); let mut r = TestRtc::new_with_rtc(info_span!("R"), r_rtc); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/unidirectional-r-create-media.rs b/tests/unidirectional-r-create-media.rs index 942fc15f..3c7ef9a7 100644 --- a/tests/unidirectional-r-create-media.rs +++ b/tests/unidirectional-r-create-media.rs @@ -17,8 +17,8 @@ pub fn unidirectional_r_create_media() -> Result<(), RtcError> { let mut l = TestRtc::new(info_span!("L")); let mut r = TestRtc::new(info_span!("R")); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/unidirectional.rs b/tests/unidirectional.rs index c6daf809..364a94ca 100644 --- a/tests/unidirectional.rs +++ b/tests/unidirectional.rs @@ -16,8 +16,8 @@ pub fn unidirectional() -> Result<(), RtcError> { let mut l = TestRtc::new(info_span!("L")); let mut r = TestRtc::new(info_span!("R")); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2); diff --git a/tests/user-rtp-header-extension.rs b/tests/user-rtp-header-extension.rs index 13b26f5e..547315d0 100644 --- a/tests/user-rtp-header-extension.rs +++ b/tests/user-rtp-header-extension.rs @@ -80,8 +80,8 @@ pub fn user_rtp_header_extension() -> Result<(), RtcError> { let mut l = TestRtc::new_with_rtc(info_span!("L"), rtc_l); let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc_r); - let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into())?; - let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into())?; + let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?; + let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?; l.add_local_candidate(host1); r.add_local_candidate(host2);