7266f655c6579d13c0363c34d3150eb968a10695
[kamailio] / src / modules / ims_diameter_server / ims_diameter_server.c
1 /*
2  * Copyright (C) 2016-2017 ng-voice GmbH, carsten@ng-voice.com
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License 
17  * along with this program; if not, write to the Free Software 
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  * 
20  */
21
22 #include "../../core/sr_module.h"
23 #include "../../core/route.h"
24 #include "../cdp/cdp_load.h"
25 #include "../cdp_avp/cdp_avp_mod.h"
26 #include "ims_diameter_server.h"
27 #include "avp_helper.h"
28 #include "../../core/fmsg.h"
29
30 MODULE_VERSION
31
32 extern gen_lock_t* process_lock; /* lock on the process table */
33
34 struct cdp_binds cdpb;
35 cdp_avp_bind_t *cdp_avp;
36
37 AAAMessage *request;
38 str responsejson;
39 str requestjson;
40
41 struct cdp_binds cdpb;
42
43 cdp_avp_bind_t *cdp_avp;
44
45 /** module functions */
46 static int mod_init(void);
47 static int mod_child_init(int);
48 static void mod_destroy(void);
49
50 int * callback_singleton; /*< Callback singleton */
51
52 int event_route_diameter = 0;
53 int event_route_diameter_response = 0;
54
55 static int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* commandcode, char* message, int async);
56 static int w_diameter_request(struct sip_msg * msg, char* appid, char* commandcode, char* message);
57 static int w_diameter_request_peer(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message);
58 static int w_diameter_request_async(struct sip_msg * msg, char* appid, char* commandcode, char* message);
59 static int w_diameter_request_peer_async(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message);
60
61
62 static cmd_export_t cmds[] = {
63         {"diameter_request", (cmd_function)w_diameter_request, 3, fixup_var_pve_str_12, 0, ANY_ROUTE},
64         {"diameter_request", (cmd_function)w_diameter_request_peer, 4, fixup_var_pve_str_12, 0, ANY_ROUTE},
65         {"diameter_request_async", (cmd_function)w_diameter_request_async, 3, fixup_var_pve_str_12, 0, ANY_ROUTE},
66         {"diameter_request_async", (cmd_function)w_diameter_request_peer_async, 4, fixup_var_pve_str_12, 0, ANY_ROUTE},
67         { 0, 0, 0, 0, 0, 0}
68 };
69
70 static param_export_t params[] = {
71     { 0, 0, 0}
72 };
73
74 static pv_export_t mod_pvs[] = {
75         { {"diameter_command", sizeof("diameter_command")-1}, PVT_OTHER, pv_get_command, 0, 0, 0, 0, 0 },
76         { {"diameter_application", sizeof("diameter_application")-1}, PVT_OTHER, pv_get_application, 0, 0, 0, 0, 0 },
77         { {"diameter_request", sizeof("diameter_request")-1}, PVT_OTHER, pv_get_request, 0, 0, 0, 0, 0 },
78         { {"diameter_response", sizeof("diameter_response")-1}, PVT_OTHER, pv_get_response, pv_set_response, 0, 0, 0, 0 },
79         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
80 };
81
82 /** module exports */
83 struct module_exports exports = {
84         "ims_diameter_server", 
85         DEFAULT_DLFLAGS, /* dlopen flags */
86         cmds,            /* Exported functions */
87         params,          /* exported statistics */
88         0,               /* exported RPC methods */
89         mod_pvs,         /* exported pseudo-variables */
90         0,               /* response handling function */
91         mod_init,        /* module initialization function */
92         mod_child_init,  /* per-child init function */
93         mod_destroy
94 };
95
96 /**
97  * init module function
98  */
99 static int mod_init(void) {
100         LM_DBG("Loading...\n");
101
102         request = 0;
103         responsejson.s = 0;
104         responsejson.len = 0;
105         requestjson.s = 0;
106         requestjson.len = 0;
107
108         callback_singleton = shm_malloc(sizeof (int));
109         *callback_singleton = 0;
110
111         cdp_avp = 0;
112         /* load the CDP API */
113         if (load_cdp_api(&cdpb) != 0) {
114                 LM_ERR("can't load CDP API\n");
115                 goto error;
116             }
117
118         cdp_avp = load_cdp_avp();
119         if (!cdp_avp) {
120                 LM_ERR("can't load CDP_AVP API\n");
121                 goto error;
122         }
123
124         event_route_diameter = route_get(&event_rt, "diameter:request");
125         if (event_route_diameter < 0) {
126                 LM_ERR("No diameter:request event route found\n");
127                 goto error;
128         }
129         LM_DBG("Found Route diameter:request: %i\n", event_route_diameter);
130
131         event_route_diameter_response = route_get(&event_rt, "diameter:response");
132         if (event_route_diameter_response < 0) {
133                 LM_WARN("No diameter:response event route found, asynchronous operations disabled.\n");
134         } else {
135                 LM_DBG("Found Route diameter:response: %i\n", event_route_diameter_response);
136         }
137
138         return 0;
139 error:
140         LM_ERR("Failed to initialise ims_diameter_server module\n");
141         return -1;
142 }
143
144 /**
145  * Initializes the module in child.
146  */
147 static int mod_child_init(int rank) {
148     LM_DBG("Initialization of module in child [%d] \n", rank);
149
150     /* don't do anything for main process and TCP manager process */
151     if (rank == PROC_MAIN || rank == PROC_TCP_MAIN) {
152         return 0;
153     }
154
155     lock_get(process_lock);
156     if ((*callback_singleton) == 0) {
157         *callback_singleton = 1;
158         cdpb.AAAAddRequestHandler(callback_cdp_request, NULL);
159     }
160     lock_release(process_lock);
161
162     return 0;
163 }
164
165
166 static void mod_destroy(void) {
167
168 }
169
170 /**
171  * Handler for incoming Diameter requests.
172  * @param request - the received request
173  * @param param - generic pointer
174  * @returns the answer to this request
175  */
176 AAAMessage* callback_cdp_request(AAAMessage *request_in, void *param) {
177         struct sip_msg *fmsg;
178         int backup_rt;
179         struct run_act_ctx ctx;
180         AAAMessage *response;
181
182         LM_DBG("Got DIAMETER-Request!\n");
183
184         if (is_req(request_in)) {
185                 LM_DBG("is request!\n");
186                 LM_DBG("Found Route diameter:request: %i\n", event_route_diameter);
187
188                 request = request_in;
189                 response = cdpb.AAACreateResponse(request_in);
190                 if (!response) return 0;
191
192                 backup_rt = get_route_type();
193                 set_route_type(REQUEST_ROUTE);
194                 init_run_actions_ctx(&ctx);
195                 fmsg = faked_msg_next();
196                 responsejson.s = 0;
197                 responsejson.len = 0;
198
199                 run_top_route(event_rt.rlist[event_route_diameter], fmsg, &ctx);
200
201                 set_route_type(backup_rt);
202                 LM_DBG("Processed Event-Route!\n");
203
204                 if (addAVPsfromJSON(response, NULL)) {
205                         return response;
206                 } else {
207                         return 0;
208                 }
209         }
210         return 0;
211 }
212
213 int w_diameter_request(struct sip_msg * msg, char* appid, char* commandcode, char* message) {
214         return diameter_request(msg, 0, appid, commandcode, message, 0);
215 }
216
217 static int w_diameter_request_peer(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message) {
218         return diameter_request(msg, peer, appid, commandcode, message, 0);
219 }
220
221 static int w_diameter_request_async(struct sip_msg * msg, char* appid, char* commandcode, char* message) {
222         return diameter_request(msg, 0, appid, commandcode, message, 1);
223 }
224
225 static int w_diameter_request_peer_async(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message) {
226         return diameter_request(msg, peer, appid, commandcode, message, 1);
227 }
228
229 void async_cdp_diameter_callback(int is_timeout, void *request, AAAMessage *response, long elapsed_msecs) {
230         struct sip_msg *fmsg;
231         int backup_rt;
232         struct run_act_ctx ctx;
233
234         if (is_timeout != 0) {
235                 LM_ERR("Error timeout when sending message via CDP\n");
236                 goto error;
237         }
238
239         if (!response) {
240                 LM_ERR("Error sending message via CDP\n");
241                 goto error;
242         }
243         if (AAAmsg2json(response, &responsejson) != 1) {
244                 LM_ERR("Failed to convert response to JSON\n");
245         }
246         request = (AAAMessage*)request;
247
248         backup_rt = get_route_type();
249         set_route_type(REQUEST_ROUTE);
250         init_run_actions_ctx(&ctx);
251         fmsg = faked_msg_next();
252
253         run_top_route(event_rt.rlist[event_route_diameter_response], fmsg, &ctx);
254
255         set_route_type(backup_rt);
256         LM_DBG("Processed Event-Route!\n");
257 error:
258         //free memory
259         if (response) cdpb.AAAFreeMessage(&response);
260 }
261
262
263 int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* commandcode, char* message, int async) {
264         str s_appid, s_commandcode, s_peer, s_message;
265         AAAMessage *req = 0;
266         AAASession *session = 0;
267         AAAMessage *resp = 0;
268
269         unsigned int i_appid, i_commandcode;
270
271         if (async && (event_route_diameter_response < 0)) {
272                 LM_ERR("Asynchronous operations disabled\n");
273                 return -1;
274         }
275
276         if (peer) {
277                 if (get_str_fparam(&s_peer, msg, (fparam_t*)peer) < 0) {
278                     LM_ERR("failed to get Peer\n");
279                     return -1;
280                 }
281                 LM_DBG("Peer %.*s\n", s_peer.len, s_peer.s);
282         }
283         if (get_str_fparam(&s_message, msg, (fparam_t*)message) < 0) {
284                 LM_ERR("failed to get Message\n");
285                 return -1;
286         }
287         if (get_str_fparam(&s_appid, msg, (fparam_t*)appid) < 0) {
288                 LM_ERR("failed to get App-ID\n");
289                 return -1;
290         }
291         if (str2int(&s_appid, &i_appid) != 0) {
292                 LM_ERR("Invalid App-ID (%.*s)\n", s_appid.len, s_appid.s);
293                 return -1;
294         }
295         LM_DBG("App-ID %i\n", i_appid);
296         if (get_str_fparam(&s_commandcode, msg, (fparam_t*)commandcode) < 0) {
297                 LM_ERR("failed to get Command-Code\n");
298                 return -1;
299         }
300         if (str2int(&s_commandcode, &i_commandcode) != 0) {
301                 LM_ERR("Invalid Command-Code (%.*s)\n", s_commandcode.len, s_commandcode.s);
302                 return -1;
303         }
304         LM_DBG("Command-Code %i\n", i_commandcode);
305         if (get_str_fparam(&s_commandcode, msg, (fparam_t*)commandcode) < 0) {
306                 LM_ERR("failed to get Command-Code\n");
307                 return -1;
308         }
309
310         session = cdpb.AAACreateSession(0);
311
312         req = cdpb.AAACreateRequest(i_appid, i_commandcode, Flag_Proxyable, session);
313         if (!req) goto error1;
314
315         if (addAVPsfromJSON(req, &s_message)) {
316                 LM_ERR("Failed to parse JSON Request\n");
317                 return -1;
318         }
319
320
321         if (peer && (s_peer.len > 0)) {
322                 if (async) {
323                         cdpb.AAASendMessageToPeer(req, &s_peer, (void*) async_cdp_diameter_callback, req);
324                         LM_DBG("Successfully sent async diameter\n");
325                         return 0;
326                 } else {
327                         resp = cdpb.AAASendRecvMessageToPeer(req, &s_peer);
328                         LM_DBG("Successfully sent diameter\n");
329                         if (resp && AAAmsg2json(resp, &responsejson) == 1) {
330                                 return 1;
331                         } else {
332                                 LM_ERR("Failed to convert response to JSON\n");
333                                 return -1;
334                         }
335                 }
336         } else {
337                 if (async) {
338                         cdpb.AAASendMessage(req, (void*) async_cdp_diameter_callback, req);
339                         LM_DBG("Successfully sent async diameter\n");
340                         return 0;
341                 } else {
342                         resp = cdpb.AAASendRecvMessage(req);
343                         LM_DBG("Successfully sent diameter\n");
344                         if (resp && AAAmsg2json(resp, &responsejson) == 1) {
345                                 return 1;
346                         } else {
347                                 LM_ERR("Failed to convert response to JSON\n");
348                                 return -1;
349                         }
350                 }
351         }
352 error1:
353         //Only free UAR IFF it has not been passed to CDP
354         if (req) cdpb.AAAFreeMessage(&req);
355         LM_ERR("Error occurred trying to send request\n");
356         return -1;
357 }
358
359