diff --git a/apps/conference/etc/conference.conf b/apps/conference/etc/conference.conf index 89087b6d..56697f4d 100644 --- a/apps/conference/etc/conference.conf +++ b/apps/conference/etc/conference.conf @@ -50,6 +50,10 @@ playout_type=adaptive_playout # # minimum_timer=90 +# - maximum Timer value we want to accept +# +#maximum_timer=900 + # session refresh (Session Timer, RFC4028) method # # INVITE - use re-INVITE diff --git a/apps/ivr/etc/ivr.conf b/apps/ivr/etc/ivr.conf index 164f5019..689afb4b 100644 --- a/apps/ivr/etc/ivr.conf +++ b/apps/ivr/etc/ivr.conf @@ -18,6 +18,10 @@ script_path=/usr/local/lib/sems/ivr/ # # minimum_timer=90 +# - maximum Timer value we want to accept +# +#maximum_timer=900 + # session refresh (Session Timer, RFC4028) method # # INVITE - use re-INVITE diff --git a/apps/sbc/etc/prepaid.sbcprofile.conf b/apps/sbc/etc/prepaid.sbcprofile.conf index 33b2b153..f41400f5 100644 --- a/apps/sbc/etc/prepaid.sbcprofile.conf +++ b/apps/sbc/etc/prepaid.sbcprofile.conf @@ -58,6 +58,7 @@ prepaid_acc_dest=$H(P-Acc-Dest) # default values #session_expires=120 #minimum_timer=90 +#maximum_timer=900 #session_refresh_method=UPDATE_FALLBACK_INVITE #accept_501_reply=yes diff --git a/apps/sbc/etc/sst_b2b.sbcprofile.conf b/apps/sbc/etc/sst_b2b.sbcprofile.conf index f95c5a98..119997de 100644 --- a/apps/sbc/etc/sst_b2b.sbcprofile.conf +++ b/apps/sbc/etc/sst_b2b.sbcprofile.conf @@ -47,9 +47,11 @@ enable_session_timer=yes # default values #session_expires=120 #minimum_timer=90 +#maximum_timer=900 #session_refresh_method=UPDATE_FALLBACK_INVITE #accept_501_reply=yes + # #This application can be routed through for achieving #two things: diff --git a/apps/webconference/etc/webconference.conf b/apps/webconference/etc/webconference.conf index cc0ec9a8..a7a57cc8 100644 --- a/apps/webconference/etc/webconference.conf +++ b/apps/webconference/etc/webconference.conf @@ -157,6 +157,10 @@ stats_dir=/var/log/sems-webconference/ # # minimum_timer=90 +# - maximum Timer value we want to accept +# +#maximum_timer=900 + # session refresh (Session Timer, RFC4028) method # # INVITE - use re-INVITE diff --git a/core/AmB2BSession.cpp b/core/AmB2BSession.cpp index 024524bd..28d66cad 100644 --- a/core/AmB2BSession.cpp +++ b/core/AmB2BSession.cpp @@ -387,6 +387,17 @@ bool AmB2BSession::replaceConnectionAddress(const string& content_type, return true; } +void AmB2BSession::updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq) { + TransMap::iterator t = relayed_req.find(old_cseq); + if (t != relayed_req.end()) { + AmSipTransaction trans = t->second; + relayed_req.erase(t); + relayed_req[new_cseq] = trans; + DBG("updated relayed_req (UAC trans): CSeq %u -> %u\n", old_cseq, new_cseq); + } +} + + void AmB2BSession::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) @@ -437,6 +448,7 @@ void AmB2BSession::onInvite2xx(const AmSipReply& reply) TransMap::iterator it = relayed_req.find(reply.cseq); bool req_fwded = it != relayed_req.end(); if(!req_fwded) { + DBG("req not fwded\n"); AmSession::onInvite2xx(reply); } else { DBG("no 200 ACK now: waiting for the 200 ACK from the other side...\n"); diff --git a/core/AmB2BSession.h b/core/AmB2BSession.h index 7cf6c142..2492ea03 100644 --- a/core/AmB2BSession.h +++ b/core/AmB2BSession.h @@ -175,7 +175,10 @@ class AmB2BSession: public AmSession /** Terminate the other leg and forget it.*/ virtual void terminateOtherLeg(); + /** @see AmSession */ + virtual void updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq); + void onSipRequest(const AmSipRequest& req); void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method); diff --git a/core/AmSession.h b/core/AmSession.h index 9ab0d209..ca60b2a5 100644 --- a/core/AmSession.h +++ b/core/AmSession.h @@ -376,6 +376,11 @@ public: /** set the session on/off hold */ virtual void setOnHold(bool hold); + /** update UAC trans state reference from old_cseq to new_cseq + e.g. if uac_auth or session_timer have resent a UAC request + */ + virtual void updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq) { } + /** * Destroy the session. * It causes the session to be erased from the active session list diff --git a/core/AmSipDialog.cpp b/core/AmSipDialog.cpp index 772c2719..3a79d3ac 100644 --- a/core/AmSipDialog.cpp +++ b/core/AmSipDialog.cpp @@ -185,6 +185,7 @@ int AmSipDialog::rel100OnRequestIn(const AmSipRequest& req) SIP_HDR_COLSP(SIP_HDR_UNSUPPORTED) SIP_EXT_100REL CRLF, next_hop_for_replies ? next_hop_ip : "", next_hop_for_replies ? next_hop_port : 0); + return 0; break; default: diff --git a/core/plug-in/echo/etc/echo.conf b/core/plug-in/echo/etc/echo.conf index 718aebcc..9485fc50 100644 --- a/core/plug-in/echo/etc/echo.conf +++ b/core/plug-in/echo/etc/echo.conf @@ -16,6 +16,10 @@ # # minimum_timer=90 +# - maximum Timer value we want to accept +# +#maximum_timer=900 + # session refresh (Session Timer, RFC4028) method # # INVITE - use re-INVITE diff --git a/core/plug-in/session_timer/SessionTimer.cpp b/core/plug-in/session_timer/SessionTimer.cpp index ffaef8c0..19168b14 100644 --- a/core/plug-in/session_timer/SessionTimer.cpp +++ b/core/plug-in/session_timer/SessionTimer.cpp @@ -80,6 +80,52 @@ bool SessionTimer::onSipRequest(const AmSipRequest& req) bool SessionTimer::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) { + if (session_timer_conf.getEnableSessionTimer() && + (reply.code == 422) && + ((trans_method == SIP_METH_INVITE) || (trans_method == SIP_METH_UPDATE))) { + std::map::iterator ri = + sent_requests.find(reply.cseq); + if (ri != sent_requests.end()) { + SIPRequestInfo& orig_req = ri->second; + + // get Min-SE + unsigned int i_minse; + string min_se_hdr = getHeader(reply.hdrs, SIP_HDR_MIN_SE, true); + if (!min_se_hdr.empty()) { + if (str2i(strip_header_params(min_se_hdr), i_minse)) { + WARN("error while parsing " SIP_HDR_MIN_SE " header value '%s'\n", + strip_header_params(min_se_hdr).c_str()); + } else { + + if (i_minse <= session_timer_conf.getMaximumTimer()) { + session_interval = i_minse; + unsigned int new_cseq = s->dlg.cseq; + // resend request with interval i_minse + if (s->dlg.sendRequest(orig_req.method,orig_req.content_type, + orig_req.body, orig_req.hdrs) == 0) { + DBG("request with new Session Interval %u successfully sent.\n", i_minse); + // undo SIP dialog status change + if (s->dlg.getStatus() != old_dlg_status) + s->dlg.setStatus(old_dlg_status); + + s->updateUACTransCSeq(reply.cseq, new_cseq); + // processed + return true; + } else { + ERROR("failed to send request with new Session Interval.\n"); + } + } else { + DBG("other side requests too high Min-SE: %u (our limit %u)\n", + i_minse, session_timer_conf.getMaximumTimer()); + } + } + } + } else { + WARN("request CSeq %u not found in sent requests; unable to retry after 422\n", + reply.cseq); + } + } + updateTimer(s,reply); return false; } @@ -96,10 +142,22 @@ bool SessionTimer::onSendRequest(const string& method, return false; } + if (session_timer_conf.getEnableSessionTimer() && + ((method == SIP_METH_INVITE) || (method == SIP_METH_UPDATE))) { + // save INVITE and UPDATE so we can resend on 422 reply + DBG("adding %d to list of sent requests.\n", cseq); + sent_requests[cseq] = SIPRequestInfo(method, + content_type, + body, + hdrs); + } + string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF; if ((method != SIP_METH_INVITE) && (method != SIP_METH_UPDATE)) goto end; + removeHeader(hdrs, SIP_HDR_SESSION_EXPIRES); + removeHeader(hdrs, SIP_HDR_MIN_SE); m_hdrs += SIP_HDR_COLSP(SIP_HDR_SESSION_EXPIRES) + int2str(session_interval) +CRLF + SIP_HDR_COLSP(SIP_HDR_MIN_SE) + int2str(min_se) + CRLF; @@ -372,10 +430,11 @@ void SessionTimer::onTimeoutEvent(AmTimeoutEvent* timeout_ev) AmSessionTimerConfig::AmSessionTimerConfig() : EnableSessionTimer(DEFAULT_ENABLE_SESSION_TIMER), SessionExpires(SESSION_EXPIRES), - MinimumTimer(MINIMUM_TIMER) + MinimumTimer(MINIMUM_TIMER), + MaximumTimer(MAXIMUM_TIMER) { - } + AmSessionTimerConfig::~AmSessionTimerConfig() { } @@ -405,6 +464,18 @@ int AmSessionTimerConfig::readFromConfig(AmConfigReader& cfg) return -1; } } + + if (cfg.hasParameter("maximum_timer")){ + int maximum_timer = 0; + if (!str2int(cfg.getParameter("maximum_timer"), maximum_timer) || + maximum_timer<=0) { + ERROR("invalid value for maximum_timer '%s'\n", + cfg.getParameter("maximum_timer").c_str()); + return -1; + } + MaximumTimer = (unsigned int) maximum_timer; + } + return 0; } diff --git a/core/plug-in/session_timer/SessionTimer.h b/core/plug-in/session_timer/SessionTimer.h index 1f53ff14..9cafbdf7 100644 --- a/core/plug-in/session_timer/SessionTimer.h +++ b/core/plug-in/session_timer/SessionTimer.h @@ -40,11 +40,12 @@ class AmTimeoutEvent; #define ID_SESSION_INTERVAL_TIMER -1 #define ID_SESSION_REFRESH_TIMER -2 -/* Session Timer defaul configuration: */ +/* Session Timer default configuration: */ #define DEFAULT_ENABLE_SESSION_TIMER 1 #define SESSION_EXPIRES 120 // seconds -#define MINIMUM_TIMER 90 //seconds +#define MINIMUM_TIMER 90 // seconds +#define MAXIMUM_TIMER 900 // seconds - 15 min /** \brief Factory of the session timer event handler */ class SessionTimerFactory: public AmSessionEventHandlerFactory @@ -71,7 +72,9 @@ class AmSessionTimerConfig unsigned int SessionExpires; /** Session Timer: Minimum Session-Expires */ unsigned int MinimumTimer; - + + unsigned int MaximumTimer; + public: AmSessionTimerConfig(); ~AmSessionTimerConfig(); @@ -90,16 +93,22 @@ public: bool getEnableSessionTimer() { return EnableSessionTimer; } unsigned int getSessionExpires() { return SessionExpires; } unsigned int getMinimumTimer() { return MinimumTimer; } + unsigned int getMaximumTimer() { return MaximumTimer; } int readFromConfig(AmConfigReader& cfg); }; +struct SIPRequestInfo; + /** \brief SessionEventHandler for implementing session timer logic for a session */ class SessionTimer: public AmSessionEventHandler { AmSessionTimerConfig session_timer_conf; AmSession* s; + // map to save sent requests, so we can resent in case of 422 + std::map sent_requests; + enum SessionRefresher { refresh_local, refresh_remote @@ -161,4 +170,22 @@ class SessionTimer: public AmSessionEventHandler }; +/** \brief contains necessary information for UAC auth of a SIP request */ +struct SIPRequestInfo { + string method; + string content_type; + string body; + string hdrs; + + SIPRequestInfo(const string& method, + const string& content_type, + const string& body, + const string& hdrs) + : method(method), content_type(content_type), + body(body), hdrs(hdrs) { } + + SIPRequestInfo() {} + +}; + #endif