mirror of
https://github.com/coral/sipcord-bridge.git
synced 2026-06-29 09:23:14 -06:00
3CX fixes
This commit is contained in:
parent
d061d2c0d2
commit
3a3a323b6b
|
|
@ -449,18 +449,15 @@ fn process_sip_command(cmd: SipCommand, calls: &Arc<DashMap<CallId, CallState>>)
|
|||
}
|
||||
|
||||
if auth_ok {
|
||||
// Add Expires header to the pre-built 200 OK
|
||||
let expires_str = format!("{}", pending.expires);
|
||||
let hdr_name = std::ffi::CString::new("Expires").unwrap();
|
||||
let hdr_value = std::ffi::CString::new(expires_str).unwrap();
|
||||
|
||||
let name = pj_str(hdr_name.as_ptr() as *mut c_char);
|
||||
let value = pj_str(hdr_value.as_ptr() as *mut c_char);
|
||||
let hdr = pjsip_generic_string_hdr_create((*tdata).pool, &name, &value);
|
||||
if !hdr.is_null() {
|
||||
pj_list_insert_before(
|
||||
&mut (*(*tdata).msg).hdr as *mut pjsip_hdr as *mut pj_list_type,
|
||||
hdr as *mut pj_list_type,
|
||||
use register_handler::append_tdata_hdr;
|
||||
append_tdata_hdr(tdata, c"Expires", &pending.expires.to_string());
|
||||
// RFC 3261 §10.3: echo the client's binding back as Contact.
|
||||
// Required for strict clients like 3CX to accept registration.
|
||||
if let Some(ref uri) = pending.contact_uri {
|
||||
append_tdata_hdr(
|
||||
tdata,
|
||||
c"Contact",
|
||||
&format!("<{}>;expires={}", uri, pending.expires),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use super::callbacks::{
|
|||
use super::ffi::types::*;
|
||||
use super::ffi::utils::pj_str_to_string;
|
||||
use pjsua::*;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::net::SocketAddr;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
|
@ -32,12 +32,17 @@ pub struct PendingRegisterTsx {
|
|||
pub tsx: SendableTsx,
|
||||
pub tdata: SendableTdata,
|
||||
pub expires: u32,
|
||||
/// Client's Contact URI, echoed back in the 200 OK per RFC 3261 §10.3.
|
||||
/// Strict clients (3CX) treat the response as a forced-unregister when
|
||||
/// their binding isn't listed.
|
||||
pub contact_uri: Option<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PendingRegisterTsx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PendingRegisterTsx")
|
||||
.field("expires", &self.expires)
|
||||
.field("contact_uri", &self.contact_uri)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +83,46 @@ unsafe fn pj_list_init_hdr(hdr: *mut pjsip_hdr) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a generic string header in `pool`. Returns null on failure (alloc or
|
||||
/// interior-NUL in `value`). pjsip duplicates name/value into `pool`, so the
|
||||
/// caller's CStrings can be dropped immediately after this returns.
|
||||
#[inline]
|
||||
unsafe fn make_string_hdr(
|
||||
pool: *mut pj_pool_t,
|
||||
name: &CStr,
|
||||
value: &str,
|
||||
) -> *mut pjsip_generic_string_hdr {
|
||||
unsafe {
|
||||
let Ok(value_c) = CString::new(value) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let name_pj = pj_str(name.as_ptr() as *mut c_char);
|
||||
let value_pj = pj_str(value_c.as_ptr() as *mut c_char);
|
||||
pjsip_generic_string_hdr_create(pool, &name_pj, &value_pj)
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a generic string header onto the message buffer in `tdata`,
|
||||
/// allocating from the tdata's own pool. Returns false on failure.
|
||||
#[inline]
|
||||
pub(super) unsafe fn append_tdata_hdr(
|
||||
tdata: *mut pjsip_tx_data,
|
||||
name: &CStr,
|
||||
value: &str,
|
||||
) -> bool {
|
||||
unsafe {
|
||||
let hdr = make_string_hdr((*tdata).pool, name, value);
|
||||
if hdr.is_null() {
|
||||
return false;
|
||||
}
|
||||
pj_list_insert_before(
|
||||
&mut (*(*tdata).msg).hdr as *mut pjsip_hdr as *mut pj_list_type,
|
||||
hdr as *mut pj_list_type,
|
||||
);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a simple stateless SIP response (no custom headers).
|
||||
unsafe fn send_simple_response(rdata: *mut pjsip_rx_data, status_code: u16, reason: &str) {
|
||||
unsafe {
|
||||
|
|
@ -97,30 +142,46 @@ unsafe fn send_simple_response(rdata: *mut pjsip_rx_data, status_code: u16, reas
|
|||
}
|
||||
}
|
||||
|
||||
/// Send a stateless 200 OK with an Expires header.
|
||||
unsafe fn send_register_ok(rdata: *mut pjsip_rx_data, expires: u32) {
|
||||
/// Send a stateless 200 OK with Expires + Contact headers.
|
||||
///
|
||||
/// RFC 3261 §10.3 step 8 requires the registrar's 200 OK to enumerate the
|
||||
/// client's current bindings via Contact header(s). Strict clients like 3CX
|
||||
/// interpret a Contact-less response as "forced unregister" and tear down the
|
||||
/// trunk even though the binding was accepted server-side.
|
||||
unsafe fn send_register_ok(rdata: *mut pjsip_rx_data, expires: u32, contact_uri: Option<&str>) {
|
||||
unsafe {
|
||||
let endpt = pjsua_get_pjsip_endpt();
|
||||
if endpt.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let expires_str = format!("{}", expires);
|
||||
let hdr_name = CString::new("Expires").unwrap();
|
||||
let hdr_value = CString::new(expires_str).unwrap();
|
||||
|
||||
let pool = pjsua_pool_create(c"register_ok".as_ptr(), 512, 512);
|
||||
let pool = pjsua_pool_create(c"register_ok".as_ptr(), 1024, 1024);
|
||||
if !pool.is_null() {
|
||||
let name = pj_str(hdr_name.as_ptr() as *mut c_char);
|
||||
let value = pj_str(hdr_value.as_ptr() as *mut c_char);
|
||||
let hdr = pjsip_generic_string_hdr_create(pool, &name, &value);
|
||||
let exp_hdr = make_string_hdr(pool, c"Expires", &expires.to_string());
|
||||
let contact_hdr = match contact_uri {
|
||||
Some(uri) => make_string_hdr(
|
||||
pool,
|
||||
c"Contact",
|
||||
&format!("<{}>;expires={}", uri, expires),
|
||||
),
|
||||
None => ptr::null_mut(),
|
||||
};
|
||||
|
||||
if !hdr.is_null() {
|
||||
if !exp_hdr.is_null() {
|
||||
let hdr_list =
|
||||
pj_pool_alloc(pool, std::mem::size_of::<pjsip_hdr>()) as *mut pjsip_hdr;
|
||||
if !hdr_list.is_null() {
|
||||
pj_list_init_hdr(hdr_list);
|
||||
pj_list_insert_before(hdr_list as *mut pj_list_type, hdr as *mut pj_list_type);
|
||||
pj_list_insert_before(
|
||||
hdr_list as *mut pj_list_type,
|
||||
exp_hdr as *mut pj_list_type,
|
||||
);
|
||||
if !contact_hdr.is_null() {
|
||||
pj_list_insert_before(
|
||||
hdr_list as *mut pj_list_type,
|
||||
contact_hdr as *mut pj_list_type,
|
||||
);
|
||||
}
|
||||
|
||||
let status = pjsip_endpt_respond_stateless(
|
||||
endpt,
|
||||
|
|
@ -143,7 +204,7 @@ unsafe fn send_register_ok(rdata: *mut pjsip_rx_data, expires: u32) {
|
|||
pj_pool_release(pool);
|
||||
}
|
||||
|
||||
// Fallback: respond without Expires header
|
||||
// Fallback: respond without extra headers
|
||||
let status =
|
||||
pjsip_endpt_respond_stateless(endpt, rdata, 200, ptr::null(), ptr::null(), ptr::null());
|
||||
if status != pj_constants__PJ_SUCCESS as i32 {
|
||||
|
|
@ -176,6 +237,7 @@ unsafe fn detect_transport(rdata: *mut pjsip_rx_data) -> crate::services::regist
|
|||
unsafe fn create_register_tsx(
|
||||
rdata: *mut pjsip_rx_data,
|
||||
expires: u32,
|
||||
contact_uri: Option<String>,
|
||||
) -> Option<PendingRegisterTsx> {
|
||||
unsafe {
|
||||
let endpt = pjsua_get_pjsip_endpt();
|
||||
|
|
@ -208,6 +270,7 @@ unsafe fn create_register_tsx(
|
|||
tsx: SendableTsx(tsx),
|
||||
tdata: SendableTdata(tdata),
|
||||
expires,
|
||||
contact_uri,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -345,7 +408,7 @@ pub unsafe extern "C" fn on_rx_request_cb(rdata: *mut pjsip_rx_data) -> pj_bool_
|
|||
params.username,
|
||||
ip_str
|
||||
);
|
||||
send_register_ok(rdata, expires);
|
||||
send_register_ok(rdata, expires, contact_uri.as_deref());
|
||||
// Send to async handler for registrar update
|
||||
if let Some(tx) = REGISTER_EVENT_TX.get() {
|
||||
let _ = tx.try_send(RegisterRequest {
|
||||
|
|
@ -390,7 +453,7 @@ pub unsafe extern "C" fn on_rx_request_cb(rdata: *mut pjsip_rx_data) -> pj_bool_
|
|||
params.username,
|
||||
ip_str
|
||||
);
|
||||
if let Some(pending) = create_register_tsx(rdata, expires) {
|
||||
if let Some(pending) = create_register_tsx(rdata, expires, contact_uri.clone()) {
|
||||
if let Some(tx) = REGISTER_EVENT_TX.get() {
|
||||
let _ = tx.try_send(RegisterRequest {
|
||||
digest_auth: params,
|
||||
|
|
@ -419,6 +482,7 @@ pub unsafe extern "C" fn on_rx_request_cb(rdata: *mut pjsip_rx_data) -> pj_bool_
|
|||
ip_str,
|
||||
params.username
|
||||
);
|
||||
let contact_uri_for_response = contact_uri.clone();
|
||||
if let Some(tx) = REGISTER_EVENT_TX.get() {
|
||||
let _ = tx.try_send(RegisterRequest {
|
||||
digest_auth: params,
|
||||
|
|
@ -429,7 +493,7 @@ pub unsafe extern "C" fn on_rx_request_cb(rdata: *mut pjsip_rx_data) -> pj_bool_
|
|||
pending_tsx: None,
|
||||
});
|
||||
}
|
||||
send_register_ok(rdata, expires);
|
||||
send_register_ok(rdata, expires, contact_uri_for_response.as_deref());
|
||||
} else {
|
||||
// No Authorization header - send 401 challenge
|
||||
tracing::debug!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue