/* * * 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 #include #include #include #include #include #include #ifdef DEBUG_DMALLOC #include #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, "