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/geoip/geoip_pv.c

406 lines
8.9 KiB

/**
* $Id$
*
* Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "../../dprint.h"
#include "../../hashes.h"
#include "../../pvar.h"
#include "geoip_pv.h"
typedef struct _sr_geoip_record {
GeoIPRecord *record;
char *time_zone;
char *region_name;
char **range;
char latitude[16];
char longitude[16];
char tomatch[256];
int flags;
} sr_geoip_record_t;
typedef struct _sr_geoip_item {
str pvclass;
unsigned int hashid;
sr_geoip_record_t r;
struct _sr_geoip_item *next;
} sr_geoip_item_t;
typedef struct _geoip_pv {
sr_geoip_item_t *item;
int type;
} geoip_pv_t;
static GeoIP *_handle_GeoIP = NULL;
static sr_geoip_item_t *_sr_geoip_list = NULL;
sr_geoip_record_t *sr_geoip_get_record(str *name)
{
sr_geoip_item_t *it = NULL;
unsigned int hashid = 0;
hashid = get_hash1_raw(name->s, name->len);
it = _sr_geoip_list;
while(it!=NULL)
{
if(it->hashid==hashid && it->pvclass.len == name->len
&& strncmp(it->pvclass.s, name->s, name->len)==0)
return &it->r;
it = it->next;
}
return NULL;
}
sr_geoip_item_t *sr_geoip_add_item(str *name)
{
sr_geoip_item_t *it = NULL;
unsigned int hashid = 0;
hashid = get_hash1_raw(name->s, name->len);
it = _sr_geoip_list;
while(it!=NULL)
{
if(it->hashid==hashid && it->pvclass.len == name->len
&& strncmp(it->pvclass.s, name->s, name->len)==0)
return it;
it = it->next;
}
/* add new */
it = (sr_geoip_item_t*)pkg_malloc(sizeof(sr_geoip_item_t));
if(it==NULL)
{
LM_ERR("no more pkg\n");
return NULL;
}
memset(it, 0, sizeof(sr_geoip_item_t));
it->pvclass.s = (char*)pkg_malloc(name->len+1);
if(it->pvclass.s==NULL)
{
LM_ERR("no more pkg.\n");
return NULL;
}
memcpy(it->pvclass.s, name->s, name->len);
it->pvclass.s[name->len] = '\0';
it->pvclass.len = name->len;
it->hashid = hashid;
it->next = _sr_geoip_list;
_sr_geoip_list = it;
return it;
}
int pv_parse_geoip_name(pv_spec_p sp, str *in)
{
geoip_pv_t *gpv=NULL;
char *p;
str pvc;
str pvs;
if(sp==NULL || in==NULL || in->len<=0)
return -1;
gpv = (geoip_pv_t*)pkg_malloc(sizeof(geoip_pv_t));
if(gpv==NULL)
return -1;
memset(gpv, 0, sizeof(geoip_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;
LM_DBG("geoip [%.*s] - key [%.*s]\n", pvc.len, pvc.s,
pvs.len, pvs.s);
gpv->item = sr_geoip_add_item(&pvc);
if(gpv->item==NULL)
goto error;
switch(pvs.len)
{
case 2:
if(strncmp(pvs.s, "cc", 2)==0)
gpv->type = 0;
else if(strncmp(pvs.s, "tz", 2)==0)
gpv->type = 1;
else goto error;
break;
case 3:
if(strncmp(pvs.s, "zip", 3)==0)
gpv->type = 2;
else if(strncmp(pvs.s, "lat", 3)==0)
gpv->type = 3;
else if(strncmp(pvs.s, "lon", 3)==0)
gpv->type = 4;
else if(strncmp(pvs.s, "dma", 3)==0)
gpv->type = 5;
else if(strncmp(pvs.s, "ips", 3)==0)
gpv->type = 6;
else if(strncmp(pvs.s, "ipe", 3)==0)
gpv->type = 7;
else goto error;
break;
case 4:
if(strncmp(pvs.s, "city", 4)==0)
gpv->type = 8;
else if(strncmp(pvs.s, "area", 4)==0)
gpv->type = 9;
else if(strncmp(pvs.s, "regc", 4)==0)
gpv->type = 10;
else if(strncmp(pvs.s, "regn", 4)==0)
gpv->type = 11;
else goto error;
break;
case 5:
if(strncmp(pvs.s, "metro", 5)==0)
gpv->type = 12;
else goto error;
break;
default:
goto error;
}
sp->pvp.pvn.u.dname = (void*)gpv;
sp->pvp.pvn.type = PV_NAME_OTHER;
return 0;
error:
LM_ERR("error at PV geoip name: %.*s\n", in->len, in->s);
return -1;
}
int pv_geoip_get_strzval(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res, char *sval)
{
str s;
if(sval==NULL)
return pv_get_null(msg, param, res);
s.s = sval;
s.len = strlen(s.s);
return pv_get_strval(msg, param, res, &s);
}
int pv_get_geoip(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res)
{
geoip_pv_t *gpv;
if(msg==NULL || param==NULL)
return -1;
gpv = (geoip_pv_t*)param->pvn.u.dname;
if(gpv==NULL)
return -1;
switch(gpv->type)
{
case 1: /* tz */
if(gpv->item->r.time_zone==NULL)
{
if(gpv->item->r.flags&1)
return pv_get_null(msg, param, res);
gpv->item->r.time_zone
= (char*)GeoIP_time_zone_by_country_and_region(
gpv->item->r.record->country_code,
gpv->item->r.record->region);
gpv->item->r.flags |= 1;
}
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.time_zone);
case 2: /* zip */
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.record->postal_code);
case 3: /* lat */
if((gpv->item->r.flags&2)==0)
{
snprintf(gpv->item->r.latitude, 15, "%f",
gpv->item->r.record->latitude);
gpv->item->r.flags |= 2;
}
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.latitude);
case 4: /* lon */
if((gpv->item->r.flags&4)==0)
{
snprintf(gpv->item->r.longitude, 15, "%f",
gpv->item->r.record->longitude);
gpv->item->r.flags |= 4;
}
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.longitude);
case 5: /* dma */
return pv_get_sintval(msg, param, res,
gpv->item->r.record->dma_code);
case 6: /* ips */
case 7: /* ipe */
if((gpv->item->r.flags&8)==0)
{
gpv->item->r.range = GeoIP_range_by_ip(_handle_GeoIP,
gpv->item->r.tomatch);
gpv->item->r.flags |= 8;
}
if(gpv->item->r.range==NULL)
return pv_get_null(msg, param, res);
if(gpv->type==6)
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.range[0]);
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.range[1]);
case 8: /* city */
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.record->city);
case 9: /* area */
return pv_get_sintval(msg, param, res,
gpv->item->r.record->area_code);
case 10: /* regc */
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.record->region);
case 11: /* regn */
if((gpv->item->r.flags&16)==0)
{
gpv->item->r.region_name
= (char*)GeoIP_region_name_by_code(
gpv->item->r.record->country_code,
gpv->item->r.record->region);
gpv->item->r.flags |= 16;
}
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.region_name);
case 12: /* metro */
return pv_get_sintval(msg, param, res,
gpv->item->r.record->metro_code);
default: /* cc */
return pv_geoip_get_strzval(msg, param, res,
gpv->item->r.record->country_code);
}
}
int geoip_init_pv(char *path)
{
_handle_GeoIP = GeoIP_open(path, GEOIP_MMAP_CACHE);
if(_handle_GeoIP==NULL)
{
LM_ERR("cannot open GeoIP database file at: %s\n", path);
return -1;
}
return 0;
}
void geoip_destroy_list(void)
{
}
void geoip_destroy_pv(void)
{
if(_handle_GeoIP!=NULL)
{
GeoIP_delete(_handle_GeoIP);
_handle_GeoIP=NULL;
}
}
void geoip_pv_reset(str *name)
{
sr_geoip_record_t *gr = NULL;
gr = sr_geoip_get_record(name);
if(gr==NULL)
return;
if(gr->range!=NULL)
GeoIP_range_by_ip_delete(gr->range);
if(gr->record!=NULL)
GeoIPRecord_delete(gr->record);
memset(gr, 0, sizeof(struct _sr_geoip_record));
}
int geoip_update_pv(str *tomatch, str *name)
{
sr_geoip_record_t *gr = NULL;
if(tomatch->len>255)
{
LM_DBG("target too long (max 255): %s\n", tomatch->s);
return -3;
}
gr = sr_geoip_get_record(name);
if(gr==NULL)
{
LM_DBG("container not found: %s\n", tomatch->s);
return - 4;
}
strncpy(gr->tomatch, tomatch->s, tomatch->len);
tomatch->s[tomatch->len] = '\0';
gr->record = GeoIP_record_by_name(_handle_GeoIP,
(const char*)gr->tomatch);
LM_DBG("attempt to match: %s\n", gr->tomatch);
if (gr->record == NULL)
{
LM_DBG("no match for: %s\n", gr->tomatch);
return -2;
}
LM_DBG("geoip PV updated for: %s\n", gr->tomatch);
return 1;
}