src/modules/uid_domain/uid_domain_mod.c
67cbb9ab
 /*
09befa54
  * Domain module
  *
  * Copyright (C) 2002-2003 Juha Heinanen
  *
aa4e8fb0
  * This file is part of sip-router, a free SIP server.
09befa54
  *
aa4e8fb0
  * sip-router 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
09befa54
  *
aa4e8fb0
  * sip-router 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.
09befa54
  *
aa4e8fb0
  * 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.,
9e1ff448
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
d69aa8d7
  *
  * History:
  * -------
  * 2003-03-11: New module interface (janakj)
c6c9f233
  * 2003-03-16: flags export parameter added (janakj)
34dbe688
  * 2003-04-05: default_uri #define used (jiri)
d8fe42f0
  * 2003-04-06: db connection closed in mod_init (janakj)
f56a6186
  * 2004-06-06  updated to the new DB api, cleanup: static dbf & handler,
  *              calls to domain_db_{bind,init,close,ver} (andrei)
09befa54
  */
 
e4eb153e
 #include "uid_domain_mod.h"
09befa54
 #include <stdio.h>
cf83221d
 #include "../../core/mem/shm_mem.h"
 #include "../../core/mem/mem.h"
 #include "../../core/sr_module.h"
 #include "../../core/ut.h"
 #include "../../core/parser/parse_from.h"
 #include "../../core/parser/parse_uri.h"
 #include "../../core/usr_avp.h"
dd549050
 #include "domain_api.h"
c9e9a023
 #include "domain_rpc.h"
b5018dc1
 #include "hash.h"
c9e9a023
 #include "domain.h"
 
09befa54
 
 /*
  * Module management function prototypes
  */
 static int mod_init(void);
 static void destroy(void);
 static int child_init(int rank);
da586335
 
 static int is_local(struct sip_msg* msg, char* s1, char* s2);
f3b34539
 static int lookup_domain(struct sip_msg* msg, char* s1, char* s2);
c1285373
 static int get_did(str* did, str* domain);
da586335
 
f3b34539
 static int lookup_domain_fixup(void** param, int param_no);
09befa54
 
ffc62e55
 MODULE_VERSION
caa40974
 
1f45aade
 /*
bc807b61
  * Version of domain table required by the module, increment this value if you
  * change the table in an backwards incompatible way
1f45aade
  */
b5018dc1
 #define DOMAIN_TABLE_VERSION 2
 #define DOMATTR_TABLE_VERSION 1
1f45aade
 
e4eb153e
 #define DOMAIN_TABLE  "uid_domain"
b5018dc1
 #define DOMAIN_COL    "domain"
 #define DID_COL       "did"
 #define FLAGS_COL     "flags"
1f45aade
 
e4eb153e
 #define DOMATTR_TABLE "uid_domain_attrs"
b5018dc1
 #define DOMATTR_DID   "did"
 #define DOMATTR_NAME  "name"
 #define DOMATTR_TYPE  "type"
 #define DOMATTR_VALUE "value"
 #define DOMATTR_FLAGS "flags"
 #define DOMAIN_COL    "domain"
 
a4adf6b9
 int db_mode = 1;  /* Enable/disable domain cache */
1f45aade
 
09befa54
 /*
  * Module parameter variables
  */
03f6db11
 static str db_url = STR_STATIC_INIT(DEFAULT_RODB_URL);
09befa54
 
b5018dc1
 str domain_table = STR_STATIC_INIT(DOMAIN_TABLE); /* Name of domain table */
c1285373
 str domain_col   = STR_STATIC_INIT(DOMAIN_COL);   /* Name of domain column */
b5018dc1
 str did_col      = STR_STATIC_INIT(DID_COL);      /* Domain id */
 str flags_col    = STR_STATIC_INIT(FLAGS_COL);    /* Domain flags */
 
 str domattr_table = STR_STATIC_INIT(DOMATTR_TABLE);
 str domattr_did   = STR_STATIC_INIT(DOMATTR_DID);
 str domattr_name  = STR_STATIC_INIT(DOMATTR_NAME);
 str domattr_type  = STR_STATIC_INIT(DOMATTR_TYPE);
 str domattr_value = STR_STATIC_INIT(DOMATTR_VALUE);
 str domattr_flags = STR_STATIC_INIT(DOMATTR_FLAGS);
 
 int load_domain_attrs = 0;  /* Load attributes for each domain by default */
 
