From 91cd45fe7bbfaf49850b474fc4c781c6ec0186a5 Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Thu, 5 Jan 2023 11:49:21 +0100 Subject: [PATCH] MT#56321 Treat 183 as 200OK (other leg), when early media forced There is a need to add an exception processing for 183 Session Progress, and the following in-dialog requests/responses, in case the 183 has been previously sent to the B2B with the hf 'P-Early-Announce: force'. This adds the following behavior. When 183 is received, and the caller has been updated with the new media capabilities, according to those needed to embed early media into the media session with the caller (via re-INVITE), then: - even though the 183 is treated similarly to 200OK in terms of media updates, do not send ACK to the leg going towards DSM, becausethe sems-b2b giving the DSM, it's still in stage INVITE/183 - do not re-negotiate the leg going towards the DSM, after the caller has been updated with the new media (a usual behavior) because the sems-b2b giving the DSM still considers the dialog here in the Early stage - do not set the leg going towards DSM into the Connected state, because by the fact, it's still in the Early stage, and setting of it into the Connected state, will break processing of BYE / CANCEL. - upon receiving the BYE from the caller (after the DSM announce is heard), answer right away with 200OK, but do not forward BYE to the let going towards DSM. Instead initiate the CANCEL towards it, because this leg is technically still in the Early stage of the dialog. Original ticket number: TT#187351 Change-Id: Id6e05202add1bcbd358eecbcd5e2cbda1a995b32 --- apps/sbc/CallLeg.cpp | 33 ++++++++++++++------ core/AmB2BSession.cpp | 63 +++++++++++++++++++++++++++++---------- core/AmB2BSession.h | 8 +++++ core/AmBasicSipDialog.cpp | 5 ++-- core/AmSipDialog.cpp | 39 ++++++++++++++++++++++-- core/AmSipDialog.h | 9 +++++- 6 files changed, 127 insertions(+), 30 deletions(-) diff --git a/apps/sbc/CallLeg.cpp b/apps/sbc/CallLeg.cpp index 6e5ab876..86927a03 100644 --- a/apps/sbc/CallLeg.cpp +++ b/apps/sbc/CallLeg.cpp @@ -504,33 +504,48 @@ void CallLeg::b2bInitial1xx(AmSipReply& reply, bool forward) void CallLeg::b2bInitial2xx(AmSipReply& reply, bool forward) { if (!setOther(reply.from_tag, forward)) { - // ignore reply which comes from non-our-peer leg? + /* ignore reply which comes from non-our-peer leg? */ DBG("2xx reply received from unknown B leg, ignoring\n"); return; } DBG("setting call status to connected with leg %s\n", getOtherId().c_str()); - // terminate all other legs than the connected one (determined by other_id) + /* terminate all other legs than the connected one (determined by other_id) */ terminateNotConnectedLegs(); - // connect media with the other leg if RTP relay is enabled + /* connect media with the other leg if RTP relay is enabled */ if (!other_legs.empty()) - other_legs.begin()->releaseMediaSession(); // remove reference hold by OtherLegInfo - other_legs.clear(); // no need to remember the connected leg here + other_legs.begin()->releaseMediaSession(); /* remove reference hold by OtherLegInfo */ + other_legs.clear(); /* no need to remember the connected leg here */ onCallConnected(reply); if (!forward) { - // we need to generate re-INVITE based on received SDP + /* we need to generate re-INVITE based on received SDP + but only if this is not previously faked 183 as 200OK, TT#187351 */ saveSessionDescription(reply.body); sendEstablishedReInvite(); - } - else if (relaySipReply(reply) != 0) { + if (dlg->getFaked183As200()) { + DBG("Re-INVITE will be not send to update the leg, because there was a faked 183 as 200OK.\n"); + //dlg->setFaked183As200(false); // should we reset upon media re-negotiation in both legs? + // but then it breaks the BYE -> CANCEL conversion. + } else { + DBG("Re-INVITE will be send to update the leg with the last media capabilities.\n"); + sendEstablishedReInvite(); + } + + } else if (relaySipReply(reply) != 0) { stopCall(StatusChangeCause::InternalError); return; } - updateCallStatus(Connected, &reply); + + /* TT#187351, do not set the leg going towards sems DSM applications + * to the connected state, if this has been previously faked (183 considered as 200OK) + * otherwise call cancelation/termination will not work properly for this leg. + */ + if (!dlg->getFaked183As200() && !dlg->getForcedEarlyAnnounce()) + updateCallStatus(Connected, &reply); } void CallLeg::onInitialReply(B2BSipReplyEvent *e) diff --git a/core/AmB2BSession.cpp b/core/AmB2BSession.cpp index 527d6780..3c1fad84 100644 --- a/core/AmB2BSession.cpp +++ b/core/AmB2BSession.cpp @@ -143,6 +143,20 @@ void AmB2BSession::finalize() AmSession::finalize(); } +void AmB2BSession::sl_reply(const string &method, unsigned cseq, bool forward, int sip_code, const char *reason) +{ + if (method != SIP_METH_ACK) { + AmSipReply n_reply; + n_reply.code = sip_code; + n_reply.reason = reason; + n_reply.cseq = cseq; + n_reply.cseq_method = method; + n_reply.from_tag = dlg->getLocalTag(); + DBG("relaying stateless B2B SIP reply %d %s\n", sip_code, reason); + relayEvent(new B2BSipReplyEvent(n_reply, forward, method, getLocalTag())); + } +} + void AmB2BSession::relayError(const string &method, unsigned cseq, bool forward, int err_code) { @@ -212,24 +226,34 @@ void AmB2BSession::onB2BEvent(B2BEvent* ev) return; } - int res = relaySip(req_ev->req); - if(res < 0) { - // reply relayed request internally - relayError(req_ev->req.method, req_ev->req.cseq, true, res); - return; - } + /* relay, unless it's a BYE dedicated for other leg with a faked 183 */ + int res = 0; + + if (req_ev->req.method == SIP_METH_BYE && dlg->getFaked183As200()) { + DBG("This BYE will not forwarded, because other leg is a faked 183 to 200OK. CANCEL required.\n"); + /* for now just answer with 200 OK, later on we must send CANCEL to the Early stage leg */ + sl_reply(req_ev->req.method, req_ev->req.cseq, true, 200, "OK"); + } else { + res = relaySip(req_ev->req); /* most requests get here */ } - - if( (req_ev->req.method == SIP_METH_BYE) - // CANCEL is handled differently: other side has already - // sent a terminate event. - //|| (req_ev->req.method == SIP_METH_CANCEL) - ) { - - onOtherBye(req_ev->req); + + if (res < 0) { + /* reply relayed request internally */ + relayError(req_ev->req.method, req_ev->req.cseq, true, res); + return; } } - return; + + if (req_ev->req.method == SIP_METH_BYE) { + /* CANCEL is handled differently: other side has already + sent a terminate event. + || (req_ev->req.method == SIP_METH_CANCEL) */ + + if (dlg->getFaked183As200()) onOtherCancel(); + else onOtherBye(req_ev->req); + } + } + return; case B2BSipReply: { @@ -602,6 +626,15 @@ void AmB2BSession::onOtherBye(const AmSipRequest& req) terminateLeg(); } +void AmB2BSession::onOtherCancel() +{ + DBG("The other leg will be canceled, because still in the Early stage.\n"); + + setStopped(); + clearRtpReceiverRelay(); + dlg->cancel(); +} + bool AmB2BSession::onOtherReply(const AmSipReply& reply) { if(reply.code >= 300) diff --git a/core/AmB2BSession.h b/core/AmB2BSession.h index 454430bd..55d17b8a 100644 --- a/core/AmB2BSession.h +++ b/core/AmB2BSession.h @@ -203,6 +203,7 @@ private: public: + void sl_reply(const string &method, unsigned cseq, bool forward, int sip_code, const char *reason); void relayError(const string &method, unsigned cseq, bool forward, int sip_code, const char *reason); void relayError(const string &method, unsigned cseq, bool forward, int err_code); @@ -254,6 +255,13 @@ private: /** handle BYE on other leg */ virtual void onOtherBye(const AmSipRequest& req); + /** handle CANCEL on other leg, TT#187351 + * This is a convertion of the BYE into the CANCEL for + * the other leg, because previously 183 message processing + * has been faked, and treated as 200OK. + */ + virtual void onOtherCancel(); + /** * Reply received from other leg has been replied * @return true if reply was processed (should be absorbed) diff --git a/core/AmBasicSipDialog.cpp b/core/AmBasicSipDialog.cpp index 8a9fc040..4432e1aa 100644 --- a/core/AmBasicSipDialog.cpp +++ b/core/AmBasicSipDialog.cpp @@ -80,9 +80,8 @@ bool AmBasicSipDialog::getUACTransPending() void AmBasicSipDialog::setStatus(Status new_status) { - DBG("setting SIP dialog status: %s->%s\n", - getStatusStr(), getStatusStr(new_status)); - + DBG("setting SIP dialog status: %s->%s. Local_tag: <%s>\n", + getStatusStr(), getStatusStr(new_status), getLocalTag().c_str()); status = new_status; } diff --git a/core/AmSipDialog.cpp b/core/AmSipDialog.cpp index 9fa92cd3..2f9fe60f 100644 --- a/core/AmSipDialog.cpp +++ b/core/AmSipDialog.cpp @@ -65,7 +65,7 @@ AmSipDialog::AmSipDialog(AmSipDialogEventHandler* h) offeranswer_enabled(true), early_session_started(false),session_started(false), pending_invites(0), - sdp_local(), sdp_remote(), force_early_announce(false) + sdp_local(), sdp_remote(), faked_183_as_200(false), force_early_announce(false) { } @@ -406,7 +406,10 @@ bool AmSipDialog::onRxReplyStatus(const AmSipReply& reply) /* 100-199 */ if (reply.code < 200) { - DBG("ignoring provisional reply in Early state"); + + string announce = getHeader(reply.hdrs, SIP_HDR_P_EARLY_ANNOUNCE, true); + setForcedEarlyAnnounce(announce.find("force") != std::string::npos); + /* we should always keep Route set for this leg updated in case the provisional response updates the list of routes for any reason */ if ((reply.code == 180 || reply.code == 183) && !reply.route.empty()) { @@ -415,11 +418,39 @@ bool AmSipDialog::onRxReplyStatus(const AmSipReply& reply) setRouteSet(reply.route); } + /* exceptionally treat 183 with the 'P-Early-Announce: force', + similarly to the 200OK response, this will properly update the caller + with the late SDP capabilities (an early announcement), + which has been put on hold during the transfer + + And furthermore will give the possibility to receive and forward BYE. + + DSM applications using it: + - early_dbprompt (early_announce) + - pre_announce */ + if (reply.code == 183 && !announce.empty() && getForcedEarlyAnnounce()) { + DBG("This is 183 with , treated exceptionally as 200OK.\n"); + + setStatus(Connected); + setFaked183As200(true); /* remember that this is a faked 200OK, indeed 183 */ + + if (reply.to_tag.empty()) { + DBG("received 2xx reply without to-tag (callid=%s): sending BYE\n", + reply.callid.c_str()); + sendRequest(SIP_METH_BYE); + } else { + setRemoteTag(reply.to_tag); + } + } + /* 200-299 */ } else if(reply.code < 300) { setStatus(Connected); setRouteSet(reply.route); + /* reset faked 183, if was previously set and this is 200OK received in this leg */ + if (getFaked183As200()) setFaked183As200(false); + if (reply.to_tag.empty()) { DBG("received 2xx reply without to-tag (callid=%s): sending BYE\n", reply.callid.c_str()); @@ -478,6 +509,10 @@ bool AmSipDialog::onRxReplyStatus(const AmSipReply& reply) } bool cont = true; + + /* For those exceptional 183 with the 'P-Early-Announce: force' + we don't want to fully imitate 200OK processing, and send ACK + further processing with ACK is only applied to real 200OK responses */ if ( (reply.code >= 200) && (reply.code < 300) && (reply.cseq_method == SIP_METH_INVITE) ) { diff --git a/core/AmSipDialog.h b/core/AmSipDialog.h index 2cfb8e42..36ec3eef 100644 --- a/core/AmSipDialog.h +++ b/core/AmSipDialog.h @@ -59,8 +59,12 @@ protected: // Reliable provisional reply support Am100rel rel100; - /* dialog variables needed to properly handle 183->200OK faking */ + /* Needed to properly handle 183->200OK faking, when 'P-Early-Announce: force' + * is added into 183, and we have to treat 183 similarly to 200OK. + * Means, we have to embed the early media into already established media session. + */ bool force_early_announce; + bool faked_183_as_200; int onTxReply(const AmSipRequest& req, AmSipReply& reply, int& flags); int onTxRequest(AmSipRequest& req, int& flags); @@ -103,6 +107,9 @@ protected: bool getOAForceSDP() const { return oa.getForceSDP(); } /* getter/setter for faked 183 as 200OK responses, TT#187351 */ + void setFaked183As200(bool value) { faked_183_as_200 = value; } + bool getFaked183As200() { return faked_183_as_200; } + void setForcedEarlyAnnounce(bool value) { force_early_announce = value; } bool getForcedEarlyAnnounce() { return force_early_announce; }