Files
LumberJack_TUI/third_party/src/GeoLite2PP.cpp
rapturate 1dab3ac7b7 Created my ip_to_geo .cpp and .hpp files and code.
Additionally, I added a .env file and started using
cmake build styles rather than g++ for more efficient
compiling and testing.
 Changes to be committed:
	new file:   .env
	new file:   CMakeLists.txt
	new file:   build/CMakeCache.txt
	new file:   build/CMakeFiles/4.3.2/CMakeCXXCompiler.cmake
	new file:   build/CMakeFiles/4.3.2/CMakeDetermineCompilerABI_CXX.bin
	new file:   build/CMakeFiles/4.3.2/CMakeSystem.cmake
	new file:   build/CMakeFiles/4.3.2/CompilerIdCXX/CMakeCXXCompilerId.cpp
	new file:   build/CMakeFiles/4.3.2/CompilerIdCXX/a.out
	new file:   build/CMakeFiles/CMakeConfigureLog.yaml
	new file:   build/CMakeFiles/CMakeDirectoryInformation.cmake
	new file:   build/CMakeFiles/InstallScripts.json
	new file:   build/CMakeFiles/Makefile.cmake
	new file:   build/CMakeFiles/Makefile2
	new file:   build/CMakeFiles/TargetDirectories.txt
	new file:   build/CMakeFiles/cmake.check_cache
	new file:   build/CMakeFiles/parselog_cli.dir/DependInfo.cmake
	new file:   build/CMakeFiles/parselog_cli.dir/build.make
	new file:   build/CMakeFiles/parselog_cli.dir/cmake_clean.cmake
	new file:   build/CMakeFiles/parselog_cli.dir/compiler_depend.internal
	new file:   build/CMakeFiles/parselog_cli.dir/compiler_depend.make
	new file:   build/CMakeFiles/parselog_cli.dir/compiler_depend.ts
	new file:   build/CMakeFiles/parselog_cli.dir/depend.make
	new file:   build/CMakeFiles/parselog_cli.dir/flags.make
	new file:   build/CMakeFiles/parselog_cli.dir/ip_to_geo/ip_to_geo.cpp.o
	new file:   build/CMakeFiles/parselog_cli.dir/ip_to_geo/ip_to_geo.cpp.o.d
	new file:   build/CMakeFiles/parselog_cli.dir/link.d
	new file:   build/CMakeFiles/parselog_cli.dir/link.txt
	new file:   build/CMakeFiles/parselog_cli.dir/log_parsing/log_parsing.cpp.o
	new file:   build/CMakeFiles/parselog_cli.dir/log_parsing/log_parsing.cpp.o.d
	new file:   build/CMakeFiles/parselog_cli.dir/main.cpp.o
	new file:   build/CMakeFiles/parselog_cli.dir/main.cpp.o.d
	new file:   build/CMakeFiles/parselog_cli.dir/progress.make
	new file:   build/CMakeFiles/parselog_cli.dir/third_party/src/GeoLite2PP.cpp.o
	new file:   build/CMakeFiles/parselog_cli.dir/third_party/src/GeoLite2PP.cpp.o.d
	new file:   build/CMakeFiles/parselog_cli.dir/third_party/src/GeoLite2PP_error_category.cpp.o
	new file:   build/CMakeFiles/parselog_cli.dir/third_party/src/GeoLite2PP_error_category.cpp.o.d
	new file:   build/CMakeFiles/progress.marks
	new file:   build/Makefile
	new file:   build/cmake_install.cmake
	new file:   build/compile_commands.json
	new file:   build/parselog_cli
	new file:   compile_commands.json
	new file:   data/GeoLite2-City.mmdb
	modified:   ip_to_geo/ip_to_geo.cpp
	modified:   ip_to_geo/ip_to_geo.hpp
	modified:   main.cpp
	deleted:    parselog_cli.exe
	deleted:    test
	renamed:    ip_to_geo/GeoLite2PP.hpp -> third_party/include/GeoLite2PP.hpp
	new file:   third_party/include/GeoLite2PP_error_category.hpp
	new file:   third_party/include/GeoLite2PP_version.hpp
	new file:   third_party/include/maxminddb.h
	new file:   third_party/include/maxminddb_config.h
	new file:   third_party/lib/libmaxminddb.a
	new file:   third_party/src/GeoLite2PP.cpp
	new file:   third_party/src/GeoLite2PP_error_category.cpp
