/* * Export vontact attrs as PV * * Copyright (C) 2008 Daniel-Constantin Mierla (asipto.com) * * 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 SIP registrar module - export contacts as PV * \ingroup registrar */ #include <string.h> #include "../../core/dprint.h" #include "../../core/mem/mem.h" #include "../../core/mod_fix.h" #include "../../core/route.h" #include "../../core/action.h" #include "../../core/fmsg.h" #include "../../core/kemi.h" #include "../../core/receive.h" #include "../usrloc/usrloc.h" #include "registrar.h" #include "common.h" #include "regpv.h" extern str reg_event_callback; typedef struct _regpv_profile { str pname; str domain; str aor; int flags; unsigned int aorhash; int nrc; ucontact_t* contacts; struct _regpv_profile *next; } regpv_profile_t; typedef struct _regpv_name { regpv_profile_t *rp; int attr; } regpv_name_t; static regpv_profile_t *_regpv_profile_list = NULL; static inline regpv_profile_t* regpv_get_profile(str *name) { regpv_profile_t *rp; if(name==NULL || name->len<=0) { LM_ERR("invalid parameters\n"); return NULL; } rp = _regpv_profile_list; while(rp) { if(rp->pname.len == name->len && strncmp(rp->pname.s, name->s, name->len)==0) return rp; rp = rp->next; } rp = (regpv_profile_t*)pkg_malloc(sizeof(regpv_profile_t)); if(rp==NULL) { LM_ERR("no more pkg\n"); return NULL; } memset(rp, 0, sizeof(regpv_profile_t)); rp->pname.s = (char*)pkg_malloc((name->len+1)*sizeof(char)); if(rp->pname.s==NULL) { LM_ERR("no more pkg\n"); pkg_free(rp); return NULL; } memcpy(rp->pname.s, name->s, name->len); rp->pname.s[name->len] = '\0'; rp->pname.len = name->len; rp->next = _regpv_profile_list; _regpv_profile_list = rp; return rp; } static void regpv_free_profile(regpv_profile_t *rpp) { ucontact_t* ptr; ucontact_t* ptr0; if(rpp==NULL) return; ptr = rpp->contacts; while(ptr) { ptr0 = ptr; ptr = ptr->next; pkg_free(ptr0); } if(rpp->domain.s!=NULL) { rpp->domain.s = 0; rpp->domain.len = 0; } if(rpp->aor.s!=NULL) { pkg_free(rpp->aor.s); rpp->aor.s = 0; rpp->aor.len = 0; } rpp->flags = 0; rpp->aorhash = 0; rpp->nrc = 0; rpp->contacts = 0; } void regpv_free_profiles(void) { regpv_profile_t *rp; regpv_profile_t *rp0; rp = _regpv_profile_list; while(rp) { if(rp->pname.s!=NULL) pkg_free(rp->pname.s); rp0 = rp; regpv_free_profile(rp0); rp = rp->next; } _regpv_profile_list = 0; } int pv_get_ulc(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { regpv_name_t *rp; regpv_profile_t *rpp; ucontact_t *c; int idx; int i; if(param==NULL) { LM_ERR("invalid params\n"); return -1; } rp = (regpv_name_t*)param->pvn.u.dname; if(rp==NULL || rp->rp==NULL) { LM_DBG("no profile in params\n"); return pv_get_null(msg, param, res); } rpp = rp->rp; if(rpp->flags==0 || rpp->contacts==NULL) { LM_DBG("profile not set or no contacts there\n"); return pv_get_null(msg, param, res); } /* get index */ if(pv_get_spec_index(msg, param, &idx, &i)!=0) { LM_ERR("invalid index\n"); return -1; } /* work only with positive indexes by now */ if(idx<0) idx = 0; /* get contact */ i = 0; c = rpp->contacts; while(c) { if(i == idx) break; i++; c = c->next; } if(c==NULL) return pv_get_null(msg, param, res); switch(rp->attr) { case 0: /* aor */ return pv_get_strval(msg, param, res, &rpp->aor); break; case 1: /* domain */ return pv_get_strval(msg, param, res, &rpp->domain); break; case 2: /* aorhash */ return pv_get_uintval(msg, param, res, rpp->aorhash); break; case 3: /* addr */ return pv_get_strval(msg, param, res, &c->c); break; case 4: /* path */ return pv_get_strval(msg, param, res, &c->path); break; case 5: /* received */ return pv_get_strval(msg, param, res, &c->received); break; case 6: /* expires */ return pv_get_uintval(msg, param, res, (unsigned int)c->expires); break; case 7: /* callid */ return pv_get_strval(msg, param, res, &c->callid); break; case 8: /* q */ return pv_get_sintval(msg, param, res, (int)c->q); break; case 9: /* cseq */ return pv_get_sintval(msg, param, res, c->cseq); break; case 10: /* flags */ return pv_get_uintval(msg, param, res, c->flags); break; case 11: /* cflags */ return pv_get_uintval(msg, param, res, c->cflags); break; case 12: /* user agent */ return pv_get_strval(msg, param, res, &c->user_agent); break; case 14: /* socket */ if(c->sock==NULL) return pv_get_null(msg, param, res); return pv_get_strval(msg, param, res, &c->sock->sock_str); break; case 15: /* modified */ return pv_get_uintval(msg, param, res, (unsigned int)c->last_modified); break; case 16: /* methods */ return pv_get_uintval(msg, param, res, c->methods); break; case 17: /* count */ return pv_get_sintval(msg, param, res, rpp->nrc); break; case 18: /* ruid */ return pv_get_strval(msg, param, res, &c->ruid); break; case 19: /* reg-id */ return pv_get_uintval(msg, param, res, c->reg_id); break; case 20: /* instance */ if(c->instance.len>0) return pv_get_strval(msg, param, res, &c->instance); break; case 21: /* conid */ if (c->sock && (c->sock->proto == PROTO_TCP || c->sock->proto == PROTO_TLS || c->sock->proto == PROTO_WS || c->sock->proto == PROTO_WSS)) return pv_get_sintval(msg, param, res, c->tcpconn_id); break; case 22: /* server_id */ return pv_get_uintval(msg, param, res, c->server_id); break; } return pv_get_null(msg, param, res); } int pv_set_ulc(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { return 0; } int pv_parse_ulc_name(pv_spec_p sp, str *in) { str pn; str pa; regpv_name_t *rp = NULL; regpv_profile_t *rpp = NULL; if(sp==NULL || in==NULL || in->len<=0) return -1; pa.s = in->s; while(pa.s < in->s + in->len - 2) { if(*pa.s=='=') break; pa.s++; } if(pa.s >= in->s + in->len - 2) { LM_ERR("invalid contact pv name %.*s\n", in->len, in->s); return -1; } if(*(pa.s+1) != '>') { LM_ERR("invalid contact pv name %.*s.\n", in->len, in->s); return -1; } pn.s = in->s; pn.len = pa.s - pn.s; LM_DBG("get profile [%.*s]\n", pn.len, pn.s); rpp = regpv_get_profile(&pn); if(rpp==NULL) { LM_ERR("cannot get profile [%.*s]\n", pn.len, pn.s); return -1; } pa.s += 2; pa.len = in->s + in->len - pa.s; LM_DBG("get attr [%.*s]\n", pa.len, pa.s); rp = (regpv_name_t*)pkg_malloc(sizeof(regpv_name_t)); if(rp==0) { LM_ERR("no more pkg\n"); return -1; } memset(rp, 0, sizeof(regpv_name_t)); rp->rp = rpp; switch(pa.len) { case 1: if(strncmp(pa.s, "q", 1)==0) rp->attr = 8; else goto error; break; case 3: if(strncmp(pa.s, "aor", 3)==0) rp->attr = 0; else goto error; break; case 4: if(strncmp(pa.s, "addr", 4)==0) rp->attr = 3; else if(strncmp(pa.s, "path", 4)==0) rp->attr = 4; else if(strncmp(pa.s, "cseq", 4)==0) rp->attr = 9; else if(strncmp(pa.s, "ruid", 4)==0) rp->attr = 18; else goto error; break; case 5: if(strncmp(pa.s, "flags", 5)==0) rp->attr = 10; else if(strncmp(pa.s, "count", 5)==0) rp->attr = 17; else if(strncmp(pa.s, "regid", 5)==0) rp->attr = 19; else if(strncmp(pa.s, "conid", 5)==0) rp->attr = 21; else goto error; break; case 6: if(strncmp(pa.s, "domain", 6)==0) rp->attr = 1; else if(strncmp(pa.s, "callid", 6)==0) rp->attr = 7; else if(strncmp(pa.s, "cflags", 6)==0) rp->attr = 11; else if(strncmp(pa.s, "socket", 6)==0) rp->attr = 14; else goto error; break; case 7: if(strncmp(pa.s, "aorhash", 7)==0) rp->attr = 2; else if(strncmp(pa.s, "expires", 7)==0) rp->attr = 6; else if(strncmp(pa.s, "methods", 7)==0) rp->attr = 16; else goto error; break; case 8: if(strncmp(pa.s, "received", 8)==0) rp->attr = 5; else if(strncmp(pa.s, "modified", 8)==0) rp->attr = 15; else if(strncmp(pa.s, "instance", 8)==0) rp->attr = 20; else goto error; break; case 9: if(strncmp(pa.s, "server_id", 9)==0) rp->attr = 22; break; case 10: if(strncmp(pa.s, "user_agent", 10)==0) rp->attr = 12; else goto error; break; default: goto error; } sp->pvp.pvn.u.dname = (void*)rp; sp->pvp.pvn.type = PV_NAME_PVAR; return 0; error: if(rp) pkg_free(rp); LM_ERR("unknown contact attr name in %.*s\n", in->len, in->s); return -1; } /** * */ static sr_kemi_xval_t _sr_kemi_reg_ulc_xval = {0}; sr_kemi_xval_t* ki_reg_ulc_rget(sip_msg_t* msg, str* rid, str* attr) { regpv_profile_t *rpp = NULL; memset(&_sr_kemi_reg_ulc_xval, 0, sizeof(sr_kemi_xval_t)); if(rid==NULL || rid->s==NULL || attr==NULL || attr->s==NULL) { LM_WARN("invalid parameters - return value 0\n"); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } rpp = regpv_get_profile(rid); if(rpp==0) { LM_WARN("result [%.*s] is not defined - return value 0\n", rid->len, rid->s); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } if(attr->len==5 && strncmp(attr->s, "count", 5)==0) { _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = rpp->nrc; return &_sr_kemi_reg_ulc_xval; } else if(attr->len==3 && strncmp(attr->s, "aor", 3)==0) { _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_STR; _sr_kemi_reg_ulc_xval.v.s = rpp->aor; return &_sr_kemi_reg_ulc_xval; } LM_WARN("attribute [%.*s] is not defined - return value 0\n", attr->len, attr->s); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } sr_kemi_xval_t* ki_reg_ulc_cget(sip_msg_t* msg, str* rid, str* attr, int idx) { regpv_profile_t *rpp = NULL; ucontact_t *c = NULL; int i = 0; memset(&_sr_kemi_reg_ulc_xval, 0, sizeof(sr_kemi_xval_t)); if(rid==NULL || rid->s==NULL || attr==NULL || attr->s==NULL) { LM_WARN("invalid parameters - return value 0\n"); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } rpp = regpv_get_profile(rid); if(rpp==0) { LM_WARN("result [%.*s] is not defined - return value 0\n", rid->len, rid->s); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } /* work only with positive indexes by now */ if(idx<0) { idx = 0; } /* get contact by index */ i = 0; for(c = rpp->contacts; c!=NULL; c = c->next) { if(i == idx) { break; } i++; } if(c == NULL) { LM_WARN("contact at index [%d] is not found - return value 0\n", idx); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } if(attr->len==4 && strncmp(attr->s, "addr", 4)==0) { _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_STR; _sr_kemi_reg_ulc_xval.v.s = c->c; return &_sr_kemi_reg_ulc_xval; } else if(attr->len==6 && strncmp(attr->s, "socket", 6)==0) { if(c->sock==NULL) { sr_kemi_xval_null(&_sr_kemi_reg_ulc_xval, SR_KEMI_XVAL_NULL_EMPTY); return &_sr_kemi_reg_ulc_xval; } _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_STR; _sr_kemi_reg_ulc_xval.v.s = c->sock->sock_str; return &_sr_kemi_reg_ulc_xval; } LM_WARN("attribute [%.*s] is not defined - return value 0\n", attr->len, attr->s); _sr_kemi_reg_ulc_xval.vtype = SR_KEMIP_INT; _sr_kemi_reg_ulc_xval.v.n = 0; return &_sr_kemi_reg_ulc_xval; } int pv_fetch_contacts_helper(sip_msg_t* msg, udomain_t* dt, str* uri, str* profile) { urecord_t* r; ucontact_t* ptr; ucontact_t* ptr0; ucontact_t* c0; regpv_profile_t *rpp; str aor = {0, 0}; int res; int olen; int ilen; int n; char *p; rpp = regpv_get_profile((str*)profile); if(rpp==0) { LM_ERR("invalid parameters\n"); return -1; } /* check and free if profile already set */ if(rpp->flags) regpv_free_profile(rpp); if (extract_aor(uri, &aor, NULL) < 0) { LM_ERR("failed to extract Address Of Record\n"); return -1; } /* copy aor and ul domain */ rpp->aor.s = (char*)pkg_malloc(aor.len*sizeof(char)); if(rpp->aor.s==NULL) { LM_ERR("no more pkg\n"); return -1; } memcpy(rpp->aor.s, aor.s, aor.len); rpp->aor.len = aor.len; rpp->domain = *((udomain_head_t*)dt)->name; rpp->flags = 1; /* copy contacts */ ilen = sizeof(ucontact_t); ul.lock_udomain(dt, &aor); res = ul.get_urecord(dt, &aor, &r); if (res > 0) { LM_DBG("'%.*s' Not found in usrloc\n", aor.len, ZSW(aor.s)); ul.unlock_udomain(dt, &aor); return -1; } ptr = r->contacts; ptr0 = NULL; n = 0; while(ptr) { olen = (ptr->c.len + ptr->received.len + ptr->path.len + ptr->callid.len + ptr->user_agent.len + ptr->ruid.len + ptr->instance.len)*sizeof(char) + ilen; c0 = (ucontact_t*)pkg_malloc(olen); if(c0==NULL) { LM_ERR("no more pkg\n"); ul.release_urecord(r); ul.unlock_udomain(dt, &aor); goto error; } memcpy(c0, ptr, ilen); c0->domain = NULL; c0->aor = NULL; c0->next = NULL; c0->prev = NULL; c0->c.s = (char*)c0 + ilen; memcpy(c0->c.s, ptr->c.s, ptr->c.len); c0->c.len = ptr->c.len; p = c0->c.s + c0->c.len; if(ptr->received.s!=NULL) { c0->received.s = p; memcpy(c0->received.s, ptr->received.s, ptr->received.len); c0->received.len = ptr->received.len; p += c0->received.len; } if(ptr->path.s!=NULL) { c0->path.s = p; memcpy(c0->path.s, ptr->path.s, ptr->path.len); c0->path.len = ptr->path.len; p += c0->path.len; } c0->callid.s = p; memcpy(c0->callid.s, ptr->callid.s, ptr->callid.len); c0->callid.len = ptr->callid.len; p += c0->callid.len; if(ptr->user_agent.s!=NULL) { c0->user_agent.s = p; memcpy(c0->user_agent.s, ptr->user_agent.s, ptr->user_agent.len); c0->user_agent.len = ptr->user_agent.len; p += c0->user_agent.len; } if(ptr->ruid.s!=NULL) { c0->ruid.s = p; memcpy(c0->ruid.s, ptr->ruid.s, ptr->ruid.len); c0->ruid.len = ptr->ruid.len; p += c0->ruid.len; } if(ptr->instance.s!=NULL) { c0->instance.s = p; memcpy(c0->instance.s, ptr->instance.s, ptr->instance.len); c0->instance.len = ptr->instance.len; p += c0->instance.len; } LM_DBG("memory block between %p - %p\n", c0, p); if ((ptr->sock) && (ptr->sock->proto == PROTO_TCP || ptr->sock->proto == PROTO_TLS || ptr->sock->proto == PROTO_WS || ptr->sock->proto == PROTO_WSS)) { c0->tcpconn_id = ptr->tcpconn_id; } if(ptr0==NULL) { rpp->contacts = c0; } else { ptr0->next = c0; c0->prev = ptr0; } n++; ptr0 = c0; ptr = ptr->next; } ul.release_urecord(r); ul.unlock_udomain(dt, &aor); rpp->nrc = n; LM_DBG("fetched <%d> contacts for <%.*s> in [%.*s]\n", n, aor.len, aor.s, rpp->pname.len, rpp->pname.s); return 1; error: regpv_free_profile(rpp); return -1; } int pv_fetch_contacts(sip_msg_t* msg, char* table, char* uri, char* profile) { str u = STR_NULL; if(fixup_get_svalue(msg, (gparam_t*)uri, &u)!=0 || u.len<=0) { LM_ERR("invalid uri parameter\n"); return -1; } return pv_fetch_contacts_helper(msg, (udomain_t*)table, &u, (str*)profile); } int ki_reg_fetch_contacts(sip_msg_t* msg, str* dtable, str* uri, str* profile) { udomain_t* d; if(ul.get_udomain(dtable->s, &d)<0) { LM_ERR("usrloc domain [%s] not found\n", dtable->s); return -1; } return pv_fetch_contacts_helper(msg, d, uri, profile); } int ki_reg_free_contacts(sip_msg_t* msg, str* profile) { regpv_profile_t *rpp; rpp = regpv_get_profile(profile); if(rpp==0) return -1; regpv_free_profile(rpp); return 1; } int pv_free_contacts(struct sip_msg* msg, char* profile, char* s2) { return ki_reg_free_contacts(msg, (str*)profile); } void reg_ul_expired_contact(ucontact_t* ptr, int type, void* param) { str profile = {"exp", 3}; regpv_profile_t *rpp; ucontact_t* c0; int backup_rt; struct run_act_ctx ctx; sip_msg_t *fmsg; int olen; int ilen; char *p; sr_kemi_eng_t *keng = NULL; if(reg_expire_event_rt<0 && reg_event_callback.s==NULL) return; if (faked_msg_init() < 0) { LM_ERR("faked_msg_init() failed\n"); return; } rpp = regpv_get_profile(&profile); if(rpp==0) { LM_ERR("error getting profile structure\n"); return; } /* check and free if profile already set */ if(rpp->flags) regpv_free_profile(rpp); /* copy aor and ul domain */ rpp->aor.s = (char*)pkg_malloc(ptr->aor->len*sizeof(char)); if(rpp->aor.s==NULL) { LM_ERR("no more pkg\n"); return; } memcpy(rpp->aor.s, ptr->aor->s, ptr->aor->len); rpp->aor.len = ptr->aor->len; rpp->domain = *ptr->domain; rpp->flags = 1; /* copy contact */ ilen = sizeof(ucontact_t); olen = (ptr->c.len + ptr->received.len + ptr->path.len + ptr->callid.len + ptr->user_agent.len + ptr->ruid.len + ptr->instance.len)*sizeof(char) + ilen; c0 = (ucontact_t*)pkg_malloc(olen); if(c0==NULL) { LM_ERR("no more pkg\n"); goto error; } memcpy(c0, ptr, ilen); c0->domain = NULL; c0->aor = NULL; c0->next = NULL; c0->prev = NULL; c0->c.s = (char*)c0 + ilen; memcpy(c0->c.s, ptr->c.s, ptr->c.len); c0->c.len = ptr->c.len; p = c0->c.s + c0->c.len; if(ptr->received.s!=NULL) { c0->received.s = p; memcpy(c0->received.s, ptr->received.s, ptr->received.len); c0->received.len = ptr->received.len; p += c0->received.len; } if(ptr->path.s!=NULL) { c0->path.s = p; memcpy(c0->path.s, ptr->path.s, ptr->path.len); c0->path.len = ptr->path.len; p += c0->path.len; } c0->callid.s = p; memcpy(c0->callid.s, ptr->callid.s, ptr->callid.len); c0->callid.len = ptr->callid.len; p += c0->callid.len; if(ptr->user_agent.s!=NULL) { c0->user_agent.s = p; memcpy(c0->user_agent.s, ptr->user_agent.s, ptr->user_agent.len); c0->user_agent.len = ptr->user_agent.len; p += c0->user_agent.len; } if(ptr->ruid.s!=NULL) { c0->ruid.s = p; memcpy(c0->ruid.s, ptr->ruid.s, ptr->ruid.len); c0->ruid.len = ptr->ruid.len; p += c0->ruid.len; } if(ptr->instance.s!=NULL) { c0->instance.s = p; memcpy(c0->instance.s, ptr->instance.s, ptr->instance.len); c0->instance.len = ptr->instance.len; p += c0->instance.len; } rpp->contacts = c0; rpp->nrc = 1; LM_DBG("saved contact for <%.*s> in [%.*s]\n", ptr->aor->len, ptr->aor->s, rpp->pname.len, rpp->pname.s); fmsg = faked_msg_next(); backup_rt = get_route_type(); set_route_type(REQUEST_ROUTE); init_run_actions_ctx(&ctx); if (reg_expire_event_rt >= 0) { run_top_route(event_rt.rlist[reg_expire_event_rt], fmsg, 0); } else { keng = sr_kemi_eng_get(); if (keng!=NULL) { str evname = str_init("usrloc:contact-expired"); if(sr_kemi_route(keng, fmsg, EVENT_ROUTE, ®_event_callback, &evname)<0) { LM_ERR("error running event route kemi callback\n"); } } } set_route_type(backup_rt); ksr_msg_env_reset(); return; error: regpv_free_profile(rpp); return; }