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.
446 lines
12 KiB
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);
|
|
}
|
|
}
|
|
|