Added more workarounds for RADIUS accounting.

git-svn-id: http://yate.null.ro/svn/yate/trunk@719 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-03-23 03:23:23 +00:00
parent 04563ce2b1
commit be257bb756
4 changed files with 113 additions and 6 deletions

View File

@ -10,9 +10,15 @@
; port: integer: UDP port to bind to, must be non-zero
;port=1810
; single_socket: bool: Share the socket for requests (not recommended)
;single_socket=false
; local_time: bool: Use local timestamps instead of GMT
;local_time=false
; short_number: bool: Prefer to use parameter "called" over "calledfull"
;short_number=false
; auth_priority: integer: Priority of the user.auth handler
;auth_priority=50
@ -33,6 +39,32 @@
;simplify=no
;[nas]
;There can be several sections named [nas] or [nas SOMETHING]
; rad_auth: bool: Enable authentication on this virtual NAS
;rad_auth=yes
; rad_acct: bool: Enable accounting on this virtual NAS
;rad_acct=yes
; rad_server: string: Name of the server to use, default [radius SOMETHING]
;rad_server=
; add: will add an attribute to the RADIUS request
;add:attribute-name=EXPRESSION
;add:subtype:attribute-name=EXPRESSION
; set: will set message parameters
;set:paramname=EXPRESSION
; ret: will set authentication returned attributes back into message
;ret:attribute-name=paramname
; Any other parameter will attempt to match the right hand Regexp
;paramname=REGEXP
[nas register]
; Sample SIP REGISTER authentication NAS
rad_acct=false
@ -78,7 +110,7 @@ add:Acct-Delay-Time=0
[radius common]
; Settings specific to this RADIUS server
; server: ipaddress: IP address of the server
; server: ipaddress: IP address of the server - must be set
;server=
; secret: string: Secret token (password) used to authenticate to the server
@ -88,7 +120,7 @@ add:Acct-Delay-Time=0
;auth_port=1812
; acct_port: integer: UDP port used by the server for accounting
acct_port=1813
;acct_port=1813
; timeout: integer: How much to wait for an answer from the server (milliseconds)
;timeout=2000

View File

