/** * 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 "../../core/dprint.h" #include "../../core/rand/fastrand.h" #include "../../core/hashes.h" #include "../../core/resolve.h" #include "../../core/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; int n = 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; n++; } if(n > 20) { LM_WARN("too many dns containers - adding nunmber %d - can fill memory\n", n); } /* 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 - 1 - 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] in [%.*s]\n", pvi.len, pvi.s, in->len, in->s); goto error; } 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_add_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); } /********** * naptrquery PV **********/ static char *naptrqrylst[] = { "count", "order", "pref", "flags", "services", "regex", "replace", NULL }; #define PV_NAPTR_MAXSTR 64 #define PV_NAPTR_MAXRECS 32 typedef struct _sr_naptr_record { unsigned short count; unsigned short order; unsigned short pref; char flags[PV_NAPTR_MAXSTR + 1]; char services[PV_NAPTR_MAXSTR + 1]; char regex[PV_NAPTR_MAXSTR + 1]; char replace[PV_NAPTR_MAXSTR + 1]; } sr_naptr_record_t; typedef struct _sr_naptr_item { str pvid; unsigned int hashid; int count; sr_naptr_record_t rr[PV_NAPTR_MAXRECS]; struct _sr_naptr_item *next; } sr_naptr_item_t; typedef struct _naptr_pv { sr_naptr_item_t *item; int type; int flags; pv_spec_t *pidx; int nidx; } naptr_pv_t; static sr_naptr_item_t *_sr_naptr_list = NULL; /********** * Add naptrquery Item * * INPUT: * Arg (1) = pvid string pointer * Arg (2) = find flag; <>0=search only * OUTPUT: naptr record pointer; NULL=not found **********/ sr_naptr_item_t *sr_naptr_add_item(str *pvid, int findflg) { sr_naptr_item_t *pitem; unsigned int hashid; LM_DBG("%s:%d %s - called: pvid => [%.*s] findflg => [%d]\n", __FILE__,__LINE__,__PRETTY_FUNCTION__, STR_FMT(pvid), findflg); /********** * o get hash * o already exists? **********/ hashid = get_hash1_raw (pvid->s, pvid->len); for (pitem = _sr_naptr_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_naptr_item_t *)pkg_malloc(sizeof (sr_naptr_item_t)); if (!pitem) { LM_ERR ("No more pkg memory!\n"); return NULL; } memset(pitem, 0, sizeof(sr_naptr_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_naptr_list; _sr_naptr_list = pitem; LM_DBG("New item [%.*s]", STR_FMT(pvid)); return pitem; } /********** * Sort NAPTR Records by Order/Pref * * INPUT: * Arg (1) = pointer to array of NAPTR records * Arg (2) = record count * OUTPUT: position past skipped **********/ void sort_naptr(struct naptr_rdata **plist, int rcount) { int idx1, idx2; struct naptr_rdata *pswap; for (idx1 = 1; idx1 < rcount; idx1++) { pswap = plist[idx1]; for (idx2 = idx1; idx2 && ( (plist[idx2 - 1]->order > pswap->order) || (plist[idx2 - 1]->order == pswap->order && plist[idx2 - 1]->pref > pswap->pref) ); --idx2) { plist [idx2] = plist [idx2 - 1]; } plist [idx2] = pswap; } return; } /********** * Parse naptrquery Name * * INPUT: * Arg (1) = pv spec pointer * Arg (2) = input string pointer * OUTPUT: 0=success **********/ int pv_parse_naptr_name(pv_spec_t *sp, str *in) { LM_DBG("%s:%d %s - called: sp => [%p] in => [%.*s]\n", __FILE__,__LINE__,__PRETTY_FUNCTION__, sp, STR_FMT(in)); char *pstr; int i, pos, sign; naptr_pv_t *dpv; str pvn = {0}, pvk = {0}, pvi = {0}; /********** * o alloc/init pvid structure * o extract pvid name * o check separator **********/ if (!sp || !in || in->len<=0) return -1; /* alloc/init pvid structure */ dpv = (naptr_pv_t *)pkg_malloc(sizeof(naptr_pv_t)); if (!dpv) { LM_ERR ("No more pkg memory!\n"); return -1; } memset(dpv, 0, sizeof(naptr_pv_t)); /* skip blank chars and init pvn.s */ pos = skip_over(in, 0, 1); if (pos == in->len) goto error; pvn.s = &in->s[pos]; pvn.len = pos; /* skip [a-zA-Z0-9] and find pvn.len */ pos = skip_over(in, pos, 0); pvn.len = pos - pvn.len; if (!pvn.len) goto error; /* skip blank chars */ 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? **********/ /* skip blank chars and init pvk.s */ pos = skip_over(in, pos + 2, 1); pvk.s = &in->s[pos]; pvk.len = pos; /* skip [a-zA-Z0-9] and find pvk.len */ pos = skip_over(in, pos, 0); pvk.len = pos - pvk.len; if (!pvk.len) goto error; for (i = 0; naptrqrylst[i]; i++) { if (strlen(naptrqrylst[i]) != pvk.len) continue; if (!strncmp(pvk.s, naptrqrylst[i], pvk.len)) { dpv->type = i; break; } } if (!naptrqrylst[i]) goto error; /* "=>count" doesn't have any index */ if (!i) goto noindex; /********** * o check for array * o extract array index and check **********/ /* skip blank between "=>order" and array index "[" */ pos = skip_over (in, pos, 1); if ((pos + 3) > in->len) goto error; if (in->s [pos] != '[') goto error; /* skip blank between index sign "[" and a numeric value */ 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; } /* empty idx */ 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 **********/ /* find the end of the index and its length */ pos = skip_over(in, pos, 0); pvi.len = pos - pvi.len; sign = 1; i = 0; pstr = pvi.s; if (*pstr == '-') { sign = -1; i++; pstr++; } /* homemade atoi() */ 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; /* skip blanks between index and the final "]" */ pos = skip_over(in, pos, 1); if (pos == in->len) goto error; /* check the final "]" */ 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 ("naptrquery (%.*s => %.*s [%.*s])\n", pvn.len, ZSW(pvn.s), pvk.len, ZSW(pvk.s), pvi.len, ZSW(pvi.s)); dpv->item = sr_naptr_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 naptrquery: %.*s@%d\n", in->len, in->s, pos); pkg_free (dpv); return -1; } int naptr_update_pv(str *naptrname, str *pvid) { LM_DBG("%s:%d %s - called: naptrname => [%.*s], pvid => [%.*s]\n", __FILE__,__LINE__,__PRETTY_FUNCTION__, STR_FMT(naptrname), STR_FMT(pvid)); int idx1, idx2, rcount; struct rdata *phead, *pnaptr; struct naptr_rdata *plist[PV_SRV_MAXRECS]; sr_naptr_item_t *pitem; sr_naptr_record_t *prec; /********** * o service name missing? * o find pvid **********/ if (!naptrname->len) { LM_DBG ("naptr name missing: %.*s\n", naptrname->len, naptrname->s); return -2; } pitem = sr_naptr_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 order/pref * o save to PV **********/ LM_DBG ("attempting to query: %.*s\n", naptrname->len, naptrname->s); phead = get_record(naptrname->s, T_NAPTR, RES_ONLY_TYPE); rcount = 0; for (pnaptr = phead; pnaptr; pnaptr = pnaptr->next) { if (rcount < PV_NAPTR_MAXRECS) { plist[rcount++] = (struct naptr_rdata *)pnaptr->rdata; } else { LM_WARN ("truncating naptr_query list to %d records!", PV_NAPTR_MAXRECS); break; } } pitem->count = rcount; if (rcount) sort_naptr(plist, rcount); for (idx1 = 0; idx1 < rcount; idx1++) { prec = &pitem->rr[idx1]; prec->order = plist[idx1]->order; prec->pref = plist[idx1]->pref; idx2 = plist[idx1]->flags_len; if (idx2 > PV_NAPTR_MAXSTR) { LM_WARN ("truncating naptr_query flags (%.*s)!", idx2, plist[idx1]->flags); idx2 = PV_NAPTR_MAXSTR; } strncpy(prec->flags, plist[idx1]->flags, idx2); prec->flags[idx2] = '\0'; idx2 = plist[idx1]->services_len; if (idx2 > PV_NAPTR_MAXSTR) { LM_WARN ("truncating naptr query services (%.*s)!", idx2, plist[idx1]->services); idx2 = PV_NAPTR_MAXSTR; } strncpy(prec->services, plist[idx1]->services, idx2); prec->services[idx2] = '\0'; idx2 = plist[idx1]->regexp_len; if (idx2 > PV_NAPTR_MAXSTR) { LM_WARN ("truncating naptr query regexp (%.*s)!", idx2, plist[idx1]->regexp); idx2 = PV_NAPTR_MAXSTR; } strncpy(prec->regex, plist[idx1]->regexp, idx2); prec->regex[idx2] = '\0'; idx2 = plist[idx1]->repl_len; if (idx2 > PV_NAPTR_MAXSTR) { LM_WARN ("truncating naptr query replace (%.*s)!", idx2, plist[idx1]->repl); idx2 = PV_NAPTR_MAXSTR; } strncpy(prec->replace, plist[idx1]->repl, idx2); prec->replace[idx2] = '\0'; } if (phead) free_rdata_list (phead); LM_DBG ("naptrquery PV updated for: %.*s (%d)\n", naptrname->len, naptrname->s, rcount); return 1; } /********** * Get naptrquery Values * * INPUT: * Arg (1) = SIP message pointer * Arg (2) = parameter pointer * Arg (3) = PV value pointer * OUTPUT: 0=success **********/ int pv_get_naptr(sip_msg_t *pmsg, pv_param_t *param, pv_value_t *res) { pv_value_t val; naptr_pv_t *dpv; LM_DBG("%s:%d %s - called: param => [%p], res => [%p]\n", __FILE__,__LINE__,__PRETTY_FUNCTION__, param, res); /********** * o sipmsg and param exist? * o PV name exists? * o count? **********/ if (!pmsg || !param) return -1; dpv = (naptr_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: /* order */ return pv_get_sintval(pmsg, param, res, dpv->item->rr[val.ri].order); case 2: /* pref */ return pv_get_sintval(pmsg, param, res, dpv->item->rr[val.ri].pref); case 3: /* flags */ return pv_get_strzval(pmsg, param, res, dpv->item->rr[val.ri].flags); case 4: /* services */ return pv_get_strzval(pmsg, param, res, dpv->item->rr[val.ri].services); case 5: /* regex */ return pv_get_strzval(pmsg, param, res, dpv->item->rr[val.ri].regex); case 6: /* replace */ return pv_get_strzval(pmsg, param, res, dpv->item->rr[val.ri].replace); } return pv_get_null (pmsg, param, res); }