1use rsipstack::transport::SipAddr;
2use arc_swap::ArcSwap;
3use rsipstack::rsip::{headers::ToTypedHeader, prelude::HeadersExt};
4use rsipstack::{
5 transaction::endpoint::MessageInspector,
6};
7use std::{net::IpAddr, sync::Arc};
8
9pub type SharedPublicAddress = Arc<ArcSwap<rsipstack::rsip::HostWithPort>>;
10
11pub fn normalize_transport(transport: Option<&rsipstack::rsip::Transport>) -> rsipstack::rsip::Transport {
12 transport.cloned().unwrap_or(rsipstack::rsip::Transport::Udp)
13}
14
15pub fn transport_for_uri(uri: &rsipstack::rsip::Uri) -> rsipstack::rsip::Transport {
16 if matches!(uri.scheme, Some(rsipstack::rsip::Scheme::Sips)) {
17 return rsipstack::rsip::Transport::Tls;
18 }
19
20 uri.params
21 .iter()
22 .find_map(|param| match param {
23 rsipstack::rsip::Param::Transport(transport) => Some(transport.clone()),
24 _ => None,
25 })
26 .unwrap_or(rsipstack::rsip::Transport::Udp)
27}
28
29pub fn find_local_addr_for_uri(addrs: &[SipAddr], uri: &rsipstack::rsip::Uri) -> Option<SipAddr> {
30 let transport = transport_for_uri(uri);
31 addrs.iter()
32 .find(|addr| normalize_transport(addr.r#type.as_ref()) == transport)
33 .cloned()
34}
35
36pub fn contact_needs_public_resolution(contact: &rsipstack::rsip::Uri) -> bool {
37 if contact.scheme.is_none() {
38 return true;
39 }
40
41 match &contact.host_with_port.host {
42 rsipstack::rsip::Host::Domain(domain) => {
43 let host = domain.to_string();
44 host.eq_ignore_ascii_case("localhost")
45 }
46 rsipstack::rsip::Host::IpAddr(ip) => is_local_or_unspecified(ip),
47 }
48}
49
50pub fn build_contact_uri(
51 local_addr: &SipAddr,
52 learned_addr: Option<rsipstack::rsip::HostWithPort>,
53 username: Option<&str>,
54 template: Option<&rsipstack::rsip::Uri>,
55) -> rsipstack::rsip::Uri {
56 let mut uri = template
57 .cloned()
58 .unwrap_or_else(|| rsipstack::rsip::Uri::from(local_addr));
59
60 uri.host_with_port = learned_addr.unwrap_or_else(|| local_addr.addr.clone());
61 if uri.scheme.is_none() {
62 uri.scheme = Some(match local_addr.r#type {
63 Some(rsipstack::rsip::Transport::Tls)
64 | Some(rsipstack::rsip::Transport::Wss)
65 | Some(rsipstack::rsip::Transport::TlsSctp) => rsipstack::rsip::Scheme::Sips,
66 _ => rsipstack::rsip::Scheme::Sip,
67 });
68 }
69
70 if uri.auth.is_none() {
71 if let Some(username) = username.filter(|value| !value.is_empty()) {
72 uri.auth = Some(rsipstack::rsip::Auth {
73 user: username.to_string(),
74 password: None,
75 });
76 }
77 }
78
79 uri
80}
81
82pub fn build_contact(
83 local_addr: &SipAddr,
84 contact_address: Option<rsipstack::rsip::HostWithPort>,
85 username: Option<&str>,
86 template: Option<&rsipstack::rsip::Uri>,
87) -> rsipstack::rsip::typed::Contact {
88 let contact_uri = build_contact_uri(local_addr, contact_address, username, template);
89 rsipstack::rsip::typed::Contact {
90 display_name: None,
91 uri: contact_uri,
92 params: vec![],
93 }
94}
95
96pub fn build_public_contact_uri(
97 learned_public_address: &SharedPublicAddress,
98 auto_learn_public_address: bool,
99 local_addr: &SipAddr,
100 username: Option<&str>,
101 template: Option<&rsipstack::rsip::Uri>,
102) -> rsipstack::rsip::Uri {
103 let selected_addr = if auto_learn_public_address
104 && normalize_transport(local_addr.r#type.as_ref()) == rsipstack::rsip::Transport::Udp
105 {
106 Some(learned_public_address.load_full().as_ref().clone())
107 } else {
108 Some(local_addr.addr.clone())
109 };
110 build_contact_uri(local_addr, selected_addr, username, template)
111}
112
113pub struct LearningMessageInspector {
114 learned_public_address: SharedPublicAddress,
115 next: Option<Box<dyn MessageInspector>>,
116}
117
118impl LearningMessageInspector {
119 pub fn new(
120 initial_address: rsipstack::rsip::HostWithPort,
121 next: Option<Box<dyn MessageInspector>>,
122 ) -> Self {
123 Self {
124 learned_public_address: Arc::new(ArcSwap::from_pointee(initial_address)),
125 next,
126 }
127 }
128
129 pub fn shared_public_address(&self) -> SharedPublicAddress {
130 self.learned_public_address.clone()
131 }
132}
133
134impl MessageInspector for LearningMessageInspector {
135 fn before_send(&self, msg: rsipstack::rsip::SipMessage, dest: Option<&SipAddr>) -> rsipstack::rsip::SipMessage {
136 if let Some(next) = &self.next {
137 next.before_send(msg, dest)
138 } else {
139 msg
140 }
141 }
142
143 fn after_received(&self, msg: rsipstack::rsip::SipMessage, from: &SipAddr) -> rsipstack::rsip::SipMessage {
144 if let rsipstack::rsip::SipMessage::Response(response) = &msg
145 && let Ok(via) = response.via_header()
146 && let Ok(via) = via.typed()
147 && via.transport == rsipstack::rsip::Transport::Udp
148 && let Some(host_with_port) = response.via_received()
149 {
150 self.learned_public_address
151 .rcu(|previous: &Arc<rsipstack::rsip::HostWithPort>| {
152 if should_update_address(previous.as_ref(), &host_with_port) {
153 Arc::new(host_with_port.clone())
154 } else {
155 previous.clone()
156 }
157 });
158 }
159
160 if let Some(next) = &self.next {
161 next.after_received(msg, from)
162 } else {
163 msg
164 }
165 }
166}
167
168pub fn should_update_address(
169 previous: &rsipstack::rsip::HostWithPort,
170 current: &rsipstack::rsip::HostWithPort,
171) -> bool {
172 if previous == current {
173 return false;
174 }
175
176 let previous_is_public = is_public_address(previous);
177 let current_is_public = is_public_address(current);
178 (!previous_is_public && current_is_public)
179 || (previous_is_public && current_is_public && previous != current)
180}
181
182fn is_public_address(host_with_port: &rsipstack::rsip::HostWithPort) -> bool {
183 match &host_with_port.host {
184 rsipstack::rsip::Host::Domain(domain) => !domain.to_string().eq_ignore_ascii_case("localhost"),
185 rsipstack::rsip::Host::IpAddr(ip) => !is_local_or_unspecified(ip),
186 }
187}
188
189fn is_local_or_unspecified(ip: &IpAddr) -> bool {
190 ip.is_loopback() || ip.is_unspecified()
191}
192
193#[cfg(test)]
194mod tests {
195 use super::{
196 SharedPublicAddress, build_contact, build_contact_uri, build_public_contact_uri,
197 contact_needs_public_resolution, find_local_addr_for_uri, should_update_address,
198 transport_for_uri,
199 };
200 use arc_swap::ArcSwap;
201 use rsipstack::rsip::transport::Transport;
202 use rsipstack::transaction::endpoint::MessageInspector;
203 use rsipstack::transport::SipAddr;
204 use std::sync::Arc;
205
206 #[test]
207 fn learns_public_address_from_response_via() {
208 let response: rsipstack::rsip::Response = concat!(
209 "SIP/2.0 401 Unauthorized\r\n",
210 "Via: SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK-1;received=203.0.113.10;rport=62000\r\n",
211 "Content-Length: 0\r\n",
212 "\r\n"
213 )
214 .try_into()
215 .unwrap();
216
217 let inspector = super::LearningMessageInspector::new(
218 "127.0.0.1:5060"
219 .parse::<std::net::SocketAddr>()
220 .unwrap()
221 .into(),
222 None,
223 );
224 let cache = inspector.shared_public_address();
225 inspector.after_received(
226 rsipstack::rsip::SipMessage::Response(response),
227 &SipAddr {
228 r#type: Some(Transport::Udp),
229 addr: "10.0.0.1:5060"
230 .parse::<std::net::SocketAddr>()
231 .unwrap()
232 .into(),
233 },
234 );
235 assert_eq!(cache.load_full().as_ref().to_string(), "203.0.113.10:62000");
236 }
237
238 #[test]
239 fn builds_contact_using_learned_public_address() {
240 let local_addr = SipAddr {
241 r#type: Some(Transport::Udp),
242 addr: "10.0.0.5:5060"
243 .parse::<std::net::SocketAddr>()
244 .unwrap()
245 .into(),
246 };
247 let template: rsipstack::rsip::Uri = "sip:alice@127.0.0.1:5060".try_into().unwrap();
248 let learned_addr = Some(
249 "203.0.113.10:62000"
250 .parse::<std::net::SocketAddr>()
251 .unwrap()
252 .into(),
253 );
254
255 let contact = build_contact_uri(&local_addr, learned_addr, Some("alice"), Some(&template));
256 assert_eq!(contact.to_string(), "sip:alice@203.0.113.10:62000");
257 }
258
259 #[test]
260 fn identifies_contacts_that_need_resolution() {
261 let local_contact: rsipstack::rsip::Uri = "sip:alice@127.0.0.1:5060".try_into().unwrap();
262 let remote_contact: rsipstack::rsip::Uri = "sip:alice@203.0.113.10:62000".try_into().unwrap();
263 assert!(contact_needs_public_resolution(&local_contact));
264 assert!(!contact_needs_public_resolution(&remote_contact));
265 }
266
267 #[test]
268 fn selects_local_addr_for_uri_transport() {
269 let addrs = vec![
270 SipAddr {
271 r#type: Some(Transport::Udp),
272 addr: "10.0.0.5:5060"
273 .parse::<std::net::SocketAddr>()
274 .unwrap()
275 .into(),
276 },
277 SipAddr {
278 r#type: Some(Transport::Tls),
279 addr: "10.0.0.5:5061"
280 .parse::<std::net::SocketAddr>()
281 .unwrap()
282 .into(),
283 },
284 ];
285
286 let uri: rsipstack::rsip::Uri = "sips:alice@example.com".try_into().unwrap();
287 let selected = find_local_addr_for_uri(&addrs, &uri).unwrap();
288
289 assert_eq!(selected.to_string(), "TLS 10.0.0.5:5061");
290 }
291
292 #[test]
293 fn builds_public_contact_from_shared_cache() {
294 let cache: SharedPublicAddress = Arc::new(ArcSwap::from_pointee(
295 "203.0.113.20:62000"
296 .parse::<std::net::SocketAddr>()
297 .unwrap()
298 .into(),
299 ));
300 let local_addr = SipAddr {
301 r#type: Some(Transport::Udp),
302 addr: "10.0.0.5:5060"
303 .parse::<std::net::SocketAddr>()
304 .unwrap()
305 .into(),
306 };
307
308 let contact = build_public_contact_uri(&cache, true, &local_addr, Some("alice"), None);
309 assert_eq!(contact.to_string(), "sip:alice@203.0.113.20:62000");
310 }
311
312 #[test]
313 fn builds_typed_contact() {
314 let local_addr = SipAddr {
315 r#type: Some(Transport::Udp),
316 addr: "10.0.0.5:5060"
317 .parse::<std::net::SocketAddr>()
318 .unwrap()
319 .into(),
320 };
321 let contact = build_contact(
322 &local_addr,
323 Some(
324 "203.0.113.20:62000"
325 .parse::<std::net::SocketAddr>()
326 .unwrap()
327 .into(),
328 ),
329 Some("alice"),
330 None,
331 );
332 assert_eq!(contact.to_string(), "<sip:alice@203.0.113.20:62000>");
333 }
334
335 #[test]
336 fn keeps_configured_contact_for_tls() {
337 let cache: SharedPublicAddress = Arc::new(ArcSwap::from_pointee(
338 "203.0.113.20:62000"
339 .parse::<std::net::SocketAddr>()
340 .unwrap()
341 .into(),
342 ));
343 let local_addr = SipAddr {
344 r#type: Some(Transport::Tls),
345 addr: "10.0.0.5:5061"
346 .parse::<std::net::SocketAddr>()
347 .unwrap()
348 .into(),
349 };
350
351 let contact = build_public_contact_uri(&cache, true, &local_addr, Some("alice"), None);
352 assert_eq!(contact.to_string(), "sips:alice@10.0.0.5:5061;transport=TLS");
353 }
354
355 #[test]
356 fn infers_transport_from_uri() {
357 let sips_uri: rsipstack::rsip::Uri = "sips:alice@example.com".try_into().unwrap();
358 let tcp_uri: rsipstack::rsip::Uri = "sip:alice@example.com;transport=tcp".try_into().unwrap();
359 assert_eq!(transport_for_uri(&sips_uri), Transport::Tls);
360 assert_eq!(transport_for_uri(&tcp_uri), Transport::Tcp);
361 }
362
363 #[test]
364 fn updates_learned_address_from_local_to_public() {
365 let previous: rsipstack::rsip::HostWithPort = "127.0.0.1:5060"
366 .parse::<std::net::SocketAddr>()
367 .unwrap()
368 .into();
369 let current: rsipstack::rsip::HostWithPort = "203.0.113.10:62000"
370 .parse::<std::net::SocketAddr>()
371 .unwrap()
372 .into();
373
374 assert!(should_update_address(&previous, ¤t,));
375 }
376
377 #[test]
378 fn does_not_update_learned_address_when_unchanged() {
379 let current: rsipstack::rsip::HostWithPort = "203.0.113.10:62000"
380 .parse::<std::net::SocketAddr>()
381 .unwrap()
382 .into();
383
384 assert!(!should_update_address(¤t, ¤t,));
385 }
386}