1use std::net::{IpAddr, SocketAddr};
4use std::sync::Arc;
5
6use sipx_sip::headers::Via;
7use sipx_sip::transaction::Reliability;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum TransportKind {
12 Udp,
14 Tcp,
16 Tls,
18 Ws,
20 Wss,
22 Quic,
24}
25
26impl TransportKind {
27 #[must_use]
29 pub fn reliability(self) -> Reliability {
30 match self {
31 Self::Udp => Reliability::Unreliable,
32 _ => Reliability::Reliable,
33 }
34 }
35
36 #[must_use]
38 pub fn as_str(self) -> &'static str {
39 match self {
40 Self::Udp => "UDP",
41 Self::Tcp => "TCP",
42 Self::Tls => "TLS",
43 Self::Ws => "WS",
44 Self::Wss => "WSS",
45 Self::Quic => "QUIC",
46 }
47 }
48
49 #[must_use]
55 pub fn is_secure(self) -> bool {
56 matches!(self, Self::Tls | Self::Wss | Self::Quic)
57 }
58
59 #[must_use]
61 pub fn default_port(self) -> u16 {
62 match self {
63 Self::Udp | Self::Tcp => 5060,
64 Self::Tls | Self::Quic => 5061,
65 Self::Ws => 80,
66 Self::Wss => 443,
67 }
68 }
69
70 #[must_use]
72 pub fn parse(token: &[u8]) -> Option<Self> {
73 match token.to_ascii_uppercase().as_slice() {
74 b"UDP" => Some(Self::Udp),
75 b"TCP" => Some(Self::Tcp),
76 b"TLS" => Some(Self::Tls),
77 b"WS" => Some(Self::Ws),
78 b"WSS" => Some(Self::Wss),
79 b"QUIC" => Some(Self::Quic),
80 _ => None,
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Hash)]
92pub struct Target {
93 pub addr: SocketAddr,
95 pub transport: TransportKind,
97 pub verify_as: Option<Arc<str>>,
100 pub path: Option<Arc<str>>,
103}
104
105impl Target {
106 #[must_use]
108 pub fn new(addr: SocketAddr, transport: TransportKind) -> Self {
109 Self {
110 addr,
111 transport,
112 verify_as: None,
113 path: None,
114 }
115 }
116
117 #[must_use]
119 pub fn udp(addr: SocketAddr) -> Self {
120 Self::new(addr, TransportKind::Udp)
121 }
122
123 #[must_use]
128 pub fn verifying(mut self, name: impl AsRef<str>) -> Self {
129 self.verify_as = Some(Arc::from(name.as_ref()));
130 self
131 }
132
133 #[must_use]
145 pub fn at_path(mut self, path: impl AsRef<str>) -> Self {
146 let path = path.as_ref();
147 self.path = Some(if path.starts_with('/') {
148 Arc::from(path)
149 } else {
150 Arc::from(format!("/{path}"))
151 });
152 self
153 }
154
155 #[must_use]
157 pub fn connection(&self) -> ConnectionKey {
158 ConnectionKey {
159 peer: self.addr,
160 transport: self.transport,
161 identity: self.verify_as.clone(),
162 path: if matches!(self.transport, TransportKind::Ws | TransportKind::Wss) {
163 self.path.clone()
164 } else {
165 None
166 },
167 }
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub struct ConnectionKey {
190 pub peer: SocketAddr,
192 pub transport: TransportKind,
194 pub identity: Option<Arc<str>>,
196 pub path: Option<Arc<str>>,
198}
199
200impl ConnectionKey {
201 #[must_use]
204 pub fn new(peer: SocketAddr, transport: TransportKind) -> Self {
205 Self {
206 peer,
207 transport,
208 identity: None,
209 path: None,
210 }
211 }
212
213 #[must_use]
218 pub fn ws_path(&self) -> &str {
219 self.path.as_deref().unwrap_or("/")
220 }
221}
222
223#[must_use]
230pub fn response_destination(via: &Via, source: SocketAddr, transport: TransportKind) -> Target {
231 if let Some(maddr) = via.maddr()
233 && let Some(addr) = parse_host(maddr)
234 {
235 let port = via.port.unwrap_or_else(|| transport.default_port());
236 return Target::new(SocketAddr::new(addr, port), transport);
237 }
238
239 let observed_port = via
244 .rport()
245 .flatten()
246 .and_then(|v| std::str::from_utf8(v).ok())
247 .and_then(|v| v.parse::<u16>().ok());
248
249 if let Some(received) = via.received()
251 && let Some(addr) = parse_host(received)
252 {
253 let port = observed_port
254 .or(via.port)
255 .unwrap_or_else(|| transport.default_port());
256 return Target::new(SocketAddr::new(addr, port), transport);
257 }
258
259 if let sipx_sip::Host::Ip(ip) = &via.host {
261 let port = observed_port
262 .or(via.port)
263 .unwrap_or_else(|| transport.default_port());
264 return Target::new(SocketAddr::new(*ip, port), transport);
265 }
266
267 Target::new(source, transport)
270}
271
272fn parse_host(raw: &[u8]) -> Option<IpAddr> {
273 std::str::from_utf8(raw).ok()?.parse().ok()
274}
275
276#[cfg(test)]
277#[allow(
278 clippy::unwrap_used,
279 clippy::expect_used,
280 clippy::panic,
281 clippy::indexing_slicing
282)]
283mod tests {
284 use super::*;
285
286 fn via(text: &str) -> Via {
287 Via::parse_one(text.as_bytes()).expect("a valid Via")
288 }
289
290 fn source() -> SocketAddr {
291 "203.0.113.9:41234".parse().expect("a valid address")
292 }
293
294 #[test]
295 fn a_plain_via_goes_to_its_sent_by() {
296 let target = response_destination(
297 &via("SIP/2.0/UDP 192.0.2.1:5060;branch=z9hG4bKx"),
298 source(),
299 TransportKind::Udp,
300 );
301 assert_eq!(target.addr.to_string(), "192.0.2.1:5060");
302 }
303
304 #[test]
305 fn a_sent_by_without_a_port_uses_the_transport_default() {
306 assert_eq!(
307 response_destination(
308 &via("SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKx"),
309 source(),
310 TransportKind::Udp
311 )
312 .addr
313 .port(),
314 5060
315 );
316 assert_eq!(
317 response_destination(
318 &via("SIP/2.0/TLS 192.0.2.1;branch=z9hG4bKx"),
319 source(),
320 TransportKind::Tls
321 )
322 .addr
323 .port(),
324 5061
325 );
326 }
327
328 #[test]
334 fn an_observed_rport_is_used_even_without_a_received() {
335 let target = response_destination(
336 &via("SIP/2.0/UDP 203.0.113.9:5060;rport=41234;branch=z9hG4bKx"),
337 source(),
338 TransportKind::Udp,
339 );
340 assert_eq!(target.addr.to_string(), "203.0.113.9:41234");
341 }
342
343 #[test]
347 fn received_and_rport_override_the_sent_by() {
348 let target = response_destination(
349 &via("SIP/2.0/UDP 10.0.0.5:5060;received=203.0.113.9;rport=41234;branch=z9hG4bKx"),
350 source(),
351 TransportKind::Udp,
352 );
353 assert_eq!(target.addr.to_string(), "203.0.113.9:41234");
354 }
355
356 #[test]
357 fn received_without_rport_uses_the_sent_by_port() {
358 let target = response_destination(
359 &via("SIP/2.0/UDP 10.0.0.5:5070;received=203.0.113.9;branch=z9hG4bKx"),
360 source(),
361 TransportKind::Udp,
362 );
363 assert_eq!(target.addr.to_string(), "203.0.113.9:5070");
364 }
365
366 #[test]
367 fn maddr_wins_over_everything() {
368 let target = response_destination(
369 &via("SIP/2.0/UDP 10.0.0.5:5060;maddr=192.0.2.99;received=203.0.113.9;branch=z9hG4bKx"),
370 source(),
371 TransportKind::Udp,
372 );
373 assert_eq!(target.addr.ip().to_string(), "192.0.2.99");
374 }
375
376 #[test]
379 fn a_hostname_sent_by_falls_back_to_the_source() {
380 let target = response_destination(
381 &via("SIP/2.0/UDP client.example.com;branch=z9hG4bKx"),
382 source(),
383 TransportKind::Udp,
384 );
385 assert_eq!(target.addr, source());
386 }
387
388 #[test]
389 fn transports_have_their_rfc_default_ports() {
390 assert_eq!(TransportKind::Udp.default_port(), 5060);
391 assert_eq!(TransportKind::Tcp.default_port(), 5060);
392 assert_eq!(TransportKind::Tls.default_port(), 5061);
393 assert_eq!(TransportKind::Ws.default_port(), 80);
394 assert_eq!(TransportKind::Wss.default_port(), 443);
395 assert_eq!(TransportKind::Quic.default_port(), 5061);
396 }
397
398 fn peer() -> SocketAddr {
399 "127.0.0.1:8088".parse().expect("a valid address")
400 }
401
402 #[test]
405 fn a_target_asks_for_the_root_unless_it_says_otherwise() {
406 let target = Target::new(peer(), TransportKind::Ws);
407 assert_eq!(target.path, None);
408 assert_eq!(target.connection().ws_path(), "/");
409 }
410
411 #[test]
412 fn a_target_can_name_the_resource_it_wants() {
413 let target = Target::new(peer(), TransportKind::Ws).at_path("/ws");
414 assert_eq!(target.path.as_deref(), Some("/ws"));
415 assert_eq!(target.connection().ws_path(), "/ws");
416 }
417
418 #[test]
422 fn a_resource_name_missing_its_leading_slash_gets_one() {
423 for named in ["ws", "/ws"] {
424 assert_eq!(
425 Target::new(peer(), TransportKind::Ws)
426 .at_path(named)
427 .connection()
428 .ws_path(),
429 "/ws"
430 );
431 }
432 assert_eq!(
433 Target::new(peer(), TransportKind::Ws)
434 .at_path("")
435 .connection()
436 .ws_path(),
437 "/",
438 "naming nothing is naming the root"
439 );
440 }
441
442 #[test]
446 fn two_resources_on_one_address_are_two_connections() {
447 let one = Target::new(peer(), TransportKind::Ws).at_path("/ws");
448 let other = Target::new(peer(), TransportKind::Ws).at_path("/sip");
449 assert_ne!(one.connection(), other.connection());
450 assert_ne!(
451 one.connection(),
452 Target::new(peer(), TransportKind::Ws).connection(),
453 "the root is a resource like any other"
454 );
455 }
456
457 #[test]
458 fn only_udp_is_unreliable() {
459 assert_eq!(TransportKind::Udp.reliability(), Reliability::Unreliable);
460 for t in [
461 TransportKind::Tcp,
462 TransportKind::Tls,
463 TransportKind::Ws,
464 TransportKind::Wss,
465 TransportKind::Quic,
466 ] {
467 assert_eq!(t.reliability(), Reliability::Reliable);
468 }
469 }
470
471 #[test]
472 fn quic_is_a_secure_reliable_via_transport() {
473 assert_eq!(TransportKind::parse(b"QUIC"), Some(TransportKind::Quic));
474 assert_eq!(TransportKind::Quic.as_str(), "QUIC");
475 assert!(TransportKind::Quic.is_secure());
476 }
477
478 #[test]
479 fn quic_pool_keys_keep_verified_names_and_transports_separate() {
480 let one = Target::new(peer(), TransportKind::Quic)
481 .verifying("one.example")
482 .connection();
483 let two = Target::new(peer(), TransportKind::Quic)
484 .verifying("two.example")
485 .connection();
486 let tls = Target::new(peer(), TransportKind::Tls)
487 .verifying("one.example")
488 .connection();
489 assert_ne!(one, two, "Q15: two authenticated names are two connections");
490 assert_ne!(one, tls, "Q16: QUIC and TLS cannot share a connection");
491 }
492
493 #[test]
494 fn quic_pool_keys_never_include_a_websocket_resource() {
495 let plain = Target::new(peer(), TransportKind::Quic)
496 .verifying("one.example")
497 .connection();
498 let with_irrelevant_path = Target::new(peer(), TransportKind::Quic)
499 .verifying("one.example")
500 .at_path("/ws")
501 .connection();
502 assert_eq!(plain, with_irrelevant_path);
503 assert_eq!(plain.path, None);
504 }
505}