Blame ipcalc-maxmind.c

Packit 71e9d6
#define _GNU_SOURCE
Packit 71e9d6
#include <sys/socket.h>
Packit 71e9d6
#include <stdio.h>
Packit 71e9d6
#include <stdlib.h>
Packit 71e9d6
#include <string.h>
Packit 71e9d6
#include <netinet/in.h>
Packit 71e9d6
Packit 71e9d6
#include "ipcalc.h"
Packit 71e9d6
Packit 71e9d6
#ifdef USE_MAXMIND
Packit 71e9d6
#include <maxminddb.h>
Packit 71e9d6
Packit 71e9d6
#ifndef MAXMINDDB_LOCATION_COUNTRY
Packit 71e9d6
#define MAXMINDDB_LOCATION_COUNTRY "/usr/share/GeoIP/GeoLite2-Country.mmdb"
Packit 71e9d6
#endif
Packit 71e9d6
Packit 71e9d6
#ifndef MAXMINDDB_LOCATION_CITY
Packit 71e9d6
#define MAXMINDDB_LOCATION_CITY "/usr/share/GeoIP/GeoLite2-City.mmdb"
Packit 71e9d6
#endif
Packit 71e9d6
Packit 71e9d6
#ifdef USE_RUNTIME_LINKING
Packit 71e9d6
# include <dlfcn.h>
Packit 71e9d6
# define LIBNAME LIBPATH"/libmaxminddb.so.0"
Packit 71e9d6
Packit 71e9d6
typedef int (*MMDB_open_fn)
Packit 71e9d6
    (const char *const filename,
Packit 71e9d6
    uint32_t flags,
Packit 71e9d6
    MMDB_s *const mmdb);
Packit 71e9d6
typedef void (*MMDB_close_fn)
Packit 71e9d6
    (MMDB_s *const mmdb);
Packit 71e9d6
typedef MMDB_lookup_result_s (*MMDB_lookup_string_fn)
Packit 71e9d6
    (MMDB_s *const mmdb,
Packit 71e9d6
    const char *const ipstr,
Packit 71e9d6
    int *const gai_error,
Packit 71e9d6
    int *const mmdb_error);
Packit 71e9d6
typedef int (*MMDB_get_value_fn)
Packit 71e9d6
    (MMDB_entry_s *const start,
Packit 71e9d6
    MMDB_entry_data_s *const entry_data,
Packit 71e9d6
    ...);
Packit 71e9d6
Packit 71e9d6
static MMDB_close_fn          pMMDB_close;
Packit 71e9d6
static MMDB_get_value_fn      pMMDB_get_value;
Packit 71e9d6
static MMDB_lookup_string_fn  pMMDB_lookup_string;
Packit 71e9d6
static MMDB_open_fn           pMMDB_open;
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
    pMMDB_close         = dlsym(ld, "MMDB_close");
Packit 71e9d6
    pMMDB_get_value     = dlsym(ld, "MMDB_get_value");
Packit 71e9d6
    pMMDB_lookup_string = dlsym(ld, "MMDB_lookup_string");
Packit 71e9d6
    pMMDB_open          = dlsym(ld, "MMDB_open");
