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.
-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);