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/sipheader.c

628 lines
16 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 <osp/osp.h>
#include <osp/ospb64.h>
#include "../../forward.h"
#include "../../parser/parse_from.h"
#include "../../parser/parse_rpid.h"
#include "../../parser/parse_rr.h"
#include "../../parser/parse_uri.h"
#include "../../data_lump.h"
#include "../../mem/mem.h"
#include "osp_mod.h"
#include "sipheader.h"
extern int _osp_use_rpid;
static void ospSkipPlus(char* e164);
static int ospAppendHeader(struct sip_msg* msg, str* header);
/*
* Copy str to buffer and check overflow
* param source Str
* param buffer Buffer
* param buffersize Size of buffer
*/
void ospCopyStrToBuffer(
str* source,
char* buffer,
int buffersize)
{
int copybytes;
if (source->len > buffersize - 1) {
LM_ERR("buffer for copying '%.*s' is too small, will copy the first '%d' bytes\n",
source->len,
source->s,
buffersize);
copybytes = buffersize - 1;
} else {
copybytes = source->len;
}
strncpy(buffer, source->s, copybytes);
buffer[copybytes] = '\0';
}
/*
* Remove '+' in E164 string
* param e164 E164 string
*/
static void ospSkipPlus(
char* e164)
{
if (*e164 == '+') {
strncpy(e164, e164 + 1, strlen(e164) - 1);
e164[strlen(e164) - 1] = '\0';
}
}
/*
* Get calling number from From header
* param msg SIP message
* param fromuser User part of From header
* param buffersize Size of fromuser buffer
* return 0 success, -1 failure
*/
int ospGetFromUserpart(
struct sip_msg* msg,
char* fromuser,
int buffersize)
{
struct to_body* from;
struct sip_uri uri;
int result = -1;
fromuser[0] = '\0';
if (msg->from != NULL) {
if (parse_from_header(msg) == 0) {
from = get_from(msg);
if (parse_uri(from->uri.s, from->uri.len, &uri) == 0) {
ospCopyStrToBuffer(&uri.user, fromuser, buffersize);
ospSkipPlus(fromuser);
result = 0;
} else {
LM_ERR("failed to parse From uri\n");
}
} else {
LM_ERR("failed to parse From header\n");
}
} else {
LM_ERR("failed to find From header\n");
}
return result;
}
/*
* Get calling number from Remote-Party-ID header
* param msg SIP message
* param rpiduser User part of Remote-Party-ID header
* param buffersize Size of fromuser buffer
* return 0 success, -1 failure
*/
int ospGetRpidUserpart(
struct sip_msg* msg,
char* rpiduser,
int buffersize)
{
struct to_body* rpid;
struct sip_uri uri;
int result = -1;
rpiduser[0] = '\0';
if (_osp_use_rpid != 0) {
if (msg->rpid != NULL) {
if (parse_rpid_header(msg) == 0) {
rpid = get_rpid(msg);
if (parse_uri(rpid->uri.s, rpid->uri.len, &uri) == 0) {
ospCopyStrToBuffer(&uri.user, rpiduser, buffersize);
ospSkipPlus(rpiduser);
result = 0;
} else {
LM_ERR("failed to parse RPID uri\n");
}
} else {
LM_ERR("failed to parse RPID header\n");
}
} else {
LM_DBG("without RPID header\n");
}
} else {
LM_DBG("do not use RPID header\n");
}
return result;
}
/*
* Get called number from To header
* param msg SIP message
* param touser User part of To header
* param buffersize Size of touser buffer
* return 0 success, -1 failure
*/
int ospGetToUserpart(
struct sip_msg* msg,
char* touser,
int buffersize)
{
struct to_body* to;
struct sip_uri uri;
int result = -1;
touser[0] = '\0';
if (msg->to != NULL) {
if (parse_headers(msg, HDR_TO_F, 0) == 0) {
to = get_to(msg);
if (parse_uri(to->uri.s, to->uri.len, &uri) == 0) {
ospCopyStrToBuffer(&uri.user, touser, buffersize);
ospSkipPlus(touser);
result = 0;
} else {
LM_ERR("failed to parse To uri\n");
}
} else {
LM_ERR("failed to parse To header\n");
}
} else {
LM_ERR("failed to find To header\n");
}
return result;
}
/*
* Get called number from Request-Line header
* param msg SIP message
* param touser User part of To header
* param buffersize Size of touser buffer
* return 0 success, -1 failure
*/
int ospGetUriUserpart(
struct sip_msg* msg,
char* uriuser,
int buffersize)
{
int result = -1;
uriuser[0] = '\0';
if (parse_sip_msg_uri(msg) >= 0) {
ospCopyStrToBuffer(&msg->parsed_uri.user, uriuser, buffersize);
ospSkipPlus(uriuser);
result = 0;
} else {
LM_ERR("failed to parse Request-Line URI\n");
}
return result;
}
/*
* Append header to SIP message
* param msg SIP message
* param header Header to be appended
* return 0 success, -1 failure
*/
static int ospAppendHeader(
struct sip_msg* msg,
str* header)
{
char* s;
struct lump* anchor;
if((msg == 0) || (header == 0) || (header->s == 0) || (header->len <= 0)) {
LM_ERR("bad parameters for appending header\n");
return -1;
}
if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
LM_ERR("failed to parse message\n");
return -1;
}
anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
if (anchor == 0) {
LM_ERR("failed to get anchor\n");
return -1;
}
s = (char*)pkg_malloc(header->len);
if (s == 0) {
LM_ERR("no pkg memory\n");
return -1;
}
memcpy(s, header->s, header->len);
if (insert_new_lump_before(anchor, s, header->len, 0) == 0) {
LM_ERR("failed to insert lump\n");
pkg_free(s);
return -1;
}
return 0;
}
/*
* Add OSP token header to SIP message
* param msg SIP message
* param token OSP authorization token
* param tokensize Size of OSP authorization token
* return 0 success, -1 failure
*/
int ospAddOspHeader(
struct sip_msg* msg,
unsigned char* token,
unsigned int tokensize)
{
str headerval;
char buffer[OSP_HEADERBUF_SIZE];
unsigned char encodedtoken[OSP_TOKENBUF_SIZE];
unsigned int encodedtokensize = sizeof(encodedtoken);
int result = -1;
if (tokensize == 0) {
LM_DBG("destination is not OSP device\n");
result = 0;
} else {
if (OSPPBase64Encode(token, tokensize, encodedtoken, &encodedtokensize) == 0) {
snprintf(buffer,
sizeof(buffer),
"%s%.*s\r\n",
OSP_TOKEN_HEADER,
encodedtokensize,
encodedtoken);
headerval.s = buffer;
headerval.len = strlen(buffer);
LM_DBG("setting osp token header field '%s'\n", buffer);
if (ospAppendHeader(msg, &headerval) == 0) {
result = 0;
} else {
LM_ERR("failed to append osp header\n");
}
} else {
LM_ERR("failed to base64 encode token\n");
}
}
return result;
}
/*
* Get OSP token from SIP message
* param msg SIP message
* param token OSP authorization token
* param tokensize Size of OSP authorization token
* return 0 success, -1 failure
*/
int ospGetOspHeader(
struct sip_msg* msg,
unsigned char* token,
unsigned int* tokensize)
{
struct hdr_field* hf;
int errorcode;
int result = -1;
parse_headers(msg, HDR_EOH_F, 0);
for (hf = msg->headers; hf; hf = hf->next) {
if ((hf->type == HDR_OTHER_T) && (hf->name.len == OSP_HEADER_SIZE - 2)) {
// possible hit
if (strncasecmp(hf->name.s, OSP_TOKEN_HEADER, OSP_HEADER_SIZE) == 0) {
if ((errorcode = OSPPBase64Decode(hf->body.s, hf->body.len, token, tokensize)) == OSPC_ERR_NO_ERROR) {
result = 0;
} else {
LM_ERR("failed to base64 decode token (%d)\n", errorcode);
LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s, hf->body.len);
}
break;
}
}
}
return result;
}
/*
* Get first VIA header and use the IP or host name
* param msg SIP message
* param sourceaddress Source address
* param buffersize Size of sourceaddress
* return 0 success, -1 failure
*/
int ospGetSourceAddress(
struct sip_msg* msg,
char* sourceaddress,
int buffersize)
{
struct hdr_field* hf;
struct via_body* via;
int result = -1;
/*
* No need to call parse_headers, called already and VIA is parsed
* anyway by default
*/
for (hf = msg->headers; hf; hf = hf->next) {
if (hf->type == HDR_VIA_T) {
// found first VIA
via = (struct via_body*)hf->parsed;
ospCopyStrToBuffer(&via->host, sourceaddress, buffersize);
LM_DBG("source address '%s'\n", sourceaddress);
result = 0;
break;
}
}
return result;
}
/*
* Get Call-ID header from SIP message
* param msg SIP message
* param callid Call ID
* return 0 success, -1 failure
*/
int ospGetCallId(
struct sip_msg* msg,
OSPTCALLID** callid)
{
struct hdr_field* hf;
int result = -1;
hf = (struct hdr_field*)msg->callid;
if (hf != NULL) {
*callid = OSPPCallIdNew(hf->body.len, (unsigned char*)hf->body.s);
if (*callid) {
result = 0;
} else {
LM_ERR("failed to allocate OSPCALLID object for '%.*s'\n", hf->body.len, hf->body.s);
}
} else {
LM_ERR("failed to find Call-ID header\n");
}
return result;
}
/*
* Get route parameters from the 1st Route or Request-Line
* param msg SIP message
* param routeparameters Route parameters
* param buffersize Size of routeparameters
* return 0 success, -1 failure
*/
int ospGetRouteParameters(
struct sip_msg* msg,
char* routeparameters,
int buffersize)
{
struct hdr_field* hf;
rr_t* rt;
struct sip_uri uri;
int result = -1;
LM_DBG("parsed uri host '%.*s' port '%d' vars '%.*s'\n",
msg->parsed_uri.host.len,
msg->parsed_uri.host.s,
msg->parsed_uri.port_no,
msg->parsed_uri.params.len,
msg->parsed_uri.params.s);
if (!(hf = msg->route)) {
LM_DBG("there is no Route headers\n");
} else if (!(rt = (rr_t*)hf->parsed)) {
LM_ERR("route headers are not parsed\n");
} else if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) != 0) {
LM_ERR("failed to parse the Route uri '%.*s'\n", rt->nameaddr.uri.len, rt->nameaddr.uri.s);
} else if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) {
LM_DBG("the Route uri is NOT mine\n");
LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no);
LM_DBG("params '%.*s'\n", uri.params.len, uri.params.s);
} else {
LM_DBG("the Route uri IS mine - '%.*s'\n", uri.params.len, uri.params.s);
LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no);
ospCopyStrToBuffer(&uri.params, routeparameters, buffersize);
result = 0;
}
if ((result == -1) && (msg->parsed_uri.params.len > 0)) {
LM_DBG("using route parameters from Request-Line uri\n");
ospCopyStrToBuffer(&msg->parsed_uri.params, routeparameters, buffersize);
routeparameters[msg->parsed_uri.params.len] = '\0';
result = 0;
}
return result;
}
/*
* Rebuild URI using called number, destination IP, and port
* param newuri URI to be built
* param called Called number
* param dest Destination IP
* param port Destination port
* param format URI format
* return 0 success, -1 failure
*/
int ospRebuildDestionationUri(
str* newuri,
char* called,
char* dest,
char* port,
int format)
{
static const str TRANS = {";transport=tcp", 14};
char* buffer;
int calledsize;
int destsize;
int portsize;
calledsize = strlen(called);
destsize = strlen(dest);
portsize = strlen(port);
LM_DBG("'%s'(%d) '%s'(%d) '%s'(%d) '%d'\n",
called,
calledsize,
dest,
destsize,
port,
portsize,
format);
/* "sip:" + called + "@" + dest + : + port + " SIP/2.0" for URI format 0 */
/* "<sip:" + called + "@" + dest + : + port> + " SIP/2.0" for URI format 1 */
newuri->s = (char*)pkg_malloc(1 + 4 + calledsize + 1 + destsize + 1 + portsize + 1 + 1 + 16 + TRANS.len);
if (newuri == NULL) {
LM_ERR("no pkg memory\n");
return -1;
}
buffer = newuri->s;
if (format == 1) {
*buffer++ = '<';
}
*buffer++ = 's';
*buffer++ = 'i';
*buffer++ = 'p';
*buffer++ = ':';
memcpy(buffer, called, calledsize);
buffer += calledsize;
*buffer++ = '@';
if (*dest == '[') {
/* leave out annoying [] */
memcpy(buffer, dest + 1, destsize - 2);
buffer += destsize - 2;
} else {
memcpy(buffer, dest, destsize);
buffer += destsize;
}
if (portsize > 0) {
*buffer++ = ':';
memcpy(buffer, port, portsize);
buffer += portsize;
}
if (format == 1) {
*buffer++ = '>';
}
/*
*buffer++ = ' ';
*buffer++ = 'S';
*buffer++ = 'I';
*buffer++ = 'P';
*buffer++ = '/';
*buffer++ = '2';
*buffer++ = '.';
*buffer++ = '0';
memcpy(buffer, TRANS.s, TRANS.len);
buffer += TRANS.len;
*buffer = '\0';
*/
newuri->len = buffer - newuri->s;
LM_DBG("new uri '%.*s'\n", newuri->len, newuri->s);
return 0;
}
/*
* Get next hop using the first Route not generated by this proxy or URI from the Request-Line
* param msg SIP message
* param nexthop Next hop IP
* param buffersize Size of nexthop
*/
void ospGetNextHop(
struct sip_msg* msg,
char* nexthop,
int buffersize)
{
struct hdr_field* hf;
struct sip_uri uri;
rr_t* rt;
int found = 0;
for (hf = msg->headers; hf; hf = hf->next) {
if (hf->type == HDR_ROUTE_T) {
for (rt = (rr_t*)hf->parsed; rt; rt = rt->next) {
if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) == 0) {
LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no);
if (check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE) != 1) {
LM_DBG("it is NOT me, FOUND!\n");
ospCopyStrToBuffer(&uri.host, nexthop, buffersize);
found = 1;
break;
} else {
LM_DBG("it IS me, keep looking\n");
}
} else {
LM_ERR("failed to parse route uri '%.*s'\n",
rt->nameaddr.uri.len,
rt->nameaddr.uri.s);
}
}
if (found == 1) {
break;
}
}
}
if (!found) {
LM_DBG("using the Request-Line instead host '%.*s' port '%d'\n",
msg->parsed_uri.host.len,
msg->parsed_uri.host.s,
msg->parsed_uri.port_no);
ospCopyStrToBuffer(&msg->parsed_uri.host, nexthop, buffersize);
found = 1;
}
}