core: added support for WS keyword for use in conditionals with proto and snd_proto
[sip-router] / route.c
diff --git a/route.c b/route.c
index 5587f12..ca357d3 100644 (file)
--- a/route.c
+++ b/route.c
  *  2008-04-23  errors are treated as false during expression evaluation
  *             unless the operator is DIFF_OP (Miklos)
  *  2008-12-03  fixups for rvalues in assignments (andrei)
+ *  2009-05-04  switched IF_T to rval_expr (andrei)
+ *  2010-06-01  special hack/support for fparam fixups so that they can handle
+ *               variable RVEs (andrei)
+ *  2010-06-18  ip comparison (comp_ip()) normalizes strings to
+ *              ip/netmask  (andrei)
  */
 
 
+/** expression evaluation, route fixups and routing lists.
+ * @file route.c
+ * @ingroup core
+ * Module: @ref core
+ */
+
 #include <stdlib.h>
 #include <sys/types.h>
 #include <regex.h>
@@ -83,6 +94,7 @@
 #include "ut.h"
 #include "rvalue.h"
 #include "switch.h"
+#include "cfg/cfg_struct.h"
 
 #define RT_HASH_SIZE   8 /* route names hash */
 
@@ -92,7 +104,9 @@ struct route_list onreply_rt;
 struct route_list failure_rt;
 struct route_list branch_rt;
 struct route_list onsend_rt;
+struct route_list event_rt;
 
