rustpbx 0.4.9

A SIP PBX implementation in Rust
Documentation
use anyhow::{Result, bail};

pub const SIP_PREFIX: &str = "sip:";
pub const RTP_PREFIX: &str = "rtp:";

pub fn make_sip_key(call_id: &str, counter: u64) -> String {
    format!("sip:{}:{:020}", call_id, counter)
}

pub fn make_rtp_key(call_id: &str, leg: i32, counter: u64) -> String {
    format!("rtp:{}:{}:{:020}", call_id, leg, counter)
}

pub fn sip_call_prefix(call_id: &str) -> String {
    format!("sip:{}:", call_id)
}

pub fn rtp_call_prefix(call_id: &str) -> String {
    format!("rtp:{}:", call_id)
}

pub fn rtp_call_leg_prefix(call_id: &str, leg: i32) -> String {
    format!("rtp:{}:{}:", call_id, leg)
}

pub fn encode_sip_value(src: &str, dst: &str, payload: &[u8]) -> Vec<u8> {
    let src_bytes = src.as_bytes();
    let dst_bytes = dst.as_bytes();
    let mut buf = Vec::with_capacity(4 + src_bytes.len() + 2 + dst_bytes.len() + payload.len());
    buf.extend_from_slice(&(src_bytes.len() as u16).to_be_bytes());
    buf.extend_from_slice(src_bytes);
    buf.extend_from_slice(&(dst_bytes.len() as u16).to_be_bytes());
    buf.extend_from_slice(dst_bytes);
    buf.extend_from_slice(payload);
    buf
}

pub fn decode_sip_value(value: &[u8]) -> Result<(String, String, Vec<u8>)> {
    if value.len() < 2 {
        bail!("sip value too short for src_len");
    }
    let src_len = u16::from_be_bytes([value[0], value[1]]) as usize;
    if value.len() < 2 + src_len + 2 {
        bail!("sip value too short for src + dst_len");
    }
    let src = String::from_utf8_lossy(&value[2..2 + src_len]).into_owned();
    let dst_len_offset = 2 + src_len;
    let dst_len = u16::from_be_bytes([value[dst_len_offset], value[dst_len_offset + 1]]) as usize;
    let dst_start = dst_len_offset + 2;
    if value.len() < dst_start + dst_len {
        bail!("sip value too short for dst + payload");
    }
    let dst = String::from_utf8_lossy(&value[dst_start..dst_start + dst_len]).into_owned();
    let payload = value[dst_start + dst_len..].to_vec();
    Ok((src, dst, payload))
}

pub fn encode_rtp_value(leg: i32, src: &str, payload: &[u8]) -> Vec<u8> {
    let src_bytes = src.as_bytes();
    let mut buf = Vec::with_capacity(4 + 2 + src_bytes.len() + payload.len());
    buf.extend_from_slice(&leg.to_be_bytes());
    buf.extend_from_slice(&(src_bytes.len() as u16).to_be_bytes());
    buf.extend_from_slice(src_bytes);
    buf.extend_from_slice(payload);
    buf
}

pub fn decode_rtp_value(value: &[u8]) -> Result<(i32, String, Vec<u8>)> {
    if value.len() < 6 {
        bail!("rtp value too short for leg + src_len");
    }
    let leg = i32::from_be_bytes([value[0], value[1], value[2], value[3]]);
    let src_len = u16::from_be_bytes([value[4], value[5]]) as usize;
    if value.len() < 6 + src_len {
        bail!("rtp value too short for src + payload");
    }
    let src = String::from_utf8_lossy(&value[6..6 + src_len]).into_owned();
    let payload = value[6 + src_len..].to_vec();
    Ok((leg, src, payload))
}

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

    #[test]
    fn test_sip_value_roundtrip() {
        let src = "192.168.1.1:5060";
        let dst = "10.0.0.1:5060";
        let payload = b"INVITE sip:bob@example.com SIP/2.0\r\n\r\n";
        let encoded = encode_sip_value(src, dst, payload);
        let (dec_src, dec_dst, dec_payload) = decode_sip_value(&encoded).unwrap();
        assert_eq!(dec_src, src);
        assert_eq!(dec_dst, dst);
        assert_eq!(dec_payload, payload);
    }

    #[test]
    fn test_rtp_value_roundtrip() {
        let leg = 1i32;
        let src = "192.168.1.1:5004";
        let payload = b"\x80\x00\x00\x2a\x00\x00\x00\xa0\x00\x00\x00\x01payload";
        let encoded = encode_rtp_value(leg, src, payload);
        let (dec_leg, dec_src, dec_payload) = decode_rtp_value(&encoded).unwrap();
        assert_eq!(dec_leg, leg);
        assert_eq!(dec_src, src);
        assert_eq!(dec_payload, payload);
    }

    #[test]
    fn test_key_uniqueness() {
        let k1 = make_sip_key("call-1", 0);
        let k2 = make_sip_key("call-1", 1);
        assert_ne!(k1, k2);
        assert!(k1 < k2);
    }

    #[test]
    fn test_prefix_correctness() {
        let sip_key = make_sip_key("call-1", 42);
        let prefix = sip_call_prefix("call-1");
        assert!(sip_key.starts_with(&prefix));

        let rtp_key = make_rtp_key("call-1", 0, 42);
        let prefix = rtp_call_prefix("call-1");
        assert!(rtp_key.starts_with(&prefix));

        let rtp_leg_key = make_rtp_key("call-1", 1, 42);
        let leg_prefix = rtp_call_leg_prefix("call-1", 1);
        assert!(rtp_leg_key.starts_with(&leg_prefix));

        let other_prefix = rtp_call_leg_prefix("call-1", 0);
        assert!(!rtp_leg_key.starts_with(&other_prefix));
    }

    #[test]
    fn test_decode_corrupt_sip_value() {
        assert!(decode_sip_value(&[]).is_err());
        assert!(decode_sip_value(&[0x00]).is_err());
    }

    #[test]
    fn test_decode_corrupt_rtp_value() {
        assert!(decode_rtp_value(&[]).is_err());
        assert!(decode_rtp_value(&[0x00, 0x01]).is_err());
    }
}