modules/uac_redirect/uac_redirect.c
f8295543
 /*
  * $Id$
  *
  * Copyright (C) 2005 Voice Sistem SRL
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
f8295543
  *
9b64b9f3
  * Kamailio is free software; you can redistribute it and/or
f8295543
  * 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.
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
f8295543
  * 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
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
f8295543
  *
  *
  * History:
  * ---------
  *  2005-06-22  first version (bogdan)
  */
 
 #include <sys/types.h> /* for regex */
 #include <regex.h>
 
 #include "../../sr_module.h"
 #include "../../str.h"
 #include "../../dprint.h"
 #include "../../mem/mem.h"
c5081ad6
 #include "../../lib/srutils/sruid.h"
eb38b3df
 #include "../../modules/tm/tm_load.h"
f8295543
 #include "rd_funcs.h"
 #include "rd_filter.h"
 
 MODULE_VERSION
 
 /* internal global variables */
 struct tm_binds rd_tmb;           /*imported functions from tm */
 cmd_function    rd_acc_fct = 0;   /*imported function from acc */
 
 /* global parameter variables */
 char *acc_db_table = "acc";
 char *acc_fct_s    = "acc_log_request";
 
 /* private parameter variables */
 char *deny_filter_s = 0;
 char *accept_filter_s = 0;
 char *def_filter_s = 0;
 
ecee1c93
 unsigned int bflags = 0;
f8295543
 
 #define ACCEPT_RULE_STR "accept"
 #define DENY_RULE_STR   "deny"
 
c5081ad6
 /* sruid to get internal uid */
 sruid_t _redirect_sruid;
f8295543
 
 
 static int redirect_init(void);
c5081ad6
 static int child_init(int rank);