+int route_type = REQUEST_ROUTE;
 
 /** script optimization level, useful for debugging.
  *  0 - no optimization
@@ -129,6 +143,7 @@ void destroy_routes()
        destroy_rlist(&onreply_rt);
        destroy_rlist(&failure_rt);
        destroy_rlist(&branch_rt);
+       destroy_rlist(&event_rt);
 }
 
 
@@ -199,6 +214,8 @@ int init_routes()
                goto error;
        if (init_rlist("on_send", &onsend_rt, ONSEND_RT_NO, RT_HASH_SIZE)<0)
                goto error;
+       if (init_rlist("event", &event_rt, EVENT_RT_NO, RT_HASH_SIZE)<0)
+               goto error;
        return 0;
 error:
        destroy_routes();
@@ -429,6 +446,7 @@ static int exp_optimize_right(struct expr* exp)
                                        if (exp->r.str.s){
                                                exp->r.str.len=rval->v.s.len;
                                                memcpy(exp->r.str.s, rval->v.s.s, rval->v.s.len);
+                                               exp->r.str.s[exp->r.str.len]=0;
                                                exp->r_type=STRING_ST;
                                                rval_destroy(rval);
                                                pkg_free(rve);
@@ -497,6 +515,7 @@ int fix_expr(struct expr* exp)
 {
        regex_t* re;
        int ret;
+       int len;
 
        ret=E_BUG;
        if (exp==0){
@@ -519,6 +538,35 @@ int fix_expr(struct expr* exp)
                                                                exp->op);
                }
        }else if (exp->type==ELEM_T){
+                       /* first calculate lengths of strings  (only right side, since 
+                         left side can never be a string) */
+                       if (exp->r_type==STRING_ST) {
+                               if (exp->r.string) len = strlen(exp->r.string);
+                               else len = 0;
+                               exp->r.str.s = exp->r.string;
+                               exp->r.str.len = len;
+                       }
+                       /* then fix & optimize rve/rvals (they might be optimized
+                          to non-rvals, e.g. string, avp a.s.o and needs to be done
+                          before MATCH_OP and other fixups) */
+                       if (exp->l_type==RVEXP_O){
+                               if ((ret=fix_rval_expr(exp->l.param))<0){
+                                       ERR("Unable to fix left rval expression\n");
+                                       return ret;
+                               }
+                               if (scr_opt_lev>=2)
+                                       exp_optimize_left(exp);
+                       }
+                       if (exp->r_type==RVE_ST){
+                               if ((ret=fix_rval_expr(exp->r.param))<0){
+                                       ERR("Unable to fix right rval expression\n");
+                                       return ret;
+                               }
+                               if (scr_opt_lev>=2)
+                                       exp_optimize_right(exp);
+                       }
+                       
+                       
                        if (exp->op==MATCH_OP){
                                     /* right side either has to be string, in which case
                                      * we turn it into regular expression, or it is regular
@@ -543,7 +591,9 @@ int fix_expr(struct expr* exp)
                                        exp->r.re=re;
                                        exp->r_type=RE_ST;
                                }else if (exp->r_type!=RE_ST && exp->r_type != AVP_ST
-                                               && exp->r_type != SELECT_ST && exp->r_type!= RVE_ST
+                                               && exp->r_type != SELECT_ST &&
+                                               exp->r_type != SELECT_UNFIXED_ST &&
+                                               exp->r_type!= RVE_ST
                                                && exp->r_type != PVAR_ST){
                                        LOG(L_CRIT, "BUG: fix_expr : invalid type for match\n");
                                        return E_BUG;
@@ -556,50 +606,21 @@ int fix_expr(struct expr* exp)
                                        return ret;
                                }
                        }
-                            /* Calculate lengths of strings */
-                       if (exp->l_type==STRING_ST) {
-                               int len;
-                               if (exp->l.string) len = strlen(exp->l.string);
-                               else len = 0;
-                               exp->l.str.s = exp->l.string;
-                               exp->l.str.len = len;
-                       }
-                       if (exp->r_type==STRING_ST) {
-                               int len;
-                               if (exp->r.string) len = strlen(exp->r.string);
-                               else len = 0;
-                               exp->r.str.s = exp->r.string;
-                               exp->r.str.len = len;
-                       }
-                       if (exp->l_type==SELECT_O) {
+                       if (exp->l_type==SELECT_UNFIXED_O) {
                                if ((ret=resolve_select(exp->l.select)) < 0) {
-                                       BUG("Unable to resolve select\n");
+                                       ERR("Unable to resolve select\n");
                                        print_select(exp->l.select);
                                        return ret;
                                }
+                               exp->l_type=SELECT_O;
                        }
-                       if ((exp->r_type==SELECT_O)||(exp->r_type==SELECT_ST)) {
+                       if (exp->r_type==SELECT_UNFIXED_ST) {
                                if ((ret=resolve_select(exp->r.select)) < 0) {
-                                       BUG("Unable to resolve select\n");
-                                       print_select(exp->l.select);
-                                       return ret;
-                               }
-                       }
-                       if (exp->l_type==RVEXP_O){
-                               if ((ret=fix_rval_expr(&exp->l.param))<0){
-                                       ERR("Unable to fix left rval expression\n");
-                                       return ret;
-                               }
-                               if (scr_opt_lev>=2)
-                                       exp_optimize_left(exp);
-                       }
-                       if (exp->r_type==RVE_ST){
-                               if ((ret=fix_rval_expr(&exp->r.param))<0){
-                                       ERR("Unable to fix right rval expression\n");
+                                       ERR("Unable to resolve select\n");
+                                       print_select(exp->r.select);
                                        return ret;
                                }
-                               if (scr_opt_lev>=2)
-                                       exp_optimize_right(exp);
+                               exp->r_type=SELECT_ST;
                        }
                        /* PVAR don't need fixing */
                        ret=0;
@@ -616,15 +637,20 @@ int fix_actions(struct action* a)
        struct action *t;
        struct proxy_l* p;
        char *tmp;
+       void *tmp_p;
        int ret;
-       union cmd_export_u* cmd;
+       int i;
+       sr31_cmd_export_t* cmd;
        str s;
        struct hostent* he;
        struct ip_addr ip;
        struct socket_info* si;
        struct lvalue* lval;
-       
-       char buf[30]; /* tmp buffer needed for module param fixups */
+       struct rval_expr* rve;
+       struct rval_expr* err_rve;
+       enum rval_type rve_type, err_type, expected_type;
+       struct rvalue* rv;
+       int rve_param_no;
 
        if (a==0){
                LOG(L_CRIT,"BUG: fix_actions: null pointer\n");
@@ -646,7 +672,8 @@ int fix_actions(struct action* a)
                                                        if (tmp==0){
                                                                LOG(L_CRIT, "ERROR: fix_actions:"
                                                                                "memory allocation failure\n");
-                                                               return E_OUT_OF_MEM;
+                                                               ret = E_OUT_OF_MEM;
+                                                               goto error;
                                                        }
                                                        t->val[0].type=STRING_ST;
                                                        t->val[0].u.string=tmp;
@@ -655,7 +682,7 @@ int fix_actions(struct action* a)
                                                        s.s = t->val[0].u.string;
                                                        s.len = strlen(s.s);
                                                        p=add_proxy(&s, t->val[1].u.number, 0); /* FIXME proto*/
-                                                       if (p==0) return E_BAD_ADDRESS;
+                                                       if (p==0) { ret =E_BAD_ADDRESS; goto error; }
                                                        t->val[0].u.data=p;
                                                        t->val[0].type=PROXY_ST;
                                                        break;
@@ -665,73 +692,208 @@ int fix_actions(struct action* a)
                                                        LOG(L_CRIT, "BUG: fix_actions: invalid type"
                                                                        "%d (should be string or number)\n",
                                                                                t->type);
-                                                       return E_BUG;
+                                                       ret = E_BUG;
+                                                       goto error;
                                        }
                                        break;
                        case IF_T:
-                               if (t->val[0].type!=EXPR_ST){
+                               if (t->val[0].type!=RVE_ST){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
-                                                               "%d for if (should be expr)\n",
+                                                               "%d for if (should be rval expr)\n",
                                                                t->val[0].type);
-                                       return E_BUG;
-                               }else if( (t->val[1].type!=ACTIONS_ST)&&(t->val[1].type!=NOSUBTYPE) ){
+                                       ret = E_BUG;
+                                       goto error;
+                               }else if( (t->val[1].type!=ACTIONS_ST) &&
+                                                       (t->val[1].type!=NOSUBTYPE) ){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
                                                                "%d for if() {...} (should be action)\n",
                                                                t->val[1].type);
-                                       return E_BUG;
-                               }else if( (t->val[2].type!=ACTIONS_ST)&&(t->val[2].type!=NOSUBTYPE) ){
+                                       ret = E_BUG;
+                                       goto error;
+                               }else if( (t->val[2].type!=ACTIONS_ST) &&
+                                                       (t->val[2].type!=NOSUBTYPE) ){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
                                                                "%d for if() {} else{...}(should be action)\n",
                                                                t->val[2].type);
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
-                               if (t->val[0].u.data){
-                                       if ((ret=fix_expr((struct expr*)t->val[0].u.data))<0)
-                                               return ret;
+                               rve=(struct rval_expr*)t->val[0].u.data;
+                               if (rve){
+                                       err_rve=0;
+                                       if (!rve_check_type(&rve_type, rve, &err_rve,
+                                                                                       &err_type, &expected_type)){
+                                               if (err_rve)
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): subexpression (%d,%d) has type"
+                                                                       " %s,  but %s is expected\n",
+                                                                       rve->fpos.s_line, rve->fpos.s_col,
+                                                                       err_rve->fpos.s_line, err_rve->fpos.s_col,
+                                                                       rval_type_name(err_type),
+                                                                       rval_type_name(expected_type) );
+                                               else
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): type mismatch?",
+                                                                       rve->fpos.s_line, rve->fpos.s_col);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       /* it's not an error anymore to have non-int in an if,
+                                          only a script warning (to allow backward compat. stuff
+                                          like if (@ruri) 
+                                       if (rve_type!=RV_INT && rve_type!=RV_NONE){
+                                               LOG(L_ERR, "fix_actions: invalid expression (%d,%d):"
+                                                               " bad type, integer expected\n",
+                                                               rve->fpos.s_line, rve->fpos.s_col);
+                                               return E_UNSPEC;
+                                       }
+                                       */
+                                       if ((ret=fix_rval_expr(t->val[0].u.data))<0)
+                                               goto error;
                                }
                                if ( (t->val[1].type==ACTIONS_ST)&&(t->val[1].u.data) ){
                                        if ((ret=fix_actions((struct action*)t->val[1].u.data))<0)
-                                               return ret;
+                                               goto error;
                                }
                                if ( (t->val[2].type==ACTIONS_ST)&&(t->val[2].u.data) ){
-                                               if ((ret=fix_actions((struct action*)t->val[2].u.data))<0)
-                                               return ret;
+                                               if ((ret=fix_actions((struct action*)t->val[2].u.data))
+                                                               <0)
+                                               goto error;
                                }
                                break;
                        case SWITCH_T:
                                if (t->val[0].type!=RVE_ST){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
-                                                               "%d for if (should be expr)\n",
+                                                               "%d for switch() (should be expr)\n",
                                                                t->val[0].type);
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }else if (t->val[1].type!=CASE_ST){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
-                                                               "%d for switch(...){...}(should be action)\n",
+                                                               "%d for switch(...){...}(should be case)\n",
                                                                t->val[1].type);
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
                                if (t->val[0].u.data){
-                                       if ((ret=fix_rval_expr(&t->val[0].u.data))<0)
-                                               return ret;
+                                       if ((ret=fix_rval_expr(t->val[0].u.data))<0)
+                                               goto error;
                                }else{
                                        LOG(L_CRIT, "BUG: fix_actions: null switch()"
                                                        " expression\n");
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
                                if ((ret=fix_switch(t))<0)
-                                       return ret;
+                                       goto error;
+                               break;
+                       case WHILE_T:
+                               if (t->val[0].type!=RVE_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for while() (should be expr)\n",
+                                                               t->val[0].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }else if (t->val[1].type!=ACTIONS_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for while(...){...}(should be action)\n",
+                                                               t->val[1].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               rve=(struct rval_expr*)t->val[0].u.data;
+                               if (rve){
+                                       err_rve=0;
+                                       if (!rve_check_type(&rve_type, rve, &err_rve,
+                                                                                       &err_type, &expected_type)){
+                                               if (err_rve)
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): subexpression (%d,%d) has type"
+                                                                       " %s,  but %s is expected\n",
+                                                                       rve->fpos.s_line, rve->fpos.s_col,
+                                                                       err_rve->fpos.s_line, err_rve->fpos.s_col,
+                                                                       rval_type_name(err_type),
+                                                                       rval_type_name(expected_type) );
+                                               else
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): type mismatch?",
+                                                                       rve->fpos.s_line, rve->fpos.s_col);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       if (rve_type!=RV_INT && rve_type!=RV_NONE){
+                                               LOG(L_ERR, "fix_actions: invalid expression (%d,%d):"
+                                                               " bad type, integer expected\n",
+                                                               rve->fpos.s_line, rve->fpos.s_col);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       if ((ret=fix_rval_expr(t->val[0].u.data))<0)
+                                               goto error;
+                               }else{
+                                       LOG(L_CRIT, "BUG: fix_actions: null while()"
+                                                       " expression\n");
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               if ( t->val[1].u.data && 
+                                       ((ret= fix_actions((struct action*)t->val[1].u.data))<0)){
+                                       goto error;
+                               }
+                               break;
+                       case DROP_T:
+                               /* only RVEs need fixing for drop/return/break */
+                               if (t->val[0].type!=RVE_ST)
+                                       break;
+                               rve=(struct rval_expr*)t->val[0].u.data;
+                               if (rve){
+                                       err_rve=0;
+                                       if (!rve_check_type(&rve_type, rve, &err_rve,
+                                                                                       &err_type, &expected_type)){
+                                               if (err_rve)
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): subexpression (%d,%d) has type"
+                                                                       " %s,  but %s is expected\n",
+                                                                       rve->fpos.s_line, rve->fpos.s_col,
+                                                                       err_rve->fpos.s_line, err_rve->fpos.s_col,
+                                                                       rval_type_name(err_type),
+                                                                       rval_type_name(expected_type) );
+                                               else
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): type mismatch?",
+                                                                       rve->fpos.s_line, rve->fpos.s_col);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       if (rve_type!=RV_INT && rve_type!=RV_NONE){
+                                               LOG(L_ERR, "fix_actions: invalid expression (%d,%d):"
+                                                               " bad type, integer expected\n",
+                                                               rve->fpos.s_line, rve->fpos.s_col);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       if ((ret=fix_rval_expr(t->val[0].u.data))<0)
+                                               goto error;
+                               }else{
+                                       LOG(L_CRIT, "BUG: fix_actions: null drop/return"
+                                                       " expression\n");
+                                       ret = E_BUG;
+                                       goto error;
+                               }
                                break;
                        case ASSIGN_T:
                        case ADD_T:
                                if (t->val[0].type !=LVAL_ST) {
                                        LOG(L_CRIT, "BUG: fix_actions: Invalid left side of"
                                                                " assignment\n");
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
                                if (t->val[1].type !=RVE_ST) {
                                        LOG(L_CRIT, "BUG: fix_actions: Invalid right side of"
                                                                " assignment (%d)\n", t->val[1].type);
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
                                lval=t->val[0].u.data;
                                if (lval->type==LV_AVP){
@@ -739,59 +901,133 @@ int fix_actions(struct action* a)
                                                LOG(L_ERR, "ERROR: You cannot change domain"
                                                                        " attributes from the script, they are"
                                                                        " read-only\n");
-                                               return E_BUG;
+                                               ret = E_BUG;
+                                               goto error;
                                        } else if (lval->lv.avps.type & AVP_CLASS_GLOBAL) {
                                                LOG(L_ERR, "ERROR: You cannot change global"
                                                                   " attributes from the script, they are"
                                                                   "read-only\n");
-                                               return E_BUG;
+                                               ret = E_BUG;
+                                               goto error;
                                        }
                                }
-                               if ((ret=fix_rval_expr(&t->val[1].u.data))<0)
-                                       return ret;
+                               if ((ret=fix_rval_expr(t->val[1].u.data))<0)
+                                       goto error;
                                break;
 
-                       case MODULE_T:
+                       case MODULE0_T:
+                       case MODULE1_T:
+                       case MODULE2_T:
                        case MODULE3_T:
                        case MODULE4_T:
                        case MODULE5_T:
                        case MODULE6_T:
                        case MODULEX_T:
                                cmd = t->val[0].u.data;
-                               if (cmd && cmd->c.fixup) {
-                                       int i;
-                                       DBG("fixing %s()\n", cmd->c.name);
+                               rve_param_no = 0;
+                               if (cmd) {
+                                       DBG("fixing %s()\n", cmd->name);
                                        if (t->val[1].u.number==0) {
-                                               ret = cmd->c.fixup(0, 0);
+                                               ret = call_fixup(cmd->fixup, 0, 0);
                                                if (ret < 0)
-                                                       return ret;
+                                                       goto error;
                                        }
-                                       /* type cast NUMBER to STRING, old modules may expect
-                                        * all STRING params during fixup */
-                                       for (i=0; i<t->val[1].u.number; i++) {
-                                               if (t->val[i+2].type == NUMBER_ST) {
-                                                       snprintf(buf, sizeof(buf)-1, "%ld", 
-                                                                               t->val[i+2].u.number);
-                                                       /* fixup currently requires string pkg_malloced*/
-                                                       t->val[i+2].u.string = pkg_malloc(strlen(buf)+1);
-                                                       if (!t->val[i+2].u.string) {
-                                                               LOG(L_CRIT, "ERROR: cannot translate NUMBER"
-                                                                                       " to STRING\n");
-                                                               return E_OUT_OF_MEM;
+                                       for (i=0; i < t->val[1].u.number; i++) {
+                                               if (t->val[i+2].type == RVE_ST) {
+                                                       rve = t->val[i+2].u.data;
+                                                       if (rve_is_constant(rve)) {
+                                                               /* if expression is constant => evaluate it
+                                                                  as string and replace it with the corresp.
+                                                                  string */
+                                                               rv = rval_expr_eval(0, 0, rve);
+                                                               if (rv == 0 ||
+                                                                               rval_get_str( 0, 0, &s, rv, 0) < 0 ) {
+                                                                       ERR("failed to fix constant rve");
+                                                                       if (rv) rval_destroy(rv);
+                                                                       ret = E_BUG;
+                                                                       goto error;
+                                                               }
+                                                               rval_destroy(rv);
+                                                               rve_destroy(rve);
+                                                               t->val[i+2].type = STRING_ST;/*asciiz string*/
+                                                               t->val[i+2].u.string = s.s;
+                                                               /* len is not used for now */
+                                                               t->val[i+2].u.str.len = s.len;
+                                                               tmp_p = t->val[i+2].u.data;
+                                                               ret = call_fixup(cmd->fixup,
+                                                                                               &t->val[i+2].u.data, i+1);
+                                                               if (t->val[i+2].u.data != tmp_p)
+                                                                       t->val[i+2].type = MODFIXUP_ST;
+                                                               if (ret < 0)
+                                                                       goto error;
+                                                       } else {
+                                                               /* expression is not constant => fixup &
+                                                                  optimize it */
+                                                               rve_param_no++;
+                                                               if ((ret=fix_rval_expr(t->val[i+2].u.data))
+                                                                               < 0) {
+                                                                       ERR("rve fixup failed\n");
+                                                                       ret = E_BUG;
+                                                                       goto error;
+                                                               }
                                                        }
-                                                       strcpy(t->val[i+2].u.string, buf);
-                                                       t->val[i+2].type = STRING_ST;
+                                               } else  if (t->val[i+2].type == STRING_ST) {
+                                                       tmp_p = t->val[i+2].u.data;
+                                                       ret = call_fixup(cmd->fixup,
+                                                                                       &t->val[i+2].u.data, i+1);
+                                                       if (t->val[i+2].u.data != tmp_p)
+                                                               t->val[i+2].type = MODFIXUP_ST;
+                                                       if (ret < 0)
+                                                               goto error;
+                                               } else {
+                                                       BUG("invalid module function param type %d\n",
+                                                                       t->val[i+2].type);
+                                                       ret = E_BUG;
+                                                       goto error;
                                                }
-                                       }
-                                       for (i=0; i<t->val[1].u.number; i++) {
-                                               void *p;
-                                               p = t->val[i+2].u.data;
-                                               ret = cmd->c.fixup(&t->val[i+2].u.data, i+1);
-                                               if (t->val[i+2].u.data != p)
-                                                       t->val[i+2].type = MODFIXUP_ST;
-                                               if (ret < 0)
-                                                       return ret;
-                                       }
+                                       } /* for */
+                                       /* here all the params are either STRING_ST
+                                          (constant RVEs), MODFIXUP_ST (fixed up)
+                                          or RVE_ST (non-ct RVEs) */
+                                       if (rve_param_no) { /* we have to fix the type */
+                                               if (cmd->fixup &&
+                                                       !(cmd->fixup_flags & FIXUP_F_FPARAM_RVE) &&
+                                                       cmd->free_fixup == 0) {
+                                                       BUG("non-ct RVEs (%d) in module function call"
+                                                                       "that does not support them (%s)\n",
+                                                                       rve_param_no, cmd->name);
+                                                       ret = E_BUG;
+                                                       goto error;
+                                               }
+                                               switch(t->type) {
+                                                       case MODULE1_T:
+                                                               t->type = MODULE1_RVE_T;
+                                                               break;
+                                                       case MODULE2_T:
+                                                               t->type = MODULE2_RVE_T;
+                                                               break;
+                                                       case MODULE3_T:
+                                                               t->type = MODULE3_RVE_T;
+                                                               break;
+                                                       case MODULE4_T:
+                                                               t->type = MODULE4_RVE_T;
+                                                               break;
+                                                       case MODULE5_T:
+                                                               t->type = MODULE5_RVE_T;
+                                                               break;
+                                                       case MODULE6_T:
+                                                               t->type = MODULE6_RVE_T;
+                                                               break;
+                                                       case MODULEX_T:
+                                                               t->type = MODULEX_RVE_T;
+                                                               break;
+                                                       default:
+                                                               BUG("unsupported module function type %d\n",
+                                                                               t->type);
+                                                               ret = E_BUG;
+                                                               goto error;
+                                               }
+                                       } /* if rve_param_no */
                                }
                                break;
                        case FORCE_SEND_SOCKET_T:
@@ -799,7 +1035,8 @@ int fix_actions(struct action* a)
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
                                                                "%d for force_send_socket\n",
                                                                t->val[0].type);
-                                       return E_BUG;
+                                       ret = E_BUG;
+                                       goto error;
                                }
                                he=resolvehost(
                                                ((struct socket_id*)t->val[0].u.data)->addr_lst->name
@@ -808,7 +1045,8 @@ int fix_actions(struct action* a)
                                        LOG(L_ERR, "ERROR: fix_actions: force_send_socket:"
                                                                " could not resolve %s\n",
                                                ((struct socket_id*)t->val[0].u.data)->addr_lst->name);
-                                       return E_BAD_ADDRESS;
+                                       ret = E_BAD_ADDRESS;
+                                       goto error;
                                }
                                hostent2ip_addr(&ip, he, 0);
                                si=find_si(&ip, ((struct socket_id*)t->val[0].u.data)->port,
@@ -818,14 +1056,165 @@ int fix_actions(struct action* a)
                                                        " argument: %s:%d (ser doesn't listen on it)\n",
                                                ((struct socket_id*)t->val[0].u.data)->addr_lst->name,
                                                        ((struct socket_id*)t->val[0].u.data)->port);
-                                       return E_BAD_ADDRESS;
+                                       ret = E_BAD_ADDRESS;
+                                       goto error;
                                }
                                t->val[0].u.data=si;
                                t->val[0].type=SOCKETINFO_ST;
                                break;
+                       case UDP_MTU_TRY_PROTO_T:
+                               if (t->val[0].type!=NUMBER_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for udp_mtu_try_proto\n",
+                                                               t->val[0].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               switch(t->val[0].u.number){
+                                       case PROTO_UDP:
+                                               t->val[0].u.number=0;
+                                               break;
+                                       case PROTO_TCP:
+                                               t->val[0].u.number=FL_MTU_TCP_FB;
+                                               break;
+                                       case PROTO_TLS:
+                                               t->val[0].u.number=FL_MTU_TLS_FB;
+                                               break;
+                                       case PROTO_SCTP:
+                                               t->val[0].u.number=FL_MTU_SCTP_FB;
+                                               break;
+                                       default:
+                                               LOG(L_CRIT, "BUG: fix actions: invalid argument for"
+                                                                       " udp_mtu_try_proto (%d)\n", 
+                                                                       (unsigned int)t->val[0].u.number);
+                               }
+                               break;
+                       case APPEND_BRANCH_T:
+                               if (t->val[0].type!=STRING_ST){
+                                       BUG("invalid subtype%d for append_branch_t\n",
+                                                               t->val[0].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               s.s=t->val[0].u.string;
+                               s.len=(s.s)?strlen(s.s):0;
+                               t->val[0].u.str=s;
+                               t->val[0].type=STR_ST;
+                               break;
+                       case ROUTE_T:
+                               if (t->val[0].type == RVE_ST) {
+                                       rve=(struct rval_expr*)t->val[0].u.data;
+                                       if (!rve_is_constant(rve)) {
+                                               if ((ret=fix_rval_expr(t->val[0].u.data)) < 0){
+                                                       ERR("route() failed to fix rve at %s:%d\n",
+                                                               (t->cfile)?t->cfile:"line", t->cline);
+                                                       ret = E_BUG;
+                                                       goto error;
+                                               }
+                                       } else {
+                                               /* rve is constant => replace it with a string */
+                                               if ((rv = rval_expr_eval(0, 0, rve)) == 0 ||
+                                                               rval_get_str(0, 0, &s, rv, 0) < 0) {
+                                                       /* out of mem. or bug ? */
+                                                       rval_destroy(rv);
+                                                       ERR("route() failed to fix ct. rve at %s:%d\n",
+                                                               (t->cfile)?t->cfile:"line", t->cline);
+                                                       ret = E_BUG;
+                                                       goto error;
+                                               }
+                                               rval_destroy(rv);
+                                               rve_destroy(rve);
+                                               t->val[0].type = STRING_ST;
+                                               t->val[0].u.string = s.s;
+                                               t->val[0].u.str.len = s.len; /* not used */
+                                               /* fall-through the STRING_ST if */
+                                       }
+                               }
+                               if (t->val[0].type == STRING_ST) {
+                                       i=route_lookup(&main_rt, t->val[0].u.string);
+                                       if (i < 0) {
+                                               ERR("route \"%s\" not found at %s:%d\n",
+                                                               t->val[0].u.string,
+                                                               (t->cfile)?t->cfile:"line", t->cline);
+                                               ret = E_SCRIPT;
+                                               goto error;
+                                       }
+                                       t->val[0].type = NUMBER_ST;
+                                       pkg_free(t->val[0].u.string);
+                                       t->val[0].u.number = i;
+                               } else if (t->val[0].type != NUMBER_ST &&
+                                                       t->val[0].type != RVE_ST) {
+                                       BUG("invalid subtype %d for route()\n",
+                                                               t->val[0].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               break;
+                       case CFG_SELECT_T:
+                               if (t->val[1].type == RVE_ST) {
+                                       rve = t->val[1].u.data;
+                                       if (rve_is_constant(rve)) {
+                                               /* if expression is constant => evaluate it
+                                                  as integer and replace it with the corresp.
+                                                  int */
+                                               rv = rval_expr_eval(0, 0, rve);
+                                               if (rv == 0 ||
+                                                               rval_get_int( 0, 0, &i, rv, 0) < 0 ) {
+                                                       ERR("failed to fix constant rve");
+                                                       if (rv) rval_destroy(rv);
+                                                       ret = E_BUG;
+                                                       goto error;
+                                               }
+                                               rval_destroy(rv);
+                                               rve_destroy(rve);
+                                               t->val[1].type = NUMBER_ST;
+                                               t->val[1].u.number = i;
+                                       } else {
+                                               /* expression is not constant => fixup &
+                                                  optimize it */
+                                               if ((ret=fix_rval_expr(rve))
+                                                               < 0) {
+                                                       ERR("rve fixup failed\n");
+                                                       ret = E_BUG;
+                                                       goto error;
+                                               }
+                                       }
+                               } else if (t->val[1].type != NUMBER_ST) {
+                                       BUG("invalid subtype %d for cfg_select()\n",
+                                                               t->val[1].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+
+                       case CFG_RESET_T:
+                               if (t->val[0].type != STRING_ST) {
+                                       BUG("invalid subtype %d for cfg_select() or cfg_reset()\n",
+                                                               t->val[0].type);
+                                       ret = E_BUG;
+                                       goto error;
+                               }
+                               tmp_p = (void *)cfg_lookup_group(t->val[0].u.string, strlen(t->val[0].u.string));
+                               if (!tmp_p) {
+                                       ERR("configuration group \"%s\" not found\n",
+                                               t->val[0].u.string);
+                                       ret = E_SCRIPT;
+                                       goto error;
+                               }
+                               pkg_free(t->val[0].u.string);
+                               t->val[0].u.data = tmp_p;
+                               t->val[0].type = CFG_GROUP_ST;
+                               break;
+                       default:
+                               /* no fixup required for the rest */
+                               break;
                }
        }
        return 0;
+
+error:
+       LM_ERR("fixing failed (code=%d) at cfg:%s:%d\n", ret,
+                       (t->cfile)?t->cfile:"", t->cline);
+       return ret;
 }
 
 
@@ -842,6 +1231,7 @@ inline static int comp_num(int op, long left, int rtype, union exp_op* r,
        pv_value_t pval;
        avp_t* avp;
        int right;
+       struct tcp_connection *con;
 
        if (unlikely(op==NO_OP)) return !(!left);
        switch(rtype){
@@ -871,6 +1261,14 @@ inline static int comp_num(int op, long left, int rtype, union exp_op* r,
                                return (op == DIFF_OP); /* not found or invalid type */
                        }
                        break;
+               case WEBSOCKET_ST:
+                       if ((con = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, 0)) != NULL) {
+                               if (con->flags & F_CONN_WS)
+                                       left = PROTO_WS;
+                       }
+                       right = r->numval;
+                       break;
+
                default:
                        LOG(L_CRIT, "BUG: comp_num: Invalid right operand (%d)\n", rtype);
                        return E_BUG;
@@ -957,7 +1355,8 @@ inline static int comp_str(int op, str* left, int rtype,
                                goto error;
                        }
                        break;
-               case STRING_ST:
+               case STRING_ST: /* strings are stored as {asciiz, len } */
+               case STR_ST:
                        right=&r->str;
                        break;
                case NUMBER_ST:
@@ -1010,6 +1409,8 @@ inline static int comp_str(int op, str* left, int rtype,
                                case SELECT_ST:
                                case RVE_ST:
                                case PVAR_ST:
+                               case STRING_ST:
+                               case STR_ST:
                                        /* we need to compile the RE on the fly */
                                        re=(regex_t*)pkg_malloc(sizeof(regex_t));
                                        if (re==0){
@@ -1031,7 +1432,6 @@ inline static int comp_str(int op, str* left, int rtype,
                                case RE_ST:
                                        ret=(regexec(r->re, left->s, 0, 0, 0)==0);
                                        break;
-                               case STRING_ST:
                                default:
                                        LOG(L_CRIT, "BUG: comp_str: Bad operator type %d, "
                                                                "for ~= \n", rtype);
@@ -1261,8 +1661,21 @@ inline static int comp_ip(int op, struct ip_addr* ip, int rtype,
        char ** h;
        int ret;
        str tmp;
+       str* right;
+       struct net net;
+       union exp_op r_expop;
+       struct rvalue* rv;
+       struct rval_cache rv_cache;
+       avp_t* avp;
+       int_str val;
+       pv_value_t pval;
+       int destroy_pval;
 
+       right=NULL; /* warning fix */
+       rv=NULL;
+       destroy_pval=0;
        ret=-1;
+       he=NULL; /* warning fix */
        switch(rtype){
                case NET_ST:
                        switch(op){
@@ -1275,66 +1688,174 @@ inline static int comp_ip(int op, struct ip_addr* ip, int rtype,
                                default:
                                        goto error_op;
                        }
-                       break;
-               case AVP_ST:
-               case STRING_ST:
-               case RE_ST:
-               case RVE_ST:
-               case SELECT_ST:
-                       switch(op){
-                               case EQUAL_OP:
-                               case MATCH_OP:
-                                       /* 1: compare with ip2str*/
-                                       ret=comp_string(op, ip_addr2a(ip), rtype, r, msg, ctx);
-                                       if (likely(ret==1)) break;
-                                       /* 2: resolve (name) & compare w/ all the ips */
-                                       if (rtype==STRING_ST){
-                                               he=resolvehost(r->str.s);
-                                               if (he==0){
-                                                       DBG("comp_ip: could not resolve %s\n",
-                                                           r->str.s);
-                                               }else if (he->h_addrtype==ip->af){
-                                                       for(h=he->h_addr_list;(ret!=1)&& (*h); h++){
-                                                               ret=(memcmp(ip->u.addr, *h, ip->len)==0);
-                                                       }
-                                                       if (ret==1) break;
-                                               }
-                                       }
-                                       /* 3: (slow) rev dns the address
-                                       * and compare with all the aliases
-                                       * !!??!! review: remove this? */
-                                       if (unlikely((received_dns & DO_REV_DNS) && 
-                                                       ((he=rev_resolvehost(ip))!=0) )){
-                                               /*  compare with primary host name */
-                                               ret=comp_string(op, he->h_name, rtype, r, msg, ctx);
-                                               /* compare with all the aliases */
-                                               for(h=he->h_aliases; (ret!=1) && (*h); h++){
-                                                       ret=comp_string(op, *h, rtype, r, msg, ctx);
-                                               }
-                                       }else{
-                                               ret=0;
-                                       }
-                                       break;
-                               case DIFF_OP:
-                                       ret=(comp_ip(EQUAL_OP, ip, rtype, r, msg, ctx) > 0) ?0:1;
-                                       break;
-                               default:
-                                       goto error_op;
-                       }
-                       break;
+                       return ret; /* exit directly */
                case MYSELF_ST: /* check if it's one of our addresses*/
                        tmp.s=ip_addr2a(ip);
                        tmp.len=strlen(tmp.s);
                        ret=check_self_op(op, &tmp, 0);
+                       return ret;
+               case STRING_ST:
+               case STR_ST:
+                       right=&r->str;
+                       break;
+               case RVE_ST:
+                       rval_cache_init(&rv_cache);
+                       rv=rval_expr_eval(ctx, msg, r->param);
+                       if (unlikely (rv==0))
+                               return (op==DIFF_OP); /* not found or error*/
+                       if (unlikely(rval_get_tmp_str(ctx, msg, &tmp, rv, 0, &rv_cache)
+                                                       < 0)){
+                               goto error;
+                       }
+                       right = &tmp;
+                       break;
+               case AVP_ST:
+                       /* we can still have AVP_ST due to the RVE optimisations
+                          (if a RVE == $avp => rve wrapper removed => pure avp) */
+                       avp = search_avp_by_index(r->attr->type, r->attr->name,
+                                                                               &val, r->attr->index);
+                       if (likely(avp && (avp->flags & AVP_VAL_STR))) right = &val.s;
+                       else return (op == DIFF_OP);
+                       break;
+               case SELECT_ST:
+                       /* see AVP_ST comment and s/AVP_ST/SELECT_ST/ */
+                       ret = run_select(&tmp, r->select, msg);
+                       if (unlikely(ret != 0))
+                               return (op == DIFF_OP); /* Not found or error */
+                       right = &tmp;
+                       break;
+               case PVAR_ST:
+                       /* see AVP_ST comment and s/AVP_ST/PVAR_ST/ */
+                       memset(&pval, 0, sizeof(pv_value_t));
+                       if (unlikely(pv_get_spec_value(msg, r->param, &pval)!=0)){
+                               return (op == DIFF_OP); /* error, not found => false */
+                       }
+                       destroy_pval=1;
+                       if (likely(pval.flags & PV_VAL_STR)){
+                               right=&pval.rs;
+                       }else{
+                               pv_value_destroy(&pval);
+                               return (op == DIFF_OP); /* not found or invalid type */
+                       }
                        break;
+               case RE_ST:
+                       if (unlikely(op != MATCH_OP))
+                               goto error_op;
+                       /* 1: compare with ip2str*/
+                       ret=comp_string(op, ip_addr2a(ip), rtype, r, msg, ctx);
+                       if (likely(ret==1))
+                               return ret;
+                       /* 3: (slow) rev dns the address
+                       * and compare with all the aliases
+                       * !!??!! review: remove this? */
+                       if (unlikely((received_dns & DO_REV_DNS) &&
+                               ((he=rev_resolvehost(ip))!=0) )){
+                               /*  compare with primary host name */
+                               ret=comp_string(op, he->h_name, rtype, r, msg, ctx);
+                               /* compare with all the aliases */
+                               for(h=he->h_aliases; (ret!=1) && (*h); h++){
+                                       ret=comp_string(op, *h, rtype, r, msg, ctx);
+                               }
+                       }else{
+                               ret=0;
+                       }
+                       return ret;
                default:
                        LOG(L_CRIT, "BUG: comp_ip: invalid type for "
                                                " src_ip or dst_ip (%d)\n", rtype);
-                       ret=-1;
+                       return -1;
        }
+       /* here "right" is set to the str we compare with */
+       r_expop.str=*right;
+       switch(op){
+               case EQUAL_OP:
+                       /* 0: try if ip or network (ip/mask) */
+                       if (mk_net_str(&net, right) == 0) {
+                               ret=(matchnet(ip, &net)==1);
+                               break;
+                       }
+                       /* 2: resolve (name) & compare w/ all the ips */
+                       he=resolvehost(right->s);
+                       if (he==0){
+                               DBG("comp_ip: could not resolve %s\n", r->str.s);
+                       }else if (he->h_addrtype==ip->af){
+                               for(h=he->h_addr_list;(ret!=1)&& (*h); h++){
+                                       ret=(memcmp(ip->u.addr, *h, ip->len)==0);
+                               }
+                               if (ret==1) break;
+                       }
+                       /* 3: (slow) rev dns the address
+                        * and compare with all the aliases
+                        * !!??!! review: remove this? */
+                       if (unlikely((received_dns & DO_REV_DNS) &&
+                                                       ((he=rev_resolvehost(ip))!=0) )){
+                               /*  compare with primary host name */
+                               ret=comp_string(op, he->h_name, STR_ST, &r_expop, msg, ctx);
+                               /* compare with all the aliases */
+                               for(h=he->h_aliases; (ret!=1) && (*h); h++){
+                                       ret=comp_string(op, *h, STR_ST, &r_expop, msg, ctx);
+                               }
+                       }else{
+                               ret=0;
+                       }
+                       break;
+               case MATCH_OP:
+                       /* 0: try if ip or network (ip/mask)
+                         (one should not use MATCH for that, but try to be nice)*/
+                       if (mk_net_str(&net, right) == 0) {
+                               ret=(matchnet(ip, &net)==1);
+                               break;
+                       }
+                       /* 1: compare with ip2str (but only for =~)*/
+                       ret=comp_string(op, ip_addr2a(ip), STR_ST, &r_expop, msg, ctx);
+                       if (likely(ret==1)) break;
+                       /* 2: resolve (name) & compare w/ all the ips */
+                       he=resolvehost(right->s);
+                       if (he==0){
+                               DBG("comp_ip: could not resolve %s\n", r->str.s);
+                       }else if (he->h_addrtype==ip->af){
+                               for(h=he->h_addr_list;(ret!=1)&& (*h); h++){
+                                       ret=(memcmp(ip->u.addr, *h, ip->len)==0);
+                               }
+                               if (ret==1) break;
+                       }
+                       /* 3: (slow) rev dns the address
+                        * and compare with all the aliases
+                        * !!??!! review: remove this? */
+                       if (unlikely((received_dns & DO_REV_DNS) &&
+                                                       ((he=rev_resolvehost(ip))!=0) )){
+                               /*  compare with primary host name */
+                               ret=comp_string(op, he->h_name, STR_ST, &r_expop, msg, ctx);
+                               /* compare with all the aliases */
+                               for(h=he->h_aliases; (ret!=1) && (*h); h++){
+                                       ret=comp_string(op, *h, STR_ST, &r_expop, msg, ctx);
+                               }
+                       }else{
+                               ret=0;
+                       }
+                       break;
+               case DIFF_OP:
+                       ret=(comp_ip(EQUAL_OP, ip, STR_ST, &r_expop, msg, ctx) > 0)?0:1;
+               break;
+               default:
+                       goto error_op;
+       }
+       if (rv){
+               rval_cache_clean(&rv_cache);
+               rval_destroy(rv);
+       }
+       if (destroy_pval)
+               pv_value_destroy(&pval);
        return ret;
 error_op:
-       LOG(L_CRIT, "BUG: comp_ip: invalid operator %d\n", op);
+       LOG(L_CRIT, "BUG: comp_ip: invalid operator %d for type %d\n", op, rtype);
+error:
+       if (unlikely(rv)){
+               rval_cache_clean(&rv_cache);
+               rval_destroy(rv);
+       }
+       if (destroy_pval)
+               pv_value_destroy(&pval);
        return -1;
 }
 
@@ -1356,8 +1877,19 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
        }
        switch(e->l_type){
        case METHOD_O:
-               ret=comp_str(e->op, &msg->first_line.u.request.method,
+               if(msg->first_line.type==SIP_REQUEST)
+               {
+                       ret=comp_str(e->op, &msg->first_line.u.request.method,
                                                e->r_type, &e->r, msg, h);
+               } else {
+                       if(parse_headers(msg, HDR_CSEQ_F, 0)!=0 || msg->cseq==NULL)
+                       {
+                               LM_ERR("cannot parse cseq header\n");
+                               goto error;
+                       }
+                       ret=comp_str(e->op, &get_cseq(msg)->method,
+                                               e->r_type, &e->r, msg, h);
+               }
                break;
        case URI_O:
                if(msg->new_uri.s) {
@@ -1547,9 +2079,14 @@ inline static int eval_elem(struct run_act_ctx* h, struct expr* e,
                ret=comp_pvar(e->op, e->l.param, e->r_type, &e->r, msg, h);
                break;
 
+       case SELECT_UNFIXED_O:
+               BUG("unexpected unfixed select operand %d\n", e->l_type);
+               break;
+/*
        default:
                LOG(L_CRIT, "BUG: eval_elem: invalid operand %d\n",
                    e->l_type);
+*/
        }
        return ret;
 error:
@@ -1658,6 +2195,8 @@ int fix_rls()
                return ret;
        if ((ret=fix_rl(&onsend_rt))!=0)
                return ret;
+       if ((ret=fix_rl(&event_rt))!=0)
+               return ret;
 
        return 0;
 }
@@ -1689,4 +2228,5 @@ void print_rls()
        print_rl(&failure_rt, "failure");
        print_rl(&branch_rt, "branch");
        print_rl(&onsend_rt, "onsend");
+       print_rl(&event_rt, "event");
 }