modules/drouting/routing.c
08e1be29
 /*
0fbd2328
  * $Id$
08e1be29
  *
0fbd2328
  * Copyright (C) 2005-2009 Voice Sistem SRL
08e1be29
  *
0fbd2328
  * This file is part of Kamailio, a free SIP server.
08e1be29
  *
0fbd2328
  * Kamailio is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
08e1be29
  *
0fbd2328
  * Kamailio is distributed in the hope that it will be useful,
08e1be29
  * 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.
08e1be29
  *
  * History:
  * ---------
  *  2005-02-20  first version (cristian)
  *  2005-02-27  ported to 0.9.0 (bogdan)
  */
 
 
 #include <stdlib.h>
 #include <errno.h>
 #include <limits.h>
 
 #include "../../str.h"
 #include "../../resolve.h"
 #include "../../mem/shm_mem.h"
 #include "../../parser/parse_uri.h"
 
 #include "routing.h"
 #include "prefix_tree.h"
 #include "dr_time.h"
 #include "parse.h"
 
 #define IDX_SIZE 32
 
 extern int dr_force_dns;
 
 rt_data_t*
 build_rt_data( void )
 {
 	rt_data_t *rdata;
 
 	if( NULL==(rdata=shm_malloc(sizeof(rt_data_t)))) {
 		LM_ERR("no more shm mem\n");
 		goto err_exit;
 	}
 	memset(rdata, 0, sizeof(rt_data_t));
 
 	INIT_PTREE_NODE(NULL, rdata->pt);
 
 	return rdata;
 err_exit:
 	return 0;
 }
 
 
 rt_info_t*
 build_rt_info(
 	int priority,
 	tmrec_t *trec,
 	/* script routing table index */
 	int route_idx,
 	/* list of destinations indexes */
 	char* dstlst,
 	pgw_t* pgw_l
 	) 
 {
 	char *tmp=NULL;
 	char *ep=NULL;
 	rt_info_t* rt = NULL;
 	int *idx = NULL, *t_idx=NULL;
 	int n=0, idx_size=0,i, grp_idx=0;
 	long t=0;
 	pgw_t *pgw=NULL;
 
 	if(NULL == (rt = (rt_info_t*)shm_malloc(sizeof(rt_info_t)))) {
 		LM_ERR("no more shm mem(1)\n");
 		goto err_exit;
 	}
 	memset(rt, 0, sizeof(rt_info_t));
 
 	idx_size = IDX_SIZE;
 	if( NULL == (idx = (int*)shm_malloc(2*idx_size*sizeof(int)))) {
 		LM_ERR("no more shm mem(2)\n");
 		goto err_exit;
 	}
 	memset(idx, 0, 2*idx_size*sizeof(int));
 
 	rt->priority = priority;
 	rt->time_rec = trec;
 	rt->route_idx = route_idx;
 	tmp=dstlst;
 	n=0;
 	/* parse the dstlst */
 	while(tmp && (*tmp!=0)) {
 		errno = 0;
 		t = strtol(tmp, &ep, 10);
 		if (ep == tmp) {
 			LM_ERR("bad id '%c' (%d)[%s]\n",
 					*ep, (int)(ep-dstlst), dstlst);
 			goto err_exit;
 		}
 		if ((!IS_SPACE(*ep)) && (*ep != SEP) && (*ep != SEP1)
 				&& (*ep != SEP_GRP) && (*ep!=0)) {
 			LM_ERR("bad char %c (%d) [%s]\n",
 					*ep, (int)(ep-dstlst), dstlst);
 			goto err_exit;
 		}
 		if (errno == ERANGE && (t== LONG_MAX || t== LONG_MIN)) {
 			LM_ERR("out of bounds\n");
 			goto err_exit;
 		}
 		idx[2*n]=t;
 		idx[2*n+1]=grp_idx;
 		if(*ep == SEP_GRP)
 			grp_idx++;
 		n++;
 		/* reallocate the array which keeps the parsed indexes */
 		if(n>=idx_size){
 			if(NULL==((t_idx)=(int*)shm_malloc((idx_size*2*2)*sizeof(int)))) {
 				LM_ERR("out of shm\n");
 				goto err_exit;
 			}
 			memset(t_idx+(2*idx_size), 0, 2*idx_size*sizeof(int));
 			memcpy(t_idx, idx, 2*idx_size*sizeof(int));
 			shm_free(idx);
 			idx_size*=2;
 			idx=t_idx;
 		}
 		if(IS_SPACE(*ep))
 			EAT_SPACE(ep);
 		if(ep && (*ep == SEP || *ep == SEP1 || *ep == SEP_GRP))
 			ep++;
 		tmp = ep;
 	}
 	if(n==0) {
 		LM_ERR("invalid n\n");
 		goto err_exit;
 	}
 	/* create the pgwl */
 	rt->pgwa_len = n;
 	if(NULL ==
 		(rt->pgwl=(pgw_list_t*)shm_malloc(rt->pgwa_len*sizeof(pgw_list_t)))) {
 		goto err_exit;
 	}
 	memset(rt->pgwl, 0, rt->pgwa_len*sizeof(pgw_list_t));
 	/* translate GW ids to GW pointers */
 	for(i=0;i<n; i++){
 		if ( NULL == (pgw = get_pgw(pgw_l, idx[2*i]))) {
 			LM_ERR("invalid GW id %d\n",
 				idx[2*i]);
 			goto err_exit;
 		}
 		rt->pgwl[i].pgw=pgw;
 		rt->pgwl[i].grpid=idx[2*i+1];
 		/* LM_DBG("added to gwlist [%d/%d/%p]\n",
 				idx[2*i], idx[2*i+1], pgw); */
 	}
 
 	shm_free(idx);
 	return rt;
 
 err_exit:
 	if(NULL!=idx)
 		shm_free(idx);
 	if((NULL != rt) && 
 		(NULL!=rt->pgwl))
 		shm_free(rt->pgwl); 
 	if(NULL!=rt)
 		shm_free(rt);
 	return NULL;
 }
 
 int
 add_rt_info(
 	ptree_node_t *pn,
 	rt_info_t* r,
 	unsigned int rgid
 	)
 {
 	rg_entry_t    *trg=NULL;
 	rt_info_wrp_t *rtl_wrp=NULL;
 	rt_info_wrp_t *rtlw=NULL;
 	int i=0;
 
 	if((NULL == pn) || (NULL == r))
 		goto err_exit;
 
 	if (NULL == (rtl_wrp = (rt_info_wrp_t*)shm_malloc(sizeof(rt_info_wrp_t)))) {
 		LM_ERR("no more shm mem\n");
 		goto err_exit;
 	}
 	memset( rtl_wrp, 0, sizeof(rt_info_wrp_t));
 	rtl_wrp->rtl = r;
 
 	if(NULL==pn->rg) {
 		/* allocate the routing groups array */
 		pn->rg_len = RG_INIT_LEN;
 		if(NULL == (pn->rg = (rg_entry_t*)shm_malloc(
 						pn->rg_len*sizeof(rg_entry_t)))) {
 			/* recover the old pointer to be able to shm_free mem */
 			goto err_exit;
 		}
 		memset( pn->rg, 0, pn->rg_len*sizeof(rg_entry_t));
 		pn->rg_pos=0;
 	}
 	/* search for the rgid up to the rg_pos */
 	for(i=0; (i<pn->rg_pos) && (pn->rg[i].rgid!=rgid); i++);
 	if((i==pn->rg_len-1)&&(pn->rg[i].rgid!=rgid)) {
 		/* realloc & copy the old rg */
 		trg = pn->rg;
 		if(NULL == (pn->rg = (rg_entry_t*)shm_malloc(
 						2*pn->rg_len*sizeof(rg_entry_t)))) {
 			/* recover the old pointer to be able to shm_free mem */
 			pn->rg = trg;
 			goto err_exit;
 		}
 		memset(pn->rg+pn->rg_len, 0, pn->rg_len*sizeof(rg_entry_t));
 		memcpy(pn->rg, trg, pn->rg_len*sizeof(rg_entry_t));
 		pn->rg_len*=2;
 		shm_free( trg );
 	}
 	/* insert into list */
 	r->ref_cnt++;
 	if(NULL==pn->rg[i].rtlw){
 		pn->rg[i].rtlw = rtl_wrp;
 		pn->rg[i].rgid = rgid;
 		pn->rg_pos++;
 		goto ok_exit;
 	}
 	if( r->priority > pn->rg[i].rtlw->rtl->priority) {
 		/* change the head of the list */
 		rtl_wrp->next = pn->rg[i].rtlw;
 		pn->rg[i].rtlw = rtl_wrp;
 		goto ok_exit;
 	}
 	rtlw = pn->rg[i].rtlw;
 	while( rtlw->next !=NULL) {
 		if(r->priority > rtlw->next->rtl->priority) {
 			rtl_wrp->next = rtlw->next;
 			rtlw->next = rtl_wrp;
 			goto ok_exit;
 		}
 		rtlw = rtlw->next;
 	}
 	/* the smallest priority is linked at the end */
 	rtl_wrp->next=NULL;
 	rtlw->next=rtl_wrp;
 ok_exit:
 	return 0;
 
 err_exit:
 	if (rtl_wrp) shm_free(rtl_wrp);
 	return -1;
 }
 
 int
 add_dst(
 	rt_data_t *r,
 	/* id */
 	int id,
 	/* ip address */ 
 	char* ip,
 	/* strip len */
 	int strip,
 	/* pri prefix */
 	char* pri,
 	/* dst type*/
 	int type,
 	/* dst attrs*/
 	char* attrs
 	)
 {
 	pgw_t *pgw=NULL, *tmp=NULL;
 	pgw_addr_t *tmpa=NULL;
 	struct hostent* he;
 	struct sip_uri uri;
 	struct ip_addr ipa;
 	int l_ip,l_pri,l_attrs;
 #define GWABUF_MAX_SIZE	512
 	char gwabuf[GWABUF_MAX_SIZE];
 	str gwas;
 
 	if (NULL==r || NULL==ip) {
 		LM_ERR("invalid parametres\n");
 		goto err_exit;
 	}
 
 	l_ip = strlen(ip);
 	l_pri = pri?strlen(pri):0;
 	l_attrs = attrs?strlen(attrs):0;
 
 	pgw = (pgw_t*)shm_malloc(sizeof(pgw_t) + l_ip + l_pri + l_attrs);
 	if (NULL==pgw) {
 		LM_ERR("no more shm mem (%u)\n",
 			(unsigned int)(sizeof(pgw_t)+l_ip+l_pri +l_attrs));
 		goto err_exit;
 	}
 	memset(pgw,0,sizeof(pgw_t));
 
 	pgw->ip.len= l_ip;
 	pgw->ip.s = (char*)(pgw+1);
 	memcpy(pgw->ip.s, ip, l_ip);
 
 	if (pri) {
 		pgw->pri.len = l_pri;
 		pgw->pri.s = ((char*)(pgw+1))+l_ip;
 		memcpy(pgw->pri.s, pri, l_pri);
 	}
 	if (attrs) {
 		pgw->attrs.len = l_attrs;
 		pgw->attrs.s = ((char*)(pgw+1))+l_ip+l_pri;
 		memcpy(pgw->attrs.s, attrs, l_attrs);
 	}
 	pgw->id = id;
 	pgw->strip = strip;
 	pgw->type = type;
 
 	/* add address in the list */
 	if(pgw->ip.len<5 || (strncasecmp("sip:", ip, 4)
 			&&strncasecmp("sips:", ip, 5)))
 	{
 		if(pgw->ip.len+4>=GWABUF_MAX_SIZE) {
 			LM_ERR("GW address (%d) longer "
 				"than %d\n",pgw->ip.len+4,GWABUF_MAX_SIZE);
 			goto err_exit;
 		}
 		memcpy(gwabuf, "sip:", 4);
 		memcpy(gwabuf+4, ip, pgw->ip.len);
 		gwas.s = gwabuf;
 		gwas.len = 4+pgw->ip.len;
 	} else {
 		gwas.s = ip;
 		gwas.len = pgw->ip.len;
 	}
 
 	memset(&uri, 0, sizeof(struct sip_uri));
 	if(parse_uri(gwas.s, gwas.len, &uri)!=0) {
 		LM_ERR("invalid uri <%.*s>\n",
 			gwas.len, gwas.s);
 		goto err_exit;
 	}
 	/* note we discard the port discovered by the resolve function - we are
 	interested only in the port that was actually configured. */
140d0a35
 	if ((he=sip_resolvehost( &uri.host, NULL, (char*)(void*)&uri.proto))==0 ) {
08e1be29
 		if(dr_force_dns)
 		{
 			LM_ERR("cannot resolve <%.*s>\n",
 				uri.host.len, uri.host.s);
 			goto err_exit;
 		} else {
 			LM_DBG("cannot resolve <%.*s> - won't be used"
 					" by is_from_gw()\n", uri.host.len, uri.host.s);
 			goto done;
 		}
 	}
 	hostent2ip_addr(&ipa, he, 0);
 	tmpa = r->pgw_addr_l;
 	while(tmpa) {
 		if(tmpa->type==type && uri.port_no==tmpa->port
 		&& ip_addr_cmp(&ipa, &tmpa->ip)) {
 			LM_DBG("gw ip addr [%s]:%d loaded\n",
 				ip_addr2a(&ipa), uri.port_no);
 			goto done;
 		}
 		tmpa = tmpa->next;
 	}
 	
 	LM_DBG("new gw ip addr [%s]\n", ip);
 	tmpa = (pgw_addr_t*)shm_malloc(sizeof(pgw_addr_t));
 	if(tmpa==NULL) {
 		LM_ERR("no more shm mem (%u)\n",
 			(unsigned int)sizeof(pgw_addr_t));
 		goto err_exit;
 	}
 	memset(tmpa, 0, sizeof(pgw_addr_t));
 	memcpy(&tmpa->ip, &ipa, sizeof(struct ip_addr));
 	tmpa->port = uri.port_no;
 	tmpa->type = type;
 	tmpa->strip = strip;
 	tmpa->next = r->pgw_addr_l;
 	r->pgw_addr_l = tmpa;
 
 done:
 	if(NULL==r->pgw_l)
 		r->pgw_l = pgw;
 	else {
 		tmp = r->pgw_l;
 		while(NULL != tmp->next)
 			tmp = tmp->next;
 		tmp->next = pgw;
 	}
 	return 0;
 
 err_exit:
 	if(NULL!=pgw)
 		shm_free(pgw);
 	return -1;
 }
 
 void
 del_pgw_list(
 		pgw_t *pgw_l
 		)
 {
 	pgw_t *t;
 	while(NULL!=pgw_l){
 		t = pgw_l;
 		pgw_l=pgw_l->next;
 		shm_free(t);
 	}
 }
 
 void
 del_pgw_addr_list(
 		pgw_addr_t *pgw_addr_l
 		)
 {
 	pgw_addr_t *t;
 	while(NULL!=pgw_addr_l){
 		t = pgw_addr_l;
 		pgw_addr_l=pgw_addr_l->next;
 		shm_free(t);
 	}
 }
 
 void 
 free_rt_data(
 		rt_data_t* rt_data,
 		int all
 		)
 {
 	int j;
 	if(NULL!=rt_data) {
 		/* del GW list */
 		del_pgw_list(rt_data->pgw_l);
 		rt_data->pgw_l = 0 ;
 		/* del GW addr list */
 		del_pgw_addr_list(rt_data->pgw_addr_l);
 		rt_data->pgw_addr_l =0;
 		/* del prefix tree */
 		del_tree(rt_data->pt);
 		/* del prefixless rules */
 		if(NULL!=rt_data->noprefix.rg) {
 			for(j=0;j<rt_data->noprefix.rg_pos;j++) {
 				if(rt_data->noprefix.rg[j].rtlw !=NULL) {
 					del_rt_list(rt_data->noprefix.rg[j].rtlw);
 					rt_data->noprefix.rg[j].rtlw = 0;
 				}
 			}
 			shm_free(rt_data->noprefix.rg);
 			rt_data->noprefix.rg = 0;
 		}
c737ff95
 		/* del top level or reset to 0 it's content */
08e1be29
 		if (all) shm_free(rt_data);
c737ff95
 		else memset(rt_data, 0, sizeof(rt_data_t));
08e1be29
 	}
 }