75a33fc7
 static db_ctx_t* db = NULL;
 db_cmd_t* load_domains_cmd = NULL, *get_did_cmd = NULL, *load_attrs_cmd = NULL;
b5018dc1
 
 struct hash_entry*** active_hash = 0; /* Pointer to current hash table */
 struct hash_entry** hash_1 = 0;       /* Pointer to hash table 1 */
 struct hash_entry** hash_2 = 0;       /* Pointer to hash table 2 */
 
 domain_t** domains_1 = 0;    /* List of domains 1 */
 domain_t** domains_2 = 0;    /* List of domains 2 */
09befa54
 
a4adf6b9
 /* Global domain structure, this one is used to store data retrieved from
bc807b61
  * database when memory cache is disabled. There is one buffer for from and
  * one buffer for to track.
a4adf6b9
  */
 static domain_t dom_buf[2];
d69aa8d7
 
 /*
  * Exported functions
  */
 static cmd_export_t cmds[] = {
db499f51
 	{"is_local",      is_local,              1, fixup_var_str_1, 0,
bc807b61
 	 REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
db499f51
 	{"lookup_domain", lookup_domain,         2, lookup_domain_fixup, 0,
bc807b61
 	 REQUEST_ROUTE|FAILURE_ROUTE },
db499f51
 	{"get_did",       (cmd_function)get_did, 0, 0, 0, 0},
 	{"bind_domain",   (cmd_function)bind_domain, 0, 0, 0, 0},
 	{0, 0, 0, 0, 0, 0}
d69aa8d7
 };
 
 
 /*
  * Exported parameters
  */
 static param_export_t params[] = {
bc807b61
 	{"db_url",	          PARAM_STR, &db_url           },
 	{"db_mode",           PARAM_INT, &db_mode          },
 	{"domain_table",      PARAM_STR, &domain_table     },
 	{"domain_col",        PARAM_STR, &domain_col       },
 	{"did_col",           PARAM_STR, &did_col          },
 	{"flags_col",         PARAM_STR, &flags_col        },
 	{"domattr_table",     PARAM_STR, &domattr_table    },
 	{"domattr_did",       PARAM_STR, &domattr_did      },
 	{"domattr_name",      PARAM_STR, &domattr_name     },
 	{"domattr_type",      PARAM_STR, &domattr_type     },
 	{"domattr_value",     PARAM_STR, &domattr_value    },
 	{"domattr_flags",     PARAM_STR, &domattr_flags    },
 	{"load_domain_attrs", PARAM_INT, &load_domain_attrs},
 	{0, 0, 0}
d69aa8d7
 };
 
 
caa40974
 /*
  * Module interface
  */
09befa54
 struct module_exports exports = {
db499f51
 	"uid_domain", /* module name */
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	cmds,         /* exported functions */
 	params,       /* exported parameters */
 	domain_rpc,   /* exported RPC methods */
 	0,            /* exported pseudo-variables */
 	0,            /* response handling function */
 	mod_init,     /* module init function */
 	child_init,   /* per-child init function */
 	destroy       /* module destroy function */
09befa54
 };
 
 
75a33fc7
 static int init_db(void)
09befa54
 {
aa72024a
 	db_fld_t load_domains_columns[] = {
75a33fc7
 		{.name = did_col.s,    DB_STR},
 		{.name = domain_col.s, DB_STR},
 		{.name = flags_col.s,  DB_BITMAP},
 		{.name = NULL}
 	};
aa72024a
 	db_fld_t get_did_columns[] = {
75a33fc7
 		{.name = did_col.s, DB_STR},
 		{.name = NULL}
 	};
aa72024a
 	db_fld_t load_attrs_columns[] = {
75a33fc7
 		{.name = domattr_name.s, .type = DB_STR},
 		{.name = domattr_type.s, .type = DB_INT},
 		{.name = domattr_value.s, .type = DB_STR},
 		{.name = domattr_flags.s, .type = DB_BITMAP},
 		{.name = NULL}
 	};
aa72024a
 	db_fld_t get_did_match[] = {
75a33fc7
 		{.name = domain_col.s, DB_STR},
 		{.name = NULL}
 	};
aa72024a
 	db_fld_t load_attrs_match[] = {
75a33fc7
 		{.name = domattr_did.s, .type = DB_STR},
 		{.name = NULL}
 	};
 
 	db = db_ctx("domain");
 	if (db == NULL) {
 		ERR("Error while initializing database layer\n");
 		return -1;
 	}
 	if (db_add_db(db, db_url.s) < 0) return -1;
 	if (db_connect(db) < 0) return -1;
bc807b61
 
537fbbea
 	DBG("prepare load_domains_cmd\n");
bc807b61
 	load_domains_cmd = db_cmd(DB_GET, db, domain_table.s, load_domains_columns,
 							  NULL, NULL);
75a33fc7
 	if (load_domains_cmd == NULL) {
 		ERR("Error while preparing load_domains database command\n");
 		return -1;
 	}
bc807b61
 
537fbbea
 	DBG("prepare get_did_cmd\n");
bc807b61
 	get_did_cmd = db_cmd(DB_GET, db, domain_table.s, get_did_columns,
 						 get_did_match, NULL);
75a33fc7
 	if (get_did_cmd == NULL) {
aa72024a
 		ERR("Error while preparing get_did database command\n");
75a33fc7
 		return -1;
 	}
b5018dc1
 
8e5a7639
 	if (load_domain_attrs) {
537fbbea
 		DBG("prepare load_attrs_cmd\n");
bc807b61
 		load_attrs_cmd = db_cmd(DB_GET, db, domattr_table.s,
 								load_attrs_columns, load_attrs_match, NULL);
8e5a7639
 		if (load_attrs_cmd == NULL) {
 			ERR("Error while preparing load_attrs database command\n");
 			return -1;
 		}
75a33fc7
 	}
b5018dc1
 
75a33fc7
 	return 0;
b5018dc1
 }
caa40974
 
b5018dc1
 
 static int allocate_tables(void)
 {
bc807b61
 	active_hash = (struct hash_entry***)shm_malloc(sizeof(struct hash_entry**));
 	hash_1 = (struct hash_entry**)shm_malloc(sizeof(struct hash_entry*)
 											 * HASH_SIZE);
 	hash_2 = (struct hash_entry**)shm_malloc(sizeof(struct hash_entry*)
 											 * HASH_SIZE);
 	domains_1 = (domain_t**)shm_malloc(sizeof(domain_t*));
 	domains_2 = (domain_t**)shm_malloc(sizeof(domain_t*));
 
 	if (!hash_1 || !hash_2 || !active_hash || !domains_1 || !domains_2) {
cf489648
 		SHM_MEM_ERROR;
75a33fc7
 		return -1;
bc807b61
 	}
 	memset(hash_1, 0, sizeof(struct hash_entry*) * HASH_SIZE);
 	memset(hash_2, 0, sizeof(struct hash_entry*) * HASH_SIZE);
 	*active_hash = hash_1;
 	*domains_1 = 0;
 	*domains_2 = 0;
 	return 0;
b5018dc1
 }
 
 static void destroy_tables(void)
 {
bc807b61
 	free_table(hash_1);
 	free_table(hash_2);
 	if (active_hash) shm_free(active_hash);
 
 	if (domains_1) {
75a33fc7
 		free_domain_list(*domains_1);
 		shm_free(domains_1);
bc807b61
 	}
 
 	if (domains_2) {
75a33fc7
 		free_domain_list(*domains_2);
 		shm_free(domains_2);
bc807b61
 	}
b5018dc1
 }
 
da586335
 
b5018dc1
 static int mod_init(void)
 {
75a33fc7
 	/* Check if cache needs to be loaded from domain table */
bc807b61
 	if (db_mode) {
75a33fc7
 		if (init_db() < 0) goto error;
 
 		if (allocate_tables() < 0) goto error;
 		if (reload_domain_list() < 0) goto error;
 
 		db_cmd_free(load_domains_cmd);
 		load_domains_cmd = NULL;
 
 		db_cmd_free(load_attrs_cmd);
 		load_attrs_cmd = NULL;
 
 		db_cmd_free(get_did_cmd);
 		get_did_cmd = NULL;
 
 		if (db) db_disconnect(db);
 		db_ctx_free(db);
 		db = NULL;
bc807b61
 	}
 
 	return 0;
 
da586335
  error:
75a33fc7
 	if (get_did_cmd) {
 		db_cmd_free(get_did_cmd);
 		get_did_cmd = NULL;
 	}
 	if (load_domains_cmd) {
 		db_cmd_free(load_domains_cmd);
 		load_domains_cmd = NULL;
 	}
 	if (load_attrs_cmd) {
 		db_cmd_free(load_attrs_cmd);
 		load_attrs_cmd = NULL;
 	}
 	if (db) db_disconnect(db);
 	db_ctx_free(db);
 	db = NULL;
bc807b61
 	return -1;
b5018dc1
 }
 
 
 static int child_init(int rank)
 {
75a33fc7
 	/* Check if database is needed by child */
bc807b61
 	if (rank > 0 || rank == PROC_RPC || rank == PROC_UNIXSOCK) {
75a33fc7
 		if (init_db() < 0) return -1;
 	}
 
bc807b61
 	return 0;
b5018dc1
 }
 
 
a4adf6b9
 static void free_old_domain(domain_t* d)
 {
bc807b61
 	int i;
75a33fc7
 
bc807b61
 	if (!d) return;
 	if (d->did.s) {
75a33fc7
 		pkg_free(d->did.s);
 		d->did.s = NULL;
bc807b61
 	}
 
 	if (d->domain) {
75a33fc7
 		for(i = 0; i < d->n; i++) {
 			if (d->domain[i].s) pkg_free(d->domain[i].s);
 		}
 		pkg_free(d->domain);
 		d->domain = NULL;
bc807b61
 	}
 
 	if (d->flags) {
75a33fc7
 		pkg_free(d->flags);
 		d->flags = NULL;
bc807b61
 	}
 
 	if (d->attrs) {
75a33fc7
 		destroy_avp_list(&d->attrs);
bc807b61
 	}
a4adf6b9
 }
 
 
b5018dc1
 static void destroy(void)
 {
bc807b61
 	/* Destroy is called from the main process only, there is no need to close
 	 * database here because it is closed in mod_init already
 	 */
 	if (!db_mode) {
75a33fc7
 		free_old_domain(&dom_buf[0]);
 		free_old_domain(&dom_buf[1]);
bc807b61
 	}
75a33fc7
 
 	if (load_domains_cmd) db_cmd_free(load_domains_cmd);
 	if (get_did_cmd) db_cmd_free(get_did_cmd);
 	if (load_attrs_cmd) db_cmd_free(load_attrs_cmd);
 
 	if (db) {
 		db_disconnect(db);
 		db_ctx_free(db);
 	}
 
bc807b61
 	destroy_tables();
b5018dc1
 }
 
1f45aade
 
79bdcee0
 
b5018dc1
 /*
  * Check if domain is local
  */
da586335
 static int is_local(struct sip_msg* msg, char* fp, char* s2)
b5018dc1
 {
d8fdebf9
 	str domain;
bc807b61
 
 	if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 0) {
75a33fc7
 		ERR("Unable to get domain to check\n");
 		return -1;
bc807b61
 	}
 
d8fdebf9
 	return is_domain_local(&domain);
7c55614b
 }
 
