use super::{
authenticate::{handle_client_authenticate, Credential},
DialogId,
};
use crate::rsip;
use crate::{
rsip_ext::RsipResponseExt,
transaction::{
endpoint::EndpointInnerRef,
key::{TransactionKey, TransactionRole},
make_tag,
transaction::Transaction,
},
transport::SipAddr,
Result,
};
use rsip::{
prelude::{HeadersExt, ToTypedHeader},
Response, SipMessage, StatusCode,
};
use tracing::{debug, info};
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>,
}
impl Registration {
pub fn new(endpoint: EndpointInnerRef, credential: Option<Credential>) -> Self {
Self {
last_seq: 0,
endpoint,
credential,
contact: None,
allow: Default::default(),
public_address: None,
}
}
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 form = 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,
form,
to,
self.last_seq,
);
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 {
info!(
"Updated public address from 401 response, will use in authenticated request: {:?} -> {:?}",
self.public_address, received
);
self.public_address = received;
self.contact = None;
}
if auth_sent {
debug!("received {} response after auth sent", resp.status_code);
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!("received {} response without credential", resp.status_code);
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 {
info!(
"Discovered public IP, will use for future registrations and calls: {:?} -> {:?}",
self.public_address, received
);
self.public_address = received;
}
info!(
"registration do_request done: {:?} {:?}",
resp.status_code,
self.contact.as_ref().map(|c| c.uri.to_string())
);
return Ok(resp);
}
_ => {
info!("registration do_request done: {:?}", resp.status_code);
return Ok(resp);
}
},
_ => break,
}
}
return Err(crate::Error::DialogError(
"registration transaction is already terminated".to_string(),
DialogId::try_from(&tx.original)?,
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![],
}
}
}