Packit 71e9d6
Packit 71e9d6
    if(pMMDB_close == NULL ||
Packit 71e9d6
       pMMDB_get_value == NULL ||
Packit 71e9d6
       pMMDB_lookup_string == NULL ||
Packit 71e9d6
       pMMDB_open == NULL) {
Packit 71e9d6
        snprintf(err, sizeof(err), "ipcalc: could not find symbols in libmaxmind\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
#define pMMDB_close         MMDB_close
Packit 71e9d6
#define pMMDB_get_value     MMDB_get_value
Packit 71e9d6
#define pMMDB_lookup_string MMDB_lookup_string
Packit 71e9d6
#define pMMDB_open          MMDB_open
Packit 71e9d6
#endif
Packit 71e9d6
Packit 71e9d6
void process_result_from_mmdb_lookup(MMDB_entry_data_s *entry_data, int status, char **output)
Packit 71e9d6
{
Packit 71e9d6
    if (MMDB_SUCCESS == status) {
Packit 71e9d6
        if (entry_data->has_data) {
Packit 71e9d6
            if (entry_data->type == MMDB_DATA_TYPE_UTF8_STRING) {
Packit 71e9d6
                *output = (char *) calloc(entry_data->data_size + 1, sizeof(char));
Packit 71e9d6
                if (NULL != *output) {
Packit 71e9d6
                    memcpy(*output, entry_data->utf8_string, entry_data->data_size);
Packit 71e9d6
                } else {
Packit 71e9d6
                    fprintf(stderr, "Memory allocation failure line %d\n", __LINE__);
Packit 71e9d6
                }
Packit 71e9d6
            }
Packit 71e9d6
        }
Packit 71e9d6
    }
Packit 71e9d6
    /* Else fail silently */
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
    MMDB_s mmdb;
Packit 71e9d6
    MMDB_entry_data_s entry_data;
Packit 71e9d6
    int gai_error, mmdb_error, status, coordinates=0;
Packit 71e9d6
    double latitude, longitude;
Packit 71e9d6
rpm-build fe32c5
    if (geo_setup() != 0)
rpm-build fe32c5
        return;
rpm-build fe32c5
Packit 71e9d6
    /* Open the system maxmind database with countries */
Packit 71e9d6
    status = pMMDB_open(MAXMINDDB_LOCATION_COUNTRY, MMDB_MODE_MMAP, &mmdb);
Packit 71e9d6
    if (MMDB_SUCCESS == status) {
Packit 71e9d6
        /* Lookup IP address in the database */
Packit 71e9d6
        MMDB_lookup_result_s result = pMMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error);
Packit 71e9d6
        if (MMDB_SUCCESS == mmdb_error) { 
Packit 71e9d6
            /* If the lookup was successfull and an entry was found */
Packit 71e9d6
            if (result.found_entry) {
Packit 71e9d6
                memset(&entry_data, 0, sizeof(MMDB_entry_data_s));
Packit 71e9d6
                /* Travel the path in the tree like structure of the MMDB and store the value if found */
Packit 71e9d6
                status = pMMDB_get_value(&result.entry, &entry_data, "country", "names", "en", NULL);
Packit 71e9d6
                process_result_from_mmdb_lookup(&entry_data, status, country);
Packit 71e9d6
                memset(&entry_data, 0, sizeof(MMDB_entry_data_s));
Packit 71e9d6
                status = pMMDB_get_value(&result.entry, &entry_data, "country", "iso_code", NULL);
Packit 71e9d6
                process_result_from_mmdb_lookup(&entry_data, status, ccode);
Packit 71e9d6
            }
Packit 71e9d6
        }
Packit 71e9d6
        /* Else fail silently */
Packit 71e9d6
        pMMDB_close(&mmdb);
Packit 71e9d6
    }
Packit 71e9d6
    /* Else fail silently */
Packit 71e9d6
Packit 71e9d6
    /* Open the system maxmind database with cities - which actually does not contain names of the cities */
Packit 71e9d6
    status = pMMDB_open(MAXMINDDB_LOCATION_CITY, MMDB_MODE_MMAP, &mmdb);
Packit 71e9d6
    if (MMDB_SUCCESS == status) {
Packit 71e9d6
        /* Lookup IP address in the database */
Packit 71e9d6
        MMDB_lookup_result_s result = pMMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error);
Packit 71e9d6
        if (MMDB_SUCCESS == mmdb_error) { 
Packit 71e9d6
            /* If the lookup was successfull and an entry was found */
Packit 71e9d6
            if (result.found_entry) {
Packit 71e9d6
                memset(&entry_data, 0, sizeof(MMDB_entry_data_s));
Packit 71e9d6
                // NOTE: Information about the city is not available in the free database, so there is not way
Packit 71e9d6
                // for me to implement this functionality right now, but it should be easy to add for anyone with
Packit 71e9d6
                // access to the paid databases.
Packit 71e9d6
                status = pMMDB_get_value(&result.entry, &entry_data, "location", "latitude", NULL);
Packit 71e9d6
                if (MMDB_SUCCESS == status) {
Packit 71e9d6
                    if (entry_data.has_data) {
Packit 71e9d6
                        if (entry_data.type == MMDB_DATA_TYPE_DOUBLE) {
Packit 71e9d6
                            latitude = entry_data.double_value;
Packit 71e9d6
                            ++coordinates;
Packit 71e9d6
                        }
Packit 71e9d6
                    }
Packit 71e9d6
                }
Packit 71e9d6
                status = pMMDB_get_value(&result.entry, &entry_data, "location", "longitude", NULL);
Packit 71e9d6
                if (MMDB_SUCCESS == status) {
Packit 71e9d6
                    if (entry_data.has_data) {
Packit 71e9d6
                        if (entry_data.type == MMDB_DATA_TYPE_DOUBLE) {
Packit 71e9d6
                            longitude = entry_data.double_value;
Packit 71e9d6
                            ++coordinates;
Packit 71e9d6
                        }
Packit 71e9d6
                    }
Packit 71e9d6
                }
Packit 71e9d6
                if (coordinates == 2) {
Packit 71e9d6
                    safe_asprintf(coord, "%f,%f", latitude, longitude);
Packit 71e9d6
                }
Packit 71e9d6
            }
Packit 71e9d6
        }
Packit 71e9d6
        /* Else fail silently */
Packit 71e9d6
        pMMDB_close(&mmdb);
Packit 71e9d6
    }
Packit 71e9d6
    /* Else fail silently */
Packit 71e9d6
}
Packit 71e9d6
Packit 71e9d6
#endif