/* * 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 "osp_mod.h" #include "term_transaction.h" #include "sipheader.h" #include "destination.h" #include "osptoolkit.h" #include "usage.h" extern char* _osp_device_ip; extern int _osp_token_format; extern int _osp_validate_callid; extern OSPTPROVHANDLE _osp_provider; /* * Get OSP token * param msg SIP message * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure */ int ospCheckHeader( struct sip_msg* msg, char* ignore1, char* ignore2) { unsigned char buffer[OSP_TOKENBUF_SIZE]; unsigned int buffersize = sizeof(buffer); if (ospGetOspHeader(msg, buffer, &buffersize) != 0) { return MODULE_RETURNCODE_FALSE; } else { return MODULE_RETURNCODE_TRUE; } } /* * Validate OSP token * param ignore1 * param ignore2 * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure MODULE_RETURNCODE_ERROR error */ int ospValidateHeader ( struct sip_msg* msg, char* ignore1, char* ignore2) { int errorcode; OSPTTRANHANDLE transaction = -1; unsigned int authorized = 0; unsigned int timelimit = 0; void* detaillog = NULL; unsigned int logsize = 0; unsigned char* callidval = (unsigned char*)""; OSPTCALLID* callid = NULL; unsigned callidsize = 0; unsigned char token[OSP_TOKENBUF_SIZE]; unsigned int tokensize = sizeof(token); osp_dest dest; int result = MODULE_RETURNCODE_FALSE; ospInitDestination(&dest); if ((errorcode = OSPPTransactionNew(_osp_provider, &transaction) != OSPC_ERR_NO_ERROR)) { LM_ERR("failed to create a new OSP transaction handle (%d)\n", errorcode); } else if ((ospGetRpidUserpart(msg, dest.calling, sizeof(dest.calling)) != 0) && (ospGetFromUserpart(msg, dest.calling, sizeof(dest.calling)) != 0)) { LM_ERR("failed to extract calling number\n"); } else if ((ospGetUriUserpart(msg, dest.called, sizeof(dest.called)) != 0) && (ospGetToUserpart(msg, dest.called, sizeof(dest.called)) != 0)) { LM_ERR("failed to extract called number\n"); } else if (ospGetCallId(msg, &callid) != 0) { LM_ERR("failed to extract call id\n"); } else if (ospGetSourceAddress(msg, dest.source, sizeof(dest.source)) != 0) { LM_ERR("failed to extract source address\n"); } else if (ospGetOspHeader(msg, token, &tokensize) != 0) { LM_ERR("failed to extract OSP authorization token\n"); } else { LM_INFO( "validate token for: " "transaction_handle '%d' " "e164_source '%s' " "e164_dest '%s' " "validate_call_id '%s' " "call_id '%.*s'\n", transaction, dest.calling, dest.called, _osp_validate_callid == 0 ? "No" : "Yes", callid->ospmCallIdLen, callid->ospmCallIdVal); if (_osp_validate_callid != 0) { callidsize = callid->ospmCallIdLen; callidval = callid->ospmCallIdVal; } errorcode = OSPPTransactionValidateAuthorisation( transaction, "", "", "", "", dest.calling, OSPC_E164, dest.called, OSPC_E164, callidsize, callidval, tokensize, token, &authorized, &timelimit, &logsize, detaillog, _osp_token_format); if ((errorcode == OSPC_ERR_NO_ERROR) && (authorized == 1)) { if (callid->ospmCallIdLen > sizeof(dest.callid) - 1) { dest.callidsize = sizeof(dest.callid) - 1; } else { dest.callidsize = callid->ospmCallIdLen; } memcpy(dest.callid, callid->ospmCallIdVal, dest.callidsize); dest.callid[dest.callidsize] = 0; dest.transid = ospGetTransactionId(transaction); dest.type = OSPC_DESTINATION; dest.authtime = time(NULL); strncpy(dest.host, _osp_device_ip, sizeof(dest.host) - 1); strncpy(dest.origcalled, dest.called, sizeof(dest.origcalled) - 1); if (ospSaveTermDestination(&dest) == -1) { LM_ERR("failed to save terminate destination\n"); ospRecordEvent(0, 500); result = MODULE_RETURNCODE_ERROR; } else { LM_DBG("call is authorized for %d seconds, call_id '%.*s' transaction_id '%llu'", timelimit, dest.callidsize, dest.callid, dest.transid); ospRecordTermTransaction(msg, dest.transid, dest.source, dest.calling, dest.called, dest.authtime); result = MODULE_RETURNCODE_TRUE; } } else { LM_ERR("token is invalid (%d)\n", errorcode); /* * Update terminating status code to 401 and report terminating setup usage. * We may need to make 401 configurable, just in case a user decides to reply with * a different code. Other options - trigger call setup usage reporting from the cpl * (after replying with an error code), or maybe use a different tm callback. */ ospRecordEvent(0, 401); result = MODULE_RETURNCODE_FALSE; } } if (transaction != -1) { OSPPTransactionDelete(transaction); } if (callid != NULL) { OSPPCallIdDelete(&callid); } return result; }