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; }