/* * $Id$ * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser 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 * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * --------- * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-01-29 removed scratchpad (jiri) * 2003-03-19 fixed set* len calculation bug & simplified a little the code * (should be a little faster now) (andrei) * replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 Added support for loose routing in forward (janakj) * 2003-04-12 FORCE_RPORT_T added (andrei) * 2003-04-22 strip_tail added (jiri) * 2003-10-02 added SET_ADV_ADDR_T & SET_ADV_PORT_T (andrei) * 2003-10-29 added FORCE_TCP_ALIAS_T (andrei) * 2004-11-30 added FORCE_SEND_SOCKET_T (andrei) * 2005-12-12 return & drop/exit differentiation (andrei) * 2005-12-19 select framework (mma) * 2006-04-12 updated *_send() calls to use a struct dest_info (andrei) * 2006-07-27 dns cache and dns based send address failover support (andrei) * 2006-12-06 on popular request last_retcode set also by module functions * (andrei) * 2007-06-14 run_actions & do_action need a ctx or handle now, no more * static vars (andrei) * 2008-11-18 support for variable parameter module functions (andrei) * 2008-12-03 use lvalues/rvalues for assignments (andrei) * 2008-12-17 added UDP_MTU_TRY_PROTO_T (andrei) * 2009-05-04 switched IF_T to rval_expr (andrei) * 2009-09-15 added SET_{FWD,RPL}_NO_CONNECT, SET_{FWD,RPL}_CLOSE (andrei) * 2010-06-01 special hack/support for fparam fixups so that they can handle * variable RVEs (andrei) */ /*! * \file * \brief SIP-router core :: * \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 "resolve.h" #ifdef USE_TCP #include "tcp_server.h" #endif #ifdef USE_SCTP #include "sctp_server.h" #endif #include "switch.h" #include "events.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 */ /* 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) */ \ 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), __VAR_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), __VAR_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; 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) { LOG(L_ERR, "ERROR: do_action: 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: LOG(L_CRIT, "BUG: do_action 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: #endif #ifdef USE_TLS case PROTO_TLS: #endif #ifdef USE_SCTP case PROTO_SCTP: #endif dst.proto=u->proto; break; default: LOG(L_ERR,"ERROR: do action: 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){ LOG(L_ERR, "ERROR: do_action: forward: secure uri" " incompatible with transport %d\n", u->proto); ret=E_BAD_PROTO; goto error_fwd_uri; } dst.proto=PROTO_TLS; } #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{ LOG(L_CRIT, "BUG: do_action: bad forward() types %d, %d\n", a->val[0].type, a->val[1].type); ret=E_BUG; goto error; } break; case SEND_T: case SEND_TCP_T: if ((a->val[0].type!= PROXY_ST)|(a->val[1].type!=NUMBER_ST)){ LOG(L_CRIT, "BUG: do_action: bad send() types %d, %d\n", a->val[0].type, a->val[1].type); ret=E_BUG; goto error; } /* init dst */ init_dest_info(&dst); ret=proxy2su(&dst.to, (struct proxy_l*)a->val[0].u.data); if (ret==0){ if (p_onsend){ tmp=p_onsend->buf; len=p_onsend->len; }else{ tmp=msg->buf; len=msg->len; } if (a->type==SEND_T){ /*udp*/ dst.proto=PROTO_UDP; /* not really needed for udp_send */ dst.send_sock=get_send_socket(msg, &dst.to, PROTO_UDP); if (dst.send_sock!=0){ ret=udp_send(&dst, tmp, len); }else{ ret=-1; } } #ifdef USE_TCP else{ /*tcp*/ dst.proto=PROTO_TCP; dst.id=0; ret=tcp_send(&dst, 0, tmp, len); } #endif }else{ ret=E_BUG; goto error; } proxy_mark((struct proxy_l*)a->val[0].u.data, ret); if (ret>=0) ret=1; break; case LOG_T: if ((a->val[0].type!=NUMBER_ST)|(a->val[1].type!=STRING_ST)){ LOG(L_CRIT, "BUG: do_action: 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, "