added support for forked INVITEs

If an INVITE has been forked and more than one branch is aimed at SEMS,
then the branch parameter of the top-via is necessary to distinguish the
different sessions.

By default, SEMS will now accept forked INVITEs.
This behavior can be switched off by setting "accept_forked_dialogs" to "no" (default=yes).

Conflicts:

	core/AmSipDispatcher.cpp
sayer/1.4-spce2.6
Stefan Sayer 15 years ago
parent df480e790a
commit d79f9edf16

@ -97,6 +97,8 @@ unsigned int AmConfig::OptionsSessionLimit = 0;
unsigned int AmConfig::OptionsSessionLimitErrCode = 503;
string AmConfig::OptionsSessionLimitErrReason = "Server overload";
bool AmConfig::AcceptForkedDialogs = true;
bool AmConfig::ShutdownMode = false;
unsigned int AmConfig::ShutdownModeErrCode = 503;
string AmConfig::ShutdownModeErrReason = "Server shutting down";
@ -512,6 +514,9 @@ int AmConfig::readConfiguration()
}
}
if(cfg.hasParameter("accept_forked_dialogs"))
AcceptForkedDialogs = !(cfg.getParameter("accept_forked_dialogs") == "no");
if(cfg.hasParameter("shutdown_mode_reply")){
string c_reply = cfg.getParameter("shutdown_mode_reply");
size_t spos = c_reply.find(" ");

@ -170,6 +170,8 @@ struct AmConfig
static unsigned int OptionsSessionLimitErrCode;
static string OptionsSessionLimitErrReason;
static bool AcceptForkedDialogs;
static bool ShutdownMode;
static unsigned int ShutdownModeErrCode;
static string ShutdownModeErrReason;

@ -27,6 +27,7 @@
#include "AmEventDispatcher.h"
#include "AmSipEvent.h"
#include "AmConfig.h"
#include "sip/hash.h"
unsigned int AmEventDispatcher::hash(const string& s1)
@ -52,12 +53,38 @@ AmEventDispatcher* AmEventDispatcher::instance()
return _instance ? _instance : ((_instance = new AmEventDispatcher()));
}
bool AmEventDispatcher::addEventQueue(const string& local_tag,
AmEventQueueInterface* q)
{
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
if (queues[queue_bucket].find(local_tag) != queues[queue_bucket].end()) {
queues_mut[queue_bucket].unlock();
return false;
}
queues[queue_bucket][local_tag] = q;
queues_mut[queue_bucket].unlock();
return true;
}
/** @return false on error */
bool AmEventDispatcher::addEventQueue(const string& local_tag,
AmEventQueueInterface* q,
const string& callid,
const string& remote_tag)
const string& remote_tag,
const string& via_branch)
{
if(local_tag.empty () ||callid.empty() || remote_tag.empty() | via_branch.empty()) {
ERROR("local_tag, callid, remote_tag or via_branch is empty");
return false;
}
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
@ -67,36 +94,54 @@ bool AmEventDispatcher::addEventQueue(const string& local_tag,
return false;
}
unsigned int id_bucket = 0;
if(!callid.empty() && !remote_tag.empty()) {
// try to find via id_lookup
id_bucket = hash(callid, remote_tag);
id_lookup_mut[id_bucket].lock();
if (id_lookup[id_bucket].find(callid+remote_tag) !=
id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return false;
}
// try to find via id_lookup
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
queues[queue_bucket][local_tag] = q;
id_lookup_mut[id_bucket].lock();
if(!callid.empty() && !remote_tag.empty()) {
id_lookup[id_bucket][callid+remote_tag] = local_tag;
if (id_lookup[id_bucket].find(id) !=
id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return false;
}
queues[queue_bucket][local_tag] = q;
id_lookup[id_bucket][id] = local_tag;
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return true;
}
AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag)
{
AmEventQueueInterface* q = NULL;
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
EvQueueMapIter qi = queues[queue_bucket].find(local_tag);
if(qi != queues[queue_bucket].end()) {
q = qi->second;
queues[queue_bucket].erase(qi);
}
queues_mut[queue_bucket].unlock();
return q;
}
AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag,
const string& callid,
const string& remote_tag)
const string& remote_tag,
const string& via_branch)
{
AmEventQueueInterface* q = NULL;
@ -110,11 +155,16 @@ AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag,
q = qi->second;
queues[queue_bucket].erase(qi);
if(!callid.empty() && !remote_tag.empty()) {
if(!callid.empty() && !remote_tag.empty() && !via_branch.empty()) {
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
DictIter di = id_lookup[id_bucket].find(id);
if(di != id_lookup[id_bucket].end()) {
id_lookup[id_bucket].erase(di);
}
@ -147,11 +197,20 @@ bool AmEventDispatcher::post(const string& local_tag, AmEvent* ev)
}
bool AmEventDispatcher::post(const string& callid, const string& remote_tag, AmEvent* ev)
bool AmEventDispatcher::post(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* ev)
{
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
DictIter di = id_lookup[id_bucket].find(id);
if (di == id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
return false;
@ -208,16 +267,24 @@ void AmEventDispatcher::dispose()
_instance = NULL;
}
}
/** this function optimizes posting of SIP Requests
- if the session does not exist, no event need to be created (req copied) */
bool AmEventDispatcher::postSipRequest(const string& callid, const string& remote_tag,
const AmSipRequest& req)
bool AmEventDispatcher::postSipRequest(const AmSipRequest& req)
{
// get local tag
bool posted = false;
string callid = req.callid;
string remote_tag = req.from_tag;
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += req.via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
DictIter di = id_lookup[id_bucket].find(id);
if (di == id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
return false;

@ -59,8 +59,8 @@ private:
AmMutex queues_mut[EVENT_DISPATCHER_BUCKETS];
/**
* Call ID + remote tag -> local tag
* (needed for CANCELs and some provisionnal answers)
* Call ID + remote tag + via_branch -> local tag
* (needed for CANCELs)
* (UAS sessions only)
*/
Dictionnary id_lookup[EVENT_DISPATCHER_BUCKETS];
@ -74,23 +74,32 @@ public:
static AmEventDispatcher* instance();
static void dispose();
bool postSipRequest(const string& callid, const string& remote_tag,
const AmSipRequest& req);
bool postSipRequest(const AmSipRequest& req);
bool post(const string& local_tag, AmEvent* ev);
bool post(const string& callid, const string& remote_tag, AmEvent* ev);
bool post(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* ev);
/* send event to all event queues. Note: event instances will be cloned */
bool broadcast(AmEvent* ev);
bool addEventQueue(const string& local_tag,
AmEventQueueInterface* q);
bool addEventQueue(const string& local_tag,
AmEventQueueInterface* q,
const string& callid="",
const string& remote_tag="");
const string& callid,
const string& remote_tag,
const string& via_branch);
AmEventQueueInterface* delEventQueue(const string& local_tag);
AmEventQueueInterface* delEventQueue(const string& local_tag,
const string& callid="",
const string& remote_tag="");
const string& callid,
const string& remote_tag,
const string& via_branch);
bool empty();
};

@ -210,6 +210,11 @@ const string& AmSession::getLocalTag() const
return dlg.local_tag;
}
const string& AmSession::getFirstBranch() const
{
return dlg.first_branch;
}
void AmSession::setUri(const string& uri)
{
DBG("AmSession::setUri(%s)\n",uri.c_str());

@ -334,6 +334,9 @@ public:
/** Gets the Session's local tag */
const string& getLocalTag() const;
/** Gets the branch param of the first via in the original INVITE*/
const string& getFirstBranch() const;
/** Sets the Session's local tag if not set already */
void setLocalTag();

@ -195,9 +195,10 @@ void AmSessionContainer::destroySession(AmSession* s)
AmEventQueueInterface* q = AmEventDispatcher::instance()->
delEventQueue(s->getLocalTag(),
s->getCallID(),
s->getRemoteTag());
s->getRemoteTag(),
s->getFirstBranch());
if(q) {
if(q) {
stopAndQueue(s);
}
else {
@ -300,7 +301,7 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
}
switch(addSession(req.callid,req.from_tag,local_tag,
session.get())) {
req.via_branch,session.get())) {
case AmSessionContainer::Inserted:
// successful case
@ -330,7 +331,8 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
session->start();
} catch (...) {
AmEventDispatcher::instance()->
delEventQueue(req.callid,req.from_tag,local_tag);
delEventQueue(req.callid,req.from_tag,local_tag,
req.via_branch);
throw;
}
@ -355,11 +357,12 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
bool AmSessionContainer::postEvent(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* event)
{
bool posted =
AmEventDispatcher::instance()->
post(callid,remote_tag,event);
AmEventDispatcher::instance()->
post(callid,remote_tag,via_branch,event);
if(!posted)
delete event;
@ -446,6 +449,7 @@ AmSessionContainer::AddSessionStatus
AmSessionContainer::addSession(const string& callid,
const string& remote_tag,
const string& local_tag,
const string& via_branch,
AmSession* session)
{
if(_container_closed.get())
@ -453,7 +457,7 @@ AmSessionContainer::addSession(const string& callid,
if(AmEventDispatcher::instance()->
addEventQueue(local_tag,(AmEventQueue*)session,
callid,remote_tag)) {
callid,remote_tag,via_branch)) {
return Inserted;
}

@ -108,6 +108,7 @@ class AmSessionContainer : public AmThread
AddSessionStatus addSession(const string& callid,
const string& remote_tag,
const string& local_tag,
const string& via_branch,
AmSession* session);
/**
@ -139,7 +140,9 @@ class AmSessionContainer : public AmThread
* post an event into the event queue of the identified dialog.
* @return false if session doesn't exist
*/
bool postEvent(const string& callid, const string& remote_tag,
bool postEvent(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* event);
/**

@ -155,6 +155,7 @@ void AmSipDialog::updateStatus(const AmSipRequest& req)
remote_party = req.from;
local_party = req.to;
route = req.route;
first_branch = req.via_branch;
}
int cont = rel100OnRequestIn(req);

@ -173,6 +173,8 @@ class AmSipDialog
string remote_tag;
string local_tag;
string first_branch;
string remote_party; // To/From
string local_party; // To/From

@ -92,7 +92,7 @@ void AmSipDispatcher::handleSipMsg(AmSipRequest &req)
}
else if(req.method == "CANCEL"){
if(ev_disp->postSipRequest(callid, remote_tag, req)){
if(ev_disp->postSipRequest(req)){
return;
}

@ -73,6 +73,7 @@ class AmSipRequest : public _AmSipMsgInDlg
string rack_method;
unsigned int rack_cseq;
string via_branch;
AmSipRequest() : _AmSipMsgInDlg() { }
~AmSipRequest() { }

@ -471,6 +471,7 @@ inline void SipCtrlInterface::sip_msg2am_request(const sip_msg *msg,
req.from_tag = c2stlstr(((sip_from_to*)msg->from->p)->tag);
req.to_tag = c2stlstr(((sip_from_to*)msg->to->p)->tag);
req.cseq = get_cseq(msg)->num;
req.via_branch = c2stlstr(msg->via_p1->branch);
req.body = c2stlstr(msg->body);
if (msg->rack) {

@ -476,6 +476,17 @@ use_default_signature=yes
#
#100rel=require
#
# accept forked dialogs on UAS side? [yes|no]
#
# no - INVITE with existing callid+remote_tag is replied with 482.
# yes - INVITE with existing callid+remote_tag+via_branch is replied with 482.
# Forked INVITEs (!= via-branch) are accepted.
#
# Default: yes
#
#accept_forked_dialogs=no
# Make SIP authenticated requests sticky to the proxy? [yes | no]
#
# If enabled, host of request-URI of out-of-dialog requests that are

Loading…
Cancel
Save