Mercurial > hg > freeDiameter
annotate extensions/rt_rewrite/rt_rewrite.c @ 1547:44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
author | Thomas Klausner <tk@giga.or.at> |
---|---|
date | Mon, 15 Jun 2020 20:33:35 +0200 |
parents | c8057892e56b |
children |
rev | line source |
---|---|
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 | |
1424
c8057892e56b
adapt extensions to fd_msg_search_avp(struct avp)
Luke Mewburn <luke@mewburn.net>
parents:
1388
diff
changeset
|
89 /* TODO: convert to fd_msg_search_avp ? */ |
1341 | 90 static int fd_avp_search_avp(msg_or_avp *where, struct dict_object *what, struct avp **avp) |
91 { | |
92 struct avp *nextavp; | |
93 struct dict_avp_data dictdata; | |
94 enum dict_object_type dicttype; | |
95 | |
96 CHECK_PARAMS((fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP)); | |
97 CHECK_FCT(fd_dict_getval(what, &dictdata)); | |
98 | |
99 /* Loop on all top AVPs */ | |
100 CHECK_FCT(fd_msg_browse(where, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)); | |
101 while (nextavp) { | |
102 struct avp_hdr *header = NULL; | |
103 struct dict_object *model = NULL; | |
104 if (fd_msg_avp_hdr(nextavp, &header) != 0) { | |
105 fd_log_notice("%s: unable to get header for AVP", MODULE_NAME); | |
106 return -1; | |
107 } | |
108 if (fd_msg_model(nextavp, &model) != 0) { | |
109 fd_log_notice("%s: unable to get model for AVP (%d, vendor %d)", MODULE_NAME, header->avp_code, header->avp_vendor); | |
110 return 0; | |
111 } | |
112 if (model == NULL) { | |
113 fd_log_notice("%s: unknown AVP (%d, vendor %d) (not in dictionary), skipping", MODULE_NAME, header->avp_code, header->avp_vendor); | |
114 } else { | |
115 if ((header->avp_code == dictdata.avp_code) && (header->avp_vendor == dictdata.avp_vendor)) { | |
116 break; | |
117 } | |
118 } | |
119 | |
120 /* Otherwise move to next AVP in the message */ | |
121 CHECK_FCT(fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); | |
122 } | |
123 | |
124 if (avp) | |
125 *avp = nextavp; | |
126 | |
127 if (*avp || nextavp) { | |
128 return 0; | |
129 } else { | |
130 return ENOENT; | |
131 } | |
132 } | |
133 | |
134 | |
135 | |
136 static msg_or_avp *find_container(msg_or_avp *msg, struct avp_target *target) | |
137 { | |
138 msg_or_avp *location = msg; | |
139 while (target->child) { | |
140 struct dict_object *avp_do; | |
141 msg_or_avp *new_location = NULL; | |
142 int ret; | |
143 | |
144 fd_log_debug("%s: looking for '%s'", MODULE_NAME, target->name); | |
145 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, target->name, &avp_do, ENOENT)) != 0) { | |
146 fd_log_error("%s: target AVP '%s' not in dictionary: %s", MODULE_NAME, target->name, strerror(ret)); | |
147 return NULL; | |
148 } | |
149 if ((ret=fd_avp_search_avp(location, avp_do, (struct avp **)&new_location)) != 0) { | |
150 fd_log_debug("%s: did not find '%s', adding it", MODULE_NAME, target->name); | |
151 struct avp *avp = NULL; | |
152 if ((ret = fd_msg_avp_new(avp_do, 0, &avp)) != 0) { | |
153 fd_log_notice("%s: unable to create new '%s' AVP", MODULE_NAME, target->name); | |
154 return NULL; | |
155 } | |
156 if ((ret=fd_msg_avp_add(location, MSG_BRW_LAST_CHILD, avp)) != 0) { | |
157 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, target->name, strerror(ret)); | |
158 return NULL; | |
159 } | |
160 fd_log_debug("%s: '%s' added", MODULE_NAME, target->name); | |
161 /* now find it in the new place */ | |
162 continue; | |
163 } else { | |
164 location = new_location; | |
165 fd_log_debug("%s: found '%s'", MODULE_NAME, target->name); | |
166 } | |
167 target = target->child; | |
168 } | |
169 fd_log_debug("%s: returning AVP for '%s'", MODULE_NAME, target->name); | |
170 return location; | |
171 } | |
172 | |
173 static int store_apply(msg_or_avp *msg, struct store **store) | |
174 { | |
175 while (*store) { | |
176 msg_or_avp *container; | |
177 struct store *next; | |
178 if ((*store)->avp) { | |
179 int ret; | |
180 if ((container=find_container(msg, (*store)->target)) == NULL) { | |
181 fd_log_error("%s: internal error, container not found", MODULE_NAME); | |
182 return -1; | |
183 } | |
184 | |
185 if ((ret=fd_msg_avp_add(container, MSG_BRW_LAST_CHILD, (*store)->avp)) != 0) { | |
186 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, (*store)->target->name, strerror(ret)); | |
187 return -1; | |
188 } | |
189 } | |
190 next = (*store)->next; | |
191 free(*store); | |
192 *store = next; | |
193 } | |
194 return 0; | |
195 } | |
196 | |
197 static int schedule_for_adding(struct avp *avp, struct avp_target *target, struct store *store) | |
198 { | |
199 if (store->avp) { | |
200 struct store *new; | |
201 if ((new=store_new()) == NULL) { | |
202 return -1; | |
203 } | |
204 new->avp = avp; | |
205 new->target = target; | |
206 new->next = store->next; | |
207 store->next = new; | |
208 } else { | |
209 store->avp = avp; | |
210 store->target = target; | |
211 } | |
212 fd_log_debug("%s: noted %s for later adding", MODULE_NAME, target->name); | |
213 return 0; | |
214 } | |
215 | |
216 static void move_avp_to_target(union avp_value *avp_value, struct avp_target *target, struct store *store) | |
217 { | |
218 struct dict_object *avp_do; | |
219 struct avp *avp; | |
220 int ret; | |
221 struct avp_target *final = target; | |
222 | |
223 while (final->child) { | |
224 final = final->child; | |
225 } | |
226 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, final->name, &avp_do, ENOENT)) != 0) { | |
227 fd_log_error("internal error, target AVP '%s' not in dictionary: %s", final->name, strerror(ret)); | |
228 return; | |
229 } | |
230 if ((ret=fd_msg_avp_new(avp_do, 0, &avp)) != 0) { | |
231 fd_log_error("internal error, error creating structure for AVP '%s': %s", final->name, strerror(ret)); | |
232 return; | |
233 } | |
234 if ((ret=fd_msg_avp_setvalue(avp, avp_value)) != 0) { | |
235 fd_log_error("internal error, cannot set value for AVP '%s': %s", final->name, strerror(ret)); | |
236 return; | |
237 } | |
238 if (schedule_for_adding(avp, target, store) != 0) { | |
239 fd_log_error("internal error, cannot add AVP '%s' to message", final->name); | |
240 return; | |
241 } | |
242 | |
243 return; | |
244 } | |
245 | |
246 | |
247 static struct avp_match *avp_to_be_replaced(const char *avp_name, struct avp_match *subtree) | |
248 { | |
249 struct avp_match *ret; | |
250 for (ret=subtree; ret != NULL; ret=ret->next) { | |
251 if (strcmp(ret->name, avp_name) == 0) { | |
252 return ret; | |
253 } | |
254 } | |
255 return NULL; | |
256 } | |
257 | |
258 /* | |
259 * msg: whole message | |
260 * avp: current AVP in message | |
261 * subtree: comparison subtree | |
262 */ | |
263 static int replace_avps(struct msg *msg, msg_or_avp *avp, struct avp_match *subtree, struct store *store) | |
264 { | |
265 int nothing_left = 1; | |
266 /* for each AVP in message */ | |
267 while (avp) { | |
268 struct avp_hdr *header = NULL; | |
269 struct dict_object *model = NULL; | |
270 const char *avp_name = NULL; | |
271 msg_or_avp *next; | |
272 int delete = 0; | |
273 | |
274 if (fd_msg_avp_hdr(avp, &header) != 0) { | |
275 fd_log_notice("internal error: unable to get header for AVP"); | |
276 return 0; | |
277 } | |
278 if (fd_msg_model(avp, &model) != 0) { | |
279 fd_log_notice("internal error: unable to get model for AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); | |
280 return 0; | |
281 } | |
282 if (model == NULL) { | |
283 fd_log_notice("unknown AVP (%d, vendor %d) (not in dictionary), skipping", header->avp_code, header->avp_vendor); | |
284 | |
285 } else { | |
286 struct dict_avp_data dictdata; | |
287 struct avp_match *subtree_match; | |
288 enum dict_avp_basetype basetype = AVP_TYPE_OCTETSTRING; | |
289 | |
290 fd_dict_getval(model, &dictdata); | |
291 avp_name = dictdata.avp_name; | |
292 basetype = dictdata.avp_basetype; | |
293 /* check if it exists in the subtree */ | |
294 if ((subtree_match = avp_to_be_replaced(avp_name, subtree))) { | |
295 /* if this should be deleted, mark as such */ | |
296 if (subtree_match->drop) { | |
297 fd_log_notice("%s: dropping AVP '%s'", MODULE_NAME, avp_name); | |
298 delete = 1; | |
299 } | |
300 /* if grouped, dive down */ | |
301 if (basetype == AVP_TYPE_GROUPED) { | |
302 msg_or_avp *child = NULL; | |
303 | |
304 fd_log_debug("%s: grouped AVP '%s'", MODULE_NAME, avp_name); | |
305 if (fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child, NULL) != 0) { | |
306 fd_log_notice("internal error: unable to browse message at AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); | |
307 return 0; | |
308 } | |
309 | |
310 /* replace_avps returns if the AVP was emptied out */ | |
311 if (replace_avps(msg, child, subtree_match->children, store)) { | |
312 fd_log_notice("%s: removing empty grouped AVP '%s'", MODULE_NAME, avp_name); | |
313 delete = 1; | |
314 } | |
315 } | |
316 else { | |
317 /* if single, remove it and add replacement AVP in target structure */ | |
318 if (subtree_match->target) { | |
319 struct avp_target *final = subtree_match->target; | |
320 while (final->child) { | |
321 final = final->child; | |
322 } | |
323 fd_log_notice("%s: moving AVP '%s' to '%s'", MODULE_NAME, avp_name, final->name); | |
324 move_avp_to_target(header->avp_value, subtree_match->target, store); | |
325 delete = 1; | |
326 } | |
327 } | |
328 } | |
329 } | |
330 fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL); | |
331 if (delete) { | |
332 /* remove AVP from message */ | |
333 fd_msg_free((msg_or_avp *)avp); | |
334 } else { | |
335 nothing_left = 0; | |
336 } | |
337 | |
338 avp = next; | |
339 } | |
340 | |
341 return nothing_left; | |
342 } | |
343 | |
344 static volatile int in_signal_handler = 0; | |
345 | |
346 /* signal handler */ | |
347 static void sig_hdlr(void) | |
348 { | |
349 struct avp_match *old_config; | |
350 | |
351 if (in_signal_handler) { | |
352 fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); | |
353 return; | |
354 } | |
355 in_signal_handler = 1; | |
356 | |
357 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { | |
358 fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); | |
359 return; | |
360 } | |
361 | |
362 /* save old config in case reload goes wrong */ | |
363 old_config = avp_match_start; | |
364 avp_match_start = NULL; | |
365 | |
366 if (rt_rewrite_conf_handle(config_file) != 0) { | |
367 fd_log_notice("%s: error reloading configuration, restoring previous configuration", MODULE_NAME); | |
368 avp_match_free(avp_match_start); | |
369 avp_match_start = old_config; | |
370 } else { | |
371 avp_match_free(old_config); | |
372 } | |
373 | |
374 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { | |
375 fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); | |
376 exit(1); | |
377 } | |
378 | |
379 fd_log_notice("%s: reloaded configuration", MODULE_NAME); | |
380 | |
381 in_signal_handler = 0; | |
382 } | |
383 | |
384 static int rt_rewrite(void * cbdata, struct msg **msg) | |
385 { | |
386 int ret; | |
387 msg_or_avp *avp = NULL; | |
388 struct store *store = NULL; | |
389 | |
390 /* nothing configured */ | |
391 if (avp_match_start == NULL) { | |
392 return 0; | |
393 } | |
394 | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
395 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
396 fd_log_error("%s: locking failed, aborting message rewrite", MODULE_NAME); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
397 return errno; |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
398 } |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
399 |
1341 | 400 if ((ret = fd_msg_parse_dict(*msg, fd_g_config->cnf_dict, NULL)) != 0) { |
401 fd_log_notice("%s: error parsing message", MODULE_NAME); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
402 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 403 return ret; |
404 } | |
405 if ((ret=fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL)) != 0) { | |
406 fd_log_notice("internal error: message has no child"); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
407 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 408 return ret; |
409 } | |
1547
44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
Thomas Klausner <tk@giga.or.at>
parents:
1424
diff
changeset
|
410 if ((store=store_new()) == NULL) { |
44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
Thomas Klausner <tk@giga.or.at>
parents:
1424
diff
changeset
|
411 fd_log_error("%s: malloc failure"); |
44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
Thomas Klausner <tk@giga.or.at>
parents:
1424
diff
changeset
|
412 pthread_rwlock_unlock(&rt_rewrite_lock); |
44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
Thomas Klausner <tk@giga.or.at>
parents:
1424
diff
changeset
|
413 return ENOMEM; |
44bb63ba9c2c
rt_rewrite: allocate object later to reduce cleanups
Thomas Klausner <tk@giga.or.at>
parents:
1424
diff
changeset
|
414 } |
1341 | 415 if (replace_avps(*msg, avp, avp_match_start->children, store) != 0) { |
416 fd_log_error("%s: replace AVP function failed", MODULE_NAME); | |
417 store_free(store); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
418 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 419 return -1; |
420 } | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
421 ret = store_apply(*msg, &store); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
422 |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
423 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
424 fd_log_error("%s: unlocking failed, returning error", MODULE_NAME); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
425 return errno; |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
426 } |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
427 |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
428 return ret; |
1341 | 429 } |
430 | |
431 /* entry point */ | |
432 static int rt_rewrite_entry(char * conffile) | |
433 { | |
434 int ret; | |
435 config_file = conffile; | |
436 | |
437 pthread_rwlock_init(&rt_rewrite_lock, NULL); | |
438 | |
439 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { | |
440 fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); | |
441 return EDEADLK; | |
442 } | |
443 | |
444 /* Parse the configuration file */ | |
445 if ((ret=rt_rewrite_conf_handle(config_file)) != 0) { | |
446 pthread_rwlock_unlock(&rt_rewrite_lock); | |
447 return ret; | |
448 } | |
449 | |
450 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { | |
451 fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); | |
452 return EDEADLK; | |
453 } | |
454 | |
455 /* Register reload callback */ | |
456 CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); | |
457 | |
458 /* Register the callback */ | |
459 if ((ret=fd_rt_fwd_register(rt_rewrite, NULL, RT_FWD_ALL, &rt_rewrite_handle)) != 0) { | |
460 fd_log_error("Cannot register callback handler"); | |
461 return ret; | |
462 } | |
463 | |
464 fd_log_notice("Extension 'Rewrite' initialized"); | |
465 return 0; | |
466 } | |
467 | |
468 /* Unload */ | |
469 void fd_ext_fini(void) | |
470 { | |
471 /* Unregister the callbacks */ | |
472 fd_rt_fwd_unregister(rt_rewrite_handle, NULL); | |
473 return ; | |
474 } | |
475 | |
476 EXTENSION_ENTRY("rt_rewrite", rt_rewrite_entry); |