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

469 lines
12 KiB

/*
* Copyright (C) 2005-2006 iptelorg GmbH
*
* 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
*
*/
/*!
* \file
* \brief Kamailio core :: The Select framework
* \ingroup core
* Module: \ref core
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "select.h"
#include "dprint.h"
#include "select_core.h"
#include "mem/mem.h"
#include "mem/shm_mem.h"
/**
* The main parser table list placeholder
* at startup use core table, modules can
* add their own via register_select_table call
*/
static select_table_t *select_list = &select_core_table;
/** the level of the select call that is beeing evaluated
* by the child process
*/
int select_level = 0;
/** pointer to the SIP uri beeing processed.
* Nested function calls can pass information to each
* other using this pointer. Only for performace reasons.
* (Miklos)
*/
struct sip_uri *select_uri_p = NULL;
/** parse a select identifier (internal version)
* Parse select string into select structure s
* moves pointer p to the first unused char.
*
* The select identifier must be of the form:
\verbatim
* [@] <sel_id> [ '.' <sel_id> ...]
*
* Where
* <sel_id> = <id> |
* <id> '[' <idx> ']'
* <id> = [a-zA-Z0-9_]+
* <idx> = <number> | <string>
* <string> = '"' <ascii> '"' |
* '\"' <ascii> '\"'
*
* Examples:
* @to.tag
* @hf_value["contact"]
* @msg.header["SER-Server-ID"]
* @eval.pop[-1]
* contact.uri.params.maddr
* cfg_get.rtp_proxy.enabled
\endverbatim
*
* @return -1 error
* p points to the first unconsumed char
* 0 success
* p points to the first unconsumed char
* s points to the select structure
*/
int w_parse_select(char**p, select_t* sel)
{
str name;
char* select_name;
if (**p=='@') (*p)++;
select_name=*p;
sel->n=0;
while (isalpha((unsigned char)*(*p))) {
if (sel->n > MAX_SELECT_PARAMS -2) {
LM_ERR("select depth exceeds max\n");
goto error;
}
name.s=(*p);
while (isalpha((unsigned char)*(*p)) ||
isdigit((unsigned char)*(*p)) || (*(*p)=='_')) (*p)++;
name.len=(*p)-name.s;
sel->params[sel->n].type=SEL_PARAM_STR;
sel->params[sel->n].v.s=name;
LM_DBG("part %d: %.*s\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
sel->n++;
if (*(*p)=='[') {
(*p)++;
if (*(*p)=='\\') (*p)++;
if (*(*p)=='"') {
(*p)++;
name.s=(*p);
while ((*(*p)!='\0') && (*(*p)!='"')) (*p)++;
if (*(*p)!='"') {
LM_ERR("end of string is missing\n");
goto error;
}
name.len=(*p)-name.s;
if (*((*p)-1)=='\\') name.len--;
(*p)++;
if (*(*p)!=']') {
LM_ERR("invalid string index, no closing ]\n");
goto error;
};
(*p)++;
sel->params[sel->n].type=SEL_PARAM_STR;
sel->params[sel->n].v.s=name;
LM_DBG("part %d: [\"%.*s\"]\n", sel->n, sel->params[sel->n].v.s.len, sel->params[sel->n].v.s.s);
} else {
name.s=(*p);
if (*(*p)=='-') (*p)++;
while (isdigit((unsigned char)*(*p))) (*p)++;
name.len=(*p)-name.s;
if (*(*p)!=']') {
LM_ERR("invalid index, no closing ]\n");
goto error;
};
(*p)++;
sel->params[sel->n].type=SEL_PARAM_INT;
sel->params[sel->n].v.i=atoi(name.s);
LM_DBG("part %d: [%d]\n", sel->n, sel->params[sel->n].v.i);
}
sel->n++;
}
if (*(*p)!='.') break;
(*p)++;
};
if (sel->n==0) {
LM_ERR("invalid select '%.*s'\n", (int)(*p - select_name), select_name);
goto error;
};
LM_DBG("end, total elements: %d, calling resolve_select\n", sel->n);
if (resolve_select(sel)<0) {
LM_ERR("error while resolve_select '%.*s'\n", (int)(*p - select_name), select_name);
goto error;
}
return 0;
error:
return -1;
}
/** parse a select identifier.
* Parse select string into select structure s and
* moves pointer p to the first unused char.
*
\verbatim
* The select identifier must be of the form:
* [@] <sel_id> [ '.' <sel_id> ...]
*
* Where
* <sel_id> = <id> |
* <id> '[' <idx> ']'
* <id> = [a-zA-Z0-9_]+
* <idx> = <number> | '-' <number> | <string>
* <string> = '"' <ascii> '"' |
* '\"' <ascii> '\"'
*
* Examples:
* @to.tag
* @hf_value["contact"]
* @msg.header["SER-Server-ID"]
* @eval.pop[-1]
* contact.uri.params.maddr
* cfg_get.rtp_proxy.enabled
\endverbatim
*
* @param p - double string (asciiz) pointer, *p is moved to the first char
* after the select identifier
* @param s - the result will be stored here
* @return < 0 on error, 0 on success
*/
int parse_select (char** p, select_t** s)
{
select_t* sel;
sel=(select_t*)pkg_malloc(sizeof(select_t));
if (!sel) {
LM_ERR("no free memory\n");
return -1;
}
memset(sel, 0, sizeof(select_t));
if (w_parse_select(p, sel)<0) {
pkg_free(sel);
return -2;
}
*s=sel;
return 0;
}
void free_select(select_t *s)
{
if (s)
pkg_free(s);
}
void shm_free_select(select_t *s)
{
if (s)
shm_free(s);
}
int shm_parse_select (char** p, select_t** s)
{
select_t* sel;
sel=(select_t*)shm_malloc(sizeof(select_t));
if (!sel) {
LM_ERR("no free shared memory\n");
return -1;
}
if (w_parse_select(p, sel)<0) {
shm_free(sel);
return -2;
}
*s=sel;
return 0;
}
int resolve_select(select_t* s)
{
select_f f;
int nested;
int param_idx = 0;
int table_idx = 0;
select_table_t* t = NULL;
int accept = 0;
f = NULL;
nested = 0;
memset (s->f, 0, sizeof(s->f));
while (param_idx<s->n) {
accept = 0;
switch (s->params[param_idx].type) {
case SEL_PARAM_STR:
LM_DBG("'%.*s'\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s);
break;
case SEL_PARAM_INT:
LM_DBG("[%d]\n", s->params[param_idx].v.i);
break;
default:
/* just to avoid the warning */
break;
}
for (t=select_list; t; t=t->next) {
table_idx = 0;
if (!t->table) continue;
while (t->table[table_idx].curr_f || t->table[table_idx].new_f) {
if (t->table[table_idx].curr_f == f) {
if ((t->table[table_idx].flags & (NESTED | CONSUME_NEXT_INT | CONSUME_NEXT_STR)) == NESTED) {
accept = 1;
} else if (t->table[table_idx].type == s->params[param_idx].type) {
switch (t->table[table_idx].type) {
case SEL_PARAM_INT:
accept = 1;
break;
case SEL_PARAM_STR:
accept = (((t->table[table_idx].name.len == s->params[param_idx].v.s.len) || !t->table[table_idx].name.len)
&& (!t->table[table_idx].name.s || !strncasecmp(t->table[table_idx].name.s, s->params[param_idx].v.s.s, s->params[param_idx].v.s.len)));
break;
default:
break;
}
};
}
if (accept) goto accepted;
table_idx++;
}
}
switch (s->params[param_idx].type) {
case SEL_PARAM_STR:
LM_ERR("Unable to resolve select '%.*s' at level %d\n", s->params[param_idx].v.s.len, s->params[param_idx].v.s.s, param_idx);
break;
case SEL_PARAM_INT:
LM_ERR("Unable to resolve select [%d] at level %d\n", s->params[param_idx].v.i, param_idx);
break;
default:
BUG ("Unable to resolve select at level %d\n", param_idx);
break;
break;
}
goto not_found;
accepted:
if (t->table[table_idx].flags & DIVERSION) {
/* if (s->params[param_idx].type == SEL_PARAM_STR) pkg_free(s->params[param_idx].v.s.s); */
/* don't free it (the mem can leak only once at startup)
* the parsed string can live inside larger string block
* e.g. when xlog's select is parsed
*/
s->params[param_idx].type = SEL_PARAM_DIV;
s->params[param_idx].v.i = t->table[table_idx].flags & DIVERSION_MASK;
}
if (t->table[table_idx].flags & CONSUME_NEXT_STR) {
if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_STR)) {
param_idx++;
} else if (!(t->table[table_idx].flags & OPTIONAL)) {
BUG ("Mandatory STR parameter not found\n");
goto not_found;
}
}
if (t->table[table_idx].flags & CONSUME_NEXT_INT) {
if ((param_idx<s->n-1) && (s->params[param_idx+1].type == SEL_PARAM_INT)) {
param_idx++;
} else if (!(t->table[table_idx].flags & OPTIONAL)) {
BUG ("Mandatory INT parameter not found\n");
goto not_found;
}
}
if (t->table[table_idx].flags & NESTED) {
if (nested < MAX_NESTED_CALLS-1) { /* need space for final function */
s->f[nested++] = f;
s->param_offset[nested] = param_idx;
} else {
BUG("MAX_NESTED_CALLS too small to resolve select\n");
goto not_found;
}
} else {
param_idx++;
}
if (t->table[table_idx].flags & FIXUP_CALL) {
select_level = nested;
s->param_offset[nested+1] = param_idx;
if (t->table[table_idx].new_f(NULL, s, NULL)<0) goto not_found;
}
f = t->table[table_idx].new_f;
if (t->table[table_idx].flags & CONSUME_ALL) {
/* sanity checks */
if (t->table[table_idx].flags & NESTED)
WARN("resolve_select: CONSUME_ALL should not be set "
"together with NESTED flag!\n");
if ((t->table[table_idx].flags & FIXUP_CALL) == 0)
WARN("resolve_select: FIXUP_CALL should be defined "
"if CONSUME_ALL flag is set!\n");
break;
}
}
if (t==NULL) {
BUG ("final node not found\n");
goto not_found;
}
if (t->table[table_idx].flags & SEL_PARAM_EXPECTED) {
BUG ("final node has SEL_PARAM_EXPECTED set (no more parameters available)\n");
goto not_found;
}
if (nested >= MAX_NESTED_CALLS) {
BUG("MAX_NESTED_CALLS too small, no space for finally resolved function\n");
goto not_found;
}
if ((nested>0) && (s->f[nested-1] == f)) {
BUG("Topmost nested function equals to final function, won't call it twice\n");
} else {
s->f[nested++] = f;
}
s->param_offset[nested] = s->n;
return 0;
not_found:
return -1;
}
int run_select(str* res, select_t* s, struct sip_msg* msg)
{
int ret, orig_level;
if (res == NULL) {
BUG("Select unprepared result space\n");
return -1;
}
if (s == 0) {
BUG("Select structure is NULL\n");
return -1;
}
if (s->f[0] == 0) {
BUG("Select structure has not been resolved\n");
return -1;
}
LM_DBG("Calling SELECT %p\n", s->f);
/* reset the uri pointer */
select_uri_p = NULL;
/* save and restore the original select_level
* because of the nested selects */
orig_level = select_level;
ret = 0;
for ( select_level=0;
(ret == 0) && (select_level<MAX_NESTED_CALLS) && (s->f[select_level] !=0 );
select_level++
) {
ret = s->f[select_level](res, s, msg);
}
select_level = orig_level;
return ret;
}
void log_select(select_t* s, int level)
{
int i;
LOG(level, "select(");
for(i = 0; i < s->n; i++) {
if (s->params[i].type == SEL_PARAM_INT) {
LOG(level, "%d,", s->params[i].v.i);
} else {
LOG(level, "%.*s,", s->params[i].v.s.len, s->params[i].v.s.s);
}
}
LOG(level, ")\n");
}
void print_select(select_t* s)
{
log_select(s, L_DBG);
}
void err_select(select_t* s)
{
log_select(s, L_ERR);
}
int register_select_table(select_row_t* mod_tab)
{
select_table_t* t;
t=(select_table_t*)pkg_malloc(sizeof(select_table_t));
if (!t) {
LM_ERR("No memory for new select_table structure\n");
return -1;
}
t->table=mod_tab;
t->next=select_list;
select_list=t;
return 0;
}