occtl: use maxminddb when available

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
This commit is contained in:
Nikos Mavrogiannopoulos
2019-10-14 14:54:40 +02:00
parent b9ff297c7d
commit 960032e065
4 changed files with 199 additions and 4 deletions

1
NEWS
View File

@@ -5,6 +5,7 @@
at the same time.
- occtl: fixed json output of show status command. Introduced tests
for checking its json output using yajl (#220).
- occtl: use maxminddb when available.
* Version 0.12.4 (released 2019-07-03)

View File

@@ -163,13 +163,28 @@ if test "$test_for_libnl" = yes;then
fi
fi
AC_ARG_WITH(maxmind,
AS_HELP_STRING([--without-maxmind], [do not try to use the maxmind library]),
test_for_maxmind=$withval,
test_for_maxmind=yes)
have_maxmind=no
if test "$test_for_maxmind" = yes;then
PKG_CHECK_MODULES(MAXMIND, libmaxminddb >= 1.0.0, [have_maxmind=yes], [have_maxmind=no])
if test "$have_maxmind" = yes;then
AC_DEFINE(HAVE_MAXMIND, 1, [have libmaxminddb])
fi
fi
AM_CONDITIONAL(HAVE_MAXMIND, test "x$have_maxmind" != xno)
AC_ARG_WITH(geoip,
AS_HELP_STRING([--without-geoip], [do not try to use the geoip library]),
test_for_geoip=$withval,
test_for_geoip=yes)
have_geoip=no
if test "$test_for_geoip" = yes;then
if test "$test_for_geoip" = yes && test "$have_maxmind" != yes;then
PKG_CHECK_MODULES(GEOIP, geoip >= 1.6.0, [have_geoip=yes], [have_geoip=no])
if test "$have_geoip" = yes;then
AC_DEFINE(HAVE_GEOIP, 1, [have libgeoip])
@@ -601,6 +616,7 @@ Summary of build options:
libnl3: ${have_libnl3}
liboath: ${have_liboath}
libgeoip: ${have_geoip}
libmaxminddb: ${have_maxmind}
glibc (sha2crypt): ${have_glibc}
local talloc: ${with_local_talloc}
local protobuf-c: ${with_local_protobuf_c}

View File

@@ -98,15 +98,22 @@ clean-local:
bin_PROGRAMS = occtl/occtl
occtl_occtl_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/occtl $(LIBNL3_CFLAGS) $(GEOIP_CFLAGS)
occtl_occtl_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/occtl $(LIBNL3_CFLAGS) $(GEOIP_CFLAGS) $(MAXMIND_CFLAGS)
occtl_occtl_SOURCES = occtl/occtl.c occtl/pager.c occtl/occtl.h occtl/time.c occtl/cache.c \
occtl/ip-cache.c occtl/nl.c occtl/ctl.h occtl/print.c occtl/json.c occtl/json.h \
occtl/hex.c occtl/hex.h occtl/unix.c occtl/geoip.c occtl/geoip.h \
occtl/hex.c occtl/hex.h occtl/unix.c occtl/geoip.h \
occtl/session-cache.c
if HAVE_MAXMIND
occtl_occtl_SOURCES += occtl/maxmind.c
else
occtl_occtl_SOURCES += occtl/geoip.c
endif
occtl_occtl_LDADD = ../gl/libgnu.a libcommon.a $(LIBREADLINE_LIBS) \
$(LIBNL3_LIBS) $(NEEDED_LIBPROTOBUF_LIBS) $(LIBTALLOC_LIBS) libccan.a \
libipc.a $(NEEDED_LIBPROTOBUF_LIBS) $(CODE_COVERAGE_LDFLAGS) \
$(LIBNETTLE_LIBS) $(GEOIP_LIBS)
$(LIBNETTLE_LIBS) $(MAXMIND_LIBS) $(GEOIP_LIBS)
# Ocpasswd

171
src/occtl/maxmind.c Normal file
View File

@@ -0,0 +1,171 @@
#include <config.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "geoip.h"
#ifdef HAVE_MAXMIND
#include <maxminddb.h>
#ifndef MAXMINDDB_LOCATION_COUNTRY
#define MAXMINDDB_LOCATION_COUNTRY "/usr/share/GeoIP/GeoLite2-Country.mmdb"
#endif
#ifndef MAXMINDDB_LOCATION_CITY
#define MAXMINDDB_LOCATION_CITY "/usr/share/GeoIP/GeoLite2-City.mmdb"
#endif
#define pMMDB_close MMDB_close
#define pMMDB_get_value MMDB_get_value
#define pMMDB_lookup_string MMDB_lookup_string
#define pMMDB_open MMDB_open
void process_result_from_mmdb_lookup(MMDB_entry_data_s * entry_data, int status,
char **output)
{
if (MMDB_SUCCESS == status) {
if (entry_data->has_data) {
if (entry_data->type == MMDB_DATA_TYPE_UTF8_STRING) {
*output =
(char *)calloc(entry_data->data_size + 1,
sizeof(char));
if (NULL != *output) {
memcpy(*output, entry_data->utf8_string,
entry_data->data_size);
} else {
fprintf(stderr,
"Memory allocation failure line %d\n",
__LINE__);
}
}
}
}
/* Else fail silently */
}
char *geo_lookup(const char *ip, char *buf, unsigned buf_size)
{
MMDB_s mmdb;
MMDB_entry_data_s entry_data;
int gai_error, mmdb_error, status, coordinates = 0;
double latitude, longitude;
char *country = NULL, *ccode = NULL;
char *coord = NULL;
unsigned found = 0;
/* Open the system maxmind database with countries */
status = pMMDB_open(MAXMINDDB_LOCATION_COUNTRY, MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS == status) {
/* Lookup IP address in the database */
MMDB_lookup_result_s result =
pMMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error);
if (MMDB_SUCCESS == mmdb_error) {
/* If the lookup was successfull and an entry was found */
if (result.found_entry) {
memset(&entry_data, 0,
sizeof(MMDB_entry_data_s));
/* Travel the path in the tree like structure of the MMDB and store the value if found */
status =
pMMDB_get_value(&result.entry, &entry_data,
"country", "names", "en",
NULL);
process_result_from_mmdb_lookup(&entry_data,
status,
&country);
memset(&entry_data, 0,
sizeof(MMDB_entry_data_s));
status =
pMMDB_get_value(&result.entry, &entry_data,
"country", "iso_code",
NULL);
process_result_from_mmdb_lookup(&entry_data,
status, &ccode);
}
}
/* Else fail silently */
pMMDB_close(&mmdb);
}
/* Else fail silently */
/* Open the system maxmind database with cities - which actually does not contain names of the cities */
status = pMMDB_open(MAXMINDDB_LOCATION_CITY, MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS == status) {
/* Lookup IP address in the database */
MMDB_lookup_result_s result =
pMMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error);
if (MMDB_SUCCESS == mmdb_error) {
/* If the lookup was successfull and an entry was found */
if (result.found_entry) {
memset(&entry_data, 0,
sizeof(MMDB_entry_data_s));
// NOTE: Information about the city is not available in the free database, so there is not way
// for me to implement this functionality right now, but it should be easy to add for anyone with
// access to the paid databases.
status =
pMMDB_get_value(&result.entry, &entry_data,
"location", "latitude",
NULL);
if (MMDB_SUCCESS == status) {
if (entry_data.has_data) {
if (entry_data.type ==
MMDB_DATA_TYPE_DOUBLE) {
latitude =
entry_data.
double_value;
++coordinates;
}
}
}
status =
pMMDB_get_value(&result.entry, &entry_data,
"location", "longitude",
NULL);
if (MMDB_SUCCESS == status) {
if (entry_data.has_data) {
if (entry_data.type ==
MMDB_DATA_TYPE_DOUBLE) {
longitude =
entry_data.
double_value;
++coordinates;
}
}
}
if (coordinates == 2) {
asprintf(&coord, "%f,%f", latitude,
longitude);
}
}
}
pMMDB_close(&mmdb);
}
if (country && coord) {
snprintf(buf, buf_size, "%s (%s)", country, coord);
found = 1;
} else if (ccode && coord) {
snprintf(buf, buf_size, "%s (%s)", ccode, coord);
found = 1;
} else if (country) {
snprintf(buf, buf_size, "%s", country);
found = 1;
} else if (ccode) {
snprintf(buf, buf_size, "%s", ccode);
found = 1;
}
free(ccode);
free(country);
free(coord);
if (found)
return buf;
else
return "unknown";
}
#endif