2026-06-03 15:22:59 -04:00

360 lines
10 KiB
C++

/* GeoLite2++ (C) 2016-2018 Stephane Charette <stephanecharette@gmail.com>
* $Id: GeoLite2PP.cpp 2549 2018-06-08 18:48:31Z stephane $
*/
#include <GeoLite2PP.hpp>
#include <GeoLite2PP_error_category.hpp>
#include <GeoLite2PP_version.hpp>
GeoLite2PP::DB::~DB( void )
{
MMDB_close( &mmdb );
return;
}
GeoLite2PP::DB::DB( const std::string &database_filename )
{
const int status = MMDB_open( database_filename.c_str(), MMDB_MODE_MMAP, &mmdb );
if (status != MMDB_SUCCESS)
{
const ErrorCategory & cat( get_error_category() );
const std::error_code ec( status, cat );
const std::string msg = "Failed to open the MMDB database \"" + database_filename + "\"";
/** @throw std::system_error if the database file cannot be opened.
* @see @ref GeoLite2PP::MMDBStatus
* @see @ref GeoLite2PP::ErrorCategory
*/
throw std::system_error( ec, msg );
}
return;
}
std::string GeoLite2PP::DB::get_lib_version_mmdb( void ) const
{
return MMDB_lib_version();
}
std::string GeoLite2PP::DB::get_lib_version_geolite2pp( void ) const
{
return GEOLITE2PP_VERSION;
}
MMDB_metadata_s GeoLite2PP::DB::get_metadata_raw( void )
{
return mmdb.metadata;
}
std::string GeoLite2PP::DB::get_metadata( void )
{
MMDB_entry_data_list_s *node = nullptr;
const int status = MMDB_get_metadata_as_entry_data_list( &mmdb, &node );
if (status)
{
/// @throw std::system_error if the lookup resulted in a MMDB error
const ErrorCategory & cat( get_error_category() );
const std::error_code ec( status, cat );
const std::string msg = "Failed to lookup up meta data";
throw std::system_error( ec, msg );
}
return to_json( node );
}
MMDB_lookup_result_s GeoLite2PP::DB::lookup_raw( const std::string &ip_address )
{
int gai_error = 0; // get_address_info() error
int mmdb_error = 0;
MMDB_lookup_result_s result = MMDB_lookup_string( &mmdb, ip_address.c_str(), &gai_error, &mmdb_error );
if (gai_error)
{
/// @throw std::invalid_argument if the address is invalid
throw std::invalid_argument( gai_strerror(gai_error) );
}
if (mmdb_error)
{
/// @throw std::system_error if the lookup resulted in a MMDB error
const ErrorCategory & cat( get_error_category() );
const std::error_code ec( mmdb_error, cat );
const std::string msg = "Database error while looking up address \"" + ip_address + "\"";
throw std::system_error( ec, msg );
}
if (result.found_entry == false)
{
/// @throw std::length_error if an entry was not found in the database
throw std::length_error( "Failed to find address \"" + ip_address + "\"" );
}
return result;
}
void GeoLite2PP::DB::create_json_from_entry( std::stringstream &ss, size_t depth, uint32_t data_size, MMDB_entry_data_list_s * &node, const bool in_array )
{
// we're normally dealing with pairs (keys + values) so double the data_size
if ( ! in_array )
{
data_size *= 2;
}
if (data_size == 0)
{
// nothing left to do
return;
}
if (node == nullptr)
{
/// @throw std::runtime_error if the data_size is non-zero but the next pointer is null
throw std::runtime_error( "invalid entry pointer or data size value" );
}
while (node && data_size)
{
const bool is_value = in_array || (data_size % 2);
const bool is_key = in_array || (! is_value);
data_size --;
if (node->entry_data.has_data == false)
{
/// @throw std::runtime_error if a node contains no data
throw std::runtime_error( "invalid entry has no data" );
}
if (is_key)
{
ss << std::string( depth * 4, ' ' );
}
switch (node->entry_data.type)
{
case MMDB_DATA_TYPE_UTF8_STRING:
{
ss << "\"" << std::string(node->entry_data.utf8_string, node->entry_data.data_size) << "\"";
if ( ! is_value )
{
ss << " : ";
}
break;
}
case MMDB_DATA_TYPE_MAP:
{
ss << std::endl << std::string( depth * 4, ' ' ) << "{" << std::endl;
create_json_from_entry( ss, depth + 1, node->entry_data.data_size, node->next );
ss << std::endl << std::string( depth * 4, ' ' ) << "}";
break;
}
case MMDB_DATA_TYPE_ARRAY:
{
ss << std::endl << std::string( depth * 4, ' ' ) << "[" << std::endl;
create_json_from_entry( ss, depth + 1, node->entry_data.data_size, node->next, true );
ss << std::endl << std::string( depth * 4, ' ' ) << "]";
break;
}
case MMDB_DATA_TYPE_DOUBLE: ss << node->entry_data.double_value; break;
case MMDB_DATA_TYPE_UINT16: ss << node->entry_data.uint16; break;
case MMDB_DATA_TYPE_UINT32: ss << node->entry_data.uint32; break;
case MMDB_DATA_TYPE_INT32: ss << node->entry_data.uint32; break;
case MMDB_DATA_TYPE_UINT64: ss << node->entry_data.uint64; break;
case MMDB_DATA_TYPE_FLOAT: ss << node->entry_data.float_value; break;
case MMDB_DATA_TYPE_BOOLEAN: ss << (node->entry_data.boolean ? "true" : "false"); break;
case MMDB_DATA_TYPE_EXTENDED:
case MMDB_DATA_TYPE_POINTER:
case MMDB_DATA_TYPE_CONTAINER:
case MMDB_DATA_TYPE_END_MARKER:
{
/// @throw std::runtime_error if the data type is invalid
throw std::runtime_error( "entry data type #" + std::to_string(node->entry_data.type) + " indicates a damaged or corrupt database" );
}
case MMDB_DATA_TYPE_BYTES:
case MMDB_DATA_TYPE_UINT128:
default:
{
/// @throw std::runtime_error if the data type is not supported (raw bytes or uint128)
throw std::runtime_error( "unhandled data type #" + std::to_string(node->entry_data.type) + " cannot be processed" );
}
}
if (is_value && data_size)
{
ss << "," << std::endl;
}
node = node->next;
}
return;
}
std::string GeoLite2PP::DB::lookup( const std::string &ip_address )
{
MMDB_lookup_result_s result = lookup_raw( ip_address );
MMDB_entry_data_list_s *node = nullptr;
const int status = MMDB_get_entry_data_list( &result.entry, &node );
if (status)
{
/// @throw std::system_error if the lookup resulted in a MMDB error
const ErrorCategory & cat( get_error_category() );
const std::error_code ec( status, cat );
const std::string msg = "Failed to lookup up address \"" + ip_address + "\"";
throw std::system_error( ec, msg );
}
return to_json( node );
}
std::string GeoLite2PP::DB::to_json( MMDB_entry_data_list_s *node )
{
#if 0
// for debug purposes:
MMDB_dump_entry_data_list( stdout, node, 0 );
#endif
std::stringstream ss;
try
{
const size_t depth = 0;
const size_t nodes = 1;
MMDB_entry_data_list_s *next = node;
create_json_from_entry( ss, depth, nodes, next );
}
catch (...)
{
MMDB_free_entry_data_list( node );
throw;
}
MMDB_free_entry_data_list( node );
return ss.str();
}
GeoLite2PP::MStr GeoLite2PP::DB::get_all_fields( const std::string &ip_address, const std::string &language )
{
MMDB_lookup_result_s result = lookup_raw( ip_address );
MStr m;
add_to_map( m, &result, "continent" , language , VCStr{ "continent" , "names" } );
add_to_map( m, &result, "registered_country" , language , VCStr{ "registered_country" , "names" } );
add_to_map( m, &result, "country" , language , VCStr{ "country" , "names" } );
add_to_map( m, &result, "city" , language , VCStr{ "city" , "names" } );
add_to_map( m, &result, "subdivision" , language , VCStr{ "subdivisions", "0" , "names" } );
add_to_map( m, &result, "subdivision_iso_code" , "" , VCStr{ "subdivisions", "0" , "iso_code" } );
add_to_map( m, &result, "country_iso_code" , "" , VCStr{ "country" , "iso_code" } );
add_to_map( m, &result, "accuracy_radius" , "" , VCStr{ "location" , "accuracy_radius" } );
add_to_map( m, &result, "latitude" , "" , VCStr{ "location" , "latitude" } );
add_to_map( m, &result, "longitude" , "" , VCStr{ "location" , "longitude" } );
add_to_map( m, &result, "time_zone" , "" , VCStr{ "location" , "time_zone" } );
add_to_map( m, &result, "postal_code" , "" , VCStr{ "postal" , "code" } );
m[ "query_ip_address" ] = ip_address;
m[ "query_language" ] = language;
return m;
}
std::string GeoLite2PP::DB::get_field( const std::string &ip_address, const std::string &language, const VCStr &v )
{
MMDB_lookup_result_s lookup = lookup_raw( ip_address );
return get_field( &lookup, language, v );
}
std::string GeoLite2PP::DB::get_field( MMDB_lookup_result_s *lookup, const std::string &language, const VCStr &v )
{
std::string str;
if (lookup)
{
MMDB_entry_s *entry = &lookup->entry;
VCStr lookup_path = v;
if (language.empty() == false)
{
lookup_path.push_back( language.c_str() );
}
if (lookup_path.size() == 0 || *lookup_path.rbegin() != nullptr)
{
// last vector entry when calling MMDB_aget_value() must be a NULL
lookup_path.push_back( nullptr );
}
MMDB_entry_data_s result;
MMDB_aget_value( entry, &result, lookup_path.data() );
if (result.has_data)
{
switch (result.type)
{
case MMDB_DATA_TYPE_UTF8_STRING: str = std::string( result.utf8_string, result.data_size ); break;
case MMDB_DATA_TYPE_DOUBLE: str = std::to_string( result.double_value ); break;
case MMDB_DATA_TYPE_UINT16: str = std::to_string( result.uint16 ); break;
case MMDB_DATA_TYPE_UINT32: str = std::to_string( result.uint32 ); break;
case MMDB_DATA_TYPE_INT32: str = std::to_string( result.uint32 ); break;
case MMDB_DATA_TYPE_UINT64: str = std::to_string( result.uint64 ); break;
case MMDB_DATA_TYPE_FLOAT: str = std::to_string( result.float_value ); break;
case MMDB_DATA_TYPE_BOOLEAN: str = (result.boolean ? "true" : "false" ); break;
default: /* data type not supported for this "quick" retrieval */ break;
}
}
else if (language == "en")
{
// ignore the language and grab the first one available
str = get_field( lookup, "0", v );
}
else if (language != "0" && language != "en")
{
// ignore the language and grab the English name
str = get_field( lookup, "0", v );
}
}
return str;
}
void GeoLite2PP::DB::add_to_map( GeoLite2PP::MStr &m, MMDB_lookup_result_s *node, const std::string &name, const std::string &language, const VCStr &v )
{
const std::string result = get_field( node, language, v );
if (result.empty() == false)
{
// try and find a decent key name to use to store this value into the map
std::string key = name;
if (key.empty() || m.count(key) != 0)
{
// try a different name
key = *v.begin();
}
if (key.empty() || m.count(key) != 0)
{
// try a different name
key = *v.rbegin();
}
m[ key ] = result;
}
return;
}