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/rtjson/rtjson_routing.c

735 lines
16 KiB

/**
* Copyright (C) 2015 Daniel-Constantin Mierla (asipto.com)
*
* This file is part of Kamailio, a free SIP server.
*
* This file 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
*
*
* This file 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "../../dprint.h"
#include "../../xavp.h"
#include "../../dset.h"
#include "../../mem/shm_mem.h"
#include "../../data_lump.h"
#include "../../lib/srutils/srjson.h"
#include "../../modules/tm/tm_load.h"
#include "../../modules/uac/api.h"
#include "rtjson_routing.h"
typedef struct rtjson_data {
srjson_doc_t *jdoc;
int idx;
} rtjson_data_t;
extern str _rtjson_xavp_name;
int rtjson_init_serial(sip_msg_t *msg, srjson_doc_t *jdoc, sr_xavp_t *iavp);
int rtjson_init_parallel(sip_msg_t *msg, srjson_doc_t *jdoc, sr_xavp_t *iavp);
/* tm */
static struct tm_binds tmb;
/* uac */
static uac_api_t uacb;
int rtjson_init(void)
{
if (load_tm_api( &tmb ) == -1) {
LM_NOTICE("cannot load the TM API - some features are diabled\n");
memset(&tmb, 0, sizeof(struct tm_binds));
}
if (load_uac_api(&uacb) < 0) {
LM_NOTICE("cannot bind to UAC API - some features are diabled\n");
memset(&uacb, 0, sizeof(uac_api_t));
}
return 0;
}
#ifdef RTJSON_STORE_SHM
/**
*
*/
void rtjson_data_free(void *ptr, sr_xavp_sfree_f sfree)
{
rtjson_data_t *rdata;
rdata = (rtjson_data_t*)ptr;
if(rdata->jdoc) {
rdata->jdoc->free_fn = sfree;
srjson_DeleteDoc(rdata->jdoc);
}
sfree(ptr);
}
/**
*
*/
void *rtjson_malloc(size_t sz)
{
return shm_malloc(sz);
}
/**
*
*/
void rtjson_free(void *ptr)
{
shm_free(ptr);
}
/**
*
*/
int rtjson_init_routes(sip_msg_t *msg, str *rdoc)
{
srjson_Hooks jhooks;
srjson_doc_t *tdoc = NULL;
sr_data_t *xdata = NULL;
rtjson_data_t *rdata = NULL;
sr_xavp_t *xavp=NULL;
str xname;
sr_xval_t xval;
memset(&jhooks, 0, sizeof(srjson_Hooks));
jhooks.malloc_fn = rtjson_malloc;
jhooks.free_fn = rtjson_free;
tdoc = srjson_NewDoc(&jhooks);
if(tdoc==NULL) {
LM_ERR("no more shm\n");
return -1;
}
tdoc->root = srjson_Parse(tdoc, rdoc->s);
if(tdoc->root == NULL) {
LM_ERR("invalid json doc [[%s]]\n", rdoc->s);
srjson_DeleteDoc(tdoc);
return -1;
}
xdata = shm_malloc(sizeof(sr_data_t));
if(xdata==NULL) {
LM_ERR("no more shm\n");
srjson_DeleteDoc(tdoc);
return -1;
}
memset(xdata, 0, sizeof(sr_data_t));
rdata = shm_malloc(sizeof(rtjson_data_t));
if(rdata==NULL) {
LM_ERR("no more shm\n");
srjson_DeleteDoc(tdoc);
shm_free(xdata);
return -1;
}
memset(rdata, 0, sizeof(rtjson_data_t));
rdata->jdoc = tdoc;
xdata->p = rdata;
xdata->pfree = rtjson_data_free;
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_STR;
xval.v.s = *rdoc;
xname.s = "json";
xname.len = 4;
if(xavp_add_value(&xname, &xval, &xavp)==NULL) {
goto error;
}
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_DATA;
xval.v.data = xdata;
xname.s = "data";
xname.len = 4;
if(xavp_add_value(&xname, &xval, &xavp)==NULL) {
goto error;
}
/* reset pointers - they are linked inside xavp now */
tdoc = NULL;
xdata = NULL;
rdata = NULL;
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_XAVP;
xval.v.xavp = xavp;
if(xavp_add_value(&_rtjson_xavp_name, &xval, NULL)==NULL) {
goto error;
}
return 0;
error:
if(xavp) xavp_destroy_list(&xavp);
if(rdata) shm_free(rdata);
if(xdata) shm_free(xdata);
if(tdoc) srjson_DeleteDoc(tdoc);
return -1;
}
#else
/**
*
*/
int rtjson_init_routes(sip_msg_t *msg, str *rdoc)
{
sr_xavp_t *xavp=NULL;
str xname;
sr_xval_t xval;
srjson_doc_t tdoc;
srjson_InitDoc(&tdoc, NULL);
tdoc.root = srjson_Parse(&tdoc, rdoc->s);
if(tdoc.root == NULL) {
LM_ERR("invalid json doc [[%s]]\n", rdoc->s);
srjson_DestroyDoc(&tdoc);
return -1;
}
/* basic validation */
srjson_DestroyDoc(&tdoc);
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_INT;
xval.v.i = 0;
xname.s = "idx";
xname.len = 3;
if(xavp_add_value(&xname, &xval, &xavp)==NULL) {
goto error;
}
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_STR;
xval.v.s = *rdoc;
xname.s = "json";
xname.len = 4;
if(xavp_add_value(&xname, &xval, &xavp)==NULL) {
goto error;
}
memset(&xval, 0, sizeof(sr_xval_t));
xval.type = SR_XTYPE_XAVP;
xval.v.xavp = xavp;
if(xavp_add_value(&_rtjson_xavp_name, &xval, NULL)==NULL) {
goto error;
}
return 0;
error:
if(xavp) xavp_destroy_list(&xavp);
return -1;
}
#endif
/**
*
*/
int rtjson_push_routes(sip_msg_t *msg)
{
sr_xavp_t *javp = NULL;
sr_xavp_t *iavp = NULL;
srjson_doc_t tdoc;
srjson_t *nj = NULL;
str val;
str xname;
int ret;
xname.s = "json";
xname.len = 4;
javp = xavp_get_child_with_sval(&_rtjson_xavp_name, &xname);
if(javp==NULL || javp->val.v.s.len<=0) {
LM_WARN("no json for routing\n");
return -1;
}
xname.s = "idx";
xname.len = 3;
iavp = xavp_get_child_with_ival(&_rtjson_xavp_name, &xname);
if(iavp==NULL) {
LM_WARN("no idx for routing\n");
return -1;
}
srjson_InitDoc(&tdoc, NULL);
tdoc.root = srjson_Parse(&tdoc, javp->val.v.s.s);
if(tdoc.root == NULL) {
LM_ERR("invalid json doc [[%s]]\n", javp->val.v.s.s);
srjson_DestroyDoc(&tdoc);
return -1;
}
nj = srjson_GetObjectItem(&tdoc, tdoc.root, "routing");
if(nj==NULL || nj->valuestring==NULL) {
LM_ERR("missing or invalid routing field\n");
goto error;
}
val.s = nj->valuestring;
val.len = strlen(val.s);
if(val.len==6 && strncmp(val.s, "serial", 6)==0) {
LM_DBG("supported routing [%.*s]\n", val.len, val.s);
ret = rtjson_init_serial(msg, &tdoc, iavp);
} else if(val.len==8 && strncmp(val.s, "parallel", 8)==0) {
LM_DBG("supported routing [%.*s]\n", val.len, val.s);
ret = rtjson_init_parallel(msg, &tdoc, iavp);
} else {
LM_ERR("unsupported routing [%.*s]\n", val.len, val.s);
goto error;
}
srjson_DestroyDoc(&tdoc);
return ret;
error:
srjson_DestroyDoc(&tdoc);
return -1;
}
/**
*
*/
int rtjson_init_serial(sip_msg_t *msg, srjson_doc_t *jdoc, sr_xavp_t *iavp)
{
srjson_t *tj = NULL;
srjson_t *nj = NULL;
srjson_t *rj = NULL;
str val;
unsigned int bflags = 0;
unsigned int old_bflags = 0;
tj = srjson_GetObjectItem(jdoc, jdoc->root, "routes");
if(tj==NULL || tj->type!=srjson_Array || tj->child==NULL) {
LM_ERR("missing or invalid routes field\n");
goto error;
}
nj = tj->child;
clear_branches();
rj = srjson_GetObjectItem(jdoc, nj, "uri");
if(rj!=NULL && rj->type==srjson_String && rj->valuestring!=NULL) {
val.s = rj->valuestring;
val.len = strlen(val.s);
LM_DBG("rewrite r-uri to: [%.*s]\n", val.len, val.s);
if (rewrite_uri(msg, &val) < 0) {
LM_ERR("unable to rewrite Request-URI\n");
goto error;
}
}
reset_dst_uri(msg);
reset_path_vector(msg);
reset_instance(msg);
reset_ruid(msg);
reset_ua(msg);
reset_force_socket(msg);
msg->reg_id = 0;
set_ruri_q(0);
rj = srjson_GetObjectItem(jdoc, nj, "dst_uri");
if(rj!=NULL && rj->type==srjson_String && rj->valuestring!=NULL) {
val.s = rj->valuestring;
val.len = strlen(val.s);
LM_DBG("rewrite dst-uri to: [%.*s]\n", val.len, val.s);
if (set_dst_uri(msg, &val) < 0) {
LM_ERR("unable to set destination uri\n");
goto error;
}
}
rj = srjson_GetObjectItem(jdoc, nj, "path");
if(rj!=NULL && rj->type==srjson_String && rj->valuestring!=NULL) {
val.s = rj->valuestring;
val.len = strlen(val.s);
LM_DBG("rewrite path to: [%.*s]\n", val.len, val.s);
if (set_path_vector(msg, &val) < 0) {
LM_ERR("unable to set path\n");
goto error;
}
}
rj = srjson_GetObjectItem(jdoc, nj, "branch_flags");
if(rj!=NULL && rj->type==srjson_Number && rj->valueint!=0) {
bflags = rj->valueint;
old_bflags = 0;
getbflagsval(0, &old_bflags);
setbflagsval(0, old_bflags|bflags);
}
iavp->val.v.i++;
return 0;
error:
return -1;
}
/**
*
*/
int rtjson_prepare_branch(sip_msg_t *msg, srjson_doc_t *jdoc, srjson_t *nj)
{
srjson_t *rj = NULL;
srjson_t *tj = NULL;
srjson_t *vj = NULL;
str xdsp = {0};
str xuri = {0};
str xhdr = {0};
unsigned int fr = 0;
unsigned int fr_inv = 0;
struct lump *anchor = NULL;
char *s;
if(tmb.set_fr!=NULL) {
rj = srjson_GetObjectItem(jdoc, nj, "fr_timer");
if(rj!=NULL && rj->type==srjson_Number && rj->valueint!=0) {
fr = rj->valueint;
}
rj = srjson_GetObjectItem(jdoc, nj, "fr_inv_timer");
if(rj!=NULL && rj->type==srjson_Number && rj->valueint!=0) {
fr_inv = rj->valueint;
}
if(fr || fr_inv) tmb.set_fr(msg, fr_inv, fr);
}
rj = srjson_GetObjectItem(jdoc, nj, "headers");
if(rj==NULL || rj->type!=srjson_Object || rj->child==NULL) {
LM_DBG("no header operations - done\n");
return 0;
}
tj = srjson_GetObjectItem(jdoc, rj, "extra");
if(tj!=NULL && tj->type==srjson_String && tj->valuestring!=0) {
xhdr.s = tj->valuestring;
xhdr.len = strlen(xhdr.s);
}
if(xhdr.len>4) {
LM_DBG("appending extra headers: [%.*s]\n", xhdr.len, xhdr.s);
anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
if(anchor == 0) {
LM_ERR("can't get anchor\n");
return -1;
}
s = pkg_malloc(xhdr.len+1);
if(s==NULL) {
LM_ERR("no more pkg\n");
return -1;
}
strncpy(s, xhdr.s, xhdr.len);
s[xhdr.len] = '\0';
if (insert_new_lump_before(anchor, s, xhdr.len, 0) == 0) {
LM_ERR("can't insert lump\n");
pkg_free(s);
return -1;
}
}
if(uacb.replace_from!=NULL) {
tj = srjson_GetObjectItem(jdoc, rj, "from");
if(tj!=NULL && tj->type==srjson_Object && rj->child!=NULL) {
vj = srjson_GetObjectItem(jdoc, tj, "display");
if(vj!=NULL && vj->type==srjson_String && vj->valuestring!=0) {
xdsp.s = vj->valuestring;
xdsp.len = strlen(xdsp.s);
}
vj = srjson_GetObjectItem(jdoc, tj, "uri");
if(vj!=NULL && vj->type==srjson_String && vj->valuestring!=0) {
xuri.s = vj->valuestring;
xuri.len = strlen(xuri.s);
}
if(xdsp.len>0 || xuri.len>0) {
uacb.replace_from(msg, &xdsp, &xuri);
}
}
}
if(uacb.replace_to!=NULL) {
tj = srjson_GetObjectItem(jdoc, rj, "to");
if(tj!=NULL && tj->type==srjson_Object && rj->child!=NULL) {
vj = srjson_GetObjectItem(jdoc, tj, "display");
if(vj!=NULL && vj->type==srjson_String && vj->valuestring!=0) {
xdsp.s = vj->valuestring;
xdsp.len = strlen(xdsp.s);
}
vj = srjson_GetObjectItem(jdoc, tj, "uri");
if(vj!=NULL && vj->type==srjson_String && vj->valuestring!=0) {
xuri.s = vj->valuestring;
xuri.len = strlen(xuri.s);
}
if(xdsp.len>0 || xuri.len>0) {
uacb.replace_from(msg, &xdsp, &xuri);
}
}
}
return 0;
}
/**
*
*/
int rtjson_append_branch(sip_msg_t *msg, srjson_doc_t *jdoc, srjson_t *nj)
{
srjson_t *rj = NULL;
str uri = {0};
str duri = {0};
str path = {0};
struct socket_info* fsocket = NULL;
unsigned int bflags = 0;
rj = srjson_GetObjectItem(jdoc, nj, "uri");
if(rj==NULL || rj->type!=srjson_String || rj->valuestring==NULL) {
return -1;
}
uri.s = rj->valuestring;
uri.len = strlen(uri.s);
rj = srjson_GetObjectItem(jdoc, nj, "dst_uri");
if(rj!=NULL && rj->type==srjson_String && rj->valuestring!=NULL) {
duri.s = rj->valuestring;
duri.len = strlen(duri.s);
}
rj = srjson_GetObjectItem(jdoc, nj, "path");
if(rj!=NULL && rj->type==srjson_String && rj->valuestring!=NULL) {
path.s = rj->valuestring;
path.len = strlen(path.s);
}
if (append_branch(msg, &uri, &duri, &path, 0, bflags,
fsocket, 0 /*instance*/, 0,
0, 0) <0) {
LM_ERR("failed to append branch\n");
goto error;
}
return 0;
error:
return -1;
}
/**
*
*/
int rtjson_init_parallel(sip_msg_t *msg, srjson_doc_t *jdoc, sr_xavp_t *iavp)
{
srjson_t *tj = NULL;
srjson_t *nj = NULL;
int ret;
tj = srjson_GetObjectItem(jdoc, jdoc->root, "routes");
if(tj==NULL || tj->type!=srjson_Array || tj->child==NULL) {
LM_ERR("missing or invalid routes field\n");
goto error;
}
nj = tj->child;
ret = rtjson_init_serial(msg, jdoc, iavp);
if(ret<0)
return ret;
/* skip first - used for r-uri */
nj = nj->next;
while(nj) {
rtjson_append_branch(msg, jdoc, nj);
iavp->val.v.i++;
nj = nj->next;
}
return 0;
error:
return -1;
}
/**
*
*/
int rtjson_next_route(sip_msg_t *msg)
{
sr_xavp_t *javp = NULL;
sr_xavp_t *iavp = NULL;
srjson_doc_t tdoc;
srjson_t *tj = NULL;
srjson_t *nj = NULL;
str val;
str xname;
int i;
xname.s = "json";
xname.len = 4;
javp = xavp_get_child_with_sval(&_rtjson_xavp_name, &xname);
if(javp==NULL || javp->val.v.s.len<=0) {
LM_WARN("no json for routing\n");
return -1;
}
xname.s = "idx";
xname.len = 3;
iavp = xavp_get_child_with_ival(&_rtjson_xavp_name, &xname);
if(iavp==NULL) {
LM_WARN("no idx for routing\n");
return -1;
}
srjson_InitDoc(&tdoc, NULL);
tdoc.root = srjson_Parse(&tdoc, javp->val.v.s.s);
if(tdoc.root == NULL) {
LM_ERR("invalid json doc [[%s]]\n", javp->val.v.s.s);
srjson_DestroyDoc(&tdoc);
return -1;
}
nj = srjson_GetObjectItem(&tdoc, tdoc.root, "routing");
if(nj==NULL || nj->valuestring==NULL) {
LM_ERR("missing or invalid routing field\n");
goto error;
}
val.s = nj->valuestring;
val.len = strlen(val.s);
if(val.len!=6 || strncmp(val.s, "serial", 6)!=0) {
LM_DBG("not serial routing [%.*s]\n", val.len, val.s);
goto error;
}
tj = srjson_GetObjectItem(&tdoc, tdoc.root, "routes");
if(tj==NULL || tj->type!=srjson_Array || tj->child==NULL) {
LM_ERR("missing or invalid routes field\n");
goto error;
}
nj = tj->child;
i = 0;
while(nj && i<iavp->val.v.i) {
nj = nj->next;
i++;
}
if(nj==NULL)
goto error;
iavp->val.v.i++;
if(rtjson_append_branch(msg, &tdoc, nj)<0)
goto error;
srjson_DestroyDoc(&tdoc);
return 0;
error:
srjson_DestroyDoc(&tdoc);
return -1;
}
/**
*
*/
int rtjson_update_branch(sip_msg_t *msg)
{
sr_xavp_t *javp = NULL;
sr_xavp_t *iavp = NULL;
srjson_doc_t tdoc;
srjson_t *tj = NULL;
srjson_t *nj = NULL;
str val;
str xname;
int i;
xname.s = "json";
xname.len = 4;
javp = xavp_get_child_with_sval(&_rtjson_xavp_name, &xname);
if(javp==NULL || javp->val.v.s.len<=0) {
LM_WARN("no json for routing\n");
return -1;
}
xname.s = "idx";
xname.len = 3;
iavp = xavp_get_child_with_ival(&_rtjson_xavp_name, &xname);
if(iavp==NULL) {
LM_WARN("no idx for routing\n");
return -1;
}
if(iavp->val.v.i<=0) {
LM_WARN("invalid branch idx for routing\n");
return -1;
}
srjson_InitDoc(&tdoc, NULL);
tdoc.root = srjson_Parse(&tdoc, javp->val.v.s.s);
if(tdoc.root == NULL) {
LM_ERR("invalid json doc [[%s]]\n", javp->val.v.s.s);
srjson_DestroyDoc(&tdoc);
return -1;
}
nj = srjson_GetObjectItem(&tdoc, tdoc.root, "routing");
if(nj==NULL || nj->valuestring==NULL) {
LM_ERR("missing or invalid routing field\n");
goto error;
}
val.s = nj->valuestring;
val.len = strlen(val.s);
if(val.len!=6 || strncmp(val.s, "serial", 6)!=0) {
LM_DBG("not serial routing [%.*s]\n", val.len, val.s);
goto error;
}
tj = srjson_GetObjectItem(&tdoc, tdoc.root, "routes");
if(tj==NULL || tj->type!=srjson_Array || tj->child==NULL) {
LM_ERR("missing or invalid routes field\n");
goto error;
}
nj = tj->child;
i = 0;
/* stop at number of branches - 1 */
while(nj && i<iavp->val.v.i-1) {
nj = nj->next;
i++;
}
if(nj==NULL)
goto error;
if(rtjson_prepare_branch(msg, &tdoc, nj)<0)
goto error;
srjson_DestroyDoc(&tdoc);
return 0;
error:
srjson_DestroyDoc(&tdoc);
return -1;
}