Skip to main content

Register against a PBX

A registrar maps an address of record such as sip:alice@example.com to the endpoint where calls should arrive. The binding expires unless the endpoint refreshes it.

The example below is a real file that CI compiles (crates/sipx-ua/examples/register.rs):

//! Register against a PBX, and keep the registration alive.
//!
//! ```text
//! cargo run --example register -- sip:alice@example.com 192.0.2.1:5060 secret
//! ```

// These samples are read by people before they are run by machines, so they are written for
// readability where the workspace lints would prefer something terser. `clone_into` over
// `to_owned` teaches nothing in a five-line example, and a sine wave has to become an `i16`
// somewhere.
#![allow(clippy::assigning_clones, clippy::cast_possible_truncation)]

use std::net::SocketAddr;

use sipx_sip::{Host, HostName, Uri};
use sipx_transport::{Config as TransportConfig, Target, bind};
use sipx_ua::{Config, Credentials, UserAgent};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let user = args
.next()
.unwrap_or_else(|| "sip:alice@example.com".to_owned());
let server: SocketAddr = args
.next()
.unwrap_or_else(|| "127.0.0.1:5060".to_owned())
.parse()?;
let password = args.next().unwrap_or_else(|| "secret".to_owned());

let (endpoint, _incoming) = bind(TransportConfig::new("0.0.0.0:0".parse()?)).await?;

let registrar = Uri::sip(Host::Name(HostName::new("example.com")?));
let config = Config::new(
format!("<{user}>"),
format!("<sip:alice@{}>", endpoint.local_addr()),
registrar,
Target::udp(server),
)
.with_credentials(Credentials::new("alice", password));

let mut agent = UserAgent::new(endpoint, config);

// A registration is a *lease*, not a request: the server decides how long it lasts, which is
// not always what was asked for, and it has to be refreshed before it expires.
let lease = agent.register().await?;
println!(
"registered for {:?}; refresh after {:?}",
lease.granted, lease.refresh_after
);
Ok(())
}

What is worth noticing

Treat registration as a lease. The registrar decides the granted lifetime, which can differ from the requested value. Lease::refresh_after is deliberately earlier than Lease::granted, because refreshing at expiry races the network. The example performs one registration; a long-running application should call UserAgent::keep_registered or schedule refreshes from the returned lease.

Digest selection is deterministic. sipx supports MD5 and the 256-bit and 512/256-bit SHA-2 variants, including their -sess forms. It selects the strongest offered algorithm as required by RFC 8760, with the registrar's order breaking equal-strength ties. Authentication failures are returned as typed errors rather than accepted as a registration.

Advertise a reachable contact. The example binds an ephemeral local socket and uses its address in Contact, which is suitable for a controlled local setup. A deployed endpoint must advertise an address the registrar can route back to. RFC 5626 Outbound can keep requests on a client-opened flow when direct inbound reachability is unavailable.

Keep path observation separate from reachability policy. Before the first success, UserAgent::registration_observation is NotRegistered. Afterwards it reports what the registrar put in the final response's top Via: Observed(address), Absent, or Invalid(reason). The convenience observed_registration_address returns an address only for the first case. This is useful for diagnostics and NAT visibility, but it is not proof that an inbound path exists. sipx never copies the value into a later Contact, route set, GRUU, Outbound or push state, SDP, ICE candidate, or media destination. Missing or malformed observation data also does not invalidate the registrar's lease. See the registration observation specification for the complete typed outcome table.

From the command line

SIPX_PASSWORD='your-password' \
sipx register sip:alice@example.com --keep-alive

Prefer SIPX_PASSWORD to --password, because command-line arguments may be visible to other local processes. Useful options include --target host:port to bypass discovery, --transport tls or --transport wss for protected signalling, and --outbound for an RFC 5626 flow. Private authorities are added with --tls-ca; certificate verification cannot be disabled.

See the CLI reference for push-refresh options, capture handling, output fields, and exit codes.