a4adf6b9
 
 static int db_load_domain(domain_t** d, unsigned long flags, str* domain)
 {
bc807b61
 	int ret;
 	int_str name, val;
 	domain_t* p;
 	str name_s = STR_STATIC_INIT(AVP_DID);
 
 	if (flags & AVP_TRACK_FROM) {
75a33fc7
 		p = &dom_buf[0];
bc807b61
 	} else {
75a33fc7
 		p = &dom_buf[1];
bc807b61
 	}
 
 	free_old_domain(p);
 
 	ret = db_get_did(&p->did, domain);
 	if (ret != 1) return ret;
 	if (load_domain_attrs) {
75a33fc7
 		if (db_load_domain_attrs(p) < 0) return -1;
bc807b61
 	}
 
75a33fc7
 	/* Create an attribute containing did of the domain */
bc807b61
 	name.s = name_s;
 	val.s = p->did;
 	if (add_avp_list(&p->attrs, AVP_CLASS_DOMAIN | AVP_NAME_STR | AVP_VAL_STR,
 					 name, val) < 0) return -1;
 
 	*d = p;
 	return 0;
a4adf6b9
 }
 
 
da586335
 static int lookup_domain(struct sip_msg* msg, char* flags, char* fp)
7c55614b
 {
bc807b61
 	str domain, tmp;
e6a3f660
 	domain_t* d = NULL;
bc807b61
 	int ret = -1;
 
 	if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 0) {
6907fb8d
 		DBG("lookup_domain: Cannot get the domain name to lookup\n");
75a33fc7
 		return -1;
bc807b61
 	}
 
 	tmp.s = pkg_malloc(domain.len);
 	if (!tmp.s) {
cf489648
 		PKG_MEM_ERROR;
75a33fc7
 		return -1;
bc807b61
 	}
 	memcpy(tmp.s, domain.s, domain.len);
 	tmp.len = domain.len;
 	strlower(&tmp);
