use super::{
authenticate::{handle_client_authenticate, Credential},
DialogId,
};
use crate::{
rsip_ext::RsipResponseExt,
transaction::{
endpoint::EndpointInnerRef,
key::{TransactionKey, TransactionRole},
make_call_id, make_tag,
transaction::Transaction,
},
transport::SipAddr,
Result,
};
use rsip::{
prelude::{HeadersExt, ToTypedHeader},
Response, SipMessage, StatusCode,
};
use tracing::debug;
pub struct Registration {
pub last_seq: u32,
pub endpoint: EndpointInnerRef,
pub credential: Option<Credential>,
pub contact: Option<rsip::typed::Contact>,
pub allow: rsip::headers::Allow,
pub public_address: Option<rsip::HostWithPort>,
pub call_id: rsip::headers::CallId,
}
impl Registration {
pub fn new(endpoint: EndpointInnerRef, credential: Option<Credential>) -> Self {
let call_id = make_call_id(endpoint.option.callid_suffix.as_deref());
Self {
last_seq: 0,
endpoint,
credential,
contact: None,
allow: Default::default(),
public_address: None,
call_id,
}
}
pub fn discovered_public_address(&self) -> Option<rsip::HostWithPort> {
self.public_address.clone()
}
pub fn expires(&self) -> u32 {
self.contact
.as_ref()
.and_then(|c| c.expires())
.map(|e| e.seconds().unwrap_or(50))
.unwrap_or(50)
}
pub async fn register(&mut self, server: rsip::Uri, expires: Option<u32>) -> Result<Response> {
self.last_seq += 1;
let mut to = rsip::typed::To {
display_name: None,
uri: server.clone(),
params: vec![],
};
if let Some(cred) = &self.credential {
to.uri.auth = Some(rsip::auth::Auth {
user: cred.username.clone(),
password: None,
});
}
let from = rsip::typed::From {
display_name: None,
uri: to.uri.clone(),
params: vec![],
}
.with_tag(make_tag());
let via = self.endpoint.get_via(None, None)?;
let contact = self.contact.clone().unwrap_or_else(|| {
let contact_host_with_port = self
.public_address
.clone()
.unwrap_or_else(|| via.uri.host_with_port.clone());
rsip::typed::Contact {
display_name: None,
uri: rsip::Uri {
auth: to.uri.auth.clone(),
scheme: Some(rsip::Scheme::Sip),
host_with_port: contact_host_with_port,
params: vec![],
headers: vec![],
},
params: vec![],
}
});
let mut request = self.endpoint.make_request(
rsip::Method::Register,
server,
via,
from,
to,
self.last_seq,
None,
);
request.headers.unique_push(self.call_id.clone().into());
request.headers.unique_push(contact.into());
request.headers.unique_push(self.allow.clone().into());
if let Some(expires) = expires {
request
.headers
.unique_push(rsip::headers::Expires::from(expires).into());
}
let key = TransactionKey::from_request(&request, TransactionRole::Client)?;
let mut tx = Transaction::new_client(key, request, self.endpoint.clone(), None);
tx.send().await?;
let mut auth_sent = false;
while let Some(msg) = tx.receive().await {
match msg {
SipMessage::Response(resp) => match resp.status_code {
StatusCode::Trying => {
continue;
}
StatusCode::ProxyAuthenticationRequired | StatusCode::Unauthorized => {
let received = resp.via_received();
if self.public_address != received {
debug!(
old = ?self.public_address,
new = ?received,
"updated public address from 401 response"
);
self.public_address = received;
self.contact = None;
}
if auth_sent {
debug!(status = %resp.status_code, "received auth response after auth sent");
return Ok(resp);
}
if let Some(cred) = &self.credential {
self.last_seq += 1;
tx = handle_client_authenticate(self.last_seq, &tx, resp, cred).await?;
tx.send().await?;
auth_sent = true;
continue;
} else {
debug!(status = %resp.status_code, "received auth response without credential");
return Ok(resp);
}
}
StatusCode::OK => {
let received = resp.via_received();
match resp.contact_header() {
Ok(contact) => {
self.contact = contact.typed().ok();
}
Err(_) => {}
};
if self.public_address != received {
debug!(
old = ?self.public_address,
new = ?received,
"discovered public IP"
);
self.public_address = received;
}
debug!(
status = %resp.status_code,
contact = ?self.contact.as_ref().map(|c| c.uri.to_string()),
"registration do_request done"
);
return Ok(resp);
}
_ => {
debug!(status = %resp.status_code, "registration do_request done");
return Ok(resp);
}
},
_ => break,
}
}
return Err(crate::Error::DialogError(
"registration transaction is already terminated".to_string(),
DialogId::try_from(&tx)?,
StatusCode::BadRequest,
));
}
pub fn create_nat_aware_contact(
username: &str,
public_address: Option<rsip::HostWithPort>,
local_address: &SipAddr,
) -> rsip::typed::Contact {
let contact_host_with_port = public_address.unwrap_or_else(|| local_address.clone().into());
let params = vec![];
rsip::typed::Contact {
display_name: None,
uri: rsip::Uri {
scheme: Some(rsip::Scheme::Sip),
auth: Some(rsip::Auth {
user: username.to_string(),
password: None,
}),
host_with_port: contact_host_with_port,
params,
headers: vec![],
},
params: vec![],
}
}
}