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

726 lines
20 KiB

/*
* ser osp module.
*
* This module enables ser 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 ser, a free SIP server.
*
* ser 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <osp/osp.h>
#include <osp/ospb64.h>
#include "../../trim.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;
LOG(L_DBG, "osp: ospCopyStrToBuffer\n");
if (source->len > buffersize - 1) {
LOG(L_ERR,
"osp: ERROR: 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)
{
LOG(L_DBG, "osp: ospSkipPlus\n");
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;
LOG(L_DBG, "osp: ospGetFromUserpart\n");
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 {
LOG(L_ERR, "osp: ERROR: failed to parse From uri\n");
}
} else {
LOG(L_ERR, "osp: ERROR: failed to parse From header\n");
}
} else {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospGetRpidUserpart\n");
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 {
LOG(L_ERR, "osp: ERROR: failed to parse RPID uri\n");
}
} else {
LOG(L_ERR, "osp: ERROR: failed to parse RPID header\n");
}
} else {
LOG(L_DBG, "osp: without RPID header\n");
}
} else {
LOG(L_DBG, "osp: 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;
LOG(L_DBG, "osp: ospGetToUserpart\n");
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 {
LOG(L_ERR, "osp: ERROR: failed to parse To uri\n");
}
} else {
LOG(L_ERR, "osp: ERROR: failed to parse To header\n");
}
} else {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospGetUriUserpart\n");
uriuser[0] = '\0';
if (parse_sip_msg_uri(msg) >= 0) {
ospCopyStrToBuffer(&msg->parsed_uri.user, uriuser, buffersize);
ospSkipPlus(uriuser);
result = 0;
} else {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospAppendHeader\n");
if((msg == 0) || (header == 0) || (header->s == 0) || (header->len <= 0)) {
LOG(L_ERR, "osp: ERROR: bad parameters for appending header\n");
return -1;
}
if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
LOG(L_ERR, "osp: ERROR: failed to parse message\n");
return -1;
}
anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
if (anchor == 0) {
LOG(L_ERR, "osp: ERROR: failed to get anchor\n");
return -1;
}
s = (char*)pkg_malloc(header->len);
if (s == 0) {
LOG(L_ERR, "osp: ERROR: no memory\n");
return -1;
}
memcpy(s, header->s, header->len);
if (insert_new_lump_before(anchor, s, header->len, 0) == 0) {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospAddOspHeader\n");
if (tokensize == 0) {
LOG(L_DBG, "osp: 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);
LOG(L_DBG, "osp: setting osp token header field '%s'\n", buffer);
if (ospAppendHeader(msg, &headerval) == 0) {
result = 0;
} else {
LOG(L_ERR, "osp: ERROR: failed to append osp header\n");
}
} else {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospGetOspHeader\n");
if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
LOG(L_ERR, "osp: ERROR: failed to parse headers\n");
return result;
}
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)) == 0) {
result = 0;
} else {
LOG(L_ERR, "osp: ERROR: failed to base64 decode token (%d)\n", errorcode);
LOG(L_ERR, "osp: ERROR: 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 via_body* via;
int result = -1;
LOG(L_DBG, "osp: ospGetSourceAddress\n");
if (msg->h_via1 || (parse_headers(msg, HDR_VIA_F, 0) == 0 && msg->h_via1)) {
via = msg->h_via1->parsed;
ospCopyStrToBuffer(&via->host, sourceaddress, buffersize);
LOG(L_DBG, "osp: source address '%s'\n", sourceaddress);
result = 0;
}
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;
LOG(L_DBG, "osp: ospGetCallId\n");
if (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0) == -1) {
LOG(L_ERR, "osp: failed to parse Call-ID\n");
return result;
}
hf = (struct hdr_field*)msg->callid;
if (hf != NULL) {
*callid = OSPPCallIdNew(hf->body.len, (unsigned char*)hf->body.s);
if (*callid) {
result = 0;
} else {
LOG(L_ERR, "osp: ERROR: failed to allocate OSPCALLID object for '%.*s'\n", hf->body.len, hf->body.s);
}
} else {
LOG(L_ERR, "osp: ERROR: 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;
LOG(L_DBG, "osp: ospGetRouterParameters\n");
LOG(L_DBG, "osp: 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)) {
LOG(L_DBG, "osp: there is no Route headers\n");
} else if (!(rt = (rr_t*)hf->parsed)) {
LOG(L_ERR, "osp: ERROR: route headers are not parsed\n");
} else if (parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) != 0) {
LOG(L_ERR, "osp: ERROR: 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) {
LOG(L_DBG, "osp: the Route uri is NOT mine\n");
LOG(L_DBG, "osp: host '%.*s' port '%d'\n", uri.host.len, uri.host.s, uri.port_no);
LOG(L_DBG, "osp: params '%.*s'\n", uri.params.len, uri.params.s);
} else {
LOG(L_DBG, "osp: the Route uri IS mine - '%.*s'\n", uri.params.len, uri.params.s);
LOG(L_DBG, "osp: 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)) {
LOG(L_DBG, "osp: 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;
LOG(L_DBG, "osp: ospRebuildDestinationUri\n");
calledsize = strlen(called);
destsize = strlen(dest);
portsize = strlen(port);
LOG(L_DBG, "osp: '%s'(%i) '%s'(%i) '%s'(%i) '%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) {
LOG(L_ERR, "osp: ERROR: no 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;
LOG(L_DBG, "osp: 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;
LOG(L_DBG, "osp: ospGetNextHop\n");
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) {
LOG(L_DBG, "osp: 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) {
LOG(L_DBG, "osp: it is NOT me, FOUND!\n");
ospCopyStrToBuffer(&uri.host, nexthop, buffersize);
found = 1;
break;
} else {
LOG(L_DBG, "osp: it IS me, keep looking\n");
}
} else {
LOG(L_ERR,
"osp: ERROR: failed to parsed route uri '%.*s'\n",
rt->nameaddr.uri.len,
rt->nameaddr.uri.s);
}
}
if (found == 1) {
break;
}
}
}
if (!found) {
LOG(L_DBG, "osp: 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;
}
}
/*
* Get direction using the first Route not generated by this proxy or URI from the Request-Line
* SER does not have is_direction as OpenSER. We have to write it.
* The problem is we cannot check append_fromtag option before running. So, ftag may not exist.
* param msg SIP message
* return 0 originating, 1 terminating, -1 failed
*/
int ospGetDirection(struct sip_msg* msg)
{
static const str FTAG = {"ftag", 4};
char parameters[OSP_HEADERBUF_SIZE];
char* tmp;
char* token;
str ftag;
struct to_body* from;
int result = -1;
LOG(L_DBG, "osp: ospGetDirection\n");
if (ospGetRouteParameters(msg, parameters, sizeof(parameters)) == 0) {
for (token = strtok_r(parameters, ";", &tmp);
token;
token = strtok_r(NULL, ";", &tmp))
{
ftag.s = token;
ftag.len = strlen(token);
/* Remove leading white space char */
trim_leading(&ftag);
if (strncmp(ftag.s, FTAG.s, FTAG.len) == 0) {
/* Remove "ftag" string */
ftag.s += FTAG.len;
ftag.len -= FTAG.len;
/* Remove leading white space char */
trim_leading(&ftag);
/* Remove '=' char */
ftag.s++;
ftag.len--;
/* Remove leading and tailing white space char */
trim(&ftag);
LOG(L_DBG, "osp: ftag '%s'\n", ftag.s);
if ((parse_from_header(msg) == 0) && ((from = get_from(msg)) != 0)) {
if ((ftag.len == from->tag_value.len) && !strncmp(ftag.s, from->tag_value.s, ftag.len)) {
LOG(L_DBG, "osp: originating\n");
result = 0;
} else {
LOG(L_DBG, "osp: terminating\n");
result = 1;
}
}
break;
} else {
LOG(L_DBG, "osp: ignoring parameter '%s'\n", token);
}
}
}
return result;
}