use rsiprtp::prelude::*;
use rsiprtp::sdp::builder::MediaBuilder;
use std::env;
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use tokio::net::UdpSocket;
use tokio::time::{sleep, timeout};
struct Config {
server: String,
port: u16,
username: String,
password: String,
destination: String,
local_ip: String,
local_port: u16,
}
impl Config {
fn from_env() -> std::result::Result<Self, String> {
Ok(Self {
server: env::var("SIP_SERVER").unwrap_or_else(|_| "127.0.0.1".to_string()),
port: env::var("SIP_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(5060),
username: env::var("SIP_USER").unwrap_or_else(|_| "1001".to_string()),
password: env::var("SIP_PASS").unwrap_or_else(|_| "secret".to_string()),
destination: env::var("SIP_DEST").unwrap_or_else(|_| "*43".to_string()),
local_ip: env::var("LOCAL_IP").unwrap_or_else(|_| "127.0.0.1".to_string()),
local_port: env::var("LOCAL_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(5061),
})
}
}
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt().init();
let config = Config::from_env()?;
println!("=== rsiprtp Basic Call Example ===");
println!("Server: {}:{}", config.server, config.port);
println!("User: {}", config.username);
println!("Destination: {}", config.destination);
println!();
let local_addr: SocketAddr = format!("{}:{}", config.local_ip, config.local_port).parse()?;
let socket = UdpSocket::bind(local_addr).await?;
println!("Bound to {}", socket.local_addr()?);
let server_addr: SocketAddr = if let Ok(ip) = config.server.parse::<IpAddr>() {
SocketAddr::new(ip, config.port)
} else {
let resolver = SipResolver::new().await?;
let targets = resolver
.resolve(&config.server, Some(TransportProtocol::Udp))
.await?;
let target = targets
.first()
.ok_or_else(|| format!("no DNS records for {}", config.server))?;
let ip = target
.addresses
.first()
.copied()
.ok_or_else(|| format!("no A/AAAA for SRV target {}", target.host))?;
println!(
"Resolved {} -> {}:{} (transport={:?})",
config.server, ip, target.port, target.transport
);
SocketAddr::new(ip, target.port)
};
socket.connect(&server_addr).await?;
let reg_config = RegistrationConfig {
registrar: format!("sip:{}", config.server),
aor: format!("sip:{}@{}", config.username, config.server),
contact: format!(
"sip:{}@{}:{}",
config.username, config.local_ip, config.local_port
),
username: config.username.clone(),
password: config.password.clone(),
expires: 3600,
local_addr: config.local_ip.clone(),
local_port: config.local_port,
transport: "UDP".to_string(),
};
let mut registration = RegistrationManager::new(reg_config);
println!("\n--- Step 1: Registering with SIP server ---");
let register_req = registration.create_register()?;
let req_bytes = register_req.to_bytes();
println!("Sending REGISTER...");
socket.send(&req_bytes).await?;
let mut buf = vec![0u8; 4096];
let response = match timeout(Duration::from_secs(5), socket.recv(&mut buf)).await {
Ok(Ok(n)) => {
let msg = SipMessage::parse(&buf[..n])?;
msg.as_response()
.cloned()
.ok_or("Expected response, got request")?
}
Ok(Err(e)) => return Err(format!("Receive error: {}", e).into()),
Err(_) => return Err("Timeout waiting for response".into()),
};
println!("Received {} {}", response.status_code(), response.reason());
if let Some(retry_req) = registration.handle_response(&response)? {
println!("Server requires authentication, retrying...");
let req_bytes = retry_req.to_bytes();
socket.send(&req_bytes).await?;
let n = timeout(Duration::from_secs(5), socket.recv(&mut buf)).await??;
let msg = SipMessage::parse(&buf[..n])?;
let response = msg.as_response().ok_or("Expected response, got request")?;
println!("Received {} {}", response.status_code(), response.reason());
registration.handle_response(response)?;
}
if registration.is_registered() {
println!("Registration successful!");
} else {
return Err("Registration failed".into());
}
println!("\n--- Step 2: Making outbound call ---");
let call_config = CallConfig::default();
let manager_config = ManagerConfig {
local_sip_addr: config.local_ip.clone(),
local_rtp_addr: config.local_ip.clone(),
rtp_port_range: (10000, 20000),
call_config,
};
let mut call_manager = CallManager::new(manager_config);
let dest_uri = format!("sip:{}@{}", config.destination, config.server);
let call_id = call_manager.create_call(dest_uri.clone());
println!("Created call {} to {}", &call_id.0, dest_uri);
let from_uri = format!("sip:{}@{}", config.username, config.server);
let from_tag = generate_tag();
let sip_call_id = generate_call_id(&config.server);
let branch = generate_branch();
let local_rtp_port = 10000u16;
let local_addr: IpAddr = config.local_ip.parse()?;
let sdp = SdpBuilder::new(local_addr)
.session_name("rsiprtp call")
.add_media(MediaBuilder::audio(local_rtp_port).pcmu().pcma())
.build();
let invite = SipRequest::builder()
.method(Method::Invite)
.uri(&dest_uri)
.via(&config.local_ip, config.local_port, "UDP", &branch)
.from(&from_uri, &from_tag)
.to(&dest_uri)
.call_id(&sip_call_id)
.cseq(1)
.contact(&format!(
"sip:{}@{}:{}",
config.username, config.local_ip, config.local_port
))
.body(sdp.to_string().into_bytes(), "application/sdp")
.build()?;
println!("Sending INVITE...");
socket.send(&invite.to_bytes()).await?;
loop {
let n = match timeout(Duration::from_secs(30), socket.recv(&mut buf)).await {
Ok(Ok(n)) => n,
Ok(Err(e)) => return Err(format!("Receive error: {}", e).into()),
Err(_) => {
println!("Call timeout");
break;
}
};
let msg = SipMessage::parse(&buf[..n])?;
if let Some(response) = msg.as_response() {
let status = response.status_code();
println!("Received {} {}", status, response.reason());
match status {
100 => {
}
180 | 183 => {
println!("Remote party is ringing...");
}
200 => {
println!("Call connected!");
let ack = SipRequest::builder()
.method(Method::Ack)
.uri(&dest_uri)
.via(&config.local_ip, config.local_port, "UDP", &branch)
.from(&from_uri, &from_tag)
.to(&dest_uri)
.to_tag(response.to_tag().as_deref().unwrap_or(""))
.call_id(&sip_call_id)
.cseq(1)
.build()?;
socket.send(&ack.to_bytes()).await?;
println!("Sent ACK");
println!("\n--- Step 3: Media session ---");
println!("Call established. Press Ctrl+C to end or waiting 10 seconds...");
sleep(Duration::from_secs(10)).await;
println!("\n--- Step 4: Ending call ---");
let bye = SipRequest::builder()
.method(Method::Bye)
.uri(&dest_uri)
.via(
&config.local_ip,
config.local_port,
"UDP",
&generate_branch(),
)
.from(&from_uri, &from_tag)
.to(&dest_uri)
.to_tag(response.to_tag().as_deref().unwrap_or(""))
.call_id(&sip_call_id)
.cseq(2)
.build()?;
socket.send(&bye.to_bytes()).await?;
println!("Sent BYE");
if let Ok(Ok(n)) = timeout(Duration::from_secs(5), socket.recv(&mut buf)).await
{
if let Ok(msg) = SipMessage::parse(&buf[..n]) {
if let Some(resp) = msg.as_response() {
println!(
"Received {} {} for BYE",
resp.status_code(),
resp.reason()
);
}
}
}
break;
}
401 | 407 => {
println!("Authentication required for call (not implemented in example)");
break;
}
_ if status >= 400 => {
println!("Call failed: {} {}", status, response.reason());
break;
}
_ => {
}
}
}
}
println!("\n--- Step 5: Unregistering ---");
let unreg = registration.create_unregister()?;
socket.send(&unreg.to_bytes()).await?;
if let Ok(Ok(n)) = timeout(Duration::from_secs(5), socket.recv(&mut buf)).await {
if let Ok(msg) = SipMessage::parse(&buf[..n]) {
if let Some(response) = msg.as_response() {
println!(
"Unregister response: {} {}",
response.status_code(),
response.reason()
);
}
}
}
println!("\n=== Example complete ===");
Ok(())
}