mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1672 lines
44 KiB
1672 lines
44 KiB
/*
|
|
*
|
|
* Copyright (C) 2001-2003 FhG Fokus
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Kamailio core :: Config file actions
|
|
* \ingroup core
|
|
* Module: \ref core
|
|
*/
|
|
|
|
|
|
|
|
#include "comp_defs.h"
|
|
|
|
#include "action.h"
|
|
#include "config.h"
|
|
#include "error.h"
|
|
#include "dprint.h"
|
|
#include "proxy.h"
|
|
#include "forward.h"
|
|
#include "udp_server.h"
|
|
#include "route.h"
|
|
#include "parser/msg_parser.h"
|
|
#include "parser/parse_uri.h"
|
|
#include "ut.h"
|
|
#include "lvalue.h"
|
|
#include "sr_module.h"
|
|
#include "select_buf.h"
|
|
#include "mem/mem.h"
|
|
#include "globals.h"
|
|
#include "dset.h"
|
|
#include "onsend.h"
|
|
#include "fmsg.h"
|
|
#include "resolve.h"
|
|
#ifdef USE_TCP
|
|
#include "tcp_server.h"
|
|
#endif
|
|
#ifdef USE_SCTP
|
|
#include "sctp_core.h"
|
|
#endif
|
|
#include "switch.h"
|
|
#include "events.h"
|
|
#include "cfg/cfg_struct.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <stdlib.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <string.h>
|
|
|
|
|
|
#ifdef DEBUG_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
int _last_returned_code = 0;
|
|
struct onsend_info* p_onsend=0; /* onsend route send info */
|
|
|
|
/* current action executed from config file */
|
|
static cfg_action_t *_cfg_crt_action = 0;
|
|
|
|
/*!< maximum number of recursive calls for blocks of actions */
|
|
static unsigned int max_recursive_level = 256;
|
|
|
|
void set_max_recursive_level(unsigned int lev)
|
|
{
|
|
max_recursive_level = lev;
|
|
}
|
|
|
|
/* return current action executed from config file */
|
|
cfg_action_t *get_cfg_crt_action(void)
|
|
{
|
|
return _cfg_crt_action;
|
|
}
|
|
|
|
/* return line in config for current executed action */
|
|
int get_cfg_crt_line(void)
|
|
{
|
|
if(_cfg_crt_action==0)
|
|
return 0;
|
|
return _cfg_crt_action->cline;
|
|
}
|
|
|
|
/* return name of config for current executed action */
|
|
char *get_cfg_crt_name(void)
|
|
{
|
|
if(_cfg_crt_action==0)
|
|
return 0;
|
|
return _cfg_crt_action->cfile;
|
|
}
|
|
|
|
/* handle the exit code of a module function call.
|
|
* (used internally in do_action())
|
|
* @param h - script handle (h->last_retcode and h->run_flags will be set).
|
|
* @param ret - module function (v0 or v2) retcode
|
|
* Side-effects: sets _last_returned_code
|
|
*/
|
|
#define MODF_HANDLE_RETCODE(h, ret) \
|
|
do { \
|
|
/* if (unlikely((ret)==0)) (h)->run_flags|=EXIT_R_F; */ \
|
|
(h)->run_flags |= EXIT_R_F & (((ret) != 0) -1); \
|
|
(h)->last_retcode=(ret); \
|
|
_last_returned_code = (h)->last_retcode; \
|
|
} while(0)
|
|
|
|
|
|
|
|
/* frees parameters converted using MODF_RVE_PARAM_CONVERT() from dst.
|
|
* (used internally in do_action())
|
|
* Assumes src is unchanged.
|
|
* Side-effects: clobbers i (int).
|
|
*/
|
|
#define MODF_RVE_PARAM_FREE(cmd, src, dst) \
|
|
for (i=0; i < (dst)[1].u.number; i++) { \
|
|
if ((src)[i+2].type == RVE_ST && (dst)[i+2].u.data) { \
|
|
if ((dst)[i+2].type == RVE_FREE_FIXUP_ST) {\
|
|
/* call free_fixup (which should restore the original
|
|
string) */ \
|
|
(void)call_fixup((cmd)->free_fixup, &(dst)[i+2].u.data, i+1); \
|
|
} else if ((dst)[i+2].type == FPARAM_DYN_ST) {\
|
|
/* completely frees fparam and restore original string */\
|
|
fparam_free_restore(&(dst)[i+2].u.data); \
|
|
} \
|
|
/* free allocated string */ \
|
|
pkg_free((dst)[i+2].u.data); \
|
|
(dst)[i+2].u.data = 0; \
|
|
} \
|
|
}
|
|
|
|
|
|
/* fills dst from src, converting RVE_ST params to STRING_ST.
|
|
* (used internally in do_action())
|
|
* @param src - source action_u_t array, as in the action structure
|
|
* @param dst - destination action_u_t array, will be filled from src.
|
|
* WARNING: dst must be cleaned when done, use MODULE_RVE_PARAM_FREE()
|
|
* Side-effects: clobbers i (int), s (str), rv (rvalue*), might jump to error.
|
|
*/
|
|
#define MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst) \
|
|
do { \
|
|
(dst)[1]=(src)[1]; \
|
|
for (i=0; i < (src)[1].u.number; i++) { \
|
|
if ((src)[2+i].type == RVE_ST) { \
|
|
rv=rval_expr_eval((h), (msg), (src)[i+2].u.data); \
|
|
if (unlikely(rv == 0 || \
|
|
rval_get_str((h), (msg), &s, rv, 0) < 0)) { \
|
|
rval_destroy(rv); \
|
|
ERR("failed to convert RVE to string\n"); \
|
|
(dst)[1].u.number = i; \
|
|
MODF_RVE_PARAM_FREE(cmd, src, dst); \
|
|
goto error; \
|
|
} \
|
|
(dst)[i+2].type = STRING_RVE_ST; \
|
|
(dst)[i+2].u.string = s.s; \
|
|
(dst)[i+2].u.str.len = s.len; \
|
|
rval_destroy(rv); \
|
|
if ((cmd)->fixup) {\
|
|
if ((cmd)->free_fixup) {\
|
|
if (likely( call_fixup((cmd)->fixup, \
|
|
&(dst)[i+2].u.data, i+1) >= 0) ) { \
|
|
/* success => mark it for calling free fixup */ \
|
|
if (likely((dst)[i+2].u.data != s.s)) \
|
|
(dst)[i+2].type = RVE_FREE_FIXUP_ST; \
|
|
} else { \
|
|
/* error calling fixup => mark conv. parameter \
|
|
and return error */ \
|
|
(dst)[1].u.number = i; \
|
|
ERR("runtime fixup failed for %s param %d\n", \
|
|
(cmd)->name, i+1); \
|
|
MODF_RVE_PARAM_FREE(cmd, src, dst); \
|
|
goto error; \
|
|
} \
|
|
} else if ((cmd)->fixup_flags & FIXUP_F_FPARAM_RVE) { \
|
|
if (likely( call_fixup((cmd)->fixup, \
|
|
&(dst)[i+2].u.data, i+1) >= 0)) { \
|
|
if ((dst)[i+2].u.data != s.s) \
|
|
(dst)[i+2].type = FPARAM_DYN_ST; \
|
|
} else { \
|
|
/* error calling fixup => mark conv. parameter \
|
|
and return error */ \
|
|
(dst)[1].u.number = i; \
|
|
ERR("runtime fixup failed for %s param %d\n", \
|
|
(cmd)->name, i+1); \
|
|
MODF_RVE_PARAM_FREE(cmd, src, dst); \
|
|
goto error; \
|
|
}\
|
|
} \
|
|
} \
|
|
} else \
|
|
(dst)[i+2]=(src)[i+2]; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
|
|
/* call a module function with normal STRING_ST params.
|
|
* (used internally in do_action())
|
|
* @param f_type - cmd_function type
|
|
* @param h
|
|
* @param msg
|
|
* @param src - source action_u_t array (e.g. action->val)
|
|
* @param params... - variable list of parameters, passed to the module
|
|
* function
|
|
* Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), cmd,
|
|
* might jump to error.
|
|
*
|
|
*/
|
|
#ifdef __SUNPRO_C
|
|
#define MODF_CALL(f_type, h, msg, src, ...) \
|
|
do { \
|
|
cmd=(src)[0].u.data; \
|
|
ret=((f_type)cmd->function)((msg), __VA_ARGS__); \
|
|
MODF_HANDLE_RETCODE(h, ret); \
|
|
} while (0)
|
|
#else /* ! __SUNPRO_C (gcc, icc a.s.o) */
|
|
#define MODF_CALL(f_type, h, msg, src, params...) \
|
|
do { \
|
|
cmd=(src)[0].u.data; \
|
|
ret=((f_type)cmd->function)((msg), ## params ); \
|
|
MODF_HANDLE_RETCODE(h, ret); \
|
|
} while (0)
|
|
#endif /* __SUNPRO_C */
|
|
|
|
|
|
|
|
/* call a module function with possible RVE params.
|
|
* (used internally in do_action())
|
|
* @param f_type - cmd_function type
|
|
* @param h
|
|
* @param msg
|
|
* @param src - source action_u_t array (e.g. action->val)
|
|
* @param dst - temporary action_u_t array used for conversions. It can be
|
|
* used for the function parameters. It's contents it's not
|
|
* valid after the call.
|
|
* @param params... - variable list of parameters, passed to the module
|
|
* function
|
|
* Side-effects: sets ret, clobbers i (int), s (str), rv (rvalue*), f, dst,
|
|
* might jump to error.
|
|
*
|
|
*/
|
|
#ifdef __SUNPRO_C
|
|
#define MODF_RVE_CALL(f_type, h, msg, src, dst, ...) \
|
|
do { \
|
|
cmd=(src)[0].u.data; \
|
|
MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst); \
|
|
ret=((f_type)cmd->function)((msg), __VA_ARGS__); \
|
|
MODF_HANDLE_RETCODE(h, ret); \
|
|
/* free strings allocated by us or fixups */ \
|
|
MODF_RVE_PARAM_FREE(cmd, src, dst); \
|
|
} while (0)
|
|
#else /* ! __SUNPRO_C (gcc, icc a.s.o) */
|
|
#define MODF_RVE_CALL(f_type, h, msg, src, dst, params...) \
|
|
do { \
|
|
cmd=(src)[0].u.data; \
|
|
MODF_RVE_PARAM_CONVERT(h, msg, cmd, src, dst); \
|
|
ret=((f_type)cmd->function)((msg), ## params ); \
|
|
MODF_HANDLE_RETCODE(h, ret); \
|
|
/* free strings allocated by us or fixups */ \
|
|
MODF_RVE_PARAM_FREE(cmd, src, dst); \
|
|
} while (0)
|
|
#endif /* __SUNPRO_C */
|
|
|
|
|
|
/* ret= 0! if action -> end of list(e.g DROP),
|
|
> 0 to continue processing next actions
|
|
and <0 on error */
|
|
int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
|
|
{
|
|
int ret;
|
|
int v;
|
|
struct dest_info dst;
|
|
char* tmp;
|
|
char *new_uri, *end, *crt;
|
|
sr31_cmd_export_t* cmd;
|
|
int len;
|
|
int user;
|
|
struct sip_uri uri, next_hop;
|
|
struct sip_uri *u;
|
|
unsigned short port;
|
|
str* dst_host;
|
|
int i, flags;
|
|
avp_t* avp;
|
|
struct search_state st;
|
|
struct switch_cond_table* sct;
|
|
struct switch_jmp_table* sjt;
|
|
struct rval_expr* rve;
|
|
struct match_cond_table* mct;
|
|
struct rvalue* rv;
|
|
struct rvalue* rv1;
|
|
struct rval_cache c1;
|
|
str s;
|
|
void *srevp[2];
|
|
/* temporary storage space for a struct action.val[] working copy
|
|
(needed to transform RVE intro STRING before calling module
|
|
functions). [0] is not used (corresp. to the module export pointer),
|
|
[1] contains the number of params, and [2..] the param values.
|
|
We need [1], because some fixup function use it
|
|
(see fixup_get_param_count()). */
|
|
static action_u_t mod_f_params[MAX_ACTIONS];
|
|
|
|
/* reset the value of error to E_UNSPEC so avoid unknowledgable
|
|
functions to return with error (status<0) and not setting it
|
|
leaving there previous error; cache the previous value though
|
|
for functions which want to process it */
|
|
prev_ser_error=ser_error;
|
|
ser_error=E_UNSPEC;
|
|
|
|
/* hook for every executed action (in use by cfg debugger) */
|
|
if(unlikely(sr_event_enabled(SREV_CFG_RUN_ACTION)))
|
|
{
|
|
srevp[0] = (void*)a;
|
|
srevp[1] = (void*)msg;
|
|
sr_event_exec(SREV_CFG_RUN_ACTION, (void*)srevp);
|
|
}
|
|
|
|
ret=E_BUG;
|
|
switch ((unsigned char)a->type){
|
|
case DROP_T:
|
|
switch(a->val[0].type){
|
|
case NUMBER_ST:
|
|
ret=(int) a->val[0].u.number;
|
|
break;
|
|
case RVE_ST:
|
|
rve=(struct rval_expr*)a->val[0].u.data;
|
|
rval_expr_eval_int(h, msg, &ret, rve);
|
|
break;
|
|
case RETCODE_ST:
|
|
ret=h->last_retcode;
|
|
break;
|
|
default:
|
|
BUG("unexpected subtype %d in DROP_T\n",
|
|
a->val[0].type);
|
|
ret=0;
|
|
goto error;
|
|
}
|
|
h->run_flags|=(unsigned int)a->val[1].u.number;
|
|
break;
|
|
case FORWARD_T:
|
|
#ifdef USE_TCP
|
|
case FORWARD_TCP_T:
|
|
#endif
|
|
#ifdef USE_TLS
|
|
case FORWARD_TLS_T:
|
|
#endif
|
|
#ifdef USE_SCTP
|
|
case FORWARD_SCTP_T:
|
|
#endif
|
|
case FORWARD_UDP_T:
|
|
/* init dst */
|
|
init_dest_info(&dst);
|
|
if (a->type==FORWARD_UDP_T) dst.proto=PROTO_UDP;
|
|
#ifdef USE_TCP
|
|
else if (a->type==FORWARD_TCP_T) dst.proto= PROTO_TCP;
|
|
#endif
|
|
#ifdef USE_TLS
|
|
else if (a->type==FORWARD_TLS_T) dst.proto= PROTO_TLS;
|
|
#endif
|
|
#ifdef USE_SCTP
|
|
else if (a->type==FORWARD_SCTP_T) dst.proto=PROTO_SCTP;
|
|
#endif
|
|
else dst.proto=PROTO_NONE;
|
|
if (a->val[0].type==URIHOST_ST){
|
|
/*parse uri*/
|
|
|
|
if (msg->dst_uri.len) {
|
|
ret = parse_uri(msg->dst_uri.s, msg->dst_uri.len,
|
|
&next_hop);
|
|
u = &next_hop;
|
|
} else {
|
|
ret = parse_sip_msg_uri(msg);
|
|
u = &msg->parsed_uri;
|
|
}
|
|
|
|
if (ret<0) {
|
|
LM_ERR("forward: bad_uri dropping packet\n");
|
|
goto error;
|
|
}
|
|
|
|
switch (a->val[1].type){
|
|
case URIPORT_ST:
|
|
port=u->port_no;
|
|
break;
|
|
case NUMBER_ST:
|
|
port=a->val[1].u.number;
|
|
break;
|
|
default:
|
|
LM_CRIT("bad forward 2nd param type (%d)\n", a->val[1].type);
|
|
ret=E_UNSPEC;
|
|
goto error_fwd_uri;
|
|
}
|
|
if (dst.proto == PROTO_NONE){ /* only if proto not set get it
|
|
from the uri */
|
|
switch(u->proto){
|
|
case PROTO_NONE:
|
|
/*dst.proto=PROTO_UDP; */
|
|
/* no proto, try to get it from the dns */
|
|
break;
|
|
case PROTO_UDP:
|
|
#ifdef USE_TCP
|
|
case PROTO_TCP:
|
|
case PROTO_WS:
|
|
#endif
|
|
#ifdef USE_TLS
|
|
case PROTO_TLS:
|
|
case PROTO_WSS:
|
|
#endif
|
|
#ifdef USE_SCTP
|
|
case PROTO_SCTP:
|
|
#endif
|
|
dst.proto=u->proto;
|
|
break;
|
|
default:
|
|
LM_ERR("forward: bad uri transport %d\n", u->proto);
|
|
ret=E_BAD_PROTO;
|
|
goto error_fwd_uri;
|
|
}
|
|
#ifdef USE_TLS
|
|
if (u->type==SIPS_URI_T){
|
|
if (u->proto==PROTO_UDP){
|
|
LM_ERR("forward: secure uri incompatible with transport %d\n",
|
|
u->proto);
|
|
ret=E_BAD_PROTO;
|
|
goto error_fwd_uri;
|
|
} else if (u->proto!=PROTO_WSS)
|
|
dst.proto=PROTO_TLS;
|
|
else
|
|
dst.proto=PROTO_WSS;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef HONOR_MADDR
|
|
if (u->maddr_val.s && u->maddr_val.len)
|
|
dst_host=&u->maddr_val;
|
|
else
|
|
#endif
|
|
dst_host=&u->host;
|
|
#ifdef USE_COMP
|
|
dst.comp=u->comp;
|
|
#endif
|
|
ret=forward_request(msg, dst_host, port, &dst);
|
|
if (ret>=0){
|
|
ret=1;
|
|
}
|
|
}else if ((a->val[0].type==PROXY_ST) && (a->val[1].type==NUMBER_ST)){
|
|
if (dst.proto==PROTO_NONE)
|
|
dst.proto=msg->rcv.proto;
|
|
proxy2su(&dst.to, (struct proxy_l*)a->val[0].u.data);
|
|
ret=forward_request(msg, 0, 0, &dst);
|
|
if (ret>=0){
|
|
ret=1;
|
|
proxy_mark((struct proxy_l*)a->val[0].u.data, ret);
|
|
}else if (ser_error!=E_OK){
|
|
proxy_mark((struct proxy_l*)a->val[0].u.data, ret);
|
|
}
|
|
}else{
|
|
LM_CRIT("bad forward() types %d, %d\n",
|
|
a->val[0].type, a->val[1].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
break;
|
|
case LOG_T:
|
|
if ((a->val[0].type!=NUMBER_ST)|(a->val[1].type!=STRING_ST)){
|
|
LM_CRIT("bad log() types %d, %d\n",
|
|
a->val[0].type, a->val[1].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
LOG_(DEFAULT_FACILITY, a->val[0].u.number, "<script>: ", "%s",
|
|
a->val[1].u.string);
|
|
ret=1;
|
|
break;
|
|
|
|
/* jku -- introduce a new branch */
|
|
case APPEND_BRANCH_T:
|
|
if (unlikely(a->val[0].type!=STR_ST)) {
|
|
LM_CRIT("bad append_branch_t %d\n", a->val[0].type );
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
getbflagsval(0, (flag_t*)&flags);
|
|
ret=append_branch(msg, &a->val[0].u.str, &msg->dst_uri,
|
|
&msg->path_vec, a->val[1].u.number,
|
|
(flag_t)flags, msg->force_send_socket,
|
|
0, 0, 0, 0);
|
|
/* if the uri is the ruri and q was also not changed, mark
|
|
ruri as consumed, to avoid having an identical branch */
|
|
if ((a->val[0].u.str.s == 0 || a->val[0].u.str.len == 0) &&
|
|
a->val[1].u.number == Q_UNSPECIFIED)
|
|
ruri_mark_consumed();
|
|
break;
|
|
|
|
/* remove last branch */
|
|
case REMOVE_BRANCH_T:
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
ret=drop_sip_branch(0) ? -1 : 1;
|
|
} else {
|
|
ret=drop_sip_branch(a->val[0].u.number) ? -1 : 1;
|
|
}
|
|
break;
|
|
|
|
/* remove all branches */
|
|
case CLEAR_BRANCHES_T:
|
|
clear_branches();
|
|
ret=1;
|
|
break;
|
|
|
|
/* jku begin: is_length_greater_than */
|
|
case LEN_GT_T:
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
LM_CRIT("bad len_gt type %d\n", a->val[0].type );
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
/* LM_DBG("message length %d, max %d\n",
|
|
msg->len, a->val[0].u.number ); */
|
|
ret = msg->len >= a->val[0].u.number ? 1 : -1;
|
|
break;
|
|
/* jku end: is_length_greater_than */
|
|
|
|
/* jku - begin : flag processing */
|
|
|
|
case SETFLAG_T:
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
LM_CRIT("bad setflag() type %d\n", a->val[0].type );
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
if (!flag_in_range( a->val[0].u.number )) {
|
|
ret=E_CFG;
|
|
goto error;
|
|
}
|
|
setflag( msg, a->val[0].u.number );
|
|
ret=1;
|
|
break;
|
|
|
|
case RESETFLAG_T:
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
LM_CRIT("bad resetflag() type %d\n", a->val[0].type );
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
if (!flag_in_range( a->val[0].u.number )) {
|
|
ret=E_CFG;
|
|
goto error;
|
|
}
|
|
resetflag( msg, a->val[0].u.number );
|
|
ret=1;
|
|
break;
|
|
|
|
case ISFLAGSET_T:
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
LM_CRIT("bad isflagset() type %d\n", a->val[0].type );
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
if (!flag_in_range( a->val[0].u.number )) {
|
|
ret=E_CFG;
|
|
goto error;
|
|
}
|
|
ret=isflagset( msg, a->val[0].u.number );
|
|
break;
|
|
/* jku - end : flag processing */
|
|
|
|
case AVPFLAG_OPER_T:
|
|
ret = 0;
|
|
if ((a->val[0].u.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL ||
|
|
(a->val[0].u.attr->type & AVP_NAME_RE)!=0) {
|
|
for (avp=search_first_avp(a->val[0].u.attr->type,
|
|
a->val[0].u.attr->name, NULL, &st);
|
|
avp;
|
|
avp = search_next_avp(&st, NULL)) {
|
|
switch (a->val[2].u.number) {
|
|
/* oper: 0..reset, 1..set, -1..no change */
|
|
case 0:
|
|
avp->flags &= ~(avp_flags_t)a->val[1].u.number;
|
|
break;
|
|
case 1:
|
|
avp->flags |= (avp_flags_t)a->val[1].u.number;
|
|
break;
|
|
default:;
|
|
}
|
|
ret = ret ||
|
|
((avp->flags & (avp_flags_t)a->val[1].u.number) != 0);
|
|
}
|
|
} else {
|
|
avp = search_avp_by_index(a->val[0].u.attr->type,
|
|
a->val[0].u.attr->name, NULL,
|
|
a->val[0].u.attr->index);
|
|
if (avp) {
|
|
switch (a->val[2].u.number) {
|
|
/* oper: 0..reset, 1..set, -1..no change */
|
|
case 0:
|
|
avp->flags &= ~(avp_flags_t)a->val[1].u.number;
|
|
break;
|
|
case 1:
|
|
avp->flags |= (avp_flags_t)a->val[1].u.number;
|
|
break;
|
|
default:;
|
|
}
|
|
ret = (avp->flags & (avp_flags_t)a->val[1].u.number) != 0;
|
|
}
|
|
}
|
|
if (ret==0)
|
|
ret = -1;
|
|
break;
|
|
case ERROR_T:
|
|
if ((a->val[0].type!=STRING_ST)|(a->val[1].type!=STRING_ST)){
|
|
LM_CRIT("bad error() types %d, %d\n", a->val[0].type, a->val[1].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
LM_NOTICE("error(\"%s\", \"%s\") "
|
|
"not implemented yet\n", a->val[0].u.string, a->val[1].u.string);
|
|
ret=1;
|
|
break;
|
|
case ROUTE_T:
|
|
if (likely(a->val[0].type == NUMBER_ST))
|
|
i = a->val[0].u.number;
|
|
else if (a->val[0].type == RVE_ST) {
|
|
rv = rval_expr_eval(h, msg, a->val[0].u.data);
|
|
rval_cache_init(&c1);
|
|
if (unlikely(rv == 0 ||
|
|
rval_get_tmp_str(h, msg, &s, rv, 0, &c1) < 0)) {
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
ERR("failed to convert RVE to string\n");
|
|
ret = E_UNSPEC;
|
|
goto error;
|
|
}
|
|
i = route_lookup(&main_rt, s.s);
|
|
if (unlikely(i < 0)) {
|
|
ERR("route \"%s\" not found at %s:%d\n",
|
|
s.s, (a->cfile)?a->cfile:"line", a->cline);
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
s.s = 0;
|
|
ret = E_SCRIPT;
|
|
goto error;
|
|
}
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
s.s = 0;
|
|
} else {
|
|
LM_CRIT("bad route() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
if (unlikely((i>=main_rt.idx)||(i<0))){
|
|
LM_ERR("invalid routing table number in"
|
|
"route(%lu) at %s:%d\n", a->val[0].u.number,
|
|
(a->cfile)?a->cfile:"line", a->cline);
|
|
ret=E_CFG;
|
|
goto error;
|
|
}
|
|
/*ret=((ret=run_actions(rlist[a->val[0].u.number],msg))<0)?ret:1;*/
|
|
ret=run_actions(h, main_rt.rlist[i], msg);
|
|
h->last_retcode=ret;
|
|
_last_returned_code = h->last_retcode;
|
|
h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
|
|
break;
|
|
case EXEC_T:
|
|
if (a->val[0].type!=STRING_ST){
|
|
LM_CRIT("bad exec() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
LM_NOTICE("exec(\"%s\") not fully implemented,"
|
|
" using dumb version...\n", a->val[0].u.string);
|
|
ret=system(a->val[0].u.string);
|
|
if (ret!=0){
|
|
LM_NOTICE("exec() returned %d\n", ret);
|
|
}
|
|
ret=1;
|
|
break;
|
|
case REVERT_URI_T:
|
|
if (msg->new_uri.s) {
|
|
pkg_free(msg->new_uri.s);
|
|
msg->new_uri.len=0;
|
|
msg->new_uri.s=0;
|
|
msg->parsed_uri_ok=0; /* invalidate current parsed uri*/
|
|
ruri_mark_new(); /* available for forking */
|
|
};
|
|
ret=1;
|
|
break;
|
|
case SET_HOST_T:
|
|
case SET_HOSTPORT_T:
|
|
case SET_HOSTPORTTRANS_T:
|
|
case SET_HOSTALL_T:
|
|
case SET_USER_T:
|
|
case SET_USERPASS_T:
|
|
case SET_PORT_T:
|
|
case SET_URI_T:
|
|
case PREFIX_T:
|
|
case STRIP_T:
|
|
case STRIP_TAIL_T:
|
|
case SET_USERPHONE_T:
|
|
user=0;
|
|
if (a->type==STRIP_T || a->type==STRIP_TAIL_T) {
|
|
if (a->val[0].type!=NUMBER_ST) {
|
|
LM_CRIT("bad set*() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
} else if (a->type!=SET_USERPHONE_T) {
|
|
if (a->val[0].type!=STRING_ST) {
|
|
LM_CRIT("bad set*() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
}
|
|
if (a->type==SET_URI_T){
|
|
if (msg->new_uri.s) {
|
|
pkg_free(msg->new_uri.s);
|
|
msg->new_uri.len=0;
|
|
}
|
|
msg->parsed_uri_ok=0;
|
|
len=strlen(a->val[0].u.string);
|
|
msg->new_uri.s=pkg_malloc(len+1);
|
|
if (msg->new_uri.s==0){
|
|
LM_ERR("memory allocation failure\n");
|
|
ret=E_OUT_OF_MEM;
|
|
goto error;
|
|
}
|
|
memcpy(msg->new_uri.s, a->val[0].u.string, len);
|
|
msg->new_uri.s[len]=0;
|
|
msg->new_uri.len=len;
|
|
ruri_mark_new(); /* available for forking */
|
|
|
|
ret=1;
|
|
break;
|
|
}
|
|
if (msg->parsed_uri_ok==0) {
|
|
if (msg->new_uri.s) {
|
|
tmp=msg->new_uri.s;
|
|
len=msg->new_uri.len;
|
|
}else{
|
|
tmp=msg->first_line.u.request.uri.s;
|
|
len=msg->first_line.u.request.uri.len;
|
|
}
|
|
if (parse_uri(tmp, len, &uri)<0){
|
|
LM_ERR("bad uri <%s>, dropping packet\n", tmp);
|
|
ret=E_UNSPEC;
|
|
goto error;
|
|
}
|
|
} else {
|
|
uri=msg->parsed_uri;
|
|
}
|
|
|
|
/* skip SET_USERPHONE_T action if the URI is already
|
|
* a tel: or tels: URI, or contains the user=phone param */
|
|
if ((a->type==SET_USERPHONE_T)
|
|
&& ((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T)
|
|
|| ((uri.user_param_val.len==5) && (memcmp(uri.user_param_val.s, "phone", 5)==0)))
|
|
) {
|
|
ret=1;
|
|
break;
|
|
}
|
|
/* SET_PORT_T does not work with tel: URIs */
|
|
if ((a->type==SET_PORT_T)
|
|
&& ((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T))
|
|
&& ((uri.flags & URI_SIP_USER_PHONE)==0)
|
|
) {
|
|
LM_ERR("port number of a tel: URI cannot be set\n");
|
|
ret=E_UNSPEC;
|
|
goto error;
|
|
}
|
|
|
|
new_uri=pkg_malloc(MAX_URI_SIZE);
|
|
if (new_uri==0){
|
|
LM_ERR("memory allocation failure\n");
|
|
ret=E_OUT_OF_MEM;
|
|
goto error;
|
|
}
|
|
end=new_uri+MAX_URI_SIZE;
|
|
crt=new_uri;
|
|
/* begin copying */
|
|
/* Preserve the URI scheme unless the host part needs
|
|
* to be rewritten, and the shceme is tel: or tels: */
|
|
switch (uri.type) {
|
|
case SIP_URI_T:
|
|
len=s_sip.len;
|
|
tmp=s_sip.s;
|
|
break;
|
|
|
|
case SIPS_URI_T:
|
|
len=s_sips.len;
|
|
tmp=s_sips.s;
|
|
break;
|
|
|
|
case TEL_URI_T:
|
|
if ((uri.flags & URI_SIP_USER_PHONE)
|
|
|| (a->type==SET_HOST_T)
|
|
|| (a->type==SET_HOSTPORT_T)
|
|
|| (a->type==SET_HOSTPORTTRANS_T)
|
|
) {
|
|
len=s_sip.len;
|
|
tmp=s_sip.s;
|
|
break;
|
|
}
|
|
len=s_tel.len;
|
|
tmp=s_tel.s;
|
|
break;
|
|
|
|
case TELS_URI_T:
|
|
if ((uri.flags & URI_SIP_USER_PHONE)
|
|
|| (a->type==SET_HOST_T)
|
|
|| (a->type==SET_HOSTPORT_T)
|
|
|| (a->type==SET_HOSTPORTTRANS_T)
|
|
) {
|
|
len=s_sips.len;
|
|
tmp=s_sips.s;
|
|
break;
|
|
}
|
|
len=s_tels.len;
|
|
tmp=s_tels.s;
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("Unsupported URI scheme (%d), reverted to sip:\n",
|
|
uri.type);
|
|
len=s_sip.len;
|
|
tmp=s_sip.s;
|
|
}
|
|
if(crt+len+1 /* colon */ >end) goto error_uri;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
*crt=':'; crt++;
|
|
|
|
/* user */
|
|
|
|
/* prefix (-jiri) */
|
|
if (a->type==PREFIX_T) {
|
|
tmp=a->val[0].u.string;
|
|
len=strlen(tmp); if(crt+len>end) goto error_uri;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
/* whatever we had before, with prefix we have username
|
|
now */
|
|
user=1;
|
|
}
|
|
|
|
if ((a->type==SET_USER_T)||(a->type==SET_USERPASS_T)) {
|
|
tmp=a->val[0].u.string;
|
|
len=strlen(tmp);
|
|
} else if (a->type==STRIP_T) {
|
|
if (a->val[0].u.number>uri.user.len) {
|
|
LM_WARN("too long strip asked; deleting username: %lu of <%.*s>\n",
|
|
a->val[0].u.number, uri.user.len, uri.user.s );
|
|
len=0;
|
|
} else if (a->val[0].u.number==uri.user.len) {
|
|
len=0;
|
|
} else {
|
|
tmp=uri.user.s + a->val[0].u.number;
|
|
len=uri.user.len - a->val[0].u.number;
|
|
}
|
|
} else if (a->type==STRIP_TAIL_T) {
|
|
if (a->val[0].u.number>uri.user.len) {
|
|
LM_WARN("too long strip_tail asked; "
|
|
" deleting username: %lu of <%.*s>\n",
|
|
a->val[0].u.number, uri.user.len, uri.user.s );
|
|
len=0;
|
|
} else if (a->val[0].u.number==uri.user.len) {
|
|
len=0;
|
|
} else {
|
|
tmp=uri.user.s;
|
|
len=uri.user.len - a->val[0].u.number;
|
|
}
|
|
} else {
|
|
tmp=uri.user.s;
|
|
len=uri.user.len;
|
|
}
|
|
|
|
if (len){
|
|
if(crt+len>end) goto error_uri;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
user=1; /* we have an user field so mark it */
|
|
}
|
|
|
|
if (a->type==SET_USERPASS_T) tmp=0;
|
|
else tmp=uri.passwd.s;
|
|
/* passwd - keep it only if user is set */
|
|
if (user && tmp){
|
|
len=uri.passwd.len; if(crt+len+1>end) goto error_uri;
|
|
*crt=':'; crt++;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
/* tel: URI parameters */
|
|
if ((uri.type==TEL_URI_T)
|
|
|| (uri.type==TELS_URI_T)
|
|
) {
|
|
tmp=uri.params.s;
|
|
if (tmp){
|
|
len=uri.params.len; if(crt+len+1>end) goto error_uri;
|
|
*crt=';'; crt++;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
}
|
|
/* host */
|
|
if ((a->type==SET_HOST_T)
|
|
|| (a->type==SET_HOSTPORT_T)
|
|
|| (a->type==SET_HOSTALL_T)
|
|
|| (a->type==SET_HOSTPORTTRANS_T)
|
|
) {
|
|
tmp=a->val[0].u.string;
|
|
if (tmp) len = strlen(tmp);
|
|
else len=0;
|
|
} else if ((uri.type==SIP_URI_T)
|
|
|| (uri.type==SIPS_URI_T)
|
|
|| (uri.flags & URI_SIP_USER_PHONE)
|
|
) {
|
|
tmp=uri.host.s;
|
|
len=uri.host.len;
|
|
} else {
|
|
tmp=0;
|
|
}
|
|
if (tmp){
|
|
if (user) { /* add @ */
|
|
if(crt+1>end) goto error_uri;
|
|
*crt='@'; crt++;
|
|
}
|
|
if(crt+len>end) goto error_uri;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
if(a->type==SET_HOSTALL_T)
|
|
goto done_seturi;
|
|
/* port */
|
|
if ((a->type==SET_HOSTPORT_T)
|
|
|| (a->type==SET_HOSTPORTTRANS_T))
|
|
tmp=0;
|
|
else if (a->type==SET_PORT_T) {
|
|
tmp=a->val[0].u.string;
|
|
if (tmp) {
|
|
len = strlen(tmp);
|
|
if(len==0) tmp = 0;
|
|
} else len = 0;
|
|
} else {
|
|
tmp=uri.port.s;
|
|
len = uri.port.len;
|
|
}
|
|
if (tmp){
|
|
if(crt+len+1>end) goto error_uri;
|
|
*crt=':'; crt++;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
/* params */
|
|
if ((a->type==SET_HOSTPORTTRANS_T)
|
|
&& uri.sip_params.s
|
|
&& uri.transport.s
|
|
) {
|
|
/* bypass the transport parameter */
|
|
if (uri.sip_params.s < uri.transport.s) {
|
|
/* there are parameters before transport */
|
|
len = uri.transport.s - uri.sip_params.s - 1;
|
|
/* ignore the ';' at the end */
|
|
if (crt+len+1>end) goto error_uri;
|
|
*crt=';'; crt++;
|
|
memcpy(crt,uri.sip_params.s,len);crt+=len;
|
|
}
|
|
len = (uri.sip_params.s + uri.sip_params.len) -
|
|
(uri.transport.s + uri.transport.len);
|
|
if (len > 0) {
|
|
/* there are parameters after transport */
|
|
if (crt+len>end) goto error_uri;
|
|
tmp = uri.transport.s + uri.transport.len;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
} else {
|
|
tmp=uri.sip_params.s;
|
|
if (tmp){
|
|
len=uri.sip_params.len; if(crt+len+1>end) goto error_uri;
|
|
*crt=';'; crt++;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
}
|
|
/* Add the user=phone param if a tel: or tels:
|
|
* URI was converted to sip: or sips:.
|
|
* (host part of a tel/tels URI was set.)
|
|
* Or in case of sip: URI and SET_USERPHONE_T action */
|
|
if (((((uri.type==TEL_URI_T) || (uri.type==TELS_URI_T))
|
|
&& ((uri.flags & URI_SIP_USER_PHONE)==0))
|
|
&& ((a->type==SET_HOST_T)
|
|
|| (a->type==SET_HOSTPORT_T)
|
|
|| (a->type==SET_HOSTPORTTRANS_T)))
|
|
|| (a->type==SET_USERPHONE_T)
|
|
) {
|
|
tmp=";user=phone";
|
|
len=strlen(tmp);
|
|
if(crt+len>end) goto error_uri;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
/* headers */
|
|
tmp=uri.headers.s;
|
|
if (tmp){
|
|
len=uri.headers.len; if(crt+len+1>end) goto error_uri;
|
|
*crt='?'; crt++;
|
|
memcpy(crt,tmp,len);crt+=len;
|
|
}
|
|
done_seturi:
|
|
*crt=0; /* null terminate the thing */
|
|
/* copy it to the msg */
|
|
if (msg->new_uri.s) pkg_free(msg->new_uri.s);
|
|
msg->new_uri.s=new_uri;
|
|
msg->new_uri.len=crt-new_uri;
|
|
msg->parsed_uri_ok=0;
|
|
ruri_mark_new(); /* available for forking */
|
|
ret=1;
|
|
break;
|
|
case IF_T:
|
|
rve=(struct rval_expr*)a->val[0].u.data;
|
|
if (unlikely(rval_expr_eval_int(h, msg, &v, rve) != 0)){
|
|
ERR("if expression evaluation failed (%d,%d-%d,%d)\n",
|
|
rve->fpos.s_line, rve->fpos.s_col,
|
|
rve->fpos.e_line, rve->fpos.e_col);
|
|
v=0; /* false */
|
|
}
|
|
if (unlikely(h->run_flags & EXIT_R_F)){
|
|
ret=0;
|
|
break;
|
|
}
|
|
h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
|
|
break in expr*/
|
|
ret=1; /*default is continue */
|
|
if (v>0) {
|
|
if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
|
|
ret=run_actions(h,
|
|
(struct action*)a->val[1].u.data, msg);
|
|
}
|
|
}else if ((a->val[2].type==ACTIONS_ST)&&a->val[2].u.data){
|
|
ret=run_actions(h,
|
|
(struct action*)a->val[2].u.data, msg);
|
|
}
|
|
break;
|
|
case MODULE0_T:
|
|
MODF_CALL(cmd_function, h, msg, a->val, 0, 0);
|
|
break;
|
|
/* instead of using the parameter number, we use different names
|
|
* for calls to functions with 3, 4, 5, 6 or variable number of
|
|
* parameters due to performance reasons */
|
|
case MODULE1_T:
|
|
MODF_CALL(cmd_function, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
0
|
|
);
|
|
break;
|
|
case MODULE2_T:
|
|
MODF_CALL(cmd_function, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
(char*)a->val[3].u.data
|
|
);
|
|
break;
|
|
case MODULE3_T:
|
|
MODF_CALL(cmd_function3, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
(char*)a->val[3].u.data,
|
|
(char*)a->val[4].u.data
|
|
);
|
|
break;
|
|
case MODULE4_T:
|
|
MODF_CALL(cmd_function4, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
(char*)a->val[3].u.data,
|
|
(char*)a->val[4].u.data,
|
|
(char*)a->val[5].u.data
|
|
);
|
|
break;
|
|
case MODULE5_T:
|
|
MODF_CALL(cmd_function5, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
(char*)a->val[3].u.data,
|
|
(char*)a->val[4].u.data,
|
|
(char*)a->val[5].u.data,
|
|
(char*)a->val[6].u.data
|
|
);
|
|
break;
|
|
case MODULE6_T:
|
|
MODF_CALL(cmd_function6, h, msg, a->val,
|
|
(char*)a->val[2].u.data,
|
|
(char*)a->val[3].u.data,
|
|
(char*)a->val[4].u.data,
|
|
(char*)a->val[5].u.data,
|
|
(char*)a->val[6].u.data,
|
|
(char*)a->val[7].u.data
|
|
);
|
|
break;
|
|
case MODULEX_T:
|
|
MODF_CALL(cmd_function_var, h, msg, a->val,
|
|
a->val[1].u.number, &a->val[2]);
|
|
break;
|
|
case MODULE1_RVE_T:
|
|
MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
0
|
|
);
|
|
break;
|
|
case MODULE2_RVE_T:
|
|
MODF_RVE_CALL(cmd_function, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
(char*)mod_f_params[3].u.data
|
|
);
|
|
break;
|
|
case MODULE3_RVE_T:
|
|
MODF_RVE_CALL(cmd_function3, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
(char*)mod_f_params[3].u.data,
|
|
(char*)mod_f_params[4].u.data
|
|
);
|
|
break;
|
|
case MODULE4_RVE_T:
|
|
MODF_RVE_CALL(cmd_function4, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
(char*)mod_f_params[3].u.data,
|
|
(char*)mod_f_params[4].u.data,
|
|
(char*)mod_f_params[5].u.data
|
|
);
|
|
break;
|
|
case MODULE5_RVE_T:
|
|
MODF_RVE_CALL(cmd_function5, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
(char*)mod_f_params[3].u.data,
|
|
(char*)mod_f_params[4].u.data,
|
|
(char*)mod_f_params[5].u.data,
|
|
(char*)mod_f_params[6].u.data
|
|
);
|
|
break;
|
|
case MODULE6_RVE_T:
|
|
MODF_RVE_CALL(cmd_function6, h, msg, a->val, mod_f_params,
|
|
(char*)mod_f_params[2].u.data,
|
|
(char*)mod_f_params[3].u.data,
|
|
(char*)mod_f_params[4].u.data,
|
|
(char*)mod_f_params[5].u.data,
|
|
(char*)mod_f_params[6].u.data,
|
|
(char*)mod_f_params[7].u.data
|
|
);
|
|
break;
|
|
case MODULEX_RVE_T:
|
|
MODF_RVE_CALL(cmd_function_var, h, msg, a->val, mod_f_params,
|
|
a->val[1].u.number, &mod_f_params[2]);
|
|
break;
|
|
case EVAL_T:
|
|
/* only eval the expression to account for possible
|
|
side-effect */
|
|
rval_expr_eval_int(h, msg, &v,
|
|
(struct rval_expr*)a->val[0].u.data);
|
|
if (h->run_flags & EXIT_R_F){
|
|
ret=0;
|
|
break;
|
|
}
|
|
h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
|
|
expr */
|
|
ret=1; /* default is continue */
|
|
break;
|
|
case SWITCH_COND_T:
|
|
sct=(struct switch_cond_table*)a->val[1].u.data;
|
|
if (unlikely( rval_expr_eval_int(h, msg, &v,
|
|
(struct rval_expr*)a->val[0].u.data) <0)){
|
|
/* handle error in expression => use default */
|
|
ret=-1;
|
|
goto sw_cond_def;
|
|
}
|
|
if (h->run_flags & EXIT_R_F){
|
|
ret=0;
|
|
break;
|
|
}
|
|
h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
|
|
in expr */
|
|
ret=1; /* default is continue */
|
|
for(i=0; i<sct->n; i++)
|
|
if (sct->cond[i]==v){
|
|
if (likely(sct->jump[i])){
|
|
ret=run_actions(h, sct->jump[i], msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
}
|
|
goto skip;
|
|
}
|
|
sw_cond_def:
|
|
if (sct->def){
|
|
ret=run_actions(h, sct->def, msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
}
|
|
break;
|
|
case SWITCH_JT_T:
|
|
sjt=(struct switch_jmp_table*)a->val[1].u.data;
|
|
if (unlikely( rval_expr_eval_int(h, msg, &v,
|
|
(struct rval_expr*)a->val[0].u.data) <0)){
|
|
/* handle error in expression => use default */
|
|
ret=-1;
|
|
goto sw_jt_def;
|
|
}
|
|
if (h->run_flags & EXIT_R_F){
|
|
ret=0;
|
|
break;
|
|
}
|
|
h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
|
|
in expr */
|
|
ret=1; /* default is continue */
|
|
if (likely(v >= sjt->first && v <= sjt->last)){
|
|
if (likely(sjt->tbl[v - sjt->first])){
|
|
ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
}
|
|
break;
|
|
}else{
|
|
for(i=0; i<sjt->rest.n; i++)
|
|
if (sjt->rest.cond[i]==v){
|
|
if (likely(sjt->rest.jump[i])){
|
|
ret=run_actions(h, sjt->rest.jump[i], msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but
|
|
let returns pass */
|
|
}
|
|
goto skip;
|
|
}
|
|
}
|
|
/* not found => try default */
|
|
sw_jt_def:
|
|
if (sjt->rest.def){
|
|
ret=run_actions(h, sjt->rest.def, msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
}
|
|
break;
|
|
case BLOCK_T:
|
|
if (likely(a->val[0].u.data)){
|
|
ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
}
|
|
break;
|
|
case MATCH_COND_T:
|
|
mct=(struct match_cond_table*)a->val[1].u.data;
|
|
rval_cache_init(&c1);
|
|
rv=0;
|
|
rv1=0;
|
|
ret=rval_expr_eval_rvint(h, msg, &rv, &v,
|
|
(struct rval_expr*)a->val[0].u.data, &c1);
|
|
|
|
if (unlikely( ret<0)){
|
|
/* handle error in expression => use default */
|
|
ret=-1;
|
|
goto match_cond_def;
|
|
}
|
|
if (h->run_flags & EXIT_R_F){
|
|
ret=0;
|
|
break;
|
|
}
|
|
h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
|
|
in expr */
|
|
if (likely(rv)){
|
|
rv1=rval_convert(h, msg, RV_STR, rv, &c1);
|
|
if (unlikely(rv1==0)){
|
|
ret=-1;
|
|
goto match_cond_def;
|
|
}
|
|
s=rv1->v.s;
|
|
}else{
|
|
/* int result in v */
|
|
rval_cache_clean(&c1);
|
|
s.s=sint2str(v, &s.len);
|
|
}
|
|
ret=1; /* default is continue */
|
|
for(i=0; i<mct->n; i++)
|
|
if (( mct->match[i].type==MATCH_STR &&
|
|
mct->match[i].l.s.len==s.len &&
|
|
memcmp(mct->match[i].l.s.s, s.s, s.len) == 0 ) ||
|
|
( mct->match[i].type==MATCH_RE &&
|
|
regexec(mct->match[i].l.regex, s.s, 0, 0, 0) == 0)
|
|
){
|
|
if (likely(mct->jump[i])){
|
|
/* make sure we cleanup first, in case run_actions()
|
|
exits the script directly via longjmp() */
|
|
if (rv1){
|
|
rval_destroy(rv1);
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}else if (rv){
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}
|
|
ret=run_actions(h, mct->jump[i], msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
goto skip;
|
|
}
|
|
goto match_cleanup;
|
|
}
|
|
match_cond_def:
|
|
if (mct->def){
|
|
/* make sure we cleanup first, in case run_actions()
|
|
exits the script directly via longjmp() */
|
|
if (rv1){
|
|
rval_destroy(rv1);
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}else if (rv){
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}
|
|
ret=run_actions(h, mct->def, msg);
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns passthrough */
|
|
break;
|
|
}
|
|
match_cleanup:
|
|
if (rv1){
|
|
rval_destroy(rv1);
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}else if (rv){
|
|
rval_destroy(rv);
|
|
rval_cache_clean(&c1);
|
|
}
|
|
break;
|
|
case WHILE_T:
|
|
i=0;
|
|
flags=0;
|
|
rve=(struct rval_expr*)a->val[0].u.data;
|
|
ret=1;
|
|
while(!(flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)) &&
|
|
(rval_expr_eval_int(h, msg, &v, rve) == 0) && v){
|
|
if (cfg_get(core, core_cfg, max_while_loops) > 0)
|
|
i++;
|
|
|
|
if (unlikely(i > cfg_get(core, core_cfg, max_while_loops))){
|
|
LM_ERR("runaway while (%d, %d): more then %d loops\n",
|
|
rve->fpos.s_line, rve->fpos.s_col,
|
|
cfg_get(core, core_cfg, max_while_loops));
|
|
ret=-1;
|
|
goto error;
|
|
}
|
|
if (likely(a->val[1].u.data)){
|
|
ret=run_actions(h, (struct action*)a->val[1].u.data, msg);
|
|
flags|=h->run_flags;
|
|
h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
|
|
returns pass-through */
|
|
}
|
|
}
|
|
break;
|
|
case FORCE_RPORT_T:
|
|
msg->msg_flags|=FL_FORCE_RPORT;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case ADD_LOCAL_RPORT_T:
|
|
msg->msg_flags|=FL_ADD_LOCAL_RPORT;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case UDP_MTU_TRY_PROTO_T:
|
|
msg->msg_flags|= (unsigned int)a->val[0].u.number & FL_MTU_FB_MASK;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case SET_ADV_ADDR_T:
|
|
if (a->val[0].type!=STR_ST){
|
|
LM_CRIT("bad set_advertised_address() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
msg->set_global_address=*((str*)a->val[0].u.data);
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case SET_ADV_PORT_T:
|
|
if (a->val[0].type!=STR_ST){
|
|
LM_CRIT("bad set_advertised_port() type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
msg->set_global_port=*((str*)a->val[0].u.data);
|
|
ret=1; /* continue processing */
|
|
break;
|
|
#ifdef USE_TCP
|
|
case FORCE_TCP_ALIAS_T:
|
|
if ( msg->rcv.proto==PROTO_TCP
|
|
#ifdef USE_TLS
|
|
|| msg->rcv.proto==PROTO_TLS
|
|
#endif
|
|
){
|
|
|
|
if (a->val[0].type==NOSUBTYPE) port=msg->via1->port;
|
|
else if (a->val[0].type==NUMBER_ST) port=(int)a->val[0].u.number;
|
|
else{
|
|
LM_CRIT("bad force_tcp_alias"
|
|
" port type %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
|
|
if (tcpconn_add_alias(msg->rcv.proto_reserved1, port,
|
|
msg->rcv.proto)!=0){
|
|
LM_ERR("receive_msg: tcp alias failed\n");
|
|
ret=E_UNSPEC;
|
|
goto error;
|
|
}
|
|
}
|
|
#endif
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case FORCE_SEND_SOCKET_T:
|
|
if (a->val[0].type!=SOCKETINFO_ST){
|
|
LM_CRIT("bad force_send_socket argument"
|
|
" type: %d\n", a->val[0].type);
|
|
ret=E_BUG;
|
|
goto error;
|
|
}
|
|
set_force_socket(msg, (struct socket_info*)a->val[0].u.data);
|
|
ret=1; /* continue processing */
|
|
break;
|
|
|
|
case ADD_T:
|
|
case ASSIGN_T:
|
|
v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
|
|
(struct rval_expr*)a->val[1].u.data);
|
|
if (likely(v>=0))
|
|
ret = 1;
|
|
else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
|
|
ret=0;
|
|
else
|
|
ret=v;
|
|
break;
|
|
case SET_FWD_NO_CONNECT_T:
|
|
msg->fwd_send_flags.f|= SND_F_FORCE_CON_REUSE;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case SET_RPL_NO_CONNECT_T:
|
|
msg->rpl_send_flags.f|= SND_F_FORCE_CON_REUSE;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case SET_FWD_CLOSE_T:
|
|
msg->fwd_send_flags.f|= SND_F_CON_CLOSE;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case SET_RPL_CLOSE_T:
|
|
msg->rpl_send_flags.f|= SND_F_CON_CLOSE;
|
|
ret=1; /* continue processing */
|
|
break;
|
|
case CFG_SELECT_T:
|
|
if (a->val[0].type != CFG_GROUP_ST) {
|
|
BUG("unsupported parameter in CFG_SELECT_T: %d\n",
|
|
a->val[0].type);
|
|
ret=-1;
|
|
goto error;
|
|
}
|
|
switch(a->val[1].type) {
|
|
case NUMBER_ST:
|
|
v=(int)a->val[1].u.number;
|
|
break;
|
|
case RVE_ST:
|
|
if (rval_expr_eval_int(h, msg, &v, (struct rval_expr*)a->val[1].u.data) < 0) {
|
|
ret=-1;
|
|
goto error;
|
|
}
|
|
break;
|
|
default:
|
|
BUG("unsupported group id type in CFG_SELECT_T: %d\n",
|
|
a->val[1].type);
|
|
ret=-1;
|
|
goto error;
|
|
}
|
|
ret=(cfg_select((cfg_group_t*)a->val[0].u.data, v) == 0) ? 1 : -1;
|
|
break;
|
|
case CFG_RESET_T:
|
|
if (a->val[0].type != CFG_GROUP_ST) {
|
|
BUG("unsupported parameter in CFG_RESET_T: %d\n",
|
|
a->val[0].type);
|
|
ret=-1;
|
|
goto error;
|
|
}
|
|
ret=(cfg_reset((cfg_group_t*)a->val[0].u.data) == 0) ? 1 : -1;
|
|
break;
|
|
/*
|
|
default:
|
|
LM_CRIT("unknown type %d\n", a->type);
|
|
*/
|
|
}
|
|
skip:
|
|
return ret;
|
|
|
|
error_uri:
|
|
LM_ERR("set*: uri too long\n");
|
|
if (new_uri) pkg_free(new_uri);
|
|
LM_ERR("run action error at: %s:%d\n", (a->cfile)?a->cfile:"", a->cline);
|
|
return E_UNSPEC;
|
|
error_fwd_uri:
|
|
/*free_uri(&uri); -- not needed anymore, using msg->parsed_uri*/
|
|
error:
|
|
LM_ERR("run action error at: %s:%d\n", (a->cfile)?a->cfile:"", a->cline);
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/* returns: 0, or 1 on success, <0 on error */
|
|
/* (0 if drop or break encountered, 1 if not ) */
|
|
int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
|
|
{
|
|
struct action* t;
|
|
int ret;
|
|
struct sr_module *mod;
|
|
unsigned int ms = 0;
|
|
|
|
ret=E_UNSPEC;
|
|
h->rec_lev++;
|
|
if (unlikely(h->rec_lev>max_recursive_level)){
|
|
LM_ERR("too many recursive routing table lookups (%d) giving up!\n", h->rec_lev);
|
|
ret=E_UNSPEC;
|
|
goto error;
|
|
}
|
|
if (unlikely(h->rec_lev==1)){
|
|
h->run_flags=0;
|
|
h->last_retcode=0;
|
|
_last_returned_code = h->last_retcode;
|
|
#ifdef USE_LONGJMP
|
|
if (unlikely(setjmp(h->jmp_env))){
|
|
h->rec_lev=0;
|
|
ret=h->last_retcode;
|
|
goto end;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (unlikely(a==0)){
|
|
LM_DBG("null action list (rec_level=%d)\n", h->rec_lev);
|
|
ret=1;
|
|
}
|
|
|
|
for (t=a; t!=0; t=t->next){
|
|
if(unlikely(cfg_get(core, core_cfg, latency_limit_action)>0))
|
|
ms = TICKS_TO_MS(get_ticks_raw());
|
|
_cfg_crt_action = t;
|
|
ret=do_action(h, t, msg);
|
|
_cfg_crt_action = 0;
|
|
if(unlikely(cfg_get(core, core_cfg, latency_limit_action)>0)) {
|
|
ms = TICKS_TO_MS(get_ticks_raw()) - ms;
|
|
if(ms >= cfg_get(core, core_cfg, latency_limit_action)) {
|
|
LOG(cfg_get(core, core_cfg, latency_log),
|
|
"alert - action [%s (%d)]"
|
|
" cfg [%s:%d] took too long [%u ms]\n",
|
|
is_mod_func(t) ?
|
|
((cmd_export_common_t*)(t->val[0].u.data))->name
|
|
: "corefunc",
|
|
t->type, (t->cfile)?t->cfile:"", t->cline, ms);
|
|
}
|
|
}
|
|
/* break, return or drop/exit stop execution of the current
|
|
block */
|
|
if (unlikely(h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F))){
|
|
if (unlikely(h->run_flags & EXIT_R_F)) {
|
|
h->last_retcode=ret;
|
|
_last_returned_code = h->last_retcode;
|
|
#ifdef USE_LONGJMP
|
|
longjmp(h->jmp_env, ret);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
/* ignore error returns */
|
|
}
|
|
|
|
h->rec_lev--;
|
|
end:
|
|
/* process module onbreak handlers if present */
|
|
if (unlikely(h->rec_lev==0 && ret==0 &&
|
|
!(h->run_flags & IGNORE_ON_BREAK_R_F)))
|
|
for (mod=modules;mod;mod=mod->next)
|
|
if (unlikely(mod->exports.onbreak_f)) {
|
|
mod->exports.onbreak_f( msg );
|
|
}
|
|
return ret;
|
|
|
|
|
|
error:
|
|
h->rec_lev--;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_LONGJMP
|
|
/** safe version of run_actions().
|
|
* It always return (it doesn't longjmp on forced script end).
|
|
* @returns 0, or 1 on success, <0 on error
|
|
* (0 if drop or break encountered, 1 if not ) */
|
|
int run_actions_safe(struct run_act_ctx* h, struct action* a,
|
|
struct sip_msg* msg)
|
|
{
|
|
struct run_act_ctx ctx;
|
|
int ret;
|
|
int ign_on_break;
|
|
|
|
/* start with a fresh action context */
|
|
init_run_actions_ctx(&ctx);
|
|
ctx.last_retcode = h->last_retcode;
|
|
ign_on_break = h->run_flags & IGNORE_ON_BREAK_R_F;
|
|
ctx.run_flags = h->run_flags | IGNORE_ON_BREAK_R_F;
|
|
ret = run_actions(&ctx, a, msg);
|
|
h->last_retcode = ctx.last_retcode;
|
|
h->run_flags = (ctx.run_flags & ~IGNORE_ON_BREAK_R_F) | ign_on_break;
|
|
return ret;
|
|
}
|
|
#endif /* USE_LONGJMP */
|
|
|
|
|
|
|
|
int run_top_route(struct action* a, sip_msg_t* msg, struct run_act_ctx *c)
|
|
{
|
|
struct run_act_ctx ctx;
|
|
struct run_act_ctx *p;
|
|
int ret;
|
|
flag_t sfbk;
|
|
|
|
p = (c)?c:&ctx;
|
|
sfbk = getsflags();
|
|
setsflagsval(0);
|
|
reset_static_buffer();
|
|
init_run_actions_ctx(p);
|
|
ret = run_actions(p, a, msg);
|
|
setsflagsval(sfbk);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int run_child_one_init_route(void)
|
|
{
|
|
struct sip_msg *fmsg;
|
|
struct run_act_ctx ctx;
|
|
int rtb, rt;
|
|
|
|
LM_DBG("attempting to run event_route[core:worker-one-init]\n");
|
|
|
|
rt = route_get(&event_rt, "core:worker-one-init");
|
|
if(rt>=0 && event_rt.rlist[rt]!=NULL) {
|
|
LM_DBG("executing event_route[core:worker-one-init] (%d)\n", rt);
|
|
if(faked_msg_init()<0)
|
|
return -1;
|
|
fmsg = faked_msg_next();
|
|
rtb = get_route_type();
|
|
set_route_type(REQUEST_ROUTE);
|
|
init_run_actions_ctx(&ctx);
|
|
run_top_route(event_rt.rlist[rt], fmsg, &ctx);
|
|
if(ctx.run_flags&DROP_R_F)
|
|
{
|
|
LM_ERR("exit due to 'drop' in event route\n");
|
|
return -1;
|
|
}
|
|
set_route_type(rtb);
|
|
}
|
|
|
|
return 0;
|
|
}
|