537fbbea
 
bc807b61
 	if (db_mode) {
75a33fc7
 		if (hash_lookup(&d, *active_hash, &tmp) == 1) {
 			set_avp_list((unsigned long)flags, &d->attrs);
 			ret = 1;
 		}
bc807b61
 	} else {
75a33fc7
 		if (db_load_domain(&d, (unsigned long)flags, &tmp) == 0) {
 			set_avp_list((unsigned long)flags, &d->attrs);
 			ret = 1;
 		}
bc807b61
 	}
 
 	pkg_free(tmp.s);
 	return ret;
c1285373
 }
 
 
 static int get_did(str* did, str* domain)
 {
bc807b61
 	str tmp;
 	domain_t* d;
 
 	if (!db_mode) {
75a33fc7
 		ERR("lookup_domain only works in cache mode\n");
 		return -1;
bc807b61
 	}
 
 	tmp.s = pkg_malloc(domain->len);
 	if (!tmp.s) {
cf489648
 		PKG_MEM_ERROR;
75a33fc7
 		return -1;
bc807b61
 	}
 	memcpy(tmp.s, domain->s, domain->len);
 	tmp.len = domain->len;
 	strlower(&tmp);
 
 	if (hash_lookup(&d, *active_hash, &tmp) == 1) {
75a33fc7
 		*did = d->did;
 		pkg_free(tmp.s);
 		return 1;
bc807b61
 	} else {
75a33fc7
 		pkg_free(tmp.s);
 		return -1;
bc807b61
 	}
7c55614b
 }
 
