/*
* Copyright (c) 2015 Red Hat, Inc. All rights reserved.
*
* This program 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 program 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, see .
*
* Authors:
* Nikos Mavrogiannopoulos
*/
#define _GNU_SOURCE /* asprintf */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ipcalc.h"
#ifdef USE_GEOIP
# include
# include
#define GEOIP_SILENCE 16 /* fix libgeoip < 1.6.3 */
# ifdef USE_RUNTIME_LINKING
# include
typedef void (*_GeoIP_setup_dbfilename_func)(void);
typedef GeoIP * (*GeoIP_open_type_func)(int type, int flags);
typedef const char * (*GeoIP_country_name_by_id_func)(GeoIP * gi, int id);
typedef void (*GeoIP_delete_func)(GeoIP * gi);
typedef GeoIPRecord * (*GeoIP_record_by_ipnum_func)(GeoIP * gi, unsigned long ipnum);
typedef int (*GeoIP_id_by_ipnum_func)(GeoIP * gi, unsigned long ipnum);
typedef int (*GeoIP_id_by_ipnum_v6_func)(GeoIP * gi, geoipv6_t ipnum);
typedef GeoIPRecord *(*GeoIP_record_by_ipnum_v6_func)(GeoIP * gi, geoipv6_t ipnum);
typedef const char *(*GeoIP_code_by_id_func)(int id);
static _GeoIP_setup_dbfilename_func p_GeoIP_setup_dbfilename;
static GeoIP_open_type_func pGeoIP_open_type;
static GeoIP_country_name_by_id_func pGeoIP_country_name_by_id;
static GeoIP_code_by_id_func pGeoIP_code_by_id;
static GeoIP_delete_func pGeoIP_delete;
static GeoIP_record_by_ipnum_func pGeoIP_record_by_ipnum;
static GeoIP_id_by_ipnum_func pGeoIP_id_by_ipnum;
static GeoIP_id_by_ipnum_v6_func pGeoIP_id_by_ipnum_v6;
static GeoIP_record_by_ipnum_v6_func pGeoIP_record_by_ipnum_v6;
#define LIBNAME LIBPATH"/libGeoIP.so.1"
int geo_setup(void)
{
static void *ld = NULL;
static int ret = 0;
static char err[256] = {0};
if (ld != NULL || ret != 0) {
if (!beSilent && err[0] != 0) {
fprintf(stderr, "%s", err);
}
return ret;
}
ld = dlopen(LIBNAME, RTLD_LAZY);
if (ld == NULL) {
snprintf(err, sizeof(err), "ipcalc: could not open %s\n", LIBNAME);
ret = -1;
goto exit;
}
p_GeoIP_setup_dbfilename = dlsym(ld, "_GeoIP_setup_dbfilename");
pGeoIP_open_type = dlsym(ld, "GeoIP_open_type");
pGeoIP_country_name_by_id = dlsym(ld, "GeoIP_country_name_by_id");
pGeoIP_delete = dlsym(ld, "GeoIP_delete");
pGeoIP_record_by_ipnum = dlsym(ld, "GeoIP_record_by_ipnum");
pGeoIP_id_by_ipnum = dlsym(ld, "GeoIP_id_by_ipnum");
pGeoIP_id_by_ipnum_v6 = dlsym(ld, "GeoIP_id_by_ipnum_v6");
pGeoIP_record_by_ipnum_v6 = dlsym(ld, "GeoIP_record_by_ipnum_v6");
pGeoIP_code_by_id = dlsym(ld, "GeoIP_code_by_id");
if (pGeoIP_open_type == NULL || pGeoIP_country_name_by_id == NULL ||
pGeoIP_delete == NULL || pGeoIP_record_by_ipnum == NULL ||
pGeoIP_id_by_ipnum == NULL || pGeoIP_id_by_ipnum_v6 == NULL ||
pGeoIP_record_by_ipnum_v6 == NULL) {
snprintf(err, sizeof(err), "ipcalc: could not find symbols in libGeoIP\n");
ret = -1;
goto exit;
}
ret = 0;
exit:
return ret;
}
# else
extern void _GeoIP_setup_dbfilename(void);
# define p_GeoIP_setup_dbfilename _GeoIP_setup_dbfilename
# define pGeoIP_open_type GeoIP_open_type
# define pGeoIP_country_name_by_id GeoIP_country_name_by_id
# define pGeoIP_delete GeoIP_delete
# define pGeoIP_record_by_ipnum GeoIP_record_by_ipnum
# define pGeoIP_id_by_ipnum GeoIP_id_by_ipnum
# define pGeoIP_id_by_ipnum_v6 GeoIP_id_by_ipnum_v6
# define pGeoIP_record_by_ipnum_v6 GeoIP_record_by_ipnum_v6
# define pGeoIP_code_by_id GeoIP_code_by_id
# endif
static void geo_ipv4_lookup(struct in_addr ip, char **country, char **ccode, char **city, char **coord)
{
GeoIP *gi;
GeoIPRecord *gir;
int country_id;
const char *p;
if (geo_setup() != 0)
return;
ip.s_addr = ntohl(ip.s_addr);
p_GeoIP_setup_dbfilename();
gi = pGeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
country_id = pGeoIP_id_by_ipnum(gi, ip.s_addr);
if (country_id < 0) {
return;
}
p = pGeoIP_country_name_by_id(gi, country_id);
if (p)
*country = safe_strdup(p);
p = pGeoIP_code_by_id(country_id);
if (p)
*ccode = safe_strdup(p);
pGeoIP_delete(gi);
}
gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
gir = pGeoIP_record_by_ipnum(gi, ip.s_addr);
if (gir && gir->city)
*city = safe_strdup(gir->city);
if (gir && gir->longitude != 0 && gir->longitude != 0)
safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
pGeoIP_delete(gi);
} else {
gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV0, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
gir = pGeoIP_record_by_ipnum(gi, ip.s_addr);
if (gir && gir->city)
*city = safe_strdup(gir->city);
if (gir && gir->longitude != 0 && gir->longitude != 0)
safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
pGeoIP_delete(gi);
}
}
return;
}
static void geo_ipv6_lookup(struct in6_addr *ip, char **country, char **ccode, char **city, char **coord)
{
GeoIP *gi;
GeoIPRecord *gir;
int country_id;
const char *p;
if (geo_setup() != 0)
return;
p_GeoIP_setup_dbfilename();
gi = pGeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
country_id = pGeoIP_id_by_ipnum_v6(gi, (geoipv6_t)*ip);
if (country_id < 0) {
return;
}
p = pGeoIP_country_name_by_id(gi, country_id);
if (p)
*country = safe_strdup(p);
p = pGeoIP_code_by_id(country_id);
if (p)
*ccode = safe_strdup(p);
pGeoIP_delete(gi);
}
gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
gir = pGeoIP_record_by_ipnum_v6(gi, (geoipv6_t)*ip);
if (gir && gir->city)
*city = safe_strdup(gir->city);
if (gir && gir->longitude != 0 && gir->longitude != 0)
safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
pGeoIP_delete(gi);
} else {
gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV0_V6, GEOIP_STANDARD | GEOIP_SILENCE);
if (gi != NULL) {
gi->charset = GEOIP_CHARSET_UTF8;
gir = pGeoIP_record_by_ipnum_v6(gi, (geoipv6_t)*ip);
if (gir && gir->city)
*city = safe_strdup(gir->city);
if (gir && gir->longitude != 0 && gir->longitude != 0)
safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
pGeoIP_delete(gi);
}
}
return;
}
void geo_ip_lookup(const char *ip, char **country, char **ccode, char **city, char **coord)
{
struct in_addr ipv4;
struct in6_addr ipv6;
if (inet_pton(AF_INET, ip, &ipv4) == 1) {
geo_ipv4_lookup(ipv4, country, ccode, city, coord);
} else if (inet_pton(AF_INET6, ip, &ipv6) == 1) {
geo_ipv6_lookup(&ipv6, country, ccode, city, coord);
}
return;
}
#endif