1use crate::config::RewriteRule;
2use async_trait::async_trait;
3use regex::Regex;
4use rsipstack::Result;
5use rsipstack::transaction::endpoint::TargetLocator;
6use rsipstack::transport::SipAddr;
7
8pub struct RewriteTargetLocator {
9 rules: Vec<(Regex, String)>,
10}
11
12impl RewriteTargetLocator {
13 pub fn new(rules: Vec<RewriteRule>) -> Self {
14 let rules = rules
15 .into_iter()
16 .filter_map(|rule| match Regex::new(&rule.r#match) {
17 Ok(re) => Some((re, rule.rewrite)),
18 Err(e) => {
19 tracing::error!("Invalid rewrite rule pattern '{}': {}", rule.r#match, e);
20 None
21 }
22 })
23 .collect();
24 Self { rules }
25 }
26}
27
28#[async_trait]
29impl TargetLocator for RewriteTargetLocator {
30 async fn locate(&self, uri: &rsip::Uri) -> Result<SipAddr> {
31 let mut target_uri_str = uri.to_string();
32 let mut matched = false;
33
34 for (re, replacement) in &self.rules {
35 if re.is_match(&target_uri_str) {
36 let new_uri = re
37 .replace_all(&target_uri_str, replacement.as_str())
38 .to_string();
39 tracing::debug!("Rewrite URI: {} -> {}", target_uri_str, new_uri);
40 target_uri_str = new_uri;
41 matched = true;
42 }
43 }
44
45 if matched {
46 let target_uri = rsip::Uri::try_from(target_uri_str.as_str())
47 .map_err(|e| rsipstack::Error::Error(format!("Invalid rewritten URI: {}", e)))?;
48
49 SipAddr::try_from(&target_uri)
50 } else {
51 SipAddr::try_from(uri)
52 }
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use rsip::Uri;
60
61 #[tokio::test]
62 async fn test_rewrite_ip() {
63 let rules = vec![RewriteRule {
64 r#match: "116.116.116.116".to_string(),
65 rewrite: "172.25.25.2".to_string(),
66 }];
67 let locator = RewriteTargetLocator::new(rules);
68
69 let uri = Uri::try_from("sip:1001@116.116.116.116:5060").unwrap();
70 let addr = locator.locate(&uri).await.unwrap();
71
72 assert_eq!(addr.addr.to_string(), "172.25.25.2:5060");
73 }
74
75 #[tokio::test]
76 async fn test_rewrite_regex() {
77 let rules = vec![RewriteRule {
78 r#match: "sip:(\\d+)@.*".to_string(),
79 rewrite: "sip:$1@internal.net".to_string(),
80 }];
81 let locator = RewriteTargetLocator::new(rules);
82
83 let uri = Uri::try_from("sip:12345@external.com").unwrap();
84 let addr = locator.locate(&uri).await.unwrap();
85
86 assert_eq!(addr.addr.to_string(), "internal.net");
87 }
88
89 #[tokio::test]
90 async fn test_no_match() {
91 let rules = vec![RewriteRule {
92 r#match: "nomatch".to_string(),
93 rewrite: "whatever".to_string(),
94 }];
95 let locator = RewriteTargetLocator::new(rules);
96
97 let uri = Uri::try_from("sip:1001@116.62.75.161:5060").unwrap();
98 let addr = locator.locate(&uri).await.unwrap();
99
100 assert_eq!(addr.addr.to_string(), "116.62.75.161:5060");
101 }
102
103 #[tokio::test]
104 async fn test_multiple_rules() {
105 let rules = vec![
106 RewriteRule {
107 r#match: "116.62.75.161".to_string(),
108 rewrite: "172.25.225.2".to_string(),
109 },
110 RewriteRule {
111 r#match: "172.25.225.2".to_string(),
112 rewrite: "10.0.0.1".to_string(),
113 },
114 ];
115 let locator = RewriteTargetLocator::new(rules);
116
117 let uri = Uri::try_from("sip:1001@116.62.75.161:5060").unwrap();
118 let addr = locator.locate(&uri).await.unwrap();
119
120 assert_eq!(addr.addr.to_string(), "10.0.0.1:5060");
121 }
122}