/** * * Copyright (C) 2015 Victor Seva (sipwise.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 #include #include "../../events.h" #include "../../lib/kcore/cmpapi.h" #include "../../pvar.h" #include "../../rpc.h" #include "../../rpc_lookup.h" #include "cfgt_int.h" #include "cfgt_json.h" static str _cfgt_route_prefix[] = { str_init("start|"), str_init("exit|"), str_init("drop|"), str_init("return|"), {0, 0} }; cfgt_node_p _cfgt_node = NULL; cfgt_hash_p _cfgt_uuid = NULL; str cfgt_hdr_prefix = {"NGCP%", 5}; str cfgt_basedir = {"/tmp", 4}; int cfgt_mask = CFGT_DP_ALL; static int shm_str_hash_alloc(struct str_hash_table *ht, int size) { ht->table = shm_malloc(sizeof(struct str_hash_head) * size); if (!ht->table) return -1; ht->size = size; return 0; } static int _cfgt_init_hashtable(struct str_hash_table *ht) { if (shm_str_hash_alloc(ht, CFGT_HASH_SIZE) != 0) { LM_ERR("Error allocating shared memory hashtable\n"); return -1; } str_hash_init(ht); return 0; } int _cfgt_pv_parse(str *param, pv_elem_p *elem) { if (param->s && param->len > 0) { if (pv_parse_format(param, elem)<0) { LM_ERR("malformed or non AVP %.*s AVP definition\n", param->len, param->s); return -1; } } return 0; } void _cfgt_remove_uuid(const str *uuid) { struct str_hash_head *head; struct str_hash_entry *entry, *back; int i; if(_cfgt_uuid==NULL) return; if(uuid) { lock_get(&_cfgt_uuid->lock); entry = str_hash_get(&_cfgt_uuid->hash, uuid->s, uuid->len); if(entry) { str_hash_del(entry); shm_free(entry->key.s); shm_free(entry); LM_DBG("uuid[%.*s] removed from hash\n", uuid->len, uuid->s); } else LM_DBG("uuid[%.*s] not found in hash\n", uuid->len, uuid->s); lock_release(&_cfgt_uuid->lock); } else { lock_get(&_cfgt_uuid->lock); for(i=0; ihash.table+i; clist_foreach_safe(head, entry, back, next) { LM_DBG("uuid[%.*s] removed from hash\n", entry->key.len, entry->key.s); str_hash_del(entry); shm_free(entry->key.s); shm_free(entry); } lock_release(&_cfgt_uuid->lock); } LM_DBG("remove all uuids. done\n"); } } int _cfgt_get_uuid_id(cfgt_node_p node) { struct str_hash_entry *entry; if(_cfgt_uuid==NULL || node==NULL || node->uuid.len == 0) return -1; lock_get(&_cfgt_uuid->lock); entry = str_hash_get(&_cfgt_uuid->hash, node->uuid.s, node->uuid.len); if(entry) { entry->u.n = entry->u.n + 1; node->msgid = entry->u.n; } else { entry = shm_malloc(sizeof(struct str_hash_entry)); if(entry==NULL) { lock_release(&_cfgt_uuid->lock); LM_ERR("No shared memory left\n"); return -1; } if (shm_str_dup(&entry->key, &node->uuid) != 0) { lock_release(&_cfgt_uuid->lock); shm_free(entry); LM_ERR("No shared memory left\n"); return -1; } entry->u.n = 1; node->msgid = 1; LM_DBG("Add new entry[%.*s]\n", node->uuid.len, node->uuid.s); str_hash_add(&_cfgt_uuid->hash, entry); } lock_release(&_cfgt_uuid->lock); LM_DBG("msgid:[%d]\n", node->msgid); return 1; } int _cfgt_get_hdr_helper(struct sip_msg *msg, str *res, int mode) { struct hdr_field *hf; char *delimiter, *end; str tmp = STR_NULL; if(msg==NULL || (mode==0 && res==NULL)) return -1; /* we need to be sure we have parsed all headers */ if(parse_headers(msg, HDR_EOH_F, 0)<0) { LM_ERR("error parsing headers\n"); return -1; } hf = msg->callid; if(!hf) return 1; if(strncmp(hf->body.s, cfgt_hdr_prefix.s, cfgt_hdr_prefix.len)==0) { tmp.s = hf->body.s+cfgt_hdr_prefix.len; delimiter = tmp.s-1; LM_DBG("Prefix detected. delimiter[%c]\n", *delimiter); if(mode==0) { end = strchr(tmp.s, *delimiter); if(end) { tmp.len = end-tmp.s; if(pkg_str_dup(res, &tmp)<0) { LM_ERR("error copying header\n"); return -1; } LM_DBG("cfgtest uuid:[%.*s]\n", res->len, res->s); return 0; } } else { tmp.len = res->len; LM_DBG("tmp[%.*s] res[%.*s]\n", tmp.len, tmp.s, res->len, res->s); return STR_EQ(tmp, *res); } } return 1; /* not found */ } int _cfgt_get_hdr(struct sip_msg *msg, str *res) { return _cfgt_get_hdr_helper(msg, res, 0); } int _cfgt_cmp_hdr(struct sip_msg *msg, str *res) { return _cfgt_get_hdr_helper(msg, res, 1); } cfgt_node_p cfgt_create_node(struct sip_msg *msg) { cfgt_node_p node; node = (cfgt_node_p) pkg_malloc(sizeof(cfgt_node_t)); if(node==NULL) { LM_ERR("cannot allocate cfgtest msgnode\n"); return node; } memset(node, 0, sizeof(cfgt_node_t)); srjson_InitDoc(&node->jdoc, NULL); if (msg) { node->msgid = msg->id; LM_DBG("msgid:%d\n", node->msgid); if(_cfgt_get_hdr(msg, &node->uuid)!=0 || node->uuid.len==0) { LM_ERR("cannot get value of cfgtest uuid header!!\n"); goto error; } } node->jdoc.root = srjson_CreateObject(&node->jdoc); if(node->jdoc.root==NULL) { LM_ERR("cannot create json root\n"); goto error; } node->flow = srjson_CreateArray(&node->jdoc); if(node->flow==NULL) { LM_ERR("cannot create json object\n"); goto error; } srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "flow\0", node->flow); node->in = srjson_CreateArray(&node->jdoc); if(node->in==NULL) { LM_ERR("cannot create json object\n"); goto error; } srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_in\0", node->in); node->out = srjson_CreateArray(&node->jdoc); if(node->out==NULL) { LM_ERR("cannot create json object\n"); goto error; } srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_out\0", node->out); LM_DBG("node created\n"); return node; error: srjson_DestroyDoc(&node->jdoc); pkg_free(node); return NULL; } void _cfgt_remove_node(cfgt_node_p node) { if(!node) return; srjson_DestroyDoc(&node->jdoc); if(node->uuid.s) pkg_free(node->uuid.s); while(node->flow_head) { node->route = node->flow_head; node->flow_head = node->route->next; pkg_free(node->route); node->route = NULL; } pkg_free(node); } int _cfgt_get_filename(int msgid, str uuid, str *dest, int *dir) { int i, lid; char buff_id[INT2STR_MAX_LEN]; char *sid; if(dest==NULL || uuid.len == 0) return -1; dest->len = cfgt_basedir.len + uuid.len; if(cfgt_basedir.s[cfgt_basedir.len-1]!='/') dest->len = dest->len + 1; sid = sint2strbuf(msgid, buff_id, INT2STR_MAX_LEN, &lid); dest->len += lid + 6; dest->s = (char *) pkg_malloc((dest->len*sizeof(char)+1)); if(dest->s==NULL) { LM_ERR("no more memory.\n"); return -1; } strncpy(dest->s, cfgt_basedir.s, cfgt_basedir.len); i = cfgt_basedir.len; if(cfgt_basedir.s[cfgt_basedir.len-1]!='/') { strncpy(dest->s+i, "/", 1); i = i + 1; } strncpy(dest->s+i, uuid.s, uuid.len); i = i + uuid.len; (*dir) = i; strncpy(dest->s+i, "\0", 1); i = i + 1; strncpy(dest->s+i, sid, lid); i = i + lid; strncpy(dest->s+i, ".json\0", 6); return 0; } int _cfgt_node2json(cfgt_node_p node) { srjson_t *jobj; if(!node) return -1; jobj = srjson_CreateStr(&node->jdoc, node->uuid.s, node->uuid.len); if(jobj==NULL) { LM_ERR("cannot create json object\n"); return -1; } srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "uuid\0", jobj); jobj = srjson_CreateNumber(&node->jdoc, (double)node->msgid); if(jobj==NULL) { LM_ERR("cannot create json object\n"); return -1; } srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "msgid\0", jobj); return 0; } void cfgt_save_node(cfgt_node_p node) { FILE *fp; str dest = STR_NULL; int dir = 0; if(_cfgt_get_filename(node->msgid, node->uuid, &dest, &dir)<0) { LM_ERR("can't build filename\n"); return; } LM_DBG("dir [%s]\n", dest.s); mkdir(dest.s, S_IRWXO|S_IXGRP|S_IRWXU); dest.s[dir] = '/'; fp = fopen(dest.s, "w"); LM_DBG("file [%s]\n", dest.s); if(fp) { pkg_free(dest.s); dest.s = srjson_Print(&node->jdoc, node->jdoc.root); if(dest.s==NULL) { LM_ERR("Cannot get the json string\n"); fclose(fp); return; } if(fputs(dest.s, fp)<0){ LM_ERR("failed writing to file\n"); } fclose(fp); node->jdoc.free_fn(dest.s); } else { LM_ERR("Can't open file [%s] to write\n", dest.s); pkg_free(dest.s); } } void _cfgt_print_node(cfgt_node_p node, int json) { char *buf = NULL; cfgt_str_list_p route; if(!node) return; if(node->flow_head) { route = node->flow_head; while(route) { if(route == node->route) LM_DBG("[--[%.*s][%d]--]\n", route->s.len, route->s.s, route->type); else LM_DBG("[%.*s][%d]\n", route->s.len, route->s.s, route->type); route = route->next; } } else LM_DBG("flow:empty\n"); if(json) { buf = srjson_PrintUnformatted(&node->jdoc, node->jdoc.root); if(buf==NULL) { LM_ERR("Cannot get the json string\n"); return; } LM_DBG("node[%p]: id:[%d] uuid:[%.*s] info:[%s]\n", node, node->msgid, node->uuid.len, node->uuid.s, buf); node->jdoc.free_fn(buf); } } int _cfgt_set_dump(struct sip_msg *msg, cfgt_node_p node, str *flow) { srjson_t *f, *vars; if(node==NULL || flow == NULL) return -1; vars = srjson_CreateObject(&node->jdoc); if(vars==NULL) { LM_ERR("cannot create json object\n"); return -1; } if(cfgt_get_json(msg, 30, &node->jdoc, vars)<0) { LM_ERR("cannot get var info\n"); return -1; } f = srjson_CreateObject(&node->jdoc); if(f==NULL) { LM_ERR("cannot create json object\n"); srjson_Delete(&node->jdoc, vars); return -1; } srjson_AddStrItemToObject(&node->jdoc, f, flow->s, flow->len, vars); srjson_AddItemToArray(&node->jdoc, node->flow, f); LM_DBG("node[%.*s] flow created\n", flow->len, flow->s); return 0; } void _cfgt_set_type(cfgt_str_list_p route, struct action *a) { switch(a->type) { case DROP_T: if(a->val[1].u.number&DROP_R_F) { route->type = CFGT_DROP_D; LM_DBG("set[%.*s]->CFGT_DROP_D\n", route->s.len, route->s.s); } if(a->val[1].u.number&RETURN_R_F){ route->type = CFGT_DROP_R; LM_DBG("set[%.*s]->CFGT_DROP_R\n", route->s.len, route->s.s); } else { route->type = CFGT_DROP_E; LM_DBG("set[%.*s]->CFGT_DROP_E\n", route->s.len, route->s.s); } break; case ROUTE_T: route->type = CFGT_ROUTE; LM_DBG("set[%.*s]->CFGT_ROUTE\n", route->s.len, route->s.s); break; default: if(route->type!=CFGT_DROP_E) { route->type = CFGT_DROP_R; LM_DBG("[%.*s] no relevant action: CFGT_DROP_R[%d]\n", route->s.len, route->s.s, a->type); } else { LM_DBG("[%.*s] already set to CFGT_DROP_E[%d]\n", route->s.len, route->s.s, a->type); } break; } } int _cfgt_add_routename(cfgt_node_p node, struct action *a, str *routename) { cfgt_str_list_p route; int ret = 0; if(!node->route) /* initial */ { node->route = pkg_malloc(sizeof(cfgt_str_list_t)); if(!node->route) { LM_ERR("No more pkg mem\n"); return -1; } memset(node->route, 0, sizeof(cfgt_str_list_t)); node->flow_head = node->route; node->route->type = CFGT_ROUTE; ret = 1; } else { LM_DBG("actual routename:[%.*s][%d]\n", node->route->s.len, node->route->s.s, node->route->type); if(node->route->prev) LM_DBG("prev routename:[%.*s][%d]\n", node->route->prev->s.len, node->route->prev->s.s, node->route->prev->type); if(node->route->next) LM_DBG("next routename:[%.*s][%d]\n", node->route->next->s.len, node->route->next->s.s, node->route->next->type); if(STR_EQ(*routename, node->route->s)) { LM_DBG("same route\n"); _cfgt_set_type(node->route, a); return 2; } else if(node->route->prev && STR_EQ(*routename, node->route->prev->s)) { LM_DBG("back to route[%.*s]\n", node->route->prev->s.len, node->route->prev->s.s); _cfgt_set_type(node->route->prev, a); return 3; } route = pkg_malloc(sizeof(cfgt_str_list_t)); if(!route) { LM_ERR("No more pkg mem\n"); return -1; } memset(route, 0, sizeof(cfgt_str_list_t)); route->prev = node->route; node->route->next = route; node->route = route; _cfgt_set_type(node->route, a); } node->route->s.s = routename->s; node->route->s.len = routename->len; LM_DBG("add[%d] route:[%.*s]\n", ret, node->route->s.len, node->route->s.s); _cfgt_print_node(node, 0); return ret; } void _cfgt_del_routename(cfgt_node_p node) { if(node->route->next!=NULL) { LM_ERR("wtf!! route->next[%p] not null!!\n", node->route->next); _cfgt_print_node(node, 0); } LM_DBG("del route[%.*s]\n", node->route->s.len, node->route->s.s); node->route = node->route->prev; pkg_free(node->route->next); node->route->next = NULL; } /* dest has to be freed */ int _cfgt_node_get_flowname(cfgt_str_list_p route, int *indx, str *dest) { int i; if(route==NULL) return -1; LM_DBG("routename:[%.*s][%d]\n", route->s.len, route->s.s, route->type); if(indx) i = *indx; else i = route->type-1; if(str_append(&_cfgt_route_prefix[i], &route->s, dest)<0) { LM_ERR("Cannot create route name\n"); return -1; } return 0; } int cfgt_process_route(struct sip_msg *msg, struct action *a) { str routename; int ret = -1; int indx = 0; str flowname = STR_NULL; if(!_cfgt_node) { LM_ERR("node empty\n"); return -1; } if (a->rname==NULL) { LM_DBG("no routename. type:%d\n", a->type); return 0; } LM_DBG("route from action:[%s]\n", a->rname); routename.s = a->rname; routename.len = strlen(a->rname); switch(_cfgt_add_routename(_cfgt_node, a, &routename)) { case 2: /* same name */ return 0; case 1: /* initial */ LM_DBG("Initial route[%.*s]. dump vars\n", _cfgt_node->route->s.len, _cfgt_node->route->s.s); if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0) { LM_ERR("cannot create flowname\n"); return -1; } ret = _cfgt_set_dump(msg, _cfgt_node, &flowname); break; case 0: /* new */ LM_DBG("Change from[%.*s] route to route[%.*s]. dump vars\n", _cfgt_node->route->prev->s.len, _cfgt_node->route->prev->s.s, _cfgt_node->route->s.len, _cfgt_node->route->s.s); if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0) { LM_ERR("cannot create flowname\n"); return -1; } ret = _cfgt_set_dump(msg, _cfgt_node, &flowname); break; case 3: /* back to previous */ if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0) { LM_ERR("cannot create flowname\n"); return -1; } ret = _cfgt_set_dump(msg, _cfgt_node, &flowname); _cfgt_del_routename(_cfgt_node); break; default: return -1; } if(flowname.s) pkg_free(flowname.s); return ret; } /* TODO: - parse first line, check if is SIP - parse for header cfgtest */ int cfgt_msgin(void *data) { srjson_t *jobj; str *buf = (str *) data; if(buf==NULL) return 0; if(_cfgt_node) { cfgt_save_node(_cfgt_node); _cfgt_remove_node(_cfgt_node); LM_DBG("node removed\n"); _cfgt_node = NULL; } LM_DBG("msg in:{%.*s}\n", buf->len, buf->s); _cfgt_node = cfgt_create_node(NULL); if(_cfgt_node) { jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len); if(jobj==NULL) { LM_ERR("cannot create json object\n"); return -1; } srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->in, jobj); return 0; } LM_ERR("_cfgt_node empty\n"); return -1; } int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar) { str unknown = {"unknown", 7}; if(_cfgt_node) { if (_cfgt_node->msgid == 0) { LM_DBG("new node\n"); if(_cfgt_get_hdr(msg, &_cfgt_node->uuid)!=0 || _cfgt_node->uuid.len==0) { LM_ERR("cannot get value of cfgtest uuid header." " Using unknown\n"); pkg_str_dup(&_cfgt_node->uuid, &unknown); } return _cfgt_get_uuid_id(_cfgt_node); } else { LM_DBG("_cfgt_node->uuid:[%.*s]\n", _cfgt_node->uuid.len, _cfgt_node->uuid.s); if(_cfgt_cmp_hdr(msg, &_cfgt_node->uuid)) { LM_DBG("same uuid\n"); return 1; } else { LM_DBG("different uuid\n"); } } } else { LM_ERR("node empty??\n"); } _cfgt_node = cfgt_create_node(msg); if(_cfgt_node) { LM_DBG("node created\n"); return 1; } return -1; } int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar) { str flowname = STR_NULL; if(_cfgt_node) { LM_DBG("dump last flow\n"); if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0) LM_ERR("cannot create flowname\n"); else _cfgt_set_dump(msg, _cfgt_node, &flowname); if(flowname.s) pkg_free(flowname.s); cfgt_save_node(_cfgt_node); } return 1; } int cfgt_msgout(void *data) { srjson_t *jobj; str *buf = (str *) data; if(buf==NULL) return 0; LM_DBG("msg out:{%.*s}\n", buf->len, buf->s); if(_cfgt_node) { jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len); if(jobj==NULL) { LM_ERR("cannot create json object\n"); return -1; } srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->out, jobj); return 0; } LM_ERR("node empty\n"); return -1; } /** * */ static const char* cfgt_rpc_mask_doc[2] = { "Specify module mask", 0 }; static void cfgt_rpc_mask(rpc_t* rpc, void* ctx){ int mask = CFGT_DP_ALL; if (rpc->scan(ctx, "*d", &mask) != 1) { rpc->fault(ctx, 500, "invalid parameters"); return; } cfgt_mask = mask; rpc->add(ctx, "s", "200 ok"); } rpc_export_t cfgt_rpc[] = { {"dbg.mask", cfgt_rpc_mask, cfgt_rpc_mask_doc, 0}, {0, 0, 0, 0} }; int cfgt_init(void) { if (rpc_register_array(cfgt_rpc)!=0) { LM_ERR("failed to register RPC commands\n"); return -1; } _cfgt_uuid = shm_malloc(sizeof(cfgt_hash_t)); if(_cfgt_uuid==NULL) { LM_ERR("Cannot allocate shared memory\n"); return -1; } if(!lock_init(&_cfgt_uuid->lock)) { LM_ERR("cannot init the lock\n"); shm_free(_cfgt_uuid); _cfgt_uuid = NULL; return -1; } if(_cfgt_init_hashtable(&_cfgt_uuid->hash)<0) return -1; sr_event_register_cb(SREV_NET_DATA_IN, cfgt_msgin); sr_event_register_cb(SREV_NET_DATA_OUT, cfgt_msgout); return 0; }