diff --git a/scripts/base/init-bare.zeek b/scripts/base/init-bare.zeek index 74bd2288e4..9c97e65982 100644 --- a/scripts/base/init-bare.zeek +++ b/scripts/base/init-bare.zeek @@ -2113,6 +2113,7 @@ type gtp_delete_pdp_ctx_response_elements: record { @load base/bif/supervisor.bif @load base/bif/packet_analysis.bif @load base/bif/CPP-load.bif +@load base/bif/mmdb.bif ## Internal function. function add_interface(iold: string, inew: string): string diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef779e13e7..1208c07b76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,6 +114,7 @@ set(BIF_SRCS communityid.bif const.bif event.bif + mmdb.bif option.bif reporter.bif stats.bif diff --git a/src/Func.cc b/src/Func.cc index 20a4a79164..b399c80600 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -52,6 +52,7 @@ #include "supervisor.bif.func_h" #include "packet_analysis.bif.func_h" #include "CPP-load.bif.func_h" +#include "mmdb.bif.func_h" #include "zeek.bif.func_def" #include "communityid.bif.func_def" @@ -62,6 +63,7 @@ #include "supervisor.bif.func_def" #include "packet_analysis.bif.func_def" #include "CPP-load.bif.func_def" +#include "mmdb.bif.func_def" // clang-format on extern RETSIGTYPE sig_handler(int signo); @@ -1049,6 +1051,7 @@ void init_primary_bifs() { #include "CPP-load.bif.func_init" #include "communityid.bif.func_init" +#include "mmdb.bif.func_init" #include "option.bif.func_init" #include "packet_analysis.bif.func_init" #include "reporter.bif.func_init" diff --git a/src/mmdb.bif b/src/mmdb.bif new file mode 100644 index 0000000000..be47a05233 --- /dev/null +++ b/src/mmdb.bif @@ -0,0 +1,518 @@ +%%{ +#ifdef USE_GEOIP +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +static int mmdb_msg_count = 0; +static constexpr int mmdb_msg_limit = 20; +static double mmdb_msg_suppression_time = 0; +static constexpr double mmdb_msg_suppression_duration = 300; + +static void report_mmdb_msg(const char* format, ...) + { + if ( zeek::run_state::network_time > mmdb_msg_suppression_time + mmdb_msg_suppression_duration ) + { + mmdb_msg_count = 0; + mmdb_msg_suppression_time = zeek::run_state::network_time; + } + + if ( mmdb_msg_count >= mmdb_msg_limit ) + return; + + ++mmdb_msg_count; + + va_list al; + va_start(al, format); + std::string msg = zeek::util::vfmt(format, al); + va_end(al); + + zeek::reporter->Info("%s", msg.data()); + } + +class MMDB { +public: + MMDB(const char* filename, struct stat info); + + ~MMDB(); + + MMDB_lookup_result_s Lookup(const struct sockaddr* const sa); + bool StaleDB(); + const char* Filename(); + +private: + MMDB_s mmdb; + struct stat file_info; + bool lookup_error; + double last_check; +}; + +MMDB::MMDB(const char* filename, struct stat info) + : file_info(info), lookup_error{false}, + last_check{zeek::run_state::network_time} + { + int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb); + + if ( MMDB_SUCCESS != status ) + { + throw std::runtime_error(MMDB_strerror(status)); + } + } + +MMDB::~MMDB() + { + MMDB_close(&mmdb); + } + +MMDB_lookup_result_s MMDB::Lookup(const struct sockaddr* const sa) + { + int mmdb_error; + MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, sa, &mmdb_error); + + if ( MMDB_SUCCESS != mmdb_error ) + { + lookup_error = true; + throw std::runtime_error(MMDB_strerror(mmdb_error)); + } + + return result; + } + +// Check to see if the Maxmind DB should be closed and reopened. This will +// happen if there was a lookup error or if the mmap'd file has been replaced +// by an external process. +bool MMDB::StaleDB() + { + struct stat buf; + + if ( lookup_error ) + return true; + + + static double mmdb_stale_check_interval = zeek::id::find_val("mmdb_stale_check_interval")->AsInterval(); + + if ( mmdb_stale_check_interval < 0.0 ) + return false; + + if ( zeek::run_state::network_time - last_check < mmdb_stale_check_interval ) + return false; + + last_check = zeek::run_state::network_time; + + if ( 0 != stat(mmdb.filename, &buf) ) + return true; + + if ( buf.st_ino != file_info.st_ino || buf.st_mtime != file_info.st_mtime ) + { + report_mmdb_msg("%s change detected for MaxMind DB [%s]", + buf.st_ino != file_info.st_ino ? "Inode" : "Modification time", + mmdb.filename); + return true; + } + + return false; + } + +const char* MMDB::Filename() + { + return mmdb.filename; + } + +std::unique_ptr mmdb_loc; +std::unique_ptr mmdb_asn; +static bool did_mmdb_loc_db_error = false; +static bool did_mmdb_asn_db_error = false; + +static bool mmdb_open(const char* filename, bool asn) + { + struct stat buf; + + if ( 0 != stat(filename, &buf) ) + { + return false; + } + + try + { + if ( asn ) + { + mmdb_asn.reset(new MMDB(filename, buf)); + } + else + { + mmdb_loc.reset(new MMDB(filename, buf)); + } + } + + catch ( const std::exception& e ) + { + if ( asn ) + did_mmdb_asn_db_error = false; + else + did_mmdb_loc_db_error = false; + + report_mmdb_msg("Failed to open MaxMind DB: %s [%s]", filename, + e.what()); + return false; + } + + return true; + } + +static bool mmdb_open_loc(const char* filename) + { + return mmdb_open(filename, false); + } + +static bool mmdb_open_asn(const char* filename) + { + return mmdb_open(filename, true); + } + +static void mmdb_check_loc() + { + if ( mmdb_loc && mmdb_loc->StaleDB() ) + { + report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_loc->Filename()); + did_mmdb_loc_db_error = false; + mmdb_loc.reset(); + } + } + +static void mmdb_check_asn() + { + if ( mmdb_asn && mmdb_asn->StaleDB() ) + { + report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_asn->Filename()); + did_mmdb_asn_db_error = false; + mmdb_asn.reset(); + } + } + +static bool mmdb_lookup(const zeek::IPAddr& addr, MMDB_lookup_result_s& result, + bool asn) + { + struct sockaddr_storage ss = {0}; + + if ( IPv4 == addr.GetFamily() ) + { + struct sockaddr_in* sa = (struct sockaddr_in*)&ss; + sa->sin_family = AF_INET; + addr.CopyIPv4(&sa->sin_addr); + } + + else + { + struct sockaddr_in6* sa = (struct sockaddr_in6*)&ss; + sa->sin6_family = AF_INET6; + addr.CopyIPv6(&sa->sin6_addr); + } + + try + { + result = asn ? mmdb_asn->Lookup((struct sockaddr*)&ss) + : mmdb_loc->Lookup((struct sockaddr*)&ss); + } + + catch ( const std::exception& e ) + { + report_mmdb_msg("MaxMind DB lookup location error [%s]", e.what()); + return false; + } + + return result.found_entry; + } + +static bool mmdb_lookup_loc(const zeek::IPAddr& addr, MMDB_lookup_result_s& result) + { + return mmdb_lookup(addr, result, false); + } + +static bool mmdb_lookup_asn(const zeek::IPAddr& addr, MMDB_lookup_result_s& result) + { + return mmdb_lookup(addr, result, true); + } + +static zeek::ValPtr mmdb_getvalue(MMDB_entry_data_s* entry_data, int status, + int data_type ) + { + switch (status) + { + case MMDB_SUCCESS: + if ( entry_data->has_data ) + { + switch (data_type) + { + case MMDB_DATA_TYPE_UTF8_STRING: + return zeek::make_intrusive( + entry_data->data_size, entry_data->utf8_string); + break; + + case MMDB_DATA_TYPE_DOUBLE: + return zeek::make_intrusive(entry_data->double_value); + break; + + case MMDB_DATA_TYPE_UINT32: + return zeek::val_mgr->Count(entry_data->uint32); + + default: + break; + } + } + break; + + case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR: + // key doesn't exist, nothing to do + break; + + default: + report_mmdb_msg("MaxMind DB error [%s]", MMDB_strerror(status)); + break; + } + + return nullptr; + } + +static bool mmdb_try_open_loc () + { + // City database is always preferred over Country database. + const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal(); + std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString(); + + if ( ! mmdb_dir.empty() ) + { + auto d = mmdb_dir + "/GeoLite2-City.mmdb"; + + if ( mmdb_open_loc(d.data()) ) + return true; + + d = mmdb_dir + "/GeoLite2-Country.mmdb"; + + if ( mmdb_open_loc(d.data()) ) + return true;; + } + + return mmdb_open_loc("/usr/share/GeoIP/GeoLite2-City.mmdb") + || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-City.mmdb") + || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-City.mmdb") + || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-City.mmdb") + || mmdb_open_loc("/usr/share/GeoIP/GeoLite2-Country.mmdb") + || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-Country.mmdb") + || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-Country.mmdb") + || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-Country.mmdb"); + } + +static bool mmdb_try_open_asn () + { + const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal(); + std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString(); + + if ( ! mmdb_dir.empty() ) + { + auto d = mmdb_dir + "/GeoLite2-ASN.mmdb"; + + if ( mmdb_open_asn(d.data()) ) + return true; + } + + return mmdb_open_asn("/usr/share/GeoIP/GeoLite2-ASN.mmdb") + || mmdb_open_asn("/var/lib/GeoIP/GeoLite2-ASN.mmdb") + || mmdb_open_asn("/usr/local/share/GeoIP/GeoLite2-ASN.mmdb") + || mmdb_open_asn("/usr/local/var/GeoIP/GeoLite2-ASN.mmdb"); + } + +#endif +%%} + +## Initializes MMDB for later use of lookup_location. +## Requires Zeek to be built with ``libmaxminddb``. +## +## f: The filename of the MaxMind City or Country DB. +## +## Returns: A boolean indicating whether the db was successfully opened. +## +## .. zeek:see:: lookup_autonomous_system +function mmdb_open_location_db%(f: string%) : bool + %{ +#ifdef USE_GEOIP + return zeek::val_mgr->Bool(mmdb_open_loc(f->CheckString())); +#else + return zeek::val_mgr->False(); +#endif + %} + +## Initializes MMDB for later use of lookup_autonomous_system. +## Requires Zeek to be built with ``libmaxminddb``. +## +## f: The filename of the MaxMind ASN DB. +## +## Returns: A boolean indicating whether the db was successfully opened. +## +## .. zeek:see:: lookup_autonomous_system +function mmdb_open_asn_db%(f: string%) : bool + %{ +#ifdef USE_GEOIP + return zeek::val_mgr->Bool(mmdb_open_asn(f->CheckString())); +#else + return zeek::val_mgr->False(); +#endif + %} + +## Performs a geo-lookup of an IP address. +## Requires Zeek to be built with ``libmaxminddb``. +## +## a: The IP address to lookup. +## +## Returns: A record with country, region, city, latitude, and longitude. +## +## .. zeek:see:: lookup_autonomous_system +function lookup_location%(a: addr%) : geo_location + %{ + static auto geo_location = zeek::id::find_type("geo_location"); + auto location = zeek::make_intrusive(geo_location); + +#ifdef USE_GEOIP + mmdb_check_loc(); + if ( ! mmdb_loc ) + { + if ( ! mmdb_try_open_loc() ) + { + if ( ! did_mmdb_loc_db_error ) + { + did_mmdb_loc_db_error = true; + zeek::emit_builtin_error("Failed to open GeoIP location database"); + } + + return location; + } + } + + MMDB_lookup_result_s result; + + if ( mmdb_lookup_loc(a->AsAddr(), result) ) + { + MMDB_entry_data_s entry_data; + int status; + + // Get Country ISO Code + status = MMDB_get_value(&result.entry, &entry_data, + "country", "iso_code", nullptr); + location->Assign(0, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_UTF8_STRING)); + + // Get Major Subdivision ISO Code + status = MMDB_get_value(&result.entry, &entry_data, + "subdivisions", "0", "iso_code", nullptr); + location->Assign(1, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_UTF8_STRING)); + + // Get City English Name + status = MMDB_get_value(&result.entry, &entry_data, + "city", "names", "en", nullptr); + location->Assign(2, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_UTF8_STRING)); + + // Get Location Latitude + status = MMDB_get_value(&result.entry, &entry_data, + "location", "latitude", nullptr); + location->Assign(3, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_DOUBLE)); + + // Get Location Longitude + status = MMDB_get_value(&result.entry, &entry_data, + "location", "longitude", nullptr); + location->Assign(4, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_DOUBLE)); + + return location; + } + +#else // not USE_GEOIP + static int missing_geoip_reported = 0; + + if ( ! missing_geoip_reported ) + { + zeek::emit_builtin_error("Zeek was not configured for GeoIP support"); + missing_geoip_reported = 1; + } +#endif + + // We can get here even if we have MMDB support if we weren't + // able to initialize it or it didn't return any information for + // the address. + + return std::move(location); + %} + +## Performs an lookup of AS number & organization of an IP address. +## Requires Zeek to be built with ``libmaxminddb``. +## +## a: The IP address to lookup. +## +## Returns: A record with autonomous system number and organization that contains *a*. +## +## .. zeek:see:: lookup_location +function lookup_autonomous_system%(a: addr%) : geo_autonomous_system + %{ + static auto geo_autonomous_system = zeek::id::find_type("geo_autonomous_system"); + auto autonomous_system = zeek::make_intrusive(geo_autonomous_system); + +#ifdef USE_GEOIP + mmdb_check_asn(); + if ( ! mmdb_asn ) + { + if ( ! mmdb_try_open_asn() ) + { + if ( ! did_mmdb_asn_db_error ) + { + did_mmdb_asn_db_error = true; + zeek::emit_builtin_error("Failed to open GeoIP ASN database"); + } + + return autonomous_system; + } + } + + MMDB_lookup_result_s result; + + if ( mmdb_lookup_asn(a->AsAddr(), result) ) + { + MMDB_entry_data_s entry_data; + int status; + + // Get Autonomous System Number + status = MMDB_get_value(&result.entry, &entry_data, + "autonomous_system_number", nullptr); + autonomous_system->Assign(0, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_UINT32)); + + // Get Autonomous System Organization + status = MMDB_get_value(&result.entry, &entry_data, + "autonomous_system_organization", nullptr); + autonomous_system->Assign(1, mmdb_getvalue(&entry_data, status, + MMDB_DATA_TYPE_UTF8_STRING)); + + return autonomous_system; + } + +#else // not USE_GEOIP + static int missing_geoip_reported = 0; + + if ( ! missing_geoip_reported ) + { + zeek::emit_builtin_error("Zeek was not configured for GeoIP ASN support"); + missing_geoip_reported = 1; + } +#endif + + // We can get here even if we have GeoIP support, if we weren't + // able to initialize it or it didn't return any information for + // the address. + return std::move(autonomous_system); + %} diff --git a/src/zeek.bif b/src/zeek.bif index dd04941170..770524ebda 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -3980,525 +3980,6 @@ function lookup_hostname%(host: string%) : addr_set return nullptr; %} -%%{ -#ifdef USE_GEOIP -#include - -extern "C" { -#include -#include -#include -#include -#include -} - -static int mmdb_msg_count = 0; -static constexpr int mmdb_msg_limit = 20; -static double mmdb_msg_suppression_time = 0; -static constexpr double mmdb_msg_suppression_duration = 300; - -static void report_mmdb_msg(const char* format, ...) - { - if ( zeek::run_state::network_time > mmdb_msg_suppression_time + mmdb_msg_suppression_duration ) - { - mmdb_msg_count = 0; - mmdb_msg_suppression_time = zeek::run_state::network_time; - } - - if ( mmdb_msg_count >= mmdb_msg_limit ) - return; - - ++mmdb_msg_count; - - va_list al; - va_start(al, format); - std::string msg = zeek::util::vfmt(format, al); - va_end(al); - - zeek::reporter->Info("%s", msg.data()); - } - -class MMDB { -public: - MMDB(const char* filename, struct stat info); - - ~MMDB(); - - MMDB_lookup_result_s Lookup(const struct sockaddr* const sa); - bool StaleDB(); - const char* Filename(); - -private: - MMDB_s mmdb; - struct stat file_info; - bool lookup_error; - double last_check; -}; - -MMDB::MMDB(const char* filename, struct stat info) - : file_info(info), lookup_error{false}, - last_check{zeek::run_state::network_time} - { - int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb); - - if ( MMDB_SUCCESS != status ) - { - throw std::runtime_error(MMDB_strerror(status)); - } - } - -MMDB::~MMDB() - { - MMDB_close(&mmdb); - } - -MMDB_lookup_result_s MMDB::Lookup(const struct sockaddr* const sa) - { - int mmdb_error; - MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, sa, &mmdb_error); - - if ( MMDB_SUCCESS != mmdb_error ) - { - lookup_error = true; - throw std::runtime_error(MMDB_strerror(mmdb_error)); - } - - return result; - } - -// Check to see if the Maxmind DB should be closed and reopened. This will -// happen if there was a lookup error or if the mmap'd file has been replaced -// by an external process. -bool MMDB::StaleDB() - { - struct stat buf; - - if ( lookup_error ) - return true; - - - static double mmdb_stale_check_interval = zeek::id::find_val("mmdb_stale_check_interval")->AsInterval(); - - if ( mmdb_stale_check_interval < 0.0 ) - return false; - - if ( zeek::run_state::network_time - last_check < mmdb_stale_check_interval ) - return false; - - last_check = zeek::run_state::network_time; - - if ( 0 != stat(mmdb.filename, &buf) ) - return true; - - if ( buf.st_ino != file_info.st_ino || buf.st_mtime != file_info.st_mtime ) - { - report_mmdb_msg("%s change detected for MaxMind DB [%s]", - buf.st_ino != file_info.st_ino ? "Inode" : "Modification time", - mmdb.filename); - return true; - } - - return false; - } - -const char* MMDB::Filename() - { - return mmdb.filename; - } - -std::unique_ptr mmdb_loc; -std::unique_ptr mmdb_asn; -static bool did_mmdb_loc_db_error = false; -static bool did_mmdb_asn_db_error = false; - -static bool mmdb_open(const char* filename, bool asn) - { - struct stat buf; - - if ( 0 != stat(filename, &buf) ) - { - return false; - } - - try - { - if ( asn ) - { - mmdb_asn.reset(new MMDB(filename, buf)); - } - else - { - mmdb_loc.reset(new MMDB(filename, buf)); - } - } - - catch ( const std::exception& e ) - { - if ( asn ) - did_mmdb_asn_db_error = false; - else - did_mmdb_loc_db_error = false; - - report_mmdb_msg("Failed to open MaxMind DB: %s [%s]", filename, - e.what()); - return false; - } - - return true; - } - -static bool mmdb_open_loc(const char* filename) - { - return mmdb_open(filename, false); - } - -static bool mmdb_open_asn(const char* filename) - { - return mmdb_open(filename, true); - } - -static void mmdb_check_loc() - { - if ( mmdb_loc && mmdb_loc->StaleDB() ) - { - report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_loc->Filename()); - did_mmdb_loc_db_error = false; - mmdb_loc.reset(); - } - } - -static void mmdb_check_asn() - { - if ( mmdb_asn && mmdb_asn->StaleDB() ) - { - report_mmdb_msg("Closing stale MaxMind DB [%s]", mmdb_asn->Filename()); - did_mmdb_asn_db_error = false; - mmdb_asn.reset(); - } - } - -static bool mmdb_lookup(const zeek::IPAddr& addr, MMDB_lookup_result_s& result, - bool asn) - { - struct sockaddr_storage ss = {0}; - - if ( IPv4 == addr.GetFamily() ) - { - struct sockaddr_in* sa = (struct sockaddr_in*)&ss; - sa->sin_family = AF_INET; - addr.CopyIPv4(&sa->sin_addr); - } - - else - { - struct sockaddr_in6* sa = (struct sockaddr_in6*)&ss; - sa->sin6_family = AF_INET6; - addr.CopyIPv6(&sa->sin6_addr); - } - - try - { - result = asn ? mmdb_asn->Lookup((struct sockaddr*)&ss) - : mmdb_loc->Lookup((struct sockaddr*)&ss); - } - - catch ( const std::exception& e ) - { - report_mmdb_msg("MaxMind DB lookup location error [%s]", e.what()); - return false; - } - - return result.found_entry; - } - -static bool mmdb_lookup_loc(const zeek::IPAddr& addr, MMDB_lookup_result_s& result) - { - return mmdb_lookup(addr, result, false); - } - -static bool mmdb_lookup_asn(const zeek::IPAddr& addr, MMDB_lookup_result_s& result) - { - return mmdb_lookup(addr, result, true); - } - -static zeek::ValPtr mmdb_getvalue(MMDB_entry_data_s* entry_data, int status, - int data_type ) - { - switch (status) - { - case MMDB_SUCCESS: - if ( entry_data->has_data ) - { - switch (data_type) - { - case MMDB_DATA_TYPE_UTF8_STRING: - return zeek::make_intrusive( - entry_data->data_size, entry_data->utf8_string); - break; - - case MMDB_DATA_TYPE_DOUBLE: - return zeek::make_intrusive(entry_data->double_value); - break; - - case MMDB_DATA_TYPE_UINT32: - return zeek::val_mgr->Count(entry_data->uint32); - - default: - break; - } - } - break; - - case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR: - // key doesn't exist, nothing to do - break; - - default: - report_mmdb_msg("MaxMind DB error [%s]", MMDB_strerror(status)); - break; - } - - return nullptr; - } - -static bool mmdb_try_open_loc () - { - // City database is always preferred over Country database. - const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal(); - std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString(); - - if ( ! mmdb_dir.empty() ) - { - auto d = mmdb_dir + "/GeoLite2-City.mmdb"; - - if ( mmdb_open_loc(d.data()) ) - return true; - - d = mmdb_dir + "/GeoLite2-Country.mmdb"; - - if ( mmdb_open_loc(d.data()) ) - return true;; - } - - return mmdb_open_loc("/usr/share/GeoIP/GeoLite2-City.mmdb") - || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-City.mmdb") - || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-City.mmdb") - || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-City.mmdb") - || mmdb_open_loc("/usr/share/GeoIP/GeoLite2-Country.mmdb") - || mmdb_open_loc("/var/lib/GeoIP/GeoLite2-Country.mmdb") - || mmdb_open_loc("/usr/local/share/GeoIP/GeoLite2-Country.mmdb") - || mmdb_open_loc("/usr/local/var/GeoIP/GeoLite2-Country.mmdb"); - } - -static bool mmdb_try_open_asn () - { - const auto& mmdb_dir_val = zeek::detail::global_scope()->Find("mmdb_dir")->GetVal(); - std::string mmdb_dir = mmdb_dir_val->AsString()->CheckString(); - - if ( ! mmdb_dir.empty() ) - { - auto d = mmdb_dir + "/GeoLite2-ASN.mmdb"; - - if ( mmdb_open_asn(d.data()) ) - return true; - } - - return mmdb_open_asn("/usr/share/GeoIP/GeoLite2-ASN.mmdb") - || mmdb_open_asn("/var/lib/GeoIP/GeoLite2-ASN.mmdb") - || mmdb_open_asn("/usr/local/share/GeoIP/GeoLite2-ASN.mmdb") - || mmdb_open_asn("/usr/local/var/GeoIP/GeoLite2-ASN.mmdb"); - } - -#endif -%%} - -## Initializes MMDB for later use of lookup_location. -## Requires Zeek to be built with ``libmaxminddb``. -## -## f: The filename of the MaxMind City or Country DB. -## -## Returns: A boolean indicating whether the db was successfully opened. -## -## .. zeek:see:: lookup_autonomous_system -function mmdb_open_location_db%(f: string%) : bool - %{ -#ifdef USE_GEOIP - return zeek::val_mgr->Bool(mmdb_open_loc(f->CheckString())); -#else - return zeek::val_mgr->False(); -#endif - %} - -## Initializes MMDB for later use of lookup_autonomous_system. -## Requires Zeek to be built with ``libmaxminddb``. -## -## f: The filename of the MaxMind ASN DB. -## -## Returns: A boolean indicating whether the db was successfully opened. -## -## .. zeek:see:: lookup_autonomous_system -function mmdb_open_asn_db%(f: string%) : bool - %{ -#ifdef USE_GEOIP - return zeek::val_mgr->Bool(mmdb_open_asn(f->CheckString())); -#else - return zeek::val_mgr->False(); -#endif - %} - -## Performs a geo-lookup of an IP address. -## Requires Zeek to be built with ``libmaxminddb``. -## -## a: The IP address to lookup. -## -## Returns: A record with country, region, city, latitude, and longitude. -## -## .. zeek:see:: lookup_autonomous_system -function lookup_location%(a: addr%) : geo_location - %{ - static auto geo_location = zeek::id::find_type("geo_location"); - auto location = zeek::make_intrusive(geo_location); - -#ifdef USE_GEOIP - mmdb_check_loc(); - if ( ! mmdb_loc ) - { - if ( ! mmdb_try_open_loc() ) - { - if ( ! did_mmdb_loc_db_error ) - { - did_mmdb_loc_db_error = true; - zeek::emit_builtin_error("Failed to open GeoIP location database"); - } - - return location; - } - } - - MMDB_lookup_result_s result; - - if ( mmdb_lookup_loc(a->AsAddr(), result) ) - { - MMDB_entry_data_s entry_data; - int status; - - // Get Country ISO Code - status = MMDB_get_value(&result.entry, &entry_data, - "country", "iso_code", nullptr); - location->Assign(0, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_UTF8_STRING)); - - // Get Major Subdivision ISO Code - status = MMDB_get_value(&result.entry, &entry_data, - "subdivisions", "0", "iso_code", nullptr); - location->Assign(1, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_UTF8_STRING)); - - // Get City English Name - status = MMDB_get_value(&result.entry, &entry_data, - "city", "names", "en", nullptr); - location->Assign(2, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_UTF8_STRING)); - - // Get Location Latitude - status = MMDB_get_value(&result.entry, &entry_data, - "location", "latitude", nullptr); - location->Assign(3, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_DOUBLE)); - - // Get Location Longitude - status = MMDB_get_value(&result.entry, &entry_data, - "location", "longitude", nullptr); - location->Assign(4, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_DOUBLE)); - - return location; - } - -#else // not USE_GEOIP - static int missing_geoip_reported = 0; - - if ( ! missing_geoip_reported ) - { - zeek::emit_builtin_error("Zeek was not configured for GeoIP support"); - missing_geoip_reported = 1; - } -#endif - - // We can get here even if we have MMDB support if we weren't - // able to initialize it or it didn't return any information for - // the address. - - return std::move(location); - %} - -## Performs an lookup of AS number & organization of an IP address. -## Requires Zeek to be built with ``libmaxminddb``. -## -## a: The IP address to lookup. -## -## Returns: A record with autonomous system number and organization that contains *a*. -## -## .. zeek:see:: lookup_location -function lookup_autonomous_system%(a: addr%) : geo_autonomous_system - %{ - static auto geo_autonomous_system = zeek::id::find_type("geo_autonomous_system"); - auto autonomous_system = zeek::make_intrusive(geo_autonomous_system); - -#ifdef USE_GEOIP - mmdb_check_asn(); - if ( ! mmdb_asn ) - { - if ( ! mmdb_try_open_asn() ) - { - if ( ! did_mmdb_asn_db_error ) - { - did_mmdb_asn_db_error = true; - zeek::emit_builtin_error("Failed to open GeoIP ASN database"); - } - - return autonomous_system; - } - } - - MMDB_lookup_result_s result; - - if ( mmdb_lookup_asn(a->AsAddr(), result) ) - { - MMDB_entry_data_s entry_data; - int status; - - // Get Autonomous System Number - status = MMDB_get_value(&result.entry, &entry_data, - "autonomous_system_number", nullptr); - autonomous_system->Assign(0, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_UINT32)); - - // Get Autonomous System Organization - status = MMDB_get_value(&result.entry, &entry_data, - "autonomous_system_organization", nullptr); - autonomous_system->Assign(1, mmdb_getvalue(&entry_data, status, - MMDB_DATA_TYPE_UTF8_STRING)); - - return autonomous_system; - } - -#else // not USE_GEOIP - static int missing_geoip_reported = 0; - - if ( ! missing_geoip_reported ) - { - zeek::emit_builtin_error("Zeek was not configured for GeoIP ASN support"); - missing_geoip_reported = 1; - } -#endif - - // We can get here even if we have GeoIP support, if we weren't - // able to initialize it or it didn't return any information for - // the address. - return std::move(autonomous_system); - %} - ## Calculates distance between two geographic locations using the haversine ## formula. Latitudes and longitudes must be given in degrees, where southern ## hemisphere latitudes are negative and western hemisphere longitudes are