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.
kamailio/modules/osp/destination.c

446 lines
12 KiB

/*
* Kamailio osp module.
*
* This module enables Kamailio to communicate with an Open Settlement
* Protocol (OSP) server. The Open Settlement Protocol is an ETSI
* defined standard for Inter-Domain VoIP pricing, authorization
* and usage exchange. The technical specifications for OSP
* (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
*
* Uli Abend was the original contributor to this module.
*
* Copyright (C) 2001-2005 Fhg Fokus
*
* This file is part of Kamailio, a free SIP server.
*
* 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
*
* Kamailio 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
*/
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "../../str.h"
#include "../../dprint.h"
#include "../../usr_avp.h"
#include "destination.h"
#include "usage.h"
/* Name of AVP of OSP destination list */
const str OSP_ORIGDEST_NAME = {"_osp_orig_dests_", 16};
const str OSP_TERMDEST_NAME = {"_osp_term_dests_", 16};
static int ospSaveDestination(osp_dest* dest, const str* name);
static void ospRecordCode(int code, osp_dest* dest);
static int ospIsToReportUsage(int code);
/*
* Initialize destination structure
* param dest Destination data structure
* return initialized destination sturcture
*/
osp_dest* ospInitDestination(
osp_dest* dest)
{
memset(dest, 0, sizeof(osp_dest));
dest->callidsize = sizeof(dest->callid);
dest->tokensize = sizeof(dest->token);
LM_DBG("callidsize '%d' tokensize '%d'\n", dest->callidsize, dest->tokensize);
return dest;
}
/*
* Save destination as an AVP
* name - OSP_ORIGDEST_NAME / OSP_TERMDEST_NAME
* value - osp_dest wrapped in a string
* param dest Destination structure
* param name Name of AVP
* return 0 success, -1 failure
*/
static int ospSaveDestination(
osp_dest* dest,
const str* name)
{
str wrapper;
int result = -1;
wrapper.s = (char*)dest;
wrapper.len = sizeof(osp_dest);
/*
* add_avp will make a private copy of both the name and value in shared memory
* which will be released by TM at the end of the transaction
*/
if (add_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)*name, (int_str)wrapper) == 0) {
LM_DBG("destination saved\n");
result = 0;
} else {
LM_ERR("failed to save destination\n");
}
return result;
}
/*
* Save originate destination
* param dest Originate destination structure
* return 0 success, -1 failure
*/
int ospSaveOrigDestination(
osp_dest* dest)
{
return ospSaveDestination(dest, &OSP_ORIGDEST_NAME);
}
/*
* Save terminate destination
* param dest Terminate destination structure
* return 0 success, -1 failure
*/
int ospSaveTermDestination(
osp_dest* dest)
{
return ospSaveDestination(dest, &OSP_TERMDEST_NAME);
}
/*
* Check if there is an unused and supported originate destination from an AVP
* name - OSP_ORIGDEST_NAME
* value - osp_dest wrapped in a string
* search unused (used==0) & supported (support==1)
* return 0 success, -1 failure
*/
int ospCheckOrigDestination(void)
{
struct usr_avp* destavp = NULL;
int_str destval;
osp_dest* dest = NULL;
int result = -1;
struct search_state st;
for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, NULL, &st);
destavp != NULL;
destavp = search_next_avp(&st, NULL))
{
get_avp_val(destavp, &destval);
/* OSP destintaion is wrapped in a string */
dest = (osp_dest*)destval.s.s;
if (dest->used == 0) {
if (dest->supported == 1) {
LM_DBG("orig dest exist\n");
result = 0;
break;
} else {
LM_DBG("destination does not been supported\n");
}
} else {
LM_DBG("destination has already been used\n");
}
}
if (result == -1) {
LM_DBG("there is not unused destination\n");
}
return result;
}
/*
* Retrieved an unused and supported originate destination from an AVP
* name - OSP_ORIGDEST_NAME
* value - osp_dest wrapped in a string
* There can be 0, 1 or more originate destinations.
* Find the 1st unused destination (used==0) & supported (support==1),
* return it, and mark it as used (used==1).
* return NULL on failure
*/
osp_dest* ospGetNextOrigDestination(void)
{
struct usr_avp* destavp = NULL;
int_str destval;
osp_dest* dest = NULL;
osp_dest* result = NULL;
struct search_state st;
for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, NULL, &st);
destavp != NULL;
destavp = search_next_avp(&st, NULL))
{
get_avp_val(destavp, &destval);
/* OSP destintaion is wrapped in a string */
dest = (osp_dest*)destval.s.s;
if (dest->used == 0) {
if (dest->supported == 1) {
LM_DBG("orig dest found\n");
dest->used = 1;
result = dest;
break;
} else {
/* Make it looks like used */
dest->used = 1;
/* 111 means wrong protocol */
dest->lastcode = 111;
LM_DBG("destination does not been supported\n");
}
} else {
LM_DBG("destination has already been used\n");
}
}
if (result == NULL) {
LM_DBG("there is not unused destination\n");
}
return result;
}
/*
* Retrieved the last used originate destination from an AVP
* name - OSP_ORIGDEST_NAME
* value - osp_dest wrapped in a string
* There can be 0, 1 or more destinations.
* Find the last used destination (used==1) & supported (support==1),
* and return it.
* In normal condition, this one is the current destination. But it may
* be wrong for loop condition.
* return NULL on failure
*/
osp_dest* ospGetLastOrigDestination(void)
{
struct usr_avp* destavp = NULL;
int_str destval;
osp_dest* dest = NULL;
osp_dest* lastdest = NULL;
struct search_state st;
for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, NULL, &st);
destavp != NULL;
destavp = search_next_avp(&st, NULL))
{
get_avp_val(destavp, &destval);
/* OSP destination is wrapped in a string */
dest = (osp_dest*)destval.s.s;
if (dest->used == 1) {
if (dest->supported == 1) {
lastdest = dest;
LM_DBG("curent destination '%s'\n", lastdest->host);
}
} else {
break;
}
}
return lastdest;
}
/*
* Retrieved the terminate destination from an AVP
* name - OSP_TERMDEST_NAME
* value - osp_dest wrapped in a string
* There can be 0 or 1 term destinations. Find and return it.
* return NULL on failure (no terminate destination)
*/
osp_dest* ospGetTermDestination(void)
{
int_str destval;
osp_dest* dest = NULL;
if (search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME, &destval, 0) != NULL) {
/* OSP destination is wrapped in a string */
dest = (osp_dest*)destval.s.s;
LM_DBG("term dest found\n");
}
return dest;
}
/*
* Record destination status
* param code Destination status
* param dest Destination
*/
static void ospRecordCode(
int code,
osp_dest* dest)
{
LM_DBG("code '%d'\n", code);
dest->lastcode = code;
switch (code) {
case 100:
if (!dest->time100) {
dest->time100 = time(NULL);
} else {
LM_DBG("100 already recorded\n");
}
break;
case 180:
case 181:
case 182:
case 183:
if (!dest->time180) {
dest->time180 = time(NULL);
} else {
LM_DBG("180, 181, 182 or 183 allready recorded\n");
}
break;
case 200:
case 202:
if (!dest->time200) {
dest->time200 = time(NULL);
} else {
LM_DBG("200 or 202 allready recorded\n");
}
break;
default:
LM_DBG("will not record time for '%d'\n", code);
}
}
/*
* Check destination status for reporting usage
* param code Destination status
* return 1 should report, 0 should not report
*/
static int ospIsToReportUsage(
int code)
{
int istime = 0;
LM_DBG("code '%d'\n", code);
if (code >= 200) {
istime = 1;
}
return istime;
}
/*
* Report call setup usage for both client and server side
* param clientcode Client status
* param servercode Server status
*/
void ospRecordEvent(
int clientcode,
int servercode)
{
osp_dest* dest;
LM_DBG("client status '%d'\n", clientcode);
if ((clientcode != 0) && (dest = ospGetLastOrigDestination())) {
ospRecordCode(clientcode, dest);
if (ospIsToReportUsage(servercode) == 1) {
ospReportOrigSetupUsage();
}
}
LM_DBG("server status '%d'\n", servercode);
if ((servercode != 0) && (dest = ospGetTermDestination())) {
ospRecordCode(servercode, dest);
if (ospIsToReportUsage(servercode) == 1) {
ospReportTermSetupUsage();
}
}
}
/*
* Dump destination information
* param dest Destination
*/
void ospDumpDestination(
osp_dest* dest)
{
LM_DBG("dest->host..........'%s'\n", dest->host);
LM_DBG("dest->used..........'%d'\n", dest->used);
LM_DBG("dest->lastcode......'%d'\n", dest->lastcode);
LM_DBG("dest->time100.......'%d'\n", (unsigned int)dest->time100);
LM_DBG("dest->time180.......'%d'\n", (unsigned int)dest->time180);
LM_DBG("dest->time200.......'%d'\n", (unsigned int)dest->time200);
}
/*
* Dump all destination information
*/
void ospDumpAllDestination(void)
{
struct usr_avp* destavp = NULL;
int_str destval;
osp_dest* dest = NULL;
int count = 0;
struct search_state st;
for (destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_ORIGDEST_NAME, NULL, &st);
destavp != NULL;
destavp = search_next_avp(&st, NULL))
{
get_avp_val(destavp, &destval);
/* OSP destination is wrapped in a string */
dest = (osp_dest*)destval.s.s;
LM_DBG("....originate '%d'....\n", count++);
ospDumpDestination(dest);
}
if (count == 0) {
LM_DBG("there is not originate destination AVP\n");
}
if (search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME, &destval, 0) != NULL) {
/* OSP destination is wrapped in a string */
dest = (osp_dest*)destval.s.s;
LM_DBG("....terminate....\n");
ospDumpDestination(dest);
} else {
LM_DBG("there is not terminate destination AVP\n");
}
}
/*
* Convert address to "[x.x.x.x]" or "host.domain" format
* param src Source address
* param dst Destination address
* param buffersize Size of dst buffer
*/
void ospConvertAddress(
char* src,
char* dst,
int buffersize)
{
struct in_addr inp;
if (inet_aton(src, &inp) != 0) {
snprintf(dst, buffersize, "[%s]", src);
} else {
snprintf(dst, buffersize, "%s", src);
}
}