Refactor CEL to avoid using the event system core
[asterisk/asterisk.git] / cel / cel_radius.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief RADIUS CEL Support
22  * \author Philippe Sultan
23  * The Radius Client Library - http://developer.berlios.de/projects/radiusclient-ng/
24  *
25  * \arg See also \ref AstCEL
26  * \ingroup cel_drivers
27  */
28
29 /*** MODULEINFO
30         <depend>radius</depend>
31         <support_level>extended</support_level>
32  ***/
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Rev$")
37
38 #include <radiusclient-ng.h>
39
40 #include "asterisk/channel.h"
41 #include "asterisk/cel.h"
42 #include "asterisk/module.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/options.h"
46
47 /*! ISO 8601 standard format */
48 #define DATE_FORMAT "%Y-%m-%d %T %z"
49
50 #define VENDOR_CODE           22736
51
52 enum {
53         PW_AST_ACCT_CODE =    101,
54         PW_AST_CIDNUM =       102,
55         PW_AST_CIDNAME =      103,
56         PW_AST_CIDANI =       104,
57         PW_AST_CIDRDNIS =     105,
58         PW_AST_CIDDNID =      106,
59         PW_AST_EXTEN =        107,
60         PW_AST_CONTEXT =      108,
61         PW_AST_CHANNAME =     109,
62         PW_AST_APPNAME =      110,
63         PW_AST_APPDATA =      111,
64         PW_AST_EVENT_TIME =   112,
65         PW_AST_AMA_FLAGS =    113,
66         PW_AST_UNIQUE_ID =    114,
67         PW_AST_USER_NAME =    115,
68         PW_AST_LINKED_ID =    116,
69 };
70
71 enum {
72         /*! Log dates and times in UTC */
73         RADIUS_FLAG_USEGMTIME = (1 << 0),
74         /*! Log Unique ID */
75         RADIUS_FLAG_LOGUNIQUEID = (1 << 1),
76         /*! Log User Field */
77         RADIUS_FLAG_LOGUSERFIELD = (1 << 2)
78 };
79
80 static char *cel_config = "cel.conf";
81
82 static char radiuscfg[PATH_MAX] = "/etc/radiusclient-ng/radiusclient.conf";
83
84 static struct ast_flags global_flags = { RADIUS_FLAG_USEGMTIME | RADIUS_FLAG_LOGUNIQUEID | RADIUS_FLAG_LOGUSERFIELD };
85
86 static rc_handle *rh = NULL;
87
88 #define RADIUS_BACKEND_NAME "CEL Radius Logging"
89
90 #define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, &y, strlen(y), VENDOR_CODE))
91
92 static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record)
93 {
94         int recordtype = PW_STATUS_STOP;
95         struct ast_tm tm;
96         char timestr[128];
97         char *amaflags;
98
99         if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) {
100                 return -1;
101         }
102         /* Account code */
103         if (!ADD_VENDOR_CODE(PW_AST_ACCT_CODE, record->account_code)) {
104                 return -1;
105         }
106         /* Source */
107         if (!ADD_VENDOR_CODE(PW_AST_CIDNUM, record->caller_id_num)) {
108                 return -1;
109         }
110         /* Destination */
111         if (!ADD_VENDOR_CODE(PW_AST_EXTEN, record->extension)) {
112                 return -1;
113         }
114         /* Destination context */
115         if (!ADD_VENDOR_CODE(PW_AST_CONTEXT, record->context)) {
116                 return -1;
117         }
118         /* Caller ID */
119         if (!ADD_VENDOR_CODE(PW_AST_CIDNAME, record->caller_id_name)) {
120                 return -1;
121         }
122         /* Caller ID ani */
123         if (!ADD_VENDOR_CODE(PW_AST_CIDANI, record->caller_id_ani)) {
124                 return -1;
125         }
126         /* Caller ID rdnis */
127         if (!ADD_VENDOR_CODE(PW_AST_CIDRDNIS, record->caller_id_rdnis)) {
128                 return -1;
129         }
130         /* Caller ID dnid */
131         if (!ADD_VENDOR_CODE(PW_AST_CIDDNID, record->caller_id_dnid)) {
132                 return -1;
133         }
134         /* Channel */
135         if (!ADD_VENDOR_CODE(PW_AST_CHANNAME, record->channel_name)) {
136                 return -1;
137         }
138         /* Last Application */
139         if (!ADD_VENDOR_CODE(PW_AST_APPNAME, record->application_name)) {
140                 return -1;
141         }
142         /* Last Data */
143         if (!ADD_VENDOR_CODE(PW_AST_APPDATA, record->application_data)) {
144                 return -1;
145         }
146         /* Event Time */
147         ast_localtime(&record->event_time, &tm,
148                 ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL);
149         ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
150         if (!rc_avpair_add(rh, send, PW_AST_EVENT_TIME, timestr, strlen(timestr), VENDOR_CODE)) {
151                 return -1;
152         }
153         /* AMA Flags */
154         amaflags = ast_strdupa(ast_channel_amaflags2string(record->amaflag));
155         if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) {
156                 return -1;
157         }
158         if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) {
159                 /* Unique ID */
160                 if (!ADD_VENDOR_CODE(PW_AST_UNIQUE_ID, record->unique_id)) {
161                         return -1;
162                 }
163         }
164         /* LinkedID */
165         if (!ADD_VENDOR_CODE(PW_AST_LINKED_ID, record->linked_id)) {
166                 return -1;
167         }
168         /* Setting Acct-Session-Id & User-Name attributes for proper generation
169            of Acct-Unique-Session-Id on server side */
170         /* Channel */
171         if (!rc_avpair_add(rh, send, PW_USER_NAME, &record->channel_name,
172                         strlen(record->channel_name), 0)) {
173                 return -1;
174         }
175         return 0;
176 }
177
178 static void radius_log(struct ast_event *event)
179 {
180         int result = ERROR_RC;
181         VALUE_PAIR *send = NULL;
182         struct ast_cel_event_record record = {
183                 .version = AST_CEL_EVENT_RECORD_VERSION,
184         };
185
186         if (ast_cel_fill_record(event, &record)) {
187                 return;
188         }
189
190         if (build_radius_record(&send, &record)) {
191                 ast_debug(1, "Unable to create RADIUS record. CEL not recorded!\n");
192                 goto return_cleanup;
193         }
194
195         result = rc_acct(rh, 0, send);
196         if (result != OK_RC) {
197                 ast_log(LOG_ERROR, "Failed to record Radius CEL record!\n");
198         }
199
200 return_cleanup:
201         if (send) {
202                 rc_avpair_free(send);
203         }
204 }
205
206 static int unload_module(void)
207 {
208         ast_cel_backend_unregister(RADIUS_BACKEND_NAME);
209         if (rh) {
210                 rc_destroy(rh);
211                 rh = NULL;
212         }
213         return AST_MODULE_LOAD_SUCCESS;
214 }
215
216 static int load_module(void)
217 {
218         struct ast_config *cfg;
219         struct ast_flags config_flags = { 0 };
220         const char *tmp;
221
222         if ((cfg = ast_config_load(cel_config, config_flags))) {
223                 ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "usegmtime")), RADIUS_FLAG_USEGMTIME);
224                 if ((tmp = ast_variable_retrieve(cfg, "radius", "radiuscfg"))) {
225                         ast_copy_string(radiuscfg, tmp, sizeof(radiuscfg));
226                 }
227                 ast_config_destroy(cfg);
228         } else {
229                 return AST_MODULE_LOAD_DECLINE;
230         }
231
232         /*
233          * start logging
234          *
235          * NOTE: Yes this causes a slight memory leak if the module is
236          * unloaded.  However, it is better than a crash if cdr_radius
237          * and cel_radius are both loaded.
238          */
239         tmp = ast_strdup("asterisk");
240         if (tmp) {
241                 rc_openlog((char *) tmp);
242         }
243
244         /* read radiusclient-ng config file */
245         if (!(rh = rc_read_config(radiuscfg))) {
246                 ast_log(LOG_NOTICE, "Cannot load radiusclient-ng configuration file %s.\n", radiuscfg);
247                 return AST_MODULE_LOAD_DECLINE;
248         }
249
250         /* read radiusclient-ng dictionaries */
251         if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) {
252                 ast_log(LOG_NOTICE, "Cannot load radiusclient-ng dictionary file.\n");
253                 rc_destroy(rh);
254                 rh = NULL;
255                 return AST_MODULE_LOAD_DECLINE;
256         }
257
258         if (ast_cel_backend_register(RADIUS_BACKEND_NAME, radius_log)) {
259                 rc_destroy(rh);
260                 rh = NULL;
261                 return AST_MODULE_LOAD_DECLINE;
262         } else {
263                 return AST_MODULE_LOAD_SUCCESS;
264         }
265 }
266
267 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RADIUS CEL Backend",
268         .load = load_module,
269         .unload = unload_module,
270         .load_pri = AST_MODPRI_CDR_DRIVER,
271 );