/* * 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 #include #include #include #include #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); } }