Skip to content

Commit

Permalink
$route variable: Add support for script "callback routes"
Browse files Browse the repository at this point in the history
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
  • Loading branch information
liviuchircu committed Dec 21, 2021
1 parent fb1e770 commit bb1fcc2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 19 deletions.
45 changes: 37 additions & 8 deletions action.c
Expand Up @@ -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;
Expand All @@ -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]);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion action.h
Expand Up @@ -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);
Expand Down
17 changes: 9 additions & 8 deletions pvar.c
Expand Up @@ -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");
Expand All @@ -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:
Expand All @@ -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");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions route.c
Expand Up @@ -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;

Expand Down

0 comments on commit bb1fcc2

Please sign in to comment.