f8295543
 static int w_set_deny(struct sip_msg* msg, char *dir, char *foo);
 static int w_set_accept(struct sip_msg* msg, char *dir, char *foo);
 static int w_get_redirect1(struct sip_msg* msg, char *dir, char *foo);
 static int w_get_redirect2(struct sip_msg* msg, char *dir, char *foo);
 static int regexp_compile(char *re_s, regex_t **re);
 static int get_redirect_fixup(void** param, int param_no);
 static int setf_fixup(void** param, int param_no);
 
 
 static cmd_export_t cmds[] = {
c4d2e802
 	{"set_deny_filter",   (cmd_function)w_set_deny,      2,  setf_fixup, 0,
f8295543
 			FAILURE_ROUTE },
c4d2e802
 	{"set_accept_filter", (cmd_function)w_set_accept,    2,  setf_fixup, 0,
f8295543
 			FAILURE_ROUTE },
c4d2e802
 	{"get_redirects",     (cmd_function)w_get_redirect2,  2,  get_redirect_fixup, 0,
f8295543
 			FAILURE_ROUTE },
c4d2e802
 	{"get_redirects",     (cmd_function)w_get_redirect1,  1,  get_redirect_fixup, 0,
f8295543
 			FAILURE_ROUTE },
80998a7f
 	{0, 0, 0, 0, 0, 0}
f8295543
 };
 
 static param_export_t params[] = {
48065a04
 	{"deny_filter",     PARAM_STRING,  &deny_filter_s    },
 	{"accept_filter",   PARAM_STRING,  &accept_filter_s  },
 	{"default_filter",  PARAM_STRING,  &def_filter_s     },
 	{"acc_function",    PARAM_STRING,  &acc_fct_s        },
 	{"acc_db_table",    PARAM_STRING,  &acc_db_table     },
ecee1c93
 	{"bflags",    		INT_PARAM,  &bflags			  },
f8295543
 	{0, 0, 0}
 };
 
 
 struct module_exports exports = {
 	"uac_redirect",
51716422
 	DEFAULT_DLFLAGS, /* dlopen flags */
f8295543
 	cmds,     /* Exported functions */
 	params,   /* Exported parameters */
739aec8b
 	0,        /* exported statistics */
de26de44
 	0,        /* exported MI functions */
71c26efd
 	0,        /* exported pseudo-variables */
3eee3a4e
 	0,        /* extra processes */
f8295543
 	redirect_init, /* Module initialization function */
3ed44241
 	0,
 	0,
c5081ad6
 	child_init /* per-child init function */
f8295543
 };
 
 
 
 int get_nr_max(char *s, unsigned char *max)
 {
 	unsigned short nr;
 	int err;
 
 	if ( s[0]=='*' && s[1]==0 ) {
 		/* is '*' -> infinit ;-) */
 		*max = 0;
 		return 0;
 	} else {
 		/* must be a positive number less than 255 */
 		nr = str2s(s, strlen(s), &err);
 		if (err==0){
 			if (nr>255){
de7fe5e9
 				LM_ERR("number too big <%d> (max=255)\n",nr);
f8295543
 				return -1;
 			}
 			*max = (unsigned char)nr;
 			return 0;
 		}else{
de7fe5e9
 			LM_ERR("bad  number <%s>\n",s);
f8295543
 			return -1;
 		}
 	}
 }
 
 
 static int get_redirect_fixup(void** param, int param_no)
 {
 	unsigned char maxb,maxt;
c0d6509a
 	struct acc_param *accp;
f8295543
 	cmd_function fct;
 	char *p;
 	char *s;
 
 	s = (char*)*param;
 	if (param_no==1) {
 		if ( (p=strchr(s,':'))!=0 ) {
 			/* have max branch also */
 			*p = 0;
 			if (get_nr_max(p+1, &maxb)!=0)
 				return E_UNSPEC;
 		} else {
 			maxb = 0; /* infinit */
 		}
 
 		/* get max total */
 		if (get_nr_max(s, &maxt)!=0)
 			return E_UNSPEC;
 
 		pkg_free(*param);
ca4cc1d9
 		*param=(void*)(long)( (((unsigned short)maxt)<<8) | maxb);
f8295543
 	} else if (param_no==2) {
 		/* acc function loaded? */
3d33733f
 		if (rd_acc_fct==0) {
 			/* must import the acc stuff */
 			if (acc_fct_s==0 || acc_fct_s[0]==0) {
 				LM_ERR("acc support enabled, but no acc function defined\n");
 				return E_UNSPEC;
 			}
 			fct = find_export(acc_fct_s, 2, REQUEST_ROUTE);
 			if ( fct==0 )
 				fct = find_export(acc_fct_s, 1, REQUEST_ROUTE);
 			if ( fct==0 ) {
 				LM_ERR("cannot import %s function; is acc loaded and proper "
 					"compiled?\n", acc_fct_s);
 				return E_UNSPEC;
 			}
 			rd_acc_fct = fct;
bb224406
 		}
 		/* set the reason str */
c0d6509a
 		accp = (struct acc_param*)pkg_malloc(sizeof(struct acc_param));
 		if (accp==0) {
de7fe5e9
 			LM_ERR("no more pkg mem\n");
bb224406
 			return E_UNSPEC;
 		}
c0d6509a
 		memset( accp, 0, sizeof(struct acc_param));
f8295543
 		if (s!=0 && *s!=0) {
c0d6509a
 			accp->reason.s = s;
 			accp->reason.len = strlen(s);
bb224406
 		} else {
c0d6509a
 			accp->reason.s = "n/a";
 			accp->reason.len = 3;
f8295543
 		}
c0d6509a
 		*param=(void*)accp;
f8295543
 	}
 
 	return 0;
 }
 
 
 static int setf_fixup(void** param, int param_no)
 {
 	unsigned short nr;
 	regex_t *filter;
 	char *s;
 
 	s = (char*)*param;
 	if (param_no==1) {
 		/* compile the filter */
 		if (regexp_compile( s, &filter)<0) {
de7fe5e9
 			LM_ERR("cannot init filter <%s>\n", s);
f8295543
 			return E_BAD_RE;
 		}
 		pkg_free(*param);
 		*param = (void*)filter;
 	} else if (param_no==2) {
 		if (s==0 || s[0]==0) {
 			nr = 0;
 		} else if (strcasecmp(s,"reset_all")==0) {
 			nr = RESET_ADDED|RESET_DEFAULT;
 		} else if (strcasecmp(s,"reset_default")==0) {
 			nr = RESET_DEFAULT;
 		} else if (strcasecmp(s,"reset_added")==0) {
 			nr = RESET_ADDED;
 		} else {
de7fe5e9
 			LM_ERR("unknown reset type <%s>\n",s);
f8295543
 			return E_UNSPEC;
 		}
 		pkg_free(*param);
 		*param = (void*)(long)nr;
 	}
 
 	return 0;
 }
 
 
 
 static int regexp_compile(char *re_s, regex_t **re)
 {
 	*re = 0;
 	if (re_s==0 || strlen(re_s)==0 ) {
 		return 0;
 	} else {
 		if ((*re=pkg_malloc(sizeof(regex_t)))==0)
 			return E_OUT_OF_MEM;
 		if (regcomp(*re, re_s, REG_EXTENDED|REG_ICASE|REG_NEWLINE) ){
 			pkg_free(*re);
 			*re = 0;
de7fe5e9
 			LM_ERR("regexp_compile:bad regexp <%s>\n", re_s);
f8295543
 			return E_BAD_RE;
 		}
 	}
 	return 0;
 }
 
 
 
 static int redirect_init(void)
 {
 	regex_t *filter;
3d33733f
 	void *p;
f8295543
 
 	/* load the TM API */
 	if (load_tm_api(&rd_tmb)!=0) {
de7fe5e9
 		LM_ERR("failed to load TM API\n");
f8295543
 		goto error;
 	}
 
3d33733f
 	p = (void*)acc_db_table;
 	/* fixup table name */
 	if(fixup_var_pve_str_12(&p, 1)<0) {
 		LM_ERR("failed to fixup acc db table\n");
 		goto error;
 	}
 	acc_db_table = p;
 
f8295543
 	/* init filter */
 	init_filters();
 
 	/* what's the default rule? */
 	if (def_filter_s) {
 		if ( !strcasecmp(def_filter_s,ACCEPT_RULE_STR) ) {
 			set_default_rule( ACCEPT_RULE );
 		} else if ( !strcasecmp(def_filter_s,DENY_RULE_STR) ) {
 			set_default_rule( DENY_RULE );
 		} else {
de7fe5e9
 			LM_ERR("unknown default filter <%s>\n",def_filter_s);
f8295543
 		}
 	}
 
 	/* if accept filter specify, compile it */
 	if (regexp_compile(accept_filter_s, &filter)<0) {
de7fe5e9
 		LM_ERR("failed to init accept filter\n");
f8295543
 		goto error;
 	}
 	add_default_filter( ACCEPT_FILTER, filter);
 
 	/* if deny filter specify, compile it */
 	if (regexp_compile(deny_filter_s, &filter)<0) {
de7fe5e9
 		LM_ERR("failed to init deny filter\n");
f8295543
 		goto error;
 	}
 	add_default_filter( DENY_FILTER, filter);
 
c5081ad6
 	if(sruid_init(&_redirect_sruid, '-', "rdir", SRUID_INC)<0)
 		return -1;
 
f8295543
 	return 0;
 error:
 	return -1;
 }
 
