active-call 0.3.76

A SIP/WebRTC voice agent
Documentation
use crate::config::RewriteRule;
use async_trait::async_trait;
use regex::Regex;
use rsipstack::Result;
use rsipstack::transaction::endpoint::TargetLocator;
use rsipstack::transport::SipAddr;

pub struct RewriteTargetLocator {
    rules: Vec<(Regex, String)>,
}

impl RewriteTargetLocator {
    pub fn new(rules: Vec<RewriteRule>) -> Self {
        let rules = rules
            .into_iter()
            .filter_map(|rule| match Regex::new(&rule.r#match) {
                Ok(re) => Some((re, rule.rewrite)),
                Err(e) => {
                    tracing::error!("Invalid rewrite rule pattern '{}': {}", rule.r#match, e);
                    None
                }
            })
            .collect();
        Self { rules }
    }
}

#[async_trait]
impl TargetLocator for RewriteTargetLocator {
    async fn locate(&self, uri: &rsipstack::rsip::Uri) -> Result<SipAddr> {
        let mut target_uri_str = uri.to_string();
        let mut matched = false;

        for (re, replacement) in &self.rules {
            if re.is_match(&target_uri_str) {
                let new_uri = re
                    .replace_all(&target_uri_str, replacement.as_str())
                    .to_string();
                tracing::debug!("Rewrite URI: {} -> {}", target_uri_str, new_uri);
                target_uri_str = new_uri;
                matched = true;
            }
        }

        if matched {
            let target_uri = rsipstack::rsip::Uri::try_from(target_uri_str.as_str())
                .map_err(|e| rsipstack::Error::Error(format!("Invalid rewritten URI: {}", e)))?;

            SipAddr::try_from(&target_uri)
        } else {
            SipAddr::try_from(uri)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rsipstack::rsip::Uri;

    #[tokio::test]
    async fn test_rewrite_ip() {
        let rules = vec![RewriteRule {
            r#match: "116.116.116.116".to_string(),
            rewrite: "172.25.25.2".to_string(),
        }];
        let locator = RewriteTargetLocator::new(rules);

        let uri = Uri::try_from("sip:1001@116.116.116.116:5060").unwrap();
        let addr = locator.locate(&uri).await.unwrap();

        assert_eq!(addr.addr.to_string(), "172.25.25.2:5060");
    }

    #[tokio::test]
    async fn test_rewrite_regex() {
        let rules = vec![RewriteRule {
            r#match: "sip:(\\d+)@.*".to_string(),
            rewrite: "sip:$1@internal.net".to_string(),
        }];
        let locator = RewriteTargetLocator::new(rules);

        let uri = Uri::try_from("sip:12345@external.com").unwrap();
        let addr = locator.locate(&uri).await.unwrap();

        assert_eq!(addr.addr.to_string(), "internal.net");
    }

    #[tokio::test]
    async fn test_no_match() {
        let rules = vec![RewriteRule {
            r#match: "nomatch".to_string(),
            rewrite: "whatever".to_string(),
        }];
        let locator = RewriteTargetLocator::new(rules);

        let uri = Uri::try_from("sip:1001@116.62.75.161:5060").unwrap();
        let addr = locator.locate(&uri).await.unwrap();

        assert_eq!(addr.addr.to_string(), "116.62.75.161:5060");
    }

    #[tokio::test]
    async fn test_multiple_rules() {
        let rules = vec![
            RewriteRule {
                r#match: "116.62.75.161".to_string(),
                rewrite: "172.25.225.2".to_string(),
            },
            RewriteRule {
                r#match: "172.25.225.2".to_string(),
                rewrite: "10.0.0.1".to_string(),
            },
        ];
        let locator = RewriteTargetLocator::new(rules);

        let uri = Uri::try_from("sip:1001@116.62.75.161:5060").unwrap();
        let addr = locator.locate(&uri).await.unwrap();

        assert_eq!(addr.addr.to_string(), "10.0.0.1:5060");
    }
}