09befa54
 
da586335
 int reload_domain_list(void)
f3b34539
 {
bc807b61
 	struct hash_entry** new_table;
 	domain_t** new_list;
 
75a33fc7
 	/* Choose new hash table and free its old contents */
bc807b61
 	if (*active_hash == hash_1) {
75a33fc7
 		free_table(hash_2);
 		new_table = hash_2;
 		new_list = domains_2;
bc807b61
 	} else {
75a33fc7
 		free_table(hash_1);
 		new_table = hash_1;
 		new_list = domains_1;
bc807b61
 	}
 
 	if (load_domains(new_list) < 0) goto error;
 	if (gen_domain_table(new_table, *new_list) < 0) goto error;
 	*active_hash = new_table;
 	return 0;
 
da586335
  error:
bc807b61
 	free_table(new_table);
 	free_domain_list(*new_list);
 	return -1;
f3b34539
 }
 
09befa54
 
da586335
 static int lookup_domain_fixup(void** param, int param_no)
09befa54
 {
e6a3f660
 	unsigned long flags=0;
bc807b61
 	char* s;
 
 	if (param_no == 1) {
75a33fc7
 		/* Determine the track and class of attributes to be loaded */
 		s = (char*)*param;
 		if (*s != '$' || (strlen(s) != 3)) {
 			ERR("Invalid parameter value, $xy expected\n");
 			return -1;
 		}
 		switch((s[1] << 8) + s[2]) {
 		case 0x4644: /* $fd */
 		case 0x6664:
 		case 0x4664:
 		case 0x6644:
 			flags = AVP_TRACK_FROM | AVP_CLASS_DOMAIN;
 			break;
bc807b61
 
75a33fc7
 		case 0x5444: /* $td */
 		case 0x7464:
 		case 0x5464:
 		case 0x7444:
 			flags = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
 			break;
bc807b61
 
75a33fc7
 		default:
 			ERR("Invalid parameter value: '%s'\n", s);
 			return -1;
 		}
bc807b61
 
75a33fc7
 		pkg_free(*param);
 		*param = (void*)flags;
bc807b61
 	} else if (param_no == 2) {
75a33fc7
 		return fixup_var_str_12(param, 2);
bc807b61
 	}
 
 	return 0;
09befa54
 }