/* * Copyright (C) 2016 kamailio.org * * 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 */ #include #include #include "../../mod_fix.h" #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../mem/mem.h" #include "../../str.h" #include "../../lib/kcore/cmpapi.h" #include "chargingvector.h" #define SIZE_CONF_ID 16 #define P_CHARGING_VECTOR "P-Charging-Vector" #define LOOPBACK_IP 16777343 #define PVC_BUF_SIZE 256 static char pcv_buf[PVC_BUF_SIZE]; static str pcv = { pcv_buf, 0 }; static str pcv_host = { NULL, 0 }; static str pcv_id = { NULL, 0 }; static uint64_t counter = 0 ; enum PCV_Status { PCV_NONE = 0, PCV_PARSED = 1, PCV_GENERATED = 2 }; static enum PCV_Status pcv_status = PCV_NONE; static unsigned int current_msg_id = (unsigned int)-1; static void sip_generate_charging_vector(char * pcv) { char s[PATH_MAX] = {0}; struct hostent* host = NULL; int cdx = 0 ; int tdx = 0 ; int idx = 0 ; int ipx = 0 ; int pid; uint64_t ct = 0 ; struct in_addr* in = NULL; static struct in_addr ip = {0}; unsigned char newConferenceIdentifier[SIZE_CONF_ID]={0}; memset(pcv,0,SIZE_CONF_ID); pid = getpid(); if ( ip.s_addr == 0 ) { if (!gethostname(s, PATH_MAX)) { if ((host = gethostbyname(s)) != NULL) { int idx = 0 ; for (idx = 0 ; host->h_addr_list[idx]!=NULL ; idx++) { in = (struct in_addr*)host->h_addr_list[idx]; if (in->s_addr == LOOPBACK_IP ) { if ( ip.s_addr == 0 ) { ip=*in; } } else { ip=*in; } } } } } ct=counter++; if ( counter > 0xFFFFFFFF ) counter=0; memset(newConferenceIdentifier,0,SIZE_CONF_ID); newConferenceIdentifier[0]='I'; newConferenceIdentifier[1]='V'; newConferenceIdentifier[2]='S'; idx=3; while ( idx < SIZE_CONF_ID ) { if ( idx < 7 ) { // 3-6 =IP newConferenceIdentifier[idx]=((ip.s_addr>>(ipx*8))&0xff); ipx++; } else if (idx < 11 ) { // 7-11 = PID newConferenceIdentifier[idx]=((pid>>(cdx*8))&0xff); cdx++; } else if (idx == 11 ) { time_t ts = time(NULL); newConferenceIdentifier[idx]=(ts&0xff); } else { // 12-16 = COUNTER newConferenceIdentifier[idx]=((ct>>(tdx*8))&0xff); tdx++; } idx++; } LM_DBG("CV generate"); int i =0; pcv[0] = '\0'; for ( i = 0 ; i < SIZE_CONF_ID ; i ++ ) { char hex[4] = {0 }; snprintf(hex,4,"%02X",newConferenceIdentifier[i]); strcat(pcv,hex); } } static unsigned int sip_param_end(const char * s, unsigned int len) { unsigned int i; for (i=0; i 0) { pcv_id.s = (char *) pvc_value, pcv_id.len = sip_param_end(pcv_id.s, len); LM_WARN("acc: parsed BUGGY P-Charging-Vector %.*s", pcv_id.len, pcv_id.s ); } return (pcv_id.s != NULL); } static int sip_get_charging_vector(struct sip_msg *msg, struct hdr_field ** hf_pcv ) { struct hdr_field *hf; char * hdrname_cstr = P_CHARGING_VECTOR; str hdrname = { hdrname_cstr , strlen( hdrname_cstr) }; /* 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; } for (hf=msg->headers; hf; hf=hf->next) { if ( hf->name.s[0] == 'P' ) { LM_INFO("acc: checking heander=%.*s\n", hf->name.len, hf->name.s ); } if ( cmp_hdrname_str(&hf->name, &hdrname) == 0) { /* * append p charging vector valus after the header name "P-Charging-Vector" and * the ": " (+2) */ char * pcv_body = pcv_buf + strlen(P_CHARGING_VECTOR) + 2; if (hf->body.len > 0) { memcpy( pcv_body, hf->body.s, hf->body.len ); pcv.len = hf->body.len + strlen(P_CHARGING_VECTOR) + 2; pcv_body[hf->body.len]= '\0'; if ( sip_parse_charging_vector( pcv_body, hf->body.len ) == 0) { LM_ERR("P-Charging-Vector header found but failed to parse value [%s].\n", pcv_body); } else { pcv_status = PCV_PARSED; } return 2; } else { pcv_id.s =0; pcv_id.len = 0; pcv_host.s = 0; pcv_host.len = 0; LM_WARN("P-Charging-Vector header found but no value.\n"); } *hf_pcv = hf; } } LM_INFO("No valid P-Charging-Vector header found.\n"); return 1; } // Remove PVC if it is in the inbound request (if it was found by sip_get_charging_vector) static int sip_remove_charging_vector(struct sip_msg *msg, struct hdr_field *hf) { struct lump* l; if ( hf != NULL ) { l=del_lump(msg, hf->name.s-msg->buf, hf->len, 0); if (l==0) { LM_ERR("no memory\n"); return -1; } return 2; } else { return 1; } } static int sip_add_charging_vector(struct sip_msg *msg) { struct lump* anchor; char * s; anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); if(anchor == 0) { LM_ERR("can't get anchor\n"); return -1; } s = (char*)pkg_malloc(pcv.len); if (!s) { LM_ERR("no pkg memory left\n"); return -1; } memcpy(s, pcv.s, pcv.len ); if (insert_new_lump_before(anchor, s, pcv.len, 0) == 0) { LM_ERR("can't insert lump\n"); pkg_free(s); return -1; } return 1; } int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2) { int generate_pcv = 0; int remove_pcv = 0; int replace_pcv = 0; int i; str flag_str; struct hdr_field * hf_pcv = NULL; pcv.len = 0; pcv_status = PCV_NONE; fixup_get_svalue(msg, (gparam_p)flags, &flag_str); // Process command flags for (i = 0; i < flag_str.len; i++) { switch (flag_str.s[i]) { case 'r': case 'R': remove_pcv = 1; break; case 'g': case 'G': generate_pcv = 1; break; case 'f': case 'F': replace_pcv = 1; generate_pcv = 1; break; default: break; } } sip_get_charging_vector(msg, &hf_pcv); /* * We need to remove the original PCV if it was present and ether * we were asked to remove it or we were asked to replace it */ if ( pcv_status == PCV_PARSED && (replace_pcv || remove_pcv) ) { i = sip_remove_charging_vector(msg, hf_pcv); if (i <= 0) return i; } /* Generate PCV if * - we were asked to generate it and it could not be obtained from the inbound packet * - or if we were asked to replace it alltogether regardless its former value */ if ( replace_pcv || (generate_pcv && pcv_status != PCV_GENERATED && pcv_status != PCV_PARSED ) ) { strcpy(pcv_buf, P_CHARGING_VECTOR); strcat(pcv_buf, ": "); char * pcv_body = pcv_buf + 19; char pcv_value[40]; /* We use the IP adress of the interface that received the message as generated-at */ if(msg->rcv.bind_address==NULL || msg->rcv.bind_address->address_str.s==NULL) { LM_ERR("No IP address for message. Failed to generate charging vector.\n"); return -2; } sip_generate_charging_vector(pcv_value); pcv.len = snprintf( pcv_body, PVC_BUF_SIZE - 19, "icid-value=%.*s; icid-generated-at=%.*s\r\n", 32, pcv_value, msg->rcv.bind_address->address_str.len, msg->rcv.bind_address->address_str.s ); pcv.len += 19; pcv_status = PCV_GENERATED; /* if generated, reparse it */ sip_parse_charging_vector( pcv_body, pcv.len-19 ); /* if it was generated, we need to send it out as a header */ LM_INFO("Generated PCV header %.*s.\n", pcv.len, pcv_buf ); i = sip_add_charging_vector(msg); if (i <= 0) { LM_ERR("Failed to add P-Charging-Vector header\n"); return i; } } current_msg_id = msg->id; return 1; } int pv_get_charging_vector(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { str pcv_pv; if ( current_msg_id != msg->id || pcv_status == PCV_NONE ) { struct hdr_field * hf_pcv = NULL; if ( sip_get_charging_vector(msg, &hf_pcv) > 0 ) { current_msg_id = msg->id; } LM_DBG("Parsed charging vector for pseudo-var \n"); } else { LM_DBG("Charging vector is in state %d for pseudo-var\n", pcv_status); } switch(pcv_status) { case PCV_GENERATED: case PCV_PARSED: switch( param->pvn.u.isname.name.n ) { case 2: pcv_pv = pcv_host; break; case 3: pcv_pv = pcv_id; break; case 1: default: pcv_pv = pcv; break; } if ( pcv_pv.len > 0 ) return pv_get_strval(msg, param, res, &pcv_pv ); else LM_WARN("No value for pseudo-var $pcv but status was %d.\n", pcv_status); break; case PCV_NONE: default: break; } return pv_get_null(msg, param, res); } int pv_parse_charging_vector_name(pv_spec_p sp, str *in) { if(sp==NULL || in==NULL || in->len<=0) return -1; switch(in->len) { case 3: if(strncmp(in->s, "all", 3)==0) sp->pvp.pvn.u.isname.name.n = 1; else goto error; break; case 5: if(strncmp(in->s, "value", 5)==0) sp->pvp.pvn.u.isname.name.n = 3; else goto error; break; case 7: if(strncmp(in->s, "genaddr", 7)==0) sp->pvp.pvn.u.isname.name.n = 2; else goto error; break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; return 0; error: LM_ERR("unknown pcv name %.*s\n", in->len, in->s); return -1; }