make the `run()` more accurately honor the threads stop
request when checking registrations in the inner-loop.
If the thread is on `wait_or_stop()` this should be managed
just quickly and gracefully.
However if we are on the `check_registration()` or
`create_registration()` doing a DI cal, threads stop request
cannot really interrupt it. So tighten it.
Change-Id: I31ef44445817d4eb8e11cf269c2bd4db2eff71ef
If the owner of the `RegThread` (`RegistrationAgentFactory`)
was asked to be destructed, make the destroy process
of the reg thread more ordered as well.
That makes the thread to stop as soon as the app factory
begins its own destruction.
This will help to avoid a possible way to end up with
a stale (or invalid) DI factory ptr.
E.g.: `RegThread` keeps on running, while all the plugins
are being torn down by that time already. In this case
the `RegThread` can simply call `getFactory4Di()` or `di_f->getInstance()`
way after the registrar_client is already partially destroyed.
Change-Id: I10e38174d3856e19f2696f8d874ea166e0a035a8
Instead of blindly sleeping, use the `run_cond.wait_for()`
for the case, when AmThread sends `.notify_all()` upon stopping.
Hence we don't have to block the whole thread for 10 seconds if
suddenly the `stop()` is requested by AmThread at the very beginning
of the sleep.
Add a dedicated `RegThread::wait_or_stop()` wrapper to handle this.
Also differentiate whether the `RegThread::wait_or_stop()` returns
due to timer exceed or there was an actual thread stop requested.
Act accordingly during the `run()` execution.
Change-Id: I1e603c462af983e633c811d10ed2d0ac506f46d3
Check whether the thread was asked to be stopped,
instead on infinitely cycling through the true statement,
relying on the fact `getFactory4Di()` will always return
a safe and valid pointer.
Change-Id: Ibc27b725eaa2c183c50bff603f7d3115cdc57b3d
If 183 has empty body, don't change the state,
because otherwise it will break the OA model exchange
in later updates during the continued early dialog stage.
Keep pretending to remain in the `Completed` state.
Change-Id: I1053740a179c8589b54a0c856caa438a92521fda
For the cases with option tags are being added/removed
from the headers (e.g. Require, Supported),
we have to cover corner cases.
Introduce according tests into existing core tests
covering headers, as well as python framework part of it.
Change-Id: Iad15add88473f9d64cc6a3d27fc6e996e4a81abf
Do not use the original iterator's position,
when removing the first token from something like:
`Supported: timer, path, replaces`
Instead build the remaining option list by appending
only those kept, trimmed tokens with commas between them.
(P.S.: this is not related to the latest SST changes)
Change-Id: I5a274072dbbc336494a0802ff7e80f0bf0b51813
For the case when ptime sample requests > than the DlbBuffer is.
Actually normal rates (e.g. PCMA/PCMU) have 20ms (ca. 160bytes),
even if the ptime would be 100ms, which is unusual, the size
requested per sample would be ca 1600bytes.
`AmAudio::samples` are `DblBuffer` based,
which has size of `AUDIO_BUFFER_SIZE * 2`, but it's not
contiguous, but exposes only half of it at a time through
the `unsigned char*` overloading operator.
Add the main guard in `AmAudio::get()`, who calculates the actual size
to be requested from the buffer to read into.
Additionally (actually less effective) prevent also overflow
in the implementation itself in `AmAudioFile::read()`, this is where
the `fread()` happens. Just for the case if the path towards it
is different in any other case.
Change-Id: Ifd381f4435f0a16ecee5612da41ea9046f6d22a4
Add a test that covers `UDP/TLS/RTP/SAVPF` transport
and particularly focuses on ICE and DTLS related attrs:
- `a=fingerprint`
- `a=setup`
- `a=tls-id`
- `a=ice-ufrag`
- `a=ice-pwd`
- `a=candidate`
Send the original INVITE with `UDP/TLS/RTP/SAVPF` SDP,
including those attrs, and then assert that the B-leg INVITE
still has them.
Also improtve the ` _make_invite()` so that it can accept
an optional SDP body so the new case can reuse existing INVITE builder.
Change-Id: Ib3ff54bec50f364b79e189bd8e61bd42bb3f6503
When parsing SDP and not being in the path
of the media stream, `fingerprint` and `setup`
aren't considered as generic attributes and
are parsed into the dedicated SdpMedia fields.
So when preparing SDP to be printed and
to be sent out, take them also into account.
(Looks like was missed when reworking stuff
last time around media processing)
Change-Id: I48437d3587321a04395b70823207292590ea6523
Gets serialized into the bool on the profile's global level.
Isn't leg specific, but global for the whole session.
Only used to remove SST related headers and option tags
from requests and replies being sent through either
`SBCCallLeg::onSendRequest()` or `SBCCallLeg::onSendReply()`.
Transparent means - do not touch existing SST headers.
SessionTimer implementation factory isn't triggered in this case.
Change-Id: Id98a8abc957be11d140eba2394fed7ebe1302ca7
This is a prolongation of the commit 5085fb134878
In case this very leg (A or B) doesn't support
SST timers, then before sending out the request/reply
to the end-point, make sure to remove SST related stuff:
- `Supported: timer` (option tag)
- `Require: timer` (option tag)
- `Session-Expires` header
- `Min-SE` header
Remove previously introduced manipulations from the
`SBCCallLeg::relayEvent()`, which is rather meant
to process the incoming request/reply event
in the other leg (and not this leg), because then
we mistakingly remove headers checking the wrong leg.
Hence `SBCCallLeg::relayEvent()` -> `AmB2BSession::relayEvent()`
is only responsible to post the relay event for this leg,
and the `SBCCallLeg::onSendRequest()` / `SBCCallLeg::onSendReply()`
are actually handlers for the end view of the SIP message being
sent out.
Additionally: introduce the `SBCCallLeg::onSendReply()` hook,
for the virtual `AmSession::onSendReply()`, that should accordingly
handle SST removal in the scope of SBCCallLeg.
Additionally: introduce the `sessionTimerSupportedByLeg()` static
helper for SBCCallLeg, to check support of SST based on A/B leg awareness.
Change-Id: Icb5d0bc5a62b06e26a46a1bceeb5c73aa8d7bf2a
In case the session originator never declared SST support
(with `aleg_sst_enable=yes`), then remove all possible SST
related headers and `timer` option tags where present.
This gets removed in all sip responses/requests going
towards the caller in this case.
Change-Id: I960fa09d00a8f324665b89d2e3ab0b44cbfcd677
Mirrors the sems-pbx fix for the shared apps/sbc and core code.
When a mid-transfer call is picked up (BLF), SEMS sends a re-INVITE
with the picker's media towards the caller while another re-INVITE is
still pending on that leg. It used to answer 491 and drop the update,
so the call connects with no audio on either side.
The P-Force-491: 0 marker exists to prevent exactly this: it tells the
receiving leg to queue the update instead of 491ing it. But
onB2BEvent() read the marker from the relayed request hdrs - which
SBCCallLeg::relayEvent() has already run the profile's header filter
over. Whitelist profiles don't list the header, so it was stripped and
the 491 fired anyway.
Read the marker earlier, in AmB2BSession::onSipRequest() while the hdrs
are still unfiltered, and carry it as a skip_491 flag on the event. The
queued update is replayed once the pending transaction ends, as before.
This CE codebase has no call_transfer module, so t/test-xdsm-race.py
reproduces the race with generic sbc.putOnHold/sbc.resumeHeld DSM
actions over the same CallLeg.cpp path: the basic race, two stacked
self-updates ahead of a collision, a plain unmarked glare that must
still get 491, and the marker surviving the whitelist filter. Each
collision carries the marker like the real pickup does, so the tests
491 on unpatched code.
Also make the tests robust on a loaded CI: recvB2BINVITE() skips
in-dialog INVITEs (those with a To-tag), and the queued updates are read
by CSeq freshness, so timer-A retransmissions of earlier re-INVITEs
piling up in the socket buffer aren't mistaken for the awaited update.
Change-Id: Ie55f0a2f33a0dde5e2c3dd8dd2fb2c82c67a06c4
For the case when the remote side doesn't explicitly state
it supports the `timer` SIP extension with `Supported: timer`,
try to fallback to `Require: timer` which is a guarantee for the case
with the UAC responsible for the SST timers.
Also add this fallback for the case with requests, e.g. when
UPDATE comes from the remote side to SEMS, and doesn't explicitly
declare the timers support with `Supported: timer` but still adds
the `Require: timer` one (most probably with UAS as a refresher).
Change-Id: Ic2ea79352535413568e4e3e9481cff1a29e43d7a
Normal shutdown already triggers the `clearRtpReceiverRelay()`
in `AmB2BSession::onBeforeDestroy()`, so the destructor should
actually normally see `media_session` ptr nulled.
But it seems in some of the fork/transfer related scenarios
this doesn't happen.
So adding the defensive clean-up on the same manner
the destructor of the `CallLeg` has (it uses the `releaseMediaSession()`,
which is essentially the same: `stop()`; `releaseReference()` and NULLing),
makes it consistent with these other_legs cleanup.
The main reason is caused by a call leg (`AmB2BSession`), which was freed
while the `AmB2BMedia::a` / `AmB2BMedia::b` still points to it.
In this case we can crash during the media-thread DTMF processing.
This fix is a hardening rather than a fix of the root cause.
The root cause still is related to the cleanup path:
certain call leg gets torn down through the path that skips (or maybe even races)
the normal `onBeforeDestroy()`/`clearRtpReceiverRelay()` cleanup,
leaving its shared `AmB2BMedia` with a dangling `a` or `b` pointer.
Meanwhile the `a` / `b` are raw, non-owned pointers, which may remain dangling.
Change-Id: Ia1ee42a27014d2086b37a787f8555e6d7681d4a8
Introduce a pair of getter and setter.
Then use them to appeal to `AmSessionTimerConfig::StrictMode`.
Then move the `AmSessionTimerConfig::StrictMode` to private
scope for incapsulation reasons, this is now possible
because of having the getter and setter.
Change-Id: I1cf6d9bbb30be925b8b885b36cbc123a96d334cb
Upon sending request/reply (`SessionTimer::onSendRequest()` /
`SessionTimer::onSendReply()`) don't re-append `Session-Expires`
(and rest of timer related headers) if this leg doesn't actually
support them.
Appending them may just confuse remote side and it may
act strictly and drop the call (e.g. remote side didn't explicitly
declare it supports timers, but we still sent it `Session-Expires`).
By default we always assume that remote side supports SST timers,
unless not proved the opposite with absent SST related headers
in any first seen 200OK or INVITE/UPDATE request. Then it gets
disabled. This policy is quite explicit and allows to gracefully
step back from SST scheduling, if remote side doesn't support timers.
Change-Id: I8581418c60bde912e01e2429844720dc19034c81
Make both `updateTimer()` overloads be returning true/false,
this will allow to react on the func caller's side to act
accordingly whenever required.
Change-Id: Iba6bfa6b21e74d657652c649064fe0a93ad6f1eb
Check also for requests (as well as for answers) that B2B's profile
actually supports timers, before to proceed with processing.
Change-Id: If86ab5a546040781715812eb882fbdb314462078
Just by default construct as true.
By default we always assume that remote side supports SST timers,
unless not proved the opposite with absent SST related headers
in any first seen 200OK or INVITE/UPDATE request.
Then it gets disabled.
This policy is quite explicit and allows to gracefully
step back from SST scheduling, if remote side doesn't support timers.
Change-Id: I422c76c42d44bcf2e775ffb9afa5842a9def1402
Introduce explicit policy for within-dialog SST support:
- whenever remote side (call leg) doesn't explicitly declare
it supports timers (with `Supported: timer`), disable all
previously set timers for this particular leg
- this touches request originator, as well as answerer
- requests: only UPDATE and INVITE methods are affected
- answers: only positive answers are affected
- by default this behavior is always disabled if not enabled
explicitly in the profile's config with `sst_strict_mode`
- sst_strict_mode takes either `yes` or `no`
P.S.: the `sst_strict_mode` must not be initialized to true
by default, because RFC 4028 explicitly tells to support
the operation even when only one UA understands session timers.
So we have to be quite accurate and not enforce the strict mode.
Change-Id: Ie6647eb061a73dc052367dbd291589198487093c
Only for UPDATE method for now, track whether remote side
actually declares it supports timers, if not remove.
This may affect things, in case remote side expects timers
should have been declared only once during the session
origination, but all in all remote side must always add
the `Supported: timer` header to keep us explicitly aware.
Additionally: improve logs.
Change-Id: I80d4efbe38701b95549d02830392b93019cc2b85
Align the port value accordingly to the requested transport.
Do take the config parameter `contact_hostport` into account
only then, when the contact URI given by DI is empty.
In case DI provides a full contact value, with a transport,
then accordingly inspect it and rebuild from scratch,
using the following uri schema:
`sip + username + @ + host + port + uuid + transport`
Default port values are:
- udp = 5060
- tcp = 5060
- tls = 5061
the config parameter has a value only as a fallback.
Change-Id: Ibf2a7a1d74d438e853c02bfe77478ac62216dbca
recvB2BINVITE now tracks the Call-ID of every completed dialog in a
class-level set (_stale_b2b_cids) and skips any INVITE whose Call-ID
appears in that set. tearDown populates the set from _current_b2b_cid,
which recvB2BINVITE sets on each successful return.
This closes the narrow race window left by the setUp non-blocking drain:
a retransmission that arrives between setUp and the recvB2BINVITE call
can no longer poison b2b_cid for the current test, regardless of timing.
Change-Id: I206ff35e0c04f80d4bcd12eaa4a7dd56acb08152
On a heavily loaded CI host the SIP T1 retransmit timer (500 ms) fires
before the test framework processes responses, leaving extra datagrams
in the shared _uas_sock that survive tearDown. Additionally, SEMS
generates its own Call-ID for B-leg dialogs, so messages from different
tests mix on the same socket.
Changes:
t/sems_tester.py:
recvB2BINVITE(): new helper — reads from shared UAS socket skipping
any non-INVITE datagrams (stale BYE/ACK from previous tests)
recvSIPForCall(sock, b2b_cid): new helper — reads from shared UAS
socket discarding messages whose Call-ID doesn't match the current
B2B dialog
t/test-b2b.py:
tearDown: increase drain timeout 0.5 s → 1.5 s (covers up to 3×T1)
All recvFromSIP(_uas_sock) INVITE reads replaced with
recvB2BINVITE() + b2b_cid = _hdr(…, "Call-ID")
All subsequent recvSIP(_uas_sock) reads replaced with
recvSIPForCall(_uas_sock, b2b_cid) to discard stale datagrams
testInviteRetransmission: "must NOT forward" block filters by Call-ID
testEarlyCancelRace / testRtpDirectionHold: BYE/CANCEL acceptance
conditions extended with `and b2b_cid in msg`
testConcurrentCalls: setup_call() uses recvB2BINVITE +
recvSIPForCall, BYE waits keyed on per-call b2b_call_id
Change-Id: Id8987d3594c487d8b78c38ed3b9f580544a83693
add B2B SBC integration test suite (port from sems-pbx)
Port the 20-test B2B suite from sems-pbx. CE-specific differences:
- sip_port=5062 (avoids conflict with orphaned sockets on :5060)
- default_bl_ttl=0 in t/b2b.conf (disables destination blacklisting
so a 503 response in one test does not block subsequent tests)
- no g729 in load_plugins (codec not present in CE)
- plugin_config_path requires trailing slash
- 100 response matched as "100 [^\r]+" (CE sends "100 Connecting"
instead of "100 Trying")
sems_tester.py: add makeUACSocket/makeUASSocket, recvSIP, recvFromSIP,
sendToSIP, assertSIP helpers; create t/run/ in setUpClass; clean up
PID file in tearDownClass; skip testPreload when rtpe-preload.so is
not loaded.
testEarlyCancelRace: use 10 s socket timeout with inner try/except
so the test passes on loaded CI machines where BYE may arrive late.
Change-Id: Iecb563dee6a27cf46b9883c42154a4486b3f6429
Internally the parsing still happens on pointers, but the externally
visible function will be using string_view eventually, when all other
users are converted.
Change-Id: I87d3be9e41fd96f656ce242edc1c403425f3eb65
If the `postDtmfEvent()` doesn't acquire the ownershop
over the allocated event, then no one actually takes care
to release it.
Modify func to return true/false, and react accordingly
on the caller's side.
Change-Id: If683eeba275d71850f67d961e9a1cdefe3fe6619
In case the override func of virtual `onStop()`
anyhow reaches the AmSession again, and falls into
the scope where the same mutex lock is used (snapshot_lock)
this will be a dead-lock.
Hence preserve such, but manually managing the lock,
and release it, when calling the virtual func.
Change-Id: I7c7c4700767dd3aed1c482559be02e3a97cca407
No need to do local copy operations, because:
- local copy on stack created
- then another copy on stack created when returning
So this does two copy operations.
Instead just do one during return, this is still covered
with the lock guard.
Additionally: split `getMaxSessionNum()` into getter
and setter, because the name is misleading now.
Hence those callers, who want to get the max session number,
are obligated to also update to the new value.
Change-Id: Icc775d9236d5c80de829c55638e1da47ae095289