Added module to perform CNAM and LNP queries via SIP INVITE / 3xx.
git-svn-id: http://voip.null.ro/svn/yate@4152 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
115541f8bc
commit
4c06a75e34
|
@ -0,0 +1,64 @@
|
|||
[priorities]
|
||||
; These settings that are read only at first initialization!
|
||||
|
||||
; Sets the priorities for installing relevant message handlers
|
||||
|
||||
; call.preroute: int: Priority of the call.preroute handler
|
||||
; Should be set between 10 and 90
|
||||
;call.preroute=50
|
||||
|
||||
; call.route: int: Priority of the call.route handler
|
||||
; Should be set between 10 and 90
|
||||
;call.route=50
|
||||
|
||||
|
||||
[cnam]
|
||||
; Controls how CNAM queries are performed, a SIP 3xx answer is expected
|
||||
; CNAM is taken from the description of P-Asserted-Identity URI
|
||||
; Query is performed on call.preroute when callername is missing and caller is E.164
|
||||
; Parameters modified: callername, querycnam
|
||||
|
||||
; Actual call parameters ${...} are substituted in the strings
|
||||
|
||||
; callto: string: Template for the resource of the CNAM request
|
||||
; This parameter is required for CNAM functionality
|
||||
; Example: callto=sip/sip:${caller}@10.0.0.1
|
||||
;callto=
|
||||
|
||||
; caller: string: Template for the caller party number of the request
|
||||
; It should not be modified
|
||||
;caller=${caller}
|
||||
|
||||
; called: string: Template for the called party number of the request
|
||||
; It can be used to force anonymous requests
|
||||
;called=${called}
|
||||
|
||||
; timeout: int: overall timeout of the SIP request in milliseconds
|
||||
; It is limited to range 1000-30000 (1s to 30s)
|
||||
;timeout=5000
|
||||
|
||||
|
||||
[lnp]
|
||||
; Controls how LNP queries are performed, a SIP 3xx answer is expected
|
||||
; Routing Number and NPDI are taken from the Contact URI
|
||||
; Query is performed on call.route when npdi is not true
|
||||
; Parameters modified: routing, npdi, querylnp
|
||||
|
||||
; Actual call parameters ${...} are substituted in the strings
|
||||
|
||||
; callto: string: Template for the resource of the LNP request
|
||||
; This parameter is required for LNP functionality
|
||||
; Example: callto=sip/sip:${called}@10.0.0.2
|
||||
;callto=
|
||||
|
||||
; caller: string: Template for the caller party number of the request
|
||||
; It can be used to force anonymous requests
|
||||
;caller=${caller}
|
||||
|
||||
; called: string: Template for the called party number of the request
|
||||
; It should not be modified
|
||||
;called=${called}
|
||||
|
||||
; timeout: int: overall timeout of the SIP request in milliseconds
|
||||
; It is limited to range 1000-30000 (1s to 30s)
|
||||
;timeout=5000
|
|
@ -66,6 +66,7 @@ PROGS := cdrbuild.yate cdrfile.yate regexroute.yate \
|
|||
server/ysnmpagent.yate \
|
||||
server/monitoring.yate \
|
||||
server/sipfeatures.yate \
|
||||
sip/sip_cnam_lnp.yate \
|
||||
server/heartbeat.yate server/clustering.yate \
|
||||
server/mgcpgw.yate server/mgcpca.yate \
|
||||
server/mrcpspeech.yate \
|
||||
|
@ -77,7 +78,7 @@ PROGS := cdrbuild.yate cdrfile.yate regexroute.yate \
|
|||
callgen.yate analyzer.yate rmanager.yate msgsniff.yate
|
||||
|
||||
LIBS :=
|
||||
DIRS := client server jabber qt4
|
||||
DIRS := client server jabber qt4 sip
|
||||
|
||||
ifneq ($(HAVE_PGSQL),no)
|
||||
PROGS := $(PROGS) server/pgsqldb.yate
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
/**
|
||||
* sip_cnam_lnp.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Query CNAM and LNP databases using SIP INVITE
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2011 Null Team
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatephone.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
namespace { // anonymous
|
||||
|
||||
class QuerySipDriver : public Driver
|
||||
{
|
||||
public:
|
||||
QuerySipDriver();
|
||||
~QuerySipDriver();
|
||||
virtual void initialize();
|
||||
virtual bool msgExecute(Message& msg, String& dest)
|
||||
{ return false; }
|
||||
virtual bool msgPreroute(Message& msg);
|
||||
virtual bool msgRoute(Message& msg);
|
||||
protected:
|
||||
bool received(Message& msg, int id);
|
||||
int waitFor(const Channel* c);
|
||||
};
|
||||
|
||||
INIT_PLUGIN(QuerySipDriver);
|
||||
|
||||
static Configuration s_cfg;
|
||||
static ObjList s_waiting;
|
||||
static int s_notify = 0;
|
||||
|
||||
|
||||
class QuerySipChannel : public Channel
|
||||
{
|
||||
public:
|
||||
enum Operation {
|
||||
CNAM,
|
||||
LNP,
|
||||
};
|
||||
QuerySipChannel(const char* addr, Operation type, Message* msg);
|
||||
~QuerySipChannel();
|
||||
virtual void disconnected(bool final, const char *reason);
|
||||
private:
|
||||
void endCnam(const NamedList& params);
|
||||
void endLnp(const NamedList& params);
|
||||
Operation m_type;
|
||||
Message* m_msg;
|
||||
};
|
||||
|
||||
QuerySipChannel::QuerySipChannel(const char* num, Operation type, Message* msg)
|
||||
: Channel(__plugin, 0, false),
|
||||
m_type(type), m_msg(msg)
|
||||
{
|
||||
switch (m_type) {
|
||||
case CNAM:
|
||||
(m_address = "cnam/") << num;
|
||||
break;
|
||||
case LNP:
|
||||
(m_address = "lnp/") << num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QuerySipChannel::~QuerySipChannel()
|
||||
{
|
||||
s_notify++;
|
||||
}
|
||||
|
||||
void QuerySipChannel::disconnected(bool final, const char *reason)
|
||||
{
|
||||
Debug(DebugAll,"QuerySipChannel::disconnected() '%s'",reason);
|
||||
switch (m_type) {
|
||||
case CNAM:
|
||||
endCnam(parameters());
|
||||
break;
|
||||
case LNP:
|
||||
endLnp(parameters());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QuerySipChannel::endCnam(const NamedList& params)
|
||||
{
|
||||
if (!params.getBoolValue("redirect"))
|
||||
return;
|
||||
// Caller Name is in the description of the P-Asserted-Identity URI
|
||||
URI ident(params.getValue("sip_p-asserted-identity"));
|
||||
if (ident.null())
|
||||
return;
|
||||
m_msg->setParam("querycnam",String::boolText(false));
|
||||
String cnam = ident.getDescription();
|
||||
if (cnam.null())
|
||||
return;
|
||||
Debug(this,DebugInfo,"CNAM '%s' for '%s'",cnam.c_str(),ident.getUser().c_str());
|
||||
m_msg->setParam("callername",cnam);
|
||||
}
|
||||
|
||||
void QuerySipChannel::endLnp(const NamedList& params)
|
||||
{
|
||||
if (!params.getBoolValue("redirect"))
|
||||
return;
|
||||
// Routing Number and NPDI are in the Contact header - already parsed
|
||||
String called = params.getValue("called");
|
||||
called.toLower();
|
||||
if (called.null())
|
||||
return;
|
||||
ObjList* list = called.split(';',false);
|
||||
if (!list)
|
||||
return;
|
||||
m_msg->setParam("querylnp",String::boolText(false));
|
||||
bool npdi = false;
|
||||
String rn;
|
||||
for (ObjList* item = list->skipNull(); item; item = item->skipNext()) {
|
||||
String* s = static_cast<String*>(item->get());
|
||||
s->trimSpaces();
|
||||
if (s->null())
|
||||
continue;
|
||||
int pos = s->find('=');
|
||||
if (pos < 0) {
|
||||
npdi = npdi || (*s == "npdi");
|
||||
}
|
||||
else if (pos > 0) {
|
||||
String key = s->substr(0,pos);
|
||||
key.trimSpaces();
|
||||
if (key == "rn")
|
||||
rn = s->substr(pos+1).trimSpaces();
|
||||
else if (key == "npdi")
|
||||
npdi = s->substr(pos+1).toBoolean(true);
|
||||
}
|
||||
}
|
||||
TelEngine::destruct(list);
|
||||
Debug(this,DebugInfo,"LNP rn='%s' npdi=%s",rn.c_str(),String::boolText(npdi));
|
||||
if (rn)
|
||||
m_msg->setParam("routing",rn);
|
||||
m_msg->setParam("npdi",String::boolText(npdi));
|
||||
}
|
||||
|
||||
|
||||
bool QuerySipDriver::msgPreroute(Message& msg)
|
||||
{
|
||||
DDebug(DebugAll,"QuerySipChannel::msgPreroute()");
|
||||
if (!msg.getBoolValue("querycnam",(0 == msg.getParam("callername"))))
|
||||
return false;
|
||||
if (!TelEngine::isE164(msg.getValue("caller")))
|
||||
return false;
|
||||
Lock mylock(this);
|
||||
String callto = s_cfg.getValue("cnam","callto");
|
||||
if (callto.null())
|
||||
return false;
|
||||
String caller = s_cfg.getValue("cnam","caller","${caller}");
|
||||
String called = s_cfg.getValue("cnam","called","${called}");
|
||||
int timeout = s_cfg.getIntValue("cnam","timeout",5000);
|
||||
mylock.drop();
|
||||
msg.replaceParams(callto);
|
||||
msg.replaceParams(caller);
|
||||
msg.replaceParams(called);
|
||||
if (timeout < 1000)
|
||||
timeout = 1000;
|
||||
else if (timeout > 30000)
|
||||
timeout = 30000;
|
||||
QuerySipChannel* c = new QuerySipChannel(caller,QuerySipChannel::CNAM,&msg);
|
||||
c->initChan();
|
||||
Message* m = c->message("call.execute",false,true);
|
||||
m->addParam("callto",callto);
|
||||
m->addParam("caller",caller);
|
||||
m->addParam("called",called);
|
||||
m->addParam("timeout",String(timeout));
|
||||
m->addParam("media",String::boolText(false));
|
||||
m->addParam("pbxassist",String::boolText(false));
|
||||
m->addParam("cdrtrack",String::boolText(false));
|
||||
m->addParam("cdrwrite",String::boolText(false));
|
||||
m->addParam("copyparams","pbxassist,cdrwrite");
|
||||
m->addParam("querycnam",String::boolText(false));
|
||||
c->deref();
|
||||
if (!Engine::enqueue(m))
|
||||
delete m;
|
||||
int t = waitFor(c);
|
||||
Debug(this,(t > 500) ? DebugNote : DebugAll,"CNAM lookup took %d msec",t);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QuerySipDriver::msgRoute(Message& msg)
|
||||
{
|
||||
DDebug(DebugAll,"QuerySipChannel::msgRoute()");
|
||||
if (!msg.getBoolValue("querylnp",!msg.getBoolValue("npdi")))
|
||||
return false;
|
||||
if (!TelEngine::isE164(msg.getValue("called")))
|
||||
return false;
|
||||
Lock mylock(this);
|
||||
String callto = s_cfg.getValue("lnp","callto");
|
||||
if (callto.null())
|
||||
return false;
|
||||
String caller = s_cfg.getValue("lnp","caller","${caller}");
|
||||
String called = s_cfg.getValue("lnp","called","${called}");
|
||||
int timeout = s_cfg.getIntValue("lnp","timeout",5000);
|
||||
mylock.drop();
|
||||
msg.replaceParams(callto);
|
||||
msg.replaceParams(caller);
|
||||
msg.replaceParams(called);
|
||||
if (timeout < 1000)
|
||||
timeout = 1000;
|
||||
else if (timeout > 30000)
|
||||
timeout = 30000;
|
||||
QuerySipChannel* c = new QuerySipChannel(caller,QuerySipChannel::LNP,&msg);
|
||||
c->initChan();
|
||||
Message* m = c->message("call.execute",false,true);
|
||||
m->addParam("callto",callto);
|
||||
m->addParam("caller",caller);
|
||||
m->addParam("called",called);
|
||||
m->addParam("timeout",String(timeout));
|
||||
m->addParam("media",String::boolText(false));
|
||||
m->addParam("pbxassist",String::boolText(false));
|
||||
m->addParam("cdrtrack",String::boolText(false));
|
||||
m->addParam("cdrwrite",String::boolText(false));
|
||||
m->addParam("copyparams","pbxassist,cdrwrite");
|
||||
m->addParam("querylnp",String::boolText(false));
|
||||
c->deref();
|
||||
if (!Engine::enqueue(m))
|
||||
delete m;
|
||||
int t = waitFor(c);
|
||||
Debug(this,(t > 500) ? DebugNote : DebugAll,"LNP lookup took %d msec",t);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QuerySipDriver::received(Message& msg, int id)
|
||||
{
|
||||
return (Private == id) ? msgPreroute(msg) : Driver::received(msg,id);
|
||||
}
|
||||
|
||||
int QuerySipDriver::waitFor(const Channel* c)
|
||||
{
|
||||
u_int64_t t = Time::msecNow();
|
||||
for (;;) {
|
||||
Lock mylock(this);
|
||||
if (!channels().find(c))
|
||||
return (int)(Time::msecNow() - t);
|
||||
int n = s_notify;
|
||||
mylock.drop();
|
||||
while (n == s_notify)
|
||||
Thread::idle();
|
||||
}
|
||||
}
|
||||
|
||||
QuerySipDriver::QuerySipDriver()
|
||||
: Driver("sip_cnam_lnp", "misc")
|
||||
{
|
||||
Output("Loaded module SipCnamLnp");
|
||||
}
|
||||
|
||||
QuerySipDriver::~QuerySipDriver()
|
||||
{
|
||||
Output("Unloading module SipCnamLnp");
|
||||
}
|
||||
|
||||
void QuerySipDriver::initialize()
|
||||
{
|
||||
Output("Initializing module SipCnamLnp");
|
||||
setup("qsip/",true);
|
||||
lock();
|
||||
s_cfg = Engine::configFile(name());
|
||||
s_cfg.load();
|
||||
unlock();
|
||||
installRelay(Private,"call.preroute",s_cfg.getIntValue("priorities","call.preroute",50));
|
||||
installRelay(Route,s_cfg.getIntValue("priorities","call.route",50));
|
||||
}
|
||||
|
||||
}; // anonymous namespace
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -153,6 +153,7 @@ for small to large scale projects.
|
|||
%{_libdir}/yate/client/jabberclient.yate
|
||||
%{_libdir}/yate/jabber/jabberserver.yate
|
||||
%{_libdir}/yate/jabber/jbfeatures.yate
|
||||
%{_libdir}/yate/sip/sip_cnam_lnp.yate
|
||||
%dir %{_sysconfdir}/yate
|
||||
%config(noreplace) %{_sysconfdir}/yate/accfile.conf
|
||||
%config(noreplace) %{_sysconfdir}/yate/cdrbuild.conf
|
||||
|
@ -202,6 +203,7 @@ for small to large scale projects.
|
|||
%config(noreplace) %{_sysconfdir}/yate/jabberclient.conf
|
||||
%config(noreplace) %{_sysconfdir}/yate/jabberserver.conf
|
||||
%config(noreplace) %{_sysconfdir}/yate/jbfeatures.conf
|
||||
%config(noreplace) %{_sysconfdir}/yate/sip_cnam_lnp.conf
|
||||
|
||||
%config %{_sysconfdir}/logrotate.d/yate
|
||||
|
||||
|
|
Loading…
Reference in New Issue