OSDN Git Service

Add MS7619SE
[uclinux-h8/uClinux-dist.git] / user / gnugk / radacct.cxx
1 /*
2  * radacct.cxx
3  *
4  * RADIUS protocol accounting logger module for GNU Gatekeeper. 
5  *
6  * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz
7  *
8  * This work is published under the GNU Public License (GPL)
9  * see file COPYING for details.
10  * We also explicitely grant the right to link this code
11  * with the OpenH323 library.
12  *
13  * $Log: radacct.cxx,v $
14  * Revision 1.20  2006/12/06 16:34:04  zvision
15  * Handle RADIUS Class attribute correctly
16  *
17  * Revision 1.19  2006/07/06 15:25:13  willamowius
18  * set all deleted pointers to NULL (most probably more than needed)
19  *
20  * Revision 1.18  2006/04/14 13:56:19  willamowius
21  * call failover code merged
22  *
23  * Revision 1.1.1.1  2005/11/21 20:19:58  willamowius
24  *
25  *
26  * Revision 1.4  2005/11/15 19:52:56  jan
27  * Michal v1 (works, but on in routed, not proxy mode)
28  *
29  * Revision 1.17  2005/04/24 16:39:44  zvision
30  * MSVC6.0 compatibility fixed
31  *
32  * Revision 1.16  2005/01/05 15:42:41  willamowius
33  * new accounting event 'connect', parameter substitution unified in parent class
34  *
35  * Revision 1.15  2005/01/04 16:47:12  willamowius
36  * space in trace msg
37  *
38  * Revision 1.14  2004/11/15 23:57:42  zvision
39  * Ability to choose between the original and the rewritten dialed number
40  *
41  * Revision 1.13  2004/11/10 18:30:41  zvision
42  * Ability to customize timestamp strings
43  *
44  * Revision 1.12  2004/07/26 12:19:41  zvision
45  * New faster Radius implementation, thanks to Pavel Pavlov for ideas!
46  *
47  * Revision 1.11.2.2  2004/07/07 23:11:07  zvision
48  * Faster and more elegant handling of Cisco VSA
49  *
50  * Revision 1.11.2.1  2004/07/07 20:50:14  zvision
51  * New, faster, Radius client implementation. Thanks to Pavel Pavlov for ideas!
52  *
53  * Revision 1.11  2004/06/25 13:33:18  zvision
54  * Better Username, Calling-Station-Id and Called-Station-Id handling.
55  * New SetupUnreg option in Gatekeeper::Auth section.
56  *
57  * Revision 1.10  2004/06/17 10:47:13  zvision
58  * New h323-ivr-out=h323-call-id accounting attribute
59  *
60  * Revision 1.9  2004/04/17 11:43:43  zvision
61  * Auth/acct API changes.
62  * Header file usage more consistent.
63  *
64  * Revision 1.8  2004/03/17 00:00:38  zvision
65  * Conditional compilation to allow to control RADIUS on Windows just by setting HA_RADIUS macro
66  *
67  * Revision 1.7  2003/10/31 00:01:24  zvision
68  * Improved accounting modules stacking control, optimized radacct/radauth a bit
69  *
70  * Revision 1.6  2003/10/15 10:16:57  zvision
71  * Fixed VC6 compiler warnings. Thanks to Hu Yuxin.
72  *
73  * Revision 1.5  2003/10/08 12:40:48  zvision
74  * Realtime accounting updates added
75  *
76  * Revision 1.4  2003/09/17 19:23:01  zvision
77  * Removed unnecessary setup-time double check.
78  * Added h323-connect-time to AcctUpdate packets.
79  *
80  * Revision 1.3  2003/09/14 21:10:34  zvision
81  * Changes due to accounting API redesign.
82  *
83  * Revision 1.2  2003/09/12 16:31:16  zvision
84  * Accounting initially added to the 2.2 branch
85  *
86  * Revision 1.1.2.5  2003/08/21 15:28:58  zvision
87  * Fixed double h323-setup-time sent in Acct-Stop
88  *
89  * Revision 1.1.2.4  2003/08/17 20:05:39  zvision
90  * Added h323-setup-time attribute to Acct-Start packets (Cisco compatibility).
91  *
92  * Revision 1.1.2.3  2003/07/31 22:58:48  zvision
93  * Added Framed-IP-Address attribute and improved h323-disconnect-cause handling
94  *
95  * Revision 1.1.2.2  2003/07/03 15:30:39  zvision
96  * Added cvs Log keyword
97  *
98  */
99 #if HAS_RADIUS
100
101 #if defined(_WIN32) && (_MSC_VER <= 1200)
102 #pragma warning(disable:4786) // warning about too long debug symbol off
103 #pragma warning(disable:4284)
104 #endif
105
106 #include <ptlib.h>
107 #include <h323pdu.h>
108 #include "gk_const.h"
109 #include "h323util.h"
110 #include "Toolkit.h"
111 #include "RasTbl.h"
112 #include "gkacct.h"
113 #include "radproto.h"
114 #include "radacct.h"
115
116 using std::vector;
117
118
119 RadAcct::RadAcct( 
120         const char* moduleName,
121         const char* cfgSecName
122         )
123         :
124         GkAcctLogger(moduleName, cfgSecName),
125         m_nasIdentifier(Toolkit::Instance()->GKName()),
126         m_radiusClient(NULL),
127         m_attrH323CallOrigin(RadiusAttr::CiscoVSA_h323_call_origin, false,
128                 PString("proxy")),
129         m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false, 
130                 PString("VoIP"))
131 {
132         // it is very important to set what type of accounting events
133         // are supported for each accounting module, otherwise Log method
134         // will no get called
135         SetSupportedEvents(RadAcctEvents);
136         
137         PConfig* cfg = GetConfig();
138         const PString& cfgSec = GetConfigSectionName();
139
140         m_radiusClient = new RadiusClient(*cfg, cfgSec);
141
142         m_nasIpAddress = m_radiusClient->GetLocalAddress();
143         if (m_nasIpAddress == INADDR_ANY) {
144                 vector<PIPSocket::Address> interfaces;
145                 Toolkit::Instance()->GetGKHome(interfaces);
146                 if (!interfaces.empty())
147                         m_nasIpAddress = interfaces.front();
148                 else
149                         PTRACE(1, "RADACCT\t" << GetName() << " cannot determine "
150                                 " NAS IP address"
151                                 );
152         }
153
154         m_appendCiscoAttributes = Toolkit::AsBool(cfg->GetString(
155                 cfgSec, "AppendCiscoAttributes", "1"
156                 ));
157         m_fixedUsername = cfg->GetString(cfgSec, "FixedUsername", "");
158
159         m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", "");
160
161         m_useDialedNumber = Toolkit::AsBool(cfg->GetString(
162                 cfgSec, "UseDialedNumber", "0"
163                 ));
164         
165         m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier);
166         m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier);
167 }
168
169 RadAcct::~RadAcct()
170 {
171         delete m_radiusClient;
172 }
173
174 GkAcctLogger::Status RadAcct::Log(
175         GkAcctLogger::AcctEvent evt, 
176         const callptr& call
177         )
178 {
179         // a workaround to prevent processing end on "sufficient" module
180         // if it is not interested in this event type
181         if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0)
182                 return Next;
183                 
184         if (m_radiusClient == NULL) {
185                 PTRACE(1,"RADACCT\t"<<GetName()<<" - null RADIUS client instance");
186                 return Fail;
187         }
188
189         if ((evt & (AcctStart | AcctStop | AcctUpdate)) && (!call)) {
190                 PTRACE(1,"RADACCT\t"<<GetName()<<" - missing call info for event "<<evt);
191                 return Fail;
192         }
193         
194         // build RADIUS Accounting-Request
195         RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccountingRequest);
196
197         pdu->AppendAttr(RadiusAttr::AcctStatusType, 
198                 (evt & AcctStart) ? RadiusAttr::AcctStatus_Start
199                 : ((evt & AcctStop) ? RadiusAttr::AcctStatus_Stop
200                 : ((evt & AcctUpdate) ? RadiusAttr::AcctStatus_InterimUpdate
201                 : ((evt & AcctOn) ? RadiusAttr::AcctStatus_AccountingOn
202                 : ((evt & AcctOff) ? RadiusAttr::AcctStatus_AccountingOff : 0)
203                 ))));
204
205         PIPSocket::Address addr;
206         WORD port;
207                                         
208         // Gk works as NAS point, so append NAS IP
209         pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
210         pdu->AppendAttr(m_attrNasIdentifier);
211         pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
212                 
213         if (evt & (AcctStart | AcctStop | AcctUpdate)) {
214                 pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
215                 pdu->AppendAttr(RadiusAttr::AcctSessionId, call->GetAcctSessionId());
216                 
217                 PBYTEArray classAttr(call->GetRADIUSClass());
218                 if (classAttr.GetSize() > 0)
219                         pdu->AppendAttr(RadiusAttr::AttrTypeClass, (const BYTE*)classAttr, classAttr.GetSize());
220
221                 endptr callingEP = call->GetCallingParty();
222                 PIPSocket::Address callerIP(0);
223                 WORD callerPort = 0;            
224                 
225                 call->GetSrcSignalAddr(callerIP, callerPort);
226
227                 const PString username = GetUsername(call);
228                 if (username.IsEmpty() && m_fixedUsername.IsEmpty())
229                         PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine User-Name"
230                                 <<" for the call no. "<<call->GetCallNumber()
231                                 );
232                 else
233                         pdu->AppendAttr(RadiusAttr::UserName, 
234                                 m_fixedUsername.IsEmpty() ? username : m_fixedUsername
235                                 );
236                 
237                 if (callerIP.IsValid())
238                         pdu->AppendAttr(RadiusAttr::FramedIpAddress, callerIP);
239                 
240                 if ((evt & AcctStart) == 0)
241                         pdu->AppendAttr(RadiusAttr::AcctSessionTime, call->GetDuration());
242         
243                 PString stationId = GetCallingStationId(call);
244                 if (!stationId)
245                         pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
246                 else
247                         PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine"
248                                 <<" Calling-Station-Id for the call "<<call->GetCallNumber()
249                                 );
250
251                 stationId = m_useDialedNumber ? GetDialedNumber(call) : GetCalledStationId(call);
252                 if (!stationId)
253                         pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
254                 else
255                         PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine"
256                                 <<" Called-Station-Id for the call no. "<<call->GetCallNumber()
257                                 );
258                 
259                 if (m_appendCiscoAttributes) {
260                         pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
261                                 GetGUIDString(call->GetConferenceIdentifier())
262                                 );
263                                                 
264                         pdu->AppendAttr(m_attrH323GwId);
265                         pdu->AppendAttr(m_attrH323CallOrigin);
266                         pdu->AppendAttr(m_attrH323CallType);
267
268                         Toolkit* const toolkit = Toolkit::Instance();
269                                 
270                         time_t tm = call->GetSetupTime();
271                         if (tm != 0)
272                                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_setup_time,
273                                         toolkit->AsString(PTime(tm), m_timestampFormat)
274                                         );
275                         
276                         if (evt & (AcctStop | AcctUpdate)) {
277                                 tm = call->GetConnectTime();
278                                 if (tm != 0)
279                                         pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_connect_time,
280                                                 toolkit->AsString(PTime(tm), m_timestampFormat)
281                                                 );
282                         }
283                         
284                         if (evt & AcctStop) {
285                                 tm = call->GetDisconnectTime();
286                                 if (tm != 0)
287                                         pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_time,
288                                                 toolkit->AsString(PTime(tm), m_timestampFormat)
289                                                 );
290                                 
291                                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_cause,
292                                         PString(PString::Unsigned, (long)(call->GetDisconnectCause()), 16)
293                                         );
294                         }                                       
295                         
296                         if (call->GetDestSignalAddr(addr,port))
297                                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_remote_address,
298                                         addr.AsString()
299                                         );
300
301                         pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair,
302                                 PString("h323-ivr-out=h323-call-id:") 
303                                         + GetGUIDString(call->GetCallIdentifier().m_guid),
304                                 true
305                                 );
306                 }
307         
308                 pdu->AppendAttr(RadiusAttr::AcctDelayTime, 0);
309         }
310                 
311         // send request and wait for response
312         RadiusPDU* response = NULL;
313         bool result = OnSendPDU(*pdu, evt, call);
314         
315         // accounting updates must be fast, so we are just sending
316         // the request to the server and are not waiting for a response
317         if (result)
318                 if (evt & AcctUpdate)
319                         result = m_radiusClient->SendRequest(*pdu);
320                 else
321                         result = m_radiusClient->MakeRequest(*pdu, response) && (response != NULL);
322                         
323         delete pdu;
324
325         if (!result) {
326                 delete response;
327                 return Fail;
328         }
329                                 
330         if (response) {
331                 // check if Access-Request has been accepted
332                 result = (response->GetCode() == RadiusPDU::AccountingResponse);
333                 if (result)
334                         result = OnReceivedPDU(*response, evt, call);
335                 else
336                         PTRACE(4, "RADACCT\t" << GetName() << " - received response is not "
337                                 " an AccountingResponse, event " << evt << ", call no. "
338                                 << (call ? call->GetCallNumber() : 0)
339                                 );
340                 delete response;
341         }
342         return result ? Ok : Fail;
343 }
344
345 bool RadAcct::OnSendPDU(
346         RadiusPDU& /*pdu*/,
347         GkAcctLogger::AcctEvent /*evt*/,
348         const callptr& /*call*/
349         )
350 {
351         return true;
352 }
353
354 bool RadAcct::OnReceivedPDU(
355         RadiusPDU& /*pdu*/,
356         GkAcctLogger::AcctEvent /*evt*/,
357         const callptr& /*call*/
358         )
359 {
360         return true;
361 }
362
363 namespace {
364         // append RADIUS based accounting logger to the global list of loggers
365         GkAcctLoggerCreator<RadAcct> RadAcctCreator("RadAcct");
366 }
367
368 #endif /* HAS_RADIUS */