ezk_sdp_types/attributes/
candidate.rs1use crate::{ice_char, not_whitespace, probe_host6};
4use bytes::Bytes;
5use bytesstr::BytesStr;
6use internal::{ws, IResult};
7use nom::bytes::complete::{tag, take_while, take_while1, take_while_m_n};
8use nom::character::complete::digit1;
9use nom::combinator::{map, map_res};
10use nom::error::context;
11use nom::multi::many0;
12use nom::sequence::{preceded, tuple};
13use std::fmt;
14use std::net::IpAddr;
15use std::str::FromStr;
16
17#[derive(Debug, thiserror::Error)]
19#[error("failed to parse candidate")]
20pub struct InvalidCandidateParamError;
21
22#[derive(Debug, Clone, Eq, PartialEq)]
24pub enum UntaggedAddress {
25 Fqdn(BytesStr),
26 IpAddress(IpAddr),
27}
28
29impl UntaggedAddress {
30 fn parse(src: &Bytes) -> impl FnMut(&str) -> IResult<&str, Self> + '_ {
31 move |i| {
32 context(
33 "parsing untagged address",
34 map(take_while(probe_host6), |address| {
35 if let Ok(address) = IpAddr::from_str(address) {
36 UntaggedAddress::IpAddress(address)
37 } else {
38 UntaggedAddress::Fqdn(BytesStr::from_parse(src, address))
39 }
40 }),
41 )(i)
42 }
43 }
44}
45
46impl fmt::Display for UntaggedAddress {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match &self {
49 UntaggedAddress::Fqdn(str) => str.fmt(f),
50 UntaggedAddress::IpAddress(addr) => addr.fmt(f),
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
59pub struct IceCandidate {
60 pub foundation: BytesStr,
62
63 pub component: u32,
67
68 pub transport: BytesStr,
72
73 pub priority: u64,
75
76 pub address: UntaggedAddress,
78
79 pub port: u16,
81
82 pub typ: BytesStr,
91
92 pub rel_addr: Option<UntaggedAddress>,
96
97 pub rel_port: Option<u16>,
101
102 pub unknown: Vec<(BytesStr, BytesStr)>,
104}
105
106impl IceCandidate {
107 pub fn parse<'i>(src: &Bytes, i: &'i str) -> IResult<&'i str, Self> {
108 context(
109 "parsing ice candidate",
110 map_res(
111 tuple((
112 take_while_m_n(1, 32, ice_char),
114 ws((
115 map_res(digit1, FromStr::from_str),
117 take_while(not_whitespace),
119 map_res(digit1, FromStr::from_str),
121 UntaggedAddress::parse(src),
123 map_res(digit1, FromStr::from_str),
125 preceded(tag("typ"), ws((take_while1(not_whitespace),))),
127 )),
128 many0(ws((
130 take_while1(not_whitespace),
132 take_while1(not_whitespace),
134 ))),
135 )),
136 |(foundation, (component, transport, priority, address, port, type_), p_ext)| -> Result<IceCandidate, InvalidCandidateParamError> {
137 let mut unknown = vec![];
138
139 let mut rel_addr = None;
140 let mut rel_port = None;
141
142 for (key, value) in p_ext {
143 match key {
144 "raddr" => rel_addr = Some(UntaggedAddress::parse(src)(value).map_err(|_| InvalidCandidateParamError)?.1),
145 "rport" => rel_port = Some(u16::from_str(value).map_err(|_| InvalidCandidateParamError)?),
146 _ => unknown.push((
147 BytesStr::from_parse(src, key),
148 BytesStr::from_parse(src, value),
149 )),
150 }
151 }
152
153 Ok(IceCandidate {
154 foundation: BytesStr::from_parse(src, foundation),
155 component,
156 transport: BytesStr::from_parse(src, transport),
157 priority,
158 address,
159 port,
160 typ: BytesStr::from_parse(src, type_.0),
161 rel_addr,
162 rel_port,
163 unknown,
164 })
165 }
166 )
167 )(i)
168 }
169}
170
171impl fmt::Display for IceCandidate {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 write!(
174 f,
175 "{} {} {} {} {} {} typ {}",
176 self.foundation,
177 self.component,
178 self.transport,
179 self.priority,
180 self.address,
181 self.port,
182 self.typ
183 )?;
184
185 if let Some(rel_addr) = &self.rel_addr {
186 write!(f, " raddr {}", rel_addr)?;
187 }
188
189 if let Some(rel_port) = &self.rel_port {
190 write!(f, " rport {}", rel_port)?;
191 }
192
193 for (key, value) in &self.unknown {
194 write!(f, " {} {}", key, value)?;
195 }
196
197 Ok(())
198 }
199}
200
201#[cfg(test)]
202mod test {
203 use super::*;
204 use std::net::Ipv4Addr;
205
206 #[test]
207 fn candidate() {
208 let input = BytesStr::from_static(
209 "12 2 TCP 2105458942 192.168.56.1 9 typ host raddr 192.168.1.22 rport 123 tcptype active",
210 );
211
212 let (rem, candidate) = IceCandidate::parse(input.as_ref(), &input).unwrap();
213
214 assert_eq!(candidate.foundation, "12");
215 assert_eq!(candidate.component, 2);
216 assert_eq!(candidate.transport, "TCP");
217 assert_eq!(candidate.priority, 2105458942);
218 assert_eq!(
219 candidate.address,
220 UntaggedAddress::IpAddress(IpAddr::V4(Ipv4Addr::new(192, 168, 56, 1)))
221 );
222 assert_eq!(candidate.port, 9);
223 assert_eq!(candidate.typ, "host");
224 assert_eq!(
225 candidate.rel_addr,
226 Some(UntaggedAddress::IpAddress(IpAddr::V4(Ipv4Addr::new(
227 192, 168, 1, 22
228 ))))
229 );
230 assert_eq!(candidate.rel_port, Some(123));
231 assert_eq!(
232 candidate.unknown[0],
233 (
234 BytesStr::from_static("tcptype"),
235 BytesStr::from_static("active")
236 )
237 );
238
239 assert!(rem.is_empty());
240 }
241
242 #[test]
243 fn candidate_print() {
244 let candidate = IceCandidate {
245 foundation: "1".into(),
246 component: 1,
247 transport: "UDP".into(),
248 priority: 1,
249 address: UntaggedAddress::IpAddress(IpAddr::V4(Ipv4Addr::LOCALHOST)),
250 port: 9,
251 typ: "host".into(),
252 rel_addr: None,
253 rel_port: None,
254 unknown: vec![],
255 };
256
257 assert_eq!(candidate.to_string(), "1 1 UDP 1 127.0.0.1 9 typ host");
258 }
259}