You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/core/plug-in/session_timer/SessionTimer.cpp

336 lines
9.7 KiB

/*
* $Id$
*
* Copyright (C) 2002-2003 Fhg Fokus
*
* This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* sems is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "SessionTimer.h"
#include "AmUtils.h"
#include "UserTimer.h"
#include "AmSipHeaders.h"
EXPORT_SESSION_EVENT_HANDLER_FACTORY(SessionTimerFactory, MOD_NAME);
int SessionTimerFactory::onLoad()
{
return 0;
}
bool SessionTimerFactory::onInvite(const AmSipRequest& req)
{
if(!checkSessionExpires(req))
return true;
return false;
}
AmSessionEventHandler* SessionTimerFactory::getHandler(AmSession* s)
{
return new SessionTimer(s);
}
SessionTimer::SessionTimer(AmSession* s)
:AmSessionEventHandler(),
s(s),
session_interval(0),
session_refresher(refresh_remote)
{}
bool SessionTimer::process(AmEvent* ev)
{
AmTimeoutEvent* timeout_ev = dynamic_cast<AmTimeoutEvent*>(ev);
if (timeout_ev) {
DBG("received timeout Event with ID %d\n", timeout_ev->data.get(0).asInt());
onTimeoutEvent(timeout_ev);
return true;
}
return false;
}
bool SessionTimer::onSipEvent(AmSipEvent* ev)
{
return false;
}
bool SessionTimer::onSipRequest(const AmSipRequest& req)
{
updateTimer(s,req);
return false;
}
bool SessionTimer::onSipReply(const AmSipReply& reply)
{
updateTimer(s,reply);
return false;
}
bool SessionTimer::onSendRequest(const string& method,
const string& content_type,
const string& body,
string& hdrs,
int flags,
unsigned int cseq)
{
string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF;
if ((method != "INVITE") && (method != "UPDATE"))
goto end;
m_hdrs += "Session-Expires: "+ int2str(session_timer_conf.getSessionExpires()) +CRLF
+ "Min-SE: " + int2str(session_timer_conf.getMinimumTimer()) + CRLF;
end:
hdrs += m_hdrs;
return false;
}
bool SessionTimer::onSendReply(const AmSipRequest& req,
unsigned int code,const string& reason,
const string& content_type,const string& body,
string& hdrs,
int flags)
{
//if (!session_timer_conf.getEnableSessionTimer())
// return "";
string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF;
if ((req.method != "INVITE") && (req.method != "UPDATE"))
return false;
// only in 2xx responses to INV/UPD
m_hdrs += "Session-Expires: " + int2str(session_interval) + ";refresher="+
(session_refresher_role==UAC ? "uac":"uas")+CRLF;
if (((session_refresher_role==UAC) && (session_refresher==refresh_remote))
|| ((session_refresher_role==UAS) && remote_timer_aware))
m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED) "timer" CRLF;
hdrs += m_hdrs;
return false;
}
/* Session Timer: -ssa */
void SessionTimer::configureSessionTimer(const AmSessionTimerConfig& conf)
{
session_timer_conf = conf;
DBG("Configured session with EnableSessionTimer = %s, SessionExpires = %u, MinimumTimer = %u\n",
session_timer_conf.getEnableSessionTimer() ? "yes":"no",
session_timer_conf.getSessionExpires(),
session_timer_conf.getMinimumTimer()
);
}
/**
* check if UAC requests too low Session-Expires
* (<locally configured Min-SE)
* Throws SessionIntervalTooSmallException if too low
*/
bool SessionTimerFactory::checkSessionExpires(const AmSipRequest& req)
{
//if (session_timer_conf.getEnableSessionTimer()) {
string session_expires = getHeader(req.hdrs, "Session-Expires", "x");
if (session_expires.length()) {
unsigned int i_se;
if (!str2i(strip_header_params(session_expires), i_se)) {
//if (i_se < session_timer_conf.getMinimumTimer()) {
//TODO: reply_error...
//throw SessionTimerException(session_timer_conf.getMinimumTimer());
//}
} else
throw AmSession::Exception(500,"internal error"); // malformed request?
}
//}
return true;
}
void SessionTimer::updateTimer(AmSession* s, const AmSipRequest& req) {
if((req.method == "INVITE")||(req.method == "UPDATE")){
remote_timer_aware =
key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED),"timer");
// determine session interval
string sess_expires_hdr = getHeader(req.hdrs, "Session-Expires", "x");
//session_interval = get_session_interval_from(req);
if (!sess_expires_hdr.empty()) {
if (str2i(strip_header_params(sess_expires_hdr),
session_interval)) {
WARN("error while parsing Session-Expires header value '%s'\n",
strip_header_params(sess_expires_hdr).c_str()); // exception?
session_interval = session_timer_conf.getSessionExpires();
}
} else {
session_interval = session_timer_conf.getSessionExpires();
}
// get Min-SE
unsigned int i_minse = session_timer_conf.getMinimumTimer();
string min_se_hdr = getHeader(req.hdrs, "Min-SE");
if (!min_se_hdr.empty()) {
if (str2i(strip_header_params(min_se_hdr),
i_minse)) {
WARN("error while parsing Min-SE header value '%s'\n",
strip_header_params(min_se_hdr).c_str()); // exception?
}
}
// calculate actual se
unsigned int min = session_timer_conf.getMinimumTimer();
if (i_minse > min)
min = i_minse;
if ((session_timer_conf.getSessionExpires() < min)||
(session_interval<min)) {
session_interval = min;
} else {
if (session_timer_conf.getSessionExpires() < session_interval)
session_interval = session_timer_conf.getSessionExpires();
}
// determine session refresher -- cf rfc4028 Table 2
// only if the remote party supports timer and asks
// to be refresher we will let the remote party do it.
// if remote supports timer and does not specify,
// could also be refresher=uac
if ((remote_timer_aware) && (!sess_expires_hdr.empty()) &&
(get_header_param(sess_expires_hdr, "refresher") == "uac")) {
DBG("session refresher will be remote UAC.\n");
session_refresher = refresh_remote;
session_refresher_role = UAC;
} else {
DBG("session refresher will be local UAS.\n");
session_refresher = refresh_local;
session_refresher_role = UAS;
}
removeTimers(s);
setTimers(s);
} else if (req.method == "BYE") { // remove all timers?
removeTimers(s);
}
}
void SessionTimer::updateTimer(AmSession* s, const AmSipReply& reply)
{
if (!session_timer_conf.getEnableSessionTimer())
return;
// only update timer on positive reply
if ((reply.code < 200) || (reply.code >= 300))
return;
// determine session interval
string sess_expires_hdr = getHeader(reply.hdrs, "Session-Expires");
if (sess_expires_hdr.empty())
sess_expires_hdr = getHeader(reply.hdrs, "x"); // compact form
session_interval = session_timer_conf.getSessionExpires();
session_refresher = refresh_local;
session_refresher_role = UAC;
if (!sess_expires_hdr.empty()) {
//session_interval = get_session_interval_from(req);
unsigned int sess_i_tmp = 0;
if (str2i(strip_header_params(sess_expires_hdr),
sess_i_tmp)) {
WARN("error while parsing Session-Expires header value '%s'\n",
strip_header_params(sess_expires_hdr).c_str()); // exception?
} else {
// this is forbidden by rfc, but to be sure against 'rogue' proxy/uas
if (sess_i_tmp < session_timer_conf.getMinimumTimer()) {
session_interval = session_timer_conf.getMinimumTimer();
} else {
session_interval = sess_i_tmp;
}
}
if (get_header_param(sess_expires_hdr, "refresher") == "uas") {
session_refresher = refresh_remote;
session_refresher_role = UAS;
}
}
removeTimers(s);
setTimers(s);
}
void SessionTimer::setTimers(AmSession* s)
{
// set session timer
DBG("Setting session interval timer: %ds, tag '%s'\n", session_interval,
s->getLocalTag().c_str());
UserTimer::instance()->
setTimer(ID_SESSION_INTERVAL_TIMER, session_interval, s->getLocalTag());
// set session refresh action timer, after half the expiration
if (session_refresher == refresh_local) {
DBG("Setting session refresh timer: %ds, tag '%s'\n", session_interval/2,
s->getLocalTag().c_str());
UserTimer::instance()->
setTimer(ID_SESSION_REFRESH_TIMER, session_interval/2, s->getLocalTag());
}
}
void SessionTimer::removeTimers(AmSession* s)
{
UserTimer::instance()->
removeTimer(ID_SESSION_REFRESH_TIMER, s->getLocalTag());
UserTimer::instance()->
removeTimer(ID_SESSION_INTERVAL_TIMER, s->getLocalTag());
}
void SessionTimer::onTimeoutEvent(AmTimeoutEvent* timeout_ev)
{
int timer_id = timeout_ev->data.get(0).asInt();
if (timer_id == ID_SESSION_REFRESH_TIMER) {
if (session_refresher == refresh_local)
// send reinvite with SDP
s->sendReinvite(true);
else
WARN("need session refresh but remote session is refresher\n");
} else if (timer_id == ID_SESSION_INTERVAL_TIMER) {
// // let the session know it got timeout
// onTimeout();
s->dlg.bye();
s->setStopped();
} else {
DBG("unknown timeout event received.\n");
}
return;
}