mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
568 lines
13 KiB
568 lines
13 KiB
/*
|
|
* Domain module
|
|
*
|
|
* Copyright (C) 2002-2003 Juha Heinanen
|
|
*
|
|
* This file is part of sip-router, a free SIP server.
|
|
*
|
|
* 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
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* History:
|
|
* -------
|
|
* 2003-03-11: New module interface (janakj)
|
|
* 2003-03-16: flags export parameter added (janakj)
|
|
* 2003-04-05: default_uri #define used (jiri)
|
|
* 2003-04-06: db connection closed in mod_init (janakj)
|
|
* 2004-06-06 updated to the new DB api, cleanup: static dbf & handler,
|
|
* calls to domain_db_{bind,init,close,ver} (andrei)
|
|
*/
|
|
|
|
#include "uid_domain_mod.h"
|
|
#include <stdio.h>
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../ut.h"
|
|
#include "../../parser/parse_from.h"
|
|
#include "../../parser/parse_uri.h"
|
|
#include "../../usr_avp.h"
|
|
#include "domain_api.h"
|
|
#include "domain_rpc.h"
|
|
#include "hash.h"
|
|
#include "domain.h"
|
|
|
|
|
|
/*
|
|
* Module management function prototypes
|
|
*/
|
|
static int mod_init(void);
|
|
static void destroy(void);
|
|
static int child_init(int rank);
|
|
|
|
static int is_local(struct sip_msg* msg, char* s1, char* s2);
|
|
static int lookup_domain(struct sip_msg* msg, char* s1, char* s2);
|
|
static int get_did(str* did, str* domain);
|
|
|
|
static int lookup_domain_fixup(void** param, int param_no);
|
|
|
|
MODULE_VERSION
|
|
|
|
/*
|
|
* Version of domain table required by the module, increment this value if you
|
|
* change the table in an backwards incompatible way
|
|
*/
|
|
#define DOMAIN_TABLE_VERSION 2
|
|
#define DOMATTR_TABLE_VERSION 1
|
|
|
|
#define DOMAIN_TABLE "uid_domain"
|
|
#define DOMAIN_COL "domain"
|
|
#define DID_COL "did"
|
|
#define FLAGS_COL "flags"
|
|
|
|
#define DOMATTR_TABLE "uid_domain_attrs"
|
|
#define DOMATTR_DID "did"
|
|
#define DOMATTR_NAME "name"
|
|
#define DOMATTR_TYPE "type"
|
|
#define DOMATTR_VALUE "value"
|
|
#define DOMATTR_FLAGS "flags"
|
|
#define DOMAIN_COL "domain"
|
|
|
|
int db_mode = 1; /* Enable/disable domain cache */
|
|
|
|
/*
|
|
* Module parameter variables
|
|
*/
|
|
static str db_url = STR_STATIC_INIT(DEFAULT_RODB_URL);
|
|
|
|
str domain_table = STR_STATIC_INIT(DOMAIN_TABLE); /* Name of domain table */
|
|
str domain_col = STR_STATIC_INIT(DOMAIN_COL); /* Name of domain column */
|
|
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 */
|
|
|
|
static db_ctx_t* db = NULL;
|
|
db_cmd_t* load_domains_cmd = NULL, *get_did_cmd = NULL, *load_attrs_cmd = NULL;
|
|
|
|
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 */
|
|
|
|
/* Global domain structure, this one is used to store data retrieved from
|
|
* database when memory cache is disabled. There is one buffer for from and
|
|
* one buffer for to track.
|
|
*/
|
|
static domain_t dom_buf[2];
|
|
|
|
/*
|
|
* Exported functions
|
|
*/
|
|
static cmd_export_t cmds[] = {
|
|
{"is_local", is_local, 1, fixup_var_str_1,
|
|
REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
|
|
{"lookup_domain", lookup_domain, 2, lookup_domain_fixup,
|
|
REQUEST_ROUTE|FAILURE_ROUTE },
|
|
{"get_did", (cmd_function)get_did, 0, 0, 0},
|
|
{"bind_domain", (cmd_function)bind_domain, 0, 0, 0},
|
|
{0, 0, 0, 0, 0}
|
|
};
|
|
|
|
|
|
/*
|
|
* Exported parameters
|
|
*/
|
|
static param_export_t params[] = {
|
|
{"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}
|
|
};
|
|
|
|
|
|
/*
|
|
* Module interface
|
|
*/
|
|
struct module_exports exports = {
|
|
"uid_domain",
|
|
cmds, /* Exported functions */
|
|
domain_rpc, /* RPC methods */
|
|
params, /* Exported parameters */
|
|
mod_init, /* module initialization function */
|
|
0, /* response function*/
|
|
destroy, /* destroy function */
|
|
0, /* cancel function */
|
|
child_init /* per-child init function */
|
|
};
|
|
|
|
|
|
static int init_db(void)
|
|
{
|
|
db_fld_t load_domains_columns[] = {
|
|
{.name = did_col.s, DB_STR},
|
|
{.name = domain_col.s, DB_STR},
|
|
{.name = flags_col.s, DB_BITMAP},
|
|
{.name = NULL}
|
|
};
|
|
db_fld_t get_did_columns[] = {
|
|
{.name = did_col.s, DB_STR},
|
|
{.name = NULL}
|
|
};
|
|
db_fld_t load_attrs_columns[] = {
|
|
{.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}
|
|
};
|
|
db_fld_t get_did_match[] = {
|
|
{.name = domain_col.s, DB_STR},
|
|
{.name = NULL}
|
|
};
|
|
db_fld_t load_attrs_match[] = {
|
|
{.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;
|
|
|
|
DBG("prepare load_domains_cmd\n");
|
|
load_domains_cmd = db_cmd(DB_GET, db, domain_table.s, load_domains_columns,
|
|
NULL, NULL);
|
|
if (load_domains_cmd == NULL) {
|
|
ERR("Error while preparing load_domains database command\n");
|
|
return -1;
|
|
}
|
|
|
|
DBG("prepare get_did_cmd\n");
|
|
get_did_cmd = db_cmd(DB_GET, db, domain_table.s, get_did_columns,
|
|
get_did_match, NULL);
|
|
if (get_did_cmd == NULL) {
|
|
ERR("Error while preparing get_did database command\n");
|
|
return -1;
|
|
}
|
|
|
|
if (load_domain_attrs) {
|
|
DBG("prepare load_attrs_cmd\n");
|
|
load_attrs_cmd = db_cmd(DB_GET, db, domattr_table.s,
|
|
load_attrs_columns, load_attrs_match, NULL);
|
|
if (load_attrs_cmd == NULL) {
|
|
ERR("Error while preparing load_attrs database command\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int allocate_tables(void)
|
|
{
|
|
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) {
|
|
ERR("No memory left\n");
|
|
return -1;
|
|
}
|
|
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;
|
|
}
|
|
|
|
static void destroy_tables(void)
|
|
{
|
|
free_table(hash_1);
|
|
free_table(hash_2);
|
|
if (active_hash) shm_free(active_hash);
|
|
|
|
if (domains_1) {
|
|
free_domain_list(*domains_1);
|
|
shm_free(domains_1);
|
|
}
|
|
|
|
if (domains_2) {
|
|
free_domain_list(*domains_2);
|
|
shm_free(domains_2);
|
|
}
|
|
}
|
|
|
|
|
|
static int mod_init(void)
|
|
{
|
|
/* Check if cache needs to be loaded from domain table */
|
|
if (db_mode) {
|
|
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;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
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;
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int child_init(int rank)
|
|
{
|
|
/* Check if database is needed by child */
|
|
if (rank > 0 || rank == PROC_RPC || rank == PROC_UNIXSOCK) {
|
|
if (init_db() < 0) return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void free_old_domain(domain_t* d)
|
|
{
|
|
int i;
|
|
|
|
if (!d) return;
|
|
if (d->did.s) {
|
|
pkg_free(d->did.s);
|
|
d->did.s = NULL;
|
|
}
|
|
|
|
if (d->domain) {
|
|
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;
|
|
}
|
|
|
|
if (d->flags) {
|
|
pkg_free(d->flags);
|
|
d->flags = NULL;
|
|
}
|
|
|
|
if (d->attrs) {
|
|
destroy_avp_list(&d->attrs);
|
|
}
|
|
}
|
|
|
|
|
|
static void destroy(void)
|
|
{
|
|
/* 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) {
|
|
free_old_domain(&dom_buf[0]);
|
|
free_old_domain(&dom_buf[1]);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
destroy_tables();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Check if domain is local
|
|
*/
|
|
static int is_local(struct sip_msg* msg, char* fp, char* s2)
|
|
{
|
|
str domain;
|
|
|
|
if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 0) {
|
|
ERR("Unable to get domain to check\n");
|
|
return -1;
|
|
}
|
|
|
|
return is_domain_local(&domain);
|
|
}
|
|
|
|
|
|
static int db_load_domain(domain_t** d, unsigned long flags, str* domain)
|
|
{
|
|
int ret;
|
|
int_str name, val;
|
|
domain_t* p;
|
|
str name_s = STR_STATIC_INIT(AVP_DID);
|
|
|
|
if (flags & AVP_TRACK_FROM) {
|
|
p = &dom_buf[0];
|
|
} else {
|
|
p = &dom_buf[1];
|
|
}
|
|
|
|
free_old_domain(p);
|
|
|
|
ret = db_get_did(&p->did, domain);
|
|
if (ret != 1) return ret;
|
|
if (load_domain_attrs) {
|
|
if (db_load_domain_attrs(p) < 0) return -1;
|
|
}
|
|
|
|
/* Create an attribute containing did of the domain */
|
|
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;
|
|
}
|
|
|
|
|
|
static int lookup_domain(struct sip_msg* msg, char* flags, char* fp)
|
|
{
|
|
str domain, tmp;
|
|
domain_t* d = NULL;
|
|
int ret = -1;
|
|
|
|
if (get_str_fparam(&domain, msg, (fparam_t*)fp) != 0) {
|
|
DBG("lookup_domain: Cannot get the domain name to lookup\n");
|
|
return -1;
|
|
}
|
|
|
|
tmp.s = pkg_malloc(domain.len);
|
|
if (!tmp.s) {
|
|
ERR("No memory left\n");
|
|
return -1;
|
|
}
|
|
memcpy(tmp.s, domain.s, domain.len);
|
|
tmp.len = domain.len;
|
|
strlower(&tmp);
|
|
|
|
if (db_mode) {
|
|
if (hash_lookup(&d, *active_hash, &tmp) == 1) {
|
|
set_avp_list((unsigned long)flags, &d->attrs);
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
if (db_load_domain(&d, (unsigned long)flags, &tmp) == 0) {
|
|
set_avp_list((unsigned long)flags, &d->attrs);
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
pkg_free(tmp.s);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int get_did(str* did, str* domain)
|
|
{
|
|
str tmp;
|
|
domain_t* d;
|
|
|
|
if (!db_mode) {
|
|
ERR("lookup_domain only works in cache mode\n");
|
|
return -1;
|
|
}
|
|
|
|
tmp.s = pkg_malloc(domain->len);
|
|
if (!tmp.s) {
|
|
ERR("No memory left\n");
|
|
return -1;
|
|
}
|
|
memcpy(tmp.s, domain->s, domain->len);
|
|
tmp.len = domain->len;
|
|
strlower(&tmp);
|
|
|
|
if (hash_lookup(&d, *active_hash, &tmp) == 1) {
|
|
*did = d->did;
|
|
pkg_free(tmp.s);
|
|
return 1;
|
|
} else {
|
|
pkg_free(tmp.s);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
int reload_domain_list(void)
|
|
{
|
|
struct hash_entry** new_table;
|
|
domain_t** new_list;
|
|
|
|
/* Choose new hash table and free its old contents */
|
|
if (*active_hash == hash_1) {
|
|
free_table(hash_2);
|
|
new_table = hash_2;
|
|
new_list = domains_2;
|
|
} else {
|
|
free_table(hash_1);
|
|
new_table = hash_1;
|
|
new_list = domains_1;
|
|
}
|
|
|
|
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;
|
|
|
|
error:
|
|
free_table(new_table);
|
|
free_domain_list(*new_list);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int lookup_domain_fixup(void** param, int param_no)
|
|
{
|
|
unsigned long flags=0;
|
|
char* s;
|
|
|
|
if (param_no == 1) {
|
|
/* 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;
|
|
|
|
case 0x5444: /* $td */
|
|
case 0x7464:
|
|
case 0x5464:
|
|
case 0x7444:
|
|
flags = AVP_TRACK_TO | AVP_CLASS_DOMAIN;
|
|
break;
|
|
|
|
default:
|
|
ERR("Invalid parameter value: '%s'\n", s);
|
|
return -1;
|
|
}
|
|
|
|
pkg_free(*param);
|
|
*param = (void*)flags;
|
|
} else if (param_no == 2) {
|
|
return fixup_var_str_12(param, 2);
|
|
}
|
|
|
|
return 0;
|
|
}
|