From bb1fcc27e279e3021537d197e97bee900fbbae37 Mon Sep 17 00:00:00 2001 From: Liviu Chircu Date: Tue, 21 Dec 2021 15:17:50 +0200 Subject: [PATCH] $route variable: Add support for script "callback routes" The idea of "callback routes" was missed during the development of $route, with this feature having been first introduced in 3.1. Conceptually, a "callback route" is a contextless route that may be invoked in a nested fashion, during the processing of a SIP message in any of the routes. For example, on a SIP BYE: 1. route 1.1 loose_route() (this triggers the "callback route", e.g. see dlg_on_hangup()) 1.2 route "dlg_on_hangup" is run, contextless! 2. script processing continues! <--- here, $route output was broken This patch fixes the issue, by adding recursion detection into the run_top_route() function, such that the $route variable works as expected regardless of how many (nested) times it is called. Many thanks to Ben Newlin for catching this issue, as well as providing a detailed report! Fixes #2681 --- action.c | 45 +++++++++++++++++++++++++++++++++++++-------- action.h | 2 +- pvar.c | 17 +++++++++-------- route.c | 4 ++-- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/action.c b/action.c index 01f17c05f07..95f5d10f6c8 100644 --- a/action.c +++ b/action.c @@ -90,7 +90,15 @@ struct route_params_level { static struct route_params_level route_params[ROUTE_MAX_REC_LEV]; static int route_rec_level = -1; +/* the current stack of route names (first route name is at index 0) */ char *route_stack[ROUTE_MAX_REC_LEV + 1]; + +/* the virtual start of the stack; typically 0, but may get temporarily bumped + * with each added nesting level of script route callback execution + * (e.g. due to logic similar to dlg_on_hangup(), followed by a SIP BYE) */ +int route_stack_start; + +/* the total size of the stack, counting from index 0 */ int route_stack_size; int curr_action_line; @@ -106,11 +114,11 @@ static int route_param_get(struct sip_msg *msg, pv_param_t *ip, /* (0 if drop or break encountered, 1 if not ) */ static inline int run_actions(struct action* a, struct sip_msg* msg) { - int ret, has_name; + int ret, _; str top_route; if (route_stack_size > ROUTE_MAX_REC_LEV) { - get_top_route_type(&top_route, &has_name); + get_top_route_type(&top_route, &_); LM_ERR("route recursion limit reached, giving up! (nested routes: %d, " "first: '%.*s', last: '%s')!\n", route_stack_size, top_route.len, top_route.s, route_stack[ROUTE_MAX_REC_LEV]); @@ -156,6 +164,7 @@ void run_error_route(struct sip_msg* msg, int init_route_stack) swap_route_type(old_route, ERROR_ROUTE); route_stack[0] = NULL; + route_stack_start = 0; route_stack_size = 1; run_actions(sroutes->error.a, msg); @@ -195,10 +204,11 @@ int run_action_list(struct action* a, struct sip_msg* msg) return ret; } - int run_top_route(struct script_route sr, struct sip_msg* msg) { - int bk_action_flags; + static int recursing; + + int bk_action_flags, route_stack_start_bkp = -1, route_stack_size_bkp; int ret; context_p ctx = NULL; @@ -216,18 +226,37 @@ int run_top_route(struct script_route sr, struct sip_msg* msg) current_processing_ctx = ctx; } + /* the recursion support allows run_top_route() to be freely called from + * other modules (e.g. dialog) in order to provide contextless script + * callbacks using routes, without losing the original route stack */ + if (recursing) { + route_stack_start_bkp = route_stack_start; + route_stack_size_bkp = route_stack_size; + route_stack_start = route_stack_size; + route_stack_size += 1; + } else { + route_stack_start = 0; + route_stack_size = 1; + recursing = 1; + } + if (route_type & (ERROR_ROUTE|LOCAL_ROUTE|STARTUP_ROUTE) || (route_type & (REQUEST_ROUTE|ONREPLY_ROUTE) && !strcmp(sr.name, "0"))) - route_stack[0] = NULL; + route_stack[route_stack_start] = NULL; else - route_stack[0] = sr.name; - - route_stack_size = 1; + route_stack[route_stack_start] = sr.name; run_actions(sr.a, msg); ret = action_flags; + if (route_stack_start_bkp != -1) { + route_stack_size = route_stack_size_bkp; + route_stack_start = route_stack_start_bkp; + } else { + recursing = 0; + } + action_flags = bk_action_flags; /* reset script tracing */ use_script_trace = 0; diff --git a/action.h b/action.h index 3058a2fb8ec..d3c7ffdbf64 100644 --- a/action.h +++ b/action.h @@ -55,7 +55,7 @@ extern int min_action_time; /* the current route call stack, containing route names */ extern char *route_stack[]; -extern int route_stack_size; +extern int route_stack_start, route_stack_size; int do_action(struct action* a, struct sip_msg* msg); int run_top_route(struct script_route sr, struct sip_msg* msg); diff --git a/pvar.c b/pvar.c index 2190aa866c4..e0aa69da10c 100644 --- a/pvar.c +++ b/pvar.c @@ -278,7 +278,7 @@ static int pv_get_route_name(struct sip_msg *msg, pv_param_t *param, static str rn_buf; str s; - int i, idx, idx_flags, len, rlen, has_name; + int i, idx, idx_flags, len, rlen, has_name, route_stack_size_ctx; if (pv_get_spec_index(msg, param, &idx, &idx_flags) != 0) { LM_ERR("invalid index\n"); @@ -290,13 +290,13 @@ static int pv_get_route_name(struct sip_msg *msg, pv_param_t *param, if (!has_name) goto unnamed_route; - len = strlen(route_stack[0]); + len = strlen(route_stack[route_stack_start]); if (pkg_str_extend(&rn_buf, s.len + 2 + len + 1) != 0) { LM_ERR("oom\n"); return pv_get_null(msg, param, res); } - len = sprintf(rn_buf.s, "%.*s[%s]", s.len, s.s, route_stack[idx]); + len = sprintf(rn_buf.s, "%.*s[%s]", s.len, s.s, route_stack[route_stack_start]); goto print_remaining; unnamed_route: @@ -310,7 +310,7 @@ static int pv_get_route_name(struct sip_msg *msg, pv_param_t *param, print_remaining: s = str_route; - for (i = 1; i < route_stack_size; i++) { + for (i = route_stack_start+1; i < route_stack_size; i++) { if (!route_stack[i]) { if (pkg_str_extend(&rn_buf, len + 3 + s.len + 1) != 0) { LM_ERR("oom\n"); @@ -346,15 +346,17 @@ static int pv_get_route_name(struct sip_msg *msg, pv_param_t *param, return pv_get_strval(msg, param, res, &s); } + route_stack_size_ctx = route_stack_size - route_stack_start; + if (idx < 0) - idx += route_stack_size; + idx += route_stack_size_ctx; /* index out of bounds -- play nice and return NULL */ - if (idx > route_stack_size - 1 || idx < 0) + if (idx > route_stack_size_ctx - 1 || idx < 0) return pv_get_null(msg, param, res); /* reverse the index, since we index the route stack backwards */ - idx = route_stack_size - idx - 1; + idx = route_stack_size_ctx - idx - 1; if (idx == 0) { get_top_route_type(&s, &has_name); @@ -388,7 +390,6 @@ static int pv_get_route_name(struct sip_msg *msg, pv_param_t *param, s.s = rn_buf.s; } - out_ok: return pv_get_strval(msg, param, res, &s); } diff --git a/route.c b/route.c index e51caf5dd6b..87989bf8efa 100644 --- a/route.c +++ b/route.c @@ -1753,13 +1753,13 @@ void get_top_route_type(str *type, int *has_name) switch (route_type) { case REQUEST_ROUTE: *type = str_route; - if (!route_stack[0]) + if (!route_stack[route_stack_start]) goto out_noname; break; case ONREPLY_ROUTE: *type = str_onreply_route; - if (!route_stack[0]) + if (!route_stack[route_stack_start]) goto out_noname; break;