/* * $Id: route.c 2584 2007-08-08 13:16:30Z miconda $ * * SIP routing engine * * Copyright (C) 2001-2003 FhG Fokus * Copyright (C) 2005-2006 Voice Sistem S.R.L. * * This file is part of openser, a free SIP server. * * openser 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 * * openser 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-01-28 scratchpad removed, src_port introduced (jiri) * 2003-02-28 scratchpad compatibility abandoned (jiri) * 2003-03-10 updated to the new module exports format (andrei) * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) * 2003-04-01 added dst_port, proto, af; renamed comp_port to comp_no, * inlined all the comp_* functions (andrei) * 2003-04-05 s/reply_route/failure_route, onreply_route introduced (jiri) * 2003-05-23 comp_ip fixed, now it will resolve its operand and compare * the ip with all the addresses (andrei) * 2003-10-10 added more operators support to comp_* (<,>,<=,>=,!=) (andrei) * 2004-10-19 added from_uri & to_uri (andrei) * 2006-03-02 MODULE_T action points to a cmd_export_t struct instead to * a function address - more info is accessible (bogdan) * Fixup failure reports the config line (bogdan) * 2006-12-22 support for script and branch flags added (bogdan) */ #include #include #include #include #include #include #include #include #include #include "route.h" #include "forward.h" #include "dprint.h" #include "proxy.h" #include "action.h" #include "sr_module.h" #include "ip_addr.h" #include "resolve.h" #include "socket_info.h" #include "blacklists.h" #include "parser/parse_uri.h" #include "parser/parse_from.h" #include "parser/parse_to.h" #include "mem/mem.h" /* main routing script table */ struct action* rlist[RT_NO]; /* reply routing table */ struct action* onreply_rlist[ONREPLY_RT_NO]; struct action* failure_rlist[FAILURE_RT_NO]; struct action* branch_rlist[BRANCH_RT_NO]; struct action* error_rlist; int route_type = REQUEST_ROUTE; static int fix_actions(struct action* a); /*fwd declaration*/ extern int return_code; /* * */ void init_route_lists() { memset(rlist, 0, sizeof(rlist)); memset(onreply_rlist, 0, sizeof(onreply_rlist)); memset(failure_rlist, 0, sizeof(failure_rlist)); memset(branch_rlist, 0, sizeof(branch_rlist)); error_rlist = 0; } /* traverses an expr tree and compiles the REs where necessary) * returns: 0 for ok, <0 if errors */ static int fix_expr(struct expr* exp) { regex_t* re; int ret; ret=E_BUG; if (exp==0){ LOG(L_CRIT, "BUG: fix_expr: null pointer\n"); return E_BUG; } if (exp->type==EXP_T){ switch(exp->op){ case AND_OP: case OR_OP: if ((ret=fix_expr(exp->left.v.expr))!=0) return ret; ret=fix_expr(exp->right.v.expr); break; case NOT_OP: case EVAL_OP: ret=fix_expr(exp->left.v.expr); break; default: LOG(L_CRIT, "BUG: fix_expr: unknown op %d\n", exp->op); } }else if (exp->type==ELEM_T){ if (exp->op==MATCH_OP || exp->op==NOTMATCH_OP){ if (exp->right.type==STRING_ST){ re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0){ LOG(L_CRIT, "ERROR: fix_expr: memory allocation" " failure\n"); return E_OUT_OF_MEM; } if (regcomp(re, (char*) exp->right.v.data, REG_EXTENDED|REG_NOSUB|REG_ICASE) ){ LOG(L_CRIT, "ERROR: fix_expr : bad re \"%s\"\n", (char*) exp->right.v.data); pkg_free(re); return E_BAD_RE; } /* replace the string with the re */ pkg_free(exp->right.v.data); exp->right.v.data=re; exp->right.type=RE_ST; }else if (exp->right.type!=RE_ST && exp->right.type!=SCRIPTVAR_ST){ LOG(L_CRIT, "BUG: fix_expr : invalid type for match\n"); return E_BUG; } } if (exp->left.type==ACTION_O){ ret=fix_actions((struct action*)exp->right.v.data); if (ret!=0){ LOG(L_CRIT, "ERROR: fix_expr : fix_actions error\n"); return ret; } } if (exp->left.type==EXPR_O){ ret=fix_expr(exp->left.v.expr); if (ret!=0){ LOG(L_CRIT, "ERROR: fix_expr : fix left exp error\n"); return ret; } } if (exp->right.type==EXPR_ST){ ret=fix_expr(exp->right.v.expr); if (ret!=0){ LOG(L_CRIT, "ERROR: fix_expr : fix rigth exp error\n"); return ret; } } ret=0; } return ret; } /* adds the proxies in the proxy list & resolves the hostnames */ /* returns 0 if ok, <0 on error */ static int fix_actions(struct action* a) { struct action *t; int ret; cmd_export_t* cmd; struct hostent* he; struct ip_addr ip; struct socket_info* si; str host; int proto, port; struct proxy_l *p; struct bl_head *blh; if (a==0){ LOG(L_CRIT,"BUG: fix_actions: null pointer\n"); return E_BUG; } for(t=a; t!=0; t=t->next){ switch(t->type){ case FORWARD_T: if (t->elem[0].type==NOSUBTYPE) break; case SEND_T: if (t->elem[0].type!=STRING_ST) { LOG(L_CRIT, "BUG: fix_actions: invalid type" "%d (should be string)\n", t->type); return E_BUG; } ret = parse_phostport( t->elem[0].u.string, strlen(t->elem[0].u.string), &host.s, &host.len, &port, &proto); if (ret!=0) { LOG(L_ERR,"ERROR:fix_actions: FORWARD/SEND bad " "argument\n"); return E_CFG; } p = add_proxy( &host,(unsigned short)port, proto); if (p==0) { LOG(L_ERR,"ERROR:fix_actions: FORWARD/SEND failed to " "add proxy"); return E_CFG; } t->elem[0].type = PROXY_ST; t->elem[0].u.data = (void*)p; break; case IF_T: if (t->elem[0].type!=EXPR_ST){ LOG(L_CRIT, "BUG: fix_actions: invalid subtype" "%d for if (should be expr)\n", t->elem[0].type); return E_BUG; }else if( (t->elem[1].type!=ACTIONS_ST) &&(t->elem[1].type!=NOSUBTYPE) ){ LOG(L_CRIT, "BUG: fix_actions: invalid subtype" "%d for if() {...} (should be action)\n", t->elem[1].type); return E_BUG; }else if( (t->elem[2].type!=ACTIONS_ST) &&(t->elem[2].type!=NOSUBTYPE) ){ LOG(L_CRIT, "BUG: fix_actions: invalid subtype" "%d for if() {} else{...}(should be action)\n", t->elem[2].type); return E_BUG; } if (t->elem[0].u.data){ if ((ret=fix_expr((struct expr*)t->elem[0].u.data))<0) return ret; } if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } if ( (t->elem[2].type==ACTIONS_ST)&&(t->elem[2].u.data) ){ if((ret=fix_actions((struct action*)t->elem[2].u.data))<0) return ret; } break; case SWITCH_T: if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } break; case CASE_T: if ( (t->elem[1].type==ACTIONS_ST)&&(t->elem[1].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[1].u.data))<0) return ret; } break; case DEFAULT_T: if ( (t->elem[0].type==ACTIONS_ST)&&(t->elem[0].u.data) ){ if ((ret=fix_actions((struct action*)t->elem[0].u.data))<0) return ret; } break; case MODULE_T: cmd = (cmd_export_t*)t->elem[0].u.data; DBG("fixing %s, line %d\n", cmd->name, t->line); if (cmd->fixup){ if (cmd->param_no>0){ ret=cmd->fixup(&t->elem[1].u.data, 1); t->elem[1].type=MODFIXUP_ST; if (ret<0) goto error; } if (cmd->param_no>1){ ret=cmd->fixup(&t->elem[2].u.data, 2); t->elem[2].type=MODFIXUP_ST; if (ret<0) goto error; } if (cmd->param_no==0){ ret=cmd->fixup( 0, 0); if (ret<0) goto error; } } break; case FORCE_SEND_SOCKET_T: if (t->elem[0].type!=SOCKID_ST){ LOG(L_CRIT, "BUG: fix_actions: invalid subtype" "%d for force_send_socket\n", t->elem[0].type); return E_BUG; } he=resolvehost(((struct socket_id*)t->elem[0].u.data)->name,0); if (he==0){ LOG(L_ERR, "ERROR: fix_actions: force_send_socket:" " could not resolve %s\n", ((struct socket_id*)t->elem[0].u.data)->name); ret = E_BAD_ADDRESS; goto error; } hostent2ip_addr(&ip, he, 0); si=find_si(&ip, ((struct socket_id*)t->elem[0].u.data)->port, ((struct socket_id*)t->elem[0].u.data)->proto); if (si==0){ LOG(L_ERR, "ERROR: fix_actions: bad force_send_socket" " argument: %s:%d (ser doesn't listen on it)\n", ((struct socket_id*)t->elem[0].u.data)->name, ((struct socket_id*)t->elem[0].u.data)->port); ret = E_BAD_ADDRESS; goto error; } t->elem[0].u.data=si; t->elem[0].type=SOCKETINFO_ST; break; case SETFLAG_T: case RESETFLAG_T: case ISFLAGSET_T: if (t->elem[0].type!=NUMBER_ST) { LOG(L_CRIT, "BUG: fix_actions: bad xxxflag() type %d\n", t->elem[0].type ); ret=E_BUG; goto error; } if (!flag_in_range( t->elem[0].u.number )) { ret=E_CFG; goto error; } break; case SETSFLAG_T: case RESETSFLAG_T: case ISSFLAGSET_T: if (t->elem[0].type!=NUMBER_ST) { LOG(L_CRIT, "BUG: fix_actions: bad xxxsflag() type %d\n", t->elem[0].type ); ret=E_BUG; goto error; } t->elem[0].u.number = fixup_flag( t->elem[0].u.number ); if (t->elem[0].u.data==0) { ret=E_CFG; goto error; } break; case SETBFLAG_T: case RESETBFLAG_T: case ISBFLAGSET_T: if (t->elem[0].type!=NUMBER_ST || t->elem[1].type!=NUMBER_ST) { LOG(L_CRIT, "BUG: fix_actions: bad xxxbflag() type " "%d,%d\n", t->elem[0].type, t->elem[0].type); ret=E_BUG; goto error; } t->elem[1].u.number = fixup_flag( t->elem[1].u.number ); if (t->elem[1].u.data==0) { ret=E_CFG; goto error; } break; case EQ_T: case PLUSEQ_T: case MINUSEQ_T: case DIVEQ_T: case MULTEQ_T: case MODULOEQ_T: case BANDEQ_T: case BOREQ_T: case BXOREQ_T: if (t->elem[1].u.data){ if ((ret=fix_expr((struct expr*)t->elem[1].u.data))<0) return ret; } break; case USE_BLACKLIST_T: if (t->elem[0].type!=STRING_ST) { LOG(L_CRIT, "BUG: fix_actions: bad USE_BLACKLIST type " "%d\n", t->elem[0].type); ret=E_BUG; goto error; } host.s = t->elem[0].u.string; host.len = strlen(host.s); blh = get_bl_head_by_name(&host); if (blh==NULL) { LOG(L_ERR, "ERROR: fix_actions: USE_BLACKLIST - list " "%s not configured\n", t->elem[0].u.string); ret=E_CFG; goto error; } t->elem[0].type = BLACKLIST_ST; t->elem[0].u.data = blh; break; } } return 0; error: LOG(L_ERR,"ERROR: fix_actions: fixing failed (code=%d) at cfg line %d\n", ret, t->line); return ret; } inline static int comp_no( int port, void *param, int op, int subtype ) { if (subtype!=NUMBER_ST) { LOG(L_CRIT, "BUG: comp_no: number expected: %d\n", subtype ); return E_BUG; } switch (op){ case EQUAL_OP: return port==(long)param; case DIFF_OP: return port!=(long)param; case GT_OP: return port>(long)param; case LT_OP: return port<(long)param; case GTE_OP: return port>=(long)param; case LTE_OP: return port<=(long)param; default: LOG(L_CRIT, "BUG: comp_no: unknown operator: %d\n", op ); return E_BUG; } } /* eval_elem helping function, returns str op param */ inline static int comp_strval(struct sip_msg *msg, int op, str* ival, operand_t *opd) { int ret; regex_t* re; char backup; char backup2; str res; xl_value_t value; if(ival==NULL || ival->s==NULL) goto error; res.s = 0; res.len = 0; if(opd->type == SCRIPTVAR_ST) { if(xl_get_spec_value(msg, opd->v.spec, &value, 0)!=0) { LOG(L_CRIT, "comp_strval: cannot get var value\n"); goto error; } if(value.flags&XL_VAL_STR) { res = value.rs; } else { res.s = sint2str(value.ri, &res.len); } } else if(opd->type == NUMBER_ST) { res.s = sint2str(opd->v.n, &res.len); }else if(opd->type == STRING_ST) { res = opd->v.s; } else { if((op!=MATCH_OP && op!=NOTMATCH_OP) || opd->type != RE_ST) { LOG(L_CRIT, "comp_strval: invalid operation %d/%d\n", op, opd->type); goto error; } } ret=-1; switch(op){ case EQUAL_OP: if(ival->len != res.len) return 0; ret=(strncasecmp(ival->s, res.s, ival->len)==0); break; case DIFF_OP: if(ival->len != res.len) return 1; ret=(strncasecmp(ival->s, res.s, ival->len)!=0); break; case MATCH_OP: case NOTMATCH_OP: backup=ival->s[ival->len];ival->s[ival->len]='\0'; if(opd->type == SCRIPTVAR_ST) { re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0){ LOG(L_CRIT, "ERROR: comp_strval: memory allocation" " failure\n"); ival->s[ival->len]=backup; goto error; } backup2 = res.s[res.len];res.s[res.len] = '\0'; if (regcomp(re, res.s, REG_EXTENDED|REG_NOSUB|REG_ICASE)) { pkg_free(re); res.s[res.len] = backup2; ival->s[ival->len]=backup; goto error; } ret=(regexec(re, ival->s, 0, 0, 0)==0); regfree(re); pkg_free(re); res.s[res.len] = backup2; } else { ret=(regexec((regex_t*)opd->v.data, ival->s, 0, 0, 0)==0); } ival->s[ival->len]=backup; if(op==NOTMATCH_OP) ret = !ret; break; default: LOG(L_CRIT, "BUG: comp_strval: unknown op %d\n", op); goto error; } return ret; error: return -1; } /* eval_elem helping function, returns str op param */ inline static int comp_str(char* str, void* param, int op, int subtype) { int ret; ret=-1; switch(op){ case EQUAL_OP: if (subtype!=STRING_ST){ LOG(L_CRIT, "BUG: comp_str: bad type %d, " "string expected\n", subtype); goto error; } ret=(strcasecmp(str, (char*)param)==0); break; case DIFF_OP: if (subtype!=STRING_ST){ LOG(L_CRIT, "BUG: comp_str: bad type %d, " "string expected\n", subtype); goto error; } ret=(strcasecmp(str, (char*)param)!=0); break; case MATCH_OP: if (subtype!=RE_ST){ LOG(L_CRIT, "BUG: comp_str: bad type %d, " " RE expected\n", subtype); goto error; } ret=(regexec((regex_t*)param, str, 0, 0, 0)==0); break; case NOTMATCH_OP: if (subtype!=RE_ST){ LOG(L_CRIT, "BUG: comp_str: bad type %d, " " RE expected!\n", subtype); goto error; } ret=(regexec((regex_t*)param, str, 0, 0, 0)!=0); break; default: LOG(L_CRIT, "BUG: comp_str: unknown op %d\n", op); goto error; } return ret; error: return -1; } /* check_self wrapper -- it checks also for the op */ inline static int check_self_op(int op, str* s, unsigned short p) { int ret; ret=check_self(s, p, 0); switch(op){ case EQUAL_OP: break; case DIFF_OP: if (ret>=0) ret=!ret; break; default: LOG(L_CRIT, "BUG: check_self_op: invalid operator %d\n", op); ret=-1; } return ret; } /* eval_elem helping function, returns an op param */ inline static int comp_ip(struct sip_msg *msg, int op, struct ip_addr* ip, operand_t *opd) { struct hostent* he; char ** h; int ret; str tmp; ret=-1; switch(opd->type){ case NET_ST: switch(op){ case EQUAL_OP: ret=(matchnet(ip, (struct net*)opd->v.data)==1); break; case DIFF_OP: ret=(matchnet(ip, (struct net*)opd->v.data)!=1); break; default: goto error_op; } break; case STRING_ST: case RE_ST: switch(op){ case EQUAL_OP: case MATCH_OP: /* 1: compare with ip2str*/ ret=comp_str(ip_addr2a(ip), opd->v.data, op, opd->type); if (ret==1) break; /* 2: resolve (name) & compare w/ all the ips */ if (opd->type==STRING_ST){ he=resolvehost((char*)opd->v.data,0); if (he==0){ DBG("comp_ip: could not resolve %s\n", (char*)opd->v.data); }else if (he->h_addrtype==(int)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? */ he=rev_resolvehost(ip); if (he==0){ print_ip( "comp_ip: could not rev_resolve ip address:" " ", ip, "\n"); ret=0; }else{ /* compare with primary host name */ ret=comp_str(he->h_name, opd->v.data, op, opd->type); /* compare with all the aliases */ for(h=he->h_aliases; (ret!=1) && (*h); h++){ ret=comp_str(*h, opd->v.data, op, opd->type); } } break; case DIFF_OP: ret=comp_ip(msg, op, ip, opd); if (ret>=0) ret=!ret; break; default: goto error_op; } break; 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); break; default: LOG(L_CRIT, "BUG: comp_ip: invalid type for " " src_ip or dst_ip (%d)\n", opd->type); ret=-1; } return ret; error_op: LOG(L_CRIT, "BUG: comp_ip: invalid operator %d\n", op); return -1; } /* compare str to str */ inline static int comp_s2s(int op, str *s1, str *s2) { char backup; char backup2; int n; int rt; int ret; regex_t* re; ret = -1; /* if any of values is NULL return FALSE */ if(s1->s==NULL || s2->s==NULL) return 0; switch(op) { case EQUAL_OP: if(s1->len != s2->len) return 0; ret=(strncasecmp(s1->s, s2->s, s2->len)==0); break; case DIFF_OP: if(s1->len != s2->len) return 1; ret=(strncasecmp(s1->s, s2->s, s2->len)!=0); break; case GT_OP: n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt>0) ret = 1; else if(rt==0 && s1->len>s1->len) ret = 1; else ret = 0; break; case GTE_OP: n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt>0) ret = 1; else if(rt==0 && s1->len>=s1->len) ret = 1; else ret = 0; break; case LT_OP: n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt<0) ret = 1; else if(rt==0 && s1->lenlen) ret = 1; else ret = 0; break; case LTE_OP: n = (s1->len>=s2->len)?s1->len:s2->len; rt = strncasecmp(s1->s,s2->s, n); if (rt<0) ret = 1; else if(rt==0 && s1->len<=s1->len) ret = 1; else ret = 0; break; case MATCH_OP: backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; ret=(regexec((regex_t*)s2, s1->s, 0, 0, 0)==0); s1->s[s1->len] = backup; break; case NOTMATCH_OP: backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; ret=(regexec((regex_t*)s2, s1->s, 0, 0, 0)!=0); s1->s[s1->len] = backup; break; case MATCHD_OP: case NOTMATCHD_OP: re=(regex_t*)pkg_malloc(sizeof(regex_t)); if (re==0) { LOG(L_CRIT, "ERROR: comp_strval: memory allocation failure\n"); return -1; } backup = s1->s[s1->len]; s1->s[s1->len] = '\0'; backup2 = s2->s[s2->len]; s2->s[s2->len] = '\0'; if (regcomp(re, s2->s, REG_EXTENDED|REG_NOSUB|REG_ICASE)) { pkg_free(re); s2->s[s2->len] = backup2; s1->s[s1->len] = backup; return -1; } if(op==MATCHD_OP) ret=(regexec(re, s1->s, 0, 0, 0)==0); else ret=(regexec(re, s1->s, 0, 0, 0)!=0); regfree(re); pkg_free(re); s2->s[s2->len] = backup2; s1->s[s1->len] = backup; break; default: LOG(L_CRIT, "BUG: comp_s2s: unknown op %d\n", op); } return ret; } /* compare nr to nr */ inline static int comp_n2n(int op, int n1, int n2) { switch(op) { case EQUAL_OP: case MATCH_OP: case MATCHD_OP: if(n1 == n2) return 1; return 0; case NOTMATCH_OP: case NOTMATCHD_OP: case DIFF_OP: if(n1 != n2) return 1; return 0; case GT_OP: if(n1 > n2) return 1; return 0; case GTE_OP: if(n1 >= n2) return 1; return 0; case LT_OP: if(n1 < n2) return 1; return 0; case LTE_OP: if(n1 <= n2) return 1; return 0; default: LOG(L_CRIT, "BUG: comp_n2n: unknown op %d\n", op); } return -1; } inline static int comp_scriptvar(struct sip_msg *msg, int op, operand_t *left, operand_t *right) { str lstr; str rstr; int ln; int rn; xl_value_t lvalue; xl_value_t rvalue; int type; lstr.s = 0; lstr.len = 0; rstr.s = 0; rstr.len = 0; ln = 0; rn =0; if(xl_get_spec_value(msg, left->v.spec, &lvalue, 0)!=0) { LOG(L_CRIT, "comp_scriptvar: cannot get left var value\n"); goto error; } if(right->type==NULLV_ST) { if(op==EQUAL_OP) { if(lvalue.flags&XL_VAL_NULL) return 1; return 0; } else { if(lvalue.flags&XL_VAL_NULL) return 0; return 1; } } lstr = lvalue.rs; ln = lvalue.ri; type = 0; if(right->type == SCRIPTVAR_ST) { if(xl_get_spec_value(msg, right->v.spec, &rvalue, 0)!=0) { LOG(L_CRIT, "comp_scriptvar: cannot get right var value\n"); goto error; } if(rvalue.flags&XL_VAL_NULL || lvalue.flags&XL_VAL_NULL ) { if (rvalue.flags&XL_VAL_NULL && lvalue.flags&XL_VAL_NULL ) return (op==EQUAL_OP)?1:0; return (op==DIFF_OP)?1:0; } if(op==MATCH_OP||op==NOTMATCH_OP) { if(!((rvalue.flags&XL_VAL_STR) && (lvalue.flags&XL_VAL_STR))) { LOG(L_CRIT, "comp_scriptvar: invalid operation %d/%d\n", op, right->type); goto error; } if(op==MATCH_OP) return comp_s2s(MATCHD_OP, &lstr, &rvalue.rs); else return comp_s2s(NOTMATCHD_OP, &lstr, &rvalue.rs); } if((rvalue.flags&XL_VAL_INT) && (lvalue.flags&XL_VAL_INT)) { /* comparing int */ rn = rvalue.ri; type =2; } else if((rvalue.flags&XL_VAL_STR) && (lvalue.flags&XL_VAL_STR)) { /* comparing string */ rstr = rvalue.rs; type =1; } else { LOG(L_CRIT, "comp_scriptvar: invalid operation %d/%d!\n", op, right->type); goto error; } } else { /* null against a not-null constant */ if(lvalue.flags&XL_VAL_NULL) return (op==DIFF_OP || op==NOTMATCH_OP || op==NOTMATCHD_OP)?1:0; if(right->type == NUMBER_ST) { if(!(lvalue.flags&XL_VAL_INT)) { LOG(L_CRIT,"comp_scriptvar: invalid operation %d/%d/%d!!\n", op, right->type, lvalue.flags); goto error; } /* comparing int */ type =2; rn = right->v.n; } else if(right->type == STRING_ST) { if(!(lvalue.flags&XL_VAL_STR)) { LOG(L_CRIT, "comp_scriptvar: invalid operation %d/%d!!!\n", op, right->type); goto error; } /* comparing string */ type =1; rstr = right->v.s; } else { if(op==MATCH_OP || op==NOTMATCH_OP) { if(!(lvalue.flags&XL_VAL_STR) || right->type != RE_ST) { LOG(L_CRIT, "comp_scriptvar: invalid operation %d/%d\n", op, right->type); goto error; } return comp_s2s(op, &lstr, (str*)right->v.expr); } /* comparing others */ type = 0; } } if(type==1) { /* compare str */ DBG("comp_scriptvar: str %d : %.*s\n", op, lstr.len, ZSW(lstr.s)); return comp_s2s(op, &lstr, &rstr); } else if(type==2) { DBG("comp_scriptvar: int %d : %d / %d\n", op, ln, rn); return comp_n2n(op, ln, rn); } else { LOG(L_CRIT, "comp_scriptvar: invalid operation %d/%d\n", op, right->type); } error: return -1; } /* returns: 0/1 (false/true) or -1 on error, -127 EXPR_DROP */ static int eval_elem(struct expr* e, struct sip_msg* msg, xl_value_t *val) { struct sip_uri uri; int ret; int retl; int retr; int ival; xl_value_t lval; xl_value_t rval; char *p; ret=E_BUG; if (e->type!=ELEM_T){ LOG(L_CRIT," BUG: eval_elem: invalid type\n"); goto error; } if(val) memset(val, 0, sizeof(xl_value_t)); switch(e->left.type){ case METHOD_O: ret=comp_strval(msg, e->op, &msg->first_line.u.request.method, &e->right); break; case URI_O: if(msg->new_uri.s){ if (e->right.type==MYSELF_ST){ if (parse_sip_msg_uri(msg)<0) ret=-1; else ret=check_self_op(e->op, &msg->parsed_uri.host, msg->parsed_uri.port_no? msg->parsed_uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &msg->new_uri, &e->right); } }else{ if (e->right.type==MYSELF_ST){ if (parse_sip_msg_uri(msg)<0) ret=-1; else ret=check_self_op(e->op, &msg->parsed_uri.host, msg->parsed_uri.port_no? msg->parsed_uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &msg->first_line.u.request.uri, &e->right); } } break; case FROM_URI_O: if (parse_from_header(msg)<0){ LOG(L_ERR, "ERROR: eval_elem: bad or missing" " From: header\n"); goto error; } if (e->right.type==MYSELF_ST){ if (parse_uri(get_from(msg)->uri.s, get_from(msg)->uri.len, &uri) < 0){ LOG(L_ERR, "ERROR: eval_elem: bad uri in From:\n"); goto error; } ret=check_self_op(e->op, &uri.host, uri.port_no?uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &get_from(msg)->uri, &e->right); } break; case TO_URI_O: if ((msg->to==0) && ((parse_headers(msg, HDR_TO_F, 0)==-1) || (msg->to==0))){ LOG(L_ERR, "ERROR: eval_elem: bad or missing" " To: header\n"); goto error; } /* to content is parsed automatically */ if (e->right.type==MYSELF_ST){ if (parse_uri(get_to(msg)->uri.s, get_to(msg)->uri.len, &uri) < 0){ LOG(L_ERR, "ERROR: eval_elem: bad uri in To:\n"); goto error; } ret=check_self_op(e->op, &uri.host, uri.port_no?uri.port_no:SIP_PORT); }else{ ret=comp_strval(msg, e->op, &get_to(msg)->uri, &e->right); } break; case SRCIP_O: ret=comp_ip(msg, e->op, &msg->rcv.src_ip, &e->right); break; case DSTIP_O: ret=comp_ip(msg, e->op, &msg->rcv.dst_ip, &e->right); break; case NUMBER_O: ret=!(!e->right.v.n); /* !! to transform it in {0,1} */ break; case ACTION_O: ret=run_action_list( (struct action*)e->right.v.data, msg); if(val) { val->flags = XL_TYPE_INT|XL_VAL_INT; val->ri = ret; } if (ret<=0) ret=(ret==0)?EXPR_DROP:0; else ret=1; return ret; case EXPR_O: retl = retr = 0; memset(&lval, 0, sizeof(xl_value_t)); memset(&rval, 0, sizeof(xl_value_t)); if(e->left.v.data) retl=eval_expr((struct expr*)e->left.v.data,msg,&lval); if(lval.flags == XL_VAL_NONE) { xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } if(e->op == BNOT_OP) { if(lval.flags&XL_VAL_INT) { if(val!=NULL) { val->flags = XL_TYPE_INT|XL_VAL_INT; val->ri = ~lval.ri; } xl_value_destroy(&lval); xl_value_destroy(&rval); return (val->ri)?1:0; } LOG(L_ERR, "eval_elem: binary NOT on non-numeric value\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } if(e->right.v.data) retr=eval_expr((struct expr*)e->right.v.data,msg,&rval); if(lval.flags&XL_TYPE_INT) { if(!(rval.flags&XL_VAL_INT)) { LOG(L_ERR, "eval_elem: invalid numeric operands\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } if(val!=NULL) val->flags = XL_TYPE_INT|XL_VAL_INT; ival = 0; switch(e->op) { case PLUS_OP: ival = lval.ri + rval.ri; break; case MINUS_OP: ival = lval.ri - rval.ri; break; case DIV_OP: if(rval.ri==0) { LOG(L_ERR, "eval_elem: divide by 0\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } else ival = lval.ri / rval.ri; break; case MULT_OP: ival = lval.ri * rval.ri; break; case MODULO_OP: if(rval.ri==0) { LOG(L_ERR, "eval_elem: divide by 0\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } else ival = lval.ri % rval.ri; break; case BAND_OP: ival = lval.ri & rval.ri; break; case BOR_OP: ival = lval.ri | rval.ri; break; case BXOR_OP: ival = lval.ri ^ rval.ri; break; default: LOG(L_ERR, "eval_elem: invalid int op %d\n", e->op); val->ri = 0; xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } xl_value_destroy(&lval); xl_value_destroy(&rval); if(val!=NULL) val->ri = ival; return (ival)?1:0; } else { if(!(rval.flags&XL_VAL_STR)) { LOG(L_ERR, "eval_elem: invalid string operands\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } if(e->op != PLUS_OP) { LOG(L_ERR, "eval_elem: invalid string operator %d\n", e->op); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } if(val==NULL) { ret = (lval.rs.len>0 || rval.rs.len>0); xl_value_destroy(&lval); xl_value_destroy(&rval); return ret; } val->rs.s=(char*)pkg_malloc((lval.rs.len+rval.rs.len+1) *sizeof(char)); if(val->rs.s==0) { LOG(L_ERR, "eval_elem: no more memory\n"); xl_value_destroy(&lval); xl_value_destroy(&rval); return 0; } val->flags = XL_VAL_PKG|XL_VAL_STR; memcpy(val->rs.s, lval.rs.s, lval.rs.len); memcpy(val->rs.s+lval.rs.len, rval.rs.s, rval.rs.len); val->rs.len = lval.rs.len + rval.rs.len; val->rs.s[val->rs.len] = '\0'; xl_value_destroy(&lval); xl_value_destroy(&rval); return 1; } break; case SRCPORT_O: ret=comp_no(msg->rcv.src_port, e->right.v.data, /* e.g., 5060 */ e->op, /* e.g. == */ e->right.type /* 5060 is number */); break; case DSTPORT_O: ret=comp_no(msg->rcv.dst_port, e->right.v.data, e->op, e->right.type); break; case PROTO_O: ret=comp_no(msg->rcv.proto, e->right.v.data, e->op, e->right.type); break; case AF_O: ret=comp_no(msg->rcv.src_ip.af, e->right.v.data, e->op, e->right.type); break; case RETCODE_O: ret=comp_no(return_code, e->right.v.data, e->op, e->right.type); break; case MSGLEN_O: ret=comp_no(msg->len, e->right.v.data, e->op, e->right.type); break; case STRINGV_O: if(val) { val->flags = XL_VAL_STR; val->rs = e->left.v.s; } /* optimization for no dup ?!?! */ return (e->left.v.s.len>0)?1:0; case NUMBERV_O: if(val) { val->flags = XL_TYPE_INT|XL_VAL_INT; val->ri = e->left.v.n; } ret=!(!e->left.v.n); /* !! to transform it in {0,1} */ return ret; case SCRIPTVAR_O: if(e->op==NO_OP) { memset(&rval, 0, sizeof(xl_value_t)); if(xl_get_spec_value(msg, e->right.v.spec, &rval, 0)==0) { if(rval.flags==XL_VAL_NONE || (rval.flags&XL_VAL_NULL) || (rval.flags&XL_VAL_EMPTY) || ((rval.flags&XL_TYPE_INT)&&rval.ri==0)) { xl_value_destroy(&rval); return 0; } if(rval.flags&XL_TYPE_INT) { xl_value_destroy(&rval); return 1; } if(rval.rs.len!=0) { xl_value_destroy(&rval); return 1; } xl_value_destroy(&rval); } return 0; } if(e->op==VALUE_OP) { if(xl_get_spec_value(msg, e->left.v.spec, &lval, 0)==0) { if(val!=NULL) memcpy(val, &lval, sizeof(xl_value_t)); if(lval.flags&XL_VAL_STR) { if(!((lval.flags&XL_VAL_PKG) || (lval.flags&XL_VAL_SHM))) { if(val!=NULL) { /* do pkg duplicate */ p = (char*)pkg_malloc((val->rs.len+1) *sizeof(char)); if(p==0) { LOG(L_ERR, "eval_elem: no more memory\n"); memset(val, 0, sizeof(xl_value_t)); return 0; } memcpy(p, val->rs.s, val->rs.len); p[val->rs.len] = 0; val->rs.s = p; val->flags|= XL_VAL_PKG; } } return 1; } if(lval.flags==XL_VAL_NONE || (lval.flags & XL_VAL_NULL) || (lval.flags & XL_VAL_EMPTY)) return 0; if(lval.flags&XL_TYPE_INT) return (lval.ri!=0); else return (lval.rs.len>0); } return 0; } ret=comp_scriptvar(msg, e->op, &e->left, &e->right); break; default: LOG(L_CRIT, "BUG: eval_elem: invalid operand %d\n", e->left.type); } if(val) { val->flags = XL_TYPE_INT|XL_VAL_INT; val->ri = ret; } return ret; error: if(val) { val->flags = XL_TYPE_INT|XL_VAL_INT; val->ri = -1; } return -1; } /* ret= 0/1 (true/false) , -1 on error or EXPR_DROP (-127) */ int eval_expr(struct expr* e, struct sip_msg* msg, xl_value_t *val) { static int rec_lev=0; int ret; rec_lev++; if (rec_lev>MAX_REC_LEV){ LOG(L_CRIT, "ERROR: eval_expr: too many expressions (%d)\n", rec_lev); ret=-1; goto skip; } if (e->type==ELEM_T){ ret=eval_elem(e, msg, val); }else if (e->type==EXP_T){ switch(e->op){ case AND_OP: ret=eval_expr(e->left.v.expr, msg, val); /* if error or false stop evaluating the rest */ if (ret!=1) break; ret=eval_expr(e->right.v.expr, msg, val); /*ret1 is 1*/ break; case OR_OP: ret=eval_expr(e->left.v.expr, msg, val); /* if true or error stop evaluating the rest */ if (ret!=0) break; ret=eval_expr(e->right.v.expr, msg, val); /* ret1 is 0 */ break; case NOT_OP: ret=eval_expr(e->left.v.expr, msg, val); if (ret<0) break; ret= ! ret; break; case EVAL_OP: ret=eval_expr(e->left.v.expr, msg, val); break; default: LOG(L_CRIT, "BUG: eval_expr: unknown op %d\n", e->op); ret=-1; } }else{ LOG(L_CRIT, "BUG: eval_expr: unknown type %d\n", e->type); ret=-1; } skip: rec_lev--; return ret; } /* adds an action list to head; a must be null terminated (last a->next=0))*/ void push(struct action* a, struct action** head) { struct action *t; if (*head==0){ *head=a; return; } for (t=*head; t->next;t=t->next); t->next=a; } int add_actions(struct action* a, struct action** head) { int ret; LOG(L_DBG, "add_actions: fixing actions...\n"); if ((ret=fix_actions(a))!=0) goto error; push(a,head); return 0; error: return ret; } /* fixes all action tables */ /* returns 0 if ok , <0 on error */ int fix_rls() { int i,ret; for(i=0;inext ) { switch (a->type) { case ROUTE_T: /* this route is already on the current path ? */ for( n=0 ; nelem[0].u.number) break; } if (n!=rcheck_stack_p) break; if (++rcheck_stack_p==RT_NO) { LOG(L_CRIT,"BUG:check_actions: stack overflow (%d)\n", rcheck_stack_p); goto error; } rcheck_stack[rcheck_stack_p] = a->elem[0].u.number; if (check_actions( rlist[a->elem[0].u.number], r_type)!=0) goto error; rcheck_stack_p--; break; case IF_T: if (check_actions((struct action*)a->elem[1].u.data, r_type)!=0) goto error; if (check_actions((struct action*)a->elem[2].u.data, r_type)!=0) goto error; break; case SWITCH_T: aitem = (struct action*)a->elem[1].u.data; for( ; aitem ; aitem=aitem->next ) { n = check_actions((struct action*)aitem->elem[1].u.data, r_type); if (n!=0) goto error; } break; case MODULE_T: /* do check :D */ fct = (cmd_export_t*)(a->elem[0].u.data); if ( (fct->flags&r_type)!=r_type ) { rcheck_status = -1; LOG(L_ERR,"ERROR:check_actions: script function " "\"%s\" (types=%d) does not support route type " "(%d)\n",fct->name, fct->flags, r_type); for( n=rcheck_stack_p-1; n>=0 ; n-- ) { LOG(L_ERR,"ERROR:check_actions: route " "stack[%d]=%d\n",n,rcheck_stack[n]); } } break; default: break; } } return 0; error: return -1; } /* check all routing tables for compatiblity between * route types and called module functions; * returns 0 if ok , <0 on error */ int check_rls() { int i,ret; rcheck_status = 0; if(rlist[0]){ if ((ret=check_actions(rlist[0],REQUEST_ROUTE))!=0){ LOG(L_ERR,"ERROR:check_rls: check failed for main " "request route\n"); return ret; } } for(i=0;i