1341
|
1 /********************************************************************************************************* |
|
2 * Software License Agreement (BSD License) * |
|
3 * Author: Thomas Klausner <tk@giga.or.at> * |
|
4 * * |
|
5 * Copyright (c) 2018, Thomas Klausner * |
|
6 * All rights reserved. * |
|
7 * * |
|
8 * Written under contract by Effortel Technologies SA, http://effortel.com/ * |
|
9 * * |
|
10 * Redistribution and use of this software in source and binary forms, with or without modification, are * |
|
11 * permitted provided that the following conditions are met: * |
|
12 * * |
|
13 * * Redistributions of source code must retain the above * |
|
14 * copyright notice, this list of conditions and the * |
|
15 * following disclaimer. * |
|
16 * * |
|
17 * * Redistributions in binary form must reproduce the above * |
|
18 * copyright notice, this list of conditions and the * |
|
19 * following disclaimer in the documentation and/or other * |
|
20 * materials provided with the distribution. * |
|
21 * * |
|
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
|
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
|
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
|
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
|
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
|
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
|
28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
|
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
|
30 *********************************************************************************************************/ |
|
31 |
|
32 #include <freeDiameter/extension.h> |
|
33 #include "rt_rewrite.h" |
|
34 |
|
35 #include <pthread.h> |
|
36 #include <signal.h> |
|
37 |
|
38 /* |
|
39 * Replace AVPs: put their values into other AVPs |
|
40 * Remove AVPs: drop them from a message |
|
41 */ |
|
42 |
|
43 /* handler */ |
|
44 static struct fd_rt_fwd_hdl * rt_rewrite_handle = NULL; |
|
45 |
|
46 static char *config_file; |
|
47 |
|
48 struct avp_match *avp_match_start = NULL; |
|
49 |
|
50 static pthread_rwlock_t rt_rewrite_lock; |
|
51 |
|
52 #define MODULE_NAME "rt_rewrite" |
|
53 |
|
54 struct store { |
|
55 struct avp *avp; |
|
56 struct avp_target *target; |
|
57 struct store *next; |
|
58 }; |
|
59 |
|
60 static struct store *store_new(void) { |
|
61 struct store *ret; |
|
62 |
|
63 if ((ret=malloc(sizeof(*ret))) == NULL) { |
|
64 fd_log_error("%s: malloc failure", MODULE_NAME); |
|
65 return NULL; |
|
66 } |
|
67 ret->avp = NULL; |
|
68 ret->target = NULL; |
|
69 ret->next = NULL; |
|
70 |
|
71 return ret; |
|
72 } |
|
73 |
|
74 static void store_free(struct store *store) |
|
75 { |
|
76 while (store) { |
|
77 struct store *next; |
|
78 if (store->avp) { |
|
79 fd_msg_free((msg_or_avp *)store->avp); |
|
80 } |
|
81 store->target = NULL; |
|
82 next = store->next; |
|
83 free(store); |
|
84 store = next; |
|
85 } |
|
86 return; |
|
87 } |
|
88 |
|
89 static int fd_avp_search_avp(msg_or_avp *where, struct dict_object *what, struct avp **avp) |
|
90 { |
|
91 struct avp *nextavp; |
|
92 struct dict_avp_data dictdata; |
|
93 enum dict_object_type dicttype; |
|
94 |
|
95 CHECK_PARAMS((fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP)); |
|
96 CHECK_FCT(fd_dict_getval(what, &dictdata)); |
|
97 |
|
98 /* Loop on all top AVPs */ |
|
99 CHECK_FCT(fd_msg_browse(where, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)); |
|
100 while (nextavp) { |
|
101 struct avp_hdr *header = NULL; |
|
102 struct dict_object *model = NULL; |
|
103 if (fd_msg_avp_hdr(nextavp, &header) != 0) { |
|
104 fd_log_notice("%s: unable to get header for AVP", MODULE_NAME); |
|
105 return -1; |
|
106 } |
|
107 if (fd_msg_model(nextavp, &model) != 0) { |
|
108 fd_log_notice("%s: unable to get model for AVP (%d, vendor %d)", MODULE_NAME, header->avp_code, header->avp_vendor); |
|
109 return 0; |
|
110 } |
|
111 if (model == NULL) { |
|
112 fd_log_notice("%s: unknown AVP (%d, vendor %d) (not in dictionary), skipping", MODULE_NAME, header->avp_code, header->avp_vendor); |
|
113 } else { |
|
114 if ((header->avp_code == dictdata.avp_code) && (header->avp_vendor == dictdata.avp_vendor)) { |
|
115 break; |
|
116 } |
|
117 } |
|
118 |
|
119 /* Otherwise move to next AVP in the message */ |
|
120 CHECK_FCT(fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); |
|
121 } |
|
122 |
|
123 if (avp) |
|
124 *avp = nextavp; |
|
125 |
|
126 if (*avp || nextavp) { |
|
127 return 0; |
|
128 } else { |
|
129 return ENOENT; |
|
130 } |
|
131 } |
|
132 |
|
133 |
|
134 |
|
135 static msg_or_avp *find_container(msg_or_avp *msg, struct avp_target *target) |
|
136 { |
|
137 msg_or_avp *location = msg; |
|
138 while (target->child) { |
|
139 struct dict_object *avp_do; |
|
140 msg_or_avp *new_location = NULL; |
|
141 int ret; |
|
142 |
|
143 fd_log_debug("%s: looking for '%s'", MODULE_NAME, target->name); |
|
144 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, target->name, &avp_do, ENOENT)) != 0) { |
|
145 fd_log_error("%s: target AVP '%s' not in dictionary: %s", MODULE_NAME, target->name, strerror(ret)); |
|
146 return NULL; |
|
147 } |
|
148 if ((ret=fd_avp_search_avp(location, avp_do, (struct avp **)&new_location)) != 0) { |
|
149 fd_log_debug("%s: did not find '%s', adding it", MODULE_NAME, target->name); |
|
150 struct avp *avp = NULL; |
|
151 if ((ret = fd_msg_avp_new(avp_do, 0, &avp)) != 0) { |
|
152 fd_log_notice("%s: unable to create new '%s' AVP", MODULE_NAME, target->name); |
|
153 return NULL; |
|
154 } |
|
155 if ((ret=fd_msg_avp_add(location, MSG_BRW_LAST_CHILD, avp)) != 0) { |
|
156 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, target->name, strerror(ret)); |
|
157 return NULL; |
|
158 } |
|
159 fd_log_debug("%s: '%s' added", MODULE_NAME, target->name); |
|
160 /* now find it in the new place */ |
|
161 continue; |
|
162 } else { |
|
163 location = new_location; |
|
164 fd_log_debug("%s: found '%s'", MODULE_NAME, target->name); |
|
165 } |
|
166 target = target->child; |
|
167 } |
|
168 fd_log_debug("%s: returning AVP for '%s'", MODULE_NAME, target->name); |
|
169 return location; |
|
170 } |
|
171 |
|
172 static int store_apply(msg_or_avp *msg, struct store **store) |
|
173 { |
|
174 while (*store) { |
|
175 msg_or_avp *container; |
|
176 struct store *next; |
|
177 if ((*store)->avp) { |
|
178 int ret; |
|
179 if ((container=find_container(msg, (*store)->target)) == NULL) { |
|
180 fd_log_error("%s: internal error, container not found", MODULE_NAME); |
|
181 return -1; |
|
182 } |
|
183 |
|
184 if ((ret=fd_msg_avp_add(container, MSG_BRW_LAST_CHILD, (*store)->avp)) != 0) { |
|
185 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, (*store)->target->name, strerror(ret)); |
|
186 return -1; |
|
187 } |
|
188 } |
|
189 next = (*store)->next; |
|
190 free(*store); |
|
191 *store = next; |
|
192 } |
|
193 return 0; |
|
194 } |
|
195 |
|
196 static int schedule_for_adding(struct avp *avp, struct avp_target *target, struct store *store) |
|
197 { |
|
198 if (store->avp) { |
|
199 struct store *new; |
|
200 if ((new=store_new()) == NULL) { |
|
201 return -1; |
|
202 } |
|
203 new->avp = avp; |
|
204 new->target = target; |
|
205 new->next = store->next; |
|
206 store->next = new; |
|
207 } else { |
|
208 store->avp = avp; |
|
209 store->target = target; |
|
210 } |
|
211 fd_log_debug("%s: noted %s for later adding", MODULE_NAME, target->name); |
|
212 return 0; |
|
213 } |
|
214 |
|
215 static void move_avp_to_target(union avp_value *avp_value, struct avp_target *target, struct store *store) |
|
216 { |
|
217 struct dict_object *avp_do; |
|
218 struct avp *avp; |
|
219 int ret; |
|
220 struct avp_target *final = target; |
|
221 |
|
222 while (final->child) { |
|
223 final = final->child; |
|
224 } |
|
225 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, final->name, &avp_do, ENOENT)) != 0) { |
|
226 fd_log_error("internal error, target AVP '%s' not in dictionary: %s", final->name, strerror(ret)); |
|
227 return; |
|
228 } |
|
229 if ((ret=fd_msg_avp_new(avp_do, 0, &avp)) != 0) { |
|
230 fd_log_error("internal error, error creating structure for AVP '%s': %s", final->name, strerror(ret)); |
|
231 return; |
|
232 } |
|
233 if ((ret=fd_msg_avp_setvalue(avp, avp_value)) != 0) { |
|
234 fd_log_error("internal error, cannot set value for AVP '%s': %s", final->name, strerror(ret)); |
|
235 return; |
|
236 } |
|
237 if (schedule_for_adding(avp, target, store) != 0) { |
|
238 fd_log_error("internal error, cannot add AVP '%s' to message", final->name); |
|
239 return; |
|
240 } |
|
241 |
|
242 return; |
|
243 } |
|
244 |
|
245 |
|
246 static struct avp_match *avp_to_be_replaced(const char *avp_name, struct avp_match *subtree) |
|
247 { |
|
248 struct avp_match *ret; |
|
249 for (ret=subtree; ret != NULL; ret=ret->next) { |
|
250 if (strcmp(ret->name, avp_name) == 0) { |
|
251 return ret; |
|
252 } |
|
253 } |
|
254 return NULL; |
|
255 } |
|
256 |
|
257 /* |
|
258 * msg: whole message |
|
259 * avp: current AVP in message |
|
260 * subtree: comparison subtree |
|
261 */ |
|
262 static int replace_avps(struct msg *msg, msg_or_avp *avp, struct avp_match *subtree, struct store *store) |
|
263 { |
|
264 int nothing_left = 1; |
|
265 /* for each AVP in message */ |
|
266 while (avp) { |
|
267 struct avp_hdr *header = NULL; |
|
268 struct dict_object *model = NULL; |
|
269 const char *avp_name = NULL; |
|
270 msg_or_avp *next; |
|
271 int delete = 0; |
|
272 |
|
273 if (fd_msg_avp_hdr(avp, &header) != 0) { |
|
274 fd_log_notice("internal error: unable to get header for AVP"); |
|
275 return 0; |
|
276 } |
|
277 if (fd_msg_model(avp, &model) != 0) { |
|
278 fd_log_notice("internal error: unable to get model for AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); |
|
279 return 0; |
|
280 } |
|
281 if (model == NULL) { |
|
282 fd_log_notice("unknown AVP (%d, vendor %d) (not in dictionary), skipping", header->avp_code, header->avp_vendor); |
|
283 |
|
284 } else { |
|
285 struct dict_avp_data dictdata; |
|
286 struct avp_match *subtree_match; |
|
287 enum dict_avp_basetype basetype = AVP_TYPE_OCTETSTRING; |
|
288 |
|
289 fd_dict_getval(model, &dictdata); |
|
290 avp_name = dictdata.avp_name; |
|
291 basetype = dictdata.avp_basetype; |
|
292 /* check if it exists in the subtree */ |
|
293 if ((subtree_match = avp_to_be_replaced(avp_name, subtree))) { |
|
294 /* if this should be deleted, mark as such */ |
|
295 if (subtree_match->drop) { |
|
296 fd_log_notice("%s: dropping AVP '%s'", MODULE_NAME, avp_name); |
|
297 delete = 1; |
|
298 } |
|
299 /* if grouped, dive down */ |
|
300 if (basetype == AVP_TYPE_GROUPED) { |
|
301 msg_or_avp *child = NULL; |
|
302 |
|
303 fd_log_debug("%s: grouped AVP '%s'", MODULE_NAME, avp_name); |
|
304 if (fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child, NULL) != 0) { |
|
305 fd_log_notice("internal error: unable to browse message at AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); |
|
306 return 0; |
|
307 } |
|
308 |
|
309 /* replace_avps returns if the AVP was emptied out */ |
|
310 if (replace_avps(msg, child, subtree_match->children, store)) { |
|
311 fd_log_notice("%s: removing empty grouped AVP '%s'", MODULE_NAME, avp_name); |
|
312 delete = 1; |
|
313 } |
|
314 } |
|
315 else { |
|
316 /* if single, remove it and add replacement AVP in target structure */ |
|
317 if (subtree_match->target) { |
|
318 struct avp_target *final = subtree_match->target; |
|
319 while (final->child) { |
|
320 final = final->child; |
|
321 } |
|
322 fd_log_notice("%s: moving AVP '%s' to '%s'", MODULE_NAME, avp_name, final->name); |
|
323 move_avp_to_target(header->avp_value, subtree_match->target, store); |
|
324 delete = 1; |
|
325 } |
|
326 } |
|
327 } |
|
328 } |
|
329 fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL); |
|
330 if (delete) { |
|
331 /* remove AVP from message */ |
|
332 fd_msg_free((msg_or_avp *)avp); |
|
333 } else { |
|
334 nothing_left = 0; |
|
335 } |
|
336 |
|
337 avp = next; |
|
338 } |
|
339 |
|
340 return nothing_left; |
|
341 } |
|
342 |
|
343 static volatile int in_signal_handler = 0; |
|
344 |
|
345 /* signal handler */ |
|
346 static void sig_hdlr(void) |
|
347 { |
|
348 struct avp_match *old_config; |
|
349 |
|
350 if (in_signal_handler) { |
|
351 fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); |
|
352 return; |
|
353 } |
|
354 in_signal_handler = 1; |
|
355 |
|
356 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { |
|
357 fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); |
|
358 return; |
|
359 } |
|
360 |
|
361 /* save old config in case reload goes wrong */ |
|
362 old_config = avp_match_start; |
|
363 avp_match_start = NULL; |
|
364 |
|
365 if (rt_rewrite_conf_handle(config_file) != 0) { |
|
366 fd_log_notice("%s: error reloading configuration, restoring previous configuration", MODULE_NAME); |
|
367 avp_match_free(avp_match_start); |
|
368 avp_match_start = old_config; |
|
369 } else { |
|
370 avp_match_free(old_config); |
|
371 } |
|
372 |
|
373 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { |
|
374 fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); |
|
375 exit(1); |
|
376 } |
|
377 |
|
378 fd_log_notice("%s: reloaded configuration", MODULE_NAME); |
|
379 |
|
380 in_signal_handler = 0; |
|
381 } |
|
382 |
|
383 static int rt_rewrite(void * cbdata, struct msg **msg) |
|
384 { |
|
385 int ret; |
|
386 msg_or_avp *avp = NULL; |
|
387 struct store *store = NULL; |
|
388 |
|
389 /* nothing configured */ |
|
390 if (avp_match_start == NULL) { |
|
391 return 0; |
|
392 } |
|
393 |
|
394 if ((store=store_new()) == NULL) { |
|
395 fd_log_error("%s: malloc failure"); |
|
396 return ENOMEM; |
|
397 } |
|
398 if ((ret = fd_msg_parse_dict(*msg, fd_g_config->cnf_dict, NULL)) != 0) { |
|
399 fd_log_notice("%s: error parsing message", MODULE_NAME); |
|
400 free(store); |
|
401 return ret; |
|
402 } |
|
403 if ((ret=fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL)) != 0) { |
|
404 fd_log_notice("internal error: message has no child"); |
|
405 free(store); |
|
406 return ret; |
|
407 } |
|
408 if (replace_avps(*msg, avp, avp_match_start->children, store) != 0) { |
|
409 fd_log_error("%s: replace AVP function failed", MODULE_NAME); |
|
410 store_free(store); |
|
411 return -1; |
|
412 } |
|
413 return store_apply(*msg, &store); |
|
414 } |
|
415 |
|
416 /* entry point */ |
|
417 static int rt_rewrite_entry(char * conffile) |
|
418 { |
|
419 int ret; |
|
420 config_file = conffile; |
|
421 |
|
422 pthread_rwlock_init(&rt_rewrite_lock, NULL); |
|
423 |
|
424 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { |
|
425 fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); |
|
426 return EDEADLK; |
|
427 } |
|
428 |
|
429 /* Parse the configuration file */ |
|
430 if ((ret=rt_rewrite_conf_handle(config_file)) != 0) { |
|
431 pthread_rwlock_unlock(&rt_rewrite_lock); |
|
432 return ret; |
|
433 } |
|
434 |
|
435 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { |
|
436 fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); |
|
437 return EDEADLK; |
|
438 } |
|
439 |
|
440 /* Register reload callback */ |
|
441 CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); |
|
442 |
|
443 /* Register the callback */ |
|
444 if ((ret=fd_rt_fwd_register(rt_rewrite, NULL, RT_FWD_ALL, &rt_rewrite_handle)) != 0) { |
|
445 fd_log_error("Cannot register callback handler"); |
|
446 return ret; |
|
447 } |
|
448 |
|
449 fd_log_notice("Extension 'Rewrite' initialized"); |
|
450 return 0; |
|
451 } |
|
452 |
|
453 /* Unload */ |
|
454 void fd_ext_fini(void) |
|
455 { |
|
456 /* Unregister the callbacks */ |
|
457 fd_rt_fwd_unregister(rt_rewrite_handle, NULL); |
|
458 return ; |
|
459 } |
|
460 |
|
461 EXTENSION_ENTRY("rt_rewrite", rt_rewrite_entry); |