From ce90db7205922fd0ee0258a969c336671363bf5c Mon Sep 17 00:00:00 2001 From: bpintea Date: Fri, 19 Nov 2010 11:30:37 +0100 Subject: [PATCH] rework of 100rel support The 100rel logic moved in great part to AmSipDialog; this way, it can be hooked directly to request/reply processing routines, the ones on top of the transactional layer (::updateStatus(..)). This allows to: - better control when to push the messages into the AmSession's onSipRequest/~Reply, fixing some issues with applications that received replies, but without sending themselves the requests - insert needed headers, no matter which request function is used (::invite() vs. ..sendRequest()) --- apps/registrar_client/SIPRegistrarClient.h | 4 + core/AmB2BSession.cpp | 3 +- core/AmConfig.cpp | 2 +- core/AmConfig.h | 3 +- core/AmSession.cpp | 207 ++++--------------- core/AmSession.h | 44 +--- core/AmSipDialog.cpp | 222 ++++++++++++++++++++- core/AmSipDialog.h | 38 +++- 8 files changed, 307 insertions(+), 216 deletions(-) diff --git a/apps/registrar_client/SIPRegistrarClient.h b/apps/registrar_client/SIPRegistrarClient.h index 06326a54..6b92cf26 100644 --- a/apps/registrar_client/SIPRegistrarClient.h +++ b/apps/registrar_client/SIPRegistrarClient.h @@ -129,6 +129,10 @@ class SIPRegistration : public AmSipDialogEventHandler, void onInvite2xx(const AmSipReply&) {} void onNoAck(unsigned int) {} void onNoPrack(const AmSipRequest &, const AmSipReply &) {} + void onInvite1xxRel(const AmSipReply &){} + void onPrack2xx(const AmSipReply &){} + void onFailure(AmSipDialogEventHandler::FailureCause cause, + const AmSipRequest*, const AmSipReply*){} /** is this registration registered? */ bool active; diff --git a/core/AmB2BSession.cpp b/core/AmB2BSession.cpp index 029a4be8..ec23ac7a 100644 --- a/core/AmB2BSession.cpp +++ b/core/AmB2BSession.cpp @@ -356,8 +356,7 @@ int AmB2BSession::sendEstablishedReInvite() { } DBG("sending re-INVITE with saved session description\n"); - return dlg.reinvite(get_100rel_hdr(reliable_1xx), - established_content_type, established_body, + return dlg.reinvite("", established_content_type, established_body, SIP_FLAGS_VERBATIM); } diff --git a/core/AmConfig.cpp b/core/AmConfig.cpp index 8ea2c904..ed630762 100644 --- a/core/AmConfig.cpp +++ b/core/AmConfig.cpp @@ -92,7 +92,7 @@ unsigned int AmConfig::OptionsSessionLimit = 0; unsigned int AmConfig::OptionsSessionLimitErrCode = 503; string AmConfig::OptionsSessionLimitErrReason = "Server overload"; -unsigned char AmConfig::rel100 = REL100_SUPPORTED; +AmSipDialog::provisional_100rel AmConfig::rel100 = REL100_SUPPORTED; vector AmConfig::CodecOrder; diff --git a/core/AmConfig.h b/core/AmConfig.h index f587b400..aba0911a 100644 --- a/core/AmConfig.h +++ b/core/AmConfig.h @@ -30,6 +30,7 @@ #include "AmSdp.h" #include "AmDtmfDetector.h" +#include "AmSipDialog.h" #include using std::string; @@ -137,7 +138,7 @@ struct AmConfig static unsigned int OptionsSessionLimitErrCode; static string OptionsSessionLimitErrReason; - static unsigned char rel100; + static AmSipDialog::provisional_100rel rel100; /** Time of no RTP after which Session is regarded as dead, 0 for no Timeout */ static unsigned int DeadRtpTime; diff --git a/core/AmSession.cpp b/core/AmSession.cpp index 5569c040..dbe9002b 100644 --- a/core/AmSession.cpp +++ b/core/AmSession.cpp @@ -66,7 +66,6 @@ AmSession::AmSession() m_dtmfDetector(this), m_dtmfEventQueue(&m_dtmfDetector), m_dtmfDetectionEnabled(true), accept_early_session(false), - reliable_1xx(AmConfig::rel100), refresh_method(REFRESH_UPDATE_FB_REINV), processing_status(SESSION_PROCESSING_EVENTS) #ifdef WITH_ZRTP @@ -79,8 +78,6 @@ AmSession::AmSession() { use_local_audio[AM_AUDIO_IN] = false; use_local_audio[AM_AUDIO_OUT] = false; - if (reliable_1xx) - dlg.rseq = 0; //??? } AmSession::~AmSession() @@ -687,40 +684,6 @@ void AmSession::onSipRequest(const AmSipRequest& req) if(req.method == SIP_METH_INVITE){ - switch(reliable_1xx) { - case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */ - if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED), - SIP_EXT_100REL) || - key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), - SIP_EXT_100REL)) { - reliable_1xx = REL100_REQUIRE; - DBG(SIP_EXT_100REL " now active.\n"); - } - break; - case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */ - if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED), - SIP_EXT_100REL) || - key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), - SIP_EXT_100REL))) { - ERROR("'" SIP_EXT_100REL "' extension required, but not advertised" - " by peer.\n"); - dlg.reply(req, 421, "Extension Required", "", "", - SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF); - if (dlg.getStatus() < AmSipDialog::Connected) - setStopped(); - return; - } - break; // 100rel required - default: - ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.", - reliable_1xx); -#ifndef NDEBUG - abort(); -#endif - case 0: /* support disabled */ - break; - } - try { onInvite(req); } @@ -765,23 +728,9 @@ void AmSession::onSipRequest(const AmSipRequest& req) } else { dlg.reply(req, 415, "Unsupported Media Type"); } - } else if( req.method == SIP_METH_PRACK ) { - if (reliable_1xx != REL100_REQUIRE) { - WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n"); - return; - } - - if ((1< timed out.\n", rpl.print().c_str()); - if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE && - (unsigned)dlg.rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) { - INFO("reliable %d reply timed out; rejecting request.\n", rpl.code); - dlg.reply(req, 504, "Server Time-out"); - // TODO: handle forking case (when more PRACKs are sent, out of which some - // might time-out/fail). - if (dlg.getStatus() < AmSipDialog::Connected) - setStopped(); - } else { - WARN("reply timed-out, but not reliable.\n"); // debugging - } + dlg.reply(req, 504, "Server Time-out"); + // TODO: handle forking case (when more PRACKs are sent, out of which some + // might time-out/fail). + if (dlg.getStatus() < AmSipDialog::Connected) + setStopped(); } void AmSession::onAudioEvent(AmAudioEvent* audio_ev) @@ -982,12 +883,6 @@ void AmSession::onBye(const AmSipRequest& req) setStopped(); } -void AmSession::onPrack(const AmSipRequest& req, unsigned cnt) -{ - DBG("handling #%u PRACK.\n", cnt); - dlg.reply(req, 200, "OK"); -} - int AmSession::acceptAudio(const string& body, const string& hdrs, string* sdp_reply) @@ -1050,46 +945,6 @@ void AmSession::onSendReply(const AmSipRequest& req, unsigned int code, const string& reason, const string& content_type, const string& body, string& hdrs, int flags) { - if (req.method == SIP_METH_INVITE) { - if (100 < code && code < 200) { - switch (reliable_1xx) { - case REL100_SUPPORTED: - hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; - break; - case REL100_REQUIRE: - // add Require HF - hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; - // add RSeq HF -#ifndef NDEBUG - if ((abs(dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == - ((1 << MAX_RSEQ_BITS) - 1)) { - ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS " - "(now %d) and recompile.\n", MAX_RSEQ_BITS); - abort(); - } -#endif - if (dlg.rseq < 0) { // RSeq not yet PRACKed - // refuse subsequent 1xx if first isn't yet PRACKed - if ((((unsigned)-dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0) - throw AmSession::Exception(491, "last reliable 1xx not yet " - "PRACKed"); - dlg.rseq --; - } else if (! dlg.rseq) { // only init rseq if 1xx is used - unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS; - rseq_1st &= 0x7fffffff; - dlg.rseq = -((signed)rseq_1st); - } else { - dlg.rseq = -(++dlg.rseq); - } - hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-dlg.rseq) + CRLF; - break; - } - } else if (code < 300 && reliable_1xx == REL100_REQUIRE) { - if (dlg.rseq < 0) // reliable 1xx is pending - throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed"); - } - } - CALL_EVENT_H(onSendReply,req,code,reason,content_type,body,hdrs,flags); } @@ -1137,19 +992,18 @@ int AmSession::sendUpdate(const string &cont_type, const string &body, return dlg.update(cont_type, body, hdrs); } -void AmSession::sendPrack(const string &sdp_offer, - const string &rseq_val, - const string &cseq_val) +void AmSession::onInvite1xxRel(const AmSipReply &reply) { - string hdrs = "RAck: " + rseq_val + " " + cseq_val + "\r\n"; - - // TODO: digest an answer based on the sdp_offer - - // TODO: should't cseq&rseq be handled in dialog, entirely?!?! - if (dlg.prack(/*cont. type*/"", /*body*/"", hdrs) < 0) + // TODO: SDP + if (dlg.prack(reply, /*cont. type*/"", /*body*/"", /*headers*/"") < 0) ERROR("failed to send PRACK request in session '%s'.\n",sid4dbg().c_str()); } +void AmSession::onPrack2xx(const AmSipReply &reply) +{ + /* TODO: SDP */ +} + string AmSession::sid4dbg() { string dbg; @@ -1165,10 +1019,9 @@ int AmSession::sendReinvite(bool updateSDP, const string& headers, int flags) RTPStream()->setLocalIP(AmConfig::LocalIP); string sdp_body; sdp.genResponse(advertisedIP(), RTPStream()->getLocalPort(), sdp_body); - return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP, - sdp_body, flags); + return dlg.reinvite(headers, SIP_APPLICATION_SDP, sdp_body, flags); } else { - return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", "", flags); + return dlg.reinvite(headers, "", "", flags); } } @@ -1183,8 +1036,7 @@ int AmSession::sendInvite(const string& headers) // Generate SDP. string sdp_body; sdp.genRequest(advertisedIP(), RTPStream()->getLocalPort(), sdp_body); - return dlg.invite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP, - sdp_body); + return dlg.invite(headers, SIP_APPLICATION_SDP, sdp_body); } void AmSession::setOnHold(bool hold) @@ -1197,6 +1049,27 @@ void AmSession::setOnHold(bool hold) unlockAudio(); } +void AmSession::onFailure(AmSipDialogEventHandler::FailureCause cause, + const AmSipRequest *req, const AmSipReply *rpl) +{ + switch (cause) { + case FAIL_REL100: + if (rpl) { + dlg.cancel(); + if (dlg.getStatus() < AmSipDialog::Connected) + setStopped(); + } else if (req) { + dlg.reply(*req, 421, "Extension Required", "", "", + SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF); + if (dlg.getStatus() < AmSipDialog::Connected) + setStopped(); + } + break; + default: + break; + } +} + // Utility for basic NAT handling: allow the config file to specify the IP // address to use in SDP bodies string AmSession::advertisedIP() diff --git a/core/AmSession.h b/core/AmSession.h index c784da23..ab3eec64 100644 --- a/core/AmSession.h +++ b/core/AmSession.h @@ -166,13 +166,6 @@ protected: /** do accept early session? */ bool accept_early_session; - /** enable the reliability of provisional replies? */ - unsigned char reliable_1xx; -#define REL100_DISABLED 0 -#define REL100_SUPPORTED 1 -#define REL100_REQUIRE 2 -#define REL100_MAX REL100_REQUIRE - vector ev_handlers; public: @@ -373,11 +366,6 @@ public: /** send an INVITE */ virtual int sendInvite(const string& headers = ""); - /** send a PRACK request */ - void sendPrack(const string &sdp_offer, - const string &rseq_val, - const string &cseq_val); - /** set the session on/off hold */ virtual void setOnHold(bool hold); @@ -469,14 +457,6 @@ public: */ virtual void onCancel() {} - /** - * onPrack is called when a PRACK request is received for the session. - * The sequencing correctness (RAck fits) is already checked. - * Should be overridden if SDP offer is expected with it. - * @param cnt order of which 1xx this PRACK is for - */ - virtual void onPrack(const AmSipRequest& req, unsigned cnt); - /** * onSessionStart will be called after call setup. * @@ -528,6 +508,15 @@ public: /** 2xx reply has been received for an INVITE transaction */ virtual void onInvite2xx(const AmSipReply& reply); + + + virtual void onInvite1xxRel(const AmSipReply &); + + /** Hook called when an answer for a locally sent PRACK is received */ + virtual void onPrack2xx(const AmSipReply &); + + virtual void onFailure(AmSipDialogEventHandler::FailureCause cause, + const AmSipRequest*, const AmSipReply*); #if 0 /** missing 2xx-ACK */ @@ -602,21 +591,6 @@ inline AmRtpAudio* AmSession::RTPStream() { return _rtp_str.get(); } -static inline string get_100rel_hdr(unsigned char reliable_1xx) -{ - switch(reliable_1xx) { - case REL100_SUPPORTED: - return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; - case REL100_REQUIRE: - return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; - default: - ERROR("BUG: unexpected reliability switch value of '%d'.\n", - reliable_1xx); - case 0: - break; - } - return ""; -} #endif diff --git a/core/AmSipDialog.cpp b/core/AmSipDialog.cpp index c234af37..426171c2 100644 --- a/core/AmSipDialog.cpp +++ b/core/AmSipDialog.cpp @@ -43,7 +43,9 @@ const char* AmSipDialog::status2str[4] = { AmSipDialog::AmSipDialog(AmSipDialogEventHandler* h) : status(Disconnected),cseq(10),r_cseq_i(false),hdl(h),pending_invites(0), outbound_proxy(AmConfig::OutboundProxy), - force_outbound_proxy(AmConfig::ForceOutboundProxy) + force_outbound_proxy(AmConfig::ForceOutboundProxy), + reliable_1xx(AmConfig::rel100), + rseq(0) { } @@ -129,11 +131,68 @@ void AmSipDialog::updateStatus(const AmSipRequest& req) local_party = req.to; route = req.route; } - - if(hdl) + + int cont = rel100OnRequestIn(req); + + if(cont && hdl) hdl->onSipRequest(req); } +int AmSipDialog::rel100OnRequestIn(const AmSipRequest& req) +{ + /* activate the 100rel, if needed */ + if (req.method == SIP_METH_INVITE) { + switch(reliable_1xx) { + case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */ + if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED), + SIP_EXT_100REL) || + key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), + SIP_EXT_100REL)) { + reliable_1xx = REL100_REQUIRE; + DBG(SIP_EXT_100REL " now active.\n"); + } + break; + case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */ + if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED), + SIP_EXT_100REL) || + key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), + SIP_EXT_100REL))) { + ERROR("'" SIP_EXT_100REL "' extension required, but not advertised" + " by peer.\n"); + if (hdl) hdl->onFailure(FAIL_REL100, &req, 0); + return 0; // has been replied + } + break; // 100rel required + default: + ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.", + reliable_1xx); +#ifndef NDEBUG + abort(); +#endif + case 0: /* support disabled */ + break; + } // switch reliable_1xx + } else if (req.method == SIP_METH_PRACK) { + if (reliable_1xx != REL100_REQUIRE) { + WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n"); + // let if float up + } else if ((1<= 200){ @@ -304,10 +364,62 @@ void AmSipDialog::updateStatus(const AmSipReply& reply) } } - if(hdl) + if(cont && hdl) hdl->onSipReply(reply, old_dlg_status, trans_method); } + +int AmSipDialog::rel100OnReplyIn(const AmSipReply &reply) +{ + if (status!=Pending && status!=Connected) + return 1; + + if (100onFailure(FAIL_REL100, 0, &reply); + } else { + DBG(SIP_EXT_100REL " now active.\n"); + if (hdl) hdl->onInvite1xxRel(reply); + } + break; + + case 0: + // 100rel support disabled + break; + default: + ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.", + reliable_1xx); +#ifndef NDEBUG + abort(); +#endif + } // switch reliable 1xx + } else if (reliable_1xx && reply.method==SIP_METH_PRACK) { + if (300 <= reply.code) { + // if PRACK fails, tear down session + if (hdl) hdl->onFailure(FAIL_REL100, 0, &reply); + } else if (200 <= reply.code) { + if (hdl) hdl->onPrack2xx(reply); + } else { + WARN("received '%d' for " SIP_METH_PRACK " method.\n", reply.code); + } + // absorbe the replys for the prack (they've been dispatched through + // onPrack2xx, if necessary) + return 0; + } + return 1; +} + void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev) { assert(to_ev); @@ -320,7 +432,7 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev) case AmSipTimeoutEvent::noPRACK: DBG("Timeout: missing PRACK\n"); - if(hdl) hdl->onNoPrack(to_ev->req, to_ev->rpl); + rel100OnTimeout(to_ev->req, to_ev->rpl); break; case AmSipTimeoutEvent::_noEv: @@ -331,6 +443,20 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev) to_ev->processed = true; } +void AmSipDialog::rel100OnTimeout(const AmSipRequest &req, + const AmSipReply &rpl) +{ + INFO("reply <%s> timed out (not PRACKed).\n", rpl.print().c_str()); + if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE && + (unsigned)rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) { + INFO("reliable %d reply timed out; rejecting request.\n", rpl.code); + if(hdl) hdl->onNoPrack(req, rpl); + } else { + WARN("reply timed-out, but not reliable.\n"); // debugging + } +} + + bool AmSipDialog::getUACTransPending() { return !uac_trans.empty(); } @@ -382,6 +508,8 @@ int AmSipDialog::reply(const AmSipRequest& req, hdl->onSendReply(req,code,reason, content_type,body,m_hdrs,flags); + rel100OnReplyOut(req, code, m_hdrs); + AmSipReply reply; reply.method = req.method; @@ -415,6 +543,53 @@ int AmSipDialog::reply(const AmSipRequest& req, return ret; } + +void AmSipDialog::rel100OnReplyOut(const AmSipRequest &req, unsigned int code, + string &hdrs) +{ + if (req.method == SIP_METH_INVITE) { + if (100 < code && code < 200) { + switch (reliable_1xx) { + case REL100_SUPPORTED: + hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; + break; + case REL100_REQUIRE: + // add Require HF + hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; + // add RSeq HF +#ifndef NDEBUG + if ((abs(rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == + ((1 << MAX_RSEQ_BITS) - 1)) { + ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS " + "(now %d) and recompile.\n", MAX_RSEQ_BITS); + abort(); + } +#endif + if (rseq < 0) { // RSeq not yet PRACKed + // refuse subsequent 1xx if first isn't yet PRACKed + if ((((unsigned)-rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0) + throw AmSession::Exception(491, "last reliable 1xx not yet " + "PRACKed"); + rseq --; + } else if (! rseq) { // only init rseq if 1xx is used + unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS; + rseq_1st &= 0x7fffffff; + rseq = -((signed)rseq_1st); + } else { + rseq = -(++rseq); + } + hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-rseq) + CRLF; + break; + default: + break; + } + } else if (code < 300 && reliable_1xx == REL100_REQUIRE) { + if (rseq < 0) // reliable 1xx is pending + throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed"); + } + } +} + /* static */ int AmSipDialog::reply_error(const AmSipRequest& req, unsigned int code, const string& reason, const string& hdrs) @@ -587,15 +762,16 @@ int AmSipDialog::transfer(const string& target) return 0; } -int AmSipDialog::prack(const string &cont_type, +int AmSipDialog::prack(const AmSipReply &reply1xx, + const string &cont_type, const string &body, const string &hdrs) { switch(status) { case Pending: + case Connected: break; case Disconnected: - case Connected: case Disconnecting: ERROR("can not send PRACK while dialog is in state '%d'.\n", status); return -1; @@ -603,7 +779,12 @@ int AmSipDialog::prack(const string &cont_type, ERROR("BUG: unexpected dialog state '%d'.\n", status); return -1; } - return sendRequest(SIP_METH_PRACK, cont_type, body, hdrs); + string h = hdrs + + SIP_HDR_COLSP(SIP_HDR_RACK) + + int2str(reply1xx.rseq) + " " + + int2str(reply1xx.cseq) + " " + + reply1xx.method + CRLF; + return sendRequest(SIP_METH_PRACK, cont_type, body, h); } int AmSipDialog::cancel() @@ -633,6 +814,8 @@ int AmSipDialog::sendRequest(const string& method, if(hdl) hdl->onSendRequest(method,content_type,body,m_hdrs,flags,cseq); + rel100OnRequestOut(method, m_hdrs); + AmSipRequest req; req.method = method; @@ -692,6 +875,27 @@ int AmSipDialog::sendRequest(const string& method, return 0; } +void AmSipDialog::rel100OnRequestOut(const string &method, string &hdrs) +{ + if (method!=SIP_METH_INVITE) // && method!=SIP_METH_OPTIONS) + return; + + switch(reliable_1xx) { + case REL100_SUPPORTED: + hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF; + return; + case REL100_REQUIRE: + hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF; + return; + default: + ERROR("BUG: unexpected reliability switch value of '%d'.\n", + reliable_1xx); + case 0: + break; + } +} + + string AmSipDialog::get_uac_trans_method(unsigned int cseq) { TransMap::iterator t = uac_trans.find(cseq); diff --git a/core/AmSipDialog.h b/core/AmSipDialog.h index 6eb03cb4..a95f3590 100644 --- a/core/AmSipDialog.h +++ b/core/AmSipDialog.h @@ -103,6 +103,19 @@ class AmSipDialogEventHandler /** Hook called when a UAS INVITE transaction did not receive the PRACK */ virtual void onNoPrack(const AmSipRequest &, const AmSipReply &)=0; + /** Hook called when a privisional reply is received with 100rel active */ + virtual void onInvite1xxRel(const AmSipReply &)=0; + + /** Hook called when an answer for a locally sent PRACK is received */ + virtual void onPrack2xx(const AmSipReply &)=0; + + enum FailureCause { + FAIL_REL100, +#define FAIL_REL100 AmSipDialogEventHandler::FAIL_REL100 + }; + virtual void onFailure(FailureCause cause, const AmSipRequest*, + const AmSipReply*)=0; + virtual ~AmSipDialogEventHandler() {}; }; @@ -123,6 +136,14 @@ class AmSipDialog int updateStatusReply(const AmSipRequest& req, unsigned int code); + int rel100OnRequestIn(const AmSipRequest &req); + int rel100OnReplyIn(const AmSipReply &reply); + void rel100OnTimeout(const AmSipRequest &req, const AmSipReply &rpl); + + void rel100OnRequestOut(const string &method, string &hdrs); + void rel100OnReplyOut(const AmSipRequest &req, unsigned int code, + string &hdrs); + public: enum Status { @@ -156,6 +177,20 @@ class AmSipDialog string next_hop_ip; unsigned short next_hop_port; + /** enable the reliability of provisional replies? */ + enum provisional_100rel { // could be a char + REL100_DISABLED, +#define REL100_DISABLED AmSipDialog::REL100_DISABLED + REL100_SUPPORTED, +#define REL100_SUPPORTED AmSipDialog::REL100_SUPPORTED + REL100_REQUIRE, + //REL100_PREFERED, //TODO +#define REL100_REQUIRE AmSipDialog::REL100_REQUIRE + REL100_MAX +#define REL100_MAX AmSipDialog::REL100_MAX + }; + enum provisional_100rel reliable_1xx; + int rseq; // RSeq for next request (NOTE: keep it signed!) unsigned int cseq; // Local CSeq for next request bool r_cseq_i; @@ -212,7 +247,8 @@ class AmSipDialog int cancel(); /** @return 0 on success */ - int prack(const string &cont_type, + int prack(const AmSipReply &reply1xx, + const string &cont_type, const string &body, const string &hdrs);