@ -80,6 +80,7 @@ private:
String m_caller;
String m_called;
String m_username;
String m_calledfull;
String m_status;
String m_reason;
bool m_first;
@ -155,6 +156,7 @@ void CdrBuilder::emit(const char *operation)
m->addParam("caller",m_caller);
m->addParam("called",m_called);
m->addParam("username",m_username);
m->addParam("calledfull",m_calledfull);
m->addParam("duration",printTime(buf,t_hangup - t_start));
m->addParam("billtime",printTime(buf,t_hangup - t_answer));
m->addParam("ringtime",printTime(buf,t_answer - t_ringing));
@ -223,6 +225,11 @@ void CdrBuilder::update(const Message& msg, int type, u_int64_t val)
if (p)
m_username = p;
}
if (m_calledfull.null()) {
p = msg.getValue("calledfull");
if (p)
m_calledfull = p;
}
p = msg.getValue("status");
if (p) {
m_status = p;

View File

@ -65,6 +65,8 @@ static ObjList acctBuilders;
static SocketAddr s_localAddr(AF_INET);
static Socket s_localSock;
static bool s_localTime = false;
static bool s_shortnum = false;
static bool s_unisocket = false;
static bool s_pb_enabled = false;
static bool s_pb_parallel = false;
static bool s_pb_simplify = false;
@ -310,6 +312,7 @@ public:
{ return m_server; }
bool setRadServer(const char* host, int authport, int acctport, const char* secret, int timeoutms = 4000, int retries = 2);
bool setRadServer(const NamedList& sect);
bool addSocket();
int doAuthenticate(ObjList* result = 0);
int doAccounting(ObjList* result = 0);
bool addAttribute(const char* attrib, const char* val, bool emptyOk = false);
@ -756,12 +759,45 @@ Socket* RadiusClient::socket() const
return m_socket ? m_socket : &s_localSock;
}
// Create and add a local UDP socket to the client request
bool RadiusClient::addSocket()
{
if (m_socket)
return true;
SocketAddr localAddr(AF_INET);
localAddr.host(s_localAddr.host());
if (!(localAddr.valid() && localAddr.host())) {
Debug(&__plugin,DebugInfo,"Invalid address '%s' - falling back to global socket",
localAddr.host().c_str());
return false;
}
// we only have UDP support
Socket* s = new Socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
if (!(s && s->valid())) {
Debug(&__plugin,DebugWarn,"Error creating UDP socket - falling back to global socket");
delete s;
return false;
}
if (!s->bind(localAddr)) {
Debug(&__plugin,DebugWarn,"Error %d binding to %s - falling back to global socket",
s->error(),localAddr.host().c_str());
delete s;
return false;
}
DDebug(&__plugin,DebugInfo,"Created new socket for request");
m_socket = s;
return true;
}
// Build incremental session ID byte
unsigned char RadiusClient::newSessionId()
{
Lock lock(s_cfgMutex);
return s_sessionId++;
}
// Set the server parameters
bool RadiusClient::setRadServer(const char* host, int authport, int acctport, const char* secret, int timeoutms, int retries)
{
// adjust an absolute minimum of 1 try with a 500ms timeout
@ -778,6 +814,7 @@ bool RadiusClient::setRadServer(const char* host, int authport, int acctport, co
return (m_server && (m_authPort || m_acctPort));
}
// Set the server parameters from a config file section
bool RadiusClient::setRadServer(const NamedList& sect)
{
return setRadServer(sect.getValue("server"),
@ -788,6 +825,7 @@ bool RadiusClient::setRadServer(const NamedList& sect)
sect.getIntValue("retries",2));
}
// Fill a data block with (pseudo) random data
bool RadiusClient::fillRandom(DataBlock& data, int len)
{
data.assign(0,len);
@ -804,6 +842,7 @@ bool RadiusClient::fillRandom(DataBlock& data, int len)
return true;
}
// Cryptographically check if the response is properly authenticated
bool RadiusClient::checkAuthenticator(const unsigned char* buffer, int length)
{
if (!buffer)
@ -819,13 +858,14 @@ bool RadiusClient::checkAuthenticator(const unsigned char* buffer, int length)
md5.update(recattr,attrlen);
md5.update(m_secret);
if (memcmp(md5.rawDigest(),recauth,16)) {
Debug(&__plugin,DebugWarn,"Authenticators do not match");
Debug(&__plugin,DebugMild,"Authenticators do not match");
return false;
}
Debug(&__plugin,DebugAll,"Authenticator matched for response");
return true;
}
// Make one request, wait for answer and optionally decode it
int RadiusClient::makeRequest(int port, unsigned char request, unsigned char* response, ObjList* result)
{
if (!(port && socket() && socket()->valid()))
@ -883,6 +923,9 @@ int RadiusClient::makeRequest(int port, unsigned char request, unsigned char* re
radpckt.append(m_authdata);
radpckt.append(attrdata);
if (!s_unisocket)
addSocket();
// we have the data ready, send it and wait for an answer
for (int r = m_retries; r > 0; r--) {
if (socket()->sendTo(radpckt.data(),radpckt.length(),sockAddr) == Socket::socketError()) {
@ -954,7 +997,7 @@ int RadiusClient::makeRequest(int port, unsigned char request, unsigned char* re
return ServerErr;
}
// Make an authentication request, wait for answer
int RadiusClient::doAuthenticate(ObjList* result)
{
unsigned char response = 0;
@ -975,6 +1018,7 @@ int RadiusClient::doAuthenticate(ObjList* result)
return AuthSuccess;
}
// Make an accounting request, wait for answer
int RadiusClient::doAccounting(ObjList* result)
{
unsigned char response = 0;
@ -995,6 +1039,7 @@ int RadiusClient::doAccounting(ObjList* result)
return AcctSuccess;
}
// Add one text attribute
bool RadiusClient::addAttribute(const char* attrib, const char* val, bool emptyOk)
{
if (null(attrib))
@ -1010,6 +1055,7 @@ bool RadiusClient::addAttribute(const char* attrib, const char* val, bool emptyO
return false;
}
// Add one numeric attribute
bool RadiusClient::addAttribute(const char* attrib, int val)
{
if (null(attrib))
@ -1023,6 +1069,7 @@ bool RadiusClient::addAttribute(const char* attrib, int val)
return false;
}
// Add one text attribute with subtype
bool RadiusClient::addAttribute(const char* attrib, unsigned char subType, const char* val, bool emptyOk)
{
if (null(attrib))
@ -1038,6 +1085,7 @@ bool RadiusClient::addAttribute(const char* attrib, unsigned char subType, const
return false;
}
// Copy from parameter list (usually message) to RADIUS attributes
void RadiusClient::addAttributes(NamedList& params, NamedList* list)
{
if (!list)
@ -1089,10 +1137,23 @@ void RadiusClient::addAttributes(NamedList& params, NamedList* list)
}
}
// Find matching NAS section and populate attributes accordingly
bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
{
const char* caller = params.getValue("caller");
const char* called = params.getValue("called");
const char* called = 0;
if (s_shortnum) {
// prefer short called over calledfull
called = params.getValue("called");
if (!called)
called = params.getValue("calledfull");
}
else {
// prefer long calledfull over called
called = params.getValue("calledfull");
if (!called)
called = params.getValue("called");
}
const char* username = params.getValue("username",caller);
Lock lock(s_cfgMutex);
NamedList* nasSect = 0;
@ -1171,6 +1232,7 @@ bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct)
return true;
}
// Copy some attributes back from RADIUS answer to parameter list (message)
bool RadiusClient::returnAttributes(NamedList& params, const ObjList* attributes)
{
Lock lock(s_cfgMutex);
@ -1195,6 +1257,7 @@ bool RadiusClient::returnAttributes(NamedList& params, const ObjList* attributes
return true;
}
bool AuthHandler::received(Message& msg)
{
String proto = msg.getValue("protocol",msg.getValue("module"));
@ -1235,6 +1298,7 @@ bool AuthHandler::received(Message& msg)
}
// Build a Cisco style (like NTP) date/time string
static bool ciscoTime(double t, String& ret)
{
time_t sec = (time_t)floor(t);
@ -1287,6 +1351,7 @@ bool AcctHandler::received(Message& msg)
RadiusClient radclient;
if (!radclient.prepareAttributes(msg,true))
return false;
// cryptographically generate an unique call leg ID
MD5 sid(billid);
sid << msg.getValue("chan");
@ -1351,7 +1416,9 @@ void RadiusModule::initialize()
s_cfg = Engine::configFile("yradius");
s_cfg.load();
s_cfgMutex.unlock();
s_localTime = s_cfg.getBoolValue("general","local_time");
s_localTime = s_cfg.getBoolValue("general","local_time",false);
s_shortnum = s_cfg.getBoolValue("general","short_number",false);
s_unisocket = s_cfg.getBoolValue("general","single_socket",false);
s_pb_enabled = s_cfg.getBoolValue("portabill","enabled",false);
s_pb_parallel = s_cfg.getBoolValue("portabill","parallel",false);
s_pb_simplify = s_cfg.getBoolValue("portabill","simplify",false);

View File

@ -1540,6 +1540,7 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char
s->setParam("called",msg.getValue("called"));
s->setParam("billid",msg.getValue("billid"));
s->setParam("username",msg.getValue("username"));
s->setParam("calledfull",m_uri.getUser());
Engine::enqueue(s);
}