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/ipops/ipops_pv.c

1136 lines
23 KiB

/**
* $Id$
*
* Copyright (C) 2013 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 <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "../../dprint.h"
#include "../../rand/fastrand.h"
#include "../../hashes.h"
#include "../../resolve.h"
#include "../../pvar.h"
#define PV_DNS_ADDR 64
#define PV_DNS_RECS 32
typedef struct _sr_dns_record {
int type;
char addr[PV_DNS_ADDR];
} sr_dns_record_t;
typedef struct _sr_dns_item {
str name;
unsigned int hashid;
char hostname[256];
int count;
int ipv4;
int ipv6;
sr_dns_record_t r[PV_DNS_RECS];
struct _sr_dns_item *next;
} sr_dns_item_t;
#define SR_DNS_PVIDX 1
typedef struct _dns_pv {
sr_dns_item_t *item;
int type;
int flags;
pv_spec_t *pidx;
int nidx;
} dns_pv_t;
static sr_dns_item_t *_sr_dns_list = NULL;
/**
*
*/
sr_dns_item_t *sr_dns_get_item(str *name)
{
sr_dns_item_t *it = NULL;
unsigned int hashid = 0;
hashid = get_hash1_raw(name->s, name->len);
it = _sr_dns_list;
while(it!=NULL)
{
if(it->hashid==hashid && it->name.len == name->len
&& strncmp(it->name.s, name->s, name->len)==0)
return it;
it = it->next;
}
return NULL;
}
/**
*
*/
sr_dns_item_t *sr_dns_add_item(str *name)
{
sr_dns_item_t *it = NULL;
unsigned int hashid = 0;
hashid = get_hash1_raw(name->s, name->len);
it = _sr_dns_list;
while(it!=NULL)
{
if(it->hashid==hashid && it->name.len == name->len
&& strncmp(it->name.s, name->s, name->len)==0)
return it;
it = it->next;
}
/* add new */
it = (sr_dns_item_t*)pkg_malloc(sizeof(sr_dns_item_t));
if(it==NULL)
{
LM_ERR("no more pkg\n");
return NULL;
}
memset(it, 0, sizeof(sr_dns_item_t));
it->name.s = (char*)pkg_malloc(name->len+1);
if(it->name.s==NULL)
{
LM_ERR("no more pkg.\n");
pkg_free(it);
return NULL;
}
memcpy(it->name.s, name->s, name->len);
it->name.s[name->len] = '\0';
it->name.len = name->len;
it->hashid = hashid;
it->next = _sr_dns_list;
_sr_dns_list = it;
return it;
}
/**
*
*/
int pv_parse_dns_name(pv_spec_t *sp, str *in)
{
dns_pv_t *dpv=NULL;
char *p;
str pvc;
str pvs;
str pvi;
int sign;
if(sp==NULL || in==NULL || in->len<=0)
return -1;
dpv = (dns_pv_t*)pkg_malloc(sizeof(dns_pv_t));
if(dpv==NULL)
return -1;
memset(dpv, 0, sizeof(dns_pv_t));
p = in->s;
while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
p++;
if(p>in->s+in->len || *p=='\0')
goto error;
pvc.s = p;
while(p < in->s + in->len)
{
if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r')
break;
p++;
}
if(p>in->s+in->len || *p=='\0')
goto error;
pvc.len = p - pvc.s;
if(*p!='=')
{
while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
p++;
if(p>in->s+in->len || *p=='\0' || *p!='=')
goto error;
}
p++;
if(*p!='>')
goto error;
p++;
pvs.len = in->len - (int)(p - in->s);
pvs.s = p;
pvi.s = 0;
pvi.len = 0;
if(pvs.s[pvs.len-1]==']') {
/* index */
p = memchr(pvs.s, '[', pvs.len-1);
if(p==NULL) {
goto error;
}
pvi.s = p + 1;
pvi.len = pvs.s + pvs.len - pvi.s;
pvs.len = p - pvs.s;
}
LM_DBG("dns [%.*s] - key [%.*s] index [%.*s]\n", pvc.len, pvc.s,
pvs.len, pvs.s, (pvi.len>0)?pvi.len:0, (pvi.s!=NULL)?pvi.s:0);
dpv->item = sr_dns_add_item(&pvc);
if(dpv->item==NULL)
goto error;
switch(pvs.len)
{
case 4:
if(strncmp(pvs.s, "addr", 4)==0)
dpv->type = 0;
else if(strncmp(pvs.s, "type", 4)==0)
dpv->type = 1;
else if(strncmp(pvs.s, "ipv4", 4)==0)
dpv->type = 2;
else if(strncmp(pvs.s, "ipv6", 4)==0)
dpv->type = 3;
else goto error;
break;
case 5:
if(strncmp(pvs.s, "count", 5)==0)
dpv->type = 4;
else goto error;
break;
default:
goto error;
}
if(pvi.len>0)
{
if(pvi.s[0]==PV_MARKER)
{
dpv->pidx = pv_cache_get(&pvi);
if(dpv->pidx==NULL)
goto error;
dpv->flags |= SR_DNS_PVIDX;
} else {
sign = 1;
p = pvi.s;
if(*p=='-')
{
sign = -1;
p++;
}
dpv->nidx = 0;
while(p<pvi.s+pvi.len && *p>='0' && *p<='9')
{
dpv->nidx = dpv->nidx * 10 + *p - '0';
p++;
}
if(p!=pvi.s+pvi.len)
{
LM_ERR("invalid index [%.*s]\n", in->len, in->s);
return -1;
}
dpv->nidx *= sign;
}
}
sp->pvp.pvn.u.dname = (void*)dpv;
sp->pvp.pvn.type = PV_NAME_OTHER;
return 0;
error:
LM_ERR("error at PV dns name: %.*s\n", in->len, in->s);
if(dpv) pkg_free(dpv);
return -1;
}
/**
*
*/
int pv_get_dns(sip_msg_t *msg, pv_param_t *param,
pv_value_t *res)
{
dns_pv_t *dpv;
pv_value_t val;
if(msg==NULL || param==NULL)
return -1;
dpv = (dns_pv_t*)param->pvn.u.dname;
if(dpv==NULL || dpv->item==NULL)
return -1;
if(dpv->pidx!=NULL)
{
if(pv_get_spec_value(msg, dpv->pidx, &val)<0
|| (!(val.flags&PV_VAL_INT)))
{
LM_ERR("failed to evaluate index variable\n");
return pv_get_null(msg, param, res);
}
} else {
val.ri = dpv->nidx;
}
if(val.ri<0)
{
if(dpv->item->count+val.ri<0) {
return pv_get_null(msg, param, res);
}
val.ri = dpv->item->count+val.ri;
}
if(val.ri>=dpv->item->count) {
return pv_get_null(msg, param, res);
}
switch(dpv->type)
{
case 0: /* address */
return pv_get_strzval(msg, param, res,
dpv->item->r[val.ri].addr);
case 1: /* type */
return pv_get_sintval(msg, param, res,
dpv->item->r[val.ri].type);
case 2: /* ipv4 */
return pv_get_sintval(msg, param, res,
dpv->item->ipv4);
case 3: /* ipv6 */
return pv_get_sintval(msg, param, res,
dpv->item->ipv6);
case 4: /* count */
return pv_get_sintval(msg, param, res,
dpv->item->count);
default: /* else */
return pv_get_null(msg, param, res);
}
}
/**
*
*/
int dns_init_pv(char *path)
{
return 0;
}
/**
*
*/
void dns_destroy_list(void)
{
return;
}
/**
*
*/
void dns_destroy_pv(void)
{
return;
}
/**
*
*/
int dns_update_pv(str *hostname, str *name)
{
sr_dns_item_t *dr = NULL;
struct addrinfo hints, *res, *p;
struct sockaddr_in *ipv4;
struct sockaddr_in6 *ipv6;
void *addr;
int status;
int i;
if(hostname->len>255)
{
LM_DBG("target hostname too long (max 255): %s\n", hostname->s);
return -2;
}
dr = sr_dns_get_item(name);
if(dr==NULL)
{
LM_DBG("container not found: %s\n", name->s);
return -3;
}
/* reset the counter */
dr->count = 0;
dr->ipv4 = 0;
dr->ipv6 = 0;
strncpy(dr->hostname, hostname->s, hostname->len);
dr->hostname[hostname->len] = '\0';
LM_DBG("attempting to resolve: %s\n", dr->hostname);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* allow any of AF_INET or AF_INET6 */
// hints.ai_socktype = SOCK_STREAM;
hints.ai_socktype = SOCK_DGRAM;
if ((status = getaddrinfo(dr->hostname, NULL, &hints, &res)) != 0)
{
LM_ERR("unable to resolve %s - getaddrinfo: %s\n",
dr->hostname, gai_strerror(status));
return -4;
}
i=0;
for(p=res; p!=NULL; p=p->ai_next)
{
if (p->ai_family==AF_INET)
{
dr->ipv4 = 1;
dr->r[i].type = 4;
ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
} else {
dr->ipv6 = 1;
dr->r[i].type = 6;
ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
}
inet_ntop(p->ai_family, addr, dr->r[i].addr,
PV_DNS_ADDR);
LM_DBG("#%d - type %d addr: %s (%d)\n", i, dr->r[i].type,
dr->r[i].addr, p->ai_socktype);
i++;
if(i==PV_DNS_RECS) {
LM_WARN("more than %d addresses for %s - truncating\n",
PV_DNS_RECS, dr->hostname);
break;
}
}
freeaddrinfo(res);
dr->count = i;
LM_DBG("dns PV updated for: %s (%d)\n", dr->hostname, i);
return 1;
}
struct _hn_pv_data {
str data;
str fullname;
str hostname;
str domain;
str ipaddr;
};
static struct _hn_pv_data *_hn_data = NULL;
/**
*
*/
int hn_pv_data_init(void)
{
char hbuf[512];
int hlen;
char *d;
struct hostent *he;
int i;
if(_hn_data != NULL)
return 0;
if (gethostname(hbuf, 512)<0) {
LM_WARN("gethostname failed - host pvs will be null\n");
return -1;
}
hlen = strlen(hbuf);
if(hlen<=0) {
LM_WARN("empty hostname result - host pvs will be null\n");
return -1;
}
_hn_data = (struct _hn_pv_data*)pkg_malloc(sizeof(struct _hn_pv_data)+46+2*(hlen+1));
if(_hn_data==NULL) {
LM_ERR("no more pkg to init hostname data\n");
return -1;
}
memset(_hn_data, 0, sizeof(struct _hn_pv_data)+46+2*(hlen+1));
_hn_data->data.len = hlen;
_hn_data->data.s = (char*)_hn_data + sizeof(struct _hn_pv_data);
_hn_data->fullname.len = hlen;
_hn_data->fullname.s = _hn_data->data.s + hlen + 1;
strcpy(_hn_data->data.s, hbuf);
strcpy(_hn_data->fullname.s, hbuf);
d=strchr(_hn_data->data.s, '.');
if (d) {
_hn_data->hostname.len = d - _hn_data->data.s;
_hn_data->hostname.s = _hn_data->data.s;
_hn_data->domain.len = _hn_data->fullname.len
- _hn_data->hostname.len-1;
_hn_data->domain.s = d+1;
} else {
_hn_data->hostname = _hn_data->fullname;
}
he=resolvehost(_hn_data->fullname.s);
if (he) {
if ((strlen(he->h_name)!=_hn_data->fullname.len)
|| strncmp(he->h_name, _hn_data->fullname.s,
_hn_data->fullname.len)) {
LM_WARN("hostname '%.*s' different than gethostbyname '%s'\n",
_hn_data->fullname.len, _hn_data->fullname.s, he->h_name);
}
if (he->h_addr_list) {
for (i=0; he->h_addr_list[i]; i++) {
if (inet_ntop(he->h_addrtype, he->h_addr_list[i], hbuf, 46)) {
if (_hn_data->ipaddr.len==0) {
_hn_data->ipaddr.len = strlen(hbuf);
_hn_data->ipaddr.s = _hn_data->fullname.s + hlen + 1;
strcpy(_hn_data->ipaddr.s, hbuf);
} else if (strncmp(_hn_data->ipaddr.s, hbuf,
_hn_data->ipaddr.len)!=0) {
LM_WARN("many IPs to hostname: %s not used\n", hbuf);
}
}
}
} else {
LM_WARN(" can't resolve hostname's address: %s\n",
_hn_data->fullname.s);
}
}
DBG("Hostname: %.*s\n", _hn_data->hostname.len, ZSW(_hn_data->hostname.s));
DBG("Domain: %.*s\n", _hn_data->domain.len, ZSW(_hn_data->domain.s));
DBG("Fullname: %.*s\n", _hn_data->fullname.len, ZSW(_hn_data->fullname.s));
DBG("IPaddr: %.*s\n", _hn_data->ipaddr.len, ZSW(_hn_data->ipaddr.s));
return 0;
}
/**
*
*/
int pv_parse_hn_name(pv_spec_p sp, str *in)
{
if(sp==NULL || in==NULL || in->len<=0)
return -1;
switch(in->len)
{
case 1:
if(strncmp(in->s, "n", 1)==0)
sp->pvp.pvn.u.isname.name.n = 0;
else if(strncmp(in->s, "f", 1)==0)
sp->pvp.pvn.u.isname.name.n = 1;
else if(strncmp(in->s, "d", 1)==0)
sp->pvp.pvn.u.isname.name.n = 2;
else if(strncmp(in->s, "i", 1)==0)
sp->pvp.pvn.u.isname.name.n = 3;
else goto error;
break;
default:
goto error;
}
sp->pvp.pvn.type = PV_NAME_INTSTR;
sp->pvp.pvn.u.isname.type = 0;
hn_pv_data_init();
return 0;
error:
LM_ERR("unknown host PV name %.*s\n", in->len, in->s);
return -1;
}
/**
*
*/
int pv_get_hn(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res)
{
if(param==NULL)
return -1;
if(_hn_data==NULL)
return pv_get_null(msg, param, res);;
switch(param->pvn.u.isname.name.n)
{
case 1:
if(_hn_data->fullname.len==0)
return pv_get_null(msg, param, res);;
return pv_get_strval(msg, param, res, &_hn_data->fullname);
case 2:
if(_hn_data->domain.len==0)
return pv_get_null(msg, param, res);;
return pv_get_strval(msg, param, res, &_hn_data->domain);
case 3:
if(_hn_data->ipaddr.len==0)
return pv_get_null(msg, param, res);;
return pv_get_strval(msg, param, res, &_hn_data->ipaddr);
default:
if(_hn_data->hostname.len==0)
return pv_get_null(msg, param, res);;
return pv_get_strval(msg, param, res, &_hn_data->hostname);
}
}
/**********
* srvquery PV
**********/
static char *srvqrylst []
= {"count", "port", "priority", "target", "weight", NULL};
#define PV_SRV_MAXSTR 64
#define PV_SRV_MAXRECS 32
typedef struct _sr_srv_record {
unsigned short priority;
unsigned short weight;
unsigned short port;
char target [PV_SRV_MAXSTR + 1];
} sr_srv_record_t;
typedef struct _sr_srv_item {
str pvid;
unsigned int hashid;
int count;
sr_srv_record_t rr [PV_SRV_MAXRECS];
struct _sr_srv_item *next;
} sr_srv_item_t;
typedef struct _srv_pv {
sr_srv_item_t *item;
int type;
int flags;
pv_spec_t *pidx;
int nidx;
} srv_pv_t;
static sr_srv_item_t *_sr_srv_list = NULL;
/**********
* Add srvquery Item
*
* INPUT:
* Arg (1) = pvid string pointer
* Arg (2) = find flag; <>0=search only
* OUTPUT: srv record pointer; NULL=not found
**********/
sr_srv_item_t *sr_srv_add_item (str *pvid, int findflg)
{
sr_srv_item_t *pitem;
unsigned int hashid;
/**********
* o get hash
* o already exists?
**********/
hashid = get_hash1_raw (pvid->s, pvid->len);
for (pitem = _sr_srv_list; pitem; pitem = pitem->next) {
if (pitem->hashid == hashid
&& pitem->pvid.len == pvid->len
&& !strncmp (pitem->pvid.s, pvid->s, pvid->len))
return pitem;
}
if (findflg)
return NULL;
/**********
* o alloc/init item structure
* o link in new item
**********/
pitem = (sr_srv_item_t *) pkg_malloc (sizeof (sr_srv_item_t));
if (!pitem) {
LM_ERR ("No more pkg memory!\n");
return NULL;
}
memset (pitem, 0, sizeof (sr_srv_item_t));
pitem->pvid.s = (char *) pkg_malloc (pvid->len + 1);
if (!pitem->pvid.s) {
LM_ERR ("No more pkg memory!\n");
pkg_free (pitem);
return NULL;
}
memcpy (pitem->pvid.s, pvid->s, pvid->len);
pitem->pvid.len = pvid->len;
pitem->hashid = hashid;
pitem->next = _sr_srv_list;
_sr_srv_list = pitem;
return pitem;
}
/**********
* Skip Over
*
* INPUT:
* Arg (1) = string pointer
* Arg (2) = starting position
* Arg (3) = whitespace flag
* OUTPUT: position past skipped
**********/
int skip_over (str *pstr, int pos, int bWS)
{
char *pchar;
/**********
* o string exists?
* o skip over
**********/
if (pos >= pstr->len)
return pstr->len;
for (pchar = &pstr->s [pos]; pos < pstr->len; pchar++, pos++) {
if (*pchar == ' ' || *pchar == '\t' || *pchar == '\n' || *pchar == '\r') {
if (bWS)
continue;
}
if ((*pchar>='A' && *pchar<='Z') || (*pchar>='a' && *pchar<='z')
|| (*pchar>='0' && *pchar<='9')) {
if (!bWS)
continue;
}
break;
}
return pos;
}
/**********
* Sort SRV Records by Weight (RFC 2782)
*
* INPUT:
* Arg (1) = pointer to array of SRV records
* Arg (2) = first record in range
* Arg (3) = last record in range
* OUTPUT: position past skipped
**********/
void sort_weights (struct srv_rdata **plist, int pos1, int pos2)
{
int idx1, idx2, lastfound;
struct srv_rdata *wlist [PV_SRV_MAXRECS];
unsigned int rand, sum, sums [PV_SRV_MAXRECS];
/**********
* place zero weights in the unordered list and then non-zero
**********/
idx2 = 0;
for (idx1 = pos1; idx1 <= pos2; idx1++) {
if (!plist [idx1]->weight) {
wlist [idx2++] = plist [idx1];
}
}
for (idx1 = pos1; idx1 <= pos2; idx1++) {
if (plist [idx1]->weight) {
wlist [idx2++] = plist [idx1];
}
}
/**********
* generate running sum list
**********/
sum = 0;
for (idx1 = 0; idx1 < idx2; idx1++) {
sum += wlist [idx1]->weight;
sums [idx1] = sum;
}
/**********
* resort randomly
**********/
lastfound = 0;
for (idx1 = pos1; idx1 <= pos2; idx1++) {
/**********
* o calculate a random number in range
* o find first unsorted
**********/
rand = fastrand_max (sum);
for (idx2 = 0; idx2 <= pos2 - pos1; idx2++) {
if (!wlist [idx2]) {
continue;
}
if (sums [idx2] >= rand) {
plist [idx1] = wlist [idx2];
wlist [idx2] = 0;
break;
}
lastfound = idx2;
}
if (idx2 > pos2 - pos1) {
plist [idx1] = wlist [lastfound];
wlist [lastfound] = 0;
}
}
return;
}
/**********
* Sort SRV Records by Priority/Weight
*
* INPUT:
* Arg (1) = pointer to array of SRV records
* Arg (2) = record count
* OUTPUT: position past skipped
**********/
void sort_srv (struct srv_rdata **plist, int rcount)
{
int idx1, idx2;
struct srv_rdata *pswap;
/**********
* sort by priority
**********/
for (idx1 = 1; idx1 < rcount; idx1++) {
pswap = plist [idx1];
for (idx2 = idx1;
idx2 && (plist [idx2 - 1]->priority > pswap->priority); --idx2) {
plist [idx2] = plist [idx2 - 1];
}
plist [idx2] = pswap;
}
/**********
* check for multiple priority
**********/
idx2 = 0;
pswap = plist [0];
for (idx1 = 1; idx1 <= rcount; idx1++) {
if ((idx1 == rcount) || (pswap->priority != plist [idx1]->priority)) {
/**********
* o range has more than one element?
* o restart range
**********/
if (idx1 - idx2 - 1) {
sort_weights (plist, idx2, idx1 - 1);
}
idx2 = idx1;
pswap = plist [idx2];
}
}
return;
}
/**********
* Parse srvquery Name
*
* INPUT:
* Arg (1) = pv spec pointer
* Arg (2) = input string pointer
* OUTPUT: 0=success
**********/
int pv_parse_srv_name (pv_spec_t *sp, str *in)
{
char *pstr;
int i, pos, sign;
srv_pv_t *dpv;
str pvi = {0}, pvk = {0}, pvn = {0};
/**********
* o alloc/init pvid structure
* o extract pvid name
* o check separator
**********/
if (!sp || !in || in->len<=0)
return -1;
dpv = (srv_pv_t *) pkg_malloc (sizeof (srv_pv_t));
if (!dpv) {
LM_ERR ("No more pkg memory!\n");
return -1;
}
memset (dpv, 0, sizeof (srv_pv_t));
pos = skip_over (in, 0, 1);
if (pos == in->len)
goto error;
pvn.s = &in->s [pos];
pvn.len = pos;
pos = skip_over (in, pos, 0);
pvn.len = pos - pvn.len;
if (!pvn.len)
goto error;
pos = skip_over (in, pos, 1);
if ((pos + 2) > in->len)
goto error;
if (strncmp (&in->s [pos], "=>", 2))
goto error;
/**********
* o extract key name
* o check key name
* o count?
**********/
pos = skip_over (in, pos + 2, 1);
pvk.s = &in->s [pos];
pvk.len = pos;
pos = skip_over (in, pos, 0);
pvk.len = pos - pvk.len;
if (!pvk.len)
goto error;
for (i = 0; srvqrylst [i]; i++) {
if (strlen (srvqrylst [i]) != pvk.len)
continue;
if (!strncmp (pvk.s, srvqrylst [i], pvk.len)) {
dpv->type = i;
break;
}
}
if (!srvqrylst [i])
goto error;
if (!i)
goto noindex;
/**********
* o check for array
* o extract array index and check
**********/
pos = skip_over (in, pos, 1);
if ((pos + 3) > in->len)
goto error;
if (in->s [pos] != '[')
goto error;
pos = skip_over (in, pos + 1, 1);
if ((pos + 2) > in->len)
goto error;
pvi.s = &in->s [pos];
pvi.len = pos;
if (in->s [pos] == PV_MARKER) {
/**********
* o search from the end back to array close
* o get PV value
**********/
for (i = in->len - 1; i != pos; --i) {
if (in->s [i] == ']')
break;
}
if (i == pos)
goto error;
pvi.len = i - pvi.len;
pos = i + 1;
dpv->pidx = pv_cache_get (&pvi);
if (!dpv->pidx)
goto error;
dpv->flags |= SR_DNS_PVIDX;
} else {
/**********
* o get index value
* o check for reverse index
* o convert string to number
**********/
pos = skip_over (in, pos, 0);
pvi.len = pos - pvi.len;
sign = 1;
i = 0;
pstr = pvi.s;
if (*pstr == '-') {
sign = -1;
i++;
pstr++;
}
for (dpv->nidx = 0; i < pvi.len; i++) {
if (*pstr >= '0' && *pstr <= '9')
dpv->nidx = (dpv->nidx * 10) + *pstr++ - '0';
}
if (i != pvi.len)
goto error;
dpv->nidx *= sign;
pos = skip_over (in, pos, 1);
if (pos == in->len)
goto error;
if (in->s [pos++] != ']')
goto error;
}
/**********
* o check for trailing whitespace
* o add data to PV
**********/
noindex:
if (skip_over (in, pos, 1) != in->len)
goto error;
LM_DBG ("srvquery (%.*s => %.*s [%.*s])\n",
pvn.len, ZSW(pvn.s), pvk.len, ZSW(pvk.s), pvi.len, ZSW(pvi.s));
dpv->item = sr_srv_add_item (&pvn, 0);
if (!dpv->item)
goto error;
sp->pvp.pvn.u.dname = (void *)dpv;
sp->pvp.pvn.type = PV_NAME_OTHER;
return 0;
error:
LM_ERR ("error at PV srvquery: %.*s@%d\n", in->len, in->s, pos);
pkg_free (dpv);
return -1;
}
int srv_update_pv (str *srvcname, str *pvid)
{
int idx1, idx2, rcount;
struct rdata *phead, *psrv;
struct srv_rdata *plist [PV_SRV_MAXRECS];
sr_srv_item_t *pitem;
sr_srv_record_t *prec;
/**********
* o service name missing?
* o find pvid
**********/
if (!srvcname->len) {
LM_DBG ("service name missing: %.*s\n", srvcname->len, srvcname->s);
return -2;
}
pitem = sr_srv_add_item (pvid, 1);
if (!pitem) {
LM_DBG ("pvid not found: %.*s\n", pvid->len, pvid->s);
return -3;
}
/**********
* o get records
* o sort by priority/weight
* o save to PV
**********/
LM_DBG ("attempting to query: %.*s\n", srvcname->len, srvcname->s);
phead = get_record (srvcname->s, T_SRV, RES_ONLY_TYPE);
rcount = 0;
for (psrv = phead; psrv; psrv = psrv->next) {
if (rcount < PV_SRV_MAXRECS) {
plist [rcount++] = (struct srv_rdata *) psrv->rdata;
} else {
LM_WARN ("truncating srv_query list to %d records!", PV_SRV_MAXRECS);
break;
}
}
pitem->count = rcount;
if (rcount)
sort_srv (plist, rcount);
for (idx1 = 0; idx1 < rcount; idx1++) {
prec = &pitem->rr [idx1];
prec->priority = plist [idx1]->priority;
prec->weight = plist [idx1]->weight;
prec->port = plist [idx1]->port;
idx2 = plist [idx1]->name_len;
if (idx2 > PV_SRV_MAXSTR) {
LM_WARN ("truncating srv_query target (%.*s)!", idx2, plist [idx1]->name);
idx2 = PV_SRV_MAXSTR;
}
strncpy (prec->target, plist [idx1]->name, idx2);
prec->target [idx2] = '\0';
}
if (phead)
free_rdata_list (phead);
LM_DBG ("srvquery PV updated for: %.*s (%d)\n",
srvcname->len, srvcname->s, rcount);
return 1;
}
/**********
* Get srvquery Values
*
* INPUT:
* Arg (1) = SIP message pointer
* Arg (2) = parameter pointer
* Arg (3) = PV value pointer
* OUTPUT: 0=success
**********/
int pv_get_srv (sip_msg_t *pmsg, pv_param_t *param, pv_value_t *res)
{
pv_value_t val;
srv_pv_t *dpv;
/**********
* o sipmsg and param exist?
* o PV name exists?
* o count?
**********/
if(!pmsg || !param)
return -1;
dpv = (srv_pv_t *) param->pvn.u.dname;
if(!dpv || !dpv->item)
return -1;
if (!dpv->type)
return pv_get_sintval (pmsg, param, res, dpv->item->count);
/**********
* o get index value
* o reverse index?
* o extract data
**********/
if (!dpv->pidx) {
val.ri = dpv->nidx;
} else {
if (pv_get_spec_value (pmsg, dpv->pidx, &val) < 0
|| !(val.flags & PV_VAL_INT)) {
LM_ERR ("failed to evaluate index variable!\n");
return pv_get_null (pmsg, param, res);
}
}
if (val.ri < 0) {
if ((dpv->item->count + val.ri) < 0)
return pv_get_null (pmsg, param, res);
val.ri = dpv->item->count + val.ri;
}
if (val.ri >= dpv->item->count)
return pv_get_null(pmsg, param, res);
switch (dpv->type) {
case 1: /* port */
return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].port);
case 2: /* priority */
return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].priority);
case 3: /* target */
return pv_get_strzval (pmsg, param, res, dpv->item->rr [val.ri].target);
case 4: /* weight */
return pv_get_sintval (pmsg, param, res, dpv->item->rr [val.ri].weight);
}
return pv_get_null (pmsg, param, res);
}