c5081ad6
 static int child_init(int rank)
 {
 	if(sruid_init(&_redirect_sruid, '-', "rdir", SRUID_INC)<0)
 		return -1;
 	return 0;
 }
f8295543
 
 static inline void msg_tracer(struct sip_msg* msg, int reset)
 {
c7231925
 	static unsigned int id  = 0;
 	static unsigned int set = 0;
f8295543
 
 	if (reset) {
 		set = 0;
 	} else {
 		if (set) {
 			if (id!=msg->id) {
de7fe5e9
 				LM_WARN("filters set but not used -> reseting to default\n");
f8295543
 				reset_filters();
 				id = msg->id;
 			}
 		} else {
 			id = msg->id;
 			set = 1;
 		}
 	}
 }
 
 
 static int w_set_deny(struct sip_msg* msg, char *re, char *flags)
 {
 	msg_tracer( msg, 0);
 	return (add_filter( DENY_FILTER, (regex_t*)re, (int)(long)flags)==0)?1:-1;
 }
 
 
 static int w_set_accept(struct sip_msg* msg, char *re, char *flags)
 {
 	msg_tracer( msg, 0);
 	return (add_filter( ACCEPT_FILTER, (regex_t*)re, (int)(long)flags)==0)?1:-1;
 }
 
 
 static int w_get_redirect2(struct sip_msg* msg, char *max_c, char *reason)
 {
 	int n;
 	unsigned short max;
 
 	msg_tracer( msg, 0);
 	/* get the contacts */
 	max = (unsigned short)(long)max_c;
ecee1c93
 	n = get_redirect(msg , (max>>8)&0xff, max&0xff, (struct acc_param*)reason, bflags);
f8295543
 	reset_filters();
 	/* reset the tracer */
 	msg_tracer( msg, 1);
 
 	return n;
 }
 
c0d6509a
 
f8295543
 static int w_get_redirect1(struct sip_msg* msg, char *max_c, char *foo)
 {
 	return w_get_redirect2(msg, max_c, 0);
 }