1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum SipViaError {
9 Empty,
11 InvalidFormat(String),
13}
14
15impl fmt::Display for SipViaError {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 Self::Empty => write!(f, "Via header is empty"),
19 Self::InvalidFormat(msg) => write!(f, "Invalid Via format: {}", msg),
20 }
21 }
22}
23
24impl std::error::Error for SipViaError {}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28#[non_exhaustive]
29pub struct SipViaEntry {
30 protocol_name: String,
31 protocol_version: String,
32 transport: String,
33 host: String,
34 port: Option<u16>,
35 params: Vec<(String, Option<String>)>,
36 rport: Option<Option<u16>>,
37}
38
39impl SipViaEntry {
40 pub fn protocol(&self) -> &str {
42 &self.protocol_name
43 }
44
45 pub fn version(&self) -> &str {
47 &self.protocol_version
48 }
49
50 pub fn transport(&self) -> &str {
52 &self.transport
53 }
54
55 pub fn host(&self) -> &str {
57 &self.host
58 }
59
60 pub fn port(&self) -> Option<u16> {
62 self.port
63 }
64
65 pub fn params(&self) -> &[(String, Option<String>)] {
67 &self.params
68 }
69
70 pub fn param(&self, key: &str) -> Option<Option<&str>> {
72 let key_lower = key.to_ascii_lowercase();
73 self.params
74 .iter()
75 .find(|(k, _)| k == &key_lower)
76 .map(|(_, v)| v.as_deref())
77 }
78
79 pub fn branch(&self) -> Option<&str> {
81 self.param("branch")
82 .flatten()
83 }
84
85 pub fn received(&self) -> Option<&str> {
87 self.param("received")
88 .flatten()
89 }
90
91 pub fn rport(&self) -> Option<Option<u16>> {
100 self.rport
101 }
102
103 fn parse(entry: &str) -> Result<Self, SipViaError> {
104 let trimmed = entry.trim();
105 if trimmed.is_empty() {
106 return Err(SipViaError::InvalidFormat("empty Via entry".to_string()));
107 }
108
109 let (main_part, params_part) = if let Some(semi_idx) = trimmed.find(';') {
111 (&trimmed[..semi_idx], Some(&trimmed[semi_idx + 1..]))
112 } else {
113 (trimmed, None)
114 };
115
116 let parts: Vec<&str> = main_part
118 .split_whitespace()
119 .collect();
120 if parts.len() != 2 {
121 return Err(SipViaError::InvalidFormat(format!(
122 "expected 'protocol/version/transport host[:port]', got '{}'",
123 main_part
124 )));
125 }
126
127 let sent_protocol = parts[0];
128 let sent_by = parts[1];
129
130 let protocol_parts: Vec<&str> = sent_protocol
132 .split('/')
133 .collect();
134 if protocol_parts.len() != 3 {
135 return Err(SipViaError::InvalidFormat(format!(
136 "expected 'protocol/version/transport', got '{}'",
137 sent_protocol
138 )));
139 }
140
141 let protocol_name = protocol_parts[0].to_string();
142 let protocol_version = protocol_parts[1].to_string();
143 let transport = protocol_parts[2].to_string();
144
145 let (host, port) = parse_host_port(sent_by)?;
148
149 let mut params = Vec::new();
151 if let Some(params_str) = params_part {
152 for param in params_str.split(';') {
153 let param = param.trim();
154 if param.is_empty() {
155 continue;
156 }
157
158 if let Some(eq_idx) = param.find('=') {
159 let key = param[..eq_idx]
160 .trim()
161 .to_ascii_lowercase();
162 let value = param[eq_idx + 1..]
163 .trim()
164 .to_string();
165 params.push((key, Some(value)));
166 } else {
167 params.push((param.to_ascii_lowercase(), None));
169 }
170 }
171 }
172
173 let rport = params
174 .iter()
175 .find(|(k, _)| k == "rport")
176 .map(|(_, v)| match v {
177 None => Ok(None),
178 Some(s) => s
179 .parse::<u16>()
180 .map(Some)
181 .map_err(|_| SipViaError::InvalidFormat(format!("invalid rport value: {s}"))),
182 })
183 .transpose()?;
184
185 Ok(Self {
186 protocol_name,
187 protocol_version,
188 transport,
189 host,
190 port,
191 params,
192 rport,
193 })
194 }
195}
196
197impl fmt::Display for SipViaEntry {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(
200 f,
201 "{}/{}/{}",
202 self.protocol_name, self.protocol_version, self.transport
203 )?;
204
205 if self
207 .host
208 .contains(':')
209 && !self
210 .host
211 .starts_with('[')
212 {
213 write!(f, " [{}]", self.host)?;
214 } else {
215 write!(f, " {}", self.host)?;
216 }
217
218 if let Some(port) = self.port {
219 write!(f, ":{}", port)?;
220 }
221
222 for (key, value) in &self.params {
223 if let Some(val) = value {
224 write!(f, ";{}={}", key, val)?;
225 } else {
226 write!(f, ";{}", key)?;
227 }
228 }
229
230 Ok(())
231 }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
236#[non_exhaustive]
237pub struct SipVia {
238 entries: Vec<SipViaEntry>,
239}
240
241impl SipVia {
242 pub fn parse(raw: &str) -> Result<Self, SipViaError> {
244 let raw = raw.trim();
245 if raw.is_empty() {
246 return Err(SipViaError::Empty);
247 }
248 Self::from_entries(crate::split_comma_entries(raw))
249 }
250
251 pub fn from_entries<'a>(
253 entries: impl IntoIterator<Item = &'a str>,
254 ) -> Result<Self, SipViaError> {
255 let entries = entries
256 .into_iter()
257 .map(SipViaEntry::parse)
258 .collect::<Result<Vec<_>, _>>()?;
259 if entries.is_empty() {
260 return Err(SipViaError::Empty);
261 }
262 Ok(Self { entries })
263 }
264
265 pub fn entries(&self) -> &[SipViaEntry] {
267 &self.entries
268 }
269
270 pub fn into_entries(self) -> Vec<SipViaEntry> {
272 self.entries
273 }
274
275 pub fn len(&self) -> usize {
277 self.entries
278 .len()
279 }
280
281 pub fn is_empty(&self) -> bool {
283 self.entries
284 .is_empty()
285 }
286}
287
288impl fmt::Display for SipVia {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 crate::fmt_joined(f, &self.entries, ", ")
291 }
292}
293
294impl_from_str_via_parse!(SipVia, SipViaError);
295
296impl IntoIterator for SipVia {
297 type Item = SipViaEntry;
298 type IntoIter = std::vec::IntoIter<SipViaEntry>;
299
300 fn into_iter(self) -> Self::IntoIter {
301 self.entries
302 .into_iter()
303 }
304}
305
306impl<'a> IntoIterator for &'a SipVia {
307 type Item = &'a SipViaEntry;
308 type IntoIter = std::slice::Iter<'a, SipViaEntry>;
309
310 fn into_iter(self) -> Self::IntoIter {
311 self.entries
312 .iter()
313 }
314}
315
316fn parse_host_port(sent_by: &str) -> Result<(String, Option<u16>), SipViaError> {
317 if sent_by.starts_with('[') {
319 if let Some(close_bracket) = sent_by.find(']') {
321 let host = sent_by[1..close_bracket].to_string();
322 let remainder = &sent_by[close_bracket + 1..];
323
324 if remainder.is_empty() {
325 return Ok((host, None));
326 }
327
328 if let Some(port_str) = remainder.strip_prefix(':') {
329 let port = port_str
330 .parse::<u16>()
331 .map_err(|_| {
332 SipViaError::InvalidFormat(format!("invalid port: {}", port_str))
333 })?;
334 return Ok((host, Some(port)));
335 }
336
337 return Err(SipViaError::InvalidFormat(format!(
338 "unexpected characters after IPv6 address: {}",
339 remainder
340 )));
341 } else {
342 return Err(SipViaError::InvalidFormat(
343 "unclosed IPv6 bracket".to_string(),
344 ));
345 }
346 }
347
348 if let Some(colon_idx) = sent_by.rfind(':') {
351 let host = sent_by[..colon_idx].to_string();
352 let port_str = &sent_by[colon_idx + 1..];
353
354 if host.contains(':') {
356 return Ok((sent_by.to_string(), None));
358 }
359
360 let port = port_str
361 .parse::<u16>()
362 .map_err(|_| SipViaError::InvalidFormat(format!("invalid port: {}", port_str)))?;
363 Ok((host, Some(port)))
364 } else {
365 Ok((sent_by.to_string(), None))
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn test_single_via() {
375 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060").unwrap();
376 assert_eq!(via.len(), 1);
377
378 let entry = &via.entries()[0];
379 assert_eq!(entry.protocol(), "SIP");
380 assert_eq!(entry.version(), "2.0");
381 assert_eq!(entry.transport(), "UDP");
382 assert_eq!(entry.host(), "198.51.100.1");
383 assert_eq!(entry.port(), Some(5060));
384 assert!(entry
385 .params()
386 .is_empty());
387 }
388
389 #[test]
390 fn test_multiple_vias() {
391 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060, SIP/2.0/TCP 203.0.113.5").unwrap();
392 assert_eq!(via.len(), 2);
393
394 let entry1 = &via.entries()[0];
395 assert_eq!(entry1.host(), "198.51.100.1");
396 assert_eq!(entry1.port(), Some(5060));
397 assert_eq!(entry1.transport(), "UDP");
398
399 let entry2 = &via.entries()[1];
400 assert_eq!(entry2.host(), "203.0.113.5");
401 assert_eq!(entry2.port(), None);
402 assert_eq!(entry2.transport(), "TCP");
403 }
404
405 #[test]
406 fn test_via_with_params() {
407 let via = SipVia::parse(
408 "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bKnashds8;received=203.0.113.10;rport=5061",
409 )
410 .unwrap();
411
412 let entry = &via.entries()[0];
413 assert_eq!(entry.branch(), Some("z9hG4bKnashds8"));
414 assert_eq!(entry.received(), Some("203.0.113.10"));
415 assert_eq!(entry.rport(), Some(Some(5061)));
416 }
417
418 #[test]
419 fn test_via_with_rport_no_value() {
420 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060;rport").unwrap();
421
422 let entry = &via.entries()[0];
423 assert_eq!(entry.rport(), Some(None));
424 }
425
426 #[test]
427 fn test_via_without_rport() {
428 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060").unwrap();
429
430 let entry = &via.entries()[0];
431 assert_eq!(entry.rport(), None);
432 }
433
434 #[test]
435 fn test_via_ipv6() {
436 let via = SipVia::parse("SIP/2.0/UDP [2001:db8::1]:5060").unwrap();
437
438 let entry = &via.entries()[0];
439 assert_eq!(entry.host(), "2001:db8::1");
440 assert_eq!(entry.port(), Some(5060));
441 }
442
443 #[test]
444 fn test_via_ipv6_no_port() {
445 let via = SipVia::parse("SIP/2.0/UDP [2001:db8::1]").unwrap();
446
447 let entry = &via.entries()[0];
448 assert_eq!(entry.host(), "2001:db8::1");
449 assert_eq!(entry.port(), None);
450 }
451
452 #[test]
453 fn test_via_hostname() {
454 let via = SipVia::parse("SIP/2.0/TLS example.com:5061").unwrap();
455
456 let entry = &via.entries()[0];
457 assert_eq!(entry.host(), "example.com");
458 assert_eq!(entry.port(), Some(5061));
459 assert_eq!(entry.transport(), "TLS");
460 }
461
462 #[test]
463 fn test_empty_via() {
464 let result = SipVia::parse("");
465 assert!(matches!(result, Err(SipViaError::Empty)));
466 }
467
468 #[test]
469 fn test_empty_via_whitespace() {
470 let result = SipVia::parse(" ");
471 assert!(matches!(result, Err(SipViaError::Empty)));
472 }
473
474 #[test]
475 fn test_invalid_format() {
476 let result = SipVia::parse("invalid");
477 assert!(matches!(result, Err(SipViaError::InvalidFormat(_))));
478 }
479
480 #[test]
481 fn test_rport_invalid_value_is_error() {
482 let result = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060;rport=garbage");
483 assert!(result.is_err());
484 }
485
486 #[test]
487 fn test_display_roundtrip() {
488 let original =
489 "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bKnashds8;received=203.0.113.10;rport";
490 let via = SipVia::parse(original).unwrap();
491 let displayed = via.to_string();
492
493 let reparsed = SipVia::parse(&displayed).unwrap();
494 assert_eq!(via, reparsed);
495 }
496
497 #[test]
498 fn test_display_multiple_vias() {
499 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060, SIP/2.0/TCP 203.0.113.5").unwrap();
500 let displayed = via.to_string();
501 assert!(displayed.contains("198.51.100.1"));
502 assert!(displayed.contains("203.0.113.5"));
503 }
504
505 #[test]
506 fn test_into_iterator() {
507 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060, SIP/2.0/TCP 203.0.113.5").unwrap();
508
509 let mut count = 0;
510 for entry in &via {
511 assert!(entry.host() == "198.51.100.1" || entry.host() == "203.0.113.5");
512 count += 1;
513 }
514 assert_eq!(count, 2);
515
516 let entries: Vec<_> = via
517 .into_iter()
518 .collect();
519 assert_eq!(entries.len(), 2);
520 }
521
522 #[test]
523 fn test_into_entries() {
524 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060, SIP/2.0/TCP 203.0.113.5").unwrap();
525 let entries = via.into_entries();
526 assert_eq!(entries.len(), 2);
527 assert_eq!(entries[0].host(), "198.51.100.1");
528 assert_eq!(entries[1].host(), "203.0.113.5");
529 }
530
531 #[test]
532 fn test_from_str() {
533 let via: SipVia = "SIP/2.0/UDP 198.51.100.1:5060"
534 .parse()
535 .unwrap();
536 assert_eq!(via.len(), 1);
537 }
538
539 #[test]
540 fn test_param_case_insensitive() {
541 let via = SipVia::parse("SIP/2.0/UDP 198.51.100.1:5060;Branch=test").unwrap();
542 let entry = &via.entries()[0];
543 assert_eq!(entry.param("branch"), Some(Some("test")));
544 assert_eq!(entry.param("BRANCH"), Some(Some("test")));
545 }
546
547 #[test]
548 fn test_display_ipv6() {
549 let via = SipVia::parse("SIP/2.0/UDP [2001:db8::1]:5060").unwrap();
550 let displayed = via.to_string();
551 assert!(displayed.contains("[2001:db8::1]"));
552 }
553
554 #[test]
555 fn from_entries_matches_parse() {
556 let a = "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK1";
557 let b = "SIP/2.0/TCP 203.0.113.5";
558 let split = SipVia::from_entries([a, b]).unwrap();
559 let joined = SipVia::parse(&format!("{a}, {b}")).unwrap();
560 assert_eq!(split, joined);
561 assert_eq!(split.to_string(), format!("{a}, {b}"));
562 }
563
564 #[test]
565 fn from_entries_bad_entry_is_error() {
566 assert!(matches!(
567 SipVia::from_entries(["SIP/2.0/UDP 198.51.100.1", "invalid"]),
568 Err(SipViaError::InvalidFormat(_))
569 ));
570 }
571
572 #[test]
573 fn from_entries_empty_is_empty_error() {
574 assert!(matches!(
575 SipVia::from_entries(std::iter::empty::<&str>()),
576 Err(SipViaError::Empty)
577 ));
578 }
579}