Doxygen Updates Janitor Work
[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 static struct ast_event_sub *event_sub = NULL;
88
89 #define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, &y, strlen(y), VENDOR_CODE))
90
91 static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record)
92 {
93         int recordtype = PW_STATUS_STOP;
94         struct ast_tm tm;
95         char timestr[128];
96         char *amaflags;
97
98         if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) {
99                 return -1;
100         }
101         /* Account code */
102         if (!ADD_VENDOR_CODE(PW_AST_ACCT_CODE, record->account_code)) {
103                 return -1;
104         }
105         /* Source */
106         if (!ADD_VENDOR_CODE(PW_AST_CIDNUM, record->caller_id_num)) {
107                 return -1;
108         }
109         /* Destination */
110         if (!ADD_VENDOR_CODE(PW_AST_EXTEN, record->extension)) {
111                 return -1;
112         }
113         /* Destination context */
114         if (!ADD_VENDOR_CODE(PW_AST_CONTEXT, record->context)) {
115                 return -1;
116         }
117         /* Caller ID */
118         if (!ADD_VENDOR_CODE(PW_AST_CIDNAME, record->caller_id_name)) {
119                 return -1;
120         }
121         /* Caller ID ani */
122         if (!ADD_VENDOR_CODE(PW_AST_CIDANI, record->caller_id_ani)) {
123                 return -1;
124         }
125         /* Caller ID rdnis */
126         if (!ADD_VENDOR_CODE(PW_AST_CIDRDNIS, record->caller_id_rdnis)) {
127                 return -1;
128         }
129         /* Caller ID dnid */
130         if (!ADD_VENDOR_CODE(PW_AST_CIDDNID, record->caller_id_dnid)) {
131                 return -1;
132         }
133         /* Channel */
134         if (!ADD_VENDOR_CODE(PW_AST_CHANNAME, record->channel_name)) {
135                 return -1;
136         }
137         /* Last Application */
138         if (!ADD_VENDOR_CODE(PW_AST_APPNAME, record->application_name)) {
139                 return -1;
140         }
141         /* Last Data */
142         if (!ADD_VENDOR_CODE(PW_AST_APPDATA, record->application_data)) {
143                 return -1;
144         }
145         /* Event Time */
146         ast_localtime(&record->event_time, &tm,
147                 ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL);
148         ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
149         if (!rc_avpair_add(rh, send, PW_AST_EVENT_TIME, timestr, strlen(timestr), VENDOR_CODE)) {
150                 return -1;
151         }
152         /* AMA Flags */
153         amaflags = ast_strdupa(ast_cel_get_ama_flag_name(record->amaflag));
154         if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) {
155                 return -1;
156         }
157         if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) {
158                 /* Unique ID */
159                 if (!ADD_VENDOR_CODE(PW_AST_UNIQUE_ID, record->unique_id)) {
160                         return -1;
161                 }
162         }
163         /* LinkedID */
164         if (!ADD_VENDOR_CODE(PW_AST_LINKED_ID, record->linked_id)) {
165                 return -1;
166         }
167         /* Setting Acct-Session-Id & User-Name attributes for proper generation
168            of Acct-Unique-Session-Id on server side */
169         /* Channel */
170         if (!rc_avpair_add(rh, send, PW_USER_NAME, &record->channel_name,
171                         strlen(record->channel_name), 0)) {
172                 return -1;
173         }
174         return 0;
175 }
176
177 static void radius_log(const struct ast_event *event, void *userdata)
178 {
179         int result = ERROR_RC;
180         VALUE_PAIR *send = NULL;
181         struct ast_cel_event_record record = {
182                 .version = AST_CEL_EVENT_RECORD_VERSION,
183         };
184
185         if (ast_cel_fill_record(event, &record)) {
186                 return;
187         }
188
189         if (build_radius_record(&send, &record)) {
190                 ast_debug(1, "Unable to create RADIUS record. CEL not recorded!\n");
191                 goto return_cleanup;
192         }
193
194         result = rc_acct(rh, 0, send);
195         if (result != OK_RC) {
196                 ast_log(LOG_ERROR, "Failed to record Radius CEL record!\n");
197         }
198
199 return_cleanup:
200         if (send) {
201                 rc_avpair_free(send);
202         }
203 }
204
205 static int unload_module(void)
206 {
207         if (event_sub) {
208                 event_sub = ast_event_unsubscribe(event_sub);
209         }
210         if (rh) {
211                 rc_destroy(rh);
212                 rh = NULL;
213         }
214         return AST_MODULE_LOAD_SUCCESS;
215 }
216
217 static int load_module(void)
218 {
219         struct ast_config *cfg;
220         struct ast_flags config_flags = { 0 };
221         const char *tmp;
222
223         if ((cfg = ast_config_load(cel_config, config_flags))) {
224                 ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "usegmtime")), RADIUS_FLAG_USEGMTIME);
225                 if ((tmp = ast_variable_retrieve(cfg, "radius", "radiuscfg"))) {
226                         ast_copy_string(radiuscfg, tmp, sizeof(radiuscfg));
227                 }
228                 ast_config_destroy(cfg);
229         } else {
230                 return AST_MODULE_LOAD_DECLINE;
231         }
232
233         /*
234          * start logging
235          *
236          * NOTE: Yes this causes a slight memory leak if the module is
237          * unloaded.  However, it is better than a crash if cdr_radius
238          * and cel_radius are both loaded.
239          */
240         tmp = ast_strdup("asterisk");
241         if (tmp) {
242                 rc_openlog((char *) tmp);
243         }
244
245         /* read radiusclient-ng config file */
246         if (!(rh = rc_read_config(radiuscfg))) {
247                 ast_log(LOG_NOTICE, "Cannot load radiusclient-ng configuration file %s.\n", radiuscfg);
248                 return AST_MODULE_LOAD_DECLINE;
249         }
250
251         /* read radiusclient-ng dictionaries */
252         if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) {
253                 ast_log(LOG_NOTICE, "Cannot load radiusclient-ng dictionary file.\n");
254                 rc_destroy(rh);
255                 rh = NULL;
256                 return AST_MODULE_LOAD_DECLINE;
257         }
258
259         event_sub = ast_event_subscribe(AST_EVENT_CEL, radius_log, "CEL Radius Logging", NULL, AST_EVENT_IE_END);
260         if (!event_sub) {
261                 rc_destroy(rh);
262                 rh = NULL;
263                 return AST_MODULE_LOAD_DECLINE;
264         } else {
265                 return AST_MODULE_LOAD_SUCCESS;
266         }
267 }
268
269 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RADIUS CEL Backend",
270         .load = load_module,
271         .unload = unload_module,
272         .load_pri = AST_MODPRI_CDR_DRIVER,
273 );