Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2010-2014 OpenSIPS Solutions |
3 | | * Copyright (C) 2005-2006 Voice Sistem S.R.L. |
4 | | * Copyright (C) 2001-2003 FhG Fokus |
5 | | * |
6 | | * This file is part of opensips, a free SIP server. |
7 | | * |
8 | | * opensips is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version |
12 | | * |
13 | | * opensips is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | * |
22 | | * History: |
23 | | * --------- |
24 | | * 2003-02-28 scratchpad compatibility abandoned (jiri) |
25 | | * 2003-01-29 removed scratchpad (jiri) |
26 | | * 2003-03-19 fixed set* len calculation bug & simplified a little the code |
27 | | * (should be a little faster now) (andrei) |
28 | | * replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) |
29 | | * 2003-04-01 Added support for loose routing in forward (janakj) |
30 | | * 2003-04-12 FORCE_RPORT_T added (andrei) |
31 | | * 2003-04-22 strip_tail added (jiri) |
32 | | * 2003-10-02 added SET_ADV_ADDR_T & SET_ADV_PORT_T (andrei) |
33 | | * 2003-10-29 added FORCE_TCP_ALIAS_T (andrei) |
34 | | * 2004-11-30 added FORCE_SEND_SOCKET_T (andrei) |
35 | | * 2005-11-29 added serialize_branches and next_branches (bogdan) |
36 | | * 2006-03-02 MODULE_T action points to a cmd_export_t struct instead to |
37 | | * a function address - more info is accessible (bogdan) |
38 | | * 2006-05-22 forward(_udp,_tcp,_tls) and send(_tcp) merged in forward() and |
39 | | * send() (bogdan) |
40 | | * 2006-12-22 functions for script and branch flags added (bogdan) |
41 | | */ |
42 | | |
43 | | /*! |
44 | | * \file |
45 | | * \brief OpenSIPS Generic functions |
46 | | */ |
47 | | |
48 | | #include "action.h" |
49 | | #include "config.h" |
50 | | #include "error.h" |
51 | | #include "dprint.h" |
52 | | #include "route.h" |
53 | | #include "parser/msg_parser.h" |
54 | | #include "ut.h" |
55 | | #include "sr_module.h" |
56 | | #include "mem/mem.h" |
57 | | #include "errinfo.h" |
58 | | #include "msg_translator.h" |
59 | | #include "mod_fix.h" |
60 | | #include "script_var.h" |
61 | | #include "xlog.h" |
62 | | #include "cfg_pp.h" |
63 | | |
64 | | #include <string.h> |
65 | | |
66 | | #ifdef DEBUG_DMALLOC |
67 | | #include <dmalloc.h> |
68 | | #endif |
69 | | |
70 | | int action_flags = 0; |
71 | | int return_code = 0; |
72 | | int max_while_loops = 10000; |
73 | | |
74 | | /* script tracing options */ |
75 | | int use_script_trace = 0; |
76 | | int script_trace_log_level = L_ALERT; |
77 | | char *script_trace_info = NULL; |
78 | | pv_elem_t script_trace_elem; |
79 | | |
80 | | extern err_info_t _oser_err_info; |
81 | | |
82 | | action_time longest_action[LONGEST_ACTION_SIZE]; |
83 | | int min_action_time=0; |
84 | | |
85 | | struct route_params_level { |
86 | | void *params; |
87 | | void *extra; /* extra params used */ |
88 | | param_getf_t get_param; |
89 | | }; |
90 | | static struct route_params_level route_params[ROUTE_MAX_REC_LEV]; |
91 | | static int route_rec_level = -1; |
92 | | |
93 | | /* the current stack of route names (first route name is at index 0) */ |
94 | | char *route_stack[ROUTE_MAX_REC_LEV + 1]; |
95 | | |
96 | | /* the virtual start of the stack; typically 0, but may get temporarily bumped |
97 | | * with each added nesting level of script route callback execution |
98 | | * (e.g. due to logic similar to dlg_on_hangup(), followed by a SIP BYE) */ |
99 | | int route_stack_start; |
100 | | |
101 | | /* the total size of the stack, counting from index 0 */ |
102 | | int route_stack_size; |
103 | | |
104 | | int curr_action_line; |
105 | | char *curr_action_file; |
106 | | |
107 | | static int for_each_handler(struct sip_msg *msg, struct action *a); |
108 | | static int route_param_get(struct sip_msg *msg, pv_param_t *ip, |
109 | | pv_value_t *res, void *params, void *extra); |
110 | | |
111 | | |
112 | | /* run actions from a route */ |
113 | | /* returns: 0, or 1 on success, <0 on error */ |
114 | | /* (0 if drop or break encountered, 1 if not ) */ |
115 | | static inline int run_actions(struct action* a, struct sip_msg* msg) |
116 | 0 | { |
117 | 0 | int ret, _, ret_lvl; |
118 | 0 | str top_route; |
119 | |
|
120 | 0 | if (route_stack_size > ROUTE_MAX_REC_LEV) { |
121 | 0 | get_top_route_type(&top_route, &_); |
122 | 0 | LM_ERR("route recursion limit reached, giving up! (nested routes: %d, " |
123 | 0 | "first: '%.*s', last: '%s')!\n", route_stack_size, |
124 | 0 | top_route.len, top_route.s, route_stack[ROUTE_MAX_REC_LEV]); |
125 | 0 | ret=E_UNSPEC; |
126 | 0 | goto error; |
127 | 0 | } |
128 | | |
129 | 0 | if (a==0){ |
130 | 0 | LM_WARN("null action list (route stack size: %d)\n", |
131 | 0 | route_stack_size); |
132 | 0 | ret=1; |
133 | 0 | goto error; |
134 | 0 | } |
135 | | |
136 | 0 | ret_lvl=script_return_push(); |
137 | |
|
138 | 0 | ret=run_action_list(a, msg); |
139 | | |
140 | | /* if 'return', reset the flag */ |
141 | 0 | if(action_flags&ACT_FL_RETURN) |
142 | 0 | action_flags &= ~ACT_FL_RETURN; |
143 | |
|
144 | 0 | script_return_pop(ret_lvl); |
145 | |
|
146 | 0 | return ret; |
147 | | |
148 | 0 | error: |
149 | 0 | return ret; |
150 | 0 | } |
151 | | |
152 | | |
153 | | int _run_actions(struct action *a, struct sip_msg *msg) |
154 | 0 | { |
155 | 0 | return run_actions(a, msg); |
156 | 0 | } |
157 | | |
158 | | |
159 | | int inside_error_route; |
160 | | void run_error_route(struct sip_msg* msg, int init_route_stack) |
161 | 0 | { |
162 | 0 | int old_route; |
163 | |
|
164 | 0 | LM_DBG("triggering\n"); |
165 | 0 | inside_error_route = 1; |
166 | |
|
167 | 0 | if (init_route_stack) { |
168 | 0 | swap_route_type(old_route, ERROR_ROUTE); |
169 | |
|
170 | 0 | route_stack[0] = NULL; |
171 | 0 | route_stack_start = 0; |
172 | 0 | route_stack_size = 1; |
173 | |
|
174 | 0 | run_actions(sroutes->error.a, msg); |
175 | 0 | set_route_type(old_route); |
176 | 0 | } else { |
177 | 0 | route_params_push_level("!error_route", NULL, 0, route_param_get); |
178 | 0 | run_actions(sroutes->error.a, msg); |
179 | 0 | route_params_pop_level(); |
180 | 0 | } |
181 | | |
182 | | /* reset error info */ |
183 | 0 | init_err_info(); |
184 | 0 | inside_error_route = 0; |
185 | 0 | } |
186 | | |
187 | | |
188 | | /* run a list of actions */ |
189 | | int run_action_list(struct action* a, struct sip_msg* msg) |
190 | 0 | { |
191 | 0 | int ret=E_UNSPEC; |
192 | 0 | struct action* t; |
193 | 0 | for (t=a; t!=0; t=t->next){ |
194 | 0 | ret=do_action(t, msg); |
195 | | /* if action returns 0, then stop processing the script */ |
196 | 0 | if(ret==0) |
197 | 0 | action_flags |= ACT_FL_EXIT; |
198 | | |
199 | | /* check for errors */ |
200 | 0 | if (_oser_err_info.eclass!=0 && sroutes->error.a!=NULL && |
201 | 0 | (route_type&(ONREPLY_ROUTE|LOCAL_ROUTE))==0 && !inside_error_route) |
202 | 0 | run_error_route(msg, 0); |
203 | | |
204 | | /* continue or not ? */ |
205 | 0 | if (action_flags & (ACT_FL_RETURN | ACT_FL_EXIT | ACT_FL_BREAK)) |
206 | 0 | break; |
207 | 0 | } |
208 | 0 | return ret; |
209 | 0 | } |
210 | | |
211 | | int run_top_route(struct script_route sr, struct sip_msg* msg) |
212 | 0 | { |
213 | 0 | static int recursing; |
214 | |
|
215 | 0 | int bk_action_flags, route_stack_start_bkp = -1, route_stack_size_bkp; |
216 | 0 | int ret; |
217 | 0 | context_p ctx = NULL; |
218 | |
|
219 | 0 | bk_action_flags = action_flags; |
220 | |
|
221 | 0 | action_flags = 0; |
222 | 0 | init_err_info(); |
223 | |
|
224 | 0 | if (current_processing_ctx==NULL) { |
225 | 0 | if ( (ctx=context_alloc(CONTEXT_GLOBAL))==NULL) { |
226 | 0 | LM_ERR("failed to allocated new global context\n"); |
227 | 0 | return -1; |
228 | 0 | } |
229 | 0 | memset( ctx, 0, context_size(CONTEXT_GLOBAL)); |
230 | 0 | set_global_context(ctx); |
231 | 0 | } |
232 | | |
233 | | /* the recursion support allows run_top_route() to be freely called from |
234 | | * other modules (e.g. dialog) in order to provide contextless script |
235 | | * callbacks using routes, without losing the original route stack */ |
236 | 0 | if (recursing) { |
237 | 0 | route_stack_start_bkp = route_stack_start; |
238 | 0 | route_stack_size_bkp = route_stack_size; |
239 | 0 | route_stack_start = route_stack_size; |
240 | 0 | route_stack_size += 1; |
241 | 0 | } else { |
242 | 0 | route_stack_start = 0; |
243 | 0 | route_stack_size = 1; |
244 | 0 | recursing = 1; |
245 | 0 | } |
246 | |
|
247 | 0 | if (route_type & (ERROR_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE) || |
248 | 0 | (route_type & |
249 | 0 | (REQUEST_ROUTE|ONREPLY_ROUTE) && !strcmp(sr.name, "0"))) |
250 | 0 | route_stack[route_stack_start] = NULL; |
251 | 0 | else |
252 | 0 | route_stack[route_stack_start] = sr.name; |
253 | |
|
254 | 0 | run_actions(sr.a, msg); |
255 | 0 | ret = action_flags; |
256 | |
|
257 | 0 | if (route_stack_start_bkp != -1) { |
258 | 0 | route_stack_size = route_stack_size_bkp; |
259 | 0 | route_stack_start = route_stack_start_bkp; |
260 | 0 | } else { |
261 | 0 | recursing = 0; |
262 | 0 | } |
263 | |
|
264 | 0 | action_flags = bk_action_flags; |
265 | | /* reset script tracing */ |
266 | 0 | use_script_trace = 0; |
267 | |
|
268 | 0 | if (ctx && current_processing_ctx) { |
269 | 0 | context_destroy(CONTEXT_GLOBAL, ctx); |
270 | 0 | context_free(ctx); |
271 | 0 | set_global_context(NULL); |
272 | 0 | } |
273 | |
|
274 | 0 | return ret; |
275 | 0 | } |
276 | | |
277 | | |
278 | | /* execute assignment operation */ |
279 | | int do_assign(struct sip_msg* msg, struct action* a) |
280 | 0 | { |
281 | 0 | str st; |
282 | 0 | int ret; |
283 | 0 | pv_value_t lval, val; |
284 | 0 | pv_spec_p dspec; |
285 | |
|
286 | 0 | dspec = (pv_spec_p)a->elem[0].u.data; |
287 | 0 | if(!pv_is_w(dspec)) |
288 | 0 | { |
289 | 0 | LM_ERR("read only PV in left expression\n"); |
290 | 0 | goto error; |
291 | 0 | } |
292 | | |
293 | 0 | memset(&val, 0, sizeof(pv_value_t)); |
294 | 0 | if(a->elem[1].type != NULLV_ST) |
295 | 0 | { |
296 | 0 | ret = eval_expr((struct expr*)a->elem[1].u.data, msg, &val); |
297 | 0 | if(ret < 0 || !(val.flags & (PV_VAL_STR | PV_VAL_INT | PV_VAL_NULL))) |
298 | 0 | { |
299 | 0 | LM_WARN("no value in right expression at %s:%d\n", |
300 | 0 | a->file, a->line); |
301 | 0 | goto error2; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | 0 | switch (a->type) { |
306 | 0 | case EQ_T: |
307 | 0 | case COLONEQ_T: |
308 | 0 | break; |
309 | 0 | case PLUSEQ_T: |
310 | 0 | case MINUSEQ_T: |
311 | 0 | case DIVEQ_T: |
312 | 0 | case MULTEQ_T: |
313 | 0 | case MODULOEQ_T: |
314 | 0 | case BANDEQ_T: |
315 | 0 | case BOREQ_T: |
316 | 0 | case BXOREQ_T: |
317 | 0 | if (pv_get_spec_value(msg, dspec, &lval) != 0) { |
318 | 0 | LM_ERR("failed to get left-hand side value\n"); |
319 | 0 | goto error; |
320 | 0 | } |
321 | | |
322 | 0 | if (lval.flags & PV_VAL_NULL || val.flags & PV_VAL_NULL) { |
323 | 0 | LM_ERR("NULL value(s) in complex assignment expressions " |
324 | 0 | "(+=, -=, etc.)\n"); |
325 | 0 | goto error; |
326 | 0 | } |
327 | | |
328 | | /* both include STR versions and neither is primarily an INT */ |
329 | 0 | if ((lval.flags & PV_VAL_STR) && (val.flags & PV_VAL_STR) && |
330 | 0 | !(lval.flags & PV_TYPE_INT) && !(val.flags & PV_TYPE_INT)) { |
331 | 0 | val.ri = 0; |
332 | |
|
333 | 0 | if (a->type != PLUSEQ_T) |
334 | 0 | goto bad_operands; |
335 | | |
336 | 0 | if (!(val.flags & PV_VAL_PKG)) { |
337 | 0 | st = val.rs; |
338 | 0 | val.rs.s = pkg_malloc(val.rs.len + lval.rs.len + 1); |
339 | 0 | if (!val.rs.s) { |
340 | 0 | val.rs.s = st.s; |
341 | 0 | LM_ERR("oom 1\n"); |
342 | 0 | goto error; |
343 | 0 | } |
344 | | |
345 | 0 | memcpy(val.rs.s, lval.rs.s, lval.rs.len); |
346 | 0 | memcpy(val.rs.s + lval.rs.len, st.s, st.len); |
347 | 0 | val.rs.len += lval.rs.len; |
348 | 0 | val.rs.s[val.rs.len] = '\0'; |
349 | 0 | val.flags |= PV_VAL_PKG; |
350 | |
|
351 | 0 | if (val.flags & PV_VAL_SHM) { |
352 | 0 | val.flags &= ~PV_VAL_SHM; |
353 | 0 | shm_free(st.s); |
354 | 0 | } |
355 | 0 | } else { |
356 | 0 | st.len = val.rs.len; |
357 | 0 | if (pkg_str_extend(&val.rs, val.rs.len + lval.rs.len + 1) != 0) { |
358 | 0 | LM_ERR("oom 2\n"); |
359 | 0 | goto error; |
360 | 0 | } |
361 | 0 | val.rs.len--; |
362 | 0 | memmove(val.rs.s + lval.rs.len, val.rs.s, st.len); |
363 | 0 | memcpy(val.rs.s, lval.rs.s, lval.rs.len); |
364 | 0 | val.rs.s[val.rs.len] = '\0'; |
365 | 0 | } |
366 | 0 | } else if ((lval.flags & PV_VAL_INT) && (val.flags & PV_VAL_INT)) { |
367 | 0 | if (val.flags & PV_VAL_STR) |
368 | 0 | val.flags &= ~PV_VAL_STR; |
369 | 0 | switch (a->type) { |
370 | 0 | case PLUSEQ_T: |
371 | 0 | val.ri = lval.ri + val.ri; |
372 | 0 | break; |
373 | 0 | case MINUSEQ_T: |
374 | 0 | val.ri = lval.ri - val.ri; |
375 | 0 | break; |
376 | 0 | case DIVEQ_T: |
377 | 0 | val.ri = lval.ri / val.ri; |
378 | 0 | break; |
379 | 0 | case MULTEQ_T: |
380 | 0 | val.ri = lval.ri * val.ri; |
381 | 0 | break; |
382 | 0 | case MODULOEQ_T: |
383 | 0 | val.ri = lval.ri % val.ri; |
384 | 0 | break; |
385 | 0 | case BANDEQ_T: |
386 | 0 | val.ri = lval.ri & val.ri; |
387 | 0 | break; |
388 | 0 | case BOREQ_T: |
389 | 0 | val.ri = lval.ri | val.ri; |
390 | 0 | break; |
391 | 0 | case BXOREQ_T: |
392 | 0 | val.ri = lval.ri ^ val.ri; |
393 | 0 | break; |
394 | 0 | } |
395 | 0 | } else { |
396 | 0 | goto bad_operands; |
397 | 0 | } |
398 | 0 | break; |
399 | 0 | default: |
400 | 0 | LM_ALERT("BUG -> unknown op type %d\n", a->type); |
401 | 0 | goto error; |
402 | 0 | } |
403 | | |
404 | 0 | script_trace("assign", |
405 | 0 | (unsigned char)a->type == EQ_T ? "equal" : |
406 | 0 | (unsigned char)a->type == COLONEQ_T ? "colon-eq" : |
407 | 0 | (unsigned char)a->type == PLUSEQ_T ? "plus-eq" : |
408 | 0 | (unsigned char)a->type == MINUSEQ_T ? "minus-eq" : |
409 | 0 | (unsigned char)a->type == DIVEQ_T ? "div-eq" : |
410 | 0 | (unsigned char)a->type == MULTEQ_T ? "mult-eq" : |
411 | 0 | (unsigned char)a->type == MODULOEQ_T? "modulo-eq" : |
412 | 0 | (unsigned char)a->type == BANDEQ_T ? "b-and-eq" : |
413 | 0 | (unsigned char)a->type == BOREQ_T ? "b-or-eq":"b-xor-eq", |
414 | 0 | msg, a->file, a->line); |
415 | |
|
416 | 0 | if(a->elem[1].type == NULLV_ST || (val.flags & PV_VAL_NULL)) |
417 | 0 | { |
418 | 0 | if(pv_set_value(msg, dspec, (int)a->type, 0)<0) |
419 | 0 | { |
420 | 0 | LM_ERR("setting PV failed\n"); |
421 | 0 | goto error; |
422 | 0 | } |
423 | 0 | } else { |
424 | 0 | if(pv_set_value(msg, dspec, (int)a->type, &val)<0) |
425 | 0 | { |
426 | 0 | LM_ERR("setting PV failed\n"); |
427 | 0 | goto error; |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | 0 | pv_value_destroy(&val); |
432 | 0 | return 1; |
433 | | |
434 | 0 | bad_operands: |
435 | 0 | LM_ERR("unsupported operand type(s) for %s: %s and %s\n", |
436 | 0 | assignop_str(a->type), |
437 | 0 | lval.flags & PV_VAL_STR ? "string" : "int", |
438 | 0 | val.flags & PV_VAL_STR ? "string" : "int"); |
439 | 0 | pv_value_destroy(&val); |
440 | 0 | return -1; |
441 | | |
442 | 0 | error: |
443 | 0 | LM_ERR("error at %s:%d\n", a->file, a->line); |
444 | 0 | error2: |
445 | 0 | pv_value_destroy(&val); |
446 | 0 | return -1; |
447 | 0 | } |
448 | | |
449 | | static pv_value_t *route_params_expand(struct sip_msg *msg, |
450 | | void *params, int params_no) |
451 | 0 | { |
452 | 0 | str tmp; |
453 | 0 | int index; |
454 | 0 | pv_value_t *route_vals, *res; |
455 | 0 | action_elem_p actions = (action_elem_p)params; |
456 | |
|
457 | 0 | route_vals = pkg_malloc(params_no * sizeof(*route_vals)); |
458 | 0 | if (!route_vals) { |
459 | 0 | LM_ERR("oom\n"); |
460 | 0 | return NULL; |
461 | 0 | } |
462 | 0 | memset(route_vals, 0, params_no * sizeof(*route_vals)); |
463 | |
|
464 | 0 | for (index = 0; index < params_no; index++) { |
465 | 0 | res = &route_vals[index]; |
466 | 0 | switch (actions[index].type) |
467 | 0 | { |
468 | 0 | case STRING_ST: |
469 | 0 | res->rs.s = actions[index].u.string; |
470 | 0 | res->rs.len = strlen(res->rs.s); |
471 | 0 | res->flags = PV_VAL_STR; |
472 | 0 | break; |
473 | | |
474 | 0 | case NUMBER_ST: |
475 | 0 | res->ri = actions[index].u.number; |
476 | 0 | res->flags = PV_VAL_INT|PV_TYPE_INT; |
477 | 0 | tmp.s = sint2str(res->ri, &tmp.len); |
478 | 0 | if (pkg_str_dup(&res->rs, &tmp) == 0) |
479 | 0 | res->flags |= PV_VAL_STR|PV_VAL_PKG; |
480 | 0 | else |
481 | 0 | LM_ERR("cannot duplicate param value\n"); |
482 | 0 | break; |
483 | | |
484 | 0 | case SCRIPTVAR_ST: |
485 | 0 | if(pv_get_spec_value(msg, (pv_spec_p)actions[index].u.data, res)==0) |
486 | 0 | { |
487 | 0 | if (pvv_is_str(res)) { |
488 | | /* but we need to duplicate the string */ |
489 | 0 | if (pkg_str_dup(&tmp, &res->rs) == 0) { |
490 | 0 | res->rs.s = tmp.s; |
491 | 0 | res->flags |= PV_VAL_PKG; |
492 | 0 | break; |
493 | 0 | } else { |
494 | 0 | LM_ERR("cannot duplicate param value\n"); |
495 | 0 | } |
496 | 0 | } else { |
497 | 0 | break; |
498 | 0 | } |
499 | 0 | } else { |
500 | 0 | LM_ERR("cannot get spec value\n"); |
501 | 0 | } |
502 | | /* fallback */ |
503 | | |
504 | 0 | default: |
505 | 0 | LM_ALERT("BUG: invalid parameter type %d\n", |
506 | 0 | actions[index].type); |
507 | | /* fallback */ |
508 | 0 | case NULLV_ST: |
509 | 0 | res->rs.s = NULL; |
510 | 0 | res->rs.len = res->ri = 0; |
511 | 0 | res->flags = PV_VAL_NULL; |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | } |
515 | 0 | return route_vals; |
516 | 0 | } |
517 | | |
518 | | static void route_params_release(pv_value_t *params, int params_no) |
519 | 0 | { |
520 | 0 | int p; |
521 | 0 | for (p = 0; p < params_no; p++) { |
522 | 0 | if (params[p].flags & PV_VAL_PKG) |
523 | 0 | pkg_free(params[p].rs.s); |
524 | 0 | } |
525 | 0 | pkg_free(params); |
526 | 0 | } |
527 | | |
528 | | |
529 | | /* function used to get parameter from a route scope */ |
530 | | static int route_param_get(struct sip_msg *msg, pv_param_t *ip, |
531 | | pv_value_t *res, void *_params, void *_extra) |
532 | 0 | { |
533 | 0 | int index; |
534 | 0 | pv_value_t tv; |
535 | 0 | pv_value_t *params = (pv_value_t *)_params; |
536 | 0 | int params_no = (int)(unsigned long)_extra; |
537 | |
|
538 | 0 | if (params_no <= 0) { |
539 | 0 | LM_DBG("route without parameters\n"); |
540 | 0 | return pv_get_null(msg, ip, res); |
541 | 0 | } |
542 | | |
543 | 0 | if(ip->pvn.type==PV_NAME_INTSTR) |
544 | 0 | { |
545 | 0 | if (ip->pvn.u.isname.type != 0) |
546 | 0 | { |
547 | 0 | LM_ERR("route $param variable accepts only integer indexes\n"); |
548 | 0 | return -1; |
549 | 0 | } |
550 | 0 | index = ip->pvn.u.isname.name.n; |
551 | 0 | } else |
552 | 0 | { |
553 | | /* pvar -> it might be another $param variable! */ |
554 | 0 | route_rec_level--; |
555 | 0 | if(pv_get_spec_value(msg, (pv_spec_p)(ip->pvn.u.dname), &tv)!=0) |
556 | 0 | { |
557 | 0 | LM_ERR("cannot get spec value\n"); |
558 | 0 | route_rec_level++; |
559 | 0 | return -1; |
560 | 0 | } |
561 | 0 | route_rec_level++; |
562 | |
|
563 | 0 | if(tv.flags&PV_VAL_NULL || tv.flags&PV_VAL_EMPTY) |
564 | 0 | { |
565 | 0 | LM_ERR("null or empty name\n"); |
566 | 0 | return -1; |
567 | 0 | } |
568 | 0 | if (!(tv.flags&PV_VAL_INT) || str2int(&tv.rs,(unsigned int*)&index) < 0) |
569 | 0 | { |
570 | 0 | LM_ERR("invalid index <%.*s>\n", tv.rs.len, tv.rs.s); |
571 | 0 | return -1; |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | 0 | if (!params) |
576 | 0 | { |
577 | 0 | LM_DBG("no parameter specified for this route\n"); |
578 | 0 | return pv_get_null(msg, ip, res); |
579 | 0 | } |
580 | | |
581 | 0 | if (index < 1 || index > params_no) |
582 | 0 | { |
583 | 0 | LM_DBG("no such parameter index %d\n", index); |
584 | 0 | return pv_get_null(msg, ip, res); |
585 | 0 | } |
586 | | |
587 | | /* the parameters start at 0, whereas the index starts from 1 */ |
588 | 0 | index--; |
589 | 0 | *res = params[index]; |
590 | 0 | res->flags &= ~PV_VAL_PKG; /* not interested in this flag */ |
591 | |
|
592 | 0 | return 0; |
593 | 0 | } |
594 | | |
595 | | #define should_skip_updating(action_type) \ |
596 | 0 | (action_type == IF_T || action_type == ROUTE_T || \ |
597 | 0 | action_type == WHILE_T || action_type == FOR_EACH_T) |
598 | | |
599 | 0 | #define update_longest_action(a) do { \ |
600 | 0 | if (execmsgthreshold && !should_skip_updating((unsigned char)(a)->type)) { \ |
601 | 0 | end_time = get_time_diff(&start); \ |
602 | 0 | if (end_time > min_action_time) { \ |
603 | 0 | for (i=0;i<LONGEST_ACTION_SIZE;i++) { \ |
604 | 0 | if (longest_action[i].a_time < end_time) { \ |
605 | 0 | memmove(longest_action+i+1,longest_action+i, \ |
606 | 0 | (LONGEST_ACTION_SIZE-i-1)*sizeof(action_time)); \ |
607 | 0 | longest_action[i].a_time=end_time; \ |
608 | 0 | longest_action[i].a = a; \ |
609 | 0 | min_action_time = longest_action[LONGEST_ACTION_SIZE-1].a_time; \ |
610 | 0 | break; \ |
611 | 0 | } \ |
612 | 0 | } \ |
613 | 0 | } \ |
614 | 0 | } \ |
615 | 0 | } while(0) |
616 | | |
617 | | /* ret= 0! if action -> end of list(e.g DROP), |
618 | | > 0 to continue processing next actions |
619 | | and <0 on error */ |
620 | | int do_action(struct action* a, struct sip_msg* msg) |
621 | 0 | { |
622 | 0 | int ret; |
623 | 0 | int v; |
624 | 0 | int i; |
625 | 0 | int len; |
626 | 0 | int cmatch; |
627 | 0 | struct action *aitem; |
628 | 0 | struct action *adefault; |
629 | 0 | pv_spec_t *spec; |
630 | 0 | pv_value_t val; |
631 | 0 | struct timeval start; |
632 | 0 | int end_time; |
633 | 0 | const cmd_export_t *cmd = NULL; |
634 | 0 | const acmd_export_t *acmd; |
635 | 0 | void* cmdp[MAX_CMD_PARAMS]; |
636 | 0 | pv_value_t tmp_vals[MAX_CMD_PARAMS]; |
637 | 0 | pv_value_t *route_p; |
638 | 0 | str sval; |
639 | | |
640 | | /* reset the value of error to E_UNSPEC so avoid unknowledgable |
641 | | functions to return with error (status<0) and not setting it |
642 | | leaving there previous error; cache the previous value though |
643 | | for functions which want to process it */ |
644 | 0 | prev_ser_error=ser_error; |
645 | 0 | ser_error=E_UNSPEC; |
646 | |
|
647 | 0 | start_expire_timer(start,execmsgthreshold); |
648 | |
|
649 | 0 | curr_action_line = a->line; |
650 | 0 | curr_action_file = a->file; |
651 | |
|
652 | 0 | ret=E_BUG; |
653 | 0 | switch ((unsigned char)a->type){ |
654 | 0 | case ASSERT_T: |
655 | 0 | if (enable_asserts) { |
656 | | /* if null expr => ignore if? */ |
657 | 0 | if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ |
658 | 0 | v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); |
659 | |
|
660 | 0 | ret=1; /*default is continue */ |
661 | |
|
662 | 0 | if (v<=0) { |
663 | 0 | ret=0; |
664 | |
|
665 | 0 | if (a->elem[1].u.string) |
666 | 0 | LM_CRIT("ASSERTION FAILED (%s) at %s:%d\n", |
667 | 0 | a->elem[1].u.string, a->file, a->line); |
668 | 0 | else |
669 | 0 | LM_CRIT("ASSERTION FAILED at %s:%d\n", |
670 | 0 | a->file, a->line); |
671 | |
|
672 | 0 | if (abort_on_assert) { |
673 | 0 | abort(); |
674 | 0 | } else { |
675 | 0 | set_err_info(OSER_EC_ASSERT, OSER_EL_CRITIC, "assertion failed"); |
676 | 0 | set_err_reply(500, "server error"); |
677 | |
|
678 | 0 | run_error_route(msg, 0); |
679 | 0 | } |
680 | 0 | } |
681 | 0 | } |
682 | 0 | } |
683 | 0 | break; |
684 | 0 | case DROP_T: |
685 | 0 | script_trace("core", "drop", msg, a->file, a->line) ; |
686 | 0 | action_flags |= ACT_FL_DROP|ACT_FL_EXIT; |
687 | 0 | break; |
688 | 0 | case EXIT_T: |
689 | 0 | script_trace("core", "exit", msg, a->file, a->line) ; |
690 | 0 | ret=0; |
691 | 0 | action_flags |= ACT_FL_EXIT; |
692 | 0 | break; |
693 | 0 | case RETURN_T: |
694 | 0 | if (a->elem[1].type == EXPR_ST) |
695 | 0 | script_return_set(msg, a->elem[1].u.data); |
696 | 0 | else |
697 | 0 | script_return_set(msg, NULL); |
698 | 0 | script_trace("core", "return", msg, a->file, a->line) ; |
699 | 0 | if (a->elem[0].type == SCRIPTVAR_ST) |
700 | 0 | { |
701 | 0 | spec = (pv_spec_t*)a->elem[0].u.data; |
702 | 0 | if(pv_get_spec_value(msg, spec, &val)!=0 |
703 | 0 | || (val.flags&PV_VAL_NULL)) |
704 | 0 | { |
705 | 0 | ret=-1; |
706 | 0 | } else { |
707 | 0 | if(!(val.flags&PV_VAL_INT)) |
708 | 0 | ret = 1; |
709 | 0 | else |
710 | 0 | ret = val.ri; |
711 | 0 | } |
712 | 0 | pv_value_destroy(&val); |
713 | 0 | } else { |
714 | 0 | ret=a->elem[0].u.number; |
715 | 0 | } |
716 | 0 | action_flags |= ACT_FL_RETURN; |
717 | 0 | break; |
718 | 0 | case LOG_T: |
719 | 0 | script_trace("core", "log", msg, a->file, a->line) ; |
720 | 0 | if ((a->elem[0].type!=NUMBER_ST)|(a->elem[1].type!=STRING_ST)){ |
721 | 0 | LM_ALERT("BUG in log() types %d, %d\n", |
722 | 0 | a->elem[0].type, a->elem[1].type); |
723 | 0 | ret=E_BUG; |
724 | 0 | break; |
725 | 0 | } |
726 | 0 | LM_GEN1(a->elem[0].u.number, "%s", a->elem[1].u.string); |
727 | 0 | ret=1; |
728 | 0 | break; |
729 | 0 | case LEN_GT_T: |
730 | 0 | script_trace("core", "len_gt", msg, a->file, a->line) ; |
731 | 0 | if (a->elem[0].type!=NUMBER_ST) { |
732 | 0 | LM_ALERT("BUG in len_gt type %d\n", |
733 | 0 | a->elem[0].type ); |
734 | 0 | ret=E_BUG; |
735 | 0 | break; |
736 | 0 | } |
737 | 0 | ret = (msg->len >= (unsigned int)a->elem[0].u.number) ? 1 : -1; |
738 | 0 | break; |
739 | 0 | case ERROR_T: |
740 | 0 | script_trace("core", "error", msg, a->file, a->line) ; |
741 | 0 | if ((a->elem[0].type!=STRING_ST)|(a->elem[1].type!=STRING_ST)){ |
742 | 0 | LM_ALERT("BUG in error() types %d, %d\n", |
743 | 0 | a->elem[0].type, a->elem[1].type); |
744 | 0 | ret=E_BUG; |
745 | 0 | break; |
746 | 0 | } |
747 | 0 | LM_ERR("error(\"%s\", \"%s\") not implemented yet\n", |
748 | 0 | a->elem[0].u.string, a->elem[1].u.string); |
749 | 0 | ret=1; |
750 | 0 | break; |
751 | 0 | case ROUTE_T: |
752 | 0 | init_str(&sval, "unknown"); |
753 | 0 | switch (a->elem[0].type) { |
754 | 0 | case NUMBER_ST: |
755 | 0 | i = a->elem[0].u.number; |
756 | 0 | break; |
757 | 0 | case SCRIPTVAR_ST: |
758 | 0 | if (pv_get_spec_value(msg, a->elem[0].u.item, &val) < 0) { |
759 | 0 | LM_ERR("cannot print route name!\n"); |
760 | 0 | i = -1; |
761 | 0 | break; |
762 | 0 | } |
763 | 0 | if (val.flags & PV_VAL_INT) |
764 | 0 | sval.s = int2str(val.ri, &sval.len); |
765 | 0 | else |
766 | 0 | sval = val.rs; |
767 | 0 | i = get_script_route_ID_by_name_str(&sval, sroutes->request, RT_NO); |
768 | 0 | break; |
769 | 0 | case SCRIPTVAR_ELEM_ST: |
770 | 0 | if (pv_printf_s(msg, a->elem[0].u.data, &sval) < 0) { |
771 | 0 | LM_ERR("cannot print route name!\n"); |
772 | 0 | i = -1; |
773 | 0 | break; |
774 | 0 | } |
775 | 0 | i = get_script_route_ID_by_name_str(&sval, sroutes->request, RT_NO); |
776 | 0 | break; |
777 | 0 | default: |
778 | 0 | i = -1; |
779 | 0 | break; |
780 | 0 | } |
781 | 0 | if (i == -1) { |
782 | 0 | LM_ALERT("unknown route(%.*s) (type %d)\n", sval.len, sval.s, |
783 | 0 | a->elem[0].type); |
784 | 0 | ret=E_BUG; |
785 | 0 | break; |
786 | 0 | } |
787 | 0 | if ((i>=RT_NO)||(i<0)){ |
788 | 0 | LM_BUG("invalid routing table number in route(%u)\n", i); |
789 | 0 | ret=E_CFG; |
790 | 0 | break; |
791 | 0 | } |
792 | 0 | script_trace("route", sroutes->request[i].name, |
793 | 0 | msg, a->file, a->line) ; |
794 | | /* check if the route has parameters */ |
795 | 0 | if (a->elem[1].type != 0) { |
796 | 0 | if (a->elem[1].type != NUMBER_ST || a->elem[2].type != SCRIPTVAR_ST) { |
797 | 0 | LM_ALERT("BUG in route() type %d/%d\n", |
798 | 0 | a->elem[1].type, a->elem[2].type); |
799 | 0 | ret=E_BUG; |
800 | 0 | break; |
801 | 0 | } |
802 | 0 | len = a->elem[1].u.number; |
803 | 0 | route_p = route_params_expand(msg, a->elem[2].u.data, len); |
804 | 0 | if (!route_p) { |
805 | 0 | LM_ERR("could not expand route params!\n"); |
806 | 0 | ret=E_OUT_OF_MEM; |
807 | 0 | break; |
808 | 0 | } |
809 | 0 | route_params_push_level(sroutes->request[i].name, |
810 | 0 | route_p, (void *)(unsigned long)len, route_param_get); |
811 | 0 | return_code=run_actions(sroutes->request[i].a, msg); |
812 | 0 | route_params_release(route_p, len); |
813 | 0 | route_params_pop_level(); |
814 | 0 | } else { |
815 | 0 | route_params_push_level(sroutes->request[i].name, NULL, 0, route_param_get); |
816 | 0 | return_code=run_actions(sroutes->request[i].a, msg); |
817 | 0 | route_params_pop_level(); |
818 | 0 | } |
819 | 0 | ret=return_code; |
820 | 0 | break; |
821 | 0 | case IF_T: |
822 | 0 | script_trace("core", "if", msg, a->file, a->line) ; |
823 | | /* if null expr => ignore if? */ |
824 | 0 | if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ |
825 | 0 | v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); |
826 | | /* set return code to expr value */ |
827 | 0 | if (v<0 || (action_flags&ACT_FL_RETURN) |
828 | 0 | || (action_flags&ACT_FL_EXIT) ){ |
829 | 0 | if (v==EXPR_DROP || (action_flags&ACT_FL_RETURN) |
830 | 0 | || (action_flags&ACT_FL_EXIT) ){ /* hack to quit on DROP*/ |
831 | 0 | ret=0; |
832 | 0 | return_code = 0; |
833 | 0 | break; |
834 | 0 | }else{ |
835 | 0 | LM_WARN("error in expression at %s:%d\n", |
836 | 0 | a->file, a->line); |
837 | 0 | } |
838 | 0 | } |
839 | | |
840 | 0 | ret=1; /*default is continue */ |
841 | 0 | if (v>0) { |
842 | 0 | if ((a->elem[1].type==ACTIONS_ST)&&a->elem[1].u.data){ |
843 | 0 | ret=run_action_list( |
844 | 0 | (struct action*)a->elem[1].u.data,msg ); |
845 | 0 | return_code = ret; |
846 | 0 | } else return_code = v; |
847 | 0 | }else{ |
848 | 0 | if ((a->elem[2].type==ACTIONS_ST)&&a->elem[2].u.data){ |
849 | 0 | ret=run_action_list( |
850 | 0 | (struct action*)a->elem[2].u.data,msg); |
851 | 0 | return_code = ret; |
852 | 0 | } else return_code = v; |
853 | 0 | } |
854 | 0 | } |
855 | 0 | break; |
856 | 0 | case WHILE_T: |
857 | 0 | script_trace("core", "while", msg, a->file, a->line) ; |
858 | | /* if null expr => ignore if? */ |
859 | 0 | if ((a->elem[0].type==EXPR_ST)&&a->elem[0].u.data){ |
860 | 0 | len = 0; |
861 | 0 | while(1) |
862 | 0 | { |
863 | 0 | if(len++ >= max_while_loops) |
864 | 0 | { |
865 | 0 | LM_ERR("max while loops reached (%d), increase the " |
866 | 0 | "'max_while_loops' global!\n", max_while_loops); |
867 | 0 | break; |
868 | 0 | } |
869 | 0 | v=eval_expr((struct expr*)a->elem[0].u.data, msg, 0); |
870 | | /* set return code to expr value */ |
871 | 0 | if (v<0 || (action_flags&ACT_FL_RETURN) |
872 | 0 | || (action_flags&ACT_FL_EXIT) ){ |
873 | 0 | if (v==EXPR_DROP || (action_flags&ACT_FL_RETURN) |
874 | 0 | || (action_flags&ACT_FL_EXIT) ){ |
875 | 0 | ret=0; |
876 | 0 | return_code = 0; |
877 | 0 | break; |
878 | 0 | }else{ |
879 | 0 | LM_WARN("error in expression at %s:%d\n", |
880 | 0 | a->file, a->line); |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | 0 | ret=1; /*default is continue */ |
885 | 0 | if (v>0) { |
886 | 0 | if ((a->elem[1].type==ACTIONS_ST) |
887 | 0 | &&a->elem[1].u.data){ |
888 | 0 | ret=run_action_list( |
889 | 0 | (struct action*)a->elem[1].u.data,msg ); |
890 | | /* check if return was done */ |
891 | 0 | if (action_flags & |
892 | 0 | (ACT_FL_RETURN|ACT_FL_EXIT|ACT_FL_BREAK)) { |
893 | 0 | action_flags &= ~ACT_FL_BREAK; |
894 | 0 | break; |
895 | 0 | } |
896 | 0 | return_code = ret; |
897 | 0 | } else { |
898 | | /* we should not get here */ |
899 | 0 | return_code = v; |
900 | 0 | break; |
901 | 0 | } |
902 | 0 | } else { |
903 | | /* condition was false */ |
904 | 0 | return_code = v; |
905 | 0 | break; |
906 | 0 | } |
907 | 0 | } |
908 | 0 | } |
909 | 0 | break; |
910 | 0 | case BREAK_T: |
911 | 0 | script_trace("core", "break", msg, a->file, a->line) ; |
912 | 0 | action_flags |= ACT_FL_BREAK; |
913 | 0 | break; |
914 | 0 | case FOR_EACH_T: |
915 | 0 | script_trace("core", "for-each", msg, a->file, a->line) ; |
916 | 0 | ret = for_each_handler(msg, a); |
917 | 0 | break; |
918 | 0 | case XDBG_T: |
919 | 0 | script_trace("core", "xdbg", msg, a->file, a->line) ; |
920 | 0 | if (a->elem[0].type == SCRIPTVAR_ELEM_ST) |
921 | 0 | { |
922 | 0 | ret = xdbg(msg, a->elem[0].u.data); |
923 | 0 | if (ret < 0) |
924 | 0 | { |
925 | 0 | LM_ERR("error while printing xdbg message\n"); |
926 | 0 | break; |
927 | 0 | } |
928 | 0 | } |
929 | 0 | else |
930 | 0 | { |
931 | 0 | LM_ALERT("BUG in xdbg() type %d\n", a->elem[0].type); |
932 | 0 | ret=E_BUG; |
933 | 0 | } |
934 | 0 | break; |
935 | 0 | case XLOG_T: |
936 | 0 | script_trace("core", "xlog", msg, a->file, a->line) ; |
937 | 0 | if (a->elem[1].u.data != NULL) |
938 | 0 | { |
939 | 0 | if (a->elem[1].type != SCRIPTVAR_ELEM_ST) |
940 | 0 | { |
941 | 0 | LM_ALERT("BUG in xlog() type %d\n", a->elem[1].type); |
942 | 0 | ret=E_BUG; |
943 | 0 | break; |
944 | 0 | } |
945 | 0 | if (a->elem[0].type != STR_ST) |
946 | 0 | { |
947 | 0 | LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); |
948 | 0 | ret=E_BUG; |
949 | 0 | break; |
950 | 0 | } |
951 | 0 | ret = xlog_2(msg,a->elem[0].u.data, a->elem[1].u.data); |
952 | 0 | if (ret < 0) |
953 | 0 | { |
954 | 0 | LM_ERR("error while printing xlog message\n"); |
955 | 0 | break; |
956 | 0 | } |
957 | 0 | } |
958 | 0 | else |
959 | 0 | { |
960 | 0 | if (a->elem[0].type != SCRIPTVAR_ELEM_ST) |
961 | 0 | { |
962 | 0 | LM_ALERT("BUG in xlog() type %d\n", a->elem[0].type); |
963 | 0 | ret=E_BUG; |
964 | 0 | break; |
965 | 0 | } |
966 | 0 | ret = xlog_1(msg,a->elem[0].u.data); |
967 | 0 | if (ret < 0) |
968 | 0 | { |
969 | 0 | LM_ERR("error while printing xlog message\n"); |
970 | 0 | break; |
971 | 0 | } |
972 | 0 | } |
973 | | |
974 | 0 | break; |
975 | 0 | case SWITCH_T: |
976 | 0 | script_trace("core", "switch", msg, a->file, a->line) ; |
977 | | #ifdef EXTRA_DEBUG |
978 | | if (a->elem[0].type!=SCRIPTVAR_ST || a->elem[1].type!=ACTIONS_ST) { |
979 | | LM_ALERT("BUG in switch() type %d\n", |
980 | | a->elem[0].type); |
981 | | ret=E_BUG; |
982 | | break; |
983 | | } |
984 | | #endif |
985 | 0 | spec = (pv_spec_t*)a->elem[0].u.data; |
986 | 0 | if(pv_get_spec_value(msg, spec, &val)!=0) |
987 | 0 | { |
988 | 0 | LM_ALERT("BUG - no value in switch()\n"); |
989 | 0 | ret=E_BUG; |
990 | 0 | break; |
991 | 0 | } |
992 | | |
993 | 0 | return_code=1; |
994 | 0 | adefault = NULL; |
995 | 0 | aitem = (struct action*)a->elem[1].u.data; |
996 | 0 | cmatch=0; |
997 | 0 | while(aitem) |
998 | 0 | { |
999 | 0 | if((unsigned char)aitem->type==DEFAULT_T) |
1000 | 0 | adefault=aitem; |
1001 | 0 | if(cmatch==0) |
1002 | 0 | { |
1003 | 0 | if(aitem->elem[0].type==STR_ST) |
1004 | 0 | { |
1005 | 0 | if(val.flags&PV_VAL_STR |
1006 | 0 | && val.rs.len==aitem->elem[0].u.s.len |
1007 | 0 | && strncasecmp(val.rs.s, aitem->elem[0].u.s.s, |
1008 | 0 | val.rs.len)==0) |
1009 | 0 | cmatch = 1; |
1010 | 0 | } else { /* number */ |
1011 | 0 | if(val.flags&PV_VAL_INT && |
1012 | 0 | val.ri==aitem->elem[0].u.number) |
1013 | 0 | cmatch = 1; |
1014 | 0 | } |
1015 | 0 | } |
1016 | 0 | if(cmatch==1) |
1017 | 0 | { |
1018 | 0 | if(aitem->elem[1].u.data) |
1019 | 0 | { |
1020 | 0 | return_code=run_action_list( |
1021 | 0 | (struct action*)aitem->elem[1].u.data, msg); |
1022 | 0 | if (action_flags & |
1023 | 0 | (ACT_FL_RETURN | ACT_FL_EXIT | ACT_FL_BREAK)) { |
1024 | 0 | action_flags &= ~ACT_FL_BREAK; |
1025 | 0 | break; |
1026 | 0 | } |
1027 | 0 | } |
1028 | | |
1029 | 0 | if (!aitem->next) |
1030 | 0 | cmatch = 0; |
1031 | 0 | } |
1032 | 0 | aitem = aitem->next; |
1033 | 0 | } |
1034 | 0 | if((cmatch==0) && (adefault!=NULL)) |
1035 | 0 | { |
1036 | 0 | LM_DBG("switch: running default statement\n"); |
1037 | 0 | if(adefault->elem[0].u.data) |
1038 | 0 | return_code=run_action_list( |
1039 | 0 | (struct action*)adefault->elem[0].u.data, msg); |
1040 | 0 | if (action_flags & ACT_FL_BREAK) |
1041 | 0 | action_flags &= ~ACT_FL_BREAK; |
1042 | 0 | } |
1043 | 0 | ret=return_code; |
1044 | 0 | break; |
1045 | 0 | case CMD_T: |
1046 | 0 | if (a->elem[0].type != CMD_ST || |
1047 | 0 | ((cmd = (const cmd_export_t*)a->elem[0].u.data_const) == NULL)) { |
1048 | 0 | LM_ALERT("BUG in module call\n"); |
1049 | 0 | break; |
1050 | 0 | } |
1051 | | |
1052 | 0 | script_trace("module", cmd->name, msg, a->file, a->line); |
1053 | |
|
1054 | 0 | if ((ret = get_cmd_fixups(msg, cmd->params, a->elem, cmdp, |
1055 | 0 | tmp_vals)) < 0) { |
1056 | 0 | LM_ERR("Failed to get fixups for command <%s> in %s, line %d\n", |
1057 | 0 | cmd->name, a->file, a->line); |
1058 | 0 | break; |
1059 | 0 | } |
1060 | | |
1061 | 0 | ret = cmd->function(msg, |
1062 | 0 | cmdp[0],cmdp[1],cmdp[2], |
1063 | 0 | cmdp[3],cmdp[4],cmdp[5], |
1064 | 0 | cmdp[6],cmdp[7]); |
1065 | |
|
1066 | 0 | if (free_cmd_fixups(cmd->params, a->elem, cmdp) < 0) { |
1067 | 0 | LM_ERR("Failed to free fixups for command <%s> in %s, line %d\n", |
1068 | 0 | cmd->name, a->file, a->line); |
1069 | 0 | break; |
1070 | 0 | } |
1071 | | |
1072 | 0 | break; |
1073 | 0 | case ASYNC_T: |
1074 | | /* first param - an ACTIONS_ST containing an ACMD_ST |
1075 | | * second param - a ROUTE_REF_ST pointing to resume route |
1076 | | * third param - an optional NUMBER_ST with a timeout */ |
1077 | 0 | aitem = (struct action *)(a->elem[0].u.data); |
1078 | 0 | acmd = (const acmd_export_t *)aitem->elem[0].u.data_const; |
1079 | |
|
1080 | 0 | if (async_script_start_f==NULL || a->elem[0].type!=ACTIONS_ST || |
1081 | 0 | a->elem[1].type!=ROUTE_REF_ST || aitem->type!=AMODULE_T) { |
1082 | 0 | LM_ALERT("BUG in async expression " |
1083 | 0 | "(is the 'tm' module loaded?)\n"); |
1084 | 0 | } else { |
1085 | 0 | script_trace("async", acmd->name, msg, a->file, a->line); |
1086 | |
|
1087 | 0 | if ((ret = get_cmd_fixups(msg, acmd->params, aitem->elem, cmdp, |
1088 | 0 | tmp_vals)) < 0) { |
1089 | 0 | LM_ERR("Failed to get fixups for async command <%s> in %s," |
1090 | 0 | " line %d\n", acmd->name, a->file, a->line); |
1091 | 0 | break; |
1092 | 0 | } |
1093 | | |
1094 | 0 | ret = async_script_start_f(msg, aitem, |
1095 | 0 | (struct script_route_ref*)a->elem[1].u.data, |
1096 | 0 | (unsigned int)a->elem[2].u.number, cmdp); |
1097 | 0 | if (ret>=0) |
1098 | 0 | action_flags |= ACT_FL_TBCONT; |
1099 | |
|
1100 | 0 | if (free_cmd_fixups(acmd->params, aitem->elem, cmdp) < 0) { |
1101 | 0 | LM_ERR("Failed to free fixups for async command <%s> in %s," |
1102 | 0 | " line %d\n", acmd->name, a->file, a->line); |
1103 | 0 | break; |
1104 | 0 | } |
1105 | 0 | } |
1106 | 0 | ret = 0; |
1107 | 0 | break; |
1108 | 0 | case LAUNCH_T: |
1109 | | /* first param - an ACTIONS_ST containing an ACMD_ST |
1110 | | * second param - an optional ROUTE_REF_ST pointing to an end route */ |
1111 | 0 | aitem = (struct action *)(a->elem[0].u.data); |
1112 | 0 | acmd = (const acmd_export_t *)aitem->elem[0].u.data_const; |
1113 | |
|
1114 | 0 | if (async_script_start_f==NULL || a->elem[0].type!=ACTIONS_ST || |
1115 | 0 | a->elem[1].type!=ROUTE_REF_ST || aitem->type!=AMODULE_T) { |
1116 | 0 | LM_ALERT("BUG in launch expression\n"); |
1117 | 0 | } else { |
1118 | 0 | script_trace("launch", acmd->name, msg, a->file, a->line); |
1119 | | /* NOTE that the routeID (a->elem[1].u.data) is set to |
1120 | | * NULL if no reporting route is set */ |
1121 | |
|
1122 | 0 | if ((ret = get_cmd_fixups(msg, acmd->params, aitem->elem, |
1123 | 0 | cmdp, tmp_vals)) < 0) { |
1124 | 0 | LM_ERR("Failed to get fixups for launch command <%s> in %s," |
1125 | 0 | " line %d\n", acmd->name, a->file, a->line); |
1126 | 0 | break; |
1127 | 0 | } |
1128 | | |
1129 | 0 | if (a->elem[2].type==SCRIPTVAR_ELEM_ST && a->elem[2].u.data) { |
1130 | 0 | if (pv_printf_s(msg, a->elem[2].u.data, &sval) < 0) { |
1131 | 0 | LM_ERR("cannot print resume route parameter!\n"); |
1132 | 0 | break; |
1133 | 0 | } |
1134 | | |
1135 | 0 | ret = async_script_launch( msg, aitem, |
1136 | 0 | (struct script_route_ref*)a->elem[1].u.data, |
1137 | 0 | &sval, cmdp); |
1138 | 0 | } else { |
1139 | 0 | ret = async_script_launch( msg, aitem, |
1140 | 0 | (struct script_route_ref*)a->elem[1].u.data, |
1141 | 0 | NULL, cmdp); |
1142 | 0 | } |
1143 | | |
1144 | 0 | if (free_cmd_fixups(acmd->params, aitem->elem, cmdp) < 0) { |
1145 | 0 | LM_ERR("Failed to free fixups for launch command <%s> in %s," |
1146 | 0 | " line %d\n", acmd->name, a->file, a->line); |
1147 | 0 | break; |
1148 | 0 | } |
1149 | 0 | } |
1150 | 0 | break; |
1151 | 0 | case EQ_T: |
1152 | 0 | case COLONEQ_T: |
1153 | 0 | case PLUSEQ_T: |
1154 | 0 | case MINUSEQ_T: |
1155 | 0 | case DIVEQ_T: |
1156 | 0 | case MULTEQ_T: |
1157 | 0 | case MODULOEQ_T: |
1158 | 0 | case BANDEQ_T: |
1159 | 0 | case BOREQ_T: |
1160 | 0 | case BXOREQ_T: |
1161 | 0 | ret = do_assign(msg, a); |
1162 | 0 | break; |
1163 | 0 | default: |
1164 | 0 | LM_ALERT("BUG - unknown type %d\n", a->type); |
1165 | 0 | goto error; |
1166 | 0 | } |
1167 | | |
1168 | 0 | if((unsigned char)a->type!=IF_T && (unsigned char)a->type!=ROUTE_T) |
1169 | 0 | return_code = ret; |
1170 | | /*skip:*/ |
1171 | |
|
1172 | 0 | update_longest_action(a); |
1173 | 0 | return ret; |
1174 | | |
1175 | 0 | error: |
1176 | 0 | LM_ERR("error in %s:%d\n", a->file, a->line); |
1177 | 0 | update_longest_action(a); |
1178 | 0 | return ret; |
1179 | 0 | } |
1180 | | |
1181 | | static int for_each_handler(struct sip_msg *msg, struct action *a) |
1182 | 0 | { |
1183 | 0 | struct sip_msg *msg_src = msg; |
1184 | 0 | pv_spec_p iter, spec; |
1185 | 0 | pv_param_t pvp; |
1186 | 0 | pv_value_t val; |
1187 | 0 | int ret = 1; |
1188 | 0 | int op = 0; |
1189 | |
|
1190 | 0 | if (a->elem[2].type == ACTIONS_ST && a->elem[2].u.data) { |
1191 | 0 | iter = a->elem[0].u.data; |
1192 | 0 | spec = a->elem[1].u.data; |
1193 | | |
1194 | | /* |
1195 | | * simple is always better. |
1196 | | * just don't allow fancy for-each statements |
1197 | | */ |
1198 | 0 | if (spec->pvp.pvi.type != PV_IDX_ALL) { |
1199 | 0 | LM_ERR("for-each must be used on a \"[*]\" index! skipping!\n"); |
1200 | 0 | return E_SCRIPT; |
1201 | 0 | } |
1202 | | |
1203 | 0 | memset(&pvp, 0, sizeof pvp); |
1204 | 0 | pvp.pvi.type = PV_IDX_INT; |
1205 | 0 | pvp.pvn = spec->pvp.pvn; |
1206 | 0 | if (spec->pvc && spec->pvc->contextf) { |
1207 | 0 | msg_src = spec->pvc->contextf(msg); |
1208 | 0 | if (!msg_src || msg_src == FAKED_REPLY) { |
1209 | 0 | LM_BUG("Invalid pv context message: %p\n", msg_src); |
1210 | 0 | return E_BUG; |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | | /* |
1215 | | * for $json iterators, better to assume script writer |
1216 | | * wants data to be interpreted, rather than not |
1217 | | * (i.e. ":=" script operator, and not simply "=") |
1218 | | */ |
1219 | 0 | if (pv_type(iter->type) == PVT_JSON) |
1220 | 0 | op = COLONEQ_T; |
1221 | |
|
1222 | 0 | for (;;) { |
1223 | 0 | if (spec->getf(msg_src, &pvp, &val) != 0) { |
1224 | 0 | LM_ERR("failed to get spec value\n"); |
1225 | 0 | return E_BUG; |
1226 | 0 | } |
1227 | | |
1228 | 0 | if (val.flags & PV_VAL_NULL) |
1229 | 0 | break; |
1230 | | |
1231 | 0 | if (iter->setf(msg, &iter->pvp, op, &val) != 0) { |
1232 | 0 | LM_ERR("failed to set scriptvar value\n"); |
1233 | 0 | return E_BUG; |
1234 | 0 | } |
1235 | | |
1236 | 0 | ret = run_action_list( |
1237 | 0 | (struct action *)a->elem[2].u.data, msg); |
1238 | | |
1239 | | /* check for "return" statements or "0" retcodes */ |
1240 | 0 | if (action_flags & (ACT_FL_RETURN | ACT_FL_EXIT | ACT_FL_BREAK)) { |
1241 | 0 | action_flags &= ~ACT_FL_BREAK; |
1242 | 0 | return ret; |
1243 | 0 | } |
1244 | | |
1245 | 0 | pvp.pvi.u.ival++; |
1246 | 0 | } |
1247 | 0 | } |
1248 | | |
1249 | 0 | return ret; |
1250 | 0 | } |
1251 | | |
1252 | | /** |
1253 | | * prints the current point of execution in the OpenSIPS script |
1254 | | * |
1255 | | * @class - optional, string to be printed meaning the class of action (if any) |
1256 | | * @action - mandatory, string with the name of action |
1257 | | * @msg - mandatory, sip message |
1258 | | * @line - line in script |
1259 | | */ |
1260 | | void __script_trace(const char *class, const char *action, struct sip_msg *msg, |
1261 | | const char *file, int line) |
1262 | 0 | { |
1263 | 0 | str val; |
1264 | |
|
1265 | 0 | if (pv_printf_s(msg, &script_trace_elem, &val) != 0) { |
1266 | 0 | LM_ERR("Failed to evaluate variables\n"); |
1267 | 0 | return; |
1268 | 0 | } |
1269 | | |
1270 | | /* Also print extra info */ |
1271 | 0 | if (script_trace_info) { |
1272 | 0 | LM_GEN1(script_trace_log_level, "[Script Trace][%s:%d][%s][%s %s]"\ |
1273 | 0 | " -> (%.*s)\n", file, line, script_trace_info, |
1274 | 0 | class?class:"", action, val.len, val.s); |
1275 | 0 | } else { |
1276 | 0 | LM_GEN1(script_trace_log_level, "[Script Trace][%s:%d][%s %s]"\ |
1277 | 0 | " -> (%.*s)\n", file, line, |
1278 | 0 | class?class:"", action, val.len, val.s); |
1279 | 0 | } |
1280 | 0 | } |
1281 | | |
1282 | | /** |
1283 | | * functions used to populate $params() vars in the route_param structure |
1284 | | */ |
1285 | | |
1286 | | void route_params_push_level(char *rt_name, void *params, void *extra, param_getf_t getf) |
1287 | 0 | { |
1288 | 0 | route_rec_level++; |
1289 | 0 | route_params[route_rec_level].params = params; |
1290 | 0 | route_params[route_rec_level].extra = extra; |
1291 | 0 | route_params[route_rec_level].get_param = getf; |
1292 | |
|
1293 | 0 | if (rt_name) { |
1294 | 0 | route_stack[route_stack_size] = rt_name; |
1295 | 0 | route_stack_size++; |
1296 | 0 | } |
1297 | 0 | } |
1298 | | |
1299 | | void route_params_pop_level(void) |
1300 | 0 | { |
1301 | 0 | route_rec_level--; |
1302 | 0 | route_stack_size--; |
1303 | 0 | } |
1304 | | |
1305 | | int route_params_run(struct sip_msg *msg, pv_param_t *ip, pv_value_t *res) |
1306 | 0 | { |
1307 | 0 | if (route_rec_level == -1) |
1308 | 0 | { |
1309 | 0 | LM_DBG("no parameter specified for this route\n"); |
1310 | 0 | return pv_get_null(msg, ip, res); |
1311 | 0 | } |
1312 | | |
1313 | 0 | return route_params[route_rec_level].get_param(msg, ip, res, |
1314 | 0 | route_params[route_rec_level].params, |
1315 | 0 | route_params[route_rec_level].extra); |
1316 | 0 | } |
1317 | | |
1318 | | |
1319 | | static const char *_sip_msg_buf = |
1320 | | "DUMMY sip:user@dummy.com SIP/2.0\r\n" |
1321 | | "Via: SIP/2.0/UDP 127.0.0.1;branch=z9hG4bKdummy\r\n" |
1322 | | "To: <sip:to@dummy.com>\r\n" |
1323 | | "From: <sip:from@dummy.com>;tag=1\r\n" |
1324 | | "Call-ID: dummy-1\r\n" |
1325 | | "CSeq: 1 DUMMY\r\n\r\n"; |
1326 | | static struct sip_msg* dummy_static_req= NULL; |
1327 | | static int dummy_static_in_used = 0; |
1328 | | |
1329 | | int is_dummy_sip_msg(struct sip_msg *req) |
1330 | 0 | { |
1331 | 0 | if (req && req->buf==_sip_msg_buf) |
1332 | 0 | return 0; |
1333 | 0 | return -1; |
1334 | 0 | } |
1335 | | |
1336 | | struct sip_msg* get_dummy_sip_msg(void) |
1337 | 0 | { |
1338 | 0 | struct sip_msg* req; |
1339 | |
|
1340 | 0 | if (dummy_static_req == NULL || dummy_static_in_used) { |
1341 | | /* if the static request is not yet allocated, or the static |
1342 | | * request is already in used (nested calls?), we better allocate |
1343 | | * a new structure */ |
1344 | 0 | LM_DBG("allocating new sip msg\n"); |
1345 | 0 | req = (struct sip_msg*)pkg_malloc(sizeof(struct sip_msg)); |
1346 | 0 | if(req == NULL) |
1347 | 0 | { |
1348 | 0 | LM_ERR("No more memory\n"); |
1349 | 0 | return NULL; |
1350 | 0 | } |
1351 | 0 | memset( req, 0, sizeof(struct sip_msg)); |
1352 | |
|
1353 | 0 | req->buf = (char*)_sip_msg_buf; |
1354 | 0 | req->len = strlen(_sip_msg_buf); |
1355 | 0 | req->rcv.src_ip.af = AF_INET; |
1356 | 0 | req->rcv.dst_ip.af = AF_INET; |
1357 | |
|
1358 | 0 | parse_msg((char*)_sip_msg_buf, strlen(_sip_msg_buf), req); |
1359 | 0 | parse_headers( req, HDR_EOH_F, 0); |
1360 | 0 | if (dummy_static_req==NULL) { |
1361 | 0 | dummy_static_req = req; |
1362 | 0 | dummy_static_in_used = 1; |
1363 | 0 | LM_DBG("setting as static to %p\n",req); |
1364 | 0 | } |
1365 | 0 | } else { |
1366 | | /* reuse the static request */ |
1367 | 0 | req = dummy_static_req; |
1368 | 0 | LM_DBG("reusing the static sip msg %p\n",req); |
1369 | 0 | } |
1370 | | |
1371 | 0 | return req; |
1372 | 0 | } |
1373 | | |
1374 | | void release_dummy_sip_msg( struct sip_msg* req) |
1375 | 0 | { |
1376 | 0 | struct hdr_field* hdrs; |
1377 | |
|
1378 | 0 | if (req==dummy_static_req) { |
1379 | | /* for the static request, just strip out the potential |
1380 | | * changes (lumps, new_uri, dst_uri, etc), but keep the parsed |
1381 | | * list of headers (this never changes) */ |
1382 | 0 | LM_DBG("cleaning the static sip msg %p\n",req); |
1383 | 0 | hdrs = req->headers; |
1384 | 0 | req->headers = NULL; |
1385 | 0 | free_sip_msg(req); |
1386 | 0 | req->headers = hdrs; |
1387 | 0 | req->msg_cb = NULL; |
1388 | 0 | req->new_uri.s = req->dst_uri.s = req->path_vec.s = NULL; |
1389 | 0 | req->new_uri.len = req->dst_uri.len = req->path_vec.len = 0; |
1390 | 0 | req->set_global_address.s = req->set_global_port.s = NULL; |
1391 | 0 | req->set_global_address.len = req->set_global_port.len = 0; |
1392 | 0 | req->add_rm = req->body_lumps = NULL; |
1393 | 0 | req->reply_lump = NULL; |
1394 | 0 | req->ruri_q = Q_UNSPECIFIED; |
1395 | 0 | req->ruri_bflags = 0; |
1396 | 0 | req->force_send_socket = NULL; |
1397 | 0 | req->parsed_uri_ok = 0; |
1398 | 0 | req->parsed_orig_ruri_ok = 0; |
1399 | 0 | req->add_to_branch_len = 0; |
1400 | 0 | req->flags = 0; |
1401 | 0 | req->msg_flags = 0; |
1402 | 0 | memset( &req->time, 0, sizeof(struct timeval)); |
1403 | 0 | dummy_static_in_used = 0; |
1404 | 0 | } else { |
1405 | 0 | LM_DBG("freeing allocated sip msg %p\n",req); |
1406 | | /* is was an 100% allocated request */ |
1407 | 0 | free_sip_msg(req); |
1408 | 0 | pkg_free(req); |
1409 | 0 | } |
1410 | 0 | } |