Blame ipcalc-geoip.c

Packit 71e9d6
/*
Packit 71e9d6
 * Copyright (c) 2015 Red Hat, Inc. All rights reserved.
Packit 71e9d6
 *
Packit 71e9d6
 * This program is free software; you can redistribute it and/or modify
Packit 71e9d6
 * it under the terms of the GNU General Public License, version 2,
Packit 71e9d6
 * as published by the Free Software Foundation.
Packit 71e9d6
 *
Packit 71e9d6
 * This program is distributed in the hope that it will be useful,
Packit 71e9d6
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 71e9d6
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 71e9d6
 * GNU General Public License for more details.
Packit 71e9d6
 *
Packit 71e9d6
 * You should have received a copy of the GNU General Public License
Packit 71e9d6
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 71e9d6
 *
Packit 71e9d6
 * Authors:
Packit 71e9d6
 *   Nikos Mavrogiannopoulos <nmav@redhat.com>
Packit 71e9d6
 */
Packit 71e9d6
Packit 71e9d6
#define _GNU_SOURCE		/* asprintf */
Packit 71e9d6
#include <ctype.h>
Packit 71e9d6
#include <stdio.h>
Packit 71e9d6
#include <stdlib.h>
Packit 71e9d6
#include <string.h>
Packit 71e9d6
#include <sys/socket.h>
Packit 71e9d6
#include <sys/types.h>
Packit 71e9d6
#include <netinet/in.h>
Packit 71e9d6
#include <arpa/inet.h>
Packit 71e9d6
#include <stdarg.h>
Packit 71e9d6
#include "ipcalc.h"
Packit 71e9d6
Packit 71e9d6
#ifdef USE_GEOIP
Packit 71e9d6
Packit 71e9d6
# include <GeoIP.h>
Packit 71e9d6
# include <GeoIPCity.h>
Packit 71e9d6
Packit 71e9d6
#define GEOIP_SILENCE 16	/* fix libgeoip < 1.6.3 */
Packit 71e9d6
Packit 71e9d6
# ifdef USE_RUNTIME_LINKING
Packit 71e9d6
#  include <dlfcn.h>
Packit 71e9d6
Packit 71e9d6
typedef void (*_GeoIP_setup_dbfilename_func)(void);
Packit 71e9d6
typedef GeoIP * (*GeoIP_open_type_func)(int type, int flags);
Packit 71e9d6
typedef const char * (*GeoIP_country_name_by_id_func)(GeoIP * gi, int id);
Packit 71e9d6
typedef void (*GeoIP_delete_func)(GeoIP * gi);
Packit 71e9d6
typedef GeoIPRecord * (*GeoIP_record_by_ipnum_func)(GeoIP * gi, unsigned long ipnum);
Packit 71e9d6
typedef int (*GeoIP_id_by_ipnum_func)(GeoIP * gi, unsigned long ipnum);
Packit 71e9d6
typedef int (*GeoIP_id_by_ipnum_v6_func)(GeoIP * gi, geoipv6_t ipnum);
Packit 71e9d6
typedef GeoIPRecord *(*GeoIP_record_by_ipnum_v6_func)(GeoIP * gi, geoipv6_t ipnum);
Packit 71e9d6
typedef const char *(*GeoIP_code_by_id_func)(int id);
Packit 71e9d6
Packit 71e9d6
static _GeoIP_setup_dbfilename_func p_GeoIP_setup_dbfilename;
Packit 71e9d6
static GeoIP_open_type_func pGeoIP_open_type;
Packit 71e9d6
static GeoIP_country_name_by_id_func pGeoIP_country_name_by_id;
Packit 71e9d6
static GeoIP_code_by_id_func pGeoIP_code_by_id;
Packit 71e9d6
static GeoIP_delete_func pGeoIP_delete;
Packit 71e9d6
static GeoIP_record_by_ipnum_func pGeoIP_record_by_ipnum;
Packit 71e9d6
static GeoIP_id_by_ipnum_func pGeoIP_id_by_ipnum;
Packit 71e9d6
static GeoIP_id_by_ipnum_v6_func pGeoIP_id_by_ipnum_v6;
Packit 71e9d6
static GeoIP_record_by_ipnum_v6_func pGeoIP_record_by_ipnum_v6;
Packit 71e9d6
Packit 71e9d6
#define LIBNAME LIBPATH"/libGeoIP.so.1"
Packit 71e9d6
Packit 71e9d6
int geo_setup(void)
Packit 71e9d6
{
Packit 71e9d6
	static void *ld = NULL;
Packit 71e9d6
	static int ret = 0;
Packit 71e9d6
	static char err[256] = {0};
Packit 71e9d6
Packit 71e9d6
	if (ld != NULL || ret != 0) {
Packit 71e9d6
	    	if (!beSilent && err[0] != 0) {
Packit 71e9d6
	    		fprintf(stderr, "%s", err);
Packit 71e9d6
		}
Packit 71e9d6
		return ret;
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	ld = dlopen(LIBNAME, RTLD_LAZY);
Packit 71e9d6
	if (ld == NULL) {
Packit 71e9d6
		snprintf(err, sizeof(err), "ipcalc: could not open %s\n", LIBNAME);
Packit 71e9d6
		ret = -1;
Packit 71e9d6
		goto exit;
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	p_GeoIP_setup_dbfilename = dlsym(ld, "_GeoIP_setup_dbfilename");
Packit 71e9d6
Packit 71e9d6
	pGeoIP_open_type = dlsym(ld, "GeoIP_open_type");
Packit 71e9d6
	pGeoIP_country_name_by_id = dlsym(ld, "GeoIP_country_name_by_id");
Packit 71e9d6
	pGeoIP_delete = dlsym(ld, "GeoIP_delete");
Packit 71e9d6
	pGeoIP_record_by_ipnum = dlsym(ld, "GeoIP_record_by_ipnum");
Packit 71e9d6
	pGeoIP_id_by_ipnum = dlsym(ld, "GeoIP_id_by_ipnum");
Packit 71e9d6
	pGeoIP_id_by_ipnum_v6 = dlsym(ld, "GeoIP_id_by_ipnum_v6");
Packit 71e9d6
	pGeoIP_record_by_ipnum_v6 = dlsym(ld, "GeoIP_record_by_ipnum_v6");
Packit 71e9d6
	pGeoIP_code_by_id = dlsym(ld, "GeoIP_code_by_id");
Packit 71e9d6
Packit 71e9d6
	if (pGeoIP_open_type == NULL || pGeoIP_country_name_by_id == NULL ||
Packit 71e9d6
	    pGeoIP_delete == NULL || pGeoIP_record_by_ipnum == NULL ||
Packit 71e9d6
	    pGeoIP_id_by_ipnum == NULL || pGeoIP_id_by_ipnum_v6 == NULL ||
Packit 71e9d6
	    pGeoIP_record_by_ipnum_v6 == NULL) {
Packit 71e9d6
		snprintf(err, sizeof(err), "ipcalc: could not find symbols in libGeoIP\n");
Packit 71e9d6
	    	ret = -1;
Packit 71e9d6
	    	goto exit;
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	ret = 0;
Packit 71e9d6
 exit:
Packit 71e9d6
	return ret;
Packit 71e9d6
}
Packit 71e9d6
Packit 71e9d6
# else
Packit 71e9d6
Packit 71e9d6
extern void _GeoIP_setup_dbfilename(void);
Packit 71e9d6
#  define p_GeoIP_setup_dbfilename _GeoIP_setup_dbfilename
Packit 71e9d6
#  define pGeoIP_open_type GeoIP_open_type
Packit 71e9d6
#  define pGeoIP_country_name_by_id GeoIP_country_name_by_id
Packit 71e9d6
#  define pGeoIP_delete GeoIP_delete
Packit 71e9d6
#  define pGeoIP_record_by_ipnum GeoIP_record_by_ipnum
Packit 71e9d6
#  define pGeoIP_id_by_ipnum GeoIP_id_by_ipnum
Packit 71e9d6
#  define pGeoIP_id_by_ipnum_v6 GeoIP_id_by_ipnum_v6
Packit 71e9d6
#  define pGeoIP_record_by_ipnum_v6 GeoIP_record_by_ipnum_v6
Packit 71e9d6
#  define pGeoIP_code_by_id GeoIP_code_by_id
Packit 71e9d6
# endif
Packit 71e9d6
Packit 71e9d6
static void geo_ipv4_lookup(struct in_addr ip, char **country, char **ccode, char **city, char **coord)
Packit 71e9d6
{
Packit 71e9d6
	GeoIP *gi;
Packit 71e9d6
	GeoIPRecord *gir;
Packit 71e9d6
	int country_id;
Packit 71e9d6
	const char *p;
Packit 71e9d6
Packit 71e9d6
	if (geo_setup() != 0)
Packit 71e9d6
		return;
Packit 71e9d6
Packit 71e9d6
	ip.s_addr = ntohl(ip.s_addr);
Packit 71e9d6
Packit 71e9d6
	p_GeoIP_setup_dbfilename();
Packit 71e9d6
Packit 71e9d6
	gi = pGeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
	if (gi != NULL) {
Packit 71e9d6
		gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
		country_id = pGeoIP_id_by_ipnum(gi, ip.s_addr);
Packit 71e9d6
		if (country_id < 0) {
Packit 71e9d6
			return;
Packit 71e9d6
		}
Packit 71e9d6
		p = pGeoIP_country_name_by_id(gi, country_id);
Packit 71e9d6
		if (p)
Packit 71e9d6
			*country = safe_strdup(p);
Packit 71e9d6
Packit 71e9d6
		p = pGeoIP_code_by_id(country_id);
Packit 71e9d6
		if (p)
Packit 71e9d6
			*ccode = safe_strdup(p);
Packit 71e9d6
Packit 71e9d6
		pGeoIP_delete(gi);
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
	if (gi != NULL) {
Packit 71e9d6
		gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
		gir = pGeoIP_record_by_ipnum(gi, ip.s_addr);
Packit 71e9d6
Packit 71e9d6
		if (gir && gir->city)
Packit 71e9d6
			*city = safe_strdup(gir->city);
Packit 71e9d6
Packit 71e9d6
		if (gir && gir->longitude != 0 && gir->longitude != 0)
Packit 71e9d6
			safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
Packit 71e9d6
Packit 71e9d6
		pGeoIP_delete(gi);
Packit 71e9d6
	} else {
Packit 71e9d6
		gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV0, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
		if (gi != NULL) {
Packit 71e9d6
			gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
			gir = pGeoIP_record_by_ipnum(gi, ip.s_addr);
Packit 71e9d6
Packit 71e9d6
			if (gir && gir->city)
Packit 71e9d6
				*city = safe_strdup(gir->city);
Packit 71e9d6
Packit 71e9d6
			if (gir && gir->longitude != 0 && gir->longitude != 0)
Packit 71e9d6
				safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
Packit 71e9d6
Packit 71e9d6
			pGeoIP_delete(gi);
Packit 71e9d6
		}
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	return;
Packit 71e9d6
}
Packit 71e9d6
Packit 71e9d6
static void geo_ipv6_lookup(struct in6_addr *ip, char **country, char **ccode, char **city, char **coord)
Packit 71e9d6
{
Packit 71e9d6
	GeoIP *gi;
Packit 71e9d6
	GeoIPRecord *gir;
Packit 71e9d6
	int country_id;
Packit 71e9d6
	const char *p;
Packit 71e9d6
Packit 71e9d6
	if (geo_setup() != 0)
Packit 71e9d6
		return;
Packit 71e9d6
Packit 71e9d6
	p_GeoIP_setup_dbfilename();
Packit 71e9d6
Packit 71e9d6
	gi = pGeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
	if (gi != NULL) {
Packit 71e9d6
		gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
		country_id = pGeoIP_id_by_ipnum_v6(gi, (geoipv6_t)*ip);
Packit 71e9d6
		if (country_id < 0) {
Packit 71e9d6
			return;
Packit 71e9d6
		}
Packit 71e9d6
		p = pGeoIP_country_name_by_id(gi, country_id);
Packit 71e9d6
		if (p)
Packit 71e9d6
			*country = safe_strdup(p);
Packit 71e9d6
Packit 71e9d6
		p = pGeoIP_code_by_id(country_id);
Packit 71e9d6
		if (p)
Packit 71e9d6
			*ccode = safe_strdup(p);
Packit 71e9d6
Packit 71e9d6
		pGeoIP_delete(gi);
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
	if (gi != NULL) {
Packit 71e9d6
		gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
		gir = pGeoIP_record_by_ipnum_v6(gi, (geoipv6_t)*ip);
Packit 71e9d6
Packit 71e9d6
		if (gir && gir->city)
Packit 71e9d6
			*city = safe_strdup(gir->city);
Packit 71e9d6
Packit 71e9d6
		if (gir && gir->longitude != 0 && gir->longitude != 0)
Packit 71e9d6
			safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
Packit 71e9d6
Packit 71e9d6
		pGeoIP_delete(gi);
Packit 71e9d6
	} else {
Packit 71e9d6
		gi = pGeoIP_open_type(GEOIP_CITY_EDITION_REV0_V6, GEOIP_STANDARD | GEOIP_SILENCE);
Packit 71e9d6
		if (gi != NULL) {
Packit 71e9d6
			gi->charset = GEOIP_CHARSET_UTF8;
Packit 71e9d6
Packit 71e9d6
			gir = pGeoIP_record_by_ipnum_v6(gi, (geoipv6_t)*ip);
Packit 71e9d6
Packit 71e9d6
			if (gir && gir->city)
Packit 71e9d6
				*city = safe_strdup(gir->city);
Packit 71e9d6
Packit 71e9d6
			if (gir && gir->longitude != 0 && gir->longitude != 0)
Packit 71e9d6
				safe_asprintf(coord, "%f,%f", gir->latitude, gir->longitude);
Packit 71e9d6
Packit 71e9d6
			pGeoIP_delete(gi);
Packit 71e9d6
		}
Packit 71e9d6
	}
Packit 71e9d6
Packit 71e9d6
	return;
Packit 71e9d6
}
Packit 71e9d6
Packit 71e9d6
void geo_ip_lookup(const char *ip, char **country, char **ccode, char **city, char **coord)
Packit 71e9d6
{
Packit 71e9d6
        struct in_addr ipv4;
Packit 71e9d6
        struct in6_addr ipv6;
Packit 71e9d6
        if (inet_pton(AF_INET, ip, &ipv4) == 1) {
Packit 71e9d6
              geo_ipv4_lookup(ipv4, country, ccode, city, coord);
Packit 71e9d6
        } else if (inet_pton(AF_INET6, ip, &ipv6) == 1) {
Packit 71e9d6
              geo_ipv6_lookup(&ipv6, country, ccode, city, coord);
Packit 71e9d6
        }
Packit 71e9d6
        return;
Packit 71e9d6
}
Packit 71e9d6
Packit 71e9d6
#endif