/**
* $Id: pdt.c,v 1.14.2.1 2005/01/21 23:02:51 bogdan Exp $
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of ser, a free SIP server.
*
* ser 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser 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-04-07: a structure for both hashes introduced (ramona)
* 2003-04-06: db connection closed in mod_init (janakj)
* 2004-06-07 updated to the new DB api (andrei)
*/
/*
* Prefix-Domains Translation - ser module
* Ramona Modroiu <modroiu@fokus.fraunhofer.de>
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "../../db/db_op.h"
#include "../../sr_module.h"
#include "../../parser/parse_fline.h"
#include "../../db/db.h"
#include "../../mem/shm_mem.h"
#include "../../mem/mem.h"
#include "../../dprint.h"
#include "../../fifo_server.h"
#include "../../unixsock_server.h"
#include "../../parser/parse_uri.h"
#include "../../parser/msg_parser.h"
#include "../../ut.h"
#include "domains.h"
MODULE_VERSION
#define NR_KEYS 2
#define DB_KEY_NAME "domain"
#define DB_KEY_CODE "code"
int hs_two_pow = 1;
/** structure containing the both hashes */
double_hash_t *hash = NULL;
/** next code to be allocated */
code_t *next_code = NULL;
/** database connection */
static db_con_t *db_con = NULL;
static db_func_t pdt_dbf;
/** parameters */
static char *db_url = "mysql://root@127.0.0.1/pdt";
char *db_table = "domains";
/** pstn prefix */
char *prefix = NULL;
code_t prefix_len = 0;
code_t code_terminator = 0;
code_t start_range = 10;
gen_lock_t l;
static int prefix2domain(struct sip_msg*, char*, char*);
static int mod_init(void);
static void mod_destroy(void);
static int mod_child_init(int r);
static int get_domainprefix_unixsock(str* msg);
static cmd_export_t cmds[]={
{"prefix2domain", prefix2domain, 0, 0, REQUEST_ROUTE},
{0, 0, 0, 0, 0}
};
static param_export_t params[]={
{"db_url", STR_PARAM, &db_url},
{"db_table", STR_PARAM, &db_table},
{"prefix", STR_PARAM, &prefix},
{"terminator", INT_PARAM, &code_terminator},
{"start_range", INT_PARAM, &start_range},
{"hsize_2pow", INT_PARAM, &hs_two_pow},
{0, 0, 0}
};
struct module_exports exports = {
"pdt",
cmds,
params,
mod_init, /* module initialization function */
0, /* response function */
mod_destroy, /* destroy function */
0, /* oncancel function */
mod_child_init /* per child init function */
};
inline code_t code_valid(code_t code)
{
code_t tmp;
tmp = code;
while(tmp)
{
if(tmp%10==code_terminator)
return 0;
tmp /= 10;
}
return 1;
}
/* the superior interval limit is kept to signal that
* there are no more prefixes available */
code_t apply_correction(code_t code)
{
code_t p, tmp, new_code;
if(code==MAX_CODE)
return MAX_CODE;
p = 1;
tmp = code;
new_code = code;
while(tmp)
{
if(tmp%10==code_terminator)
{
tmp += 1;
if(MAX_CODE-p <= new_code)
return MAX_CODE;
new_code += p;
}
if(p>MAX_CODE_10)
return MAX_CODE;
p *= 10;
tmp /= 10;
}
return new_code;
}
inline int prefix_valid()
{
char *p;
if(!prefix)
return 1;
p=prefix;
prefix_len = 0;
while(*p!='\0')
{
prefix_len++;
if( *p<'0' || *p>'9' )
{
DBG("PDT: prefix_valid: supplied parameter as prefix is not valid\n");
return 0;
}
p++;
}
return 1;
}
/**
* init module function
*/
static int mod_init(void)
{
db_res_t* db_res = NULL;
code_t i, code;
dc_t* cell;
DBG("PDT: initializing...\n");
if(hs_two_pow<0)
{
LOG(L_ERR, "PDT: mod_init: hash_size_two_pow must be"
" positive and less than %d\n", MAX_HSIZE_TWO_POW);
return -1;
}
if(code_terminator>9 || code_terminator<0)
{
LOG(L_ERR, "PDT: mod_init: code_terminator must be a digit\n");
return -1;
}
if(!prefix_valid())
return -1;
next_code = (code_t*)shm_malloc(sizeof(code_t));
if(!next_code)
{
LOG(L_ERR, "PDT: mod_init: cannot allocate next_code!\n");
return -1;
}
if(lock_init(&l) == 0)
{
shm_free(next_code);
LOG(L_ERR, "PDT: mod_init: cannot init the lock\n");
return -1;
}
if(register_fifo_cmd(get_domainprefix, "get_domainprefix", 0)<0)
{
LOG(L_ERR, "PDT: mod_init: cannot register fifo command 'get_domaincode'\n");
goto error1;
}
if(unixsock_register_cmd("get_domainprefix", get_domainprefix_unixsock)<0)
{
LOG(L_ERR, "PDT: mod_init: cannot register unixsock command 'get_domainprefix'\n");
goto error1;
}
/* binding to mysql module */
if(bind_dbmod(db_url, &pdt_dbf))
{
LOG(L_ERR, "PDT: mod_init: Database module not found\n");
goto error1;
}
if (!DB_CAPABILITY(pdt_dbf, DB_CAP_ALL)) {
LOG(L_ERR, "PDT: mod_init: Database module does not "
"implement all functions needed by the module\n");
goto error1;
}
/* open a connection with the database */
db_con = pdt_dbf.init(db_url);
if(!db_con)
{
LOG(L_ERR, "PDT: mod_init: Error while connecting to database\n");
goto error1;
}
else
{
if (pdt_dbf.use_table(db_con, db_table) < 0) {
LOG(L_ERR, "PDT: mod_init: Error in use_table\n");
goto error1;
}
DBG("PDT: mod_init: Database connection opened successfully\n");
}
/* init hashes in share memory */
if( (hash = init_double_hash(hs_two_pow)) == NULL)
{
LOG(L_ERR, "PDT: mod_init: hash could not be allocated\n");
goto error2;
}
/* loading all information from database */
*next_code = 0;
if(pdt_dbf.query(db_con, NULL, NULL, NULL, NULL, 0, 0, "code", &db_res)==0)
{
for(i=0; i<RES_ROW_N(db_res); i++)
{
code = RES_ROWS(db_res)[i].values[0].val.int_val;
if (!code_valid(code))
{
LOG(L_ERR, "PDT: mod_init: existing code contains the terminator\n");
goto error;
}
if (*next_code < code)
*next_code = code;
cell=new_cell(
(char*)(RES_ROWS(db_res)[i].values[1].val.string_val), code);
if(cell == NULL)
goto error;
if(add_to_double_hash(hash, cell)<0)
{
LOG(L_ERR, "PDT: mod_init: could not add information from database"
" into shared-memory hashes\n");
goto error;
}
}
// clear up here
//print_hash(hash->dhash, hash->hash_size);
//print_hash(hash->chash, hash->hash_size);
(*next_code)++;
if (*next_code < start_range)
*next_code = start_range;
*next_code = apply_correction(*next_code);
DBG("PDT: mod_init: next_code:%d\n", *next_code);
/* free up the space allocated for response */
if(pdt_dbf.free_result(db_con, db_res)<0)
{
LOG(L_ERR, "PDT: mod_init: error when freeing"
" up the response space\n");
}
}
else
{
/* query to database failed */
LOG(L_ERR, "PDT: mod_init: query to database failed\n");
goto error;
}
pdt_dbf.close(db_con); /* janakj - close the connection */
/* success code */
return 0;
error:
free_double_hash(hash);
hash = 0;
error2:
pdt_dbf.close(db_con);
db_con = 0;
error1:
shm_free(next_code);
next_code = 0;
lock_destroy(&l);
return -1;
}
/* each child get a new connection to the database */
static int mod_child_init(int r)
{
DBG("PDT: mod_child_init #%d / pid <%d>\n", r, getpid());
db_con = pdt_dbf.init(db_url);
if(!db_con)
{
LOG(L_ERR,"PDT: child %d: Error while connecting database\n",r);
return -1;
}
else
{
if (pdt_dbf.use_table(db_con, db_table) < 0) {
LOG(L_ERR, "PDT:child %d: Error in use_table\n", r);
return -1;
}
DBG("PDT:child %d: Database connection opened successfully\n",r);
}
return 0;
}
/* change the r-uri if it is a PSTN format */
static int prefix2domain(struct sip_msg* msg, char* str1, char* str2)
{
char *host_port;
code_t code=0, i;
int digit;
if(!msg)
return -1;
/* parse the uri, if not yet */
if(msg->parsed_uri_ok==0)
if(parse_sip_msg_uri(msg)<0)
{
LOG(L_ERR,"PDT:prefix2domain: ERROR while parsing the R-URI\n");
return -1;
}
/* if the user part begin with the prefix for PSTN users, extract the code*/
if (msg->parsed_uri.user.len<=0)
{
DBG("PDT: prefix2domain: user part of the message was empty\n");
return 1;
}
if(prefix_len>0 && strncasecmp(prefix, msg->parsed_uri.user.s, prefix_len)!=0)
{
DBG("PDT: prefix2domain: PSTN prefix did not matched\n");
return 1;
}
i=0;
code=0;
digit = msg->parsed_uri.user.s[prefix_len+i]-'0';
while (digit != code_terminator)
{
if (digit<0 || digit>9)
{
DBG("PDT: prefix2domain: domain_code not well formed\n");
return -1;
}
if(MAX_CODE_10<code || (MAX_CODE_10==code && MAX_CODE-MAX_CODE_R<=digit))
{
DBG("PDT: prefix2domain: domain_code not well formed\n");
return -1;
}
code=code*10+digit;
i++;
digit = msg->parsed_uri.user.s[prefix_len+i]-'0';
}
/* find the domain that corresponds to that code */
if(!(host_port=get_domain_from_hash(hash->chash, hash->hash_size, code)))
{
LOG(L_ERR, "PDT: get_domain_from_hash: required "
"code %d is not allocated yet\n", code);
return -1;
}
/* update the new uri */
if(update_new_uri(msg, prefix_len+i+1, host_port)<0)
{
DBG("PDT: prefix2domain: new_uri cannot be updated\n");
return -1;
}
return 1;
}
/* change the uri according to translation of the prefix */
int update_new_uri(struct sip_msg *msg, int code_len, char* host_port)
{
char *tmp;
int uri_len = 0, user_len = 0;
/* flag to show that ruri is not parsed */
msg->parsed_uri_ok = 0;
/* compute the new uri length */
uri_len = 4/*sip:*/ + msg->parsed_uri.user.len-code_len +
( msg->parsed_uri.passwd.len ? msg->parsed_uri.passwd.len + 1:0 ) +
strlen(host_port) + 1/*@*/ +
(msg->parsed_uri.params.len ? msg->parsed_uri.params.len + 1:0 ) +
(msg->parsed_uri.headers.len ? msg->parsed_uri.headers.len + 1:0 );
if (uri_len > MAX_URI_SIZE)
{
LOG(L_ERR, "PDT: update_new_uri(): uri is too long\n");
return -1;
}
/* space for the new uri */
tmp = (char*)pkg_malloc(uri_len+1);
if(tmp == NULL)
{
LOG(L_ERR, "PDT: update_new_uri: error allocating space\n");
return -1;
}
/* construct the new uri */
strcpy(tmp, "sip:");
/* add user part */
user_len = msg->parsed_uri.user.len-code_len;
strncat(tmp, msg->parsed_uri.user.s+code_len, user_len);
/* add password, if that exists */
if(msg->parsed_uri.passwd.s && msg->parsed_uri.passwd.len > 0)
{
strcat(tmp, ":");
strncat(tmp, msg->parsed_uri.passwd.s,
msg->parsed_uri.passwd.len);
}
strcat(tmp,"@");
/* add host(and port) part of the uri */
strcat(tmp, host_port);
if(msg->parsed_uri.params.s && msg->parsed_uri.params.len > 0)
{
strcat(tmp, ";");
strncat(tmp, msg->parsed_uri.params.s, msg->parsed_uri.params.len);
}
if(msg->parsed_uri.headers.s && msg->parsed_uri.headers.len > 0)
{
strcat(tmp, "?");
strncat(tmp, msg->parsed_uri.headers.s, msg->parsed_uri.headers.len);
}
/* free space of the old new_uri */
if(msg->new_uri.s)
{
pkg_free(msg->new_uri.s);
msg->new_uri.len = 0;
}
/* setup the new uri */
msg->new_uri.s = tmp;
msg->new_uri.len = uri_len;
// here to clear
DBG("PDT: update_new_uri: len=%d uri=%.*s\n", msg->new_uri.len,
msg->new_uri.len, msg->new_uri.s);
return 0;
}
static void mod_destroy(void)
{
DBG("PDT: mod_destroy : Cleaning up\n");
if (hash)
free_double_hash(hash);
if (db_con && pdt_dbf.close)
pdt_dbf.close(db_con);
if (next_code)
shm_free(next_code);
lock_destroy(&l);
}
/*
Fifo command example:
":get_domaincode:[response_file]\n
domain_name\n
authorization_to_register_domains\n
\n
"
*/
int get_domainprefix(FILE *stream, char *response_file)
{
db_key_t db_keys[NR_KEYS];
db_val_t db_vals[NR_KEYS];
db_op_t db_ops[NR_KEYS] = {OP_EQ, OP_EQ};
code_t code;
dc_t* cell;
char domain_name[256];
str sdomain;
char authorization[10];
str sauth;
int authorized=0;
/* read a line -the domain name parameter- from the fifo */
sdomain.s = domain_name;
if(!read_line(sdomain.s, 255, stream, &sdomain.len) || sdomain.len==0)
{
LOG(L_ERR, "PDT: get_domaincode: could not read from fifo\n");
fifo_reply(response_file, "400 |get_domaincode: could not "
"read from fifo\n");
return 1;
}
domain_name[sdomain.len] = '\0';
/* read a line -the authorization to register new domains- from the fifo */
sauth.s = authorization;
if(!read_line(sauth.s, 3, stream, &sauth.len) || sauth.len==0)
{
LOG(L_ERR, "PDT: get_domaincode: could not read from fifo\n");
fifo_reply(response_file, "400 |get_domaincode: could not "
"read from fifo\n");
return 1;
}
/* see what kind of user we have */
authorized = sauth.s[0]-'0';
lock_get(&l);
/* search the domain in the hashtable */
cell = get_code_from_hash(hash->dhash, hash->hash_size, domain_name);
/* the domain is registered */
if(cell)
{
lock_release(&l);
/* domain already in the database */
fifo_reply(response_file, "201 |Domain name= %.*s"
"Domain code= %d%d\n",
sdomain.len, sdomain.s, cell->code, code_terminator);
return 0;
}
/* domain not registered yet */
/* user not authorized to register new domains */
if(!authorized)
{
lock_release(&l);
fifo_reply(response_file, "203 |Domain name not registered yet\n");
return 0;
}
code = *next_code;
*next_code = apply_correction(code+1);
/* prepare for insertion into database */
db_keys[0] = DB_KEY_CODE;
db_keys[1] = DB_KEY_NAME;
db_vals[0].type = DB_INT;
db_vals[0].nul = 0;
db_vals[0].val.int_val = code;
db_vals[1].type = DB_STR;
db_vals[1].nul = 0;
db_vals[1].val.str_val.s = sdomain.s;
db_vals[1].val.str_val.len = sdomain.len;
DBG("%d %.*s\n", code, sdomain.len, sdomain.s);
/* insert a new domain into database */
if(pdt_dbf.insert(db_con, db_keys, db_vals, NR_KEYS)<0)
{
/* next available code is still code */
*next_code = code;
lock_release(&l);
LOG(L_ERR, "PDT: get_domaincode: error storing a"
" new domain\n");
fifo_reply(response_file, "204 |Cannot register the new domain in a"
" consistent way\n");
return -1;
}
/* insert the new domain into hashtables, too */
cell = new_cell(sdomain.s, code);
if(add_to_double_hash(hash, cell)<0)
goto error;
lock_release(&l);
/* user authorized to register new domains */
fifo_reply(response_file, "202 |Domain name= %.*s"
" New domain code= %d%d\n",
sdomain.len, sdomain.s, code, code_terminator);
return 0;
error:
/* next available code is still code */
*next_code = code;
/* delete from database */
if(pdt_dbf.delete(db_con, db_keys, db_ops, db_vals, NR_KEYS)<0)
LOG(L_ERR,"PDT: get_domaincode: database/share-memory are inconsistent\n");
lock_release(&l);
return -1;
}
static int get_domainprefix_unixsock(str* msg)
{
db_key_t db_keys[NR_KEYS];
db_val_t db_vals[NR_KEYS];
db_op_t db_ops[NR_KEYS] = {OP_EQ, OP_EQ};
code_t code;
dc_t* cell;
str sdomain, sauth;
int authorized=0;
/* read a line -the domain name parameter- from the fifo */
if(unixsock_read_line(&sdomain, msg) != 0)
{
unixsock_reply_asciiz("400 Domain expected\n");
goto send_err;
}
/* read a line -the authorization to register new domains- from the fifo */
if(unixsock_read_line(&sauth, msg) != 0)
{
unixsock_reply_asciiz("400 Authorization expected\n");
goto send_err;
}
sdomain.s[sdomain.len] = '\0';
/* see what kind of user we have */
authorized = sauth.s[0]-'0';
lock_get(&l);
/* search the domain in the hashtable */
cell = get_code_from_hash(hash->dhash, hash->hash_size, sdomain.s);
/* the domain is registered */
if(cell)
{
lock_release(&l);
/* domain already in the database */
unixsock_reply_printf("201 Domain name=%.*s Domain code=%d%d\n",
sdomain.len, ZSW(sdomain.s), cell->code, code_terminator);
unixsock_reply_send();
return 0;
}
/* domain not registered yet */
/* user not authorized to register new domains */
if(!authorized)
{
lock_release(&l);
unixsock_reply_asciiz("203 Domain name not registered yet\n");
unixsock_reply_send();
return 0;
}
code = *next_code;
*next_code = apply_correction(code+1);
/* prepare for insertion into database */
db_keys[0] = DB_KEY_CODE;
db_keys[1] = DB_KEY_NAME;
db_vals[0].type = DB_INT;
db_vals[0].nul = 0;
db_vals[0].val.int_val = code;
db_vals[1].type = DB_STR;
db_vals[1].nul = 0;
db_vals[1].val.str_val.s = sdomain.s;
db_vals[1].val.str_val.len = sdomain.len;
DBG("%d %.*s\n", code, sdomain.len, sdomain.s);
/* insert a new domain into database */
if(pdt_dbf.insert(db_con, db_keys, db_vals, NR_KEYS)<0)
{
/* next available code is still code */
*next_code = code;
lock_release(&l);
LOG(L_ERR, "PDT: get_domaincode: error storing a"
" new domain\n");
unixsock_reply_asciiz("204 Cannot register the new domain in a consistent way\n");
unixsock_reply_send();
return -1;
}
/* insert the new domain into hashtables, too */
cell = new_cell(sdomain.s, code);
if(add_to_double_hash(hash, cell)<0)
goto error;
lock_release(&l);
/* user authorized to register new domains */
unixsock_reply_printf("202 Domain name=%.*s New domain code=%d%d\n",
sdomain.len, ZSW(sdomain.s), code, code_terminator);
unixsock_reply_send();
return 0;
error:
/* next available code is still code */
*next_code = code;
/* delete from database */
if(pdt_dbf.delete(db_con, db_keys, db_ops, db_vals, NR_KEYS)<0)
LOG(L_ERR,"PDT: get_domaincode: database/share-memory are inconsistent\n");
lock_release(&l);
unixsock_reply_asciiz("500 Database/shared-memory are inconsistent\n");
send_err:
unixsock_reply_send();
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1